agentflow

package module
v0.0.0-...-b246809 Latest Latest
Warning

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

Go to latest
Published: Apr 5, 2026 License: MIT Imports: 16 Imported by: 0

README

agentflow

A production-grade Go framework for building agentic AI systems.

An agentic AI system is one where a language model autonomously decides to invoke tools, observes results, and continues reasoning in a loop until the task is complete. agentflow provides the core abstractions and orchestration to build such systems with any AI provider that supports tool use (function calling).

Table of Contents

Features

  • Provider-agnostic -- works with OpenAI, Anthropic, Google Gemini, Groq, OpenRouter, or any custom provider
  • Streaming-first -- real-time event delivery via Go channels (17 event types)
  • Native tool calling -- uses provider-native function calling APIs with JSON validation
  • Fuzzy tool name matching -- handles models that shorten tool names (e.g. "search" matches "web_search")
  • Tool call repair -- validation errors return full schema with field descriptions so the model can self-correct
  • Guaranteed tool results -- every tool call gets a result, even if execution is cancelled or interrupted
  • Loop detection -- SHA-256 signature tracking detects repetitive tool calling patterns (configurable threshold)
  • Early tool emission -- tool calls emitted mid-stream when JSON arguments are valid, reducing latency
  • Auto-compaction -- automatically compacts conversation on "context too large" errors and retries
  • Graceful tool fallback -- models that don't support native tool calling (e.g. Groq compound) automatically retry without tools
  • Critical error handling -- tool panics terminate the loop; validation errors are non-critical and sent to model for self-correction
  • Execution modes -- ModeLocal for full access, ModeRemote for server-safe tools only
  • Input validation -- automatic JSON Schema validation of tool inputs before execution
  • Rate limiting -- token bucket rate limiter for provider API calls
  • Structured logging -- optional slog integration for model calls, retries, compaction, budget
  • Trace context propagation -- W3C traceparent headers propagated to provider HTTP requests
  • Tool timeout and retry -- configurable per-tool timeout and retry on failure
  • Circuit breaker -- middleware that blocks tools after consecutive failures
  • Error strategy -- configurable error handling (transform errors, abort loop)
  • Anthropic thinking -- native thinking_delta support for extended thinking/reasoning
  • Sub-agent system -- spawn child agents, parallel orchestration, delegate_task tool
  • Agent cloning -- derive specialized agents from a common base via Clone()
  • Session persistence -- save/resume conversations with file-based or in-memory stores
  • Token budget -- enforce consumption limits with warning thresholds
  • Concurrent tool execution -- safe tools run in parallel, unsafe tools run in isolation
  • Hook system -- intercept any lifecycle phase, multi-phase hooks supported
  • Permission control -- AllowAll, DenyList, AllowList, ReadOnly, Chain, or custom checkers
  • Result size management -- automatic truncation of oversized tool outputs
  • Context compaction -- sliding window, token-aware, AI-powered summarization, staged
  • Multi-provider fallback -- automatic failover across providers with health checking
  • Multimodal -- send images (base64 or URL) alongside text
  • 14 built-in tools -- bash, file ops, search, HTTP, deep search, web search, calculator, date/time, URL reader, and more
  • Generic typed tools -- tools.NewTyped[I]() with auto schema generation from struct tags
  • Event filtering -- FilterEvents() helper to consume only specific event types
  • Panic recovery -- tool panics are caught, marked critical, and terminate the loop safely
  • Smart retry logic -- checks x-should-retry header, io.ErrUnexpectedEOF, status 408/409/429/5xx
  • Context overflow detection -- IsContextTooLarge() detects token limit errors across providers
  • Zero external dependencies -- core uses only the Go standard library
  • Comprehensive test suite -- provider unit tests, OpenRouter integration tests, race tests, benchmarks

Installation

go get github.com/CanArslanDev/agentflow

Requires Go 1.23 or later.

Quick Start

package main

import (
    "context"
    "fmt"

    "github.com/CanArslanDev/agentflow"
    "github.com/CanArslanDev/agentflow/provider/groq"
    "github.com/CanArslanDev/agentflow/tools"
)

type CalcInput struct {
    Expression string `json:"expression" description:"Math expression to evaluate"`
}

func main() {
    provider := groq.New("gsk-...", "llama-3.3-70b-versatile")

    calculator := tools.NewTyped[CalcInput](
        "calculator", "Evaluate a math expression",
        []string{"expression"},
        func(_ context.Context, input CalcInput, _ agentflow.ProgressFunc) (*agentflow.ToolResult, error) {
            return &agentflow.ToolResult{Content: "42"}, nil
        },
    )

    agent := agentflow.NewAgent(provider,
        agentflow.WithTools(calculator),
        agentflow.WithSystemPrompt("You are a helpful assistant."),
        agentflow.WithMaxTurns(10),
    )

    for ev := range agent.Run(context.Background(), []agentflow.Message{
        agentflow.NewUserMessage("What is 6 * 7?"),
    }) {
        switch ev.Type {
        case agentflow.EventTextDelta:
            fmt.Print(ev.TextDelta.Text)
        case agentflow.EventToolStart:
            fmt.Printf("\n[tool: %s]\n", ev.ToolStart.ToolCall.Name)
        case agentflow.EventTurnEnd:
            fmt.Printf("\n(done: %s)\n", ev.TurnEnd.Reason)
        }
    }
}

Architecture

                        Agent.Run(ctx, messages)
                                |
                          <-chan Event  (17 event types)
                                |
    +---------------------------+---------------------------+
    |                    AGENTIC LOOP                       |
    |                                                       |
    |   +----------+     +-----------+     +----------+     |
    |   | PreModel |---->| Rate Limit|---->| Provider |     |
    |   |  Hooks   |     | + Retry   |     | Stream   |     |
    |   +----------+     +-----------+     +----+-----+     |
    |                                           |           |
    |                  Tool Calls?              |           |
    |                  /          \              |           |
    |                Yes          No --> return  |           |
    |                /                          |           |
    |   +-------------------+                   |           |
    |   |  Tool Pipeline:   |                   |           |
    |   |  1. Validate      |                   |           |
    |   |  2. PreHooks      |                   |           |
    |   |  3. Permission    |                   |           |
    |   |  4. Schema Check  |                   |           |
    |   |  5. Execute+Retry |                   |           |
    |   |  6. Error Strategy|                   |           |
    |   |  7. Size Limit    |                   |           |
    |   |  8. PostHooks     |                   |           |
    |   +--------+----------+                   |           |
    |            |                              |           |
    |   Append Results --> continue (next turn) |           |
    +-------------------------------------------------------+

Core Concepts

Provider

Abstracts the AI model API. Implement the Provider interface to add new backends:

type Provider interface {
    CreateStream(ctx context.Context, req *Request) (Stream, error)
    ModelID() string
}

Optionally implement HealthChecker for proactive health checking:

type HealthChecker interface {
    HealthCheck(ctx context.Context) error
}

Built-in providers:

Provider Package Models Auth
OpenAI provider/openai gpt-4o, gpt-4o-mini, gpt-4-turbo Authorization: Bearer sk-...
Anthropic provider/anthropic claude-sonnet-4, claude-haiku-4.5, claude-opus-4 x-api-key: sk-ant-...
Google Gemini provider/gemini gemini-2.0-flash, gemini-2.5-pro API key in URL param
Groq provider/groq llama-3.3-70b, mixtral-8x7b Authorization: Bearer gsk_...
OpenRouter provider/openrouter All models via unified API Authorization: Bearer sk-or-...
Ollama provider/ollama Any Ollama model (llama3, mistral, etc.) None (URL-based)
Fallback provider/fallback Cascading failover across any providers N/A
Mock provider/mock Deterministic testing without API calls N/A
Tool

Three ways to define tools:

Generic typed (recommended -- type-safe, auto schema):

type SearchInput struct {
    Query      string `json:"query" description:"Search query"`
    MaxResults int    `json:"max_results,omitempty" description:"Max results"`
}

tool := tools.NewTyped[SearchInput](
    "search", "Search the web",
    []string{"query"},
    func(ctx context.Context, input SearchInput, p agentflow.ProgressFunc) (*agentflow.ToolResult, error) {
        return &agentflow.ToolResult{Content: "results for: " + input.Query}, nil
    },
)

Builder pattern:

tool := tools.New("my_tool", "Description").
    WithSchema(map[string]any{...}).
    ConcurrencySafe(true).ReadOnly(true).RemoteSafe().
    WithExecute(fn).Build()

Interface:

type Tool interface {
    Name() string
    Description() string
    InputSchema() map[string]any
    Execute(ctx context.Context, input json.RawMessage, progress ProgressFunc) (*ToolResult, error)
    IsConcurrencySafe(input json.RawMessage) bool
    IsReadOnly(input json.RawMessage) bool
}
Hook

Intercept the execution pipeline at five lifecycle phases:

Phase When Capabilities
HookPreToolUse Before tool execution Block execution, modify input
HookPostToolUse After tool execution Log results, inject messages
HookPreModelCall Before API call Block call, inject context
HookPostModelCall After API response Log usage, inspect response
HookOnTurnEnd Loop would terminate Force continuation via injected messages

Hooks can implement MultiPhaseHook to fire at multiple phases without duplicate registration.

Event Stream

17 event types covering the full agent lifecycle:

Event Description
EventTextDelta Streaming text chunk from the model
EventThinkingDelta Reasoning/thinking content from the model
EventToolStart Tool execution beginning
EventToolProgress Incremental progress from a running tool
EventToolEnd Tool execution completed (with result and duration)
EventTurnStart New agentic loop iteration starting
EventTurnEnd Loop iteration or entire agent run finished
EventMessage Complete message added to conversation history
EventUsage Token usage statistics for the turn
EventBudgetWarning Token consumption crossed warning threshold
EventCompaction Context history was compacted
EventRetry Provider call being retried
EventPermissionDenied Tool blocked by permission checker
EventHookBlocked Hook blocked execution
EventSubAgentStart Sub-agent spawned
EventSubAgentEnd Sub-agent completed
EventError Recoverable error (with retry indicator)

Filter events for simpler consumption:

for ev := range agentflow.FilterEvents(agent.Run(ctx, msgs), agentflow.EventTextDelta, agentflow.EventTurnEnd) {
    // only text deltas and turn ends
}
Agent Cloning

Derive specialized agents from a common base:

base := agentflow.NewAgent(provider, agentflow.WithMaxTurns(10), agentflow.WithTools(tools...))
researcher := base.Clone(agentflow.WithSystemPrompt("You are a researcher."))
writer := base.Clone(agentflow.WithSystemPrompt("You are a writer."))

