core

package
v1.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 20, 2026 License: MIT Imports: 19 Imported by: 0

Documentation

Overview

Package core provides a conversation engine for LLM-powered agents.

Core concepts:

  • Agent: the main entry point. Create with NewAgent(), run queries with Run().
  • Provider: interface for LLM backends. Implement Chat() and ChatStream().
  • ToolExecutor: interface for tool execution. Implement GetTools() and Execute().
  • UI: optional interface for interactive output. Use NoopUI for headless mode.
  • EventPublisher: optional interface for event-driven output.

Quick start:

agent, err := core.NewAgent(core.Options{
    Provider: &myProvider{},
    Executor: core.NoopExecutor,
})
result, err := agent.Run(ctx, "Hello")

The core package has zero external dependencies. The events and internal/test packages are optional.

Index

Constants

View Source
const (
	// EventTypeQueryStarted is published when a new query begins.
	// Data map keys: "query" (string), "model" (string)
	EventTypeQueryStarted = "query_started"
	// EventTypeQueryCompleted is published when a query finishes.
	// Data map keys: "query", "response", "tokens", "cost", "duration_ms"
	EventTypeQueryCompleted = "query_completed"
	// EventTypeError is published on errors.
	// Data map keys: "message" (string), "error" (string)
	EventTypeError = "error"
	// EventTypeToolStart is published when a tool call begins.
	// Data map keys: "tool_name", "tool_call_id", "arguments", "tool_index"
	EventTypeToolStart = "tool_start"
	// EventTypeToolEnd is published when a tool call ends.
	// Data map keys: "tool_call_id", "tool_name", "status", "result", "duration_ms"
	EventTypeToolEnd = "tool_end"
	// EventTypeStreamChunk is published for each streaming content chunk.
	// Data map keys: "chunk" (string), "content_type" (string: "text"|"reasoning")
	EventTypeStreamChunk = "stream_chunk"
	// EventTypeMetricsUpdate is published with token usage updates.
	// Data map keys: "total_tokens", "context_tokens", "max_context_tokens", "iteration", "total_cost"
	EventTypeMetricsUpdate = "metrics_update"
	// EventTypeCompaction is published when context compaction occurs.
	// Data map keys: "strategy", "messages_before", "messages_after", "message_count_delta", "tokens_saved"
	EventTypeCompaction = "compaction"
)

Generic event type constants used by the core package. These are the only event types that any consumer of core needs to handle.

View Source
const (
	// StructuralRecentToKeep is the number of trailing messages preserved
	// intact across a structural compaction pass. The most recent causal chain
	// must survive so the model can continue reasoning about the current step.
	StructuralRecentToKeep = 24

	// StructuralMinMessagesToCompact is the minimum total message count below
	// which structural compaction is skipped. Below this, the conversation is
	// short enough that compaction yields more loss than gain.
	StructuralMinMessagesToCompact = 30

	// StructuralMinMiddleMessages is the smallest middle-segment size that
	// justifies a summary rewrite. Smaller middles aren't worth the LLM call.
	StructuralMinMiddleMessages = 6

	// LayeredThreshold is the middle-segment size above which compaction
	// produces three graduated layers (brief / summary / detailed) instead of
	// a single summary, retaining more recent-middle detail.
	LayeredThreshold = 30

	// MinLayerSize is the minimum messages per layer in layered compaction.
	MinLayerSize = 10

	BriefWordLimit    = 150
	SummaryWordLimit  = 250
	DetailedWordLimit = 350
)

Structural compaction tuning. These mirror the values that previously lived in sprout's PruningConfig.Structural block and informed seed's chat-loop expectations.

View Source
const (
	// ToolStatusCompleted indicates the tool ran and returned a normal
	// result. This is the default published when Status is unset.
	ToolStatusCompleted = "completed"

	// ToolStatusError indicates the tool failed before or during execution.
	// Reasons include: argument parse errors, circuit-breaker rejections,
	// pre-execute hook rejections, handler errors, and execution timeouts.
	// The error detail is in Message.Content.
	ToolStatusError = "error"
)

Tool status values for the Message.Status field. Set by an Executor on the Message returned for a tool call; read by the chat loop when it publishes the EventTypeToolEnd event so consumers (CLI tool log, WebUI status badges) can distinguish successful executions from failures.

An empty Status (zero value) is treated as ToolStatusCompleted by the chat loop — Executors that don't tag results keep the historical "always completed" behavior.

View Source
const DefaultSystemPrompt = "You are a helpful assistant that can execute tools to complete tasks."

DefaultSystemPrompt is the minimal system prompt used when none is provided.

View Source
const MetaKeyCheckpoint = "checkpoint"

MetaKeyCheckpoint is the Meta key set on messages inserted by checkpoint compaction so they can be identified without string matching.

Variables

View Source
var (
	// ErrNoProvider is returned when NewAgent is called without a Provider.
	ErrNoProvider = errors.New("no provider configured")

	// ErrNoExecutor is returned when NewAgent is called without a ToolExecutor.
	ErrNoExecutor = errors.New("no tool executor configured")

	// ErrInterrupted is returned when the conversation is interrupted by the user.
	ErrInterrupted = errors.New("conversation interrupted")

	// ErrMaxIterations is returned when the maximum iteration count is exceeded.
	ErrMaxIterations = errors.New("maximum iterations exceeded")

	// ErrPaused is returned when Run is called while the agent is paused.
	ErrPaused = errors.New("agent is paused")

	// ErrZeroChoices is returned when the provider returns a valid response
	// with zero choices, indicating an empty completion.
	ErrZeroChoices = errors.New("provider returned zero choices")
)

Sentinel errors returned by the agent lifecycle.

Functions

func BuildCheckpointCompactedMessages

func BuildCheckpointCompactedMessages(messages []Message, checkpoints []TurnCheckpoint) ([]Message, []TurnCheckpoint)

BuildCheckpointCompactedMessages replaces consumed checkpoint ranges with summary messages and returns the compacted message list and updated checkpoints.

A checkpoint is "consumable" when: - Its StartIndex and EndIndex are valid (within the messages slice) - The messages in the range [StartIndex, EndIndex] exist - The checkpoint has a non-empty Summary

All checkpoint indices reference the raw state.Messages() slice. Because state.Messages() only appends (never inserts or deletes in the middle), checkpoint indices remain stable across calls. Consumed checkpoints are kept in the returned list so that every call to prepareMessages() can re-apply their summaries against the growing message history.

The function works from oldest to newest checkpoint (by StartIndex):

  1. For each consumable checkpoint, replace messages[StartIndex:EndIndex+1] with a single summary message (role "user", content from checkpoint.ActionableSummary if non-empty and ≤500 bytes, otherwise checkpoint.Summary)
  2. Handle consecutive-assistant boundaries: if the inserted summary message would create two consecutive assistant messages with no tool calls between them, merge or adjust.

The summary message role is "user" to maintain proper conversation flow (system -> user -> assistant pattern).

Return:

  • compactedMessages: the new message slice with checkpoints applied
  • updatedCheckpoints: all checkpoints preserved with their original indices (consumed checkpoints are kept so future calls re-apply their summaries)

func ClassifyError

func ClassifyError(err error, provider string) error

ClassifyError wraps a raw provider error in a typed error based on error message patterns. If err is nil or already a typed error, it is returned unchanged.

func IsAuthError

func IsAuthError(err error) bool

IsAuthError reports whether err is an AuthError.

func IsBlankResponse added in v0.1.1

func IsBlankResponse(err error) bool

IsBlankResponse reports whether err is a BlankResponseError.

func IsClientError added in v0.2.1

func IsClientError(err error) bool

IsClientError reports whether err is a ClientError.

func IsContentFiltered added in v0.1.1

func IsContentFiltered(err error) bool

IsContentFiltered reports whether err is a ContentFilteredError.

func IsContextOverflow

func IsContextOverflow(err error) bool

