zoobzio January 14, 2025 Edit this page

API Reference

Constructor

New

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,
    chit.WithSystemPrompt("You are helpful."),
    chit.WithMetadata(map[string]any{"user": "123"}),
)

Chat Methods

Handle

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 start
  • ProcessingStarted — before calling processor/continuation
  • TurnResumed — if resuming from continuation
  • ProcessingCompleted — on success
  • ProcessingFailed — on error
  • ResponseEmitted — when emitting response
  • TurnYielded — when storing continuation
err := chat.Handle(ctx, "Hello!")
if err != nil {
    log.Printf("handle failed: %v", err)
}

ID

func (c *Chat) ID() string

Returns the unique identifier for this Chat. Generated via uuid.New() at creation.

chatID := chat.ID()
log.Printf("Chat %s started", chatID)

Session

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()
for _, msg := range messages {
    fmt.Printf("%s: %s\n", msg.Role, msg.Content)
}

Config

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 != "" {
    log.Printf("Using system prompt: %s", chat.Config().SystemPrompt)
}

HasContinuation

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() {
    fmt.Println("Waiting for user response...")
}

GetPipeline

func (c *Chat) GetPipeline() pipz.Chainable[*ChatRequest]

Returns the internal pipeline for composition.

Implements ChatProvider for use with WithFallback.

fallbackChat := chit.New(fallbackProcessor, emitter)
mainChat := chit.New(processor, emitter,
    chit.WithFallback(fallbackChat),
)

Configuration Options

WithConfig

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{
    SystemPrompt: "Be concise.",
    Metadata:     map[string]any{"version": "2.0"},
}))

WithSystemPrompt

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,
    chit.WithSystemPrompt("You are a helpful assistant."),
)

WithSession

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()
session.Append("user", "Previous message")

chat := chit.New(processor, emitter, chit.WithSession(session))

WithMetadata

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{
    "user_id":    "123",
    "request_id": "abc-def",
}))

Pipeline Options

WithRetry

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))

WithBackoff

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))

WithTimeout

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))

WithCircuitBreaker

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))

WithRateLimit

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))

WithErrorHandler

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 {
    log.Printf("Error: %v", err.Err)
    return nil
})
chat := chit.New(processor, emitter, chit.WithErrorHandler(errorHandler))

WithFallback

func WithFallback(fallback ChatProvider) Option

Adds a fallback processor for resilience. If the primary fails, the fallback is tried.

fallback := chit.New(fallbackProcessor, emitter)
chat := chit.New(processor, emitter, chit.WithFallback(fallback))

WithMiddleware

func WithMiddleware(processors ...pipz.Chainable[*ChatRequest]) Option

Adds pre-processing steps before the terminal. Processors run in order.

chat := chit.New(processor, emitter,
    chit.WithMiddleware(
        chit.UseValidation(4096),
        chit.UseLogging(logger),
    ),
)

Middleware Helpers

UseValidation

func UseValidation(maxLength int) pipz.Chainable[*ChatRequest]

Creates middleware that validates input length. Returns error if input exceeds maxLength or is empty.

UseLogging

func UseLogging(logger *slog.Logger) pipz.Chainable[*ChatRequest]

Creates middleware that logs requests using slog.

UseMetrics

func UseMetrics(recorder MetricsRecorder) pipz.Chainable[*ChatRequest]

Creates middleware that records metrics via the MetricsRecorder interface.

UseEnrich

func UseEnrich(enricher Enricher) pipz.Chainable[*ChatRequest]

Creates middleware that enriches requests. Best-effort: returns original on error.

UseApply

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.

UseEffect

func UseEffect(id pipz.Identity, fn func(context.Context, *ChatRequest) error) pipz.Chainable[*ChatRequest]

Creates custom middleware using pipz.Effect. Side effect only.

UseTransform

func UseTransform(id pipz.Identity, fn func(context.Context, *ChatRequest) *ChatRequest) pipz.Chainable[*ChatRequest]

Creates custom middleware using pipz.Transform. Cannot fail.

UseMutate

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.

Context Helpers

WithEmitter

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)
result, err := processor.Process(ctx, input, session)

EmitterFromContext

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 {
    emitter.Push(ctx, chit.Resource{
        Type: "data",
        URI:  "db://users/123",
    })
}

Next Steps