Execution Modes

// Local mode (default) -- all tools available
agent := agentflow.NewAgent(provider,
    agentflow.WithTools(builtin.All()...),
    agentflow.WithExecutionMode(agentflow.ModeLocal),
)

// Remote mode -- only remote-safe tools
agent := agentflow.NewAgent(provider,
    agentflow.WithTools(builtin.All()...),
    agentflow.WithExecutionMode(agentflow.ModeRemote),
)
Locality ModeLocal ModeRemote Examples
ToolLocalOnly Allowed Blocked bash, read_file, write_file, edit_file, glob, grep
ToolRemoteSafe Allowed Allowed http_request, web_search, deep_search
ToolAny Allowed Allowed sleep, ask_user

Sub-Agent System

// Single child
events := agent.SpawnChild(ctx, agentflow.SubAgentConfig{
    SystemPrompt: "You are a research specialist.",
    MaxTurns:     5,
}, "Research Go concurrency patterns")

// Parallel orchestration
results := agentflow.Orchestrate(ctx, agent, agentflow.SubAgentConfig{
    MaxTurns: 3,
}, []string{"Topic A", "Topic B", "Topic C"})

Session Persistence

import "github.com/CanArslanDev/agentflow/session/filestore"

store, _ := filestore.New("./sessions")
agent := agentflow.NewAgent(provider, agentflow.WithSessionStore(store))

session := &agentflow.Session{Metadata: map[string]any{"user": "alice"}}
for ev := range agent.RunSession(ctx, session, messages) { ... }

// Resume later
events, _ := agent.Resume(ctx, session.ID, "Continue please")

Token Budget Management

agent := agentflow.NewAgent(provider,
    agentflow.WithTokenBudget(agentflow.TokenBudget{
        MaxTokens:        100000,
        WarningThreshold: 0.8,
    }),
)

Tool Result Management

agent := agentflow.NewAgent(provider,
    agentflow.WithMaxResultSize(2000),
)
Limiter Behavior
TruncateLimiter (default) Keeps 80% head + 20% tail
HeadTailLimiter Configurable head/tail ratio
NoLimiter Pass-through

Context Compaction

import "github.com/CanArslanDev/agentflow/compactor"

// Sliding window: keep last N messages
agentflow.WithCompactor(compactor.NewSlidingWindow(20, 0))

// Token-aware: trigger based on estimated token count
agentflow.WithCompactor(compactor.NewTokenWindow(8000, 20))

// AI-powered: summarize older messages
agentflow.WithCompactor(compactor.NewSummary(provider, 20, 0))

// Staged: try light compaction first, then heavier
agentflow.WithCompactor(compactor.NewStaged(
    compactor.NewSlidingWindow(30, 40),
    compactor.NewSummary(provider, 10, 0),
))

Input Validation

Tool inputs are automatically validated against their InputSchema() before execution. Supports type checking, required fields, enum values, nested objects, and array items.

// Disable if schemas are informational only
agentflow.WithDisableInputValidation()

Rate Limiting

// 10 requests per second with burst of 10
agentflow.WithRateLimiter(agentflow.NewTokenBucketLimiter(10, time.Second))

// 60 requests per minute
agentflow.WithRateLimiter(agentflow.NewTokenBucketLimiter(60, time.Minute))

Structured Logging

agentflow.WithLogger(slog.Default())

Logs model calls (start/end with duration), retries, compaction, budget warnings, validation failures, and tool retries.

Trace Context Propagation

The observability.Tracer automatically propagates W3C traceparent headers to provider HTTP requests. All providers forward Request.Metadata entries as HTTP headers.

import "github.com/CanArslanDev/agentflow/observability"

tracer := observability.NewTracer()
for _, h := range tracer.Hooks() {
    agent = agentflow.NewAgent(provider, agentflow.WithHook(h))
}
// after run:
trace := tracer.Finish()

Multi-Provider Fallback

import "github.com/CanArslanDev/agentflow/provider/fallback"

provider := fallback.New(
    groq.New(groqKey, "llama-3.3-70b-versatile"),
    openrouter.New(orKey, "anthropic/claude-sonnet-4-20250514"),
)

Multimodal Support

msg := agentflow.NewImageMessage("What do you see?",
    agentflow.ImageContent{MediaType: "image/png", Data: base64String},
)
msg := agentflow.NewImageURLMessage("Describe this", "https://example.com/photo.jpg")

Built-in Tools

12 ready-to-use tools in tools/builtin:

Tool Description Locality Concurrent ReadOnly
bash Execute shell commands LocalOnly No No
read_file Read file contents with offset/limit LocalOnly Yes Yes
write_file Create or overwrite files LocalOnly No No
edit_file String replacement in files LocalOnly No No
list_dir List directory contents LocalOnly Yes Yes
glob Find files by pattern (supports **) LocalOnly Yes Yes
grep Search file contents LocalOnly Yes Yes
http_request HTTP requests (GET/POST/PUT/DELETE) RemoteSafe Yes Yes
web_search Web search via DuckDuckGo RemoteSafe Yes Yes
deep_search Multi-step web research (search + fetch + extract) RemoteSafe Yes Yes
sleep Pause execution Any Yes Yes
ask_user Prompt user for input (callback) Any No Yes

Registry presets:

builtin.All()      // all 11 tools (ask_user requires separate registration)
builtin.Local()    // alias for All()
builtin.Remote()   // remote-safe: http_request, web_search, deep_search, sleep
builtin.ReadOnly() // only read-only tools

Middleware

import "github.com/CanArslanDev/agentflow/middleware"

// Structured logging
for _, h := range middleware.Logging(slog.Default()) {
    opts = append(opts, agentflow.WithHook(h))
}

// Metrics collection
metrics := middleware.NewMetrics()
for _, h := range metrics.Hooks() {
    opts = append(opts, agentflow.WithHook(h))
}

// Circuit breaker: block tool after 3 consecutive failures, reset after 30s
cb := middleware.NewCircuitBreaker(3, 30*time.Second)
for _, h := range cb.Hooks() {
    opts = append(opts, agentflow.WithHook(h))
}

// Tool timeout and retry (via config, not middleware)
agentflow.WithToolTimeout(30 * time.Second)
agentflow.WithToolRetries(2)

// Error strategy
agentflow.WithErrorStrategy(agentflow.ErrorStrategyFunc(
    func(call *agentflow.ToolCall, result *agentflow.ToolResult) (*agentflow.ToolResult, agentflow.ErrorAction) {
        if call.Name == "critical_tool" {
            return result, agentflow.ErrorActionAbort
        }
        return result, agentflow.ErrorActionDefault
    },
))

Extension Packages

Package Import Path Purpose
compactor agentflow/compactor SlidingWindow, TokenWindow, Summary, Staged, ContextCollapser
team agentflow/team Multi-agent coordination with mailbox and shared memory
observability agentflow/observability Tracer (spans) and CostTracker (token pricing)
trigger agentflow/trigger Scheduled agent execution on intervals
plan agentflow/plan Plan(), PlanAndExecute(), ExtractMemories()
skill agentflow/skill Skill registry, execution, and YAML parsing
task agentflow/task Task store for agent work tracking

HTTP Server Deployment

The streaming example shows how to deploy agentflow as an HTTP API with SSE streaming:

agent := agentflow.NewAgent(provider,
    agentflow.WithTools(builtin.Remote()...),
    agentflow.WithExecutionMode(agentflow.ModeRemote),
    agentflow.WithRateLimiter(agentflow.NewTokenBucketLimiter(10, time.Second)),
    agentflow.WithToolTimeout(30 * time.Second),
    agentflow.WithLogger(slog.Default()),
)

// SSE streaming endpoint
http.HandleFunc("/chat", func(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "text/event-stream")
    flusher := w.(http.Flusher)

    for ev := range agent.Run(r.Context(), messages) {
        data, _ := json.Marshal(eventToJSON(ev))
        fmt.Fprintf(w, "data: %s\n\n", data)
        flusher.Flush()
    }
})

See _examples/streaming/ for the full implementation with both SSE and synchronous JSON endpoints.

Examples

Example Description
basic Minimal agent with a calculator tool
chat Interactive chat loop with built-in tools
custom_tools File system agent with metrics and permissions
streaming HTTP SSE server with remote-safe tools

Testing

# Unit tests (no API key)
go test ./... -run "Test[^I]" -timeout 60s

# Integration tests (requires Groq API key)
GROQ_API_KEY=gsk-... go test ./... -run "TestIntegration_" -timeout 180s

# Benchmarks
go test -bench=. -benchmem -run="^$"

# Race detector
go test -run "TestRace_" -race -timeout 30s

Project Structure

agentflow/
    -- Core (17 source files) --
    agent.go              Agent, Run(), RunSync(), RunSession(), Resume(), Clone()
    config.go             Config, 22 WithXxx() options
    tool.go               Tool interface, ExecutionMode, ToolLocality
    message.go            Message, ContentBlock, multimodal, documents
    event.go              17 EventTypes, FilterEvents()
    hook.go               Hook, MultiPhaseHook, HookFunc
    permission.go         PermissionChecker, AllowAll, DenyList, AllowList, Chain
    provider.go           Provider, Stream, HealthChecker
    ratelimit.go          RateLimiter, TokenBucketLimiter
    subagent.go           SpawnChild, SpawnChildren, Orchestrate
    session.go            SessionStore, Session
    budget.go             TokenBudget, budgetTracker
    result.go             ResultLimiter, TruncateLimiter, HeadTailLimiter
    compactor.go          Compactor interface
    streaming_executor.go Overlapping model gen with tool exec
    errors.go             ProviderError, ToolError, ErrorStrategy
    doc.go                Package documentation

    -- Extension packages --
    compactor/            SlidingWindow, TokenWindow, Summary, Staged
    team/                 Team coordination, Mailbox, SharedMemory
    observability/        Tracer, CostTracker
    trigger/              Scheduled execution
    plan/                 Plan mode, memory extraction
    skill/                Skill registry
    task/                 Task store

    -- Providers --
    provider/openai/      OpenAI (uses internal/sse)
    provider/anthropic/   Anthropic (custom SSE parser)
    provider/gemini/      Google Gemini (custom SSE parser)
    provider/groq/        Groq (uses internal/sse)
    provider/openrouter/  OpenRouter (uses internal/sse)
    provider/ollama/      Ollama/RunPod (JSONL streaming)
    provider/fallback/    Multi-provider failover
    provider/mock/        Deterministic mock

    -- Internal --
    internal/sse/         Shared SSE parser
    internal/jsonschema/  JSON Schema validator

    -- Infrastructure --
    session/filestore/    File-based session store
    session/memstore/     In-memory session store
    middleware/           Logging, metrics, recovery, circuit breaker
    tools/builder.go      Fluent ToolBuilder
    tools/typed.go        Generic TypedTool[I]
    tools/builtin/        12 built-in tools

    -- Examples --
    _examples/basic/      Minimal agent
    _examples/chat/       Interactive chat
    _examples/custom_tools/ Multi-tool with metrics
    _examples/streaming/  HTTP SSE server

