Skip to content

Programmatic API Reference

Complete Go API reference for the github.com/kadirpekel/hector package. This reference covers all programmatic APIs for building agents, tools, runners, and RAG systems in Go.

Package Overview

Package Description
pkg/builder Fluent builder API for constructing components
pkg/agent Agent types, interfaces, and execution contexts
pkg/tool Tool interface and execution
pkg/model LLM interface abstraction
pkg/session Session and state management
pkg/memory Memory index and search
pkg/runner Session execution and orchestration
pkg/runtime Runtime lifecycle management
pkg/config Configuration structures
pkg/rag Document stores and embeddings
pkg Top-level convenience functions

Builder Package (pkg/builder)

LLM Builder

Constructs LLM instances from providers.

import "github.com/kadirpekel/hector/pkg/builder"

llm := builder.NewLLM("openai"). // "openai", "anthropic", "gemini", "ollama"
    Model("gpt-4o").
    APIKey(os.Getenv("OPENAI_API_KEY")).
    Temperature(0.7).
    MaxTokens(4096).
    BaseURL("https://api.openai.com/v1"). // optional custom endpoint
    MustBuild() // or Build() for error handling

Methods:

Method Description
NewLLM(provider string) Starts building an LLM for the specified provider
Model(name string) Sets the model name
APIKey(key string) Sets the API key
BaseURL(url string) Sets a custom API base URL
Temperature(temp float64) Sets sampling temperature (0.0-2.0)
MaxTokens(n int) Sets max tokens to generate
Build() (model.LLM, error) Finalizes and returns the LLM
MustBuild() model.LLM Panics on error

Agent Builder

Constructs intelligent agents with LLMs, tools, and sub-agents.

agent, err := builder.NewAgent("assistant").
    WithName("My Assistant").
    WithDescription("A helpful AI assistant").
    WithLLM(llm).
    WithInstruction("You are a helpful assistant.").
    WithTools(tool1, tool2).
    WithSubAgents(specialist1, specialist2). // Transfer pattern
    WithReasoning(reasoningConfig).
    EnableStreaming(true).
    Build()

Methods:

Method Description
NewAgent(id string) Starts building an agent with the given ID
WithName(name string) Sets the human-readable display name
WithDescription(desc string) Sets the agent description
WithLLM(llm model.LLM) Sets the LLM instance
WithInstruction(inst string) Sets the system instruction
WithTools(tools ...tool.Tool) Adds tools to the agent
WithTool(t tool.Tool) Adds a single tool
WithSubAgents(agents ...agent.Agent) Adds sub-agents (creates transfer tools)
WithReasoning(config *config.ReasoningConfig) Configures reasoning loop
EnableStreaming(enable bool) Enables/disables streaming responses
Build() (agent.Agent, error) Finalizes the agent

Tool Builders

Function Tool (Typed)

Creates tools from Go functions with automatic JSON schema generation from struct tags.

import "github.com/kadirpekel/hector/pkg/tool"

type WeatherArgs struct {
    City string `json:"city" jsonschema:"required,description=The city to check weather for"`
    Unit string `json:"unit" jsonschema:"description=Temperature unit (celsius/fahrenheit),default=celsius"`
}

weatherTool, err := builder.FunctionTool(
    "get_weather",
    "Get current weather for a city",
    func(ctx tool.Context, args WeatherArgs) (map[string]any, error) {
        // Implementation
        return map[string]any{
            "city": args.City,
            "temp": 22,
            "unit": args.Unit,
            "conditions": "Sunny",
        }, nil
    },
)

// Or use MustFunctionTool to panic on error
tool := builder.MustFunctionTool("name", "description", handler)

JSON Schema Tags:

Tag Description
json:"fieldname" JSON field name
jsonschema:"required" Field is required
jsonschema:"description=..." Field description
jsonschema:"default=..." Default value
jsonschema:"enum=a,b,c" Allowed values

Runner Builder

Constructs a runner to manage sessions and execute agents.

runner, err := builder.NewRunner("my-app").
    WithAgent(agent).
    WithSessionService(sessionSvc). // optional custom session storage
    Build()

Methods:

Method Description
NewRunner(appName string) Starts building a runner
WithAgent(agent agent.Agent) Sets the root agent
WithSessionService(svc session.Service) Sets custom session storage
Build() (*runner.Runner, error) Finalizes the runner

Reasoning Builder

Configures the agent's reasoning loop behavior.

cfg := builder.NewReasoning().
    MaxIterations(10).
    EnableExitTool(true).
    EnableEscalateTool(true).
    CompletionInstruction("Call exit_loop when done.").
    Build()

agent := builder.NewAgent("assistant").
    WithReasoning(cfg).
    Build()

Methods:

Method Description
MaxIterations(n int) Maximum reasoning loop iterations
EnableExitTool(bool) Enable explicit exit_loop tool
EnableEscalateTool(bool) Enable escalate tool for parent delegation
CompletionInstruction(string) Custom instruction for loop termination

RAG Builders

Embedder Builder

embedder := builder.NewEmbedder("openai"). // "openai", "ollama", "cohere"
    Model("text-embedding-3-small").
    APIKey(os.Getenv("OPENAI_API_KEY")).
    MustBuild()

Vector Provider Builder

vectorProvider := builder.NewVectorProvider("chromem"). // "chromem", "qdrant", etc.
    PersistPath("./data/vectors").
    MustBuild()

Document Store Builder

store, err := builder.NewDocumentStore("knowledge_base").
    FromDirectory("./documents").
    WithEmbedder(embedder).
    WithVectorProvider(vectorProvider).
    Build()

// Index documents
store.Index(ctx)

Agent Package (pkg/agent)

Agent Interface

type Agent interface {
    Name() string
    Run(ctx InvocationContext) iter.Seq2[*Event, error]
}

Invocation Context

Provides access to invocation data and services.

type InvocationContext interface {
    // Identity
    InvocationID() string
    AgentName() string

    // Input
    UserContent() *Content

    // State
    State() State
    ReadonlyState() ReadonlyState

    // Services
    Session() Session
    Memory() Memory
    Artifacts() Artifacts

    // Control
    EndInvocation()
    Ended() bool
}

Usage in Custom Agent:

agent, err := agent.New(agent.Config{
    Name: "custom",
    Run: func(ctx agent.InvocationContext) iter.Seq2[*agent.Event, error] {
        return func(yield func(*agent.Event, error) bool) {
            // Read state
            value, _ := ctx.State().Get("counter")

            // Update state
            ctx.State().Set("counter", value.(int) + 1)

            // Access user input
            user := ctx.UserContent()

            // Create response
            event := agent.NewEvent(ctx.InvocationID())
            event.Message = &a2a.Message{
                Role: a2a.MessageRoleAgent,
                Parts: []a2a.Part{
                    a2a.TextPart{Text: "Hello!"},
                },
            }
            yield(event, nil)
        }
    },
})

Readonly Context

Safe to pass to tools and callbacks (no state mutation).

type ReadonlyContext interface {
    InvocationID() string
    AgentName() string
    UserContent() *Content
    ReadonlyState() ReadonlyState
    UserID() string
    SessionID() string
}

Callback Context

Allows state modification in callbacks.

type CallbackContext interface {
    ReadonlyContext
    State() State
    Artifacts() Artifacts
}

Event Structure

type Event struct {
    ID           string
    InvocationID string
    Author       string       // Agent name
    Branch       string       // Agent hierarchy path
    Message      *a2a.Message
    Partial      bool         // Streaming chunk
    Actions      Actions
}

type Actions struct {
    ToolCalls  []ToolCall       // Tool invocations
    Transfer   *Transfer        // Delegation to another agent
    StateDelta map[string]any   // State changes
}

Content Structure

type Content struct {
    Role  string
    Parts []a2a.Part
}

Tool Package (pkg/tool)

Hector uses a layered tool interface hierarchy:

Tool (base)
  ├── CallableTool       - Simple synchronous execution
  ├── StreamingTool      - Real-time incremental output
  ├── CancellableTool    - Supports targeted cancellation
  ├── IsLongRunning()    - Async operations
  └── RequiresApproval() - HITL pattern

Tool Interface (Base)

type Tool interface {
    Name() string
    Description() string
    IsLongRunning() bool
    RequiresApproval() bool
}