IsContextOverflow reports whether err is a ContextOverflowError.

func IsRateLimit

func IsRateLimit(err error) bool

IsRateLimit reports whether err is a RateLimitError.

func IsTransient

func IsTransient(err error) bool

IsTransient reports whether err is a TransientError.

func RecordTurnCheckpointAsync

func RecordTurnCheckpointAsync(state *State, messages []Message, startIndex, endIndex int, timeout time.Duration, onCheckpoint func(TurnCheckpoint))

RecordTurnCheckpointAsync asynchronously builds a checkpoint from the given messages and stores it in state. It spawns a goroutine to compute the summary so it doesn't block the conversation loop. The onCheckpoint callback, if non-nil, is invoked with the built checkpoint in its own goroutine after it is stored in state. If the callback panics, the panic is caught and the agent continues normally.

The message slice is snapshotted immediately (before the goroutine starts) so the background computation sees a consistent view even if the caller mutates the original slice. If the summary computation takes longer than timeout, a minimal checkpoint is stored instead.

Types

type Agent

type Agent struct {
	// contains filtered or unexported fields
}

Agent is the main entry point for the conversation engine.

func NewAgent

func NewAgent(opts Options) (*Agent, error)

NewAgent creates a new Agent from the given options. Returns an error if required options (Provider or ToolExecutor) are not provided.

func (*Agent) Checkpoints added in v1.0.0

func (a *Agent) Checkpoints() []TurnCheckpoint

Checkpoints returns a copy of all recorded turn checkpoints.

func (*Agent) ExportState

func (a *Agent) ExportState() ([]byte, error)

ExportState serializes the current state to JSON.

func (*Agent) ImportState

func (a *Agent) ImportState(data []byte) error

ImportState deserializes state from JSON.

func (*Agent) InjectInput

func (a *Agent) InjectInput(input string) bool

InjectInput injects a user message into the conversation via a buffered channel. Returns true if the input was accepted (queued for the next loop iteration), false if a prior injection is still pending. The injection is fire-and-forget with backpressure: a true return means the input is queued, but the caller has no guarantee it was consumed yet.

func (*Agent) Interrupt added in v1.0.0

func (a *Agent) Interrupt()

Interrupt cancels the currently running query, if any. This provides a way to stop the agent independently of the caller's context.

Interrupt() has no effect if no query is running. After a Run() call completes (whether normally or due to interruption), the interrupt is automatically reset for the next call.

If Run() returns ErrInterrupted, the caller can retry by calling Run() again — the interrupt has already been reset.

func (*Agent) IsPaused

func (a *Agent) IsPaused() bool

IsPaused returns whether the agent is paused.

func (*Agent) Pause

func (a *Agent) Pause()

Pause pauses the agent for user clarification.

func (*Agent) Provider

func (a *Agent) Provider() Provider

Provider returns the provider (for accessing Info, etc.).

func (*Agent) ReasoningBuffer

func (a *Agent) ReasoningBuffer() *StreamingBuffer

ReasoningBuffer returns the reasoning streaming buffer.

func (*Agent) ResetInterrupt added in v1.0.0

func (a *Agent) ResetInterrupt() context.Context

ResetInterrupt recreates the interrupt context. This is called automatically at the start of each Run() call, but can be called manually if needed. Returns the new interruptCtx so the caller can capture it atomically.

func (*Agent) Resume

func (a *Agent) Resume()

Resume resumes a paused agent.

func (*Agent) Run

func (a *Agent) Run(ctx context.Context, query string) (string, error)

Run executes a single query through the conversation loop.

The query can be cancelled in two ways:

  1. Cancelling the caller's context (standard Go pattern).
  2. Calling Interrupt() on this Agent (independent external cancellation).

Interrupt() resets after each Run() call, so it only affects the current query.

func (*Agent) RunStream

func (a *Agent) RunStream(ctx context.Context, query string) (string, error)

RunStream executes a single query through the streaming conversation loop. It uses provider.ChatStream() instead of provider.Chat(), so content is delivered incrementally via the StreamHandler callbacks. The return value is the final response content extracted from conversation state.

Like Run, this respects both the caller's context cancellation and Interrupt().

func (*Agent) SetFlushCallback

func (a *Agent) SetFlushCallback(fn func())

SetFlushCallback sets a callback to flush streaming output.

func (*Agent) SetProvider

func (a *Agent) SetProvider(p Provider)

SetProvider swaps the provider at runtime. The new provider will be used for all subsequent calls. It is safe to call between queries; calling during an active query may cause undefined behavior. Panics if p is nil — use NewAgent to create an agent without a provider and then call SetProvider with a non-nil provider.

func (*Agent) SetSystemPrompt

func (a *Agent) SetSystemPrompt(prompt string)

SetSystemPrompt updates the system prompt for future queries.

func (*Agent) State

func (a *Agent) State() *State

State returns the agent's conversation state.

func (*Agent) Steer

func (a *Agent) Steer(msg Message)

Steer queues a transient message that will be appended to the next API call made by this agent. The message is consumed once and is not persisted in the conversation state. Use Steer to inject temporary guidance (e.g., "Focus on security concerns" or "Respond in JSON format") that should influence only the next model response.

Steer must be called between Run() calls. If called during an active Run(), the message is queued and will not be consumed until the next Run().

Example:

agent.Steer(core.Message{Role: "user", Content: "Focus on performance."})

func (*Agent) SteerSystem

func (a *Agent) SteerSystem(content string)

SteerSystem is a convenience for injecting a system-level steering message. It creates a Message with Role "system" and queues it via Steer, so the guidance takes effect on the next API call and is consumed once (like all steer messages). The message is not persisted in the conversation state.

Example:

agent.SteerSystem("Focus on performance, not correctness.")

func (*Agent) StreamingBuffer

func (a *Agent) StreamingBuffer() *StreamingBuffer

StreamingBuffer returns the content streaming buffer.

type AgentState

type AgentState struct {
	Messages         []Message        `json:"messages"`
	SessionID        string           `json:"session_id"`
	TotalTokens      int              `json:"total_tokens"`
	TotalCost        float64          `json:"total_cost"`
	PromptTokens     int              `json:"prompt_tokens"`
	CompletionTokens int              `json:"completion_tokens"`
	Checkpoints      []TurnCheckpoint `json:"checkpoints,omitempty"`
}

AgentState tracks the state of an agent's conversation.

type AgentStreamHandler

type AgentStreamHandler struct {
	// contains filtered or unexported fields
}

AgentStreamHandler is a concrete StreamHandler implementation that writes streamed content to the agent's buffers and publishes events via the agent's eventPublisher.

func NewAgentStreamHandler

func NewAgentStreamHandler(agent *Agent, state *State) *AgentStreamHandler

NewAgentStreamHandler creates a new StreamHandler for the given agent.

func (*AgentStreamHandler) OnContent

func (h *AgentStreamHandler) OnContent(content string)

OnContent handles a streaming content chunk: writes to the stream buffer, delegates to OutputManager.PublishOutput for event publishing, and invokes the flush callback. Empty content chunks are silently ignored.

func (*AgentStreamHandler) OnDone

func (h *AgentStreamHandler) OnDone(resp *ChatResponse)

OnDone handles the end of streaming: records token usage, publishes metrics, and syncs the accumulated streaming buffer content back into the ChatResponse so that finalize() (which reads resp.ToMessage()) returns the same content the buffer accumulated. This eliminates the fragility where provider.ChatStream builds a different response than what OnContent deltas produced.

func (*AgentStreamHandler) OnError

func (h *AgentStreamHandler) OnError(err error)

OnError handles streaming errors: publishes an error event via the bus.

func (*AgentStreamHandler) OnReasoning

func (h *AgentStreamHandler) OnReasoning(reasoning string)

OnReasoning handles a streaming reasoning chunk: writes to the reasoning buffer, delegates to OutputManager.PublishOutput for event publishing, and invokes the flush callback. Empty reasoning chunks are silently ignored.