License

MIT -- see LICENSE.

Documentation

Overview

Package agentflow provides a production-grade framework for building agentic AI systems in Go.

An agentic AI system is one where a language model can autonomously decide to invoke tools, observe results, and continue reasoning in a loop until the task is complete. This package provides the core abstractions and orchestration logic to build such systems with any AI provider that supports tool use (function calling).

Architecture

The framework is built around five core interfaces:

  • Provider abstracts an AI model API (Anthropic, OpenAI, OpenRouter, etc.)
  • Tool defines a capability the agent can invoke (web search, file read, API call, etc.)
  • Hook intercepts the execution pipeline at defined lifecycle phases
  • PermissionChecker controls whether a tool is allowed to execute
  • Compactor manages conversation history when context limits are approached

The Agentic Loop

The core of the framework is a simple but powerful loop:

for {
    response := provider.CreateStream(messages)
    toolCalls := extractToolCalls(response)
    if len(toolCalls) == 0 {
        break // model is done
    }
    results := executor.Execute(toolCalls)
    messages = append(messages, results...)
}

The loop streams events through a channel, allowing real-time observation of every step: text deltas, tool invocations, progress updates, and completion signals.

Quick Start

provider := openrouter.New("sk-or-...", "anthropic/claude-sonnet-4-20250514")
agent := agentflow.NewAgent(provider,
    agentflow.WithTools(myTool),
    agentflow.WithSystemPrompt("You are a helpful assistant."),
)

messages := []agentflow.Message{agentflow.NewUserMessage("Hello")}
for ev := range agent.Run(ctx, messages) {
    // handle events
}

Index

Constants

View Source
const (
	DefaultMaxConcurrency  = 10
	DefaultEventBufferSize = 256
)

Default configuration values.

View Source
const DefaultMaxResultChars = 50000

Default maximum result size in characters.

Variables

View Source
var (
	// ErrToolNotFound is returned when a model requests a tool that is not registered.
	ErrToolNotFound = errors.New("agentflow: tool not found")

	// ErrPermissionDenied is returned when a tool invocation is blocked by the permission checker.
	ErrPermissionDenied = errors.New("agentflow: permission denied")

	// ErrMaxTurnsExceeded is returned when the agentic loop exceeds the configured turn limit.
	ErrMaxTurnsExceeded = errors.New("agentflow: max turns exceeded")

	// ErrStreamClosed is returned when attempting to read from a closed stream.
	ErrStreamClosed = errors.New("agentflow: stream closed")

	// ErrInputValidation is returned when tool input fails schema validation.
	ErrInputValidation = errors.New("agentflow: input validation failed")

	// ErrHookBlocked is returned when a hook blocks tool execution or loop continuation.
	ErrHookBlocked = errors.New("agentflow: blocked by hook")

	// ErrProviderUnavailable is returned when the provider cannot be reached.
	ErrProviderUnavailable = errors.New("agentflow: provider unavailable")

	// ErrSessionNotFound is returned when a session ID does not exist in the store.
	ErrSessionNotFound = errors.New("agentflow: session not found")

	// ErrToolLoop is returned when the agent detects a repeated tool calling pattern.
	ErrToolLoop = errors.New("agentflow: tool calling loop detected")
)

Sentinel errors returned by the framework.

Functions

func FilterEvents

func FilterEvents(ch <-chan Event, types ...EventType) <-chan Event

FilterEvents returns a channel that only delivers events matching the specified types. The returned channel is closed when the input channel is closed.

for ev := range agentflow.FilterEvents(agent.Run(ctx, msgs), agentflow.EventTextDelta, agentflow.EventTurnEnd) {
    // only text deltas and turn ends
}

func GenerateSessionID

func GenerateSessionID() string

GenerateSessionID creates a cryptographically random session identifier.

func IsContextTooLargeError

func IsContextTooLargeError(err error) bool

IsContextTooLargeError reports whether err indicates a context size overflow.

func IsHealthy

func IsHealthy(ctx context.Context, p Provider) error

IsHealthy checks if a provider is healthy. If the provider does not implement HealthChecker, it is assumed healthy.

func IsRetryableError

func IsRetryableError(err error) bool

IsRetryableError reports whether err is a retryable provider error.

func IsToolAllowed

func IsToolAllowed(t Tool, mode ExecutionMode) bool

IsToolAllowed checks whether a tool is permitted in the given execution mode. In ModeLocal, all tools are allowed. In ModeRemote, only ToolRemoteSafe and ToolAny tools are permitted.

func SessionPreview

func SessionPreview(messages []Message, maxLen int) string

SessionPreview extracts a preview string from the first user message.

Types

type Agent

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

Agent orchestrates the agentic loop: model calls, tool execution, and message management. It is the primary entry point for consumers of the framework.

An Agent is immutable after construction — configuration is set via Option functions passed to NewAgent. The same Agent can be used for multiple concurrent Run calls, each with its own independent conversation state.

func NewAgent

func NewAgent(provider Provider, opts ...Option) *Agent

NewAgent creates an Agent with the given provider and options. The provider handles communication with the AI model, while options configure tools, hooks, permissions, and loop behavior.

agent := agentflow.NewAgent(provider,
    agentflow.WithTools(searchTool, calcTool),
    agentflow.WithSystemPrompt("You are helpful."),
    agentflow.WithMaxTurns(10),
)

func (*Agent) AddHook

func (a *Agent) AddHook(h Hook)

AddHook appends a lifecycle hook to the agent. This is useful when hooks need to be added after initial construction (e.g., by extension packages).

func (*Agent) Clone

func (a *Agent) Clone(opts ...Option) *Agent

Clone creates a copy of this agent with the same configuration, then applies the given options on top. Useful for deriving specialized agents from a common base without SubAgentConfig.

base := agentflow.NewAgent(provider, agentflow.WithMaxTurns(10))
researcher := base.Clone(agentflow.WithSystemPrompt("You are a researcher."))
writer := base.Clone(agentflow.WithSystemPrompt("You are a writer."))

func (*Agent) Resume

func (a *Agent) Resume(ctx context.Context, sessionID string, additionalMessage string) (<-chan Event, error)

Resume loads a previously saved session and continues the conversation. The agent picks up where it left off, using the stored message history.

If additionalMessage is non-empty, it is appended as a new user message before the loop resumes.

for ev := range agent.Resume(ctx, "session-id-123", "Continue from where you left off") { ... }

func (*Agent) Run

func (a *Agent) Run(ctx context.Context, messages []Message) <-chan Event

Run executes the agentic loop asynchronously. It returns a channel that delivers events as they occur: streaming text, tool invocations, progress updates, and completion signals. The channel is closed when the loop terminates.

Cancel the context to stop the loop gracefully. In-flight tool executions will receive the cancellation signal via their context.

for ev := range agent.Run(ctx, messages) {
    switch ev.Type {
    case agentflow.EventTextDelta:
        fmt.Print(ev.TextDelta.Text)
    case agentflow.EventTurnEnd:
        fmt.Println("Done:", ev.TurnEnd.Reason)
    }
}

func (*Agent) RunSession

func (a *Agent) RunSession(ctx context.Context, session *Session, messages []Message) <-chan Event

RunSession executes the agentic loop with automatic session persistence. The session is saved after each turn completes. If the session has an empty ID, one is generated automatically.

Use this instead of Run when you need crash recovery and conversation history.

session := &agentflow.Session{
    Metadata: map[string]any{"user": "alice"},
}
for ev := range agent.RunSession(ctx, session, messages) { ... }
// session.ID is now set and can be used with Resume()

func (*Agent) RunSync

func (a *Agent) RunSync(ctx context.Context, messages []Message) ([]Message, error)

RunSync is a convenience wrapper that collects all events and returns the final conversation history. Useful for non-streaming use cases or testing.

Returns the complete message history including all assistant and tool result messages generated during the loop. Returns an error if the loop terminated due to an unrecoverable error.

func (*Agent) SpawnChild

func (a *Agent) SpawnChild(ctx context.Context, cfg SubAgentConfig, task string) <-chan Event

SpawnChild creates and runs a child agent with the given configuration and task. The child runs in a new goroutine and returns an event channel, just like Run().

The child agent inherits unset fields from the parent. The task string becomes the initial user message for the child's conversation.

Cancel the context to stop the child and all its in-flight tool executions.

events := agent.SpawnChild(ctx, agentflow.SubAgentConfig{
    SystemPrompt: "You are a research specialist.",
    MaxTurns:     5,
}, "Find information about Go concurrency patterns")

for ev := range events {
    // handle child events
}

func (*Agent) SpawnChildren

func (a *Agent) SpawnChildren(ctx context.Context, cfg SubAgentConfig, tasks []string) <-chan Event

SpawnChildren launches multiple child agents in parallel and merges their event streams into a single channel. Each child receives its own task string. Events are tagged with the child index via EventSubAgentStart/End events.

All children share the same context — canceling it stops all of them.

func (*Agent) Tools

func (a *Agent) Tools() []string

Tools returns the registered tool names.

type BudgetWarningEvent

type BudgetWarningEvent struct {
	ConsumedTokens int     // Total tokens consumed so far.
	MaxTokens      int     // The configured budget limit.
	Percentage     float64 // Consumption as a fraction (0.0–1.0).
}

BudgetWarningEvent signals that token consumption crossed the configured threshold.

type CompactionEvent

type CompactionEvent struct {
	BeforeCount int // Message count before compaction.
	AfterCount  int // Message count after compaction.
	TurnCount   int
}

CompactionEvent signals that conversation history was compacted.

type Compactor

type Compactor interface {
	// ShouldCompact reports whether the current message history needs compaction.
	// Called at the beginning of each agentic loop iteration before the model call.
	ShouldCompact(messages []Message, usage *Usage) bool

	// Compact reduces the message history to fit within context limits.
	// Returns the compacted messages that replace the original history.
	// The implementation may use summarization, truncation, or any other strategy.
	Compact(ctx context.Context, messages []Message) ([]Message, error)
}

