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
- Variables
- func BuildCheckpointCompactedMessages(messages []Message, checkpoints []TurnCheckpoint) ([]Message, []TurnCheckpoint)
- func ClassifyError(err error, provider string) error
- func IsAuthError(err error) bool
- func IsBlankResponse(err error) bool
- func IsClientError(err error) bool
- func IsContentFiltered(err error) bool
- func IsContextOverflow(err error) bool
- func IsRateLimit(err error) bool
- func IsTransient(err error) bool
- func RecordTurnCheckpointAsync(state *State, messages []Message, startIndex, endIndex int, ...)
- type Agent
- func (a *Agent) Checkpoints() []TurnCheckpoint
- func (a *Agent) ExportState() ([]byte, error)
- func (a *Agent) ImportState(data []byte) error
- func (a *Agent) InjectInput(input string) bool
- func (a *Agent) Interrupt()
- func (a *Agent) IsPaused() bool
- func (a *Agent) Pause()
- func (a *Agent) Provider() Provider
- func (a *Agent) ReasoningBuffer() *StreamingBuffer
- func (a *Agent) ResetInterrupt() context.Context
- func (a *Agent) Resume()
- func (a *Agent) Run(ctx context.Context, query string) (string, error)
- func (a *Agent) RunStream(ctx context.Context, query string) (string, error)
- func (a *Agent) SetFlushCallback(fn func())
- func (a *Agent) SetProvider(p Provider)
- func (a *Agent) SetSystemPrompt(prompt string)
- func (a *Agent) State() *State
- func (a *Agent) Steer(msg Message)
- func (a *Agent) SteerSystem(content string)
- func (a *Agent) StreamingBuffer() *StreamingBuffer
- type AgentState
- type AgentStreamHandler
- type AuthError
- type BlankResponseError
- type ChatChoice
- type ChatRequest
- type ChatResponse
- type ChatUsage
- type ClientError
- type CompactionResult
- type ContentFilteredError
- type ContextOverflowError
- type ConversationHandler
- type ConversationOptimizer
- type ConversationOptimizerOptions
- type ConversationPruner
- func (cp *ConversationPruner) MinMessagesToKeep() int
- func (cp *ConversationPruner) Prune(ctx context.Context, messages []Message, currentTokens, maxTokens int, ...) []Message
- func (cp *ConversationPruner) RecentMessagesToKeep() int
- func (cp *ConversationPruner) SetRecentMessagesToKeep(n int)
- func (cp *ConversationPruner) SetSlidingWindowSize(n int)
- func (cp *ConversationPruner) SetStrategy(s PruningStrategy)
- func (cp *ConversationPruner) SetThreshold(t float64)
- func (cp *ConversationPruner) ShouldPrune(currentTokens, maxTokens int) bool
- func (cp *ConversationPruner) SlidingWindowSize() int
- func (cp *ConversationPruner) Strategy() PruningStrategy
- func (cp *ConversationPruner) Threshold() float64
- type EventPublisher
- type ExponentialBackoff
- type FallbackParseResult
- type FallbackParser
- type FallbackParserOptions
- type ImageData
- type LLMSummarizer
- type Message
- func DropOldestCheckpointSummaries(messages []Message, targetTokens int) []Message
- func DropOldestTurns(messages []Message, targetTokens int) []Message
- func ToolErrorMessage(toolCallID, toolName string, errMsg string) Message
- func ToolResultMessage(toolCallID, toolName string, content string) Message
- type MessageImportance
- type NormalizedToolCalls
- type Options
- type OutputEvent
- type OutputManager
- type ParameterConfig
- type Provider
- type ProviderInfo
- type PruneCallOptions
- type PrunerOptions
- type PruningStrategy
- type RateLimitError
- type ResponseValidator
- type ResponseValidatorOptions
- type RetryConfig
- type State
- func (s *State) AddCheckpoint(cp TurnCheckpoint)
- func (s *State) AddCost(cost float64)
- func (s *State) AddMessage(msg Message)
- func (s *State) AddTokens(prompt, completion, total int)
- func (s *State) ClearCheckpoints()
- func (s *State) EnsureSessionID()
- func (s *State) ExportState() ([]byte, error)
- func (s *State) GetCheckpoints() []TurnCheckpoint
- func (s *State) ImportState(data []byte) error
- func (s *State) LastAssistantMessage() *Message
- func (s *State) Len() int
- func (s *State) Messages() []Message
- func (s *State) SessionID() string
- func (s *State) SetCheckpoints(cps []TurnCheckpoint)
- func (s *State) SetMessages(msgs []Message)
- func (s *State) SetSessionID(id string)
- func (s *State) TotalCost() float64
- func (s *State) TotalTokens() int
- type StreamHandler
- type StreamingBuffer
- type StructuralCompactionResult
- type SummarizerHint
- type Tool
- type ToolCall
- type ToolCallFunction
- type ToolCallNormalizer
- type ToolCategory
- type ToolConfig
- type ToolExecutor
- type ToolFunction
- type ToolHandler
- type ToolHandlerWithImages
- type ToolParameter
- type ToolParameters
- type ToolRegistry
- func (r *ToolRegistry) Execute(ctx context.Context, calls []ToolCall) []Message
- func (r *ToolRegistry) GetTool(name string) *Tool
- func (r *ToolRegistry) GetTools() []Tool
- func (r *ToolRegistry) HasTool(name string) bool
- func (r *ToolRegistry) Register(config ToolConfig) error
- func (r *ToolRegistry) RegisterAll(configs []ToolConfig) error
- func (r *ToolRegistry) ToolNames() []string
- func (r *ToolRegistry) Unregister(name string) bool
- type ToolRegistryOptions
- type TransientError
- type TurnCheckpoint
- type TurnSummaryBuilder
- type UI
Constants ¶
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.
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.
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.
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.
const MetaKeyCheckpoint = "checkpoint"
MetaKeyCheckpoint is the Meta key set on messages inserted by checkpoint compaction so they can be identified without string matching.
Variables ¶
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):
- 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)
- 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 ¶
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 ¶
IsAuthError reports whether err is an AuthError.
func IsBlankResponse ¶ added in v0.1.1
IsBlankResponse reports whether err is a BlankResponseError.
func IsClientError ¶ added in v0.2.1
IsClientError reports whether err is a ClientError.
func IsContentFiltered ¶ added in v0.1.1
IsContentFiltered reports whether err is a ContentFilteredError.
func IsContextOverflow ¶
IsContextOverflow reports whether err is a ContextOverflowError.
func IsRateLimit ¶
IsRateLimit reports whether err is a RateLimitError.
func IsTransient ¶
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 ¶
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 ¶
ExportState serializes the current state to JSON.
func (*Agent) ImportState ¶
ImportState deserializes state from JSON.
func (*Agent) InjectInput ¶
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) ReasoningBuffer ¶
func (a *Agent) ReasoningBuffer() *StreamingBuffer
ReasoningBuffer returns the reasoning streaming buffer.
func (*Agent) ResetInterrupt ¶ added in v1.0.0
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) Run ¶
Run executes a single query through the conversation loop.
The query can be cancelled in two ways:
- Cancelling the caller's context (standard Go pattern).
- Calling Interrupt() on this Agent (independent external cancellation).
Interrupt() resets after each Run() call, so it only affects the current query.
func (*Agent) RunStream ¶
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 ¶
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 ¶
SetSystemPrompt updates the system prompt for future queries.
func (*Agent) Steer ¶
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 ¶
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.
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 ¶
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 ¶
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 ¶
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
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
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
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
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.
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 (*State) AddCheckpoint ¶
func (s *State) AddCheckpoint(cp TurnCheckpoint)
AddCheckpoint appends a turn checkpoint to the state.
func (*State) AddMessage ¶
AddMessage appends a message to the conversation.
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 ¶
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 ¶
ImportState deserializes state from JSON.
func (*State) LastAssistantMessage ¶
LastAssistantMessage returns the most recent assistant message, or nil if none exists.
func (*State) SetCheckpoints ¶
func (s *State) SetCheckpoints(cps []TurnCheckpoint)
SetCheckpoints replaces the checkpoint list.
func (*State) SetMessages ¶
SetMessages replaces the message list.
func (*State) SetSessionID ¶
SetSessionID sets the session ID.
func (*State) TotalTokens ¶
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) String ¶
func (b *StreamingBuffer) String() string
String returns the current buffer contents.
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 ¶
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:
- Strips <|channel|> suffix from tool names
- Generates synthetic IDs for tool calls missing one
- Deduplicates by ID+arguments (first occurrence wins)
- Repairs malformed JSON arguments
- 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
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.
Source Files
¶
- agent.go
- backoff.go
- checkpoint_compaction.go
- circuit_breaker.go
- compaction.go
- conversation.go
- conversation_optimizer.go
- error_classifier.go
- errors.go
- fallback_parser.go
- finalize.go
- fp_bare_json.go
- fp_json.go
- fp_named_blocks.go
- fp_tool_blocks.go
- fp_xml.go
- interfaces.go
- message_pipeline.go
- noop.go
- output_manager.go
- pruner.go
- response_validator.go
- retry.go
- state.go
- streaming.go
- structural_compaction.go
- summarizer.go
- tool_call_normalizer.go
- tool_registry.go
- tool_registry_args.go
- turn_checkpoints.go
- turn_summary.go
- types.go