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.
Chat
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 tracking
- A session (zyn.Session) for conversation history
- A processor for reasoning
- An emitter for output
- An 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
Processor
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 {
Process(ctx context.Context, input string, session *zyn.Session) (Result, error)
}
What happens inside is up to you:
- Call an LLM via cogito
- Execute actions via ago
- Fetch data via scio
- Any 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) {
// Your logic here
return &chit.Response{Content: "Hello"}, nil
})
See: Processor interface reference
Result
A Result represents the outcome of processing. There are two kinds:
Response
Processing is complete. The content should be emitted to the user.
&chit.Response{
Content: "Here's your answer.",
Metadata: map[string]any{"tokens": 150},
}
Yield
Processing is paused, awaiting more input. The prompt should be emitted, and the continuation stored for later.
&chit.Yield{
Prompt: "What's your email address?",
Continuation: func(ctx context.Context, email string) (chit.Result, error) {
// Resume processing with the email
return &chit.Response{Content: "Thanks, " + email}, nil
},
}
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.
Continuation
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 continuation
- Instead of calling the processor, it calls the continuation
- The 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
Emitter
The Emitter interface handles all output to the client. It has two channels:
Emit — Streaming Text
Emit(ctx context.Context, msg Message) error
Used for conversational content. Chat calls this with Response content or Yield prompts.
Push — Structured Resources
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)
emitter.Push(ctx, chit.Resource{
Type: "data",
URI: "db://users/123",
Payload: userData,
})
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
Session
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 processor
- Adds assistant response after processing
- Applies system prompt at creation
You can also provide your own session via WithSession() for session persistence or pre-populated history.
Signals
Chit emits lifecycle signals via capitan for observability:
| Signal | When |
|---|---|
ChatCreated | New chat instantiated |
InputReceived | User input received |
ProcessingStarted | Processor called |
ProcessingCompleted | Processor returned successfully |
ProcessingFailed | Processor returned error |
ResponseEmitted | Response sent to emitter |
TurnYielded | Processing yielded for input |
TurnResumed | Continuation resumed |
Subscribe to these for logging, metrics, tracing, or debugging.
See: Signals reference
Next Steps
- Architecture — how the lifecycle flows internally
- API Reference — function signatures and behavior
- Types Reference — complete type documentation