Compactor manages conversation history when the context window limit is approached. Implementations decide when compaction is needed and how to reduce the message history while preserving essential context.

type Config

type Config struct {
	// MaxTurns limits the number of agentic loop iterations. Each iteration
	// consists of a model call and optional tool execution. Zero means unlimited.
	MaxTurns int

	// MaxConcurrency limits the number of tools executing in parallel within
	// a single batch. Defaults to DefaultMaxConcurrency.
	MaxConcurrency int

	// SystemPrompt is prepended to the conversation as a system message.
	SystemPrompt string

	// Temperature controls the randomness of the model's output.
	// nil uses the provider's default.
	Temperature *float64

	// MaxTokens limits the maximum tokens in the model's response.
	// Zero uses the provider's default.
	MaxTokens int

	// RetryPolicy configures automatic retries for transient provider errors.
	// nil means no retries.
	RetryPolicy *RetryPolicy

	// EventBufferSize is the capacity of the event channel returned by Agent.Run.
	// Defaults to DefaultEventBufferSize.
	EventBufferSize int

	// OnEvent is an optional synchronous callback invoked for every event
	// before it is sent to the channel. Use for lightweight processing that
	// must see every event. Keep execution fast to avoid blocking the loop.
	OnEvent func(Event)

	// TokenBudget limits total token consumption across all turns.
	// nil means no budget limit.
	TokenBudget *TokenBudget

	// ResultLimiter controls how oversized tool results are handled.
	// Default: TruncateLimiter.
	ResultLimiter ResultLimiter

	// MaxResultChars is the maximum character count for a single tool result.
	// Results exceeding this are passed through the ResultLimiter.
	// Default: DefaultMaxResultChars (50000).
	MaxResultChars int

	// ExecutionMode controls which tools are visible to the model.
	// ModeLocal (default) allows all tools. ModeRemote restricts to remote-safe only.
	ExecutionMode ExecutionMode

	// DisableInputValidation skips JSON Schema validation of tool inputs.
	// Default: false (validation enabled). Set to true if tool schemas are
	// informational only and should not block execution on mismatch.
	DisableInputValidation bool

	// RateLimiter controls the rate of provider API calls. When set, the agent
	// waits for permission before each CreateStream call. nil means no rate limiting.
	RateLimiter RateLimiter

	// ToolRetries is the number of times to retry a failed tool execution before
	// returning the error to the model. Zero means no retries (default). Only
	// retries when the tool returns IsError: true.
	ToolRetries int

	// ToolTimeout sets a maximum duration for individual tool executions.
	// If a tool does not complete within this duration, its context is cancelled
	// and an error result is returned. Zero means no timeout (default).
	ToolTimeout time.Duration

	// ErrorStrategy controls how tool execution errors are handled. When set,
	// it is called after a tool returns IsError: true, allowing error transformation
	// or loop abortion. nil uses default behavior (send error to model as-is).
	ErrorStrategy ErrorStrategy

	// Logger enables structured logging for agent lifecycle events: model calls,
	// retries, compaction, budget warnings, and validation errors. nil disables
	// logging (zero overhead). Use log/slog for structured output.
	Logger *slog.Logger

	// ThinkingPrompt enables agentic thinking for non-native thinking models.
	// When set, the first turn's text output is emitted as EventThinkingDelta
	// instead of EventTextDelta. After the thinking turn completes, AnswerPrompt
	// is injected as a user message to trigger the final answer turn.
	// If the provider natively supports thinking (emits StreamEventThinkingDelta),
	// this feature is automatically disabled for that request.
	// Empty string means disabled (default).
	ThinkingPrompt string

	// AnswerPrompt is injected after the thinking turn to trigger the final answer.
	// Only used when ThinkingPrompt is set.
	AnswerPrompt string

	// ProviderExtras carries provider-specific parameters merged into every
	// request body. Examples: OpenRouter "plugins" for file parsing, Anthropic
	// cache control. Each provider picks the keys it understands.
	ProviderExtras map[string]any
}

Config controls Agent behavior. Fields are set via functional Option values passed to NewAgent. Zero values indicate defaults.

type ContentBlock

type ContentBlock struct {
	Type ContentBlockType `json:"type"`

	// Text is set when Type == ContentText.
	Text string `json:"text,omitempty"`

	// ToolCall is set when Type == ContentToolCall.
	ToolCall *ToolCall `json:"tool_call,omitempty"`

	// ToolResult is set when Type == ContentToolResult.
	ToolResult *ToolResultBlock `json:"tool_result,omitempty"`

	// Image is set when Type == ContentImage.
	Image *ImageContent `json:"image,omitempty"`

	// Document is set when Type == ContentDocument.
	Document *DocumentContent `json:"document,omitempty"`
}

ContentBlock is a discriminated union within a message. Exactly one of the typed fields is set, determined by the Type field.

type ContentBlockType

type ContentBlockType int

ContentBlockType discriminates the variant within a ContentBlock.

const (
	// ContentText represents a plain text content block.
	ContentText ContentBlockType = iota

	// ContentToolCall represents the model's request to invoke a tool.
	ContentToolCall

	// ContentToolResult represents the outcome of a tool execution sent back to the model.
	ContentToolResult

	// ContentImage represents an image content block (base64 or URL).
	ContentImage

	// ContentDocument represents a document/file content block (base64 or URL).
	// Supported by providers that accept file inputs (PDF, text, CSV, etc.).
	ContentDocument
)

type ContentDelta

type ContentDelta struct {
	Text string
}

ContentDelta represents an incremental text chunk from the model's response.

type DocumentContent

type DocumentContent struct {
	// Filename is the original filename (e.g., "report.pdf").
	Filename string `json:"filename"`

	// MediaType is the MIME type (e.g., "application/pdf", "text/plain", "text/csv").
	MediaType string `json:"media_type"`

	// Data is the base64-encoded file content. Set this for inline documents.
	Data string `json:"data,omitempty"`

	// URL is a publicly accessible document URL. Set this for URL-referenced documents.
	URL string `json:"url,omitempty"`
}

DocumentContent holds document data for file-based messages. Either Data (base64) or URL should be set, not both.

type ErrorAction

type ErrorAction int

ErrorAction determines how the agent loop handles a tool error.

const (
	// ErrorActionDefault sends the error to the model as-is (default behavior).
	ErrorActionDefault ErrorAction = iota

	// ErrorActionAbort terminates the agent loop immediately.
	ErrorActionAbort
)

type ErrorEvent

type ErrorEvent struct {
	Err       error
	Retrying  bool
	TurnCount int
}

ErrorEvent carries a recoverable error from the agentic loop.

type ErrorStrategy

type ErrorStrategy interface {
	// OnToolError is called when a tool returns IsError: true. It receives the
	// tool call and the error result, and returns a (possibly modified) result
	// and an action.
	OnToolError(call *ToolCall, result *ToolResult) (*ToolResult, ErrorAction)
}

ErrorStrategy controls how the agent handles tool execution errors. Implementations can transform error results, suppress them, or abort the loop.

type ErrorStrategyFunc

type ErrorStrategyFunc func(call *ToolCall, result *ToolResult) (*ToolResult, ErrorAction)

ErrorStrategyFunc adapts a function to the ErrorStrategy interface.

func (ErrorStrategyFunc) OnToolError

func (f ErrorStrategyFunc) OnToolError(call *ToolCall, result *ToolResult) (*ToolResult, ErrorAction)

OnToolError delegates to the wrapped function.

type Event

type Event struct {
	Type EventType

	TextDelta        *TextDeltaEvent        // Type == EventTextDelta
	ThinkDelta       *TextDeltaEvent        // Type == EventThinkingDelta
	ToolStart        *ToolStartEvent        // Type == EventToolStart
	ToolProgress     *ProgressEvent         // Type == EventToolProgress
	ToolEnd          *ToolEndEvent          // Type == EventToolEnd
	TurnStart        *TurnStartEvent        // Type == EventTurnStart
	TurnEnd          *TurnEndEvent          // Type == EventTurnEnd
	Message          *Message               // Type == EventMessage
	Error            *ErrorEvent            // Type == EventError
	Usage            *UsageEvent            // Type == EventUsage
	SubAgentStart    *SubAgentStartEvent    // Type == EventSubAgentStart
	SubAgentEnd      *SubAgentEndEvent      // Type == EventSubAgentEnd
	BudgetWarning    *BudgetWarningEvent    // Type == EventBudgetWarning
	Compaction       *CompactionEvent       // Type == EventCompaction
	Retry            *RetryEvent            // Type == EventRetry
	PermissionDenied *PermissionDeniedEvent // Type == EventPermissionDenied
	HookBlocked      *HookBlockedEvent      // Type == EventHookBlocked

	// SubAgentIndex identifies which child agent emitted this event.
	// Zero for the parent agent's own events. Set by SpawnChildren.
	SubAgentIndex int
}

Event is the primary output type of the agentic loop. It is a discriminated union where exactly one of the typed fields is set, determined by the Type field.

Events are delivered through the channel returned by Agent.Run. Consumers switch on Type to handle each variant.

type EventType

type EventType int

EventType discriminates the variant within an Event.

const (
	// EventTextDelta carries streaming text from the model's response.
	EventTextDelta EventType = iota

	// EventThinkingDelta carries streaming thinking/reasoning content from the model.
	EventThinkingDelta

	// EventToolStart signals that a tool execution is beginning.
	EventToolStart

	// EventToolProgress carries incremental progress from a running tool.
	EventToolProgress

	// EventToolEnd signals that a tool execution has completed.
	EventToolEnd

	// EventTurnStart signals the beginning of a new agentic loop iteration.
	EventTurnStart

	// EventTurnEnd signals the end of a loop iteration, or the loop itself.
	EventTurnEnd

	// EventMessage carries a complete message added to the conversation history.
	EventMessage

	// EventError carries a recoverable error (e.g., a retry is in progress).
	EventError

	// EventUsage carries token usage statistics for the current turn.
	EventUsage

	// EventSubAgentStart signals that a sub-agent has been spawned.
	EventSubAgentStart

	// EventSubAgentEnd signals that a sub-agent has completed.
	EventSubAgentEnd

	// EventBudgetWarning signals that token consumption crossed the warning threshold.
	EventBudgetWarning

	// EventCompaction signals that conversation history was compacted.
	EventCompaction

	// EventRetry signals that a provider call is being retried after a transient error.
	EventRetry

	// EventPermissionDenied signals that a tool call was blocked by the permission checker.
	EventPermissionDenied

	// EventHookBlocked signals that a hook blocked tool execution or the model call.
	EventHookBlocked
)