func (*AgentStreamHandler) Response

func (h *AgentStreamHandler) Response() *ChatResponse

Response returns the final ChatResponse from OnDone, or nil if not yet set.

type AuthError

type AuthError struct {
	// Provider is the provider that returned the auth error.
	Provider string
	// Wrapped is the underlying error.
	Wrapped error
}

AuthError indicates authentication failure with a provider.

func (*AuthError) Error

func (e *AuthError) Error() string

func (*AuthError) Unwrap

func (e *AuthError) Unwrap() error

type BlankResponseError added in v0.1.1

type BlankResponseError struct {
	// Provider is the provider that produced the blank responses.
	Provider string
	// Count is the number of consecutive blank/repetitive responses.
	Count int
}

BlankResponseError indicates the model produced consecutive blank or repetitive responses and the conversation was force-finalized.

func (*BlankResponseError) Error added in v0.1.1

func (e *BlankResponseError) Error() string

type ChatChoice

type ChatChoice struct {
	Index        int     `json:"index"`
	Message      Message `json:"message"`
	FinishReason string  `json:"finish_reason"`
}

ChatChoice represents one possible completion from the model.

type ChatRequest

type ChatRequest struct {
	Model      string    `json:"model,omitempty"`
	Messages   []Message `json:"messages"`
	Tools      []Tool    `json:"tools,omitempty"`
	ToolChoice string    `json:"tool_choice,omitempty"`
	MaxTokens  int       `json:"max_tokens,omitempty"`
	Reasoning  string    `json:"reasoning,omitempty"`
	Stream     bool      `json:"stream,omitempty"`
}

ChatRequest is a request to the chat completion endpoint.

type ChatResponse

type ChatResponse struct {
	ID      string       `json:"id"`
	Object  string       `json:"object,omitempty"`  // e.g. "chat.completion"
	Created int64        `json:"created,omitempty"` // Unix timestamp
	Model   string       `json:"model"`
	Choices []ChatChoice `json:"choices"`
	Usage   ChatUsage    `json:"usage"`
}

ChatResponse is a response from the chat completion endpoint.

func (ChatResponse) ToMessage

func (r ChatResponse) ToMessage() Message

ToMessage returns the first choice's message from the response, or an empty message.

type ChatUsage

type ChatUsage struct {
	PromptTokens     int     `json:"prompt_tokens"`
	CompletionTokens int     `json:"completion_tokens"`
	TotalTokens      int     `json:"total_tokens"`
	EstimatedCost    float64 `json:"estimated_cost,omitempty"`
	Cost             float64 `json:"cost,omitempty"`
	// CachedTokens is the number of prompt tokens served from cache (OpenRouter).
	CachedTokens int `json:"cached_tokens,omitempty"`
	// CacheWriteTokens is the number of prompt tokens written to cache (OpenRouter).
	CacheWriteTokens *int `json:"cache_write_tokens,omitempty"`
}

ChatUsage tracks token usage and cost for a request/response.

type ClientError added in v0.2.1

type ClientError struct {
	// Provider is the provider that returned the error.
	Provider string
	// Wrapped is the underlying error.
	Wrapped error
}

ClientError indicates the request was invalid or cannot be fulfilled (e.g., HTTP 400 Bad Request, 404 Not Found, 422 Unprocessable Entity). These errors are permanent — retrying will not help.

func (*ClientError) Error added in v0.2.1

func (e *ClientError) Error() string

func (*ClientError) Unwrap added in v0.2.1

func (e *ClientError) Unwrap() error

type CompactionResult

type CompactionResult struct {
	Messages     []Message
	Strategy     string // "none", "tool_trim", "checkpoint_drop", "truncation", or "emergency"
	TokensBefore int
	TokensAfter  int
}

CompactionResult holds the output of a compaction operation along with metadata about what strategy was used and how much was saved.

func Compact added in v0.1.7

func Compact(messages []Message, tokenLimit int) CompactionResult

Compact reduces messages to fit within the token limit. If under the limit or too few messages, returns as-is. Otherwise tries dropping checkpoint summaries, then turns, then falls back to emergency truncation.

func (CompactionResult) MessageCountDelta

func (r CompactionResult) MessageCountDelta(before int) int

MessageCountDelta returns how many messages were removed.

func (CompactionResult) TokensSaved

func (r CompactionResult) TokensSaved() int

TokensSaved returns the estimated tokens saved by compaction.

type ContentFilteredError added in v0.1.1

type ContentFilteredError struct {
	// Provider is the provider that returned the content filter.
	Provider string
}

ContentFilteredError indicates the provider's content filter blocked the response after a retry attempt was already made.

func (*ContentFilteredError) Error added in v0.1.1

func (e *ContentFilteredError) Error() string

type ContextOverflowError

type ContextOverflowError struct {
	// TokensUsed is the number of tokens that were estimated or used.
	TokensUsed int
	// TokensLimit is the provider's context window limit.
	TokensLimit int
	// Wrapped is the underlying error.
	Wrapped error
}

ContextOverflowError indicates the context window is exceeded.

func (*ContextOverflowError) Error

func (e *ContextOverflowError) Error() string

func (*ContextOverflowError) Unwrap

func (e *ContextOverflowError) Unwrap() error

type ConversationHandler

type ConversationHandler struct {
	// contains filtered or unexported fields
}

ConversationHandler manages the high-level conversation flow.

func (*ConversationHandler) ProcessQuery

func (ch *ConversationHandler) ProcessQuery(ctx context.Context, query string) (string, error)

ProcessQuery handles a user query through the complete conversation flow.

func (*ConversationHandler) ProcessQueryStream

func (ch *ConversationHandler) ProcessQueryStream(ctx context.Context, query string) (string, error)

ProcessQueryStream handles a user query through the streaming conversation flow. It uses provider.ChatStream() instead of provider.Chat(), so content is delivered incrementally via StreamHandler callbacks. The streaming buffer is populated as content arrives. The return value is the final response content extracted from conversation state, just like ProcessQuery.

type ConversationOptimizer

type ConversationOptimizer struct {
	// contains filtered or unexported fields
}

ConversationOptimizer reduces redundant conversation history by deduplicating repeated file reads and transient shell command outputs. It mutates the provided message slice in place (replacing tool-result Content with placeholders) but is safe to use because prepareMessages() only ever passes it ephemeral copies of state — the stored conversation is never modified.

func NewConversationOptimizer

func NewConversationOptimizer(opts ConversationOptimizerOptions) *ConversationOptimizer

NewConversationOptimizer creates a new optimizer from the given options.

func (*ConversationOptimizer) OptimizeConversation

func (opt *ConversationOptimizer) OptimizeConversation(messages []Message) []Message

OptimizeConversation processes the message list, replacing redundant file reads and shell command outputs with compact placeholders, then masking large consumed tool results to bound context bloat from chatty tools. Returns the (possibly modified) message slice.

type ConversationOptimizerOptions

type ConversationOptimizerOptions struct {
	// Enabled enables optimization. When false, OptimizeConversation is a no-op.
	Enabled bool
	// KnownToolFn classifies tool names. Return ToolCategoryUnknown to skip a tool.
	// If nil, the optimizer treats all tools as unknown (no optimization).
	// Must be deterministic: the same tool name should always return the same category.
	KnownToolFn func(name string) ToolCategory
}

ConversationOptimizerOptions configures the optimizer.

type ConversationPruner added in v1.1.0

type ConversationPruner struct {
	// contains filtered or unexported fields
}

ConversationPruner reduces conversation history according to a configured strategy. Construct via NewConversationPruner; call Prune from the chat loop when the optimizer/structural-compaction pair couldn't bring usage below the trigger threshold.

func NewConversationPruner added in v1.1.0

func NewConversationPruner(opts PrunerOptions) *ConversationPruner

