zoobzio January 14, 2025 Edit this page

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.

See: Result types reference

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:

  1. Chat detects it has a stored continuation
  2. Instead of calling the processor, it calls the continuation
  3. 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:

SignalWhen
ChatCreatedNew chat instantiated
InputReceivedUser input received
ProcessingStartedProcessor called
ProcessingCompletedProcessor returned successfully
ProcessingFailedProcessor returned error
ResponseEmittedResponse sent to emitter
TurnYieldedProcessing yielded for input
TurnResumedContinuation resumed

Subscribe to these for logging, metrics, tracing, or debugging.

See: Signals reference

Next Steps