type ExecutionMode

type ExecutionMode int

ExecutionMode determines which environment the agent is running in. This controls which tools are visible to the model.

const (
	// ModeLocal allows all tools. Use when the agent runs on the user's machine
	// where filesystem and shell access are expected.
	ModeLocal ExecutionMode = iota

	// ModeRemote restricts tools to only those marked as remote-safe.
	// Use when the agent runs on a server where local filesystem/shell access
	// is inappropriate or dangerous. The model only sees remote-safe tools —
	// it cannot call tools it doesn't know about.
	ModeRemote
)

type HeadTailLimiter

type HeadTailLimiter struct {
	// HeadRatio is the fraction of maxChars allocated to the head (0.0–1.0).
	// Default: 0.7 (70% head, 30% tail).
	HeadRatio float64
}

HeadTailLimiter keeps only the first N and last M characters, discarding the middle. Simpler than TruncateLimiter with configurable head/tail ratio.

func (HeadTailLimiter) Limit

func (l HeadTailLimiter) Limit(result *ToolResult, maxChars int) *ToolResult

Limit applies head/tail truncation.

type HealthChecker

type HealthChecker interface {
	// HealthCheck tests whether the provider is reachable and operational.
	// Returns nil if healthy, or an error describing the failure.
	HealthCheck(ctx context.Context) error
}

HealthChecker is an optional interface that providers can implement to support proactive health checking. Use with fallback providers to detect unhealthy backends before they cause request failures.

type Hook

type Hook interface {
	// Phase returns when this hook should fire.
	Phase() HookPhase

	// Execute runs the hook logic. Returning a non-nil HookAction modifies
	// the pipeline behavior (block execution, modify input, inject messages).
	// Return nil action to proceed without modification.
	Execute(ctx context.Context, hc *HookContext) (*HookAction, error)
}

Hook intercepts the agent execution pipeline at defined lifecycle phases. Hooks execute synchronously in registration order. Keep hook execution fast to avoid blocking the agentic loop.

type HookAction

type HookAction struct {
	// Block, if true, prevents the current operation from proceeding.
	// For PreToolUse: the tool is not executed; BlockReason is sent to the model.
	// For PreModelCall: the model call is skipped; the loop terminates.
	Block       bool
	BlockReason string

	// ModifiedInput, if non-nil, replaces the tool's input before execution.
	// Only effective for PreToolUse hooks.
	ModifiedInput json.RawMessage

	// InjectMessages appends additional messages to the conversation before
	// the next model call. Effective for PostToolUse and OnTurnEnd hooks.
	InjectMessages []Message
}

HookAction tells the execution pipeline how to proceed after a hook fires. All fields are optional; a nil HookAction means "continue normally."

type HookBlockedEvent

type HookBlockedEvent struct {
	Phase     HookPhase
	ToolCall  *ToolCall // Non-nil for PreToolUse blocks.
	Reason    string
	TurnCount int
}

HookBlockedEvent signals that a hook blocked execution.

type HookContext

type HookContext struct {
	// Phase indicates which lifecycle phase triggered this hook.
	Phase HookPhase

	// ToolCall is set for tool-phase hooks (PreToolUse, PostToolUse).
	ToolCall *ToolCall

	// ToolResult is set for PostToolUse hooks.
	ToolResult *ToolResult

	// Messages is the current conversation history at the time the hook fires.
	Messages []Message

	// TurnCount is the current iteration number of the agentic loop.
	TurnCount int

	// Metadata is a mutable key-value bag that persists across hooks within a
	// single agent run. Hooks can use it to communicate state to each other.
	Metadata map[string]any
}

HookContext provides read access to the current execution state when a hook fires.

type HookFunc

type HookFunc struct {
	HookPhase HookPhase
	Fn        func(ctx context.Context, hc *HookContext) (*HookAction, error)
}

HookFunc is an adapter that allows ordinary functions to be used as hooks. It pairs a phase with a function, eliminating the need for a struct that implements the Hook interface for simple cases.

func (HookFunc) Execute

func (h HookFunc) Execute(ctx context.Context, hc *HookContext) (*HookAction, error)

Execute delegates to the wrapped function.

func (HookFunc) Phase

func (h HookFunc) Phase() HookPhase

Phase returns the hook's firing phase.

type HookPhase

type HookPhase int

HookPhase defines when a hook fires in the execution pipeline.

const (
	// HookPreToolUse fires after input validation, before permission check.
	// Can block execution or modify input.
	HookPreToolUse HookPhase = iota

	// HookPostToolUse fires after tool execution completes.
	// Can inspect results, log metrics, or inject follow-up messages.
	HookPostToolUse

	// HookPreModelCall fires before each model API call.
	// Can modify messages, inject system context, or block the call.
	HookPreModelCall

	// HookPostModelCall fires after the model response is fully received.
	// Can inspect the response, log usage, or trigger side effects.
	HookPostModelCall

	// HookOnTurnEnd fires when the agentic loop would normally terminate
	// (no more tool calls). Can inject messages to force continuation.
	HookOnTurnEnd
)

type ImageContent

type ImageContent struct {
	// MediaType is the MIME type (e.g., "image/png", "image/jpeg", "image/webp", "image/gif").
	MediaType string `json:"media_type"`

	// Data is the base64-encoded image data. Set this for inline images.
	Data string `json:"data,omitempty"`

	// URL is a publicly accessible image URL. Set this for URL-referenced images.
	URL string `json:"url,omitempty"`
}

ImageContent holds image data for multimodal messages. Either Data (base64) or URL should be set, not both.

type LocalityAware

type LocalityAware interface {
	Locality() ToolLocality
}

LocalityAware is an optional interface that tools can implement to declare their execution environment compatibility. Tools that do not implement this interface are treated as ToolLocalOnly — the safe default that prevents accidental remote execution of local-only tools.

type Message

type Message struct {
	Role    Role           `json:"role"`
	Content []ContentBlock `json:"content"`
}

Message is a single entry in the conversation history. Each message has a role and one or more content blocks that can be text, tool calls, or tool results.

func NewAssistantMessage

func NewAssistantMessage(text string) Message

NewAssistantMessage creates a Message with a single text content block from the assistant.

func NewDocumentMessage

func NewDocumentMessage(text string, docs ...DocumentContent) Message

NewDocumentMessage creates a user Message with text and one or more documents. Use for requests where you want the model to analyze uploaded files.

msg := agentflow.NewDocumentMessage("Summarize this PDF",
    agentflow.DocumentContent{Filename: "report.pdf", MediaType: "application/pdf", Data: base64Data},
)

func NewImageMessage

func NewImageMessage(text string, images ...ImageContent) Message

NewImageMessage creates a user Message with text and one or more images. Use for vision/multimodal requests where you want the model to analyze images.

msg := agentflow.NewImageMessage("What's in this image?",
    agentflow.ImageContent{MediaType: "image/png", Data: base64Data},
)

func NewImageURLMessage

func NewImageURLMessage(text, imageURL string) Message

NewImageURLMessage is a convenience for creating an image message from a URL.

func NewUserMessage

func NewUserMessage(text string) Message

NewUserMessage creates a Message with a single text content block from the user.

func (Message) Documents

func (m Message) Documents() []DocumentContent

Documents extracts all document content blocks from the message.

func (Message) Images

func (m Message) Images() []ImageContent

Images extracts all image content blocks from the message.

func (Message) TextContent

func (m Message) TextContent() string

TextContent extracts and concatenates all text blocks from the message.

func (Message) ToolCalls

func (m Message) ToolCalls() []ToolCall

ToolCalls extracts all tool call blocks from the message.

func (Message) ToolResults

func (m Message) ToolResults() []ToolResultBlock

ToolResults extracts all tool result blocks from the message.

type MultiPhaseHook

type MultiPhaseHook interface {
	// Phases returns all phases this hook should fire at.
	Phases() []HookPhase

	// Execute runs the hook logic for the given phase.
	Execute(ctx context.Context, hc *HookContext) (*HookAction, error)
}

MultiPhaseHook is an optional interface for hooks that fire at multiple phases. Hooks implementing this interface have their Execute called for each phase returned by Phases(), eliminating the need to register duplicate hooks.

type NoLimiter

type NoLimiter struct{}

NoLimiter passes all results through without modification. Use when you handle result sizing externally or don't need protection.

func (NoLimiter) Limit

func (NoLimiter) Limit(result *ToolResult, _ int) *ToolResult

Limit returns the result unchanged.

type Option

type Option func(*Agent)

Option is a functional option for configuring an Agent.

func WithCompactor

func WithCompactor(c Compactor) Option

WithCompactor sets the context compaction strategy.

func WithDisableInputValidation

func WithDisableInputValidation() Option

WithDisableInputValidation disables JSON Schema validation of tool inputs. Use this if tool schemas are informational only or if validation causes false positives with a particular model's output format.

func WithErrorStrategy

func WithErrorStrategy(s ErrorStrategy) Option

WithErrorStrategy sets a custom error handling strategy for tool failures. The strategy can transform error results or abort the loop entirely.

agent := agentflow.NewAgent(provider,
    agentflow.WithErrorStrategy(agentflow.ErrorStrategyFunc(
        func(call *agentflow.ToolCall, result *agentflow.ToolResult) (*agentflow.ToolResult, agentflow.ErrorAction) {
            if call.Name == "critical_tool" {
                return result, agentflow.ErrorActionAbort
            }
            return result, agentflow.ErrorActionDefault
        },
    )),
)

func WithEventBufferSize

func WithEventBufferSize(n int) Option

WithEventBufferSize sets the capacity of the event channel.

func WithExecutionMode

func WithExecutionMode(mode ExecutionMode) Option

WithExecutionMode sets the execution environment mode. ModeRemote restricts the agent to only remote-safe tools — local-only tools are completely hidden from the model. ModeLocal (default) allows all registered tools.

// Server deployment — only web search and HTTP tools are available:
agent := agentflow.NewAgent(provider,
    agentflow.WithTools(builtin.All()...),
    agentflow.WithExecutionMode(agentflow.ModeRemote),
)

func WithHook

func WithHook(h Hook) Option

WithHook registers a lifecycle hook with the agent.

func WithLogger

func WithLogger(logger *slog.Logger) Option

WithLogger enables structured logging for agent lifecycle events. The logger receives log entries for model calls, retries, compaction, budget warnings, and input validation errors. nil disables logging.

agent := agentflow.NewAgent(provider,
    agentflow.WithLogger(slog.Default()),
)