NewConversationPruner builds a pruner from the supplied options, falling back to seed defaults for any zero-valued field.

func (*ConversationPruner) MinMessagesToKeep added in v1.1.0

func (cp *ConversationPruner) MinMessagesToKeep() int

MinMessagesToKeep returns the floor below which no strategy is allowed to reduce the message list.

func (*ConversationPruner) Prune added in v1.1.0

func (cp *ConversationPruner) Prune(ctx context.Context, messages []Message, currentTokens, maxTokens int, opts PruneCallOptions) []Message

Prune reduces messages according to the configured strategy and returns the pruned slice. Always preserves the system message and at least cp.minMessagesToKeep messages.

func (*ConversationPruner) RecentMessagesToKeep added in v1.1.0

func (cp *ConversationPruner) RecentMessagesToKeep() int

RecentMessagesToKeep returns how many recent messages the pruner protects from removal across all strategies.

func (*ConversationPruner) SetRecentMessagesToKeep added in v1.1.0

func (cp *ConversationPruner) SetRecentMessagesToKeep(n int)

SetRecentMessagesToKeep overrides the recent-preservation window at runtime.

func (*ConversationPruner) SetSlidingWindowSize added in v1.1.0

func (cp *ConversationPruner) SetSlidingWindowSize(n int)

SetSlidingWindowSize overrides the sliding-window strategy size.

func (*ConversationPruner) SetStrategy added in v1.1.0

func (cp *ConversationPruner) SetStrategy(s PruningStrategy)

SetStrategy overrides the strategy at runtime. Useful for tests and for consumers that want to flip strategy on the fly.

func (*ConversationPruner) SetThreshold added in v1.1.0

func (cp *ConversationPruner) SetThreshold(t float64)

SetThreshold overrides the trigger fraction at runtime. Values outside (0, 1) are ignored.

func (*ConversationPruner) ShouldPrune added in v1.1.0

func (cp *ConversationPruner) ShouldPrune(currentTokens, maxTokens int) bool

ShouldPrune reports whether the configured strategy and current usage suggest a prune pass is needed. Wired into the chat loop's threshold check so callers can sequence: trigger → optimize → (still over?) → ShouldPrune? → Prune.

func (*ConversationPruner) SlidingWindowSize added in v1.1.0

func (cp *ConversationPruner) SlidingWindowSize() int

SlidingWindowSize returns the configured window size used by the sliding-window strategy.

func (*ConversationPruner) Strategy added in v1.1.0

func (cp *ConversationPruner) Strategy() PruningStrategy

Strategy returns the pruner's currently configured strategy.

func (*ConversationPruner) Threshold added in v1.1.0

func (cp *ConversationPruner) Threshold() float64

Threshold returns the configured trigger fraction (0–1).

type EventPublisher

type EventPublisher interface {
	Publish(eventType string, data any)
}

EventPublisher is the interface for publishing events. It is implemented by events.EventBus and can be satisfied by any custom event system, allowing seed to be used without the events package.

type ExponentialBackoff

type ExponentialBackoff struct {
	InitialDelay time.Duration // delay for the first retry
	MaxDelay     time.Duration // cap on delay growth (0 = no cap)
	Multiplier   float64       // exponential growth factor (typically > 1)
	MaxAttempts  int           // total number of attempts (0 = unlimited)
	Jitter       float64       // jitter: 0 = none, (0,1) = partial [base, base*(1+jitter)], >= 1 = full jitter [0, base]
	// contains filtered or unexported fields
}

ExponentialBackoff implements exponential backoff with jitter for retry logic. It is NOT safe for concurrent use by multiple goroutines.

Usage:

backoff := &ExponentialBackoff{InitialDelay: 100 * time.Millisecond, MaxDelay: 10 * time.Second, MaxAttempts: 5}
for backoff.NextAttempt() {
    time.Sleep(backoff.Delay())
    if ok := doWork(); ok {
        break
    }
}

func NewExponentialBackoff

func NewExponentialBackoff(initialDelay, maxDelay time.Duration, multiplier float64, maxAttempts int, jitter float64) *ExponentialBackoff

NewExponentialBackoff creates a configured ExponentialBackoff with a global random source. Pass a custom randSource to make jitter deterministic in tests.

func (*ExponentialBackoff) Delay

func (b *ExponentialBackoff) Delay() time.Duration

Delay returns the current delay duration with jitter applied. Jitter adds a random percentage of the base delay to prevent thundering herd. If Jitter is in (0, 1), the delay is [base, base * (1 + Jitter)]. If Jitter is >= 1, full jitter mode replaces delay with [0, base]. Panics if called before NextAttempt() or if randSrc is nil.

func (*ExponentialBackoff) NextAttempt

func (b *ExponentialBackoff) NextAttempt() bool

NextAttempt advances to the next retry and returns true if the attempt count has not exceeded MaxAttempts. Returns false immediately if MaxAttempts is already reached or exceeded.

func (*ExponentialBackoff) Reset

func (b *ExponentialBackoff) Reset()

Reset resets the backoff state so it can be reused for a new retry sequence.

func (*ExponentialBackoff) WithRand

func (b *ExponentialBackoff) WithRand(rs randSource) *ExponentialBackoff

WithRand sets a custom random source (useful for deterministic tests).

type FallbackParseResult

type FallbackParseResult struct {
	ToolCalls      []ToolCall
	CleanedContent string
}

FallbackParseResult contains extracted tool calls and cleaned content.

type FallbackParser

type FallbackParser struct {
	// contains filtered or unexported fields
}

FallbackParser extracts tool calls from malformed LLM response content.

func NewFallbackParser

func NewFallbackParser(opts FallbackParserOptions) *FallbackParser

NewFallbackParser creates a new FallbackParser with the given options.

func (*FallbackParser) Parse

func (fp *FallbackParser) Parse(content string) *FallbackParseResult

Parse extracts tool calls from malformed LLM response content.

func (*FallbackParser) ShouldUseFallback

func (fp *FallbackParser) ShouldUseFallback(content string, hasStructuredToolCalls bool) bool

ShouldUseFallback returns true when structured tool_calls are missing and the content contains patterns suggestive of tool calls.

