agentkit

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

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

Go to latest
Published: Jan 14, 2026 License: MIT Imports: 22 Imported by: 0

README

AgentKit

A Go framework for building LLM-powered agents with tool calling, streaming, and elegant DX.

Go Reference Go Report Card

Installation

go get github.com/darkostanimirovic/agentkit@latest

Philosophy

AgentKit is inspired by Pydantic AI's design but adapted for Go best practices:

  • Explicit over implicit: No magic decorators, clear function calls
  • Type-safe: Leverages Go generics for context dependencies
  • Composable: Builder pattern for tools, functional options for configuration
  • Channel-based: Native Go channels for streaming events
  • Framework-agnostic: Can be used with any database, web framework, or LLM provider
  • Modern API: Uses OpenAI's Responses API for stateful conversations and advanced features

Architecture

AgentKit uses OpenAI's Responses API (not the older Chat Completions API) for:

  • Stateful conversations: Automatic conversation management with previous_response_id
  • Built-in tools: Support for web search, file search, and other OpenAI-provided tools
  • Better streaming: More robust streaming with server-sent events
  • Future-proof: Access to the latest OpenAI features like reasoning models and structured outputs

The framework handles the complexity of:

  • Converting between OpenAI tool formats and Response API formats
  • Managing conversation state across multiple turns
  • Tool execution and result handling
  • Streaming response parsing and event emission

Quick Start

package main

import (
    "context"
    "log"
    "os"

    "github.com/darkostanimirovic/agentkit"
)

func main() {
    // Create agent
    agent, err := agentkit.New(agentkit.Config{
        APIKey:       os.Getenv("OPENAI_API_KEY"),
        Model:        "gpt-4o-mini",
        SystemPrompt: buildSystemPrompt,
        MaxIterations: 5,
    })
    if err != nil {
        log.Fatal(err)
    }

    // Register tools
    agent.AddTool(
        agentkit.NewTool("search").
            WithDescription("Search for information").
            WithParameter("query", agentkit.String().Required().WithDescription("Search query")).
            WithHandler(searchHandler).
            Build(),
    )

    // Run agent with streaming
    ctx := agentkit.WithDeps(context.Background(), myDeps)
    events := agent.Run(ctx, "Find information about Go best practices")

    for event := range events {
        switch event.Type {
        case agentkit.EventTypeThinkingChunk:
            fmt.Print(event.Data["chunk"])
        case agentkit.EventTypeActionDetected:
            fmt.Printf("Tool: %s\n", event.Data["description"])
        case agentkit.EventTypeFinalOutput:
            fmt.Printf("Done: %s\n", event.Data["response"])
        }
    }
}

func buildSystemPrompt(ctx context.Context) string {
    deps, err := agentkit.GetDeps[MyDeps](ctx)
    if err != nil {
        return "You are a helpful assistant."
    }
    return fmt.Sprintf("You are an assistant for %s", deps.UserName)
}

func searchHandler(ctx context.Context, args map[string]any) (any, error) {
    query := args["query"].(string)
    // Perform search...
    return map[string]any{
        "results": []string{"result1", "result2"},
    }, nil
}

Core Concepts

Agent

The orchestrator that manages LLM interactions, tool calling, and streaming.

agent, err := agentkit.New(agentkit.Config{
    APIKey:          os.Getenv("OPENAI_API_KEY"),
    Model:           "gpt-4o-mini",
    SystemPrompt:    buildPrompt,
    MaxIterations:   5,
    Temperature:     0.7,
    StreamResponses: true,
})
if err != nil {
    log.Fatal(err)
}

For reasoning models, use ReasoningEffort instead of Temperature:

agent, err := agentkit.New(agentkit.Config{
    APIKey:          os.Getenv("OPENAI_API_KEY"),
    Model:           "o1-mini",
    SystemPrompt:    buildPrompt,
    MaxIterations:   5,
    ReasoningEffort: agentkit.ReasoningEffortHigh, // none, minimal, low, medium, high, or xhigh
    StreamResponses: true,
})

Note: If you specify ReasoningEffort, it will be used instead of Temperature. Only set one or the other based on your model's capabilities.

Configuration

Key Config fields (all optional unless noted):

  • APIKey (required unless LLMProvider is set)
  • Model (any OpenAI model name)
  • SystemPrompt (func that builds instructions from context)
  • MaxIterations, Temperature (for GPT models)
  • ReasoningEffort (for reasoning models: use constants ReasoningEffortNone, ReasoningEffortMinimal, ReasoningEffortLow, ReasoningEffortMedium, ReasoningEffortHigh, or ReasoningEffortXHigh; if set, Temperature is ignored)
  • StreamResponses (stream SSE events vs. single response)
  • Retry, Timeout (see sections below)
  • ConversationStore, Approval
  • LLMProvider (custom provider or MockLLM)
  • Logging, EventBuffer
  • ParallelToolExecution
Tools

Tools are functions the LLM can call. Build them with a fluent API:

tool := agentkit.NewTool("assign_team").
    WithDescription("Assign work item to a team").
    WithParameter("team_slug", agentkit.String().Required()).
    WithParameter("reasoning", agentkit.String().Optional()).
    WithHandler(func(ctx context.Context, args map[string]any) (any, error) {
        teamSlug := args["team_slug"].(string)
        // Execute tool logic...
        return map[string]any{"success": true}, nil
    }).
    Build()

agent.AddTool(tool)

Defaults are sensible: if you don't supply formatters, AgentKit renders a pending message and a success/error summary based on the tool name or error/success fields.

tool := agentkit.NewTool("assign_team").
    WithHandler(assignTeamHandler).
    WithPendingFormatter(func(_ string, args map[string]any) string {
        return fmt.Sprintf("Assigning to %s...", args["team_slug"])
    }).
    WithResultFormatter(func(_ string, result any) string {
        return fmt.Sprintf("✓ Assigned to %v", result)
    }).
    Build()
Struct-Based Tools

Generate tool schemas from Go structs and get typed handler input. Structured Outputs are enabled by default.

type SearchParams struct {
    Query  string   `json:"query" required:"true" desc:"Search query"`
    Labels []string `json:"labels" desc:"Optional filter labels"`
    Limit  int      `json:"limit" default:"10"`
}

toolBuilder, err := agentkit.NewStructTool("search", func(ctx context.Context, args SearchParams) (any, error) {
    return map[string]any{"hits": 3}, nil
})
if err != nil {
    log.Fatal(err)
}
tool := toolBuilder.Build()
agent.AddTool(tool)
OpenAI Structured Outputs

AgentKit automatically enables OpenAI Structured Outputs for all tools by default. This ensures the model's output always matches your schema exactly, with guaranteed type-safety and no hallucinated fields.

Key features:

  • ✅ All tools use strict: true by default
  • ✅ Automatic additionalProperties: false for all object schemas (added at Build time)
  • ✅ Automatic type: "object" and properties: {} for tools with empty/minimal schemas
  • ✅ Optional fields use anyOf with null type
  • ✅ All parameter names are in the required array (with null unions for optional)
  • ✅ Works with WithParameter(), WithRawParameters(), WithJSONSchema(), and even tools with no parameters

Two ways to define schemas:

Use Go structs with tags to automatically generate schemas. Supports nested objects, enums, descriptions, and more.

type SearchFilters struct {
    EmailDomain string `json:"email_domain" desc:"Filter by email domain"`
    Status      string `json:"status" required:"true" enum:"active,inactive" desc:"User status"`
    AgeRange    struct {
        Min int `json:"min" desc:"Minimum age"`
        Max int `json:"max" desc:"Maximum age"`
    } `json:"age_range"`
}

filtersSchema, _ := agentkit.StructToSchema[SearchFilters]()
tool := agentkit.NewTool("search_users").
    WithParameter("filters", filtersSchema).
    Build()

Supported struct tags:

  • json: Field name (use "-" to skip field)
  • required:"true": Mark field as required (omit for optional fields)
  • desc: Field description
  • enum: Comma-separated allowed values
  • default: Default value
2. Fluent API (Good for simple inline schemas)
// Structured Outputs enabled by default
tool := agentkit.NewTool("create_user").
    WithParameter("name", agentkit.String().Required()).
    WithParameter("email", agentkit.String().Required()).
    WithParameter("nickname", agentkit.String().Optional()). // Uses anyOf with null
    Build()

// Disable strict mode only if needed (not recommended)
tool := agentkit.NewTool("legacy_tool").
    WithParameter("data", agentkit.String()).
    WithStrictMode(false). // Disables Structured Outputs
    Build()
Complex Schemas
tool := agentkit.NewTool("complex_search").
    WithParameter("filters", agentkit.Object().
        WithProperty("status", agentkit.String().WithEnum("open", "closed")).
        WithProperty("labels", agentkit.Array("string")).
        WithProperty("assignee", agentkit.Object().
            WithProperty("id", agentkit.String().Required()).
            WithProperty("name", agentkit.String().Optional()), // Nested optional field
        ).
        Required(),
    ).
    Build()

// Array of complex objects
tool := agentkit.NewTool("batch_update").
    WithParameter("users", agentkit.ArrayOf(
        agentkit.Object().
            WithProperty("id", agentkit.String().Required()).
            WithProperty("name", agentkit.String().Required()),
    ).Required()).
    Build()

// Raw JSON Schema for maximum control
// Note: additionalProperties: false is automatically added when strict mode is enabled
tool := agentkit.NewTool("advanced").
    WithJSONSchema(map[string]any{
        "type": "object",
        "properties": map[string]any{
            "query": map[string]any{"type": "string"},
        },
        "required": []string{"query"},
        // additionalProperties: false added automatically
    }).
    Build()
Approval Flows

Require human approval for sensitive tools:

agent, _ := agentkit.New(agentkit.Config{
    APIKey: os.Getenv("OPENAI_API_KEY"),
    Approval: &agentkit.ApprovalConfig{
        Tools: []string{"assign_team", "deploy"},
        Handler: func(ctx context.Context, req agentkit.ApprovalRequest) (bool, error) {
            // Persist request + wait for approval response.
            return true, nil
        },
    },
})
Multi-Agent Coordination

AgentKit provides two natural patterns for agent coordination, mimicking how real people work together:

1. Handoffs - Delegation

When one agent delegates work to another who works independently:

// Create specialized agents
researchAgent, _ := agentkit.New(researchConfig)
coordinatorAgent, _ := agentkit.New(coordinatorConfig)