CallableTool Interface

Extends Tool with synchronous execution:

type CallableTool interface {
    Tool
    Call(ctx Context, args map[string]any) (map[string]any, error)
    Schema() map[string]any
}

StreamingTool Interface

For tools that produce incremental output:

type StreamingTool interface {
    Tool
    CallStreaming(ctx Context, args map[string]any) iter.Seq2[*Result, error]
    Schema() map[string]any
}

Tool Context

type Context interface {
    agent.CallbackContext

    FunctionCallID() string
    Actions() *agent.EventActions
    SearchMemory(ctx context.Context, query string) (*agent.MemorySearchResponse, error)
    Task() agent.CancellableTask
}

Custom CallableTool Implementation

type WeatherTool struct{}

func (t *WeatherTool) Name() string {
    return "get_weather"
}

func (t *WeatherTool) Description() string {
    return "Fetches current weather for a location"
}

func (t *WeatherTool) IsLongRunning() bool {
    return false
}

func (t *WeatherTool) RequiresApproval() bool {
    return false
}

func (t *WeatherTool) Schema() map[string]any {
    return map[string]any{
        "type": "object",
        "properties": map[string]any{
            "city": map[string]any{
                "type":        "string",
                "description": "City name",
            },
        },
        "required": []string{"city"},
    }
}

func (t *WeatherTool) Call(ctx tool.Context, args map[string]any) (map[string]any, error) {
    city := args["city"].(string)
    // Implementation...
    return map[string]any{
        "temperature": 22,
        "conditions":  "Sunny",
    }, nil
}

Custom StreamingTool Implementation

For tools that produce incremental output (e.g., command execution):

func (t *CommandTool) CallStreaming(ctx tool.Context, args map[string]any) iter.Seq2[*tool.Result, error] {
    return func(yield func(*tool.Result, error) bool) {
        // Execute command and stream output...
        for line := range outputLines {
            if !yield(&tool.Result{Content: line, Streaming: true}, nil) {
                return // Client disconnected
            }
        }
        // Final result
        yield(&tool.Result{Content: finalOutput, Streaming: false}, nil)
    }
}

Session Package (pkg/session)

Session Interface

type Session interface {
    ID() string
    AppName() string
    UserID() string
    State() State
    Events() Events
    LastUpdateTime() time.Time
}

State Interface

type State interface {
    Get(key string) (any, error)
    Set(key string, value any) error
    Delete(key string) error
    All() iter.Seq2[string, any]
}

State Scopes (Key Prefixes):

Prefix Scope Lifecycle
(none) Session Persists within session
user: User Shared across user's sessions
app: Application Global across all users
temp: Temporary Cleared after invocation
// Session-specific
ctx.State().Set("last_search", "weather")

// User-level preferences
ctx.State().Set("user:theme", "dark")

// App-level configuration
ctx.State().Set("app:version", "2.0.0")

// Temporary (auto-cleared)
ctx.State().Set("temp:processing", true)

Events Interface

type Events interface {
    All() iter.Seq[*agent.Event]
    At(index int) *agent.Event
    Len() int
}

Session Service

type Service interface {
    Get(ctx context.Context, req *GetRequest) (*GetResponse, error)
    Create(ctx context.Context, req *CreateRequest) (*CreateResponse, error)
    AppendEvent(ctx context.Context, session Session, event *agent.Event) error
    List(ctx context.Context, req *ListRequest) (*ListResponse, error)
    Delete(ctx context.Context, req *DeleteRequest) error
}

Backends:

// In-memory (development)
svc := session.InMemoryService()

// SQL (production)
svc, err := session.NewSQLSessionService(db, "sqlite") // or "postgres", "mysql"

Memory Package (pkg/memory)

Index Service

type IndexService interface {
    Index(ctx context.Context, sess agent.Session) error
    Search(ctx context.Context, req *SearchRequest) (*SearchResponse, error)
    Rebuild(ctx context.Context, sessions session.Service, appName, userID string) error
    Clear(ctx context.Context, appName, userID, sessionID string) error
    Name() string
}

Search Request/Response