It uses a three-tier confidence model:

  • Tier 1: Strong patterns (code fences, XML tags, quoted JSON keys) trigger immediately with a single match.
  • Tier 2a: Weak patterns (bare keywords like "name:" or "arguments:") require at least two independent matches to avoid false positives on normal conversational text.
  • Tier 2b: One weak pattern plus a JSON structure marker ({" or [") triggers a single weak match, catching patterns like: "name: search\n{\"query\": \"hello\"}"

type FallbackParserOptions

type FallbackParserOptions struct {
	// KnownToolNames returns true if the given name is a registered tool.
	// When nil, all extracted tool names are accepted.
	KnownToolNames func(string) bool
	// Debug enables verbose logging (printed to stderr).
	Debug bool
}

FallbackParserOptions configures the parser.

type ImageData

type ImageData struct {
	URL    string `json:"url,omitempty"`
	Base64 string `json:"base64,omitempty"`
	Type   string `json:"type,omitempty"` // MIME type (image/jpeg, image/png, etc.)
}

ImageData represents an image to include in a message. Images are typically provided as base64-encoded strings.

type LLMSummarizer added in v1.1.0

type LLMSummarizer func(ctx context.Context, messages []Message, hint SummarizerHint) (string, error)

LLMSummarizer is the seed extension point for LLM-based structural compaction. Consumers (like sprout) provide an implementation that calls their LLM to compress a window of older conversation history into a single summary string.

The function receives the messages to summarize (read-only — implementations must not mutate the slice) and a SummarizerHint shaping the response. It returns the raw summary text — seed wraps it into the standard "Compacted earlier conversation state:" header before splicing into history.

A nil summarizer disables LLM-based structural compaction; seed then falls back to its rule-based compaction (drop oldest turns + emergency truncate).

Implementations should:

  • Honor ctx cancellation. Long-running summary calls block the chat loop.
  • Return ("", nil) if the summary would be empty or low-value; seed treats this as "skip LLM summary, fall through to structural compaction".
  • Return (s, err) with err non-nil if the underlying call failed; seed logs and falls back to rule-based compaction.

type Message

type Message struct {
	Role             string            `json:"role"`
	Content          string            `json:"content"`
	ReasoningContent string            `json:"reasoning_content,omitempty"`
	ToolCallID       string            `json:"tool_call_id,omitempty"`
	ToolCalls        []ToolCall        `json:"tool_calls,omitempty"`
	Images           []ImageData       `json:"images,omitempty"`
	Meta             map[string]string `json:"-"`

	// Status, when set on a Message returned from a ToolExecutor, indicates
	// how a tool call resolved. Use the ToolStatus* constants. The chat loop
	// reads this when publishing EventTypeToolEnd so consumers can render
	// success vs error indicators. Empty defaults to ToolStatusCompleted —
	// keeps the field opt-in for Executors that don't tag results.
	Status string `json:"status,omitempty"`
}

Message represents a single message in a conversation.

func DropOldestCheckpointSummaries added in v0.1.7

func DropOldestCheckpointSummaries(messages []Message, targetTokens int) []Message

DropOldestCheckpointSummaries drops checkpoint summary messages (oldest first) until the token count is at or below targetTokens, or there are no more removable checkpoint messages outside the recent boundary.

It uses defaultRecentToKeep as the recent boundary: messages within the last defaultRecentToKeep positions are never dropped.

This function works on a copy and never mutates the original slice.

func DropOldestTurns added in v0.1.8

func DropOldestTurns(messages []Message, targetTokens int) []Message

DropOldestTurns drops complete turns (user + assistant + tool chain) oldest first until the token count is at or below targetTokens.

It uses defaultRecentToKeep as the recent boundary and works on a copy of the input slice. Falls back to individual message dropping when no complete turns remain.

func ToolErrorMessage added in v1.1.2

func ToolErrorMessage(toolCallID, toolName string, errMsg string) Message

ToolErrorMessage creates a Message for a failed tool result. Status is set to ToolStatusError so the chat loop's tool_end event publishes the "error" status and consumers (CLI, WebUI) can render error indicators. errMsg becomes the Message Content — keep it concise and human-readable; the LLM sees it as the tool's output.

func ToolResultMessage added in v0.1.6

func ToolResultMessage(toolCallID, toolName string, content string) Message

ToolResultMessage creates a Message for a successful tool result. The returned Message has Status set to ToolStatusCompleted so the chat loop's tool_end event carries the right status without callers having to remember to tag it.

func (*Message) SetMeta added in v0.1.7

func (m *Message) SetMeta(key, value string)

SetMeta sets a key-value pair in the Meta map, initializing it if nil.

type MessageImportance added in v1.1.0

type MessageImportance struct {
	Index           int
	Role            string
	IsUserQuery     bool
	HasToolCalls    bool
	IsToolResult    bool
	IsError         bool
	ContentLength   int
	TokenEstimate   int
	Age             int     // distance from the end of the slice
	ImportanceScore float64 // 0 (drop) to 1 (always keep)
}

MessageImportance is the scored representation of a single message. Public because it appears in the return path of the importance scorer and is useful for tests and external diagnostics.

type NormalizedToolCalls

type NormalizedToolCalls []ToolCall

NormalizedToolCalls is a slice of ToolCall values that have been validated and cleaned by the ToolCallNormalizer. It is a distinct type to make it clear in API signatures which calls have been normalized.

type Options

type Options struct {
	Provider       Provider     // required — LLM communication
	Executor       ToolExecutor // required — tool execution
	UI             UI           // nil = headless
	SystemPrompt   string       // empty = minimal default for testing
	MaxIterations  int          // 0 = unlimited
	MaxTokens      int          // 0 = use provider default
	Debug          bool
	EventPublisher EventPublisher // nil = no events
	// OnIteration is an optional callback invoked synchronously at the start of
	// each conversation-loop iteration. It receives the iteration number (0-based),
	// the current message count in state, the estimated token count for the prompt,
	// and the model's context window size. The token estimate is computed from the
	// prepared message list (before any compaction). The agent does not await a
	// result or handle errors from this callback; if the callback panics, the
	// panic is caught and logged (the agent continues).
	OnIteration func(iteration int, messages int, tokenEstimate int, contextSize int)
	// Optimizer is used to optimize conversation history across iterations.
	Optimizer   *ConversationOptimizer
	RetryConfig RetryConfig // retry behavior for transient errors; zero values use defaults
	// CompactionTriggerFraction is the share of the model's context window
	// above which the chat loop runs proactive compaction before sending the
	// next request. Valid range is (0, 1]; zero (the default) uses
	// defaultCompactionTriggerFraction (0.85). Set to 1.0 to keep the legacy
	// "only compact when the estimate already exceeds the window" behavior.
	CompactionTriggerFraction float64
	// LLMSummarizer, if non-nil, enables LLM-based structural compaction. The
	// compaction pipeline calls it to compress a window of older middle history
	// into a single summary message, preserving intent better than the
	// rule-based drop/truncate fallback. See the LLMSummarizer doc for the
	// contract. When nil, seed uses its rule-based compaction only.
	LLMSummarizer LLMSummarizer
	// Pruner, if non-nil, replaces the default rule-based compaction with the
	// configured pruning strategy (sliding-window / importance / hybrid /
	// adaptive). The pruner runs alongside the optimizer and LLMSummarizer.
	// When nil, seed uses its built-in Compact() pipeline.
	Pruner *ConversationPruner
	// InitialMessages seeds the agent's conversation state with pre-existing
	// messages (e.g., from a previous query in the same session). When empty,
	// the agent starts with a blank slate.
	InitialMessages []Message
	// DisableFallbackParser disables the fallback tool-call parser. When
	// disabled, malformed tool calls in model responses will not be recovered.
	// Default (false): fallback parser is enabled when tools are configured.
	DisableFallbackParser bool
	// DisableValidator disables the response validator (truncation/tentative
	// detection). When disabled, incomplete responses will not trigger
	// automatic continuation. Default (false): validator is enabled.
	DisableValidator bool
	// DisableNormalizer disables the tool call normalizer. When disabled,
	// structured tool calls will not be cleaned before execution. Default
	// (false): normalizer is enabled.
	DisableNormalizer bool
	// OnCheckpoint is an optional fire-and-forget callback invoked synchronously
	// after each completed turn. It receives the TurnCheckpoint summarizing what
	// happened in the turn. The callback is invoked immediately after the
	// checkpoint is built and stored in state, before finalize() returns.
	// If the callback panics, the panic is caught and the agent continues
	// normally.
	OnCheckpoint func(TurnCheckpoint)
}

Options configures an Agent.

type OutputEvent

type OutputEvent struct {
	Type      string // "content", "reasoning", "tool_result", "agent_message", "error"
	Content   string
	Source    string // origin of the output (e.g., "stream", "provider", "tool")
	Timestamp time.Time
	Metadata  map[string]string
}

OutputEvent represents a generic output event emitted through the async output channel.

type OutputManager

type OutputManager interface {
	// Buffer access
	ContentBuffer() *StreamingBuffer
	ReasoningBuffer() *StreamingBuffer

	// Flush callback management
	SetFlushCallback(fn func())
	Flush()

	// Async output channel for goroutine-safe background output delivery
	AsyncOutput() <-chan OutputEvent
	PublishOutput(event OutputEvent)

	// Event metadata (session ID, model, etc.) attached to output events
	SetEventMetadata(key string, value string)
	GetEventMetadata(key string) string

	// Reset clears all buffers and pending async output
	Reset()

	// Close shuts down the async output channel
	Close()
}

OutputManager manages all output streams from the agent, including content and reasoning buffers, async output delivery, flush callbacks, and event metadata. The eventPublisher parameter may be nil (no events) or any EventPublisher implementation.

func NewOutputManager

func NewOutputManager(eventBus EventPublisher) OutputManager

NewOutputManager creates a new OutputManager with the given optional event publisher.

type ParameterConfig added in v0.1.6

type ParameterConfig struct {
	Name         string
	Type         string
	Required     bool
	Alternatives []string
	Description  string
}

ParameterConfig defines a single parameter within a tool's schema.

type Provider

type Provider interface {
	// Chat sends a chat request and returns the response.
	Chat(ctx context.Context, req *ChatRequest) (*ChatResponse, error)

	// ChatStream sends a chat request and streams the response via the handler.
	ChatStream(ctx context.Context, req *ChatRequest, handler StreamHandler) error

	// Info returns metadata about the provider and its model.
	Info() ProviderInfo

	// EstimateTokens returns an approximate token count for the request.
	EstimateTokens(req *ChatRequest) int
}

Provider represents an LLM provider that can be used for chat completions.

type ProviderInfo

type ProviderInfo struct {
	Model       string `json:"model"`
	ContextSize int    `json:"context_size"`
	HasVision   bool   `json:"has_vision"`
}

ProviderInfo contains metadata about a provider and its model.

type PruneCallOptions added in v1.1.0

type PruneCallOptions struct {
	// Optimizer enables hybrid/adaptive paths to run dedup + observation
	// masking + LLM structural compaction before importance-based pruning.
	// May be nil.
	Optimizer *ConversationOptimizer

	// Summarizer is invoked by the hybrid/adaptive paths when they take the
	// LLM-summary route. May be nil; nil disables LLM summary inside the
	// pruner's pipelines.
	Summarizer LLMSummarizer

	// Provider names the LLM provider. Currently used only to switch the
	// importance-based path into the tool-call-aware variant for providers
	// with strict tool-call/result pairing requirements (Minimax, DeepSeek).
	Provider string

	// IsAgenticFlow signals that the caller is an autonomous loop that
	// benefits from a required-headroom guarantee after pruning. Affects
	// the final headroom-enforcement pass.
	IsAgenticFlow bool

	// RequiredAvailableTokens, if non-zero, overrides
	// defaultAgenticRequiredAvailable for the headroom pass. Only consulted
	// when IsAgenticFlow is true.
	RequiredAvailableTokens int
}

PruneCallOptions are passed per-call into Prune. They carry references to the optimizer + summarizer the pruner needs for the hybrid and adaptive strategies, plus context flags used to choose tactics.

type PrunerOptions added in v1.1.0

type PrunerOptions struct {
	Strategy             PruningStrategy
	ContextThreshold     float64 // fraction of max tokens that triggers pruning
	MinMessagesToKeep    int
	RecentMessagesToKeep int
	SlidingWindowSize    int
}

PrunerOptions configures a ConversationPruner. Zero values use seed defaults; only override fields you explicitly want to differ.

type PruningStrategy added in v1.1.0

type PruningStrategy string

PruningStrategy selects how the pruner reduces conversation history. The strategies trade off implementation complexity for content preservation: sliding-window is dumbest-and-fastest, adaptive is smartest-and-priciest.

const (
	// PruneStrategyNone disables pruning. ShouldPrune always returns false.
	PruneStrategyNone PruningStrategy = "none"
	// PruneStrategySlidingWindow keeps the system message plus the most
	// recent N messages. Cheap and predictable; loses all middle context.
	PruneStrategySlidingWindow PruningStrategy = "sliding_window"
	// PruneStrategyImportance scores every message and keeps the
	// highest-scoring subset within a token budget. Preserves anomalies
	// (errors, tool calls) better than sliding-window.
	PruneStrategyImportance PruningStrategy = "importance"
	// PruneStrategyHybrid runs the optimizer (dedup + observation masking +
	// LLM summary) first, then importance-based pruning over the result.
	PruneStrategyHybrid PruningStrategy = "hybrid"
	// PruneStrategyAdaptive inspects the conversation shape (length, tool
	// density, file-read footprint, current context usage) and dispatches to
	// whichever sub-strategy fits best. This is the recommended default.
	PruneStrategyAdaptive PruningStrategy = "adaptive"
)

type RateLimitError

type RateLimitError struct {
	// Provider is the provider that returned the rate limit error.
	Provider string
	// RetryAfter is a suggested delay before retrying (from Retry-After header or similar).
	RetryAfter time.Duration
	// Attempt is the request attempt number when the limit was hit.
	Attempt int
	// Wrapped is the underlying error.
	Wrapped error
}

RateLimitError indicates the provider has rate-limited requests.

func (*RateLimitError) Error

func (e *RateLimitError) Error() string

func (*RateLimitError) Unwrap

func (e *RateLimitError) Unwrap() error

type ResponseValidator

type ResponseValidator struct {
	// contains filtered or unexported fields
}

ResponseValidator inspects LLM response content for quality issues like truncation, tentativeness, or other patterns that suggest the response should not be finalized yet.

It has zero dependencies on Agent or concrete types — all input is passed explicitly and the DebugLog callback is optional.

func NewResponseValidator

func NewResponseValidator(opts ResponseValidatorOptions) *ResponseValidator

NewResponseValidator creates a new ResponseValidator with the given options.

func (*ResponseValidator) IsIncomplete

func (rv *ResponseValidator) IsIncomplete(content string) bool

IsIncomplete checks if a response appears to be incomplete or truncated. It returns true if any of these conditions are detected:

- Trailing "..." (ellipsis at end) - Abrupt ending (ends with comma, hyphen, or no punctuation on non-code/URL text) - Unusually short (<10 words and not a known complete-short answer) - Unclosed code blocks (odd number of ``` markers)

func (*ResponseValidator) LooksLikeTentativePostToolResponse

func (rv *ResponseValidator) LooksLikeTentativePostToolResponse(content string) bool

LooksLikeTentativePostToolResponse detects when the LLM has run tools but instead of giving a real response, it's just planning what to do next.

These "tentative" responses should trigger another loop iteration. A response is considered tentative when:

  • It is under 40 words (longer responses are considered substantive even if they start with planning language)
  • It starts with a planning prefix (case-insensitive), such as "Let me...", "I'll...", "I need to...", "I'm going to...", etc.

func (*ResponseValidator) LooksTruncated

func (rv *ResponseValidator) LooksTruncated(content string) bool

LooksTruncated checks if a response appears structurally truncated. It is a subset of IsIncomplete that excludes the shortness heuristic, making it safe for use in the conversation continuation loop where short but complete answers (e.g., "Done.") should not trigger a retry.

It returns true if any of these conditions are detected:

- Trailing "..." (ellipsis at end) - Abrupt ending (ends with comma or hyphen) - Unclosed code blocks (odd number of ``` markers)

type ResponseValidatorOptions

type ResponseValidatorOptions struct {
	// DebugLog is an optional callback for debug output. When nil,
	// debug logging is disabled.
	DebugLog func(format string, args ...interface{})
}

ResponseValidatorOptions configures a ResponseValidator.

type RetryConfig

type RetryConfig struct {
	// MaxAttempts is the total number of attempts (initial + retries).
	// Zero means use the default of 3. Setting to 1 means no retries
	// (only the initial attempt).
	MaxAttempts int

	// InitialDelay is the delay before the first retry.
	// Zero means use the default of 100ms.
	InitialDelay time.Duration

	// MaxDelay caps the exponential backoff growth.
	// Zero means use the default of 5s.
	MaxDelay time.Duration

	// Multiplier is the exponential growth factor.
	// Zero means use the default of 2.0.
	Multiplier float64

	// Jitter adds randomness to delays. 0 = none, (0,1) = partial, >=1 = full.
	// Zero means use the default of 0.0 (no jitter).
	Jitter float64
}

RetryConfig configures retry behavior for transient provider errors. Zero values use sensible defaults.

func (RetryConfig) InitialDelayOrDefault

func (rc RetryConfig) InitialDelayOrDefault() time.Duration

InitialDelayOrDefault returns InitialDelay or the default (100ms).

func (RetryConfig) JitterOrDefault

func (rc RetryConfig) JitterOrDefault() float64

func (RetryConfig) MaxAttemptsOrDefault

func (rc RetryConfig) MaxAttemptsOrDefault() int

MaxAttemptsOrDefault returns MaxAttempts or the default (3).

func (RetryConfig) MaxDelayOrDefault

func (rc RetryConfig) MaxDelayOrDefault() time.Duration

MaxDelayOrDefault returns MaxDelay or the default (5s).

func (RetryConfig) MultiplierOrDefault

func (rc RetryConfig) MultiplierOrDefault() float64

MultiplierOrDefault returns Multiplier or the default (2.0).

type State

type State struct {
	// contains filtered or unexported fields
}

State holds the conversation state for an agent.

func NewState

func NewState() *State

NewState creates a new State.

func (*State) AddCheckpoint

func (s *State) AddCheckpoint(cp TurnCheckpoint)

AddCheckpoint appends a turn checkpoint to the state.

func (*State) AddCost

func (s *State) AddCost(cost float64)

AddCost adds to the cost counter.

func (*State) AddMessage

func (s *State) AddMessage(msg Message)

AddMessage appends a message to the conversation.

func (*State) AddTokens

func (s *State) AddTokens(prompt, completion, total int)

AddTokens adds to the token counters.

func (*State) ClearCheckpoints

func (s *State) ClearCheckpoints()

ClearCheckpoints removes all checkpoints.

func (*State) EnsureSessionID

func (s *State) EnsureSessionID()

EnsureSessionID generates a session ID if not already set.

func (*State) ExportState

func (s *State) ExportState() ([]byte, error)

ExportState serializes the state to JSON.

func (*State) GetCheckpoints

func (s *State) GetCheckpoints() []TurnCheckpoint

GetCheckpoints returns a copy of the checkpoint list.

func (*State) ImportState

func (s *State) ImportState(data []byte) error

ImportState deserializes state from JSON.

func (*State) LastAssistantMessage

func (s *State) LastAssistantMessage() *Message

LastAssistantMessage returns the most recent assistant message, or nil if none exists.

func (*State) Len

func (s *State) Len() int

Len returns the number of messages.

func (*State) Messages

func (s *State) Messages() []Message

Messages returns a copy of the message list.

func (*State) SessionID

func (s *State) SessionID() string

SessionID returns the current session ID.

func (*State) SetCheckpoints

func (s *State) SetCheckpoints(cps []TurnCheckpoint)

SetCheckpoints replaces the checkpoint list.

func (*State) SetMessages

func (s *State) SetMessages(msgs []Message)

SetMessages replaces the message list.

func (*State) SetSessionID

func (s *State) SetSessionID(id string)

SetSessionID sets the session ID.

func (*State) TotalCost

func (s *State) TotalCost() float64

TotalCost returns the total cost.

func (*State) TotalTokens

func (s *State) TotalTokens() int

TotalTokens returns the total token count.

type StreamHandler

type StreamHandler interface {
	OnContent(content string)
	OnReasoning(reasoning string)
	OnDone(resp *ChatResponse)
	OnError(err error)
}

StreamHandler handles streamed responses from a Provider.

type StreamingBuffer

type StreamingBuffer struct {
	// contains filtered or unexported fields
}

StreamingBuffer captures streamed output for controlled display.

func NewStreamingBuffer

func NewStreamingBuffer() *StreamingBuffer

NewStreamingBuffer creates a new streaming buffer.

func (*StreamingBuffer) Len

func (b *StreamingBuffer) Len() int

Len returns the current buffer length.

func (*StreamingBuffer) Reset

func (b *StreamingBuffer) Reset()

Reset clears the buffer.

func (*StreamingBuffer) String

func (b *StreamingBuffer) String() string

String returns the current buffer contents.

func (*StreamingBuffer) Write

func (b *StreamingBuffer) Write(p []byte) (int, error)

Write appends content to the buffer.

type StructuralCompactionResult added in v1.1.0

type StructuralCompactionResult struct {
	Messages   []Message
	Strategy   string
	Summarized int // count of messages that were compressed into the summary
}

StructuralCompactionResult reports what happened in a structural compaction pass. Strategy is one of "none", "single_summary", "layered_summary".

func CompactWithLLMSummary added in v1.1.0

func CompactWithLLMSummary(ctx context.Context, messages []Message, summarizer LLMSummarizer) StructuralCompactionResult

CompactWithLLMSummary rewrites the middle of the message list into one or more durable summary messages using the supplied LLMSummarizer. The opening task anchor (system message + first user/assistant turn) and the recent causal chain are preserved intact.

Returns Strategy == "none" with the input unchanged when:

  • summarizer is nil
  • len(messages) < StructuralMinMessagesToCompact
  • the middle segment is smaller than StructuralMinMiddleMessages
  • the summarizer returns empty / an error for every layer

Otherwise returns the compacted slice. The caller is expected to follow up with rule-based Compact() if more reduction is still needed.

type SummarizerHint added in v1.1.0

type SummarizerHint struct {
	// DetailLevel is one of "brief", "summary", "detailed", or "" (default).
	// Layered compaction uses these to graduate detail across age bands of the
	// conversation: brief for oldest content, detailed for newest middle.
	DetailLevel string

	// MaxWords caps the requested length of the returned summary. Zero means
	// "use the summarizer's default budget".
	MaxWords int
}

SummarizerHint conveys optional shaping parameters from seed's compaction pipeline to a consumer-supplied LLMSummarizer. All fields are advisory: a summarizer that ignores hints still satisfies the contract.

type Tool

type Tool struct {
	Type     string       `json:"type"`
	Function ToolFunction `json:"function"`
}

Tool represents a tool definition that can be provided to the model. The structure matches the OpenAI function-calling wire format where tool definitions are nested under a "function" key.

type ToolCall

type ToolCall struct {
	ID       string           `json:"id"`
	Type     string           `json:"type"`
	Function ToolCallFunction `json:"function"`
}

ToolCall represents a function call requested by the model.

type ToolCallFunction

type ToolCallFunction struct {
	Name      string `json:"name"`
	Arguments string `json:"arguments"`
}

ToolCallFunction represents the function details of a tool call.

type ToolCallNormalizer

type ToolCallNormalizer struct {
	// contains filtered or unexported fields
}

ToolCallNormalizer cleans up structured tool calls returned by the model before they are executed. It handles common model output irregularities:

  1. Strips <|channel|> suffix from tool names
  2. Generates synthetic IDs for tool calls missing one
  3. Deduplicates by ID+arguments (first occurrence wins)
  4. Repairs malformed JSON arguments
  5. Normalizes Type field to "function"

func NewToolCallNormalizer

func NewToolCallNormalizer() *ToolCallNormalizer

NewToolCallNormalizer creates a new ToolCallNormalizer.

func (*ToolCallNormalizer) Normalize

func (n *ToolCallNormalizer) Normalize(calls []ToolCall) NormalizedToolCalls

Normalize processes a slice of ToolCall values and returns a cleaned, deduplicated NormalizedToolCalls slice. Calls with empty names after normalization or unrepairable JSON arguments are dropped.

type ToolCategory

type ToolCategory int

ToolCategory classifies a tool for optimization purposes.

const (
	// ToolCategoryUnknown means the optimizer should skip this tool.
	ToolCategoryUnknown ToolCategory = iota
	// ToolCategoryFileRead indicates a tool that reads file contents.
	ToolCategoryFileRead
	// ToolCategoryShellCommand indicates a tool that runs shell commands.
	ToolCategoryShellCommand
)

type ToolConfig added in v0.1.6

type ToolConfig struct {
	Name              string
	Description       string
	Parameters        []ParameterConfig
	Handler           ToolHandler
	HandlerWithImages ToolHandlerWithImages
	Aliases           []string
	Timeout           time.Duration
	MaxResultSize     int
	SafeForParallel   bool
}

ToolConfig configures a tool registered in the registry.

type ToolExecutor

type ToolExecutor interface {
	// GetTools returns the list of available tools.
	GetTools() []Tool

	// Execute runs the given tool calls and returns the resulting messages.
	Execute(ctx context.Context, calls []ToolCall) []Message
}

ToolExecutor represents a system that can execute tool calls.

var NoopExecutor ToolExecutor = &noopExecutor{}

NoopExecutor is a ToolExecutor with no tools. Use it when the agent only needs to produce text responses without tool execution.

type ToolFunction added in v0.1.1

type ToolFunction struct {
	Name        string      `json:"name"`
	Description string      `json:"description"`
	Parameters  interface{} `json:"parameters"`
}

ToolFunction describes a tool's identity and parameter schema. Parameters is an interface{} to accept any JSON schema structure (seed's ToolParameters for native tools, or arbitrary schemas from providers).