// Direct handoff
result, err := coordinatorAgent.Handoff(
    ctx,
    researchAgent,
    "Research the top 3 Go web frameworks",
    agentkit.WithIncludeTrace(true), // Optional: see how they worked
    agentkit.WithMaxTurns(10),
)

fmt.Printf("Research: %s\n", result.Response)

As a Tool (LLM decides when to delegate):

// The description tells the LLM WHEN to use this tool.
// The LLM will provide the actual task when it calls the tool.
coordinatorAgent.AddTool(
    researchAgent.AsHandoffTool(
        "research_agent",                           // Tool name
        "Delegate to research specialist when you need deep research on technical topics", // When to use
    ),
)

// When coordinator runs, the LLM autonomously decides:
// Tool call: { "tool": "research_agent", "parameters": { "task": "Research top 3 Go web frameworks" } }
//                                                          ^^^^^ LLM provides this dynamically

// Or create reusable configuration
handoffConfig := agentkit.NewHandoffConfiguration(
    coordinatorAgent,
    researchAgent,
    agentkit.WithIncludeTrace(true),
)

coordinatorAgent.AddTool(
    handoffConfig.AsTool(
        "research_agent",
        "Delegate to research specialist when you need deep research on technical topics",
    ),
)
2. Collaboration - Peer Discussion

When multiple agents need to discuss a topic as equals:

// Create a collaborative session
session := agentkit.NewCollaborationSession(
    facilitatorAgent,  // Runs the discussion
    engineerAgent,     // Peers who contribute
    designerAgent,
    productAgent,
)

// Discuss a topic
result, err := session.Discuss(
    ctx,
    "Should we use WebSockets or Server-Sent Events?",
)

fmt.Printf("Decision: %s\n", result.FinalResponse)

As a Tool (LLM decides when to collaborate and what to discuss):

designSession := agentkit.NewCollaborationSession(
    facilitatorAgent,
    engineerAgent,
    designerAgent,
    productAgent,
)

coordinatorAgent.AddTool(
    designSession.AsTool(
        "design_discussion",
        "Form a collaborative design discussion on a specific topic",
    ),
)

// LLM will call with: {"topic": "authentication flow design"}

When to use what:

  • Handoff: One agent needs focused work done independently ("Go research this and report back")
  • Collaboration: Multiple perspectives needed on a topic ("Let's all discuss this together")

See docs/COORDINATION.md for comprehensive examples and patterns.

Agents as Tools (Composition)

Note: The handoff and collaboration patterns above are preferred for new code. The methods below are maintained for backward compatibility.

Agents can be composed by using one agent as a tool for another. There are two approaches:

1. Using AsTool (Simple)

The quickest way to add an agent as a tool:

researchAgent, _ := agentkit.New(researchConfig)

mainAgent, _ := agentkit.New(mainConfig)
mainAgent.AddTool(researchAgent.AsTool("researcher", "Can perform deep research on a topic"))

Both approaches automatically handle event bubbling so the parent agent receives events from delegated agents in real-time.

Parallel Tool Execution
agent, err := agentkit.New(agentkit.Config{
    APIKey: os.Getenv("OPENAI_API_KEY"),
    Model:  "gpt-4o-mini",
    ParallelToolExecution: &agentkit.ParallelConfig{
        Enabled:       true,
        MaxConcurrent: 3,
        SafetyMode:    agentkit.SafetyModeOptimistic, // Pessimistic disables parallel execution
    },
})
if err != nil {
    log.Fatal(err)
}

tool := agentkit.NewTool("serial_tool").
    WithConcurrency(agentkit.ConcurrencySerial).
    WithHandler(handler).
    Build()
Observability & Logging

AgentKit separates agent events from internal logs:

  • Events (streamed via channel): What the agent is doing - thinking chunks, tool calls, results, final output. This is the primary output for CLI applications and UIs.
  • Logs (via slog): Internal diagnostics for debugging - iteration counts, chunk metadata, errors. These go to stderr by default (following Unix conventions).
agent, err := agentkit.New(agentkit.Config{
    APIKey:  os.Getenv("OPENAI_API_KEY"),
    Model:   "gpt-4o-mini",
    Logging: agentkit.LoggingConfig{}.Silent(), // Disable internal logs
})

// Handle events only - no log pollution
for event := range agent.Run(ctx, "do something") {
    switch event.Type {
    case agentkit.EventTypeThinkingChunk:
        fmt.Print(event.Data["chunk"]) // Clean stdout
    case agentkit.EventTypeFinalOutput:
        fmt.Printf("\n%s\n", event.Data["response"])
    }
}
Development/Debugging
agent, err := agentkit.New(agentkit.Config{
    APIKey:  os.Getenv("OPENAI_API_KEY"),
    Model:   "gpt-4o-mini"),
    Logging: agentkit.LoggingConfig{}.Verbose(), // Debug-level logs to stderr
})
Custom Logging
agent, err := agentkit.New(agentkit.Config{
    APIKey: os.Getenv("OPENAI_API_KEY"),
    Model:  "gpt-4o-mini",
    Logging: &agentkit.LoggingConfig{
        Level:           slog.LevelInfo,
        Handler:         customHandler,        // Use your own handler
        LogPrompts:      true,                 // Log prompts to file
        LogResponses:    true,
        LogToolCalls:    true,
        RedactSensitive: true,
        PromptLogPath:   "/var/log/agentkit/prompts.log",
    },
})

Default Behavior:

  • Logs go to stderr (not stdout) following Unix conventions
  • Use .Silent() for CLI apps where you only want events
  • Use .Verbose() for development debugging

Migration Note: Prior versions logged to stdout by default. This has been changed to stderr to prevent log pollution in CLI applications. To restore the old behavior:

Logging: &agentkit.LoggingConfig{
    Handler: slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelInfo}),
}

AgentKit also provides middleware hooks for custom observability:

agent.Use(myMiddleware)
Timeouts & Retries

Configure overall run time, per-LLM call, per-tool, and stream read timeouts. Add retry backoff for transient API errors.

agent, _ := agentkit.New(agentkit.Config{
    APIKey: os.Getenv("OPENAI_API_KEY"),
    Retry: &agentkit.RetryConfig{
        MaxRetries:   3,
        InitialDelay: time.Second,
        MaxDelay:     30 * time.Second,
        Multiplier:   2.0,
    },
    Timeout: &agentkit.TimeoutConfig{
        AgentExecution: 2 * time.Minute,
        LLMCall:        30 * time.Second,
        ToolExecution:  15 * time.Second,
        StreamChunk:    5 * time.Second,
    },
})
Testing With Mock LLM
mock := agentkit.NewMockLLM().
    WithResponse("Searching...", []agentkit.ToolCall{
        {Name: "search", Arguments: map[string]any{"query": "timeout"}},
    }).
    WithFinalResponse("Done")

agent, err := agentkit.New(agentkit.Config{
    Model:           "gpt-4o-mini",
    LLMProvider:     mock,
    StreamResponses: false,
    Logging: &agentkit.LoggingConfig{
        LogPrompts: false,
    },
})
if err != nil {
    log.Fatal(err)
}
Trace IDs
ctx := agentkit.WithTraceID(context.Background(), "trace-123")
ctx = agentkit.WithSpanID(ctx, "span-456")
events := agent.Run(ctx, "triage issue")
Event Utilities
events := agent.Run(ctx, "triage issue")
filtered := agentkit.FilterEvents(events, agentkit.EventTypeActionDetected, agentkit.EventTypeFinalOutput)

recorder := agentkit.NewEventRecorder()
recorded := recorder.Record(filtered)

for range recorded {
    // consume filtered events
}

_ = recorder.Events() // replay later
Context & Dependencies

Pass dependencies through context with type safety:

type MyDeps struct {
    DB     *database.DB
    UserID string
}

// Add to context
ctx := agentkit.WithDeps(context.Background(), MyDeps{
    DB:     db,
    UserID: "123",
})

// Retrieve in tools
func myHandler(ctx context.Context, args map[string]any) (any, error) {
    deps, err := agentkit.GetDeps[MyDeps](ctx)
    if err != nil {
        return nil, err
    }
    // Use deps.DB, deps.UserID...
}
Events

Stream events during agent execution:

events := agent.Run(ctx, "user message")

for event := range events {
    switch event.Type {
    case agentkit.EventTypeThinkingChunk:
        // LLM thinking process
    case agentkit.EventTypeActionDetected:
        // Tool about to be called
    case agentkit.EventTypeActionResult:
        // Tool execution result
    case agentkit.EventTypeFinalOutput:
        // Agent finished
    case agentkit.EventTypeError:
        // Error occurred
    }
}
Conversation Store

Persist multi-turn conversations and resume later:

store := agentkit.NewMemoryConversationStore()
agent, _ := agentkit.New(agentkit.Config{
    APIKey:            os.Getenv("OPENAI_API_KEY"),
    ConversationStore: store,
})

ctx := agentkit.WithConversation(context.Background(), "conv-123")
events := agent.Run(ctx, "continue where we left off")

Real-World Examples

Multi-Turn Conversation (Persistence)
store := agentkit.NewMemoryConversationStore()
agent, _ := agentkit.New(agentkit.Config{
    APIKey:            os.Getenv("OPENAI_API_KEY"),
    Model:             "gpt-4o-mini",
    ConversationStore: store,
})

ctx := agentkit.WithConversation(context.Background(), "conv-123")
events := agent.Run(ctx, "continue where we left off")
RAG With Vector DB
tool := agentkit.NewTool("retrieve_context").
    WithParameter("query", agentkit.String().Required()).
    WithHandler(func(ctx context.Context, args map[string]any) (any, error) {
        hits := vectorDB.Search(args["query"].(string))
        return map[string]any{"chunks": hits}, nil
    }).
    Build()
Production Deployment Tips
agent, _ := agentkit.New(agentkit.Config{
    APIKey: os.Getenv("OPENAI_API_KEY"),
    Retry: &agentkit.RetryConfig{MaxRetries: 3},
    Timeout: &agentkit.TimeoutConfig{
        AgentExecution: 2 * time.Minute,
        LLMCall:        30 * time.Second,
        ToolExecution:  15 * time.Second,
    },
})
Error Handling Patterns
for event := range agent.Run(ctx, "do work") {
    if event.Type == agentkit.EventTypeError {
        log.Printf("agent error: %v", event.Data["error"])
    }
}
Performance Optimization
agent, _ := agentkit.New(agentkit.Config{
    ParallelToolExecution: &agentkit.ParallelConfig{Enabled: true, MaxConcurrent: 4},
    EventBuffer:           100,
})
Security Best Practices
agent, _ := agentkit.New(agentkit.Config{
    Logging: &agentkit.LoggingConfig{
        RedactSensitive: true,
        LogPrompts:      false,
    },
    Approval: &agentkit.ApprovalConfig{
        Tools: []string{"deploy", "close_issue"},
        Handler: approvalHandler,
    },
})