type SearchRequest struct {
    Query   string
    UserID  string
    AppName string
}

type SearchResponse struct {
    Results []SearchResult
}

type SearchResult struct {
    SessionID string
    EventID   string
    Content   string
    Author    string
    Timestamp time.Time
    Score     float64
    Metadata  map[string]any
}

Runner Package (pkg/runner)

Runner

Manages agent execution across sessions.

type Runner struct {
    // ...
}

func (r *Runner) Run(
    ctx context.Context,
    userID string,
    sessionID string,
    content *agent.Content,
    config RunConfig,
) iter.Seq2[*agent.Event, error]

Usage:

runner, _ := builder.NewRunner("my-app").
    WithAgent(myAgent).
    Build()

content := &agent.Content{
    Role:  "user",
    Parts: []a2a.Part{a2a.TextPart{Text: "Hello!"}},
}

for event, err := range runner.Run(ctx, "user-1", "session-1", content, agent.RunConfig{}) {
    if err != nil {
        log.Fatal(err)
    }
    if event.Message != nil {
        fmt.Println(event.Message.Parts)
    }
}

Runtime Package (pkg/runtime)

Runtime Creation

runtime, err := runtime.New(cfg,
    runtime.WithSessionService(sessionSvc),
    runtime.WithObservability(obs),
)

Runtime Options

Option Description
WithSessionService(svc) Custom session storage
WithObservability(obs) Observability manager
WithSubAgents(agentName, agents) Inject sub-agents
WithAgentTools(agentName, agents) Inject agents as tools
WithDirectTools(agentName, tools) Inject custom tools
WithLLMFactory(factory) Custom LLM factory
WithEmbedderFactory(factory) Custom embedder factory
WithToolsetFactory(factory) Custom toolset factory

Custom Tool Injection:

runtime.New(cfg,
    runtime.WithDirectTools("assistant", []tool.Tool{
        &MyCustomTool{},
    }),
)

Custom Sub-Agents:

runtime.New(cfg,
    runtime.WithSubAgents("coordinator", []agent.Agent{
        customSpecialist,
    }),
)

Custom Factory:

runtime.New(cfg,
    runtime.WithLLMFactory(func(cfg *config.LLMConfig) (model.LLM, error) {
        if cfg.Provider == "custom" {
            return &CustomLLM{}, nil
        }
        return DefaultLLMFactory(cfg)
    }),
)

Runtime Reload

err := runtime.Reload(newConfig)

Atomically swaps components while preserving active sessions.

Top-Level Package (pkg)

AgentAsTool

Wraps an agent to be used as a tool (delegation pattern).

import "github.com/kadirpekel/hector/pkg"

researcherAgent, _ := builder.NewAgent("researcher").
    WithLLM(llm).
    WithInstruction("Research topics thoroughly").
    Build()

managerAgent, _ := builder.NewAgent("manager").
    WithLLM(llm).
    WithTool(pkg.AgentAsTool(researcherAgent)).
    Build()

Callbacks

Agent Callbacks

Before Agent:

type BeforeAgentCallback func(CallbackContext) (*a2a.Message, error)

beforeAgent := func(ctx agent.CallbackContext) (*a2a.Message, error) {
    // Validate, inject context, or skip execution
    if !authorized {
        return &a2a.Message{
            Role: a2a.MessageRoleAgent,
            Parts: []a2a.Part{
                a2a.TextPart{Text: "Unauthorized"},
            },
        }, nil // Skip agent execution
    }
    return nil, nil // Continue
}

After Agent:

type AfterAgentCallback func(CallbackContext) (*a2a.Message, error)

Model Callbacks

Before Model:

type BeforeModelCallback func(ctx CallbackContext, req *model.Request) (*model.Response, error)

After Model:

type AfterModelCallback func(ctx CallbackContext, resp *model.Response, err error) (*model.Response, error)

Tool Callbacks

Before Tool:

type BeforeToolCallback func(ctx tool.Context, t tool.Tool, args map[string]any) (map[string]any, error)

beforeTool := func(ctx tool.Context, t tool.Tool, args map[string]any) (map[string]any, error) {
    log.Info("Tool called", "name", t.Name(), "args", args)

    // Validate arguments
    if err := validate(args); err != nil {
        return nil, err
    }

    // Modify arguments
    args["timestamp"] = time.Now()
    return args, nil
}