func WithMaxConcurrency

func WithMaxConcurrency(n int) Option

WithMaxConcurrency sets the maximum number of parallel tool executions.

func WithMaxResultSize

func WithMaxResultSize(chars int) Option

WithMaxResultSize sets the maximum characters for a single tool result. Results exceeding this are passed through the configured ResultLimiter.

func WithMaxTokens

func WithMaxTokens(n int) Option

WithMaxTokens sets the maximum tokens in the model's response.

func WithMaxTurns

func WithMaxTurns(n int) Option

WithMaxTurns sets the maximum number of agentic loop iterations.

func WithOnEvent

func WithOnEvent(fn func(Event)) Option

WithOnEvent sets a synchronous event callback. The callback is invoked for every event before it is sent to the channel. Keep execution fast.

func WithPermission

func WithPermission(p PermissionChecker) Option

WithPermission sets the permission checker for tool invocations. Only one permission checker is active; the last one set wins. Use ChainPermission to compose multiple checkers.

func WithProviderExtras

func WithProviderExtras(extras map[string]any) Option

WithProviderExtras sets provider-specific parameters that are merged into every request body. Each provider picks the keys it understands and ignores the rest. This enables provider-specific features without breaking the provider-agnostic API.

Example: OpenRouter file-parser plugin for PDF support:

agent := agentflow.NewAgent(provider,
    agentflow.WithProviderExtras(map[string]any{
        "plugins": []map[string]any{
            {"id": "file-parser", "pdf": map[string]any{"engine": "native"}},
        },
    }),
)

func WithRateLimiter

func WithRateLimiter(limiter RateLimiter) Option

WithRateLimiter sets a rate limiter for provider API calls. The agent will call limiter.Wait() before each model request, blocking if the rate limit is exceeded.

agent := agentflow.NewAgent(provider,
    agentflow.WithRateLimiter(agentflow.NewTokenBucketLimiter(10, time.Second)),
)

func WithResultLimiter

func WithResultLimiter(limiter ResultLimiter) Option

WithResultLimiter sets a custom result limiter for oversized tool outputs.

func WithRetryPolicy

func WithRetryPolicy(p RetryPolicy) Option

WithRetryPolicy configures automatic retries for transient provider errors.

func WithSessionStore

func WithSessionStore(store SessionStore) Option

WithSessionStore enables session persistence. When set, RunSession and Resume methods become available for saving and restoring conversation state.

func WithSystemPrompt

func WithSystemPrompt(s string) Option

WithSystemPrompt sets the system prompt prepended to every model call.

func WithTemperature

func WithTemperature(t float64) Option

WithTemperature sets the model's temperature parameter.

func WithThinkingPrompt

func WithThinkingPrompt(thinkPrompt, answerPrompt string) Option

WithThinkingPrompt enables agentic thinking for non-native thinking models. The first turn emits all model output as EventThinkingDelta (not EventTextDelta). After the thinking turn, answerPrompt is injected as a user message and the model generates a normal response emitted as EventTextDelta.

If the provider natively supports thinking (emits StreamEventThinkingDelta), this feature is automatically disabled — native thinking takes precedence.

agent := agentflow.NewAgent(provider,
    agentflow.WithThinkingPrompt(
        "Analyze this step by step. Do not provide a final answer yet.",
        "Now provide a clear and concise final answer.",
    ),
)

func WithTokenBudget

func WithTokenBudget(budget TokenBudget) Option

WithTokenBudget sets a token consumption limit for the agent run. The loop terminates with TurnEndBudgetExhausted when the budget is exceeded.

agent := agentflow.NewAgent(provider,
    agentflow.WithTokenBudget(agentflow.TokenBudget{
        MaxTokens:        100000,
        WarningThreshold: 0.8,
    }),
)

func WithTool

func WithTool(t Tool) Option

WithTool registers a single tool with the agent.

func WithToolRetries

func WithToolRetries(n int) Option

WithToolRetries sets the number of retry attempts for failed tool executions. Only retries when a tool returns IsError: true. Zero (default) means no retries.

agent := agentflow.NewAgent(provider,
    agentflow.WithToolRetries(2), // retry up to 2 times on error
)

func WithToolTimeout

func WithToolTimeout(d time.Duration) Option

WithToolTimeout sets a maximum duration for individual tool executions. Tools that exceed this duration have their context cancelled and receive an error result. Zero (default) means no timeout.

agent := agentflow.NewAgent(provider,
    agentflow.WithToolTimeout(30 * time.Second),
)

func WithTools

func WithTools(tools ...Tool) Option

WithTools registers multiple tools with the agent.

type OrchestrateResult

type OrchestrateResult struct {
	Index  int
	Task   string
	Result string
	Error  error
}

OrchestrateResult holds the outcome of a single sub-agent task.

func Orchestrate

func Orchestrate(ctx context.Context, parent *Agent, cfg SubAgentConfig, tasks []string) []OrchestrateResult

Orchestrate runs multiple tasks in parallel using sub-agents and collects results. This is a high-level utility for common fan-out/fan-in patterns.

results := agentflow.Orchestrate(ctx, agent, agentflow.SubAgentConfig{
    SystemPrompt: "You are a research assistant.",
    MaxTurns:     5,
}, []string{
    "Research Go concurrency patterns",
    "Research Go error handling best practices",
    "Research Go testing strategies",
})

for _, r := range results {
    fmt.Println(r.Task, "→", r.Result)
}

func (OrchestrateResult) String

func (r OrchestrateResult) String() string

type PermissionChecker

type PermissionChecker interface {
	// Check returns the permission decision for a tool invocation. The checker
	// receives both the call details and the tool definition, enabling fine-grained
	// decisions based on the specific input.
	Check(ctx context.Context, call *ToolCall, tool Tool) (PermissionResult, error)
}

PermissionChecker controls whether a tool is allowed to execute for a given invocation. Implementations can be static rule-based, interactive, or policy-driven.

func AllowAll

func AllowAll() PermissionChecker

AllowAll returns a PermissionChecker that permits every tool invocation. Use in trusted environments where all tools are safe to execute.

func AllowList

func AllowList(names ...string) PermissionChecker

AllowList returns a PermissionChecker that allows only tools whose names appear in the provided list. All other tools are denied.

func ChainPermission

func ChainPermission(checkers ...PermissionChecker) PermissionChecker

ChainPermission evaluates multiple checkers in order. The first non-Allow result wins. If all checkers return Allow, the final result is Allow. An empty chain allows everything.

func DenyAll

func DenyAll() PermissionChecker

DenyAll returns a PermissionChecker that blocks every tool invocation.

func DenyList

func DenyList(names ...string) PermissionChecker

DenyList returns a PermissionChecker that denies tools whose names appear in the provided list. All other tools are allowed.

func ReadOnlyPermission

func ReadOnlyPermission() PermissionChecker

ReadOnlyPermission returns a PermissionChecker that allows only tools where IsReadOnly returns true for the given input. Write operations are denied.

type PermissionDeniedEvent

type PermissionDeniedEvent struct {
	ToolCall ToolCall
}

PermissionDeniedEvent signals that a tool call was blocked by permissions.

type PermissionFunc

type PermissionFunc func(ctx context.Context, call *ToolCall, tool Tool) (PermissionResult, error)

PermissionFunc is an adapter that allows ordinary functions to be used as permission checkers, similar to http.HandlerFunc.

func (PermissionFunc) Check

func (f PermissionFunc) Check(ctx context.Context, call *ToolCall, tool Tool) (PermissionResult, error)

Check delegates to the wrapped function.

type PermissionResult

type PermissionResult int

PermissionResult is the outcome of a permission check.

const (
	// PermissionAllow permits the tool execution to proceed.
	PermissionAllow PermissionResult = iota

	// PermissionDeny blocks the tool execution. The model receives an error
	// message indicating the tool was denied.
	PermissionDeny

	// PermissionAsk indicates an external decision is needed (e.g., prompting
	// the user). The framework calls the AskFunc configured on the Agent.
	PermissionAsk
)

type ProgressEvent

type ProgressEvent struct {
	// ToolCallID identifies which tool invocation this progress belongs to.
	// Set automatically by the executor; tools do not need to set this.
	ToolCallID string

	// Message is a human-readable progress description.
	Message string

	// Data carries tool-specific structured progress information.
	Data any
}

ProgressEvent describes incremental progress from a tool execution.

type ProgressFunc

type ProgressFunc func(ProgressEvent)

ProgressFunc is called by tools to report incremental progress during execution. Progress events are forwarded to the agent's event stream in real time.

type Provider

type Provider interface {
	// CreateStream initiates a streaming model call and returns a Stream that
	// yields events as they arrive from the API. The caller must consume the
	// stream to completion or cancel via context.
	//
	// The Request contains provider-agnostic fields (messages, tools, parameters).
	// Implementations convert these into vendor-specific API formats.
	CreateStream(ctx context.Context, req *Request) (Stream, error)

	// ModelID returns the identifier of the model this provider targets
	// (e.g., "anthropic/claude-sonnet-4-20250514", "openai/gpt-4o").
	ModelID() string
}

Provider abstracts an AI model API that supports tool use (function calling). Implementations handle authentication, request formatting, and response parsing specific to each vendor (Anthropic, OpenAI, OpenRouter, etc.).

The interface operates at the stream level rather than request/response level, enabling real-time event delivery to consumers.

type ProviderError

type ProviderError struct {
	StatusCode      int
	Message         string
	Retryable       bool
	Err             error
	ResponseHeaders http.Header
}

ProviderError wraps an error from the AI provider with status and retry information.

func (*ProviderError) Error

func (e *ProviderError) Error() string

func (*ProviderError) IsContextTooLarge

func (e *ProviderError) IsContextTooLarge() bool

IsContextTooLarge reports whether the error indicates the request context exceeds the model's maximum token limit.

func (*ProviderError) IsRetryable

func (e *ProviderError) IsRetryable() bool

IsRetryable reports whether the error is transient and the operation can be retried. Checks multiple signals following the pattern used by production AI SDKs:

  • io.ErrUnexpectedEOF (stream ended unexpectedly)
  • x-should-retry response header
  • HTTP status codes: 408, 409, 429, 5xx

func (*ProviderError) Unwrap

func (e *ProviderError) Unwrap() error

type RateLimiter

type RateLimiter interface {
	// Wait blocks until the caller is allowed to proceed. Returns an error
	// if the context is cancelled or the limiter is otherwise unable to grant
	// permission.
	Wait(ctx context.Context) error
}