API Reference

Agent Methods
  • New(cfg Config) (*Agent, error) - Create new agent
  • AddTool(tool Tool) - Register a tool
  • Use(m Middleware) - Register middleware hooks
  • Run(ctx context.Context, userMessage string) <-chan Event - Execute agent
Coordination

Handoffs:

  • agent.Handoff(ctx, to, task, ...opts) - Delegate task to another agent
  • agent.AsHandoffTool(name, desc, ...opts) - Convert agent to handoff tool
  • NewHandoffConfiguration(from, to, ...opts) - Create reusable handoff config
  • config.AsTool(name, desc) - Convert handoff config to tool
  • WithFullContext(bool), WithMaxTurns(int), WithContext(HandoffContext) - Handoff options

Collaborations:

  • NewCollaborationSession(facilitator, ...peers) - Create collaboration session
  • session.Discuss(ctx, topic) - Execute collaborative discussion
  • session.Configure(...opts) - Add options to session
  • session.AsTool(name, desc) - Convert session to tool
  • WithMaxRounds(int), WithRoundTimeout(duration), WithCaptureHistory(bool) - Collaboration options
Config & Context
  • Config - Agent configuration (model, retries, timeouts, logging, etc.)
  • DefaultConfig() - Default configuration values
  • WithDeps(ctx, deps) / GetDeps[T](ctx) - Type-safe dependency injection
  • WithConversation(ctx, id) / GetConversationID(ctx) - Conversation IDs
  • WithTraceID(ctx, id) / WithSpanID(ctx, id) - Trace correlation
Approvals
  • ApprovalConfig - Tool approval settings
  • ApprovalHandler / ApprovalRequest - Approval callback types
Retry & Timeout
  • RetryConfig, DefaultRetryConfig(), WithRetry(...)
  • TimeoutConfig, DefaultTimeoutConfig(), NoTimeouts()
Conversation Store
  • ConversationStore - Persistence interface
  • NewMemoryConversationStore() - In-memory store for tests/dev
Tool Builder
  • NewTool(name string) *ToolBuilder - Start building a tool
  • NewStructTool(name string, handler) - Build from struct tags
  • SchemaFromStruct(sample any) - Generate JSON schema from struct tags
  • StructToSchema[T any]() (*ParameterSchema, error) - Convert struct type to ParameterSchema (recommended)
  • WithDescription(desc string) - Set tool description
  • WithParameter(name string, schema ParameterSchema) - Add parameter
  • WithJSONSchema(schema map[string]any) - Set raw JSON schema
  • WithConcurrency(mode ConcurrencyMode) - Control parallel execution
  • WithStrictMode(strict bool) - Enable/disable OpenAI Structured Outputs (default: true)
  • WithHandler(handler ToolHandler) - Set execution handler
  • Build() Tool - Construct the tool
Parameter Schemas
  • String() - String parameter
  • Array(itemType string) - Array parameter
  • ArrayOf(itemSchema *ParameterSchema) - Array of complex items
  • Object() - Object schema builder
  • StructToSchema[T any]() - Generate schema from Go struct with tags
  • WithProperty(name string, schema *ParameterSchema) - Add object property
  • WithDescription(desc string) - Add description
  • Required() - Mark as required
  • Optional() - Mark as optional (uses anyOf with null in strict mode)
  • WithEnum(values ...string) - Restrict to enum values
  • ToMap() - Convert to map for OpenAI (no strict mode wrapping)
  • ToMapStrict() - Convert with strict mode (anyOf for optional fields)
Parallel Tool Execution
  • ParallelConfig - Tool execution configuration
  • ConcurrencySerial - Tool runs exclusively
  • ConcurrencyParallel - Tool can run in parallel
Event Helpers
  • ThinkingChunk(chunk string) Event
  • ActionDetected(toolName, toolID string) Event
  • ActionResult(toolName string, result any) Event
  • FinalOutput(summary, response string) Event
  • Error(err error) Event
Event Utilities
  • FilterEvents(input <-chan Event, types ...EventType) <-chan Event
  • NewEventRecorder() *EventRecorder
Testing Utilities
  • LLMProvider - Provider abstraction
  • NewMockLLM() - Deterministic LLM for tests

Design Principles

  1. Explicit Configuration: No hidden magic, everything is configured explicitly
  2. Type Safety: Generics for dependency injection, strong typing throughout
  3. Composability: Tools are independent units that compose together
  4. Streaming First: Built for real-time SSE responses
  5. Error Handling: Errors are events, gracefully handled
  6. Go Idioms: Follows Go best practices (builders, options, interfaces)

Comparison to Other Frameworks

Feature AgentKit (Go) Pydantic AI (Python) LangChain (Python) OpenAI SDK
Tool registration Builder API Decorators Chains/Tools Functions/Tools
Streaming Channel events async iter callbacks stream events
Typed deps WithDeps[T] RunContext custom manual
Mocking MockLLM + LLMProvider test clients mocks stub client
Parallel tools Config + per-tool concurrency custom limited model-driven
When to Use AgentKit
  • You want Go-native APIs with explicit configuration and no magic decorators.
  • You need streaming events and tool execution in a single agent loop.
  • You want easy testability without calling real LLMs.
Performance Characteristics
  • Streaming-first design keeps UI responsive with minimal buffering.
  • Parallel tool execution is configurable with per-tool concurrency gates.
  • Prompt logging is optional and can be disabled for high-throughput systems.
Limitations & Gotchas
  • Struct-tag schemas are best-effort; complex validation is still manual.
  • Tool outputs are returned as JSON-compatible values; custom types should be mapped.
  • The underlying LLM provider still controls which tools are called.

Testing

AgentKit has comprehensive test coverage:

# Run all tests
go test ./pkg/agentkit/...

# Run with coverage
go test ./pkg/agentkit/... -cover

# Generate coverage report
go test ./pkg/agentkit/... -coverprofile=coverage.out
go tool cover -html=coverage.out

Coverage Strategy:

  • 100% coverage for all public APIs (events, tools, context, builders)
  • Integration tests verify end-to-end tool execution and event streaming
  • Agent orchestration (Run method) is tested via the MockLLM and LLMProvider hooks
  • Target: 85%+ for framework APIs (achieved), full integration testing for LLM orchestration

Test Categories:

  1. Unit tests: Event helpers, tool builders, context management, parameter schemas
  2. Integration tests: Tool registration → execution, multi-tool scenarios, context flow
  3. Real-world usage: Inbox agent implementation serves as integration test

Project Structure

agentkit/
├── *.go              # Core library (public API)
├── *_test.go         # Tests
├── examples/         # Example applications
│   ├── basic/        # Simple agent example
│   ├── multi-agent/  # Multi-agent orchestration
│   └── rag/          # RAG implementation
├── internal/         # Private packages
│   └── testutil/     # Test utilities
└── docs/             # Documentation

See docs/PROJECT_STRUCTURE.md for detailed information.

Examples

Check out the examples/ directory for complete working examples:

  • Basic Agent - Simple tool usage and event handling
  • Multi-Agent - Agent composition and orchestration
  • RAG - Retrieval augmented generation

Documentation

LLM Tracing

AgentKit includes built-in support for LLM observability through an extensible tracing interface. Currently supports Langfuse via OpenTelemetry.

// Create Langfuse tracer
tracer, err := agentkit.NewLangfuseTracer(agentkit.LangfuseConfig{
    PublicKey: os.Getenv("LANGFUSE_PUBLIC_KEY"),
    SecretKey: os.Getenv("LANGFUSE_SECRET_KEY"),
    BaseURL:   "https://cloud.langfuse.com",
    Enabled:   true,
})
if err != nil {
    log.Fatal(err)
}
defer tracer.Shutdown(context.Background())

// Create agent with tracing
agent, err := agentkit.New(agentkit.Config{
    APIKey: os.Getenv("OPENAI_API_KEY"),
    Tracer: tracer,  // Enable tracing
})

Traces automatically include:

  • Agent execution flows
  • LLM generations with token usage and costs
  • Tool executions with inputs and outputs
  • Error details and timing information

See docs/TRACING.md for complete setup instructions.

Future Enhancements

  • Tool result validation
  • Multi-agent orchestration
  • Struct-tag schema generation
  • Parallel tool execution control
  • More provider adapters (Anthropic, etc.)

License

MIT License - see LICENSE for details

Documentation

Overview

Package agentkit provides a flexible framework for building LLM-powered agents with tool calling.

Package agentkit provides types and client for OpenAI's Responses API

Package agentkit provides tracing capabilities for LLM applications

Index

Constants

This section is empty.

Variables

View Source
var (
	NewMemoryConversationStore = conversation.NewMemoryConversationStore
	DefaultRetryConfig         = retry.DefaultRetryConfig
	DefaultTimeoutConfig       = timeout.DefaultTimeoutConfig
	DefaultLoggingConfig       = logging.DefaultLoggingConfig
	DefaultParallelConfig      = parallel.DefaultParallelConfig
	ErrConversationNotFound    = conversation.ErrConversationNotFound
)

Function re-exports for convenience

View Source
var (
	ErrMissingAPIKey          = errors.New("agentkit: APIKey is required")
	ErrInvalidIterations      = errors.New("agentkit: MaxIterations must be between 1 and 100")
	ErrInvalidTemperature     = errors.New("agentkit: Temperature must be between 0.0 and 2.0")
	ErrInvalidReasoningEffort = errors.New("agentkit: ReasoningEffort must be valid")
)

Common validation errors.