After Tool:

type AfterToolCallback func(ctx tool.Context, t tool.Tool, args, result map[string]any, err error) (map[string]any, error)

Working Memory Strategies

Strategy Interface

type WorkingMemoryStrategy interface {
    FilterEvents(events []*agent.Event) []*agent.Event
    CheckAndSummarize(ctx context.Context, events []*agent.Event) (*agent.Event, error)
}

Built-in Strategies

All (No Filtering):

// Include entire conversation history

Buffer Window:

type BufferWindow struct {
    MaxMessages int
}

func (b *BufferWindow) FilterEvents(events []*agent.Event) []*agent.Event {
    if len(events) <= b.MaxMessages {
        return events
    }
    return events[len(events)-b.MaxMessages:]
}

Token Window:

// Keep messages within token budget

Summary Buffer:

// Summarize old messages when exceeding threshold

Artifacts

The Artifacts interface provides artifact storage operations:

type Artifacts interface {
    Save(ctx context.Context, name string, part a2a.Part) (*ArtifactSaveResponse, error)
    List(ctx context.Context) (*ArtifactListResponse, error)
    Load(ctx context.Context, name string) (*ArtifactLoadResponse, error)
    LoadVersion(ctx context.Context, name string, version int) (*ArtifactLoadResponse, error)
}

Usage:

// Save artifact (a2a.Part can be FilePart, DataPart, etc.)
resp, err := ctx.Artifacts().Save(ctx, "chart.png", a2a.FilePart{
    Name:     "chart.png",
    MimeType: "image/png",
    Bytes:    imageBytes,
})

// List artifacts
list, err := ctx.Artifacts().List(ctx)
for _, info := range list.Artifacts {
    fmt.Println(info.Name, info.Version)
}

// Load artifact
artifact, err := ctx.Artifacts().Load(ctx, "chart.png")

Complete Example

package main

import (
    "context"
    "fmt"
    "os"

    "github.com/a2aproject/a2a-go/a2a"
    "github.com/kadirpekel/hector/pkg"
    "github.com/kadirpekel/hector/pkg/builder"
    "github.com/kadirpekel/hector/pkg/agent"
    "github.com/kadirpekel/hector/pkg/tool"
)

type WeatherArgs struct {
    City string `json:"city" jsonschema:"required,description=City name"`
}

func main() {
    ctx := context.Background()

    // 1. Build LLM
    llm := builder.NewLLM("openai").
        Model("gpt-4o").
        APIKey(os.Getenv("OPENAI_API_KEY")).
        MustBuild()

    // 2. Create tools
    weatherTool := builder.MustFunctionTool(
        "get_weather",
        "Get weather for a city",
        func(ctx tool.Context, args WeatherArgs) (map[string]any, error) {
            return map[string]any{"temp": 22, "conditions": "Sunny"}, nil
        },
    )

    // 3. Create sub-agent
    researcher, _ := builder.NewAgent("researcher").
        WithLLM(llm).
        WithInstruction("Research topics thoroughly").
        Build()

    // 4. Build main agent with sub-agent as tool
    assistant, _ := builder.NewAgent("assistant").
        WithName("AI Assistant").
        WithLLM(llm).
        WithTools(weatherTool).
        WithTool(pkg.AgentAsTool(researcher)).
        WithInstruction("You are a helpful assistant.").
        EnableStreaming(true).
        Build()

    // 5. Create runner
    runner, _ := builder.NewRunner("my-app").
        WithAgent(assistant).
        Build()

    // 6. Run conversation
    content := &agent.Content{
        Role:  "user",
        Parts: []a2a.Part{a2a.TextPart{Text: "What's the weather in Berlin?"}},
    }

    for event, err := range runner.Run(ctx, "user-1", "session-1", content, agent.RunConfig{}) {
        if err != nil {
            panic(err)
        }
        if event.Message != nil {
            for _, part := range event.Message.Parts {
                if text, ok := part.(a2a.TextPart); ok {
                    fmt.Print(text.Text)
                }
            }
        }
    }
}