RateLimiter controls the rate of API calls to a provider. Implementations block until the caller is permitted to proceed, or return an error if the context is cancelled while waiting.

type Request

type Request struct {
	// Messages is the conversation history including user messages, assistant
	// responses, and tool results from previous iterations.
	Messages []Message

	// SystemPrompt is an optional system-level instruction prepended to the conversation.
	SystemPrompt string

	// Tools is the list of tool definitions available to the model for this request.
	Tools []ToolDefinition

	// MaxTokens limits the maximum number of tokens in the model's response.
	MaxTokens int

	// Temperature controls the randomness of the model's output.
	// nil means the provider's default is used.
	Temperature *float64

	// StopSequences are optional strings that cause the model to stop generating
	// when encountered. Support varies by provider.
	StopSequences []string

	// Metadata carries key-value pairs that providers may propagate as HTTP
	// headers. Use this for trace context propagation (e.g., "traceparent",
	// "tracestate") or custom request tagging. Providers add these as headers
	// prefixed with nothing — keys map directly to header names.
	Metadata map[string]string

	// ProviderExtras carries provider-specific parameters that are merged into
	// the request body. Examples: OpenRouter "plugins" for file parsing,
	// Anthropic cache control, Gemini safety settings. Each provider picks
	// the keys it understands and ignores the rest.
	ProviderExtras map[string]any
}

Request is the provider-agnostic model request built by the agentic loop.

type ResultLimiter

type ResultLimiter interface {
	// Limit inspects a tool result and returns a potentially modified version.
	// If the result is within acceptable bounds, it is returned unchanged.
	// maxChars is the configured maximum character count.
	Limit(result *ToolResult, maxChars int) *ToolResult
}

ResultLimiter controls how oversized tool results are handled before being sent back to the model. Large results can exhaust the context window and degrade model performance.

type RetryEvent

type RetryEvent struct {
	Attempt   int           // Current retry attempt (1-based).
	Delay     time.Duration // Delay before this retry.
	Err       error         // The error that triggered the retry.
	TurnCount int
}

RetryEvent signals that a provider call is being retried.

type RetryPolicy

type RetryPolicy struct {
	// MaxRetries is the maximum number of retry attempts. Zero means no retries.
	MaxRetries int

	// BaseDelay is the initial delay before the first retry.
	// Subsequent retries use exponential backoff: BaseDelay * 2^attempt.
	BaseDelay time.Duration

	// MaxDelay caps the maximum delay between retries.
	MaxDelay time.Duration
}

RetryPolicy configures automatic retries for transient errors.

type Role

type Role string

Role identifies the author of a message in the conversation.

const (
	RoleUser      Role = "user"
	RoleAssistant Role = "assistant"
	RoleSystem    Role = "system"
)

type Session

type Session struct {
	// ID uniquely identifies this session. Generated automatically if empty.
	ID string `json:"id"`

	// Messages is the full conversation history at the time of save.
	Messages []Message `json:"messages"`

	// Metadata is an arbitrary key-value bag for application-specific data
	// (user ID, task description, tags, etc.).
	Metadata map[string]any `json:"metadata,omitempty"`

	// CreatedAt is when the session was first created.
	CreatedAt time.Time `json:"created_at"`

	// UpdatedAt is when the session was last saved.
	UpdatedAt time.Time `json:"updated_at"`

	// ModelID records which model was used, for reference when resuming.
	ModelID string `json:"model_id,omitempty"`

	// TurnCount records how many turns were completed before saving.
	TurnCount int `json:"turn_count"`
}

Session holds the complete state needed to resume an agent conversation.

type SessionInfo

type SessionInfo struct {
	ID        string         `json:"id"`
	CreatedAt time.Time      `json:"created_at"`
	UpdatedAt time.Time      `json:"updated_at"`
	ModelID   string         `json:"model_id,omitempty"`
	TurnCount int            `json:"turn_count"`
	Metadata  map[string]any `json:"metadata,omitempty"`

	// Preview is the first user message text, truncated for display.
	Preview string `json:"preview,omitempty"`
}

SessionInfo is a lightweight summary returned by SessionStore.List.

type SessionStore

type SessionStore interface {
	// Save persists the current conversation state. If a session with the given
	// ID already exists, it is overwritten.
	Save(ctx context.Context, session *Session) error

	// Load retrieves a previously saved session by ID. Returns ErrSessionNotFound
	// if the session does not exist.
	Load(ctx context.Context, id string) (*Session, error)

	// List returns metadata for all stored sessions, ordered by last update time
	// (most recent first).
	List(ctx context.Context) ([]SessionInfo, error)

	// Delete removes a session by ID. Returns nil if the session does not exist.
	Delete(ctx context.Context, id string) error
}

SessionStore persists conversation state so agents can survive restarts, resume interrupted work, and maintain history across multiple invocations.

Implementations must be safe for concurrent use from multiple goroutines.

type Stream

type Stream interface {
	// Next blocks until the next event is available. Returns io.EOF when the
	// stream is complete. Any other error indicates a failure in the stream.
	Next() (StreamEvent, error)

	// Close releases resources associated with the stream. Safe to call
	// multiple times. Must be called even if Next returned an error.
	Close() error

	// Usage returns token usage statistics after the stream completes.
	// Returns nil if the stream has not finished or the provider does not
	// report usage information.
	Usage() *Usage
}

Stream yields events from a model response. The caller reads events sequentially via Next() until io.EOF signals completion. The stream must be closed when done, even after encountering an error.

Implementations must be safe to consume from a single goroutine. The StreamEvent returned by Next is only valid until the next call to Next.

type StreamEvent

type StreamEvent struct {
	Type StreamEventType

	// Delta is set when Type == StreamEventDelta.
	Delta *ContentDelta

	// ThinkingDelta is set when Type == StreamEventThinkingDelta.
	ThinkingDelta *ContentDelta

	// ToolCall is set when Type == StreamEventToolCall.
	ToolCall *ToolCall

	// Error is set when Type == StreamEventError.
	Error error

	// Usage is set when Type == StreamEventUsage.
	Usage *Usage
}

StreamEvent is a discriminated union of events from a model stream. Exactly one of the typed fields is set, determined by the Type field.

type StreamEventType

type StreamEventType int

StreamEventType discriminates the variant within a StreamEvent.

const (
	// StreamEventDelta carries a text content delta from the model.
	StreamEventDelta StreamEventType = iota

	// StreamEventToolCall carries a complete tool invocation from the model.
	StreamEventToolCall

	// StreamEventError carries a non-fatal error from the stream.
	StreamEventError

	// StreamEventDone signals that the stream has completed normally.
	StreamEventDone

	// StreamEventUsage carries token usage information, typically at stream end.
	StreamEventUsage

	// StreamEventThinkingDelta carries a thinking/reasoning content delta.
	StreamEventThinkingDelta
)

type SubAgentConfig

type SubAgentConfig struct {
	// Provider overrides the parent's provider. nil inherits the parent's.
	Provider Provider

	// Tools overrides the parent's tool set. nil inherits the parent's tools.
	// Use an empty slice to spawn a child with no tools.
	Tools []Tool

	// SystemPrompt overrides the parent's system prompt for the child.
	SystemPrompt string

	// MaxTurns limits the child's loop iterations. Zero inherits the parent's.
	MaxTurns int

	// MaxTokens overrides the response token limit. Zero inherits.
	MaxTokens int

	// MaxConcurrency overrides parallel tool execution limit. Zero inherits.
	MaxConcurrency int

	// Hooks are additional hooks for the child. Parent hooks are NOT inherited
	// to keep child execution isolated.
	Hooks []Hook

	// Permission overrides the parent's permission checker. nil inherits.
	Permission PermissionChecker
}

SubAgentConfig configures a child agent spawned by a parent. Fields left at zero values inherit from the parent agent.

type SubAgentEndEvent

type SubAgentEndEvent struct {
	Index  int    // Child index matching SubAgentStartEvent.
	Task   string // The original task.
	Result string // The sub-agent's final text response.
}

SubAgentEndEvent signals that a sub-agent has completed its work.

type SubAgentStartEvent

type SubAgentStartEvent struct {
	Index int    // Child index (0-based) within SpawnChildren.
	Task  string // The task delegated to the sub-agent.
}

SubAgentStartEvent signals that a sub-agent has been spawned.

type TextDeltaEvent

type TextDeltaEvent struct {
	Text string
}

TextDeltaEvent carries an incremental text chunk from the model.

type TimeoutAware

type TimeoutAware interface {
	Timeout() time.Duration
}

TimeoutAware is an optional interface that tools can implement to declare their own execution timeout. When implemented and the returned duration is positive, this timeout takes precedence over the global WithToolTimeout configuration. Tools that do not implement this interface use the global timeout.

type TokenBucketLimiter

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

TokenBucketLimiter implements a token bucket rate limiting algorithm. It allows bursts up to the bucket capacity and refills tokens at a steady rate.

limiter := agentflow.NewTokenBucketLimiter(10, time.Second) // 10 requests/sec
agent := agentflow.NewAgent(provider,
    agentflow.WithRateLimiter(limiter),
)

func NewTokenBucketLimiter

func NewTokenBucketLimiter(capacity int, interval time.Duration) *TokenBucketLimiter

NewTokenBucketLimiter creates a rate limiter that allows up to capacity requests per interval. The bucket starts full, allowing an initial burst.

NewTokenBucketLimiter(5, time.Second)    // 5 req/sec, burst of 5
NewTokenBucketLimiter(60, time.Minute)   // 60 req/min, burst of 60
NewTokenBucketLimiter(1, 200*time.Millisecond) // 5 req/sec, no burst

func (*TokenBucketLimiter) Wait

func (l *TokenBucketLimiter) Wait(ctx context.Context) error

Wait blocks until a token is available or the context is cancelled.

type TokenBudget

type TokenBudget struct {
	// MaxTokens is the total token limit for the entire agent run.
	// Includes both prompt and completion tokens across all turns.
	MaxTokens int

	// WarningThreshold is the fraction (0.0–1.0) at which a budget warning
	// event is emitted. For example, 0.8 means warn at 80% consumption.
	// Zero disables the warning. Values above 1.0 are clamped to 1.0.
	WarningThreshold float64
}

TokenBudget controls the maximum token consumption for a single agent run. When the budget is exhausted, the agentic loop terminates gracefully with TurnEndBudgetExhausted. A warning event is emitted when the threshold is crossed.

type Tool