View Source
var (
	ErrCollaborationNoFacilitator = errors.New("agentkit: collaboration requires a facilitator agent")
	ErrCollaborationNoPeers       = errors.New("agentkit: collaboration requires at least one peer agent")
	ErrCollaborationTopicEmpty    = errors.New("agentkit: collaboration topic cannot be empty")
	ErrCollaborationFailed        = errors.New("agentkit: collaboration failed")
)
View Source
var (
	ErrHandoffAgentNil      = errors.New("agentkit: handoff target agent cannot be nil")
	ErrHandoffTaskEmpty     = errors.New("agentkit: handoff task cannot be empty")
	ErrHandoffExecutionFail = errors.New("agentkit: handoff execution failed")
)
View Source
var DefaultModelCosts = map[string]ModelCostConfig{

	"gpt-5.2": {
		InputCostPer1MTokens:  2.50,
		OutputCostPer1MTokens: 10.00,
	},

	"gpt-4o": {
		InputCostPer1MTokens:  5.00,
		OutputCostPer1MTokens: 15.00,
	},
	"gpt-4o-2024-11-20": {
		InputCostPer1MTokens:  2.50,
		OutputCostPer1MTokens: 10.00,
	},

	"gpt-4o-mini": {
		InputCostPer1MTokens:  0.150,
		OutputCostPer1MTokens: 0.600,
	},
	"gpt-4o-mini-2024-07-18": {
		InputCostPer1MTokens:  0.150,
		OutputCostPer1MTokens: 0.600,
	},

	"gpt-4-turbo": {
		InputCostPer1MTokens:  10.00,
		OutputCostPer1MTokens: 30.00,
	},
	"gpt-4-turbo-2024-04-09": {
		InputCostPer1MTokens:  10.00,
		OutputCostPer1MTokens: 30.00,
	},

	"gpt-5.1-codex-max": {
		InputCostPer1MTokens:  2.50,
		OutputCostPer1MTokens: 10.00,
	},
}

DefaultModelCosts provides FALLBACK pricing for common OpenAI models. These are used when: 1. Dynamic pricing is disabled (ModelPricingAPIURL = "") 2. API fetch fails or times out 3. Model not found in API response

By default, AgentKit fetches real-time pricing from models.dev API automatically. The API fetch is non-blocking and has a conservative timeout (5 seconds).

Priority order for pricing: 1. Custom pricing (via RegisterModelCost) - highest priority 2. Dynamic pricing (from API) - fetched automatically 3. Fallback pricing (below) - used if API unavailable

Fallback prices last updated: January 8, 2026 Source: https://openai.com/api/pricing/

View Source
var DisableCostCalculation = false

DisableCostCalculation can be set to true to skip all cost calculations. This is useful if you don't need cost tracking or want to avoid outdated pricing estimates.

View Source
var ErrDepsNotFound = errors.New("agentkit: dependencies not found in context")

ErrDepsNotFound is returned when dependencies are not found in context

View Source
var ErrInvalidStructSchema = errors.New("agentkit: struct schema requires a struct type")

ErrInvalidStructSchema is returned when a schema cannot be built from the provided type.

View Source
var ModelPricingAPIURL = "https://models.dev/api.json"

ModelPricingAPIURL is the endpoint for fetching real-time model pricing Set to empty string to disable dynamic price fetching

View Source
var ModelPricingTimeout = 5 * time.Second

ModelPricingTimeout is the timeout for fetching model prices from the API

Functions

func FilterEvents

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

FilterEvents forwards only events with matching types.

func GetAgentName

func GetAgentName(ctx context.Context) (string, bool)

GetAgentName retrieves the agent name from the context.

func GetConversationID

func GetConversationID(ctx context.Context) (string, bool)

GetConversationID retrieves the conversation ID from the context

func GetDeps

func GetDeps[T any](ctx context.Context) (T, error)

GetDeps retrieves dependencies from the context, returning an error if not found. This is the preferred method for accessing dependencies as it allows for proper error handling.

func GetIteration

func GetIteration(ctx context.Context) (int, bool)

GetIteration retrieves the iteration index from the context.

func GetSpanID

func GetSpanID(ctx context.Context) (string, bool)

GetSpanID retrieves the span ID from the context.

func GetTraceID

func GetTraceID(ctx context.Context) (string, bool)

GetTraceID retrieves the trace ID from the context.

func MustGetDeps deprecated

func MustGetDeps[T any](ctx context.Context) T

MustGetDeps retrieves dependencies from the context or panics.

Deprecated: Use GetDeps instead for better error handling. This method is kept for backward compatibility but should only be used in controlled environments where dependencies are guaranteed to exist.

func RefreshModelCosts

func RefreshModelCosts()

RefreshModelCosts triggers a fresh fetch of model costs from the API This is useful if you want to update prices without restarting the application

func RegisterModelCost

func RegisterModelCost(model string, config ModelCostConfig)

RegisterModelCost registers a custom model cost configuration This takes precedence over both API-fetched and default pricing

func SchemaFromStruct

func SchemaFromStruct(sample any) (map[string]any, error)

SchemaFromStruct builds a JSON schema object from a struct value or pointer.

func WithAgentName

func WithAgentName(ctx context.Context, name string) context.Context

WithAgentName adds the agent name to the context.

func WithConversation

func WithConversation(ctx context.Context, conversationID string) context.Context

WithConversation adds a conversation ID to the context

func WithDeps

func WithDeps(ctx context.Context, deps any) context.Context

WithDeps adds dependencies to the context

func WithEventPublisher

func WithEventPublisher(ctx context.Context, publisher EventPublisher) context.Context

WithEventPublisher adds an event publisher to the context

func WithIteration

func WithIteration(ctx context.Context, iteration int) context.Context

WithIteration adds the iteration index to the context.

func WithSpanID

func WithSpanID(ctx context.Context, spanID string) context.Context

WithSpanID adds a span ID to the context for request correlation.

func WithTraceID

func WithTraceID(ctx context.Context, traceID string) context.Context

WithTraceID adds a trace ID to the context for request correlation.

func WithTracer

func WithTracer(ctx context.Context, tracer Tracer) context.Context

WithTracer adds a tracer to the context for delegated agent inheritance (handoffs/collaboration)

Types

type APIError

type APIError struct {
	StatusCode int
	Code       interface{} `json:"code"`
	Message    string      `json:"message"`
	Type       string      `json:"type"`
}

APIError represents an error returned by the Responses API

func (*APIError) Error

func (e *APIError) Error() string

type Agent

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

Agent orchestrates LLM interactions with tool calling and streaming.

func New

func New(cfg Config) (*Agent, error)

New creates a new agent with the given configuration.

func (*Agent) AddContext

func (a *Agent) AddContext(ctx context.Context, conversationID string, content string) error

func (*Agent) AddTool

func (a *Agent) AddTool(tool Tool)

AddTool registers a tool with the agent.

func (*Agent) AppendToConversation

func (a *Agent) AppendToConversation(ctx context.Context, conversationID string, turn ConversationTurn) error

func (*Agent) AsHandoffTool

func (a *Agent) AsHandoffTool(name, description string, opts ...HandoffOption) Tool

AsHandoffTool converts an agent into a Tool that can be registered with another agent. This enables handoffs to be triggered by the LLM through tool calling.

Example:

researchAgent := agentkit.NewAgent(researchConfig)
coordinatorAgent := agentkit.NewAgent(coordinatorConfig)

// Register as a tool
coordinatorAgent.RegisterTool(researchAgent.AsHandoffTool(
    "research_agent",
    "Delegate research tasks to a specialized research agent",
))

func (*Agent) AsTool

func (a *Agent) AsTool(name, description string) Tool

AsTool converts the agent into a tool that can be used by other agents.

func (*Agent) ClearConversation

func (a *Agent) ClearConversation(ctx context.Context, conversationID string) error

func (*Agent) DeleteConversation

func (a *Agent) DeleteConversation(ctx context.Context, conversationID string) error

func (*Agent) ForkConversation

func (a *Agent) ForkConversation(ctx context.Context, originalID, newID, userMessage string) error

func (*Agent) GetConversation

func (a *Agent) GetConversation(ctx context.Context, conversationID string) (Conversation, error)

Conversation management methods

func (*Agent) Handoff

func (a *Agent) Handoff(ctx context.Context, to *Agent, task string, opts ...HandoffOption) (*HandoffResult, error)

Handoff delegates a task to another agent. The receiving agent works independently with an isolated context, then returns the result. The delegating agent can optionally see the full execution trace (thinking, tool calls, etc.) to understand how the work was done. Real-time event streaming to parent ALWAYS happens.

Example:

researchAgent := agentkit.NewAgent(researchConfig)
result, err := coordinator.Handoff(ctx, researchAgent,
    "Research the top 3 Go web frameworks in 2026",
    WithFullContext(true),
)

func (*Agent) Run

func (a *Agent) Run(ctx context.Context, userMessage string) <-chan Event

Run executes the agent with streaming events.

func (*Agent) SaveConversation

func (a *Agent) SaveConversation(ctx context.Context, conv Conversation) error

func (*Agent) Use

func (a *Agent) Use(m Middleware)

Use registers middleware for agent execution hooks.

type ApprovalConfig

type ApprovalConfig struct {
	// Tools is a list of tool names that require approval
	// If empty, no tools require approval
	Tools []string

	// Handler is called for approval requests
	// If nil, all tools in Tools list will be automatically denied
	Handler ApprovalHandler

	// AllTools, if true, requires approval for ALL tool calls
	AllTools bool
}

ApprovalConfig configures which tools require approval

type ApprovalHandler

type ApprovalHandler func(ctx context.Context, request ApprovalRequest) (bool, error)

ApprovalHandler is called when a tool requires approval before execution Returns true to approve, false to deny

type ApprovalRequest

type ApprovalRequest struct {
	ToolName       string         `json:"tool_name"`
	Arguments      map[string]any `json:"arguments"`
	Description    string         `json:"description"`     // Human-friendly description
	ConversationID string         `json:"conversation_id"` // If available
	CallID         string         `json:"call_id"`         // Unique call identifier
}

ApprovalRequest contains information about a tool call that requires approval

type CollaborationContribution

type CollaborationContribution struct {
	Agent   string    // Agent identifier
	Content string    // What the agent said
	Time    time.Time // When they contributed
}

CollaborationContribution represents one agent's input in a round.

type CollaborationOption

type CollaborationOption func(*collaborationOptions)

CollaborationOption configures a collaboration session.

func WithCaptureHistory

func WithCaptureHistory(capture bool) CollaborationOption

WithCaptureHistory enables capturing the full conversation history.

func WithMaxRounds

func WithMaxRounds(max int) CollaborationOption

WithMaxRounds sets the maximum number of discussion rounds. Each round gives every agent a chance to contribute.

func WithRoundTimeout

func WithRoundTimeout(timeout time.Duration) CollaborationOption

WithRoundTimeout sets a timeout for each discussion round.

