[{"data":1,"prerenderedAt":3750},["ShallowReactive",2],{"search-sections-chit":3,"nav-chit":801,"content-tree-chit":841,"footer-resources":859,"content-/v0.0.1/learn/overview":3628,"surround-/v0.0.1/learn/overview":3748},[4,10,14,20,25,30,35,40,44,49,54,59,64,69,74,79,83,88,92,97,102,107,113,118,123,128,133,138,143,148,152,157,161,166,171,176,181,186,191,196,201,205,210,215,220,225,230,234,239,243,248,253,258,263,268,273,277,282,287,292,297,302,306,311,315,319,324,329,334,339,343,348,353,358,363,368,372,377,382,387,391,396,400,404,407,412,417,422,427,432,437,442,447,452,457,462,467,472,477,482,486,491,494,498,503,507,512,517,521,526,531,536,539,544,549,554,559,563,568,573,578,583,588,593,598,603,606,611,616,621,626,631,636,641,646,650,655,660,664,669,672,676,680,684,689,693,697,701,705,709,714,719,724,728,733,738,743,748,753,758,763,767,771,776,780,784,788,792,797],{"id":5,"title":6,"titles":7,"content":8,"level":9},"/v0.0.1/learn/overview","Overview",[],"Conversation lifecycle management for LLM-powered applications",1,{"id":11,"title":6,"titles":12,"content":13,"level":9},"/v0.0.1/learn/overview#overview",[],"Chit is a conversation lifecycle manager for LLM-powered applications. It provides a focused controller that orchestrates pluggable processors, manages multi-turn interactions, and streams responses — without dictating how you build your reasoning stack.",{"id":15,"title":16,"titles":17,"content":18,"level":19},"/v0.0.1/learn/overview#the-idea","The Idea",[6],"Chat applications share common infrastructure: session management, turn-taking, response streaming, observability. But the \"brain\" — how you reason, execute actions, fetch data — varies wildly between applications. Chit separates these concerns. It handles the conversation lifecycle while you wire together your own reasoning primitives. The result: a thin orchestration layer that gets out of your way.",2,{"id":21,"title":22,"titles":23,"content":24,"level":19},"/v0.0.1/learn/overview#the-implementation","The Implementation",[6],"Chit provides: Chat controller — manages conversation state and turn-takingProcessor interface — pluggable brain for reasoning and executionEmitter interface — dual-channel output (streaming text, structured resources)Continuation pattern — clean multi-turn workflows without callback hellSession integration — built on zyn for conversation historySignal observability — lifecycle events via capitan",{"id":26,"title":27,"titles":28,"content":29,"level":19},"/v0.0.1/learn/overview#what-it-enables","What It Enables",[6],"Build chat UIs with streaming responsesImplement multi-step workflows that pause for user inputWire cogito (reasoning), ago (actions), and scio (data) into a coherent chat experienceObserve conversation lifecycle for logging, metrics, and debuggingTest chat logic with provided mocks and helpers",{"id":31,"title":32,"titles":33,"content":34,"level":19},"/v0.0.1/learn/overview#next-steps","Next Steps",[6],"Quickstart — get running in minutesConcepts — understand the core abstractionsAPI Reference — complete function documentation",{"id":36,"title":37,"titles":38,"content":39,"level":9},"/v0.0.1/learn/quickstart","Quickstart",[],"Get up and running with chit in minutes",{"id":41,"title":37,"titles":42,"content":43,"level":9},"/v0.0.1/learn/quickstart#quickstart",[],"",{"id":45,"title":46,"titles":47,"content":48,"level":19},"/v0.0.1/learn/quickstart#installation","Installation",[37],"go get github.com/zoobz-io/chit Requires Go 1.21 or later.",{"id":50,"title":51,"titles":52,"content":53,"level":19},"/v0.0.1/learn/quickstart#basic-usage","Basic Usage",[37],"A minimal chat application needs three things: a processor (your brain), an emitter (your output), and the chat controller. package main\n\nimport (\n    \"context\"\n    \"fmt\"\n\n    \"github.com/zoobz-io/chit\"\n    \"github.com/zoobz-io/zyn\"\n)\n\nfunc main() {\n    // 1. Define your processor — this is where reasoning happens\n    processor := chit.ProcessorFunc(func(ctx context.Context, input string, session *zyn.Session) (chit.Result, error) {\n        // Your LLM call, tool execution, or other logic goes here\n        return &chit.Response{Content: \"You said: \" + input}, nil\n    })\n\n    // 2. Define your emitter — this is where output goes\n    emitter := &PrintEmitter{}\n\n    // 3. Create the chat\n    chat := chit.New(processor, emitter,\n        chit.WithSystemPrompt(\"You are a helpful assistant.\"),\n    )\n\n    // 4. Handle user input\n    ctx := context.Background()\n    _ = chat.Handle(ctx, \"Hello!\")\n    // Output: You said: Hello!\n}\n\n// PrintEmitter writes messages to stdout\ntype PrintEmitter struct{}\n\nfunc (e *PrintEmitter) Emit(_ context.Context, msg chit.Message) error {\n    fmt.Println(msg.Content)\n    return nil\n}\n\nfunc (e *PrintEmitter) Push(_ context.Context, _ chit.Resource) error {\n    return nil\n}\n\nfunc (e *PrintEmitter) Close() error {\n    return nil\n}",{"id":55,"title":56,"titles":57,"content":58,"level":19},"/v0.0.1/learn/quickstart#the-processor","The Processor",[37],"The processor is your pluggable brain. It receives user input and conversation history, then returns either a Response (complete) or a Yield (awaiting more input). processor := chit.ProcessorFunc(func(ctx context.Context, input string, session *zyn.Session) (chit.Result, error) {\n    // Access conversation history\n    messages := session.Messages()\n\n    // Return a complete response\n    return &chit.Response{\n        Content:  \"Hello!\",\n        Metadata: map[string]any{\"model\": \"gpt-4\"},\n    }, nil\n}) Wire your own reasoning stack here — cogito for thinking, ago for actions, scio for data. See the Concepts guide for more.",{"id":60,"title":61,"titles":62,"content":63,"level":19},"/v0.0.1/learn/quickstart#multi-turn-conversations","Multi-Turn Conversations",[37],"When your processor needs more information, yield with a continuation: processor := chit.ProcessorFunc(func(ctx context.Context, input string, session *zyn.Session) (chit.Result, error) {\n    if input == \"start\" {\n        return &chit.Yield{\n            Prompt: \"What's your name?\",\n            Continuation: func(ctx context.Context, name string) (chit.Result, error) {\n                return &chit.Response{Content: \"Hello, \" + name + \"!\"}, nil\n            },\n        }, nil\n    }\n    return &chit.Response{Content: \"Say 'start' to begin.\"}, nil\n}) The chat controller stores the continuation. The next call to Handle() resumes from where you left off. See the Testing guide for YieldingProcessor helper.",{"id":65,"title":66,"titles":67,"content":68,"level":19},"/v0.0.1/learn/quickstart#streaming-resources","Streaming Resources",[37],"Processors can push structured data alongside text responses by retrieving the emitter from context: processor := chit.ProcessorFunc(func(ctx context.Context, input string, session *zyn.Session) (chit.Result, error) {\n    // Get emitter from context\n    emitter := chit.EmitterFromContext(ctx)\n\n    // Push structured data to client\n    if emitter != nil {\n        emitter.Push(ctx, chit.Resource{\n            Type:    \"data\",\n            URI:     \"db://users/123\",\n            Payload: map[string]string{\"name\": \"Alice\"},\n        })\n    }\n\n    return &chit.Response{Content: \"Found user Alice.\"}, nil\n})",{"id":70,"title":71,"titles":72,"content":73,"level":19},"/v0.0.1/learn/quickstart#configuration-options","Configuration Options",[37],"chat := chit.New(processor, emitter,\n    chit.WithSystemPrompt(\"You are a helpful assistant.\"),\n    chit.WithSession(existingSession),  // Use existing zyn.Session\n    chit.WithMetadata(map[string]any{\"user_id\": \"123\"}),\n    chit.WithConfig(&chit.Config{\n        SystemPrompt: \"Custom prompt\",\n        Metadata:     map[string]any{\"version\": \"1.0\"},\n    }),\n)",{"id":75,"title":76,"titles":77,"content":78,"level":19},"/v0.0.1/learn/quickstart#reliability-options","Reliability Options",[37],"Chit uses pipz for composable reliability patterns. Add them as options: chat := chit.New(processor, emitter,\n    chit.WithSystemPrompt(\"You are helpful.\"),\n\n    // Middleware (runs before processor)\n    chit.WithMiddleware(\n        chit.UseValidation(4096),  // Reject inputs over 4096 chars\n    ),\n\n    // Reliability wrappers\n    chit.WithRetry(3),                           // Retry up to 3 times\n    chit.WithTimeout(30 * time.Second),          // 30s timeout\n    chit.WithRateLimit(10, 5),                   // 10 req/s, burst of 5\n    chit.WithCircuitBreaker(5, 30*time.Second),  // Open after 5 failures\n) See the Reliability Guide for custom middleware and advanced patterns.",{"id":80,"title":32,"titles":81,"content":82,"level":19},"/v0.0.1/learn/quickstart#next-steps",[37],"Concepts — understand Chat, Processor, Emitter, and ResultArchitecture — how the lifecycle works internallyReliability Guide — retry, timeout, middlewareTesting Guide — mocks and helpers for testingAPI Reference — complete function documentation html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}",{"id":84,"title":85,"titles":86,"content":87,"level":9},"/v0.0.1/learn/concepts","Concepts",[],"Core abstractions in chit",{"id":89,"title":85,"titles":90,"content":91,"level":9},"/v0.0.1/learn/concepts#concepts",[],"Chit is built around a small set of abstractions that separate conversation lifecycle from reasoning logic. Understanding these concepts lets you reason about how the pieces fit together.",{"id":93,"title":94,"titles":95,"content":96,"level":19},"/v0.0.1/learn/concepts#chat","Chat",[85],"Chat is the conversation controller. It's not data flowing through a pipeline — it's the orchestrator that manages state, delegates to your processor, and streams output through your emitter. A Chat owns: A unique ID for trackingA session (zyn.Session) for conversation historyA processor for reasoningAn emitter for outputAn optional continuation for multi-turn state Think of Chat as the conductor. It doesn't play instruments (that's your processor). It coordinates timing and flow. See: Chat type reference",{"id":98,"title":99,"titles":100,"content":101,"level":19},"/v0.0.1/learn/concepts#processor","Processor",[85],"The Processor interface is your pluggable brain. Chit calls your processor with user input and conversation history. Your processor returns a result. type Processor interface {\n    Process(ctx context.Context, input string, session *zyn.Session) (Result, error)\n} What happens inside is up to you: Call an LLM via cogitoExecute actions via agoFetch data via scioAny combination of the above Chit doesn't care. It just needs a Result back. The ProcessorFunc adapter lets you use a function directly: processor := chit.ProcessorFunc(func(ctx context.Context, input string, session *zyn.Session) (chit.Result, error) {\n    // Your logic here\n    return &chit.Response{Content: \"Hello\"}, nil\n}) See: Processor interface reference",{"id":103,"title":104,"titles":105,"content":106,"level":19},"/v0.0.1/learn/concepts#result","Result",[85],"A Result represents the outcome of processing. There are two kinds:",{"id":108,"title":109,"titles":110,"content":111,"level":112},"/v0.0.1/learn/concepts#response","Response",[85,104],"Processing is complete. The content should be emitted to the user. &chit.Response{\n    Content:  \"Here's your answer.\",\n    Metadata: map[string]any{\"tokens\": 150},\n}",3,{"id":114,"title":115,"titles":116,"content":117,"level":112},"/v0.0.1/learn/concepts#yield","Yield",[85,104],"Processing is paused, awaiting more input. The prompt should be emitted, and the continuation stored for later. &chit.Yield{\n    Prompt: \"What's your email address?\",\n    Continuation: func(ctx context.Context, email string) (chit.Result, error) {\n        // Resume processing with the email\n        return &chit.Response{Content: \"Thanks, \" + email}, nil\n    },\n} This is how chit handles multi-turn workflows cleanly. No callback hell, no state machines in userland — just return a Yield with how to continue. See: Result types reference",{"id":119,"title":120,"titles":121,"content":122,"level":19},"/v0.0.1/learn/concepts#continuation","Continuation",[85],"A Continuation is the function that resumes processing after a Yield. type Continuation func(ctx context.Context, input string) (Result, error) When the user provides the next input: Chat detects it has a stored continuationInstead of calling the processor, it calls the continuationThe continuation returns a Result (which could be another Yield) Continuations can chain indefinitely for complex multi-step flows. Reliability parity: Continuations run through the same pipeline options as fresh processor calls. If you configure retry, timeout, rate limiting, or middleware on your Chat, those apply equally when resuming a continuation. The continuation is wrapped in a terminal and the same reliability wrappers are applied. See: Continuation type reference",{"id":124,"title":125,"titles":126,"content":127,"level":19},"/v0.0.1/learn/concepts#emitter","Emitter",[85],"The Emitter interface handles all output to the client. It has two channels:",{"id":129,"title":130,"titles":131,"content":132,"level":112},"/v0.0.1/learn/concepts#emit-streaming-text","Emit — Streaming Text",[85,125],"Emit(ctx context.Context, msg Message) error Used for conversational content. Chat calls this with Response content or Yield prompts.",{"id":134,"title":135,"titles":136,"content":137,"level":112},"/v0.0.1/learn/concepts#push-structured-resources","Push — Structured Resources",[85,125],"Push(ctx context.Context, resource Resource) error Used for structured data like fetched records, context enrichment, or tool results. Your processor can retrieve the emitter from context and push resources during processing. emitter := chit.EmitterFromContext(ctx)\nemitter.Push(ctx, chit.Resource{\n    Type:    \"data\",\n    URI:     \"db://users/123\",\n    Payload: userData,\n}) This separation lets you stream text while also delivering structured data — useful for rich chat UIs that display both conversation and data panels. See: Emitter interface reference",{"id":139,"title":140,"titles":141,"content":142,"level":19},"/v0.0.1/learn/concepts#session","Session",[85],"Chit uses zyn.Session for conversation history. The session tracks messages by role (system, user, assistant) and provides the context window for your processor. Chat manages the session automatically: Adds user input before calling processorAdds assistant response after processingApplies system prompt at creation You can also provide your own session via WithSession() for session persistence or pre-populated history.",{"id":144,"title":145,"titles":146,"content":147,"level":19},"/v0.0.1/learn/concepts#signals","Signals",[85],"Chit emits lifecycle signals via capitan for observability: SignalWhenChatCreatedNew chat instantiatedInputReceivedUser input receivedProcessingStartedProcessor calledProcessingCompletedProcessor returned successfullyProcessingFailedProcessor returned errorResponseEmittedResponse sent to emitterTurnYieldedProcessing yielded for inputTurnResumedContinuation resumed Subscribe to these for logging, metrics, tracing, or debugging. See: Signals reference",{"id":149,"title":32,"titles":150,"content":151,"level":19},"/v0.0.1/learn/concepts#next-steps",[85],"Architecture — how the lifecycle flows internallyAPI Reference — function signatures and behaviorTypes Reference — complete type documentation html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}",{"id":153,"title":154,"titles":155,"content":156,"level":9},"/v0.0.1/learn/architecture","Architecture",[],"How chit works internally",{"id":158,"title":154,"titles":159,"content":160,"level":9},"/v0.0.1/learn/architecture#architecture",[],"This document covers how chit works internally. It's for power users who want to understand the implementation, contribute, or debug complex scenarios.",{"id":162,"title":163,"titles":164,"content":165,"level":19},"/v0.0.1/learn/architecture#component-overview","Component Overview",[154],"┌─────────────────────────────────────────────────────────────┐\n│                          Chat                               │\n│  ┌─────────┐  ┌───────────┐  ┌─────────┐  ┌─────────────┐  │\n│  │   ID    │  │  Session  │  │ Config  │  │Continuation │  │\n│  └─────────┘  └───────────┘  └─────────┘  └─────────────┘  │\n│                      │                           │          │\n│                      ▼                           ▼          │\n│  ┌─────────────────────────────────────────────────────┐   │\n│  │                    Handle()                          │   │\n│  │  ┌──────────┐              ┌──────────────────┐     │   │\n│  │  │Processor │◄────────────►│   Continuation   │     │   │\n│  │  └──────────┘              └──────────────────┘     │   │\n│  │        │                                             │   │\n│  │        ▼                                             │   │\n│  │  ┌──────────┐                                        │   │\n│  │  │ Emitter  │───────────────────────────────────────►│   │\n│  │  └──────────┘                            Output      │   │\n│  └─────────────────────────────────────────────────────┘   │\n└─────────────────────────────────────────────────────────────┘ Chat owns all state. Handle() is the single entry point. It delegates to either the Processor (fresh input) or Continuation (resumed input), then routes the result through the Emitter.",{"id":167,"title":168,"titles":169,"content":170,"level":19},"/v0.0.1/learn/architecture#pipeline-architecture","Pipeline Architecture",[154],"Chit uses pipz to compose reliability patterns. When you configure options like WithRetry or WithTimeout, chit builds a processing pipeline: Input → [Rate Limit] → [Timeout] → [Retry] → [Middleware...] → Terminal → Result The Terminal is the innermost layer that calls the actual processor. Everything else wraps it.",{"id":172,"title":173,"titles":174,"content":175,"level":112},"/v0.0.1/learn/architecture#continuation-pipeline","Continuation Pipeline",[154,168],"When a continuation is resumed, chit builds a separate pipeline with the same options: func (c *Chat) buildContinuationPipeline(cont Continuation) pipz.Chainable[*ChatRequest] {\n    pipeline := NewContinuationTerminal(cont)  // Different terminal\n    for _, opt := range c.pipelineOpts {       // Same options applied\n        pipeline = opt(pipeline)\n    }\n    return pipeline\n} This ensures reliability parity — a continuation gets the same retry, timeout, rate limiting, and middleware as a fresh processor call.",{"id":177,"title":178,"titles":179,"content":180,"level":19},"/v0.0.1/learn/architecture#the-handle-lifecycle","The Handle Lifecycle",[154],"Every call to Handle(ctx, input) follows this flow: ┌─────────────────────────────────────────────┐\n│               Handle(input)                 │\n├─────────────────────────────────────────────┤\n│  1. Emit InputReceived signal               │\n│  2. Inject emitter into context             │\n│  3. Append user message to session          │\n│  4. Build ChatRequest                       │\n│  5. Check for continuation                  │\n│     ├─ YES: Emit TurnResumed                │\n│     │       Build continuation pipeline     │\n│     │       Process through pipeline        │\n│     └─ NO:  Process through main pipeline   │\n│  6. On error: Emit ProcessingFailed         │\n│  7. On success: Emit ProcessingCompleted    │\n│  8. Handle result via handleResult()        │\n└─────────────────────────────────────────────┘",{"id":182,"title":183,"titles":184,"content":185,"level":112},"/v0.0.1/learn/architecture#result-handling","Result Handling",[154,178],"┌─────────────────────────────────────────────┐\n│            handleResult(result)             │\n├─────────────────────────────────────────────┤\n│  Switch on result type:                     │\n│                                             │\n│  Response:                                  │\n│    1. Append to session as assistant        │\n│    2. Emit ResponseEmitted signal           │\n│    3. Call emitter.Emit() with message      │\n│                                             │\n│  Yield:                                     │\n│    1. Store continuation on Chat            │\n│    2. Emit TurnYielded signal               │\n│    3. Call emitter.Emit() with prompt       │\n│                                             │\n│  Unknown: Return ErrUnknownResultType       │\n└─────────────────────────────────────────────┘",{"id":187,"title":188,"titles":189,"content":190,"level":19},"/v0.0.1/learn/architecture#concurrency-model","Concurrency Model",[154],"Chat uses a mutex to protect mutable state: continuation — stored/cleared during yield/resumeAll session mutations happen under lock or after unlock The lock is held briefly: Lock at start of HandleRead/clear continuationUnlock before calling processor or continuation This allows long-running processor calls without blocking other goroutines that might query HasContinuation().",{"id":192,"title":193,"titles":194,"content":195,"level":19},"/v0.0.1/learn/architecture#context-injection","Context Injection",[154],"Before calling the processor, Chat injects the emitter into context: ctx = WithEmitter(ctx, c.emitter)\nresult, err := c.processor.Process(ctx, input, c.session) This allows processors (or underlying primitives like scio) to push resources during processing without needing explicit emitter references. Retrieve it with: emitter := chit.EmitterFromContext(ctx)\nif emitter != nil {\n    emitter.Push(ctx, resource)\n}",{"id":197,"title":198,"titles":199,"content":200,"level":19},"/v0.0.1/learn/architecture#signal-flow","Signal Flow",[154],"Signals are emitted at key lifecycle points via capitan: PointSignalFieldsNew()ChatCreatedchat_idHandle() startInputReceivedchat_id, input, input_sizeBefore processProcessingStartedchat_idIf resumingTurnResumedchat_idOn errorProcessingFailedchat_id, processing_duration, errorOn successProcessingCompletedchat_id, processing_durationResponse emitResponseEmittedchat_id, role, content_sizeYield storeTurnYieldedchat_id, prompt ResourcePushed is available but not emitted by Chat — it's for processors that push resources to emit themselves.",{"id":202,"title":203,"titles":204,"content":43,"level":19},"/v0.0.1/learn/architecture#design-qa","Design Q&A",[154],{"id":206,"title":207,"titles":208,"content":209,"level":112},"/v0.0.1/learn/architecture#why-is-emitter-required-not-optional","Why is Emitter required, not optional?",[154,203],"Chat is fundamentally about streaming output. A chat without output isn't useful. Making it required: Eliminates nil checks throughoutMakes the contract explicitSimplifies testing (always provide a mock)",{"id":211,"title":212,"titles":213,"content":214,"level":112},"/v0.0.1/learn/architecture#why-inject-emitter-via-context-instead-of-passing-directly","Why inject Emitter via context instead of passing directly?",[154,203],"Context injection decouples processors from chit's Emitter type. A processor built with cogito/ago/scio doesn't need to know about chit — it just checks context for an emitter. This enables: Reusable processors across different chat implementationsTesting processors in isolationGradual adoption",{"id":216,"title":217,"titles":218,"content":219,"level":112},"/v0.0.1/learn/architecture#why-store-continuation-on-chat-instead-of-returning-it","Why store continuation on Chat instead of returning it?",[154,203],"Storing the continuation makes the API simpler for callers: // Simple: just call Handle repeatedly\nchat.Handle(ctx, \"start\")\nchat.Handle(ctx, \"my name\")\n\n// vs. Alternative: caller manages continuation\nresult := chat.Handle(ctx, \"start\")\nif yield, ok := result.(*Yield); ok {\n    result = yield.Continuation(ctx, \"my name\")\n} The Chat-manages-continuation approach matches how real chat UIs work — you don't see the continuation machinery, you just send messages.",{"id":221,"title":222,"titles":223,"content":224,"level":112},"/v0.0.1/learn/architecture#why-dual-channel-emitter-emit-push","Why dual-channel Emitter (Emit + Push)?",[154,203],"Text and structured data serve different purposes: Emit — conversational flow, rendered as chat bubblesPush — data payloads, rendered in side panels or used by UI logic Separating them lets UI implementations handle each appropriately. A terminal UI might ignore Push; a web UI might render pushed resources in a data viewer.",{"id":226,"title":227,"titles":228,"content":229,"level":19},"/v0.0.1/learn/architecture#performance","Performance",[154],"Memory: Chat holds references, not copies. Session messages accumulate but that's zyn's concern. Latency: Handle adds minimal overhead — signal emission, mutex acquire/release, context creation. The processor dominates latency. Concurrency: Safe for concurrent Handle() calls, but calls serialize on the mutex. For parallel conversations, use separate Chat instances.",{"id":231,"title":32,"titles":232,"content":233,"level":19},"/v0.0.1/learn/architecture#next-steps",[154],"Testing Guide — test your processors and emittersAPI Reference — function signaturesTypes Reference — complete type documentation html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}",{"id":235,"title":236,"titles":237,"content":238,"level":9},"/v0.0.1/guides/testing","Testing Guide",[],"How to test code that uses chit",{"id":240,"title":236,"titles":241,"content":242,"level":9},"/v0.0.1/guides/testing#testing-guide",[],"Chit provides test helpers in the github.com/zoobz-io/chit/testing package. This guide covers how to test processors, emitters, and chat interactions.",{"id":244,"title":245,"titles":246,"content":247,"level":19},"/v0.0.1/guides/testing#test-helpers","Test Helpers",[236],"Import the testing package: import (\n    \"github.com/zoobz-io/chit\"\n    helpers \"github.com/zoobz-io/chit/testing\"\n)",{"id":249,"title":250,"titles":251,"content":252,"level":112},"/v0.0.1/guides/testing#mockprocessor","MockProcessor",[236,245],"A configurable mock that records calls and returns controlled responses. func TestMyFeature(t *testing.T) {\n    mock := &helpers.MockProcessor{\n        ProcessFunc: func(ctx context.Context, input string, session *zyn.Session) (chit.Result, error) {\n            return &chit.Response{Content: \"mocked: \" + input}, nil\n        },\n    }\n\n    chat := chit.New(mock, &helpers.CollectingEmitter{})\n    _ = chat.Handle(context.Background(), \"hello\")\n\n    // Verify calls\n    if len(mock.Calls) != 1 {\n        t.Errorf(\"expected 1 call, got %d\", len(mock.Calls))\n    }\n    if mock.Calls[0].Input != \"hello\" {\n        t.Errorf(\"expected input 'hello', got %q\", mock.Calls[0].Input)\n    }\n} Fields: ProcessFunc — custom behavior (optional, defaults to empty response)Calls — slice of ProcessCall{Input, Session} for verification",{"id":254,"title":255,"titles":256,"content":257,"level":112},"/v0.0.1/guides/testing#mockemitter","MockEmitter",[236,245],"A configurable mock for testing output behavior. func TestEmitterErrors(t *testing.T) {\n    emitErr := errors.New(\"emit failed\")\n    mock := &helpers.MockEmitter{\n        EmitFunc: func(ctx context.Context, msg chit.Message) error {\n            return emitErr\n        },\n    }\n\n    chat := chit.New(helpers.EchoProcessor(), mock)\n    err := chat.Handle(context.Background(), \"test\")\n\n    if !errors.Is(err, emitErr) {\n        t.Errorf(\"expected emit error, got %v\", err)\n    }\n} Fields: EmitFunc, PushFunc, CloseFunc — custom behavior (optional)Messages — recorded emitted messagesResources — recorded pushed resourcesClosed — whether Close was called",{"id":259,"title":260,"titles":261,"content":262,"level":112},"/v0.0.1/guides/testing#collectingemitter","CollectingEmitter",[236,245],"A simple emitter that collects all output for assertions. func TestChatOutput(t *testing.T) {\n    emitter := &helpers.CollectingEmitter{}\n    processor := helpers.EchoProcessor()\n\n    chat := chit.New(processor, emitter)\n    _ = chat.Handle(context.Background(), \"hello\")\n\n    // Check collected content\n    if emitter.Content() != \"Echo: hello\" {\n        t.Errorf(\"unexpected content: %q\", emitter.Content())\n    }\n\n    // Or inspect individual messages\n    if len(emitter.Messages) != 1 {\n        t.Fatalf(\"expected 1 message, got %d\", len(emitter.Messages))\n    }\n    if emitter.Messages[0].Role != \"assistant\" {\n        t.Errorf(\"expected assistant role\")\n    }\n} Methods: Content() — returns all message content concatenated",{"id":264,"title":265,"titles":266,"content":267,"level":112},"/v0.0.1/guides/testing#echoprocessor","EchoProcessor",[236,245],"A processor that echoes input, useful for testing chat flow. processor := helpers.EchoProcessor()\n// Returns Response{Content: \"Echo: \" + input}",{"id":269,"title":270,"titles":271,"content":272,"level":112},"/v0.0.1/guides/testing#yieldingprocessor","YieldingProcessor",[236,245],"A processor that yields on first call and responds on continuation. processor := helpers.YieldingProcessor(\"What's your name?\", \"Hello\")\n\n// First call yields\nresult, _ := processor.Process(ctx, \"start\", session)\nyield := result.(*chit.Yield)\n// yield.Prompt == \"What's your name?\"\n\n// Continuation responds\nresumed, _ := yield.Continuation(ctx, \"Alice\")\nresp := resumed.(*chit.Response)\n// resp.Content == \"Hello: Alice\"",{"id":274,"title":275,"titles":276,"content":43,"level":19},"/v0.0.1/guides/testing#testing-patterns","Testing Patterns",[236],{"id":278,"title":279,"titles":280,"content":281,"level":112},"/v0.0.1/guides/testing#testing-a-custom-processor","Testing a Custom Processor",[236,275],"func TestMyProcessor(t *testing.T) {\n    processor := NewMyProcessor(/* dependencies */)\n    session := zyn.NewSession()\n\n    result, err := processor.Process(context.Background(), \"test input\", session)\n\n    if err != nil {\n        t.Fatalf(\"unexpected error: %v\", err)\n    }\n\n    resp, ok := result.(*chit.Response)\n    if !ok {\n        t.Fatal(\"expected Response\")\n    }\n\n    if resp.Content != \"expected output\" {\n        t.Errorf(\"unexpected content: %q\", resp.Content)\n    }\n}",{"id":283,"title":284,"titles":285,"content":286,"level":112},"/v0.0.1/guides/testing#testing-multi-turn-flows","Testing Multi-Turn Flows",[236,275],"func TestMultiTurnFlow(t *testing.T) {\n    emitter := &helpers.CollectingEmitter{}\n    processor := helpers.YieldingProcessor(\"Name?\", \"Hello\")\n\n    chat := chit.New(processor, emitter)\n    ctx := context.Background()\n\n    // First turn yields\n    _ = chat.Handle(ctx, \"start\")\n\n    if !chat.HasContinuation() {\n        t.Fatal(\"expected continuation after yield\")\n    }\n\n    // Second turn resumes\n    _ = chat.Handle(ctx, \"Alice\")\n\n    if chat.HasContinuation() {\n        t.Error(\"expected no continuation after response\")\n    }\n\n    // Verify final output includes resumed response\n    if !strings.Contains(emitter.Content(), \"Hello: Alice\") {\n        t.Errorf(\"expected resumed response in output: %q\", emitter.Content())\n    }\n}",{"id":288,"title":289,"titles":290,"content":291,"level":112},"/v0.0.1/guides/testing#testing-emitter-push","Testing Emitter Push",[236,275],"func TestResourcePush(t *testing.T) {\n    emitter := &helpers.CollectingEmitter{}\n\n    processor := chit.ProcessorFunc(func(ctx context.Context, input string, session *zyn.Session) (chit.Result, error) {\n        if e := chit.EmitterFromContext(ctx); e != nil {\n            e.Push(ctx, chit.Resource{\n                Type: \"data\",\n                URI:  \"test://resource\",\n            })\n        }\n        return &chit.Response{Content: \"done\"}, nil\n    })\n\n    chat := chit.New(processor, emitter)\n    _ = chat.Handle(context.Background(), \"test\")\n\n    if len(emitter.Resources) != 1 {\n        t.Fatalf(\"expected 1 resource, got %d\", len(emitter.Resources))\n    }\n\n    if emitter.Resources[0].URI != \"test://resource\" {\n        t.Errorf(\"unexpected URI: %q\", emitter.Resources[0].URI)\n    }\n}",{"id":293,"title":294,"titles":295,"content":296,"level":112},"/v0.0.1/guides/testing#testing-error-handling","Testing Error Handling",[236,275],"func TestProcessorError(t *testing.T) {\n    expectedErr := errors.New(\"processor failed\")\n    processor := &helpers.MockProcessor{\n        ProcessFunc: func(ctx context.Context, input string, session *zyn.Session) (chit.Result, error) {\n            return nil, expectedErr\n        },\n    }\n\n    chat := chit.New(processor, &helpers.CollectingEmitter{})\n    err := chat.Handle(context.Background(), \"test\")\n\n    if !errors.Is(err, expectedErr) {\n        t.Errorf(\"expected processor error, got %v\", err)\n    }\n}",{"id":298,"title":299,"titles":300,"content":301,"level":112},"/v0.0.1/guides/testing#testing-with-signals","Testing with Signals",[236,275],"func TestSignalEmission(t *testing.T) {\n    var receivedChatID string\n\n    // Subscribe before creating chat\n    cancel := capitan.Subscribe(chit.ChatCreated, func(ctx context.Context, fields capitan.Fields) {\n        receivedChatID = chit.FieldChatID.Get(fields)\n    })\n    defer cancel()\n\n    chat := chit.New(helpers.EchoProcessor(), &helpers.CollectingEmitter{})\n\n    if receivedChatID != chat.ID() {\n        t.Errorf(\"expected chat ID %q, got %q\", chat.ID(), receivedChatID)\n    }\n}",{"id":303,"title":32,"titles":304,"content":305,"level":19},"/v0.0.1/guides/testing#next-steps",[236],"Troubleshooting Guide — common issues and solutionsAPI Reference — function documentationTypes Reference — type documentation html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}",{"id":307,"title":308,"titles":309,"content":310,"level":9},"/v0.0.1/guides/troubleshooting","Troubleshooting Guide",[],"Common issues and solutions when using chit",{"id":312,"title":308,"titles":313,"content":314,"level":9},"/v0.0.1/guides/troubleshooting#troubleshooting-guide",[],"This guide covers common issues and their solutions when working with chit.",{"id":316,"title":317,"titles":318,"content":43,"level":19},"/v0.0.1/guides/troubleshooting#errors","Errors",[308],{"id":320,"title":321,"titles":322,"content":323,"level":112},"/v0.0.1/guides/troubleshooting#errunknownresulttype","ErrUnknownResultType",[308,317],"chit: unknown result type Cause: Your processor returned a Result that isn't *Response or *Yield. Solution: Ensure your processor returns one of the valid result types: // Correct\nreturn &chit.Response{Content: \"hello\"}, nil\nreturn &chit.Yield{Prompt: \"question?\", Continuation: ...}, nil\n\n// Wrong - returning the interface, not a concrete type\nvar result chit.Result\nreturn result, nil  // result is nil, causes ErrUnknownResultType",{"id":325,"title":326,"titles":327,"content":328,"level":112},"/v0.0.1/guides/troubleshooting#errnilprocessor","ErrNilProcessor",[308,317],"chit: processor is required Cause: Passed nil as the processor to chit.New(). Solution: Always provide a valid processor: // Wrong\nchat := chit.New(nil, emitter)\n\n// Correct\nchat := chit.New(myProcessor, emitter)",{"id":330,"title":331,"titles":332,"content":333,"level":112},"/v0.0.1/guides/troubleshooting#errnilemitter","ErrNilEmitter",[308,317],"chit: emitter is required Cause: Passed nil as the emitter to chit.New(). Solution: Always provide a valid emitter: // Wrong\nchat := chit.New(processor, nil)\n\n// Correct\nchat := chit.New(processor, &MyEmitter{})",{"id":335,"title":336,"titles":337,"content":338,"level":112},"/v0.0.1/guides/troubleshooting#erremitterclosed","ErrEmitterClosed",[308,317],"chit: emitter is closed Cause: Attempted to emit after calling Close() on the emitter. Solution: Don't call Handle() after closing the emitter. If you need to reuse the chat, create a new emitter.",{"id":340,"title":341,"titles":342,"content":43,"level":19},"/v0.0.1/guides/troubleshooting#common-issues","Common Issues",[308],{"id":344,"title":345,"titles":346,"content":347,"level":112},"/v0.0.1/guides/troubleshooting#continuation-not-being-called","Continuation Not Being Called",[308,341],"Symptom: After yielding, the next input goes to the processor instead of the continuation. Possible causes: New Chat instance: Each Chat has its own continuation state. If you're creating a new Chat for each request, the continuation is lost. // Wrong - new chat each time\nfunc handleRequest(input string) {\n    chat := chit.New(processor, emitter)  // Fresh chat, no continuation\n    chat.Handle(ctx, input)\n}\n\n// Correct - reuse the chat\nvar chat = chit.New(processor, emitter)\nfunc handleRequest(input string) {\n    chat.Handle(ctx, input)  // Same chat, continuation preserved\n} Processor not returning Yield: Verify your processor actually returns a *Yield with a non-nil Continuation. // Check with HasContinuation()\nif chat.HasContinuation() {\n    // Next Handle() will use continuation\n}",{"id":349,"title":350,"titles":351,"content":352,"level":112},"/v0.0.1/guides/troubleshooting#emitterfromcontext-returns-nil","EmitterFromContext Returns Nil",[308,341],"Symptom: chit.EmitterFromContext(ctx) returns nil inside your processor. Possible causes: Context not from Handle: Chat injects the emitter into context before calling your processor. If you're calling the processor directly (e.g., in tests), the emitter won't be there. // In tests, inject manually if needed\nctx := chit.WithEmitter(context.Background(), &helpers.CollectingEmitter{})\nresult, err := processor.Process(ctx, input, session) Context was replaced: If your processor creates a new context instead of deriving from the one passed in, the emitter is lost. // Wrong\nctx := context.Background()  // New context, no emitter\n\n// Correct\nctx, cancel := context.WithTimeout(ctx, 5*time.Second)  // Derived, keeps emitter\ndefer cancel()",{"id":354,"title":355,"titles":356,"content":357,"level":112},"/v0.0.1/guides/troubleshooting#session-not-updating","Session Not Updating",[308,341],"Symptom: Messages aren't appearing in the session after Handle(). Possible causes: Checking before Handle returns: Session is updated synchronously within Handle. If you're checking concurrently, you might read stale state.Using wrong session: If you provided a session via WithSession(), verify you're checking the same instance. session := zyn.NewSession()\nchat := chit.New(processor, emitter, chit.WithSession(session))\n\nchat.Handle(ctx, \"hello\")\n\n// Check the same session instance\nmessages := session.Messages()",{"id":359,"title":360,"titles":361,"content":362,"level":112},"/v0.0.1/guides/troubleshooting#signals-not-firing","Signals Not Firing",[308,341],"Symptom: Subscribed to signals but handler isn't called. Possible causes: Subscribed after creation: Signal subscriptions must be active before the signal is emitted. // Wrong - subscribe after New()\nchat := chit.New(processor, emitter)\ncapitan.Subscribe(chit.ChatCreated, handler)  // Too late, already emitted\n\n// Correct - subscribe before New()\ncapitan.Subscribe(chit.ChatCreated, handler)\nchat := chit.New(processor, emitter)  // Handler called Subscription cancelled: Check if the cancel function was called. cancel := capitan.Subscribe(chit.InputReceived, handler)\n// Don't call cancel() until you're done listening",{"id":364,"title":365,"titles":366,"content":367,"level":112},"/v0.0.1/guides/troubleshooting#race-conditions","Race Conditions",[308,341],"Symptom: Sporadic test failures or inconsistent behavior under load. Possible causes: Concurrent Handle calls: While Chat is thread-safe, concurrent calls serialize. If your processor has its own race conditions, they'll surface here.Shared mutable state in processor: Processors should be stateless or use proper synchronization. // Wrong - shared state without synchronization\ntype MyProcessor struct {\n    count int  // Race condition!\n}\n\n// Correct - use atomic or mutex\ntype MyProcessor struct {\n    count atomic.Int64\n}",{"id":369,"title":370,"titles":371,"content":43,"level":19},"/v0.0.1/guides/troubleshooting#debugging-tips","Debugging Tips",[308],{"id":373,"title":374,"titles":375,"content":376,"level":112},"/v0.0.1/guides/troubleshooting#enable-signal-logging","Enable Signal Logging",[308,370],"Subscribe to all signals to trace the lifecycle: signals := []capitan.Signal{\n    chit.ChatCreated,\n    chit.InputReceived,\n    chit.ProcessingStarted,\n    chit.ProcessingCompleted,\n    chit.ProcessingFailed,\n    chit.ResponseEmitted,\n    chit.TurnYielded,\n    chit.TurnResumed,\n}\n\nfor _, sig := range signals {\n    s := sig  // Capture for closure\n    capitan.Subscribe(s, func(ctx context.Context, fields capitan.Fields) {\n        log.Printf(\"[%s] %v\", s.Name(), fields)\n    })\n}",{"id":378,"title":379,"titles":380,"content":381,"level":112},"/v0.0.1/guides/troubleshooting#inspect-chat-state","Inspect Chat State",[308,370],"fmt.Printf(\"Chat ID: %s\\n\", chat.ID())\nfmt.Printf(\"Has continuation: %v\\n\", chat.HasContinuation())\nfmt.Printf(\"Session messages: %d\\n\", len(chat.Session().Messages()))\nfmt.Printf(\"System prompt: %q\\n\", chat.Config().SystemPrompt)",{"id":383,"title":384,"titles":385,"content":386,"level":112},"/v0.0.1/guides/troubleshooting#use-collectingemitter-for-debugging","Use CollectingEmitter for Debugging",[308,370],"emitter := &helpers.CollectingEmitter{}\nchat := chit.New(processor, emitter)\n\nchat.Handle(ctx, input)\n\nfmt.Printf(\"Emitted messages: %d\\n\", len(emitter.Messages))\nfor i, msg := range emitter.Messages {\n    fmt.Printf(\"  [%d] %s: %s\\n\", i, msg.Role, msg.Content)\n}\n\nfmt.Printf(\"Pushed resources: %d\\n\", len(emitter.Resources))\nfor i, res := range emitter.Resources {\n    fmt.Printf(\"  [%d] %s: %s\\n\", i, res.Type, res.URI)\n}",{"id":388,"title":32,"titles":389,"content":390,"level":19},"/v0.0.1/guides/troubleshooting#next-steps",[308],"Testing Guide — test helpers and patternsArchitecture — understand internalsAPI Reference — function documentation html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}",{"id":392,"title":393,"titles":394,"content":395,"level":9},"/v0.0.1/guides/reliability","Reliability Guide",[],"Adding retry, timeout, rate limiting, and middleware to chit",{"id":397,"title":393,"titles":398,"content":399,"level":9},"/v0.0.1/guides/reliability#reliability-guide",[],"Chit uses pipz for composable reliability patterns. This guide covers how to add retry logic, timeouts, rate limiting, circuit breakers, and custom middleware to your chat applications.",{"id":401,"title":168,"titles":402,"content":403,"level":19},"/v0.0.1/guides/reliability#pipeline-architecture",[393],"When you create a Chat, chit builds a processing pipeline: Input → [Middleware] → [Reliability Wrappers] → Processor → Result The processor (your brain) is the terminal. Everything else wraps it, executing in order.",{"id":405,"title":76,"titles":406,"content":43,"level":19},"/v0.0.1/guides/reliability#reliability-options",[393],{"id":408,"title":409,"titles":410,"content":411,"level":112},"/v0.0.1/guides/reliability#retry","Retry",[393,76],"Retry failed processing up to N times: chat := chit.New(processor, emitter,\n    chit.WithRetry(3), // Retry up to 3 times\n)",{"id":413,"title":414,"titles":415,"content":416,"level":112},"/v0.0.1/guides/reliability#backoff","Backoff",[393,76],"Retry with exponential backoff between attempts: chat := chit.New(processor, emitter,\n    chit.WithBackoff(3, 100*time.Millisecond), // 3 attempts, 100ms base delay\n)\n// Delays: 100ms, 200ms, 400ms",{"id":418,"title":419,"titles":420,"content":421,"level":112},"/v0.0.1/guides/reliability#timeout","Timeout",[393,76],"Cancel processing if it takes too long: chat := chit.New(processor, emitter,\n    chit.WithTimeout(30 * time.Second),\n)",{"id":423,"title":424,"titles":425,"content":426,"level":112},"/v0.0.1/guides/reliability#circuit-breaker","Circuit Breaker",[393,76],"Stop calling a failing processor to prevent cascade failures: chat := chit.New(processor, emitter,\n    chit.WithCircuitBreaker(5, 30*time.Second), // Open after 5 failures, recover after 30s\n) States: Closed: Normal operationOpen: All calls fail immediately (after N consecutive failures)Half-Open: After recovery timeout, allows one test call",{"id":428,"title":429,"titles":430,"content":431,"level":112},"/v0.0.1/guides/reliability#rate-limiting","Rate Limiting",[393,76],"Limit requests per second: chat := chit.New(processor, emitter,\n    chit.WithRateLimit(10, 5), // 10 requests/second, burst of 5\n)",{"id":433,"title":434,"titles":435,"content":436,"level":19},"/v0.0.1/guides/reliability#combining-options","Combining Options",[393],"Options wrap inside-out. Order matters: chat := chit.New(processor, emitter,\n    chit.WithMiddleware(chit.UseValidation(4096)), // Innermost (runs first)\n    chit.WithRetry(3),\n    chit.WithTimeout(30 * time.Second),\n    chit.WithRateLimit(10, 5), // Outermost (checked first)\n) Execution order: Rate limit checkTimeout wrapper startsRetry loop beginsMiddleware runs (validation)Processor executes",{"id":438,"title":439,"titles":440,"content":441,"level":19},"/v0.0.1/guides/reliability#continuation-reliability","Continuation Reliability",[393],"Reliability options apply to both fresh processor calls and continuation resumptions. When a processor yields a continuation: return &chit.Yield{\n    Prompt: \"What's your name?\",\n    Continuation: func(ctx context.Context, name string) (chit.Result, error) {\n        // This continuation has the same reliability as the initial call\n        return &chit.Response{Content: \"Hello, \" + name}, nil\n    },\n}, nil The next Handle() call resumes the continuation through the same pipeline: Input → [Middleware] → [Reliability Wrappers] → Continuation → Result This means: Retry: If the continuation fails transiently, it retriesTimeout: Slow continuations are cancelledRate limiting: Continuation calls count against rate limitsMiddleware: Validation, logging, etc. apply to continuation input chat := chit.New(processor, emitter,\n    chit.WithMiddleware(chit.UseValidation(100)),\n    chit.WithRetry(3),\n    chit.WithTimeout(5 * time.Second),\n)\n\n// First call - processor runs through pipeline\nchat.Handle(ctx, \"start\") // yields continuation\n\n// Second call - continuation runs through SAME pipeline\nchat.Handle(ctx, \"Alice\") // retry, timeout, validation all apply The continuation is wrapped in a ContinuationTerminal and the same PipelineOption functions are applied, ensuring consistent behavior.",{"id":443,"title":444,"titles":445,"content":446,"level":19},"/v0.0.1/guides/reliability#custom-middleware","Custom Middleware",[393],"Middleware runs before the processor. Use it for validation, logging, enrichment, or transformation.",{"id":448,"title":449,"titles":450,"content":451,"level":112},"/v0.0.1/guides/reliability#built-in-middleware","Built-in Middleware",[393,444],"// Validate input length\nchit.UseValidation(maxLength int)\n\n// Log requests\nchit.UseLogging(logger *slog.Logger)\n\n// Record metrics\nchit.UseMetrics(recorder MetricsRecorder)\n\n// Enrich requests (best-effort)\nchit.UseEnrich(enricher Enricher)",{"id":453,"title":454,"titles":455,"content":456,"level":112},"/v0.0.1/guides/reliability#custom-middleware-with-pipz","Custom Middleware with pipz",[393,444],"Use pipz primitives directly for full control: import \"github.com/zoobz-io/pipz\"\n\n// Apply: transform or fail\ncustomValidator := pipz.Apply(\n    pipz.NewIdentity(\"myapp:validate\", \"Custom validation\"),\n    func(ctx context.Context, req *chit.ChatRequest) (*chit.ChatRequest, error) {\n        if containsBadWords(req.Input) {\n            return nil, errors.New(\"inappropriate content\")\n        }\n        return req, nil\n    },\n)\n\n// Effect: side effect, no modification\nauditLogger := pipz.Effect(\n    pipz.NewIdentity(\"myapp:audit\", \"Audit logging\"),\n    func(ctx context.Context, req *chit.ChatRequest) error {\n        log.Printf(\"User %s: %s\", req.ChatID, req.Input)\n        return nil\n    },\n)\n\n// Transform: modify without failing\nnormalizer := pipz.Transform(\n    pipz.NewIdentity(\"myapp:normalize\", \"Input normalization\"),\n    func(ctx context.Context, req *chit.ChatRequest) *chit.ChatRequest {\n        req.Input = strings.TrimSpace(req.Input)\n        return req\n    },\n)\n\nchat := chit.New(processor, emitter,\n    chit.WithMiddleware(normalizer, customValidator, auditLogger),\n)",{"id":458,"title":459,"titles":460,"content":461,"level":112},"/v0.0.1/guides/reliability#middleware-helpers","Middleware Helpers",[393,444],"Chit provides convenience wrappers: // UseApply - transform or fail\nchit.UseApply(id, func(ctx, req) (*ChatRequest, error))\n\n// UseEffect - side effect only\nchit.UseEffect(id, func(ctx, req) error)\n\n// UseTransform - transform without failing\nchit.UseTransform(id, func(ctx, req) *ChatRequest)\n\n// UseMutate - conditional transform\nchit.UseMutate(id, transformFn, conditionFn)",{"id":463,"title":464,"titles":465,"content":466,"level":19},"/v0.0.1/guides/reliability#error-handling","Error Handling",[393],"Add an error handler pipeline for observability: errorHandler := pipz.Effect(\n    pipz.NewIdentity(\"myapp:error-handler\", \"Error handler\"),\n    func(ctx context.Context, err *pipz.Error[*chit.ChatRequest]) error {\n        log.Printf(\"Processing failed: %v (path: %v)\", err.Err, err.Path)\n        metrics.RecordError(err.Err)\n        return nil\n    },\n)\n\nchat := chit.New(processor, emitter,\n    chit.WithErrorHandler(errorHandler),\n) The error handler receives rich context: err.Err — the underlying errorerr.Path — the pipeline path where it failederr.Duration — how long before failureerr.InputData — the request that failed",{"id":468,"title":469,"titles":470,"content":471,"level":19},"/v0.0.1/guides/reliability#fallback","Fallback",[393],"Use a backup processor when the primary fails: primaryChat := chit.New(primaryProcessor, emitter)\nfallbackChat := chit.New(fallbackProcessor, emitter)\n\nchat := chit.New(primaryProcessor, emitter,\n    chit.WithFallback(fallbackChat),\n) Both must implement ChatProvider. The fallback's pipeline is used when the primary fails.",{"id":473,"title":474,"titles":475,"content":476,"level":19},"/v0.0.1/guides/reliability#chatrequest","ChatRequest",[393],"The ChatRequest struct flows through the pipeline: type ChatRequest struct {\n    Input     string       // User input\n    Session   *zyn.Session // Conversation history\n    ChatID    string       // Chat instance ID\n    RequestID string       // Unique request ID\n    Result    Result       // Output (populated by terminal)\n} Middleware can read/modify any field except Result (set by the terminal).",{"id":478,"title":479,"titles":480,"content":481,"level":19},"/v0.0.1/guides/reliability#observability","Observability",[393],"pipz emits signals for reliability events: SignalWhenpipz.SignalRetryAttemptStartBefore each retry attemptpipz.SignalRetryAttemptFailAfter a failed retry attemptpipz.SignalRetryExhaustedAll retries exhaustedpipz.SignalTimeoutTriggeredTimeout exceededpipz.SignalCircuitBreakerOpenedCircuit openedpipz.SignalCircuitBreakerClosedCircuit closedpipz.SignalRateLimitThrottledRate limit hit Subscribe via capitan for logging, metrics, or alerting.",{"id":483,"title":32,"titles":484,"content":485,"level":19},"/v0.0.1/guides/reliability#next-steps",[393],"Testing Guide — mock processors and emittersTroubleshooting — common issuesAPI Reference — function documentation html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}",{"id":487,"title":488,"titles":489,"content":490,"level":9},"/v0.0.1/reference/api","API Reference",[],"Complete function documentation for chit",{"id":492,"title":488,"titles":493,"content":43,"level":9},"/v0.0.1/reference/api#api-reference",[],{"id":495,"title":496,"titles":497,"content":43,"level":19},"/v0.0.1/reference/api#constructor","Constructor",[488],{"id":499,"title":500,"titles":501,"content":502,"level":112},"/v0.0.1/reference/api#new","New",[488,496],"func New(processor Processor, emitter Emitter, opts ...Option) *Chat Creates a new Chat with the given processor and emitter. Both processor and emitter are required. Passing nil for either is undefined behavior (future versions may return an error). Options are applied in order. If no session is provided via WithSession, a new zyn.Session is created. If a system prompt is configured, it's appended to the session as a system message. Emits ChatCreated signal on creation. chat := chit.New(processor, emitter,\n    chit.WithSystemPrompt(\"You are helpful.\"),\n    chit.WithMetadata(map[string]any{\"user\": \"123\"}),\n)",{"id":504,"title":505,"titles":506,"content":43,"level":19},"/v0.0.1/reference/api#chat-methods","Chat Methods",[488],{"id":508,"title":509,"titles":510,"content":511,"level":112},"/v0.0.1/reference/api#handle","Handle",[488,505],"func (c *Chat) Handle(ctx context.Context, input string) error Processes user input through the conversation lifecycle. If a continuation exists from a previous Yield, it's called with the input. Otherwise, the processor is called. The result determines what happens next: *Response: Content is emitted and added to session*Yield: Prompt is emitted and continuation is stored Returns any error from the processor, continuation, or emitter. Signals emitted: InputReceived — at startProcessingStarted — before calling processor/continuationTurnResumed — if resuming from continuationProcessingCompleted — on successProcessingFailed — on errorResponseEmitted — when emitting responseTurnYielded — when storing continuation err := chat.Handle(ctx, \"Hello!\")\nif err != nil {\n    log.Printf(\"handle failed: %v\", err)\n}",{"id":513,"title":514,"titles":515,"content":516,"level":112},"/v0.0.1/reference/api#id","ID",[488,505],"func (c *Chat) ID() string Returns the unique identifier for this Chat. Generated via uuid.New() at creation. chatID := chat.ID()\nlog.Printf(\"Chat %s started\", chatID)",{"id":518,"title":140,"titles":519,"content":520,"level":112},"/v0.0.1/reference/api#session",[488,505],"func (c *Chat) Session() *zyn.Session Returns the underlying zyn.Session for this Chat. The session contains conversation history. Modifying it directly is allowed but may lead to inconsistent state if done during Handle. messages := chat.Session().Messages()\nfor _, msg := range messages {\n    fmt.Printf(\"%s: %s\\n\", msg.Role, msg.Content)\n}",{"id":522,"title":523,"titles":524,"content":525,"level":112},"/v0.0.1/reference/api#config","Config",[488,505],"func (c *Chat) Config() *Config Returns the configuration for this Chat. Never returns nil — a default empty Config is created if none provided. if chat.Config().SystemPrompt != \"\" {\n    log.Printf(\"Using system prompt: %s\", chat.Config().SystemPrompt)\n}",{"id":527,"title":528,"titles":529,"content":530,"level":112},"/v0.0.1/reference/api#hascontinuation","HasContinuation",[488,505],"func (c *Chat) HasContinuation() bool Returns true if the Chat is awaiting input to resume a previously yielded processing step. Thread-safe. if chat.HasContinuation() {\n    fmt.Println(\"Waiting for user response...\")\n}",{"id":532,"title":533,"titles":534,"content":535,"level":112},"/v0.0.1/reference/api#getpipeline","GetPipeline",[488,505],"func (c *Chat) GetPipeline() pipz.Chainable[*ChatRequest] Returns the internal pipeline for composition. Implements ChatProvider for use with WithFallback. fallbackChat := chit.New(fallbackProcessor, emitter)\nmainChat := chit.New(processor, emitter,\n    chit.WithFallback(fallbackChat),\n)",{"id":537,"title":71,"titles":538,"content":43,"level":19},"/v0.0.1/reference/api#configuration-options",[488],{"id":540,"title":541,"titles":542,"content":543,"level":112},"/v0.0.1/reference/api#withconfig","WithConfig",[488,71],"func WithConfig(config *Config) Option Sets the complete configuration for the Chat. Replaces any existing config. Use WithSystemPrompt or WithMetadata for partial updates. chat := chit.New(processor, emitter, chit.WithConfig(&chit.Config{\n    SystemPrompt: \"Be concise.\",\n    Metadata:     map[string]any{\"version\": \"2.0\"},\n}))",{"id":545,"title":546,"titles":547,"content":548,"level":112},"/v0.0.1/reference/api#withsystemprompt","WithSystemPrompt",[488,71],"func WithSystemPrompt(prompt string) Option Sets the system prompt for the Chat. Creates a Config if none exists. The prompt is appended to the session as a system message during Chat creation. chat := chit.New(processor, emitter,\n    chit.WithSystemPrompt(\"You are a helpful assistant.\"),\n)",{"id":550,"title":551,"titles":552,"content":553,"level":112},"/v0.0.1/reference/api#withsession","WithSession",[488,71],"func WithSession(session *zyn.Session) Option Sets a pre-existing session for the Chat. Use this for session persistence or to provide pre-populated conversation history. If not provided, a new session is created. Note: If both WithSession and WithSystemPrompt are used, the system prompt is appended to the provided session. session := zyn.NewSession()\nsession.Append(\"user\", \"Previous message\")\n\nchat := chit.New(processor, emitter, chit.WithSession(session))",{"id":555,"title":556,"titles":557,"content":558,"level":112},"/v0.0.1/reference/api#withmetadata","WithMetadata",[488,71],"func WithMetadata(metadata map[string]any) Option Sets metadata on the Chat's configuration. Creates a Config if none exists. Useful for passing application-specific data like user IDs or request context. chat := chit.New(processor, emitter, chit.WithMetadata(map[string]any{\n    \"user_id\":    \"123\",\n    \"request_id\": \"abc-def\",\n}))",{"id":560,"title":561,"titles":562,"content":43,"level":19},"/v0.0.1/reference/api#pipeline-options","Pipeline Options",[488],{"id":564,"title":565,"titles":566,"content":567,"level":112},"/v0.0.1/reference/api#withretry","WithRetry",[488,561],"func WithRetry(maxAttempts int) Option Adds retry logic to the pipeline. Failed requests are retried up to maxAttempts times. chat := chit.New(processor, emitter, chit.WithRetry(3))",{"id":569,"title":570,"titles":571,"content":572,"level":112},"/v0.0.1/reference/api#withbackoff","WithBackoff",[488,561],"func WithBackoff(maxAttempts int, baseDelay time.Duration) Option Adds retry logic with exponential backoff. Delays double after each failure. chat := chit.New(processor, emitter, chit.WithBackoff(3, 100*time.Millisecond))",{"id":574,"title":575,"titles":576,"content":577,"level":112},"/v0.0.1/reference/api#withtimeout","WithTimeout",[488,561],"func WithTimeout(duration time.Duration) Option Adds timeout protection. Operations exceeding the duration are canceled. chat := chit.New(processor, emitter, chit.WithTimeout(30*time.Second))",{"id":579,"title":580,"titles":581,"content":582,"level":112},"/v0.0.1/reference/api#withcircuitbreaker","WithCircuitBreaker",[488,561],"func WithCircuitBreaker(failures int, recovery time.Duration) Option Adds circuit breaker protection. After failures consecutive failures, the circuit opens for recovery duration. chat := chit.New(processor, emitter, chit.WithCircuitBreaker(5, 30*time.Second))",{"id":584,"title":585,"titles":586,"content":587,"level":112},"/v0.0.1/reference/api#withratelimit","WithRateLimit",[488,561],"func WithRateLimit(rps float64, burst int) Option Adds rate limiting. rps is requests per second, burst is burst capacity. chat := chit.New(processor, emitter, chit.WithRateLimit(10, 5))",{"id":589,"title":590,"titles":591,"content":592,"level":112},"/v0.0.1/reference/api#witherrorhandler","WithErrorHandler",[488,561],"func WithErrorHandler(handler pipz.Chainable[*pipz.Error[*ChatRequest]]) Option Adds error handling pipeline for observability. errorHandler := pipz.Effect(id, func(ctx context.Context, err *pipz.Error[*chit.ChatRequest]) error {\n    log.Printf(\"Error: %v\", err.Err)\n    return nil\n})\nchat := chit.New(processor, emitter, chit.WithErrorHandler(errorHandler))",{"id":594,"title":595,"titles":596,"content":597,"level":112},"/v0.0.1/reference/api#withfallback","WithFallback",[488,561],"func WithFallback(fallback ChatProvider) Option Adds a fallback processor for resilience. If the primary fails, the fallback is tried. fallback := chit.New(fallbackProcessor, emitter)\nchat := chit.New(processor, emitter, chit.WithFallback(fallback))",{"id":599,"title":600,"titles":601,"content":602,"level":112},"/v0.0.1/reference/api#withmiddleware","WithMiddleware",[488,561],"func WithMiddleware(processors ...pipz.Chainable[*ChatRequest]) Option Adds pre-processing steps before the terminal. Processors run in order. chat := chit.New(processor, emitter,\n    chit.WithMiddleware(\n        chit.UseValidation(4096),\n        chit.UseLogging(logger),\n    ),\n)",{"id":604,"title":459,"titles":605,"content":43,"level":19},"/v0.0.1/reference/api#middleware-helpers",[488],{"id":607,"title":608,"titles":609,"content":610,"level":112},"/v0.0.1/reference/api#usevalidation","UseValidation",[488,459],"func UseValidation(maxLength int) pipz.Chainable[*ChatRequest] Creates middleware that validates input length. Returns error if input exceeds maxLength or is empty.",{"id":612,"title":613,"titles":614,"content":615,"level":112},"/v0.0.1/reference/api#uselogging","UseLogging",[488,459],"func UseLogging(logger *slog.Logger) pipz.Chainable[*ChatRequest] Creates middleware that logs requests using slog.",{"id":617,"title":618,"titles":619,"content":620,"level":112},"/v0.0.1/reference/api#usemetrics","UseMetrics",[488,459],"func UseMetrics(recorder MetricsRecorder) pipz.Chainable[*ChatRequest] Creates middleware that records metrics via the MetricsRecorder interface.",{"id":622,"title":623,"titles":624,"content":625,"level":112},"/v0.0.1/reference/api#useenrich","UseEnrich",[488,459],"func UseEnrich(enricher Enricher) pipz.Chainable[*ChatRequest] Creates middleware that enriches requests. Best-effort: returns original on error.",{"id":627,"title":628,"titles":629,"content":630,"level":112},"/v0.0.1/reference/api#useapply","UseApply",[488,459],"func UseApply(id pipz.Identity, fn func(context.Context, *ChatRequest) (*ChatRequest, error)) pipz.Chainable[*ChatRequest] Creates custom middleware using pipz.Apply. Can transform or fail.",{"id":632,"title":633,"titles":634,"content":635,"level":112},"/v0.0.1/reference/api#useeffect","UseEffect",[488,459],"func UseEffect(id pipz.Identity, fn func(context.Context, *ChatRequest) error) pipz.Chainable[*ChatRequest] Creates custom middleware using pipz.Effect. Side effect only.",{"id":637,"title":638,"titles":639,"content":640,"level":112},"/v0.0.1/reference/api#usetransform","UseTransform",[488,459],"func UseTransform(id pipz.Identity, fn func(context.Context, *ChatRequest) *ChatRequest) pipz.Chainable[*ChatRequest] Creates custom middleware using pipz.Transform. Cannot fail.",{"id":642,"title":643,"titles":644,"content":645,"level":112},"/v0.0.1/reference/api#usemutate","UseMutate",[488,459],"func UseMutate(id pipz.Identity, fn func(context.Context, *ChatRequest) *ChatRequest, condition func(context.Context, *ChatRequest) bool) pipz.Chainable[*ChatRequest] Creates conditional middleware. Transformation only applies if condition returns true.",{"id":647,"title":648,"titles":649,"content":43,"level":19},"/v0.0.1/reference/api#context-helpers","Context Helpers",[488],{"id":651,"title":652,"titles":653,"content":654,"level":112},"/v0.0.1/reference/api#withemitter","WithEmitter",[488,648],"func WithEmitter(ctx context.Context, e Emitter) context.Context Returns a new context with the given Emitter attached. Called automatically by Chat before invoking the processor. Use manually when testing processors in isolation. ctx := chit.WithEmitter(context.Background(), myEmitter)\nresult, err := processor.Process(ctx, input, session)",{"id":656,"title":657,"titles":658,"content":659,"level":112},"/v0.0.1/reference/api#emitterfromcontext","EmitterFromContext",[488,648],"func EmitterFromContext(ctx context.Context) Emitter Retrieves the Emitter from the context. Returns nil if no Emitter is present. Processors use this to push resources during processing. if emitter := chit.EmitterFromContext(ctx); emitter != nil {\n    emitter.Push(ctx, chit.Resource{\n        Type: \"data\",\n        URI:  \"db://users/123\",\n    })\n}",{"id":661,"title":32,"titles":662,"content":663,"level":19},"/v0.0.1/reference/api#next-steps",[488],"Types Reference — type definitions and fieldsConcepts — mental modelsTesting Guide — test helpers html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}",{"id":665,"title":666,"titles":667,"content":668,"level":9},"/v0.0.1/reference/types","Types Reference",[],"Complete type documentation for chit",{"id":670,"title":666,"titles":671,"content":43,"level":9},"/v0.0.1/reference/types#types-reference",[],{"id":673,"title":94,"titles":674,"content":675,"level":19},"/v0.0.1/reference/types#chat",[666],"The conversation lifecycle controller. type Chat struct {\n    // contains filtered or unexported fields\n} Chat manages conversation state, delegates to a processor for reasoning, and streams output through an emitter. It handles turn-taking via continuations. Methods: Handle(ctx, input) — process user inputID() — unique identifierSession() — conversation historyConfig() — configurationHasContinuation() — check if awaiting input See: API Reference",{"id":677,"title":523,"titles":678,"content":679,"level":19},"/v0.0.1/reference/types#config",[666],"Configuration for a Chat. type Config struct {\n    SystemPrompt string\n    Metadata     map[string]any\n} FieldTypeDescriptionSystemPromptstringSystem message that guides conversation behaviorMetadatamapstringanyOptional additional configuration data",{"id":681,"title":99,"titles":682,"content":683,"level":19},"/v0.0.1/reference/types#processor",[666],"Interface for reasoning and execution logic. type Processor interface {\n    Process(ctx context.Context, input string, session *zyn.Session) (Result, error)\n} Implement this interface to define how your chat handles user input. The processor receives the input and conversation history, and returns a Result. The context contains the Emitter (retrievable via EmitterFromContext) for pushing resources during processing.",{"id":685,"title":686,"titles":687,"content":688,"level":19},"/v0.0.1/reference/types#processorfunc","ProcessorFunc",[666],"Adapter to use a function as a Processor. type ProcessorFunc func(ctx context.Context, input string, session *zyn.Session) (Result, error) processor := chit.ProcessorFunc(func(ctx context.Context, input string, session *zyn.Session) (chit.Result, error) {\n    return &chit.Response{Content: \"Hello\"}, nil\n})",{"id":690,"title":104,"titles":691,"content":692,"level":19},"/v0.0.1/reference/types#result",[666],"Interface for processing outcomes. type Result interface {\n    IsComplete() bool\n    IsYielded() bool\n} Two concrete implementations exist: *Response and *Yield. MethodResponseYieldIsComplete()truefalseIsYielded()falsetrue",{"id":694,"title":109,"titles":695,"content":696,"level":19},"/v0.0.1/reference/types#response",[666],"A complete result with content to emit. type Response struct {\n    Content  string\n    Metadata map[string]any\n} FieldTypeDescriptionContentstringResponse text to emit to the userMetadatamapstringanyOptional additional information return &chit.Response{\n    Content:  \"Here's your answer.\",\n    Metadata: map[string]any{\"tokens\": 150},\n}, nil",{"id":698,"title":115,"titles":699,"content":700,"level":19},"/v0.0.1/reference/types#yield",[666],"Indicates processing paused awaiting further input. type Yield struct {\n    Prompt       string\n    Continuation Continuation\n    Metadata     map[string]any\n} FieldTypeDescriptionPromptstringMessage to emit while awaiting inputContinuationContinuationFunction to call with next inputMetadatamapstringanyOptional additional information return &chit.Yield{\n    Prompt: \"What's your email?\",\n    Continuation: func(ctx context.Context, email string) (chit.Result, error) {\n        return &chit.Response{Content: \"Thanks, \" + email}, nil\n    },\n}, nil",{"id":702,"title":120,"titles":703,"content":704,"level":19},"/v0.0.1/reference/types#continuation",[666],"Function that resumes processing with new input. type Continuation func(ctx context.Context, input string) (Result, error) Continuations can return either *Response (to complete) or *Yield (to chain another continuation).",{"id":706,"title":125,"titles":707,"content":708,"level":19},"/v0.0.1/reference/types#emitter",[666],"Interface for streaming output to the client. type Emitter interface {\n    Emit(ctx context.Context, msg Message) error\n    Push(ctx context.Context, resource Resource) error\n    Close() error\n} MethodPurposeEmitStream conversational textPushSend structured data synchronouslyCloseSignal end of output, release resources",{"id":710,"title":711,"titles":712,"content":713,"level":19},"/v0.0.1/reference/types#message","Message",[666],"A streamed response unit. type Message struct {\n    Role     string\n    Content  string\n    Metadata map[string]any\n} FieldTypeDescriptionRolestringMessage sender (e.g., \"assistant\", \"system\")ContentstringText content of the messageMetadatamapstringanyOptional additional information",{"id":715,"title":716,"titles":717,"content":718,"level":19},"/v0.0.1/reference/types#resource","Resource",[666],"Structured data pushed to the client. type Resource struct {\n    Type     string\n    URI      string\n    Payload  any\n    Metadata map[string]any\n} FieldTypeDescriptionTypestringKind of resource (\"data\", \"context\", \"tool_result\")URIstringSource URI if retrieved from a data sourcePayloadanyThe structured data being deliveredMetadatamapstringanyOptional additional information emitter.Push(ctx, chit.Resource{\n    Type:    \"data\",\n    URI:     \"db://users/123\",\n    Payload: map[string]string{\"name\": \"Alice\"},\n})",{"id":720,"title":721,"titles":722,"content":723,"level":19},"/v0.0.1/reference/types#option","Option",[666],"Functional option for configuring a Chat. type Option func(*Chat) See API Reference for available options.",{"id":725,"title":474,"titles":726,"content":727,"level":19},"/v0.0.1/reference/types#chatrequest",[666],"The request object that flows through the pipz pipeline. type ChatRequest struct {\n    Input     string\n    Session   *zyn.Session\n    ChatID    string\n    RequestID string\n    Result    Result\n} FieldTypeDescriptionInputstringUser input to processSession*zyn.SessionConversation historyChatIDstringID of the chat instanceRequestIDstringUnique identifier for this requestResultResultOutput (populated by terminal) Implements pipz.Cloner for concurrent middleware support.",{"id":729,"title":730,"titles":731,"content":732,"level":19},"/v0.0.1/reference/types#chatprovider","ChatProvider",[666],"Interface for types that can provide a pipeline for composition. type ChatProvider interface {\n    GetPipeline() pipz.Chainable[*ChatRequest]\n} Used by WithFallback to compose fallback pipelines. Chat implements this interface.",{"id":734,"title":735,"titles":736,"content":737,"level":19},"/v0.0.1/reference/types#pipelineoption","PipelineOption",[666],"Function that wraps a pipeline with additional behavior. type PipelineOption func(pipz.Chainable[*ChatRequest]) pipz.Chainable[*ChatRequest] Used internally. See API Reference for the Option-returning wrappers.",{"id":739,"title":740,"titles":741,"content":742,"level":19},"/v0.0.1/reference/types#terminal-functions","Terminal Functions",[666],"Internal functions that create the innermost pipeline layer.",{"id":744,"title":745,"titles":746,"content":747,"level":112},"/v0.0.1/reference/types#newterminal","NewTerminal",[666,740],"func NewTerminal(processor Processor) pipz.Chainable[*ChatRequest] Creates the terminal that calls the Processor. Used for fresh input processing.",{"id":749,"title":750,"titles":751,"content":752,"level":112},"/v0.0.1/reference/types#newcontinuationterminal","NewContinuationTerminal",[666,740],"func NewContinuationTerminal(cont Continuation) pipz.Chainable[*ChatRequest] Creates a terminal that calls the given continuation. Used when resuming yielded processing. Both terminals: Receive a *ChatRequest with input and sessionCall their target (processor or continuation) with the inputStore the result in req.ResultAre wrapped with the same pipeline options for reliability parity",{"id":754,"title":755,"titles":756,"content":757,"level":19},"/v0.0.1/reference/types#metricsrecorder","MetricsRecorder",[666],"Interface for recording chat metrics. type MetricsRecorder interface {\n    RecordRequest(chatID string, inputLength int)\n    RecordLatency(chatID string, duration time.Duration)\n} Used by UseMetrics middleware.",{"id":759,"title":760,"titles":761,"content":762,"level":19},"/v0.0.1/reference/types#enricher","Enricher",[666],"Interface for enriching chat requests. type Enricher interface {\n    Enrich(ctx context.Context, req *ChatRequest) error\n} Used by UseEnrich middleware.",{"id":764,"title":317,"titles":765,"content":766,"level":19},"/v0.0.1/reference/types#errors",[666],"Sentinel errors for chit operations. var (\n    ErrUnknownResultType = errors.New(\"chit: unknown result type\")\n    ErrNilProcessor      = errors.New(\"chit: processor is required\")\n    ErrNilEmitter        = errors.New(\"chit: emitter is required\")\n    ErrEmitterClosed     = errors.New(\"chit: emitter is closed\")\n) ErrorWhenErrUnknownResultTypeProcessor returned a Result that isn't *Response or *YieldErrNilProcessorAttempted to create Chat without a ProcessorErrNilEmitterAttempted to create Chat without an EmitterErrEmitterClosedAttempted to emit after Emitter was closed",{"id":768,"title":145,"titles":769,"content":770,"level":19},"/v0.0.1/reference/types#signals",[666],"Lifecycle signals emitted via capitan. var (\n    ChatCreated         = capitan.NewSignal(\"chit.chat.created\", ...)\n    InputReceived       = capitan.NewSignal(\"chit.input.received\", ...)\n    ProcessingStarted   = capitan.NewSignal(\"chit.processing.started\", ...)\n    ProcessingCompleted = capitan.NewSignal(\"chit.processing.completed\", ...)\n    ProcessingFailed    = capitan.NewSignal(\"chit.processing.failed\", ...)\n    ResponseEmitted     = capitan.NewSignal(\"chit.response.emitted\", ...)\n    ResourcePushed      = capitan.NewSignal(\"chit.resource.pushed\", ...)\n    TurnYielded         = capitan.NewSignal(\"chit.turn.yielded\", ...)\n    TurnResumed         = capitan.NewSignal(\"chit.turn.resumed\", ...)\n) SignalWhen EmittedChatCreatedNew Chat instantiatedInputReceivedHandle() called with user inputProcessingStartedBefore calling processor/continuationProcessingCompletedProcessor/continuation returned successfullyProcessingFailedProcessor/continuation returned errorResponseEmittedResponse emitted via EmitterResourcePushedAvailable for processors pushing resourcesTurnYieldedContinuation stored, awaiting inputTurnResumedContinuation being called",{"id":772,"title":773,"titles":774,"content":775,"level":19},"/v0.0.1/reference/types#signal-fields","Signal Fields",[666],"Field keys for signal event data. var (\n    FieldChatID             = capitan.NewStringKey(\"chat_id\")\n    FieldInput              = capitan.NewStringKey(\"input\")\n    FieldInputSize          = capitan.NewIntKey(\"input_size\")\n    FieldProcessingDuration = capitan.NewDurationKey(\"processing_duration\")\n    FieldRole               = capitan.NewStringKey(\"role\")\n    FieldContentSize        = capitan.NewIntKey(\"content_size\")\n    FieldResourceType       = capitan.NewStringKey(\"resource_type\")\n    FieldResourceURI        = capitan.NewStringKey(\"resource_uri\")\n    FieldPrompt             = capitan.NewStringKey(\"prompt\")\n    FieldError              = capitan.NewErrorKey(\"error\")\n) FieldTypeUsed InFieldChatIDstringAll signalsFieldInputstringInputReceivedFieldInputSizeintInputReceivedFieldProcessingDurationtime.DurationProcessingCompleted, ProcessingFailedFieldRolestringResponseEmittedFieldContentSizeintResponseEmittedFieldResourceTypestringResourcePushedFieldResourceURIstringResourcePushedFieldPromptstringTurnYieldedFieldErrorerrorProcessingFailed",{"id":777,"title":245,"titles":778,"content":779,"level":19},"/v0.0.1/reference/types#test-helpers",[666],"The github.com/zoobz-io/chit/testing package provides test utilities.",{"id":781,"title":250,"titles":782,"content":783,"level":112},"/v0.0.1/reference/types#mockprocessor",[666,245],"type MockProcessor struct {\n    ProcessFunc func(ctx context.Context, input string, session *zyn.Session) (chit.Result, error)\n    Calls       []ProcessCall\n}\n\ntype ProcessCall struct {\n    Input   string\n    Session *zyn.Session\n}",{"id":785,"title":255,"titles":786,"content":787,"level":112},"/v0.0.1/reference/types#mockemitter",[666,245],"type MockEmitter struct {\n    EmitFunc  func(ctx context.Context, msg chit.Message) error\n    PushFunc  func(ctx context.Context, resource chit.Resource) error\n    CloseFunc func() error\n    Messages  []chit.Message\n    Resources []chit.Resource\n    Closed    bool\n}",{"id":789,"title":260,"titles":790,"content":791,"level":112},"/v0.0.1/reference/types#collectingemitter",[666,245],"type CollectingEmitter struct {\n    Messages  []chit.Message\n    Resources []chit.Resource\n}\n\nfunc (e *CollectingEmitter) Content() string",{"id":793,"title":794,"titles":795,"content":796,"level":112},"/v0.0.1/reference/types#factory-functions","Factory Functions",[666,245],"func EchoProcessor() chit.Processor\nfunc YieldingProcessor(prompt, finalResponse string) chit.Processor See: Testing Guide",{"id":798,"title":32,"titles":799,"content":800,"level":19},"/v0.0.1/reference/types#next-steps",[666],"API Reference — function documentationConcepts — mental modelsArchitecture — internal design html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}",[802],{"title":803,"path":804,"stem":805,"children":806,"page":820},"V001","/v0.0.1","v0.0.1",[807,821,832],{"title":808,"path":809,"stem":810,"children":811,"page":820},"Learn","/v0.0.1/learn","v0.0.1/1.learn",[812,814,816,818],{"title":6,"path":5,"stem":813,"description":8},"v0.0.1/1.learn/1.overview",{"title":37,"path":36,"stem":815,"description":39},"v0.0.1/1.learn/2.quickstart",{"title":85,"path":84,"stem":817,"description":87},"v0.0.1/1.learn/3.concepts",{"title":154,"path":153,"stem":819,"description":156},"v0.0.1/1.learn/4.architecture",false,{"title":822,"path":823,"stem":824,"children":825,"page":820},"Guides","/v0.0.1/guides","v0.0.1/2.guides",[826,828,830],{"title":236,"path":235,"stem":827,"description":238},"v0.0.1/2.guides/1.testing",{"title":308,"path":307,"stem":829,"description":310},"v0.0.1/2.guides/2.troubleshooting",{"title":393,"path":392,"stem":831,"description":395},"v0.0.1/2.guides/3.reliability",{"title":833,"path":834,"stem":835,"children":836,"page":820},"Reference","/v0.0.1/reference","v0.0.1/4.reference",[837,839],{"title":488,"path":487,"stem":838,"description":490},"v0.0.1/4.reference/1.api",{"title":666,"path":665,"stem":840,"description":668},"v0.0.1/4.reference/2.types",[842],{"title":803,"path":804,"stem":805,"children":843,"page":820},[844,850,855],{"title":808,"path":809,"stem":810,"children":845,"page":820},[846,847,848,849],{"title":6,"path":5,"stem":813},{"title":37,"path":36,"stem":815},{"title":85,"path":84,"stem":817},{"title":154,"path":153,"stem":819},{"title":822,"path":823,"stem":824,"children":851,"page":820},[852,853,854],{"title":236,"path":235,"stem":827},{"title":308,"path":307,"stem":829},{"title":393,"path":392,"stem":831},{"title":833,"path":834,"stem":835,"children":856,"page":820},[857,858],{"title":488,"path":487,"stem":838},{"title":666,"path":665,"stem":840},[860,2738,3226],{"id":861,"title":862,"body":863,"description":43,"extension":2731,"icon":2732,"meta":2733,"navigation":1135,"path":2734,"seo":2735,"stem":2736,"__hash__":2737},"resources/readme.md","README",{"type":864,"value":865,"toc":2720},"minimark",[866,870,938,941,944,949,952,1237,1240,1244,1261,1264,1268,2083,2087,2198,2202,2245,2249,2252,2610,2618,2622,2695,2699,2707,2710,2716],[867,868,869],"h1",{"id":869},"chit",[871,872,873,884,892,900,908,916,923,930],"p",{},[874,875,879],"a",{"href":876,"rel":877},"https://github.com/zoobz-io/chit/actions/workflows/ci.yml",[878],"nofollow",[880,881],"img",{"alt":882,"src":883},"CI Status","https://github.com/zoobz-io/chit/workflows/CI/badge.svg",[874,885,888],{"href":886,"rel":887},"https://codecov.io/gh/zoobz-io/chit",[878],[880,889],{"alt":890,"src":891},"codecov","https://codecov.io/gh/zoobz-io/chit/graph/badge.svg?branch=main",[874,893,896],{"href":894,"rel":895},"https://goreportcard.com/report/github.com/zoobz-io/chit",[878],[880,897],{"alt":898,"src":899},"Go Report Card","https://goreportcard.com/badge/github.com/zoobz-io/chit",[874,901,904],{"href":902,"rel":903},"https://github.com/zoobz-io/chit/security/code-scanning",[878],[880,905],{"alt":906,"src":907},"CodeQL","https://github.com/zoobz-io/chit/workflows/CodeQL/badge.svg",[874,909,912],{"href":910,"rel":911},"https://pkg.go.dev/github.com/zoobz-io/chit",[878],[880,913],{"alt":914,"src":915},"Go Reference","https://pkg.go.dev/badge/github.com/zoobz-io/chit.svg",[874,917,919],{"href":918},"LICENSE",[880,920],{"alt":921,"src":922},"License","https://img.shields.io/github/license/zoobz-io/chit",[874,924,926],{"href":925},"go.mod",[880,927],{"alt":928,"src":929},"Go Version","https://img.shields.io/github/go-mod/go-version/zoobz-io/chit",[874,931,934],{"href":932,"rel":933},"https://github.com/zoobz-io/chit/releases",[878],[880,935],{"alt":936,"src":937},"Release","https://img.shields.io/github/v/release/zoobz-io/chit",[871,939,940],{},"Conversation lifecycle controller for LLM-powered applications.",[871,942,943],{},"Manage user conversations, orchestrate pluggable processors, and stream responses — while keeping internal reasoning separate from what users see.",[945,946,948],"h2",{"id":947},"the-conversation-belongs-to-you","The Conversation Belongs to You",[871,950,951],{},"Your processor receives input and read-only history. What it does with an LLM is its own business.",[953,954,958],"pre",{"className":955,"code":956,"language":957,"meta":43,"style":43},"language-go shiki shiki-themes","// Processor handles reasoning — how it talks to LLMs is an implementation detail\nprocessor := chit.ProcessorFunc(func(ctx context.Context, input string, history []chit.Message) (chit.Result, error) {\n    // history is the user-facing conversation (read-only)\n    // Internal LLM calls, chain-of-thought, tool use — none of it pollutes history\n    response := callYourLLM(input, history)\n    return &chit.Response{Content: response}, nil\n})\n\n// Emitter streams output to the user\nemitter := &StreamingEmitter{writer: w}\n\n// Chat manages the lifecycle\nchat := chit.New(processor, emitter)\n\n// Handle user input — chit manages history, you manage reasoning\nchat.Handle(ctx, \"What's the weather in Tokyo?\")\n","go",[959,960,961,969,1055,1060,1066,1089,1124,1130,1137,1143,1169,1174,1180,1205,1210,1216],"code",{"__ignoreMap":43},[962,963,965],"span",{"class":964,"line":9},"line",[962,966,968],{"class":967},"sLkEo","// Processor handles reasoning — how it talks to LLMs is an implementation detail\n",[962,970,971,975,978,981,985,988,991,995,997,1001,1005,1007,1010,1013,1016,1019,1021,1024,1027,1029,1031,1033,1036,1039,1041,1043,1045,1047,1050,1052],{"class":964,"line":19},[962,972,974],{"class":973},"sh8_p","processor",[962,976,977],{"class":973}," :=",[962,979,980],{"class":973}," chit",[962,982,984],{"class":983},"sq5bi",".",[962,986,686],{"class":987},"s5klm",[962,989,990],{"class":983},"(",[962,992,994],{"class":993},"sUt3r","func",[962,996,990],{"class":983},[962,998,1000],{"class":999},"sSYET","ctx",[962,1002,1004],{"class":1003},"sYBwO"," context",[962,1006,984],{"class":983},[962,1008,1009],{"class":1003},"Context",[962,1011,1012],{"class":983},",",[962,1014,1015],{"class":999}," input",[962,1017,1018],{"class":1003}," string",[962,1020,1012],{"class":983},[962,1022,1023],{"class":999}," history",[962,1025,1026],{"class":983}," []",[962,1028,869],{"class":1003},[962,1030,984],{"class":983},[962,1032,711],{"class":1003},[962,1034,1035],{"class":983},")",[962,1037,1038],{"class":983}," (",[962,1040,869],{"class":1003},[962,1042,984],{"class":983},[962,1044,104],{"class":1003},[962,1046,1012],{"class":983},[962,1048,1049],{"class":1003}," error",[962,1051,1035],{"class":983},[962,1053,1054],{"class":983}," {\n",[962,1056,1057],{"class":964,"line":112},[962,1058,1059],{"class":967},"    // history is the user-facing conversation (read-only)\n",[962,1061,1063],{"class":964,"line":1062},4,[962,1064,1065],{"class":967},"    // Internal LLM calls, chain-of-thought, tool use — none of it pollutes history\n",[962,1067,1069,1072,1074,1077,1079,1082,1084,1086],{"class":964,"line":1068},5,[962,1070,1071],{"class":973},"    response",[962,1073,977],{"class":973},[962,1075,1076],{"class":987}," callYourLLM",[962,1078,990],{"class":983},[962,1080,1081],{"class":973},"input",[962,1083,1012],{"class":983},[962,1085,1023],{"class":973},[962,1087,1088],{"class":983},")\n",[962,1090,1092,1096,1099,1101,1103,1105,1108,1112,1115,1118,1121],{"class":964,"line":1091},6,[962,1093,1095],{"class":1094},"sW3Qg","    return",[962,1097,1098],{"class":1094}," &",[962,1100,869],{"class":1003},[962,1102,984],{"class":983},[962,1104,109],{"class":1003},[962,1106,1107],{"class":983},"{",[962,1109,1111],{"class":1110},"sBGCq","Content",[962,1113,1114],{"class":983},":",[962,1116,1117],{"class":973}," response",[962,1119,1120],{"class":983},"},",[962,1122,1123],{"class":993}," nil\n",[962,1125,1127],{"class":964,"line":1126},7,[962,1128,1129],{"class":983},"})\n",[962,1131,1133],{"class":964,"line":1132},8,[962,1134,1136],{"emptyLinePlaceholder":1135},true,"\n",[962,1138,1140],{"class":964,"line":1139},9,[962,1141,1142],{"class":967},"// Emitter streams output to the user\n",[962,1144,1146,1149,1151,1153,1156,1158,1161,1163,1166],{"class":964,"line":1145},10,[962,1147,1148],{"class":973},"emitter",[962,1150,977],{"class":973},[962,1152,1098],{"class":1094},[962,1154,1155],{"class":1003},"StreamingEmitter",[962,1157,1107],{"class":983},[962,1159,1160],{"class":1110},"writer",[962,1162,1114],{"class":983},[962,1164,1165],{"class":973}," w",[962,1167,1168],{"class":983},"}\n",[962,1170,1172],{"class":964,"line":1171},11,[962,1173,1136],{"emptyLinePlaceholder":1135},[962,1175,1177],{"class":964,"line":1176},12,[962,1178,1179],{"class":967},"// Chat manages the lifecycle\n",[962,1181,1183,1186,1188,1190,1192,1194,1196,1198,1200,1203],{"class":964,"line":1182},13,[962,1184,1185],{"class":973},"chat",[962,1187,977],{"class":973},[962,1189,980],{"class":973},[962,1191,984],{"class":983},[962,1193,500],{"class":987},[962,1195,990],{"class":983},[962,1197,974],{"class":973},[962,1199,1012],{"class":983},[962,1201,1202],{"class":973}," emitter",[962,1204,1088],{"class":983},[962,1206,1208],{"class":964,"line":1207},14,[962,1209,1136],{"emptyLinePlaceholder":1135},[962,1211,1213],{"class":964,"line":1212},15,[962,1214,1215],{"class":967},"// Handle user input — chit manages history, you manage reasoning\n",[962,1217,1219,1221,1223,1225,1227,1229,1231,1235],{"class":964,"line":1218},16,[962,1220,1185],{"class":973},[962,1222,984],{"class":983},[962,1224,509],{"class":987},[962,1226,990],{"class":983},[962,1228,1000],{"class":973},[962,1230,1012],{"class":983},[962,1232,1234],{"class":1233},"sxAnc"," \"What's the weather in Tokyo?\"",[962,1236,1088],{"class":983},[871,1238,1239],{},"The user sees a clean conversation. Your processor can have elaborate internal dialogues with the LLM — retries, tool calls, multi-step reasoning — and none of it leaks through.",[945,1241,1243],{"id":1242},"install","Install",[953,1245,1249],{"className":1246,"code":1247,"language":1248,"meta":43,"style":43},"language-bash shiki shiki-themes","go get github.com/zoobz-io/chit\n","bash",[959,1250,1251],{"__ignoreMap":43},[962,1252,1253,1255,1258],{"class":964,"line":9},[962,1254,957],{"class":987},[962,1256,1257],{"class":1233}," get",[962,1259,1260],{"class":1233}," github.com/zoobz-io/chit\n",[871,1262,1263],{},"Requires Go 1.24+.",[945,1265,1267],{"id":1266},"quick-start","Quick Start",[953,1269,1271],{"className":955,"code":1270,"language":957,"meta":43,"style":43},"package main\n\nimport (\n    \"context\"\n    \"fmt\"\n\n    \"github.com/zoobz-io/chit\"\n)\n\n// SimpleEmitter collects messages for demonstration\ntype SimpleEmitter struct {\n    Messages []chit.Message\n}\n\nfunc (e *SimpleEmitter) Emit(_ context.Context, msg chit.Message) error {\n    e.Messages = append(e.Messages, msg)\n    fmt.Printf(\"[%s]: %s\\n\", msg.Role, msg.Content)\n    return nil\n}\nfunc (e *SimpleEmitter) Push(_ context.Context, _ chit.Resource) error { return nil }\nfunc (e *SimpleEmitter) Close() error                                  { return nil }\n\nfunc main() {\n    // Processor returns responses or yields for multi-turn\n    processor := chit.ProcessorFunc(func(_ context.Context, input string, history []chit.Message) (chit.Result, error) {\n        // First message — ask for clarification\n        if len(history) == 1 {\n            return &chit.Yield{\n                Prompt: \"Which city would you like weather for?\",\n                Continuation: func(_ context.Context, city string, _ []chit.Message) (chit.Result, error) {\n                    return &chit.Response{Content: fmt.Sprintf(\"Weather in %s: Sunny, 22°C\", city)}, nil\n                },\n            }, nil\n        }\n        return &chit.Response{Content: \"Hello! How can I help?\"}, nil\n    })\n\n    emitter := &SimpleEmitter{}\n    chat := chit.New(processor, emitter)\n\n    // First call yields, asking for more info\n    chat.Handle(context.Background(), \"What's the weather?\")\n    // [assistant]: Which city would you like weather for?\n\n    // Second call resumes with the answer\n    chat.Handle(context.Background(), \"Tokyo\")\n    // [assistant]: Weather in Tokyo: Sunny, 22°C\n\n    // History tracked automatically\n    fmt.Printf(\"Conversation has %d messages\\n\", len(chat.History()))\n}\n",[959,1272,1273,1281,1285,1294,1299,1304,1308,1313,1317,1321,1326,1339,1353,1357,1361,1409,1441,1492,1499,1504,1559,1591,1596,1608,1614,1678,1684,1709,1726,1740,1798,1845,1851,1859,1865,1892,1898,1903,1918,1942,1947,1953,1980,1986,1991,1997,2021,2027,2032,2038,2078],{"__ignoreMap":43},[962,1274,1275,1278],{"class":964,"line":9},[962,1276,1277],{"class":993},"package",[962,1279,1280],{"class":1003}," main\n",[962,1282,1283],{"class":964,"line":19},[962,1284,1136],{"emptyLinePlaceholder":1135},[962,1286,1287,1290],{"class":964,"line":112},[962,1288,1289],{"class":993},"import",[962,1291,1293],{"class":1292},"soy-K"," (\n",[962,1295,1296],{"class":964,"line":1062},[962,1297,1298],{"class":1233},"    \"context\"\n",[962,1300,1301],{"class":964,"line":1068},[962,1302,1303],{"class":1233},"    \"fmt\"\n",[962,1305,1306],{"class":964,"line":1091},[962,1307,1136],{"emptyLinePlaceholder":1135},[962,1309,1310],{"class":964,"line":1126},[962,1311,1312],{"class":1233},"    \"github.com/zoobz-io/chit\"\n",[962,1314,1315],{"class":964,"line":1132},[962,1316,1088],{"class":1292},[962,1318,1319],{"class":964,"line":1139},[962,1320,1136],{"emptyLinePlaceholder":1135},[962,1322,1323],{"class":964,"line":1145},[962,1324,1325],{"class":967},"// SimpleEmitter collects messages for demonstration\n",[962,1327,1328,1331,1334,1337],{"class":964,"line":1171},[962,1329,1330],{"class":993},"type",[962,1332,1333],{"class":1003}," SimpleEmitter",[962,1335,1336],{"class":993}," struct",[962,1338,1054],{"class":983},[962,1340,1341,1344,1346,1348,1350],{"class":964,"line":1176},[962,1342,1343],{"class":1110},"    Messages",[962,1345,1026],{"class":983},[962,1347,869],{"class":1003},[962,1349,984],{"class":983},[962,1351,1352],{"class":1003},"Message\n",[962,1354,1355],{"class":964,"line":1182},[962,1356,1168],{"class":983},[962,1358,1359],{"class":964,"line":1207},[962,1360,1136],{"emptyLinePlaceholder":1135},[962,1362,1363,1365,1367,1370,1373,1376,1378,1381,1383,1386,1388,1390,1392,1394,1397,1399,1401,1403,1405,1407],{"class":964,"line":1212},[962,1364,994],{"class":993},[962,1366,1038],{"class":983},[962,1368,1369],{"class":999},"e ",[962,1371,1372],{"class":1094},"*",[962,1374,1375],{"class":1003},"SimpleEmitter",[962,1377,1035],{"class":983},[962,1379,1380],{"class":987}," Emit",[962,1382,990],{"class":983},[962,1384,1385],{"class":999},"_",[962,1387,1004],{"class":1003},[962,1389,984],{"class":983},[962,1391,1009],{"class":1003},[962,1393,1012],{"class":983},[962,1395,1396],{"class":999}," msg",[962,1398,980],{"class":1003},[962,1400,984],{"class":983},[962,1402,711],{"class":1003},[962,1404,1035],{"class":983},[962,1406,1049],{"class":1003},[962,1408,1054],{"class":983},[962,1410,1411,1414,1416,1419,1422,1426,1428,1431,1433,1435,1437,1439],{"class":964,"line":1218},[962,1412,1413],{"class":973},"    e",[962,1415,984],{"class":983},[962,1417,1418],{"class":973},"Messages",[962,1420,1421],{"class":973}," =",[962,1423,1425],{"class":1424},"skxcq"," append",[962,1427,990],{"class":983},[962,1429,1430],{"class":973},"e",[962,1432,984],{"class":983},[962,1434,1418],{"class":973},[962,1436,1012],{"class":983},[962,1438,1396],{"class":973},[962,1440,1088],{"class":983},[962,1442,1444,1447,1449,1452,1454,1457,1461,1464,1466,1470,1473,1475,1477,1479,1482,1484,1486,1488,1490],{"class":964,"line":1443},17,[962,1445,1446],{"class":973},"    fmt",[962,1448,984],{"class":983},[962,1450,1451],{"class":987},"Printf",[962,1453,990],{"class":983},[962,1455,1456],{"class":1233},"\"[",[962,1458,1460],{"class":1459},"scyPU","%s",[962,1462,1463],{"class":1233},"]: ",[962,1465,1460],{"class":1459},[962,1467,1469],{"class":1468},"suWN2","\\n",[962,1471,1472],{"class":1233},"\"",[962,1474,1012],{"class":983},[962,1476,1396],{"class":973},[962,1478,984],{"class":983},[962,1480,1481],{"class":973},"Role",[962,1483,1012],{"class":983},[962,1485,1396],{"class":973},[962,1487,984],{"class":983},[962,1489,1111],{"class":973},[962,1491,1088],{"class":983},[962,1493,1495,1497],{"class":964,"line":1494},18,[962,1496,1095],{"class":1094},[962,1498,1123],{"class":993},[962,1500,1502],{"class":964,"line":1501},19,[962,1503,1168],{"class":983},[962,1505,1507,1509,1511,1513,1515,1517,1519,1522,1524,1526,1528,1530,1532,1534,1537,1539,1541,1543,1545,1547,1550,1553,1556],{"class":964,"line":1506},20,[962,1508,994],{"class":993},[962,1510,1038],{"class":983},[962,1512,1369],{"class":999},[962,1514,1372],{"class":1094},[962,1516,1375],{"class":1003},[962,1518,1035],{"class":983},[962,1520,1521],{"class":987}," Push",[962,1523,990],{"class":983},[962,1525,1385],{"class":999},[962,1527,1004],{"class":1003},[962,1529,984],{"class":983},[962,1531,1009],{"class":1003},[962,1533,1012],{"class":983},[962,1535,1536],{"class":999}," _",[962,1538,980],{"class":1003},[962,1540,984],{"class":983},[962,1542,716],{"class":1003},[962,1544,1035],{"class":983},[962,1546,1049],{"class":1003},[962,1548,1549],{"class":983}," {",[962,1551,1552],{"class":1094}," return",[962,1554,1555],{"class":993}," nil",[962,1557,1558],{"class":983}," }\n",[962,1560,1562,1564,1566,1568,1570,1572,1574,1577,1580,1582,1585,1587,1589],{"class":964,"line":1561},21,[962,1563,994],{"class":993},[962,1565,1038],{"class":983},[962,1567,1369],{"class":999},[962,1569,1372],{"class":1094},[962,1571,1375],{"class":1003},[962,1573,1035],{"class":983},[962,1575,1576],{"class":987}," Close",[962,1578,1579],{"class":983},"()",[962,1581,1049],{"class":1003},[962,1583,1584],{"class":983},"                                  {",[962,1586,1552],{"class":1094},[962,1588,1555],{"class":993},[962,1590,1558],{"class":983},[962,1592,1594],{"class":964,"line":1593},22,[962,1595,1136],{"emptyLinePlaceholder":1135},[962,1597,1599,1601,1604,1606],{"class":964,"line":1598},23,[962,1600,994],{"class":993},[962,1602,1603],{"class":987}," main",[962,1605,1579],{"class":983},[962,1607,1054],{"class":983},[962,1609,1611],{"class":964,"line":1610},24,[962,1612,1613],{"class":967},"    // Processor returns responses or yields for multi-turn\n",[962,1615,1617,1620,1622,1624,1626,1628,1630,1632,1634,1636,1638,1640,1642,1644,1646,1648,1650,1652,1654,1656,1658,1660,1662,1664,1666,1668,1670,1672,1674,1676],{"class":964,"line":1616},25,[962,1618,1619],{"class":973},"    processor",[962,1621,977],{"class":973},[962,1623,980],{"class":973},[962,1625,984],{"class":983},[962,1627,686],{"class":987},[962,1629,990],{"class":983},[962,1631,994],{"class":993},[962,1633,990],{"class":983},[962,1635,1385],{"class":999},[962,1637,1004],{"class":1003},[962,1639,984],{"class":983},[962,1641,1009],{"class":1003},[962,1643,1012],{"class":983},[962,1645,1015],{"class":999},[962,1647,1018],{"class":1003},[962,1649,1012],{"class":983},[962,1651,1023],{"class":999},[962,1653,1026],{"class":983},[962,1655,869],{"class":1003},[962,1657,984],{"class":983},[962,1659,711],{"class":1003},[962,1661,1035],{"class":983},[962,1663,1038],{"class":983},[962,1665,869],{"class":1003},[962,1667,984],{"class":983},[962,1669,104],{"class":1003},[962,1671,1012],{"class":983},[962,1673,1049],{"class":1003},[962,1675,1035],{"class":983},[962,1677,1054],{"class":983},[962,1679,1681],{"class":964,"line":1680},26,[962,1682,1683],{"class":967},"        // First message — ask for clarification\n",[962,1685,1687,1690,1693,1695,1698,1700,1703,1707],{"class":964,"line":1686},27,[962,1688,1689],{"class":1094},"        if",[962,1691,1692],{"class":1424}," len",[962,1694,990],{"class":983},[962,1696,1697],{"class":973},"history",[962,1699,1035],{"class":983},[962,1701,1702],{"class":1094}," ==",[962,1704,1706],{"class":1705},"sMAmT"," 1",[962,1708,1054],{"class":983},[962,1710,1712,1715,1717,1719,1721,1723],{"class":964,"line":1711},28,[962,1713,1714],{"class":1094},"            return",[962,1716,1098],{"class":1094},[962,1718,869],{"class":1003},[962,1720,984],{"class":983},[962,1722,115],{"class":1003},[962,1724,1725],{"class":983},"{\n",[962,1727,1729,1732,1734,1737],{"class":964,"line":1728},29,[962,1730,1731],{"class":1110},"                Prompt",[962,1733,1114],{"class":983},[962,1735,1736],{"class":1233}," \"Which city would you like weather for?\"",[962,1738,1739],{"class":983},",\n",[962,1741,1743,1746,1748,1751,1753,1755,1757,1759,1761,1763,1766,1768,1770,1772,1774,1776,1778,1780,1782,1784,1786,1788,1790,1792,1794,1796],{"class":964,"line":1742},30,[962,1744,1745],{"class":1110},"                Continuation",[962,1747,1114],{"class":983},[962,1749,1750],{"class":993}," func",[962,1752,990],{"class":983},[962,1754,1385],{"class":999},[962,1756,1004],{"class":1003},[962,1758,984],{"class":983},[962,1760,1009],{"class":1003},[962,1762,1012],{"class":983},[962,1764,1765],{"class":999}," city",[962,1767,1018],{"class":1003},[962,1769,1012],{"class":983},[962,1771,1536],{"class":999},[962,1773,1026],{"class":983},[962,1775,869],{"class":1003},[962,1777,984],{"class":983},[962,1779,711],{"class":1003},[962,1781,1035],{"class":983},[962,1783,1038],{"class":983},[962,1785,869],{"class":1003},[962,1787,984],{"class":983},[962,1789,104],{"class":1003},[962,1791,1012],{"class":983},[962,1793,1049],{"class":1003},[962,1795,1035],{"class":983},[962,1797,1054],{"class":983},[962,1799,1801,1804,1806,1808,1810,1812,1814,1816,1818,1821,1823,1826,1828,1831,1833,1836,1838,1840,1843],{"class":964,"line":1800},31,[962,1802,1803],{"class":1094},"                    return",[962,1805,1098],{"class":1094},[962,1807,869],{"class":1003},[962,1809,984],{"class":983},[962,1811,109],{"class":1003},[962,1813,1107],{"class":983},[962,1815,1111],{"class":1110},[962,1817,1114],{"class":983},[962,1819,1820],{"class":973}," fmt",[962,1822,984],{"class":983},[962,1824,1825],{"class":987},"Sprintf",[962,1827,990],{"class":983},[962,1829,1830],{"class":1233},"\"Weather in ",[962,1832,1460],{"class":1459},[962,1834,1835],{"class":1233},": Sunny, 22°C\"",[962,1837,1012],{"class":983},[962,1839,1765],{"class":973},[962,1841,1842],{"class":983},")},",[962,1844,1123],{"class":993},[962,1846,1848],{"class":964,"line":1847},32,[962,1849,1850],{"class":983},"                },\n",[962,1852,1854,1857],{"class":964,"line":1853},33,[962,1855,1856],{"class":983},"            },",[962,1858,1123],{"class":993},[962,1860,1862],{"class":964,"line":1861},34,[962,1863,1864],{"class":983},"        }\n",[962,1866,1868,1871,1873,1875,1877,1879,1881,1883,1885,1888,1890],{"class":964,"line":1867},35,[962,1869,1870],{"class":1094},"        return",[962,1872,1098],{"class":1094},[962,1874,869],{"class":1003},[962,1876,984],{"class":983},[962,1878,109],{"class":1003},[962,1880,1107],{"class":983},[962,1882,1111],{"class":1110},[962,1884,1114],{"class":983},[962,1886,1887],{"class":1233}," \"Hello! How can I help?\"",[962,1889,1120],{"class":983},[962,1891,1123],{"class":993},[962,1893,1895],{"class":964,"line":1894},36,[962,1896,1897],{"class":983},"    })\n",[962,1899,1901],{"class":964,"line":1900},37,[962,1902,1136],{"emptyLinePlaceholder":1135},[962,1904,1906,1909,1911,1913,1915],{"class":964,"line":1905},38,[962,1907,1908],{"class":973},"    emitter",[962,1910,977],{"class":973},[962,1912,1098],{"class":1094},[962,1914,1375],{"class":1003},[962,1916,1917],{"class":983},"{}\n",[962,1919,1921,1924,1926,1928,1930,1932,1934,1936,1938,1940],{"class":964,"line":1920},39,[962,1922,1923],{"class":973},"    chat",[962,1925,977],{"class":973},[962,1927,980],{"class":973},[962,1929,984],{"class":983},[962,1931,500],{"class":987},[962,1933,990],{"class":983},[962,1935,974],{"class":973},[962,1937,1012],{"class":983},[962,1939,1202],{"class":973},[962,1941,1088],{"class":983},[962,1943,1945],{"class":964,"line":1944},40,[962,1946,1136],{"emptyLinePlaceholder":1135},[962,1948,1950],{"class":964,"line":1949},41,[962,1951,1952],{"class":967},"    // First call yields, asking for more info\n",[962,1954,1956,1958,1960,1962,1964,1967,1969,1972,1975,1978],{"class":964,"line":1955},42,[962,1957,1923],{"class":973},[962,1959,984],{"class":983},[962,1961,509],{"class":987},[962,1963,990],{"class":983},[962,1965,1966],{"class":973},"context",[962,1968,984],{"class":983},[962,1970,1971],{"class":987},"Background",[962,1973,1974],{"class":983},"(),",[962,1976,1977],{"class":1233}," \"What's the weather?\"",[962,1979,1088],{"class":983},[962,1981,1983],{"class":964,"line":1982},43,[962,1984,1985],{"class":967},"    // [assistant]: Which city would you like weather for?\n",[962,1987,1989],{"class":964,"line":1988},44,[962,1990,1136],{"emptyLinePlaceholder":1135},[962,1992,1994],{"class":964,"line":1993},45,[962,1995,1996],{"class":967},"    // Second call resumes with the answer\n",[962,1998,2000,2002,2004,2006,2008,2010,2012,2014,2016,2019],{"class":964,"line":1999},46,[962,2001,1923],{"class":973},[962,2003,984],{"class":983},[962,2005,509],{"class":987},[962,2007,990],{"class":983},[962,2009,1966],{"class":973},[962,2011,984],{"class":983},[962,2013,1971],{"class":987},[962,2015,1974],{"class":983},[962,2017,2018],{"class":1233}," \"Tokyo\"",[962,2020,1088],{"class":983},[962,2022,2024],{"class":964,"line":2023},47,[962,2025,2026],{"class":967},"    // [assistant]: Weather in Tokyo: Sunny, 22°C\n",[962,2028,2030],{"class":964,"line":2029},48,[962,2031,1136],{"emptyLinePlaceholder":1135},[962,2033,2035],{"class":964,"line":2034},49,[962,2036,2037],{"class":967},"    // History tracked automatically\n",[962,2039,2041,2043,2045,2047,2049,2052,2055,2058,2060,2062,2064,2066,2068,2070,2072,2075],{"class":964,"line":2040},50,[962,2042,1446],{"class":973},[962,2044,984],{"class":983},[962,2046,1451],{"class":987},[962,2048,990],{"class":983},[962,2050,2051],{"class":1233},"\"Conversation has ",[962,2053,2054],{"class":1459},"%d",[962,2056,2057],{"class":1233}," messages",[962,2059,1469],{"class":1468},[962,2061,1472],{"class":1233},[962,2063,1012],{"class":983},[962,2065,1692],{"class":1424},[962,2067,990],{"class":983},[962,2069,1185],{"class":973},[962,2071,984],{"class":983},[962,2073,2074],{"class":987},"History",[962,2076,2077],{"class":983},"()))\n",[962,2079,2081],{"class":964,"line":2080},51,[962,2082,1168],{"class":983},[945,2084,2086],{"id":2085},"capabilities","Capabilities",[2088,2089,2090,2106],"table",{},[2091,2092,2093],"thead",{},[2094,2095,2096,2100,2103],"tr",{},[2097,2098,2099],"th",{},"Feature",[2097,2101,2102],{},"Description",[2097,2104,2105],{},"Docs",[2107,2108,2109,2123,2135,2154,2167,2184],"tbody",{},[2094,2110,2111,2115,2118],{},[2112,2113,2114],"td",{},"Processor Interface",[2112,2116,2117],{},"Pluggable reasoning with read-only history",[2112,2119,2120],{},[874,2121,85],{"href":2122},"docs/learn/concepts",[2094,2124,2125,2128,2131],{},[2112,2126,2127],{},"Yield & Continue",[2112,2129,2130],{},"Multi-turn conversations via continuations",[2112,2132,2133],{},[874,2134,85],{"href":2122},[2094,2136,2137,2140,2148],{},[2112,2138,2139],{},"Pipeline Resilience",[2112,2141,2142,2143],{},"Retry, timeout, circuit breaker via ",[874,2144,2147],{"href":2145,"rel":2146},"https://github.com/zoobz-io/pipz",[878],"pipz",[2112,2149,2150],{},[874,2151,2153],{"href":2152},"docs/guides/reliability","Reliability",[2094,2155,2156,2159,2162],{},[2112,2157,2158],{},"Emitter Abstraction",[2112,2160,2161],{},"Stream responses, push resources",[2112,2163,2164],{},[874,2165,154],{"href":2166},"docs/learn/architecture",[2094,2168,2169,2172,2180],{},[2112,2170,2171],{},"Signal Observability",[2112,2173,2174,2175],{},"Lifecycle events via ",[874,2176,2179],{"href":2177,"rel":2178},"https://github.com/zoobz-io/capitan",[878],"capitan",[2112,2181,2182],{},[874,2183,154],{"href":2166},[2094,2185,2186,2189,2192],{},[2112,2187,2188],{},"Testing Utilities",[2112,2190,2191],{},"Mock processors and emitters",[2112,2193,2194],{},[874,2195,2197],{"href":2196},"docs/guides/testing","Testing",[945,2199,2201],{"id":2200},"why-chit","Why chit?",[2203,2204,2205,2213,2219,2229,2239],"ul",{},[2206,2207,2208,2212],"li",{},[2209,2210,2211],"strong",{},"Clean separation"," — User conversation stays clean; internal LLM reasoning is processor's business",[2206,2214,2215,2218],{},[2209,2216,2217],{},"Turn-taking built in"," — Yield/Continue pattern for multi-turn without manual state management",[2206,2220,2221,2224,2225,2228],{},[2209,2222,2223],{},"Pipeline-native"," — Wrap with ",[874,2226,2147],{"href":2145,"rel":2227},[878]," for retry, timeout, rate limiting, circuit breakers",[2206,2230,2231,2234,2235,2238],{},[2209,2232,2233],{},"Observable"," — Lifecycle signals via ",[874,2236,2179],{"href":2177,"rel":2237},[878]," without instrumentation code",[2206,2240,2241,2244],{},[2209,2242,2243],{},"Bring your own LLM"," — Processor interface works with any LLM client or framework",[945,2246,2248],{"id":2247},"bring-your-own-reasoning","Bring Your Own Reasoning",[871,2250,2251],{},"Chit manages the conversation lifecycle. How you reason is up to you.",[953,2253,2255],{"className":955,"code":2254,"language":957,"meta":43,"style":43},"// Use zyn for typed LLM interactions\nprocessor := chit.ProcessorFunc(func(ctx context.Context, input string, history []chit.Message) (chit.Result, error) {\n    // Create internal session — separate from user history\n    session := zyn.NewSession()\n    session.Append(zyn.RoleSystem, \"You are a helpful assistant.\")\n\n    // Add user context\n    for _, msg := range history {\n        session.Append(zyn.Role(msg.Role), msg.Content)\n    }\n\n    // Call LLM — internal retries, tool use, etc. stay internal\n    response, _ := synapse.Process(ctx, session)\n    return &chit.Response{Content: response}, nil\n})\n\n// Add resilience via pipz options\nchat := chit.New(processor, emitter,\n    chit.WithRetry(3),\n    chit.WithTimeout(30*time.Second),\n    chit.WithCircuitBreaker(5, time.Minute),\n)\n",[959,2256,2257,2262,2324,2329,2347,2373,2377,2382,2402,2439,2444,2448,2453,2482,2506,2510,2514,2519,2541,2558,2581,2606],{"__ignoreMap":43},[962,2258,2259],{"class":964,"line":9},[962,2260,2261],{"class":967},"// Use zyn for typed LLM interactions\n",[962,2263,2264,2266,2268,2270,2272,2274,2276,2278,2280,2282,2284,2286,2288,2290,2292,2294,2296,2298,2300,2302,2304,2306,2308,2310,2312,2314,2316,2318,2320,2322],{"class":964,"line":19},[962,2265,974],{"class":973},[962,2267,977],{"class":973},[962,2269,980],{"class":973},[962,2271,984],{"class":983},[962,2273,686],{"class":987},[962,2275,990],{"class":983},[962,2277,994],{"class":993},[962,2279,990],{"class":983},[962,2281,1000],{"class":999},[962,2283,1004],{"class":1003},[962,2285,984],{"class":983},[962,2287,1009],{"class":1003},[962,2289,1012],{"class":983},[962,2291,1015],{"class":999},[962,2293,1018],{"class":1003},[962,2295,1012],{"class":983},[962,2297,1023],{"class":999},[962,2299,1026],{"class":983},[962,2301,869],{"class":1003},[962,2303,984],{"class":983},[962,2305,711],{"class":1003},[962,2307,1035],{"class":983},[962,2309,1038],{"class":983},[962,2311,869],{"class":1003},[962,2313,984],{"class":983},[962,2315,104],{"class":1003},[962,2317,1012],{"class":983},[962,2319,1049],{"class":1003},[962,2321,1035],{"class":983},[962,2323,1054],{"class":983},[962,2325,2326],{"class":964,"line":112},[962,2327,2328],{"class":967},"    // Create internal session — separate from user history\n",[962,2330,2331,2334,2336,2339,2341,2344],{"class":964,"line":1062},[962,2332,2333],{"class":973},"    session",[962,2335,977],{"class":973},[962,2337,2338],{"class":973}," zyn",[962,2340,984],{"class":983},[962,2342,2343],{"class":987},"NewSession",[962,2345,2346],{"class":983},"()\n",[962,2348,2349,2351,2353,2356,2358,2361,2363,2366,2368,2371],{"class":964,"line":1068},[962,2350,2333],{"class":973},[962,2352,984],{"class":983},[962,2354,2355],{"class":987},"Append",[962,2357,990],{"class":983},[962,2359,2360],{"class":973},"zyn",[962,2362,984],{"class":983},[962,2364,2365],{"class":973},"RoleSystem",[962,2367,1012],{"class":983},[962,2369,2370],{"class":1233}," \"You are a helpful assistant.\"",[962,2372,1088],{"class":983},[962,2374,2375],{"class":964,"line":1091},[962,2376,1136],{"emptyLinePlaceholder":1135},[962,2378,2379],{"class":964,"line":1126},[962,2380,2381],{"class":967},"    // Add user context\n",[962,2383,2384,2387,2389,2391,2393,2395,2398,2400],{"class":964,"line":1132},[962,2385,2386],{"class":1094},"    for",[962,2388,1536],{"class":973},[962,2390,1012],{"class":983},[962,2392,1396],{"class":973},[962,2394,977],{"class":973},[962,2396,2397],{"class":1094}," range",[962,2399,1023],{"class":973},[962,2401,1054],{"class":983},[962,2403,2404,2407,2409,2411,2413,2415,2417,2419,2421,2424,2426,2428,2431,2433,2435,2437],{"class":964,"line":1139},[962,2405,2406],{"class":973},"        session",[962,2408,984],{"class":983},[962,2410,2355],{"class":987},[962,2412,990],{"class":983},[962,2414,2360],{"class":973},[962,2416,984],{"class":983},[962,2418,1481],{"class":987},[962,2420,990],{"class":983},[962,2422,2423],{"class":973},"msg",[962,2425,984],{"class":983},[962,2427,1481],{"class":973},[962,2429,2430],{"class":983},"),",[962,2432,1396],{"class":973},[962,2434,984],{"class":983},[962,2436,1111],{"class":973},[962,2438,1088],{"class":983},[962,2440,2441],{"class":964,"line":1145},[962,2442,2443],{"class":983},"    }\n",[962,2445,2446],{"class":964,"line":1171},[962,2447,1136],{"emptyLinePlaceholder":1135},[962,2449,2450],{"class":964,"line":1176},[962,2451,2452],{"class":967},"    // Call LLM — internal retries, tool use, etc. stay internal\n",[962,2454,2455,2457,2459,2461,2463,2466,2468,2471,2473,2475,2477,2480],{"class":964,"line":1182},[962,2456,1071],{"class":973},[962,2458,1012],{"class":983},[962,2460,1536],{"class":973},[962,2462,977],{"class":973},[962,2464,2465],{"class":973}," synapse",[962,2467,984],{"class":983},[962,2469,2470],{"class":987},"Process",[962,2472,990],{"class":983},[962,2474,1000],{"class":973},[962,2476,1012],{"class":983},[962,2478,2479],{"class":973}," session",[962,2481,1088],{"class":983},[962,2483,2484,2486,2488,2490,2492,2494,2496,2498,2500,2502,2504],{"class":964,"line":1207},[962,2485,1095],{"class":1094},[962,2487,1098],{"class":1094},[962,2489,869],{"class":1003},[962,2491,984],{"class":983},[962,2493,109],{"class":1003},[962,2495,1107],{"class":983},[962,2497,1111],{"class":1110},[962,2499,1114],{"class":983},[962,2501,1117],{"class":973},[962,2503,1120],{"class":983},[962,2505,1123],{"class":993},[962,2507,2508],{"class":964,"line":1212},[962,2509,1129],{"class":983},[962,2511,2512],{"class":964,"line":1218},[962,2513,1136],{"emptyLinePlaceholder":1135},[962,2515,2516],{"class":964,"line":1443},[962,2517,2518],{"class":967},"// Add resilience via pipz options\n",[962,2520,2521,2523,2525,2527,2529,2531,2533,2535,2537,2539],{"class":964,"line":1494},[962,2522,1185],{"class":973},[962,2524,977],{"class":973},[962,2526,980],{"class":973},[962,2528,984],{"class":983},[962,2530,500],{"class":987},[962,2532,990],{"class":983},[962,2534,974],{"class":973},[962,2536,1012],{"class":983},[962,2538,1202],{"class":973},[962,2540,1739],{"class":983},[962,2542,2543,2546,2548,2550,2552,2555],{"class":964,"line":1501},[962,2544,2545],{"class":973},"    chit",[962,2547,984],{"class":983},[962,2549,565],{"class":987},[962,2551,990],{"class":983},[962,2553,2554],{"class":1705},"3",[962,2556,2557],{"class":983},"),\n",[962,2559,2560,2562,2564,2566,2568,2571,2574,2576,2579],{"class":964,"line":1506},[962,2561,2545],{"class":973},[962,2563,984],{"class":983},[962,2565,575],{"class":987},[962,2567,990],{"class":983},[962,2569,2570],{"class":1705},"30",[962,2572,2573],{"class":973},"*time",[962,2575,984],{"class":983},[962,2577,2578],{"class":973},"Second",[962,2580,2557],{"class":983},[962,2582,2583,2585,2587,2589,2591,2594,2596,2599,2601,2604],{"class":964,"line":1561},[962,2584,2545],{"class":973},[962,2586,984],{"class":983},[962,2588,580],{"class":987},[962,2590,990],{"class":983},[962,2592,2593],{"class":1705},"5",[962,2595,1012],{"class":983},[962,2597,2598],{"class":973}," time",[962,2600,984],{"class":983},[962,2602,2603],{"class":973},"Minute",[962,2605,2557],{"class":983},[962,2607,2608],{"class":964,"line":1593},[962,2609,1088],{"class":983},[871,2611,2612,2613,2617],{},"Your processor implementation can use ",[874,2614,2360],{"href":2615,"rel":2616},"https://github.com/zoobz-io/zyn",[878]," for typed synapses, raw API calls, or any other approach. Chit doesn't care — it just manages what the user sees.",[945,2619,2621],{"id":2620},"documentation","Documentation",[2203,2623,2624,2652,2675],{},[2206,2625,2626,2628],{},[2209,2627,808],{},[2203,2629,2630,2636,2642,2647],{},[2206,2631,2632,2635],{},[874,2633,6],{"href":2634},"docs/learn/overview"," — Purpose and design philosophy",[2206,2637,2638,2641],{},[874,2639,37],{"href":2640},"docs/learn/quickstart"," — Get started in minutes",[2206,2643,2644,2646],{},[874,2645,85],{"href":2122}," — Processors, results, emitters, history",[2206,2648,2649,2651],{},[874,2650,154],{"href":2166}," — Internal design and pipeline integration",[2206,2653,2654,2656],{},[2209,2655,822],{},[2203,2657,2658,2663,2670],{},[2206,2659,2660,2662],{},[874,2661,2197],{"href":2196}," — Mock processors and emitters",[2206,2664,2665,2669],{},[874,2666,2668],{"href":2667},"docs/guides/troubleshooting","Troubleshooting"," — Common issues and solutions",[2206,2671,2672,2674],{},[874,2673,2153],{"href":2152}," — Retry, timeout, circuit breakers",[2206,2676,2677,2679],{},[2209,2678,833],{},[2203,2680,2681,2688],{},[2206,2682,2683,2687],{},[874,2684,2686],{"href":2685},"docs/reference/api","API"," — Complete function documentation",[2206,2689,2690,2694],{},[874,2691,2693],{"href":2692},"docs/reference/types","Types"," — Message, Result, Response, Yield",[945,2696,2698],{"id":2697},"contributing","Contributing",[871,2700,2701,2702,2706],{},"See ",[874,2703,2705],{"href":2704},"CONTRIBUTING","CONTRIBUTING.md"," for guidelines.",[945,2708,921],{"id":2709},"license",[871,2711,2712,2713,2715],{},"MIT License — see ",[874,2714,918],{"href":918}," for details.",[2717,2718,2719],"style",{},"html pre.shiki code .sLkEo, html code.shiki .sLkEo{--shiki-default:var(--shiki-comment)}html pre.shiki code .sh8_p, html code.shiki .sh8_p{--shiki-default:var(--shiki-text)}html pre.shiki code .sq5bi, html code.shiki .sq5bi{--shiki-default:var(--shiki-punctuation)}html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html pre.shiki code .sSYET, html code.shiki .sSYET{--shiki-default:var(--shiki-parameter)}html pre.shiki code .sYBwO, html code.shiki .sYBwO{--shiki-default:var(--shiki-type)}html pre.shiki code .sW3Qg, html code.shiki .sW3Qg{--shiki-default:var(--shiki-operator)}html pre.shiki code .sBGCq, html code.shiki .sBGCq{--shiki-default:var(--shiki-property)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .soy-K, html code.shiki .soy-K{--shiki-default:#BBBBBB}html pre.shiki code .skxcq, html code.shiki .skxcq{--shiki-default:var(--shiki-builtin)}html pre.shiki code .scyPU, html code.shiki .scyPU{--shiki-default:var(--shiki-placeholder)}html pre.shiki code .suWN2, html code.shiki .suWN2{--shiki-default:var(--shiki-tag)}html pre.shiki code .sMAmT, html code.shiki .sMAmT{--shiki-default:var(--shiki-number)}",{"title":43,"searchDepth":19,"depth":19,"links":2721},[2722,2723,2724,2725,2726,2727,2728,2729,2730],{"id":947,"depth":19,"text":948},{"id":1242,"depth":19,"text":1243},{"id":1266,"depth":19,"text":1267},{"id":2085,"depth":19,"text":2086},{"id":2200,"depth":19,"text":2201},{"id":2247,"depth":19,"text":2248},{"id":2620,"depth":19,"text":2621},{"id":2697,"depth":19,"text":2698},{"id":2709,"depth":19,"text":921},"md","book-open",{},"/readme",{"title":862,"description":43},"readme","U479xsYgP1p9yjnaVSt7ZZ1NWQlwYqX7MHYKz8n6J8I",{"id":2739,"title":2740,"body":2741,"description":43,"extension":2731,"icon":3220,"meta":3221,"navigation":1135,"path":3222,"seo":3223,"stem":3224,"__hash__":3225},"resources/security.md","Security",{"type":864,"value":2742,"toc":3205},[2743,2747,2751,2754,2793,2797,2800,2805,2810,2813,2852,2856,2859,2908,2912,2938,2942,2945,2949,2952,3059,3063,3066,3097,3101,3104,3130,3134,3137,3162,3166,3180,3184,3187,3193,3196,3202],[867,2744,2746],{"id":2745},"security-policy","Security Policy",[945,2748,2750],{"id":2749},"supported-versions","Supported Versions",[871,2752,2753],{},"We release patches for security vulnerabilities. Which versions are eligible for receiving such patches depends on the CVSS v3.0 Rating:",[2088,2755,2756,2769],{},[2091,2757,2758],{},[2094,2759,2760,2763,2766],{},[2097,2761,2762],{},"Version",[2097,2764,2765],{},"Supported",[2097,2767,2768],{},"Status",[2107,2770,2771,2782],{},[2094,2772,2773,2776,2779],{},[2112,2774,2775],{},"latest",[2112,2777,2778],{},"✅",[2112,2780,2781],{},"Active development",[2094,2783,2784,2787,2790],{},[2112,2785,2786],{},"\u003C latest",[2112,2788,2789],{},"❌",[2112,2791,2792],{},"Security fixes only for critical issues",[945,2794,2796],{"id":2795},"reporting-a-vulnerability","Reporting a Vulnerability",[871,2798,2799],{},"We take the security of chit seriously. If you have discovered a security vulnerability in this project, please report it responsibly.",[2801,2802,2804],"h3",{"id":2803},"how-to-report","How to Report",[871,2806,2807],{},[2209,2808,2809],{},"Please DO NOT report security vulnerabilities through public GitHub issues.",[871,2811,2812],{},"Instead, please report them via one of the following methods:",[2814,2815,2816,2839],"ol",{},[2206,2817,2818,2821,2822],{},[2209,2819,2820],{},"GitHub Security Advisories"," (Preferred)",[2203,2823,2824,2833,2836],{},[2206,2825,2826,2827,2832],{},"Go to the ",[874,2828,2831],{"href":2829,"rel":2830},"https://github.com/zoobz-io/chit/security",[878],"Security tab"," of this repository",[2206,2834,2835],{},"Click \"Report a vulnerability\"",[2206,2837,2838],{},"Fill out the form with details about the vulnerability",[2206,2840,2841,2844],{},[2209,2842,2843],{},"Email",[2203,2845,2846,2849],{},[2206,2847,2848],{},"Send details to the repository maintainer through GitHub profile contact information",[2206,2850,2851],{},"Use PGP encryption if possible for sensitive details",[2801,2853,2855],{"id":2854},"what-to-include","What to Include",[871,2857,2858],{},"Please include the following information (as much as you can provide) to help us better understand the nature and scope of the possible issue:",[2203,2860,2861,2867,2873,2879,2885,2890,2896,2902],{},[2206,2862,2863,2866],{},[2209,2864,2865],{},"Type of issue"," (e.g., prompt injection, data leakage, stream hijacking, etc.)",[2206,2868,2869,2872],{},[2209,2870,2871],{},"Full paths of source file(s)"," related to the manifestation of the issue",[2206,2874,2875,2878],{},[2209,2876,2877],{},"The location of the affected source code"," (tag/branch/commit or direct URL)",[2206,2880,2881,2884],{},[2209,2882,2883],{},"Any special configuration required"," to reproduce the issue",[2206,2886,2887,2884],{},[2209,2888,2889],{},"Step-by-step instructions",[2206,2891,2892,2895],{},[2209,2893,2894],{},"Proof-of-concept or exploit code"," (if possible)",[2206,2897,2898,2901],{},[2209,2899,2900],{},"Impact of the issue",", including how an attacker might exploit the issue",[2206,2903,2904,2907],{},[2209,2905,2906],{},"Your name and affiliation"," (optional)",[2801,2909,2911],{"id":2910},"what-to-expect","What to Expect",[2203,2913,2914,2920,2926,2932],{},[2206,2915,2916,2919],{},[2209,2917,2918],{},"Acknowledgment",": We will acknowledge receipt of your vulnerability report within 48 hours",[2206,2921,2922,2925],{},[2209,2923,2924],{},"Initial Assessment",": Within 7 days, we will provide an initial assessment of the report",[2206,2927,2928,2931],{},[2209,2929,2930],{},"Resolution Timeline",": We aim to resolve critical issues within 30 days",[2206,2933,2934,2937],{},[2209,2935,2936],{},"Disclosure",": We will coordinate with you on the disclosure timeline",[2801,2939,2941],{"id":2940},"preferred-languages","Preferred Languages",[871,2943,2944],{},"We prefer all communications to be in English.",[945,2946,2948],{"id":2947},"security-best-practices","Security Best Practices",[871,2950,2951],{},"When using chit in your applications, we recommend:",[2814,2953,2954,2975,2991,3011,3027,3043],{},[2206,2955,2956,2959],{},[2209,2957,2958],{},"Keep Dependencies Updated",[953,2960,2962],{"className":1246,"code":2961,"language":1248,"meta":43,"style":43},"go get -u github.com/zoobz-io/chit\n",[959,2963,2964],{"__ignoreMap":43},[962,2965,2966,2968,2970,2973],{"class":964,"line":9},[962,2967,957],{"class":987},[962,2969,1257],{"class":1233},[962,2971,2972],{"class":993}," -u",[962,2974,1260],{"class":1233},[2206,2976,2977,2980],{},[2209,2978,2979],{},"Protect LLM Credentials",[2203,2981,2982,2985,2988],{},[2206,2983,2984],{},"Never hardcode API keys in source code",[2206,2986,2987],{},"Use environment variables or secret managers",[2206,2989,2990],{},"Rotate keys regularly",[2206,2992,2993,2996],{},[2209,2994,2995],{},"Validate LLM Outputs",[2203,2997,2998,3001,3004],{},[2206,2999,3000],{},"Treat LLM responses as untrusted input",[2206,3002,3003],{},"Validate extracted data before using in critical paths",[2206,3005,3006,3007,3010],{},"Use typed extraction (Extract",[962,3008,3009],{},"T",") for structured data",[2206,3012,3013,3016],{},[2209,3014,3015],{},"Stream Security",[2203,3017,3018,3021,3024],{},[2206,3019,3020],{},"Implement authentication before providing Emitter access",[2206,3022,3023],{},"Validate that clients are authorized to receive streamed data",[2206,3025,3026],{},"Consider rate limiting on stream connections",[2206,3028,3029,3032],{},[2209,3030,3031],{},"Context Management",[2203,3033,3034,3037,3040],{},[2206,3035,3036],{},"Be mindful of what data accumulates in Chat entries",[2206,3038,3039],{},"Review what information flows between primitives",[2206,3041,3042],{},"Use Fork primitives for content moderation routing",[2206,3044,3045,3048],{},[2209,3046,3047],{},"Resource Management",[2203,3049,3050,3053,3056],{},[2206,3051,3052],{},"Use context timeouts for all operations",[2206,3054,3055],{},"Implement rate limiting for LLM calls",[2206,3057,3058],{},"Monitor token usage and costs",[945,3060,3062],{"id":3061},"security-features","Security Features",[871,3064,3065],{},"chit includes several built-in security features:",[2203,3067,3068,3074,3080,3086,3091],{},[2206,3069,3070,3073],{},[2209,3071,3072],{},"Type Safety",": Generic types prevent type confusion in extracted data",[2206,3075,3076,3079],{},[2209,3077,3078],{},"Context Support",": Built-in cancellation and timeout support",[2206,3081,3082,3085],{},[2209,3083,3084],{},"Error Isolation",": Errors are properly wrapped and traced",[2206,3087,3088,3090],{},[2209,3089,479],{},": Full signal emission for audit trails via capitan",[2206,3092,3093,3096],{},[2209,3094,3095],{},"Fork Primitives",": Binary routing for content validation and filtering",[945,3098,3100],{"id":3099},"llm-specific-considerations","LLM-Specific Considerations",[871,3102,3103],{},"When building LLM-powered chatbots:",[2203,3105,3106,3112,3118,3124],{},[2206,3107,3108,3111],{},[2209,3109,3110],{},"Prompt Injection",": Validate and sanitize user inputs before including in prompts",[2206,3113,3114,3117],{},[2209,3115,3116],{},"Data Exfiltration",": Be cautious about what context is sent to external LLM providers",[2206,3119,3120,3123],{},[2209,3121,3122],{},"Hallucination",": Don't trust LLM outputs for security-critical decisions without validation",[2206,3125,3126,3129],{},[2209,3127,3128],{},"Cost Attacks",": Implement safeguards against requests designed to consume excessive tokens",[945,3131,3133],{"id":3132},"automated-security-scanning","Automated Security Scanning",[871,3135,3136],{},"This project uses:",[2203,3138,3139,3144,3150,3156],{},[2206,3140,3141,3143],{},[2209,3142,906],{},": GitHub's semantic code analysis for security vulnerabilities",[2206,3145,3146,3149],{},[2209,3147,3148],{},"Dependabot",": Automated dependency updates",[2206,3151,3152,3155],{},[2209,3153,3154],{},"golangci-lint",": Static analysis including security linters (gosec)",[2206,3157,3158,3161],{},[2209,3159,3160],{},"Codecov",": Coverage tracking to ensure security-critical code is tested",[945,3163,3165],{"id":3164},"vulnerability-disclosure-policy","Vulnerability Disclosure Policy",[2203,3167,3168,3171,3174,3177],{},[2206,3169,3170],{},"Security vulnerabilities will be disclosed via GitHub Security Advisories",[2206,3172,3173],{},"We follow a 90-day disclosure timeline for non-critical issues",[2206,3175,3176],{},"Critical vulnerabilities may be disclosed sooner after patches are available",[2206,3178,3179],{},"We will credit reporters who follow responsible disclosure practices",[945,3181,3183],{"id":3182},"credits","Credits",[871,3185,3186],{},"We thank the following individuals for responsibly disclosing security issues:",[871,3188,3189],{},[3190,3191,3192],"em",{},"This list is currently empty. Be the first to help improve our security!",[3194,3195],"hr",{},[871,3197,3198,3201],{},[2209,3199,3200],{},"Last Updated",": 2026-01-13",[2717,3203,3204],{},"html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":43,"searchDepth":19,"depth":19,"links":3206},[3207,3208,3214,3215,3216,3217,3218,3219],{"id":2749,"depth":19,"text":2750},{"id":2795,"depth":19,"text":2796,"children":3209},[3210,3211,3212,3213],{"id":2803,"depth":112,"text":2804},{"id":2854,"depth":112,"text":2855},{"id":2910,"depth":112,"text":2911},{"id":2940,"depth":112,"text":2941},{"id":2947,"depth":19,"text":2948},{"id":3061,"depth":19,"text":3062},{"id":3099,"depth":19,"text":3100},{"id":3132,"depth":19,"text":3133},{"id":3164,"depth":19,"text":3165},{"id":3182,"depth":19,"text":3183},"shield",{},"/security",{"title":2740,"description":43},"security","R64GusKHVKUV8MJIgu5sj_wuFbWc988c9y6nhOFdI0A",{"id":3227,"title":2698,"body":3228,"description":3236,"extension":2731,"icon":959,"meta":3624,"navigation":1135,"path":3625,"seo":3626,"stem":2697,"__hash__":3627},"resources/contributing.md",{"type":864,"value":3229,"toc":3605},[3230,3234,3237,3241,3244,3248,3286,3290,3294,3312,3315,3331,3333,3344,3348,3352,3366,3370,3381,3385,3390,3393,3413,3417,3420,3434,3438,3466,3469,3472,3485,3488,3506,3509,3521,3525,3533,3537,3540,3584,3588,3599,3602],[867,3231,3233],{"id":3232},"contributing-to-chit","Contributing to chit",[871,3235,3236],{},"Thank you for your interest in contributing to chit! This guide will help you get started.",[945,3238,3240],{"id":3239},"code-of-conduct","Code of Conduct",[871,3242,3243],{},"By participating in this project, you agree to maintain a respectful and inclusive environment for all contributors.",[945,3245,3247],{"id":3246},"getting-started","Getting Started",[2814,3249,3250,3253,3259,3265,3268,3274,3277,3283],{},[2206,3251,3252],{},"Fork the repository",[2206,3254,3255,3256],{},"Clone your fork: ",[959,3257,3258],{},"git clone https://github.com/yourusername/chit.git",[2206,3260,3261,3262],{},"Create a feature branch: ",[959,3263,3264],{},"git checkout -b feature/your-feature-name",[2206,3266,3267],{},"Make your changes",[2206,3269,3270,3271],{},"Run tests: ",[959,3272,3273],{},"make test",[2206,3275,3276],{},"Commit your changes with a descriptive message",[2206,3278,3279,3280],{},"Push to your fork: ",[959,3281,3282],{},"git push origin feature/your-feature-name",[2206,3284,3285],{},"Create a Pull Request",[945,3287,3289],{"id":3288},"development-guidelines","Development Guidelines",[2801,3291,3293],{"id":3292},"code-style","Code Style",[2203,3295,3296,3299,3306,3309],{},[2206,3297,3298],{},"Follow standard Go conventions",[2206,3300,3301,3302,3305],{},"Run ",[959,3303,3304],{},"go fmt"," before committing",[2206,3307,3308],{},"Add comments for exported functions and types",[2206,3310,3311],{},"Keep functions small and focused",[2801,3313,2197],{"id":3314},"testing",[2203,3316,3317,3320,3325,3328],{},[2206,3318,3319],{},"Write tests for new functionality",[2206,3321,3322,3323],{},"Ensure all tests pass: ",[959,3324,3273],{},[2206,3326,3327],{},"Include benchmarks for performance-critical code",[2206,3329,3330],{},"Aim for good test coverage",[2801,3332,2621],{"id":2620},[2203,3334,3335,3338,3341],{},[2206,3336,3337],{},"Update documentation for API changes",[2206,3339,3340],{},"Add examples for new features",[2206,3342,3343],{},"Keep doc comments clear and concise",[945,3345,3347],{"id":3346},"types-of-contributions","Types of Contributions",[2801,3349,3351],{"id":3350},"bug-reports","Bug Reports",[2203,3353,3354,3357,3360,3363],{},[2206,3355,3356],{},"Use GitHub Issues",[2206,3358,3359],{},"Include minimal reproduction code",[2206,3361,3362],{},"Describe expected vs actual behavior",[2206,3364,3365],{},"Include Go version and OS",[2801,3367,3369],{"id":3368},"feature-requests","Feature Requests",[2203,3371,3372,3375,3378],{},[2206,3373,3374],{},"Open an issue for discussion first",[2206,3376,3377],{},"Explain the use case",[2206,3379,3380],{},"Consider backwards compatibility",[2801,3382,3384],{"id":3383},"code-contributions","Code Contributions",[3386,3387,3389],"h4",{"id":3388},"adding-primitives","Adding Primitives",[871,3391,3392],{},"New chat primitives should:",[2203,3394,3395,3401,3404,3407,3410],{},[2206,3396,3397,3398],{},"Implement ",[959,3399,3400],{},"pipz.Chainable[*Chat]",[2206,3402,3403],{},"Follow the existing pattern (builder methods, capitan signals)",[2206,3405,3406],{},"Emit appropriate signals for observability",[2206,3408,3409],{},"Include comprehensive tests",[2206,3411,3412],{},"Add documentation with examples",[3386,3414,3416],{"id":3415},"examples","Examples",[871,3418,3419],{},"New examples should:",[2203,3421,3422,3425,3428,3431],{},[2206,3423,3424],{},"Demonstrate a real-world chatbot scenario",[2206,3426,3427],{},"Include tests",[2206,3429,3430],{},"Have descriptive comments",[2206,3432,3433],{},"Follow the existing structure",[945,3435,3437],{"id":3436},"pull-request-process","Pull Request Process",[2814,3439,3440,3446,3451,3456,3461],{},[2206,3441,3442,3445],{},[2209,3443,3444],{},"Keep PRs focused"," - One feature/fix per PR",[2206,3447,3448],{},[2209,3449,3450],{},"Write descriptive commit messages",[2206,3452,3453],{},[2209,3454,3455],{},"Update tests and documentation",[2206,3457,3458],{},[2209,3459,3460],{},"Ensure CI passes",[2206,3462,3463],{},[2209,3464,3465],{},"Respond to review feedback",[945,3467,2197],{"id":3468},"testing-1",[871,3470,3471],{},"Run the full test suite:",[953,3473,3475],{"className":1246,"code":3474,"language":1248,"meta":43,"style":43},"make test\n",[959,3476,3477],{"__ignoreMap":43},[962,3478,3479,3482],{"class":964,"line":9},[962,3480,3481],{"class":987},"make",[962,3483,3484],{"class":1233}," test\n",[871,3486,3487],{},"Run with race detection:",[953,3489,3491],{"className":1246,"code":3490,"language":1248,"meta":43,"style":43},"go test -race ./...\n",[959,3492,3493],{"__ignoreMap":43},[962,3494,3495,3497,3500,3503],{"class":964,"line":9},[962,3496,957],{"class":987},[962,3498,3499],{"class":1233}," test",[962,3501,3502],{"class":993}," -race",[962,3504,3505],{"class":1233}," ./...\n",[871,3507,3508],{},"Run benchmarks:",[953,3510,3512],{"className":1246,"code":3511,"language":1248,"meta":43,"style":43},"make bench\n",[959,3513,3514],{"__ignoreMap":43},[962,3515,3516,3518],{"class":964,"line":9},[962,3517,3481],{"class":987},[962,3519,3520],{"class":1233}," bench\n",[945,3522,3524],{"id":3523},"project-structure","Project Structure",[953,3526,3531],{"className":3527,"code":3529,"language":3530},[3528],"language-text","chit/\n├── *.go              # Core library files (primitives, chat, emitter)\n├── *_test.go         # Tests\n├── testing/          # Test helpers and integration tests\n└── docs/             # Documentation\n","text",[959,3532,3529],{"__ignoreMap":43},[945,3534,3536],{"id":3535},"commit-messages","Commit Messages",[871,3538,3539],{},"Follow conventional commits:",[2203,3541,3542,3548,3554,3560,3566,3572,3578],{},[2206,3543,3544,3547],{},[959,3545,3546],{},"feat:"," New feature",[2206,3549,3550,3553],{},[959,3551,3552],{},"fix:"," Bug fix",[2206,3555,3556,3559],{},[959,3557,3558],{},"docs:"," Documentation changes",[2206,3561,3562,3565],{},[959,3563,3564],{},"test:"," Test additions/changes",[2206,3567,3568,3571],{},[959,3569,3570],{},"refactor:"," Code refactoring",[2206,3573,3574,3577],{},[959,3575,3576],{},"perf:"," Performance improvements",[2206,3579,3580,3583],{},[959,3581,3582],{},"chore:"," Maintenance tasks",[945,3585,3587],{"id":3586},"questions","Questions?",[2203,3589,3590,3593,3596],{},[2206,3591,3592],{},"Open an issue for questions",[2206,3594,3595],{},"Check existing issues first",[2206,3597,3598],{},"Be patient and respectful",[871,3600,3601],{},"Thank you for contributing to chit!",[2717,3603,3604],{},"html pre.shiki code .s5klm, html code.shiki .s5klm{--shiki-default:var(--shiki-function)}html pre.shiki code .sxAnc, html code.shiki .sxAnc{--shiki-default:var(--shiki-string)}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .sUt3r, html code.shiki .sUt3r{--shiki-default:var(--shiki-keyword)}",{"title":43,"searchDepth":19,"depth":19,"links":3606},[3607,3608,3609,3614,3619,3620,3621,3622,3623],{"id":3239,"depth":19,"text":3240},{"id":3246,"depth":19,"text":3247},{"id":3288,"depth":19,"text":3289,"children":3610},[3611,3612,3613],{"id":3292,"depth":112,"text":3293},{"id":3314,"depth":112,"text":2197},{"id":2620,"depth":112,"text":2621},{"id":3346,"depth":19,"text":3347,"children":3615},[3616,3617,3618],{"id":3350,"depth":112,"text":3351},{"id":3368,"depth":112,"text":3369},{"id":3383,"depth":112,"text":3384},{"id":3436,"depth":19,"text":3437},{"id":3468,"depth":19,"text":2197},{"id":3523,"depth":19,"text":3524},{"id":3535,"depth":19,"text":3536},{"id":3586,"depth":19,"text":3587},{},"/contributing",{"title":2698,"description":3236},"HJfRTygwEYof7aR6hZgs2GHqAK3i5Ii9ISsFaXxc9cE",{"id":3629,"title":6,"author":3630,"body":3631,"description":8,"extension":2731,"meta":3740,"navigation":1135,"path":5,"published":3741,"readtime":3742,"seo":3743,"stem":813,"tags":3744,"updated":3741,"__hash__":3747},"chit/v0.0.1/1.learn/1.overview.md","zoobzio",{"type":864,"value":3632,"toc":3734},[3633,3636,3638,3641,3644,3647,3650,3653,3691,3694,3711,3714],[867,3634,6],{"id":3635},"overview",[871,3637,13],{},[945,3639,16],{"id":3640},"the-idea",[871,3642,3643],{},"Chat applications share common infrastructure: session management, turn-taking, response streaming, observability. But the \"brain\" — how you reason, execute actions, fetch data — varies wildly between applications.",[871,3645,3646],{},"Chit separates these concerns. It handles the conversation lifecycle while you wire together your own reasoning primitives. The result: a thin orchestration layer that gets out of your way.",[945,3648,22],{"id":3649},"the-implementation",[871,3651,3652],{},"Chit provides:",[2203,3654,3655,3661,3667,3673,3679,3685],{},[2206,3656,3657,3660],{},[2209,3658,3659],{},"Chat controller"," — manages conversation state and turn-taking",[2206,3662,3663,3666],{},[2209,3664,3665],{},"Processor interface"," — pluggable brain for reasoning and execution",[2206,3668,3669,3672],{},[2209,3670,3671],{},"Emitter interface"," — dual-channel output (streaming text, structured resources)",[2206,3674,3675,3678],{},[2209,3676,3677],{},"Continuation pattern"," — clean multi-turn workflows without callback hell",[2206,3680,3681,3684],{},[2209,3682,3683],{},"Session integration"," — built on zyn for conversation history",[2206,3686,3687,3690],{},[2209,3688,3689],{},"Signal observability"," — lifecycle events via capitan",[945,3692,27],{"id":3693},"what-it-enables",[2203,3695,3696,3699,3702,3705,3708],{},[2206,3697,3698],{},"Build chat UIs with streaming responses",[2206,3700,3701],{},"Implement multi-step workflows that pause for user input",[2206,3703,3704],{},"Wire cogito (reasoning), ago (actions), and scio (data) into a coherent chat experience",[2206,3706,3707],{},"Observe conversation lifecycle for logging, metrics, and debugging",[2206,3709,3710],{},"Test chat logic with provided mocks and helpers",[945,3712,32],{"id":3713},"next-steps",[2203,3715,3716,3722,3728],{},[2206,3717,3718,3721],{},[874,3719,37],{"href":3720},"./quickstart"," — get running in minutes",[2206,3723,3724,3727],{},[874,3725,85],{"href":3726},"./concepts"," — understand the core abstractions",[2206,3729,3730,3733],{},[874,3731,488],{"href":3732},"../reference/api"," — complete function documentation",{"title":43,"searchDepth":19,"depth":19,"links":3735},[3736,3737,3738,3739],{"id":3640,"depth":19,"text":16},{"id":3649,"depth":19,"text":22},{"id":3693,"depth":19,"text":27},{"id":3713,"depth":19,"text":32},{},"2025-01-15T00:00:00.000Z",null,{"title":6,"description":8},[869,3745,3746,1185],"conversation","llm","zhgsu6n4bs0imkEAJbzMjE8ItjXJFh702nxYPNoOr18",[3742,3749],{"title":37,"path":36,"stem":815,"description":39,"children":-1},1776268286718]