type Tool interface {
	// Name returns the unique identifier for this tool. Must be alphanumeric
	// with underscores, matching the pattern [a-zA-Z_][a-zA-Z0-9_]*.
	Name() string

	// Description returns a human-readable description sent to the model.
	// A clear, specific description significantly improves tool selection accuracy.
	Description() string

	// InputSchema returns the JSON Schema object describing the tool's input parameters.
	// This schema is sent to the model and used for input validation before execution.
	InputSchema() map[string]any

	// Execute runs the tool with the given validated input. The context carries
	// cancellation signals from the agent loop. Implementations should respect
	// context cancellation for long-running operations.
	//
	// The progress function, if non-nil, can be called to report incremental
	// progress updates that are forwarded to the event stream.
	Execute(ctx context.Context, input json.RawMessage, progress ProgressFunc) (*ToolResult, error)

	// IsConcurrencySafe reports whether this tool can safely execute in parallel
	// with other concurrency-safe tools given the specific input. Read-only tools
	// should return true. Tools that mutate shared state should return false.
	IsConcurrencySafe(input json.RawMessage) bool

	// IsReadOnly reports whether the tool performs only read operations for the
	// given input. Used by permission checkers to make access control decisions.
	IsReadOnly(input json.RawMessage) bool
}

Tool defines a capability that an agent can invoke. Tools are registered with an Agent and presented to the AI model as callable functions. The model decides when and how to invoke tools based on their name, description, and input schema.

Implementations must be safe for concurrent use if IsConcurrencySafe returns true.

func SubAgentTool

func SubAgentTool(provider Provider, systemPrompt string, maxTurns int) Tool

SubAgentTool creates a Tool that allows the AI model to spawn sub-agents. When the model calls this tool, a child agent is created to handle the delegated task, and its final response is returned as the tool result.

agent := agentflow.NewAgent(provider,
    agentflow.WithTools(
        agentflow.SubAgentTool(provider, "You are a researcher.", 5),
    ),
)

type ToolCall

type ToolCall struct {
	ID    string          `json:"id"`
	Name  string          `json:"name"`
	Input json.RawMessage `json:"input"`
}

ToolCall represents the model's request to invoke a tool. The ID is generated by the provider and must be referenced in the corresponding ToolResultBlock.

type ToolDefinition

type ToolDefinition struct {
	Name        string         `json:"name"`
	Description string         `json:"description"`
	InputSchema map[string]any `json:"input_schema"`
}

ToolDefinition is the serializable schema sent to the AI provider. It describes a tool's interface so the model knows how to invoke it.

type ToolEndEvent

type ToolEndEvent struct {
	ToolCall ToolCall
	Result   ToolResult
	Duration time.Duration
}

ToolEndEvent signals that a tool invocation has completed.

type ToolError

type ToolError struct {
	ToolName   string
	ToolCallID string
	Err        error
}

ToolError wraps an error from tool execution with the tool name and call ID.

func (*ToolError) Error

func (e *ToolError) Error() string

func (*ToolError) Unwrap

func (e *ToolError) Unwrap() error

type ToolLocality

type ToolLocality int

ToolLocality declares which execution environments a tool supports.

const (
	// ToolLocalOnly means the tool requires local machine access (filesystem,
	// shell, local processes). Blocked in ModeRemote.
	ToolLocalOnly ToolLocality = iota

	// ToolRemoteSafe means the tool is safe for server execution. It does not
	// access the local filesystem or run local commands. Allowed in both modes.
	ToolRemoteSafe

	// ToolAny means the tool has no environment dependency. It works identically
	// in both local and remote modes (e.g., sleep, pure computation).
	ToolAny
)

type ToolResult

type ToolResult struct {
	// Content is the textual result sent to the model. For successful operations,
	// this contains the output data. For failures, this contains the error description.
	Content string

	// IsError indicates the tool execution failed. The model receives Content as
	// an error message and can adapt its approach (retry, use a different tool, etc.).
	IsError bool

	// Metadata is not sent to the model but is included in ToolEndEvent for
	// observability. Use it for execution metrics, debug data, or audit information.
	Metadata map[string]any
}

ToolResult is the outcome of a tool execution. The Content field is sent back to the model, while Metadata is included only in events for observability.

type ToolResultBlock

type ToolResultBlock struct {
	ToolCallID string `json:"tool_use_id"`
	Content    string `json:"content"`
	IsError    bool   `json:"is_error,omitempty"`
}

ToolResultBlock is the content block sent back to the model after tool execution. It references the original ToolCall by ID so the model can correlate results.

type ToolStartEvent

type ToolStartEvent struct {
	ToolCall ToolCall
}

ToolStartEvent signals that a tool invocation is about to execute.

type TruncateLimiter

type TruncateLimiter struct{}

TruncateLimiter truncates oversized results with a notice showing how much content was omitted. The truncation preserves the beginning and end of the content for maximum context.

func (TruncateLimiter) Limit

func (TruncateLimiter) Limit(result *ToolResult, maxChars int) *ToolResult

Limit truncates the result content if it exceeds maxChars. It preserves the first 80% and last 20% of the allowed size, inserting a truncation notice in the middle.

type TurnEndEvent

type TurnEndEvent struct {
	TurnNumber int
	Reason     TurnEndReason
	Messages   []Message // Final conversation history at loop termination.
}

TurnEndEvent signals the end of an iteration or the entire loop.

type TurnEndReason

type TurnEndReason string

TurnEndReason describes why the agentic loop iteration or the loop itself ended.

const (
	// TurnEndComplete means the model finished without requesting tool calls.
	TurnEndComplete TurnEndReason = "completed"

	// TurnEndMaxTurns means the configured maximum turn limit was reached.
	TurnEndMaxTurns TurnEndReason = "max_turns"

	// TurnEndAborted means the context was cancelled by the caller.
	TurnEndAborted TurnEndReason = "aborted"

	// TurnEndError means an unrecoverable error terminated the loop.
	TurnEndError TurnEndReason = "error"

	// TurnEndHookBlock means a hook prevented the loop from continuing.
	TurnEndHookBlock TurnEndReason = "hook_blocked"

	// TurnEndBudgetExhausted means the token budget was fully consumed.
	TurnEndBudgetExhausted TurnEndReason = "budget_exhausted"
)

type TurnStartEvent

type TurnStartEvent struct {
	TurnNumber int
}

TurnStartEvent signals the beginning of a new iteration in the agentic loop.

type Usage

type Usage struct {
	PromptTokens     int `json:"prompt_tokens"`
	CompletionTokens int `json:"completion_tokens"`
	TotalTokens      int `json:"total_tokens"`
}

Usage reports token consumption for a model request.

type UsageEvent

type UsageEvent struct {
	Usage     Usage
	TurnCount int
}

UsageEvent carries token usage statistics for a single model call.

Directories

Path Synopsis
_examples
basic command
Basic example: a minimal agent with a working calculator tool.
Basic example: a minimal agent with a working calculator tool.
chat command
custom_tools command
Custom tools example: an agent with multiple tools including web search and file reading.
Custom tools example: an agent with multiple tools including web search and file reading.
streaming command
Streaming example: production-ready HTTP SSE endpoint for an agentic AI.
Streaming example: production-ready HTTP SSE endpoint for an agentic AI.
internal
jsonschema
Package jsonschema provides lightweight JSON Schema validation for tool inputs.
Package jsonschema provides lightweight JSON Schema validation for tool inputs.
sse
Package sse provides shared types and SSE stream parsing for OpenAI-compatible chat completion APIs.
Package sse provides shared types and SSE stream parsing for OpenAI-compatible chat completion APIs.
Package middleware provides reusable hooks for common cross-cutting concerns like logging, metrics collection, and panic recovery.
Package middleware provides reusable hooks for common cross-cutting concerns like logging, metrics collection, and panic recovery.
Package observability provides tracing and cost tracking for agent runs.
Package observability provides tracing and cost tracking for agent runs.
Package plan provides a two-phase planning workflow for agents.
Package plan provides a two-phase planning workflow for agents.
provider
anthropic
Package anthropic provides an agentflow Provider implementation for the Anthropic Messages API (https://docs.anthropic.com/en/api/messages).
Package anthropic provides an agentflow Provider implementation for the Anthropic Messages API (https://docs.anthropic.com/en/api/messages).
fallback
Package fallback provides a Provider that cascades through multiple providers.
Package fallback provides a Provider that cascades through multiple providers.
gemini
Package gemini provides an agentflow Provider implementation for the Google Gemini API (https://ai.google.dev/api/generate-content).
Package gemini provides an agentflow Provider implementation for the Google Gemini API (https://ai.google.dev/api/generate-content).
groq
Package groq provides an agentflow Provider implementation for the Groq API.
Package groq provides an agentflow Provider implementation for the Groq API.
mock
Package mock provides a deterministic mock provider for testing agentflow agents.
Package mock provides a deterministic mock provider for testing agentflow agents.
ollama
Package ollama provides an agentflow Provider implementation for Ollama-compatible APIs (https://github.com/ollama/ollama/blob/main/docs/api.md).
Package ollama provides an agentflow Provider implementation for Ollama-compatible APIs (https://github.com/ollama/ollama/blob/main/docs/api.md).
openai
Package openai provides an agentflow Provider implementation for the OpenAI Chat Completions API (https://platform.openai.com/docs/api-reference/chat).
Package openai provides an agentflow Provider implementation for the OpenAI Chat Completions API (https://platform.openai.com/docs/api-reference/chat).
openrouter
Package openrouter provides an agentflow Provider implementation for the OpenRouter API (https://openrouter.ai).
Package openrouter provides an agentflow Provider implementation for the OpenRouter API (https://openrouter.ai).
session
filestore
Package filestore provides a file-system-based SessionStore implementation.
Package filestore provides a file-system-based SessionStore implementation.
memstore
Package memstore provides an in-memory SessionStore implementation.
Package memstore provides an in-memory SessionStore implementation.
Package skill provides reusable workflow templates that agents can invoke by name.
Package skill provides reusable workflow templates that agents can invoke by name.
Package task provides a thread-safe task store for agents to create, track, and manage units of work during execution.
Package task provides a thread-safe task store for agents to create, track, and manage units of work during execution.
Package team provides multi-agent coordination with shared communication channels and memory.
Package team provides multi-agent coordination with shared communication channels and memory.
Package tools provides utilities for building agentflow tools with a fluent API.
Package tools provides utilities for building agentflow tools with a fluent API.
builtin
Package builtin provides ready-to-use tools for common agent operations.
Package builtin provides ready-to-use tools for common agent operations.
Package trigger provides scheduled agent execution.
Package trigger provides scheduled agent execution.

Jump to

Keyboard shortcuts

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