type ToolHandler added in v0.1.6

type ToolHandler func(ctx context.Context, args map[string]interface{}) (string, error)

ToolHandler is the standard handler for a registered tool.

type ToolHandlerWithImages added in v0.1.6

type ToolHandlerWithImages func(ctx context.Context, args map[string]interface{}) ([]ImageData, string, error)

ToolHandlerWithImages is a handler that may also return image data.

type ToolParameter

type ToolParameter struct {
	Type        string `json:"type"`
	Description string `json:"description,omitempty"`
}

ToolParameter defines a single parameter within a tool's schema.

type ToolParameters

type ToolParameters struct {
	Type       string                   `json:"type"`
	Properties map[string]ToolParameter `json:"properties"`
	Required   []string                 `json:"required,omitempty"`
}

ToolParameters defines the schema for a tool's arguments.

type ToolRegistry added in v0.1.6

type ToolRegistry struct {
	PreExecuteHook  func(name string, args map[string]interface{}) error
	PostExecuteHook func(name string, result string) string
	// contains filtered or unexported fields
}

ToolRegistry manages a collection of registered tools and executes them according to requests received from the conversation engine.

func NewToolRegistry added in v0.1.6

func NewToolRegistry(opts ToolRegistryOptions) *ToolRegistry

NewToolRegistry creates a new ToolRegistry with the given options.