type CollaborationResult

type CollaborationResult struct {
	FinalResponse string               // The synthesized final answer
	Rounds        []CollaborationRound // History of the discussion
	Summary       string               // Summary of the collaboration
	Participants  []string             // Names/IDs of participating agents
	Metadata      map[string]any       // Additional metadata
}

CollaborationResult contains the outcome of a collaborative discussion.

type CollaborationRound

type CollaborationRound struct {
	Number        int                         // Round number (1-indexed)
	Contributions []CollaborationContribution // Each agent's contribution
	Synthesis     string                      // How the facilitator synthesized this round
}

CollaborationRound represents one round of discussion.

type CollaborationSession

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

CollaborationSession represents a real-time discussion between multiple agents. Unlike handoffs, collaborations are not hierarchical - all agents are peers who contribute to a shared conversation. Think of this as a breakout room where everyone hashes out ideas together.

func NewCollaborationSession

func NewCollaborationSession(facilitator *Agent, peers ...*Agent) *CollaborationSession

NewCollaborationSession creates a new collaboration session. The facilitator runs the conversation, and peers contribute as equals.

Example:

session := agentkit.NewCollaborationSession(
    facilitatorAgent,
    engineerAgent, designerAgent, productAgent,
)

result, err := session.Discuss(ctx, "How should we design the authentication API?")

func (*CollaborationSession) AsTool

func (cs *CollaborationSession) AsTool(name, description string, opts ...CollaborationOption) Tool

AsTool converts a collaboration session into a Tool that can be registered with another agent. This enables collaborations to be triggered by the LLM through tool calling, with the topic provided dynamically at runtime.

Example:

session := agentkit.NewCollaborationSession(facilitator, engineer, designer, product)

coordinatorAgent.AddTool(session.AsTool(
    "design_collaboration",
    "Form a collaborative discussion with engineering, design, and product teams",
))

The LLM can then decide when to collaborate and provide the topic:

coordinatorAgent.Run(ctx, "We need to decide on the authentication approach...")
// LLM calls: design_collaboration(topic: "How should we design the authentication API?")

func (*CollaborationSession) Configure

Configure applies options to the collaboration session.

func (*CollaborationSession) Discuss

Discuss starts a collaborative discussion on a topic. All agents participate as peers, sharing ideas and building on each other's contributions.

Example:

result, err := session.Discuss(ctx,
    "What's the best approach for handling user sessions?",
    WithMaxRounds(5),
)

type ConcurrencyMode

type ConcurrencyMode string

ConcurrencyMode controls whether a tool can run in parallel.

const (
	ConcurrencyParallel ConcurrencyMode = "parallel"
	ConcurrencySerial   ConcurrencyMode = "serial"
)

type Config

type Config struct {
	APIKey                string
	Model                 string
	SystemPrompt          SystemPromptFunc
	MaxIterations         int
	Temperature           float32
	ReasoningEffort       providers.ReasoningEffort
	ReasoningSummary      string
	TextVerbosity         string
	TextFormat            string
	Store                 bool
	StreamResponses       bool
	ToolChoice            string
	Retry                 *RetryConfig
	Timeout               *TimeoutConfig
	ConversationStore     ConversationStore
	Approval              *ApprovalConfig
	Provider              providers.Provider
	LLMProvider           LLMProvider // DEPRECATED: Use Provider instead
	Logging               *LoggingConfig
	EventBuffer           int
	ParallelToolExecution *ParallelConfig
	Tracer                Tracer
	AgentName             string
}

Config holds agent configuration.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns sensible defaults.

func (Config) Validate

func (c Config) Validate() error

Validate checks if the configuration is valid.

type Conversation

type Conversation = conversation.Conversation

Type aliases for internal package types

type ConversationStore

type ConversationStore = conversation.ConversationStore

Type aliases for internal package types

type ConversationTurn

type ConversationTurn = conversation.ConversationTurn

Type aliases for internal package types

type CostInfo

type CostInfo struct {
	PromptCost     float64 // Estimated cost for prompt tokens in USD
	CompletionCost float64 // Estimated cost for completion tokens in USD
	TotalCost      float64 // Estimated total cost in USD
}

CostInfo tracks cost breakdown NOTE: OpenAI's API does NOT provide cost information. Cost is estimated based on published pricing and may be inaccurate. Set DisableCostCalculation = true to disable cost estimation entirely.

func CalculateCost

func CalculateCost(model string, promptTokens, completionTokens int) *CostInfo

CalculateCost calculates the ESTIMATED cost of an LLM call based on token usage. Returns nil if: - Cost calculation is disabled (DisableCostCalculation = true) - No tokens were used - Model pricing is unknown

NOTE: OpenAI's API provides usage (tokens) but NOT cost. This function estimates cost based on: 1. Real-time pricing from models.dev API (if available and fetched) 2. Fallback to hardcoded pricing (if API unavailable)

The API fetch happens asynchronously and never blocks execution.

type Event

type Event struct {
	Type      EventType      `json:"type"`
	Data      map[string]any `json:"data"`
	Timestamp time.Time      `json:"timestamp"`
	TraceID   string         `json:"trace_id,omitempty"`
	SpanID    string         `json:"span_id,omitempty"`
}

Event represents a streaming event emitted during agent execution

func ActionDetected

func ActionDetected(description, toolID string) Event

ActionDetected creates an action detected event

func ActionResult

func ActionResult(description string, result any) Event

ActionResult creates an action result event

func AgentComplete

func AgentComplete(agentName, output string, totalTokens, iterations int, durationMs int64) Event

AgentComplete creates an agent complete event

func AgentCompleteWithUsage

func AgentCompleteWithUsage(agentName, output string, usage providers.TokenUsage, iterations int, durationMs int64) Event

AgentCompleteWithUsage creates an agent complete event including detailed token usage.

func AgentStart

func AgentStart(agentName string) Event

AgentStart creates an agent start event

func ApprovalDenied

func ApprovalDenied(toolName, callID, reason string) Event

ApprovalDenied creates an approval denied event

func ApprovalGranted

func ApprovalGranted(toolName, callID string) Event

ApprovalGranted creates an approval granted event

func ApprovalNeeded

func ApprovalNeeded(request ApprovalRequest) Event

ApprovalNeeded is an alias for ApprovalRequired

func ApprovalRejected

func ApprovalRejected(request ApprovalRequest) Event

ApprovalRejected creates an approval rejected event

func ApprovalRequired

func ApprovalRequired(request ApprovalRequest) Event

ApprovalRequired creates an approval required event

func CollaborationAgentContribution

func CollaborationAgentContribution(agentName, contribution string) Event

CollaborationAgentContribution creates a collaboration agent contribution event

func Decision

func Decision(action string, confidence float64, reasoning string) Event

Decision creates a decision event

func Error

func Error(err error) Event

Error creates an error event

func FinalOutput

func FinalOutput(summary, response string) Event

FinalOutput creates a final output event

func HandoffComplete

func HandoffComplete(fromAgent, toAgent, result string) Event

HandoffComplete creates a handoff complete event

func HandoffStart

func HandoffStart(fromAgent, toAgent, task, reason string) Event

HandoffStart creates a handoff start event

func NewEvent

func NewEvent(eventType EventType, data map[string]any) Event

NewEvent creates a new event with the current timestamp

func Progress

func Progress(iteration, maxIterations int, description string) Event

Progress creates a progress event

func ReasoningChunk

func ReasoningChunk(chunk string) Event

ReasoningChunk creates a reasoning summary chunk event

func ResponseChunk

func ResponseChunk(chunk string) Event

ResponseChunk creates a response text chunk event

func Thinking

func Thinking(content string) Event

Thinking creates a thinking event (alias for ThinkingChunk)

func ThinkingChunk

func ThinkingChunk(chunk string) Event

ThinkingChunk creates a thinking chunk event

func ToolError

func ToolError(toolName string, err error) Event

ToolError creates a tool execution error event

func ToolResult

func ToolResult(toolName string, result any) Event

ToolResult creates a tool result event (alias for ActionResult)

type EventPublisher

type EventPublisher func(Event)

EventPublisher is a function that publishes events

func GetEventPublisher

func GetEventPublisher(ctx context.Context) (EventPublisher, bool)

GetEventPublisher retrieves the event publisher from the context

type EventRecorder

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

EventRecorder captures events for replay or inspection.

func NewEventRecorder

func NewEventRecorder() *EventRecorder

NewEventRecorder creates a new recorder.

func (*EventRecorder) Events

func (r *EventRecorder) Events() []Event

Events returns a copy of recorded events.

func (*EventRecorder) Record

func (r *EventRecorder) Record(input <-chan Event) <-chan Event

Record captures events while forwarding them.

type EventType

type EventType string

EventType represents the type of streaming event

const (
	// Content streaming events
	EventTypeThinkingChunk  EventType = "thinking_chunk"
	EventTypeReasoningChunk EventType = "reasoning_chunk"
	EventTypeResponseChunk  EventType = "response_chunk"
	EventTypeFinalOutput    EventType = "final_output"

	// Agent lifecycle events
	EventTypeAgentStart    EventType = "agent.start"
	EventTypeAgentComplete EventType = "agent.complete"

	// Tool execution events
	EventTypeActionDetected EventType = "action_detected"
	EventTypeActionResult   EventType = "action_result"

	// Multi-agent coordination events
	EventTypeHandoffStart              EventType = "handoff.start"
	EventTypeHandoffComplete           EventType = "handoff.complete"
	EventTypeCollaborationAgentMessage EventType = "collaboration.agent.contribution"

	// Human-in-the-loop events
	EventTypeApprovalRequired EventType = "approval_required"
	EventTypeApprovalGranted  EventType = "approval_granted"
	EventTypeApprovalDenied   EventType = "approval_denied"

	// Progress and decision events
	EventTypeProgress EventType = "progress"
	EventTypeDecision EventType = "decision"

	// Error events
	EventTypeError EventType = "error"
)

type GenerationOptions

type GenerationOptions struct {
	// Name of the generation
	Name string
	// Model name (e.g., "gpt-4o")
	Model string
	// ModelParameters like temperature, max_tokens, etc.
	ModelParameters map[string]any
	// Input prompt/messages
	Input any
	// Output completion/response
	Output any
	// Usage token counts
	Usage *UsageInfo
	// Cost in USD (optional, can be calculated from usage)
	Cost *CostInfo
	// Metadata stores arbitrary key-value data
	Metadata map[string]any
	// StartTime when generation started
	StartTime time.Time
	// EndTime when generation completed
	EndTime time.Time
	// CompletionStartTime when the model began generating (for streaming)
	CompletionStartTime *time.Time
	// PromptName links to a managed prompt in Langfuse
	PromptName string
	// PromptVersion links to a specific prompt version
	PromptVersion int
	// Level specifies the log level
	Level LogLevel
	// StatusMessage describes errors or warnings
	StatusMessage string
}

GenerationOptions holds data for an LLM generation

type Handoff

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

Handoff represents a delegation of work from one agent to another. The receiving agent works in an isolated context and returns results. This mimics how real people delegate: "Go figure this out and report back."

type HandoffConfiguration

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

HandoffConfiguration represents a reusable handoff setup. This allows creating a handoff configuration once and converting it to a tool.

func NewHandoffConfiguration

func NewHandoffConfiguration(from, to *Agent, opts ...HandoffOption) *HandoffConfiguration

NewHandoffConfiguration creates a reusable handoff configuration. This is useful when you want to create the configuration once and convert it to a tool.

Example:

handoffConfig := agentkit.NewHandoffConfiguration(coordinator, researchAgent, WithFullContext(true))
tool := handoffConfig.AsTool("research", "Delegate research tasks")
coordinator.RegisterTool(tool)

func (*HandoffConfiguration) AsTool

func (h *HandoffConfiguration) AsTool(name, description string) Tool

AsTool converts the handoff configuration into a Tool that can be registered with an agent. The LLM will decide when to use this tool and what task to provide.

Example:

researchHandoff := agentkit.NewHandoffConfiguration(coordinator, researchAgent)
tool := researchHandoff.AsTool(
    "delegate_research",
    "Delegate research tasks to a specialized research agent",
)
coordinator.RegisterTool(tool)

func (*HandoffConfiguration) Configure

Configure applies additional options to the handoff configuration.

func (*HandoffConfiguration) Execute

func (h *HandoffConfiguration) Execute(ctx context.Context, task string) (*HandoffResult, error)

Execute performs the handoff with a specific task.

type HandoffContext

type HandoffContext struct {
	Background string         // Context about why this handoff is happening
	Metadata   map[string]any // Additional structured data
}

HandoffContext provides additional information for the delegated agent.

type HandoffOption

type HandoffOption func(*handoffOptions)

HandoffOption configures a handoff.

func WithContext

func WithContext(ctx HandoffContext) HandoffOption

WithContext provides additional background information to the delegated agent.

func WithFullContext

func WithFullContext(include bool) HandoffOption

WithFullContext controls whether to return the full conversation context (thinking, tool calls, etc.) OR just the final result in the HandoffResult. When false, only the final response is returned. When true, the complete execution trace is included, which is useful for debugging or learning from the approach taken but increases context usage. NOTE: Real-time event streaming to parent agents ALWAYS happens regardless of this setting.

func WithMaxTurns

func WithMaxTurns(max int) HandoffOption

WithMaxTurns limits the number of conversation turns the delegated agent can take.

type HandoffResult

type HandoffResult struct {
	Response string             // The final response from the delegated agent
	Summary  string             // Optional summary of the work done
	Trace    []HandoffTraceItem // Execution trace (if fullContext was enabled)
	Metadata map[string]any     // Additional result metadata
}

HandoffResult contains the outcome of a delegation.

type HandoffTraceItem

type HandoffTraceItem struct {
	Type    string `json:"type"`    // "thought", "tool_call", "tool_result", "response"
	Content string `json:"content"` // The actual content of this step
}

HandoffTraceItem represents a single step in the delegated agent's execution.

type LLMProvider

type LLMProvider interface {
	CreateResponse(ctx context.Context, req ResponseRequest) (*ResponseObject, error)
	CreateResponseStream(ctx context.Context, req ResponseRequest) (ResponseStreamClient, error)
}

LLMProvider abstracts the Responses API client for testing and custom providers. DEPRECATED: Use Provider interface instead for better decoupling.

func NewProviderAdapter

func NewProviderAdapter(provider providers.Provider) LLMProvider

NewProviderAdapter creates an adapter from a Provider to LLMProvider.

type LogLevel

type LogLevel string

LogLevel represents the severity level

const (
	LogLevelDebug   LogLevel = "DEBUG"
	LogLevelDefault LogLevel = "DEFAULT"
	LogLevelWarning LogLevel = "WARNING"
	LogLevelError   LogLevel = "ERROR"
)

type LoggingConfig

type LoggingConfig = logging.LoggingConfig

Type aliases for internal package types

type Middleware

type Middleware = middleware.Middleware

Type aliases for internal package types

type MockLLM

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

MockLLM is a convenience wrapper around providers/mock.Provider for easier testing. It provides a builder pattern for configuring mock responses.

func NewMockLLM

func NewMockLLM() *MockLLM

NewMockLLM creates a new mock LLM provider for testing. This is a convenience wrapper around providers/mock that maintains backward compatibility with the old testing API.

Usage:

mock := agentkit.NewMockLLM().
    WithResponse("thinking...", []agentkit.ToolCall{
        {Name: "search", Args: map[string]any{"query": "test"}},
    }).
    WithFinalResponse("done")

agent, _ := agentkit.New(agentkit.Config{
    LLMProvider: mock,
    Model:       "test-model",
})

func (*MockLLM) CreateResponse

func (m *MockLLM) CreateResponse(ctx context.Context, req ResponseRequest) (*ResponseObject, error)

CreateResponse implements the LLMProvider interface for backward compatibility.

func (*MockLLM) CreateResponseStream

func (m *MockLLM) CreateResponseStream(ctx context.Context, req ResponseRequest) (ResponseStreamClient, error)

CreateResponseStream implements the LLMProvider interface for backward compatibility.

func (*MockLLM) WithFinalResponse

func (m *MockLLM) WithFinalResponse(text string) *MockLLM

WithFinalResponse appends a mock final response without tool calls.

func (*MockLLM) WithResponse

func (m *MockLLM) WithResponse(text string, toolCalls []ToolCall) *MockLLM

WithResponse appends a mock response with optional tool calls.

func (*MockLLM) WithStream

func (m *MockLLM) WithStream(chunks []providers.StreamChunk) *MockLLM

WithStream appends a mock stream of response chunks.

type ModelCostConfig

type ModelCostConfig struct {
	InputCostPer1MTokens  float64 // Cost per 1M input tokens in USD
	OutputCostPer1MTokens float64 // Cost per 1M output tokens in USD
}

ModelCostConfig defines the pricing for a specific model

type NoOpTracer

type NoOpTracer struct{}

NoOpTracer is a tracer that does nothing (used when tracing is disabled)

func (*NoOpTracer) Flush

func (n *NoOpTracer) Flush(ctx context.Context) error

func (*NoOpTracer) LogEvent

func (n *NoOpTracer) LogEvent(ctx context.Context, name string, attributes map[string]any) error

func (*NoOpTracer) LogGeneration

func (n *NoOpTracer) LogGeneration(ctx context.Context, opts GenerationOptions) error

func (*NoOpTracer) SetSpanAttributes

func (n *NoOpTracer) SetSpanAttributes(ctx context.Context, attributes map[string]any) error

func (*NoOpTracer) SetSpanOutput

func (n *NoOpTracer) SetSpanOutput(ctx context.Context, output any) error

func (*NoOpTracer) SetTraceAttributes

func (n *NoOpTracer) SetTraceAttributes(ctx context.Context, attributes map[string]any) error

func (*NoOpTracer) StartSpan

func (n *NoOpTracer) StartSpan(ctx context.Context, name string, opts ...SpanOption) (context.Context, func())

func (*NoOpTracer) StartTrace

func (n *NoOpTracer) StartTrace(ctx context.Context, name string, opts ...TraceOption) (context.Context, func())

type ParallelConfig

type ParallelConfig = parallel.ParallelConfig

Type aliases for internal package types

type ParameterSchema

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

ParameterSchema defines a tool parameter schema

func Array

func Array(itemType string) *ParameterSchema

Array creates an array parameter schema

func ArrayOf

func ArrayOf(itemSchema *ParameterSchema) *ParameterSchema

ArrayOf creates an array parameter schema for complex item schemas.

func Object

func Object() *ParameterSchema

Object creates an object parameter schema.

func String

func String() *ParameterSchema

String creates a string parameter schema

func StructToSchema

func StructToSchema[T any]() (*ParameterSchema, error)

StructToSchema converts a Go struct type to a ParameterSchema using reflection. It supports struct tags: `json`, `required`, `desc`, `enum`, `default`. This is useful for defining complex parameter schemas without manual WithProperty chains.

Supported struct tags:

  • json: field name (use "-" to skip field)
  • required: "true" marks field as required
  • desc: field description
  • enum: comma-separated allowed values
  • default: default value

Example:

type Filters struct {
    EmailDomain string `json:"email_domain" desc:"Filter by email domain"`
    Status      string `json:"status" required:"true" enum:"active,inactive"`
    AgeRange    struct {
        Min int `json:"min" desc:"Minimum age"`
        Max int `json:"max" desc:"Maximum age"`
    } `json:"age_range"`
}

schema, _ := agentkit.StructToSchema[Filters]()
tool := agentkit.NewTool("search").
    WithParameter("filters", schema).
    Build()

func (*ParameterSchema) Optional

func (ps *ParameterSchema) Optional() *ParameterSchema

Optional marks the parameter as optional

func (*ParameterSchema) Required

func (ps *ParameterSchema) Required() *ParameterSchema

Required marks the parameter as required

func (*ParameterSchema) ToMap

func (ps *ParameterSchema) ToMap() map[string]any

ToMap converts the schema to a map for OpenAI

func (*ParameterSchema) ToMapStrict

func (ps *ParameterSchema) ToMapStrict() map[string]any

ToMapStrict converts the schema to a map with strict mode enabled This wraps optional fields in anyOf with null and ensures all constraints are met

func (*ParameterSchema) WithDescription

func (ps *ParameterSchema) WithDescription(desc string) *ParameterSchema

WithDescription sets the parameter description

func (*ParameterSchema) WithEnum

func (ps *ParameterSchema) WithEnum(values ...string) *ParameterSchema

WithEnum sets allowed values for the parameter

func (*ParameterSchema) WithProperty

func (ps *ParameterSchema) WithProperty(name string, schema *ParameterSchema) *ParameterSchema

WithProperty adds a property to an object parameter schema.