func (*ToolRegistry) Execute added in v0.1.6

func (r *ToolRegistry) Execute(ctx context.Context, calls []ToolCall) []Message

Execute runs the given tool calls and returns the resulting messages.

func (*ToolRegistry) GetTool added in v0.1.6

func (r *ToolRegistry) GetTool(name string) *Tool

GetTool returns the Tool definition for the given name (resolving aliases).

func (*ToolRegistry) GetTools added in v0.1.6

func (r *ToolRegistry) GetTools() []Tool

GetTools returns all registered tool definitions, sorted by name.

func (*ToolRegistry) HasTool added in v0.1.6

func (r *ToolRegistry) HasTool(name string) bool

HasTool reports whether a tool exists by canonical name or alias.

func (*ToolRegistry) Register added in v0.1.6

func (r *ToolRegistry) Register(config ToolConfig) error

Register adds a single tool to the registry.

func (*ToolRegistry) RegisterAll added in v0.1.6

func (r *ToolRegistry) RegisterAll(configs []ToolConfig) error

RegisterAll registers multiple tools. Returns the first error encountered.

func (*ToolRegistry) ToolNames added in v0.1.6

func (r *ToolRegistry) ToolNames() []string

ToolNames returns all registered tool canonical names.

func (*ToolRegistry) Unregister added in v0.1.6