type PendingFormatter

type PendingFormatter func(toolName string, args map[string]any) string

PendingFormatter formats the display message when a tool is about to execute It receives the tool name and parsed arguments

type ProviderAdapter

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

ProviderAdapter adapts the new Provider interface to the legacy LLMProvider interface. This maintains backward compatibility while allowing new code to use the cleaner Provider interface.

func (*ProviderAdapter) CreateResponse

func (a *ProviderAdapter) CreateResponse(ctx context.Context, req ResponseRequest) (*ResponseObject, error)

CreateResponse implements LLMProvider using the new Provider interface.

func (*ProviderAdapter) CreateResponseStream

func (a *ProviderAdapter) CreateResponseStream(ctx context.Context, req ResponseRequest) (ResponseStreamClient, error)

CreateResponseStream implements LLMProvider streaming using the new Provider interface.

type ReasoningEffort

type ReasoningEffort string

ReasoningEffort controls the reasoning strength for reasoning models (o1/o3)

const (
	// ReasoningEffortNone disables reasoning entirely (best for low-latency tasks)
	ReasoningEffortNone ReasoningEffort = "none"
	// ReasoningEffortMinimal generates very few reasoning tokens for fast time-to-first-token
	ReasoningEffortMinimal ReasoningEffort = "minimal"
	// ReasoningEffortLow favors speed and economical token usage
	ReasoningEffortLow ReasoningEffort = "low"
	// ReasoningEffortMedium provides balanced reasoning depth and speed (default)
	ReasoningEffortMedium ReasoningEffort = "medium"
	// ReasoningEffortHigh allocates significant computational depth for complex problems
	ReasoningEffortHigh ReasoningEffort = "high"
	// ReasoningEffortXHigh allocates the largest possible portion of tokens for reasoning (newest models)
	ReasoningEffortXHigh ReasoningEffort = "xhigh"
)

type ResponseAnnotation

type ResponseAnnotation struct {
	Type string `json:"type"`
}

ResponseAnnotation represents an annotation in content

type ResponseContentItem

type ResponseContentItem struct {
	Type        string               `json:"type"`
	Text        string               `json:"text,omitempty"`
	ImageURL    *ResponseImageURL    `json:"image_url,omitempty"`
	Annotations []ResponseAnnotation `json:"annotations,omitempty"`
	ToolCallID  string               `json:"tool_call_id,omitempty"`
	CallID      string               `json:"call_id,omitempty"`
	Content     string               `json:"content,omitempty"`
	Output      string               `json:"output,omitempty"`
}

ResponseContentItem represents a content item in input/output

type ResponseDelta

type ResponseDelta struct {
	Type      string                `json:"type"`
	Index     int                   `json:"index"`
	Content   []ResponseContentItem `json:"content,omitempty"`
	ToolCalls []ResponseToolCall    `json:"tool_calls,omitempty"`
}

ResponseDelta is deprecated - using event-based streaming instead

type ResponseError

type ResponseError struct {
	Code    string `json:"code"`
	Message string `json:"message"`
}

ResponseError represents an error in the response

type ResponseImageURL

type ResponseImageURL struct {
	URL string `json:"url"`
}

ResponseImageURL represents an image URL in content

type ResponseInput

type ResponseInput struct {
	Role    string                `json:"role"`
	Content []ResponseContentItem `json:"content"`
}

ResponseInput represents input to the model

type ResponseObject

type ResponseObject struct {
	ID                 string               `json:"id"`
	Object             string               `json:"object"`
	CreatedAt          int64                `json:"created_at"`
	Status             string               `json:"status"` // "completed", "failed", "in_progress", "cancelled", "queued", "incomplete"
	Model              string               `json:"model"`
	Output             []ResponseOutputItem `json:"output"`
	Usage              ResponseUsage        `json:"usage"`
	Error              *ResponseError       `json:"error"`
	PreviousResponseID string               `json:"previous_response_id"`
	Temperature        float32              `json:"temperature"`
	ParallelToolCalls  bool                 `json:"parallel_tool_calls"`
	ToolChoice         any                  `json:"tool_choice"`
	Tools              []ResponseTool       `json:"tools"`
}

ResponseObject represents a response from the API

type ResponseOutputItem

type ResponseOutputItem struct {
	Type      string                `json:"type"` // "message", "reasoning", "function_call", etc.
	ID        string                `json:"id"`
	Status    string                `json:"status"`
	Role      string                `json:"role,omitempty"`
	Name      string                `json:"name,omitempty"`      // For function_call type
	CallID    string                `json:"call_id,omitempty"`   // For function_call type
	Arguments string                `json:"arguments,omitempty"` // For function_call type
	Content   []ResponseContentItem `json:"content,omitempty"`
	Summary   []ResponseContentItem `json:"summary,omitempty"`
	ToolCalls []ResponseToolCall    `json:"tool_calls,omitempty"` // Deprecated - function_call items are separate
}

ResponseOutputItem represents an item in the output array

type ResponseReasoning

type ResponseReasoning struct {
	Effort  ReasoningEffort `json:"effort,omitempty"`
	Summary string          `json:"summary,omitempty"`
}

ResponseReasoning represents reasoning configuration for reasoning models

type ResponseRequest

type ResponseRequest struct {
	Model              string              `json:"model"`
	Input              any                 `json:"input,omitempty"` // string or []ResponseInput
	Instructions       string              `json:"instructions,omitempty"`
	Temperature        float32             `json:"temperature,omitempty"` // For GPT models only (ignored for o1/o3)
	MaxOutputTokens    int                 `json:"max_output_tokens,omitempty"`
	Tools              []ResponseTool      `json:"tools,omitempty"`
	ToolChoice         any                 `json:"tool_choice,omitempty"` // string or ResponseToolChoice
	Stream             bool                `json:"stream,omitempty"`
	Store              bool                `json:"store,omitempty"`
	PreviousResponseID string              `json:"previous_response_id,omitempty"`
	ParallelToolCalls  bool                `json:"parallel_tool_calls,omitempty"`
	TopP               float32             `json:"top_p,omitempty"`
	Text               *ResponseTextConfig `json:"text,omitempty"`
	Metadata           map[string]string   `json:"metadata,omitempty"`
	Reasoning          *ResponseReasoning  `json:"reasoning,omitempty"` // For reasoning models (gpt-5/o-series): use ResponseReasoning with effort
}

ResponseRequest represents a request to create a response Note: For reasoning models (gpt-5, o-series), set Reasoning.Effort instead of Temperature. Temperature is ignored for reasoning models.

type ResponseStream

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

ResponseStream wraps a streaming response

func (*ResponseStream) Close

func (s *ResponseStream) Close() error

Close closes the stream

func (*ResponseStream) ReadChunk

func (s *ResponseStream) ReadChunk() (*ResponseStreamChunk, error)

ReadChunk is an alias for Recv() to satisfy ResponseStreamClient interface

func (*ResponseStream) Recv

Recv receives the next chunk from the stream

type ResponseStreamChunk

type ResponseStreamChunk struct {
	Type           string               `json:"type"` // Event type
	SequenceNumber int                  `json:"sequence_number,omitempty"`
	ResponseID     string               `json:"response_id,omitempty"`
	Response       *ResponseObject      `json:"response,omitempty"`
	Error          *ResponseError       `json:"error,omitempty"` // For error events
	ItemID         string               `json:"item_id,omitempty"`
	OutputIndex    int                  `json:"output_index,omitempty"`
	Delta          string               `json:"delta,omitempty"`     // For delta events
	Text           string               `json:"text,omitempty"`      // For done events with text
	Item           *ResponseOutputItem  `json:"item,omitempty"`      // For added/done events
	Name           string               `json:"name,omitempty"`      // For function_call_arguments.done
	Arguments      string               `json:"arguments,omitempty"` // For function_call_arguments.done
	Status         string               `json:"status,omitempty"`
	Output         []ResponseOutputItem `json:"output,omitempty"` // For response.done
	Usage          *ResponseUsage       `json:"usage,omitempty"`
	Obfuscation    string               `json:"obfuscation,omitempty"` // Sent by API, purpose unclear
}

ResponseStreamChunk represents a streaming response chunk Responses API uses event-based streaming with specific event types: - response.output_item.added: New output item started - response.output_text.delta: Text content delta - response.function_call_arguments.delta: Function arguments delta - response.function_call_arguments.done: Function call complete (includes name and arguments) - response.done: Response complete

type ResponseStreamClient

type ResponseStreamClient interface {
	ReadChunk() (*ResponseStreamChunk, error)
	Close() error
}

ResponseStreamClient defines the interface for streaming responses

type ResponseTextConfig

type ResponseTextConfig struct {
	Format    ResponseTextFormat `json:"format"`
	Verbosity string             `json:"verbosity,omitempty"`
}

ResponseTextConfig represents text configuration

type ResponseTextFormat

type ResponseTextFormat struct {
	Type       string `json:"type"` // "text" or "json_schema"
	JSONSchema any    `json:"json_schema,omitempty"`
}

ResponseTextFormat represents text format configuration

type ResponseTokensDetails

type ResponseTokensDetails struct {
	CachedTokens    int `json:"cached_tokens,omitempty"`
	ReasoningTokens int `json:"reasoning_tokens,omitempty"`
}

ResponseTokensDetails represents detailed token information

type ResponseTool

type ResponseTool struct {
	Type        string         `json:"type"`
	Name        string         `json:"name"`
	Description string         `json:"description,omitempty"`
	Parameters  map[string]any `json:"parameters,omitempty"`
	Strict      bool           `json:"strict,omitempty"`
}

ResponseTool represents a tool definition for Responses API Note: In Responses API, name/description/parameters are at top level, not nested

type ResponseToolCall

type ResponseToolCall struct {
	ID        string `json:"id"`
	CallID    string `json:"call_id"`
	Type      string `json:"type"` // "function_call"
	Name      string `json:"name"`
	Arguments string `json:"arguments"`
}

ResponseToolCall represents a tool call in output For Responses API, tool calls are of type "function_call" not "tool_call"

type ResponseToolChoice

type ResponseToolChoice struct {
	Type     string                `json:"type"` // "auto", "required", "none", or "function"
	Function *ResponseToolFunction `json:"function,omitempty"`
}

ResponseToolChoice represents tool choice configuration

type ResponseToolFunction

type ResponseToolFunction struct {
	Name string `json:"name"`
}

ResponseToolFunction represents a specific function choice

type ResponseUsage

type ResponseUsage struct {
	InputTokens         int                   `json:"input_tokens"`
	OutputTokens        int                   `json:"output_tokens"`
	ReasoningTokens     int                   `json:"reasoning_tokens,omitempty"` // For reasoning models (o1, o3)
	TotalTokens         int                   `json:"total_tokens"`
	InputTokensDetails  ResponseTokensDetails `json:"input_tokens_details"`
	OutputTokensDetails ResponseTokensDetails `json:"output_tokens_details"`
}

ResponseUsage represents token usage

type ResponsesClient

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

ResponsesClient wraps OpenAI's Responses API

func NewResponsesClient

func NewResponsesClient(apiKey string, logger *slog.Logger) *ResponsesClient

NewResponsesClient creates a new Responses API client

func (*ResponsesClient) CreateResponse

func (c *ResponsesClient) CreateResponse(ctx context.Context, req ResponseRequest) (*ResponseObject, error)

CreateResponse creates a non-streaming response

func (*ResponsesClient) CreateResponseStream

func (c *ResponsesClient) CreateResponseStream(ctx context.Context, req ResponseRequest) (ResponseStreamClient, error)

CreateResponseStream creates a streaming response

type ResultFormatter

type ResultFormatter func(toolName string, result any) string

ResultFormatter formats the display message when a tool completes It receives the tool name and the result returned by the handler

type RetryConfig

type RetryConfig = retry.RetryConfig

Type aliases for internal package types

type SpanConfig

type SpanConfig struct {
	// Type specifies the span type (span, generation, event, tool, retrieval)
	Type SpanType
	// Input is the input data for this operation
	Input any
	// Metadata stores arbitrary key-value data
	Metadata map[string]any
	// Level specifies the log level (DEBUG, DEFAULT, WARNING, ERROR)
	Level LogLevel
}

SpanConfig holds configuration for a span

type SpanOption

type SpanOption func(*SpanConfig)

SpanOption configures span creation

func WithLogLevel

func WithLogLevel(level LogLevel) SpanOption

func WithSpanInput

func WithSpanInput(input any) SpanOption

func WithSpanMetadata

func WithSpanMetadata(metadata map[string]any) SpanOption

func WithSpanType

func WithSpanType(spanType SpanType) SpanOption

Option functions for span configuration

type SpanType

type SpanType string

SpanType represents the type of observation

const (
	// SpanTypeSpan is a generic span for non-LLM operations
	SpanTypeSpan SpanType = "span"
	// SpanTypeGeneration tracks LLM calls
	SpanTypeGeneration SpanType = "generation"
	// SpanTypeEvent tracks point-in-time events
	SpanTypeEvent SpanType = "event"
	// SpanTypeTool tracks tool/function calls
	SpanTypeTool SpanType = "tool"
	// SpanTypeRetrieval tracks RAG retrieval steps
	SpanTypeRetrieval SpanType = "retrieval"
)

type SystemPromptFunc

type SystemPromptFunc func(ctx context.Context) string

SystemPromptFunc builds the system prompt from context.

type TimeoutConfig

type TimeoutConfig = timeout.TimeoutConfig

Type aliases for internal package types

type Tool

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

Tool represents an agent tool with its metadata and handler

func (*Tool) Execute

func (t *Tool) Execute(ctx context.Context, argsJSON string) (any, error)

Execute runs the tool handler

func (*Tool) FormatPending

func (t *Tool) FormatPending(args map[string]any) string

FormatPending formats the pending message for this tool

func (*Tool) FormatResult

func (t *Tool) FormatResult(result any) string

FormatResult formats the result message for this tool

func (*Tool) Name

func (t *Tool) Name() string

Name returns the tool name

func (*Tool) ToOpenAI

func (t *Tool) ToOpenAI() interface{}

ToOpenAI converts the tool to OpenAI function definition. DEPRECATED: This method is kept for backward compatibility but couples the tool to OpenAI. New code should use ToToolDefinition() instead.

func (*Tool) ToToolDefinition

func (t *Tool) ToToolDefinition() providers.ToolDefinition

ToToolDefinition converts the tool to a provider-agnostic ToolDefinition.

type ToolBuilder

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

ToolBuilder helps construct tools with a fluent API

func NewStructTool

func NewStructTool[T any](name string, handler func(context.Context, T) (any, error)) (*ToolBuilder, error)

NewStructTool creates a tool builder using a struct type for schema and decoding.

func NewTool

func NewTool(name string) *ToolBuilder

NewTool creates a new tool builder

func (*ToolBuilder) Build

func (tb *ToolBuilder) Build() Tool

Build returns the constructed tool

func (*ToolBuilder) WithConcurrency

func (tb *ToolBuilder) WithConcurrency(mode ConcurrencyMode) *ToolBuilder

WithConcurrency controls whether a tool can run in parallel.

func (*ToolBuilder) WithDescription

func (tb *ToolBuilder) WithDescription(desc string) *ToolBuilder

WithDescription sets the tool description

func (*ToolBuilder) WithHandler

func (tb *ToolBuilder) WithHandler(handler ToolHandler) *ToolBuilder

WithHandler sets the tool handler function

func (*ToolBuilder) WithJSONSchema

func (tb *ToolBuilder) WithJSONSchema(schema map[string]any) *ToolBuilder

WithJSONSchema sets the full JSON schema for complex tools.

func (*ToolBuilder) WithParameter

func (tb *ToolBuilder) WithParameter(name string, schema *ParameterSchema) *ToolBuilder

WithParameter adds a parameter to the tool

func (*ToolBuilder) WithPendingFormatter

func (tb *ToolBuilder) WithPendingFormatter(formatter PendingFormatter) *ToolBuilder

WithPendingFormatter sets the formatter for pending tool execution messages

func (*ToolBuilder) WithRawParameters

func (tb *ToolBuilder) WithRawParameters(params map[string]any) *ToolBuilder

WithRawParameters sets the full parameters schema for complex tools.

func (*ToolBuilder) WithResultFormatter

func (tb *ToolBuilder) WithResultFormatter(formatter ResultFormatter) *ToolBuilder

WithResultFormatter sets the formatter for tool result messages

func (*ToolBuilder) WithStrictMode

func (tb *ToolBuilder) WithStrictMode(strict bool) *ToolBuilder

WithStrictMode enables or disables OpenAI Structured Outputs for this tool. When true (default), the tool schema uses strict JSON Schema validation, ensuring the model output always matches the schema exactly. Disable only if you need to use JSON Schema features not supported by strict mode.

type ToolCall

type ToolCall = providers.ToolCall

ToolCall represents a tool call for testing purposes. This is an alias for providers.ToolCall to maintain backward compatibility.

type ToolHandler

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

ToolHandler is a function that executes a tool

type TraceConfig

type TraceConfig struct {
	// UserID identifies the end-user
	UserID string
	// SessionID groups related traces (e.g., conversation thread)
	SessionID string
	// Tags categorize the trace
	Tags []string
	// Metadata stores arbitrary key-value data
	Metadata map[string]any
	// Input is the initial input for the trace
	Input any
	// Version tracks the application version
	Version string
	// Environment specifies the deployment environment (production, staging, etc.)
	Environment string
	// Release identifies the release version
	Release string
	// StartTime is the explicit start time for the trace (optional)
	StartTime *time.Time
}

TraceConfig holds configuration for a trace

type TraceOption

type TraceOption func(*TraceConfig)

TraceOption configures trace creation

func WithEnvironment

func WithEnvironment(env string) TraceOption

func WithMetadata

func WithMetadata(metadata map[string]any) TraceOption

func WithRelease

func WithRelease(release string) TraceOption

func WithSessionID

func WithSessionID(sessionID string) TraceOption

func WithTags

func WithTags(tags ...string) TraceOption

func WithTraceInput

func WithTraceInput(input any) TraceOption

func WithTraceStartTime

func WithTraceStartTime(startTime time.Time) TraceOption

WithTraceStartTime sets an explicit start time for the trace

func WithUserID

func WithUserID(userID string) TraceOption

Option functions for trace configuration

func WithVersion

func WithVersion(version string) TraceOption

type Tracer

type Tracer interface {
	// StartTrace creates a new trace context for the agent run
	// Returns a context with the trace attached and a function to end the trace
	StartTrace(ctx context.Context, name string, opts ...TraceOption) (context.Context, func())

	// StartSpan creates a new span within the current trace
	// Spans represent individual operations like tool calls or LLM generations
	StartSpan(ctx context.Context, name string, opts ...SpanOption) (context.Context, func())

	// LogGeneration records an LLM generation event
	LogGeneration(ctx context.Context, opts GenerationOptions) error

	// LogEvent records a simple event within the trace
	LogEvent(ctx context.Context, name string, attributes map[string]any) error

	// SetTraceAttributes sets attributes on the current trace
	SetTraceAttributes(ctx context.Context, attributes map[string]any) error

	// SetSpanOutput sets the output on the current span (observation)
	SetSpanOutput(ctx context.Context, output any) error

	// SetSpanAttributes sets attributes on the current span as observation metadata
	SetSpanAttributes(ctx context.Context, attributes map[string]any) error

	// Flush ensures all pending traces are sent (important for short-lived applications)
	Flush(ctx context.Context) error
}

Tracer defines the interface for tracing LLM operations This interface allows for multiple tracing backend implementations

func GetTracer

func GetTracer(ctx context.Context) Tracer

GetTracer retrieves the tracer from the context Returns nil if no tracer is in the context

type UsageInfo

type UsageInfo struct {
	PromptTokens     int
	CompletionTokens int
	ReasoningTokens  int // For reasoning models (o1, o3)
	TotalTokens      int
}

UsageInfo tracks token consumption This data comes directly from OpenAI's API response

Directories

Path Synopsis
internal
testutil
Package testutil provides testing utilities for AgentKit.
Package testutil provides testing utilities for AgentKit.
Package providers defines provider-agnostic interfaces and domain models for LLM interactions.
Package providers defines provider-agnostic interfaces and domain models for LLM interactions.
mock
Package mock implements a mock Provider for testing.
Package mock implements a mock Provider for testing.
openai
Package openai implements the Provider interface for OpenAI's Responses API.
Package openai implements the Provider interface for OpenAI's Responses API.
tracing
langfuse
Package langfuse provides Langfuse tracing implementation via OpenTelemetry
Package langfuse provides Langfuse tracing implementation via OpenTelemetry

Jump to

Keyboard shortcuts

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