func (r *ToolRegistry) Unregister(name string) bool

Unregister removes a tool and its aliases by canonical name.

type ToolRegistryOptions added in v0.1.6

type ToolRegistryOptions struct {
	DefaultTimeout          time.Duration
	MaxResultSize           int
	EventPublisher          EventPublisher
	PreExecuteHook          func(name string, args map[string]interface{}) error
	PostExecuteHook         func(name string, result string) string
	CircuitBreakerThreshold int
	CircuitBreakerTimeout   time.Duration
}

ToolRegistryOptions configures a ToolRegistry.

type TransientError

type TransientError struct {
	// Op is the operation that failed (e.g. "chat", "stream").
	Op string
	// Provider is the provider that returned the error.
	Provider string
	// RetryAfter is a suggested delay before retrying (zero means use default backoff).
	RetryAfter time.Duration
	// Wrapped is the underlying error.
	Wrapped error
}

TransientError indicates a temporary failure that may succeed on retry.

func (*TransientError) Error

func (e *TransientError) Error() string

func (*TransientError) Unwrap

func (e *TransientError) Unwrap() error

type TurnCheckpoint

type TurnCheckpoint struct {
	// StartIndex is the index of the first message in the turn (the user query).
	StartIndex int `json:"start_index"`

	// EndIndex is the index of the last message in the turn (the final assistant response).
	EndIndex int `json:"end_index"`

	// UserMessage is the original user query that started the turn, truncated
	// to 2000 characters. This is the best target for embedding-based search
	// to find "what did the user ask that led to this outcome?"
	UserMessage string `json:"user_message"`

	// Summary is a concise description of what happened in the turn.
	Summary string `json:"summary"`

	// ActionableSummary is a bullet-list of accomplishments with file paths,
	// commands run, and other concrete details useful for continued context.
	ActionableSummary string `json:"actionable_summary"`
}

TurnCheckpoint captures a summary of a completed conversation turn. It records the message range consumed by the turn and a compact summary that can replace the original messages during context compaction.

func BuildCheckpointSummary

func BuildCheckpointSummary(messages []Message) TurnCheckpoint

BuildCheckpointSummary is a convenience function that creates a checkpoint summary from messages without requiring a builder instance.

type TurnSummaryBuilder

type TurnSummaryBuilder struct {
	// KnownFileTools is a set of tool names that operate on files.
	// If nil, the default set is used.
	KnownFileTools map[string]bool

	// KnownShellTools is a set of tool names that execute shell commands.
	// If nil, the default set is used.
	KnownShellTools map[string]bool

	// KnownErrorPatterns are substrings that indicate a tool result is an error.
	// If nil, the default set is used.
	KnownErrorPatterns []string
}

TurnSummaryBuilder builds a TurnCheckpoint from a slice of messages representing a single conversation turn. It extracts the user question, tool calls, errors, files modified, and final status to produce both a narrative summary and an actionable bullet list.

func NewTurnSummaryBuilder

func NewTurnSummaryBuilder() *TurnSummaryBuilder

NewTurnSummaryBuilder creates a new builder with default configuration.

func (*TurnSummaryBuilder) Build

func (b *TurnSummaryBuilder) Build(messages []Message) TurnCheckpoint

Build constructs a TurnCheckpoint from the given messages. The messages should represent a single turn: starting with a user query, followed by any number of assistant/tool-call/tool-result cycles, and ending with the final assistant response. Returns a checkpoint with StartIndex=0 and EndIndex=len(messages)-1 since the caller is responsible for setting the actual indices in state.

type UI

type UI interface {
	// Prompt displays a prompt and returns the user's input.
	Prompt(message string) (string, error)

	// Confirm displays a confirmation message and returns the user's choice.
	Confirm(message string) (bool, error)

	// Print writes output without a trailing newline.
	Print(message string)

	// PrintLine writes output with a trailing newline.
	PrintLine(message string)
}

UI represents a user interface for prompting and output.

var NoopUI UI = &noopUI{}

NoopUI is a headless UI implementation that discards all output and never prompts. Use it when the agent runs without a terminal or interactive layer.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL