golem

package module
v0.8.2 Latest Latest
Warning

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

Go to latest
Published: Sep 19, 2026 License: MIT Imports: 14 Imported by: 0

README

golem

Golem

Golem is a Go-first framework for building dependable AI agents: typed dependencies and outputs, explicit tools, composable models, and an observable execution loop.

It takes inspiration from the ergonomics of Python agent frameworks such as Pydantic AI, but follows Go's strengths instead: compile-time contracts, context.Context, explicit error handling, small interfaces, and standard-library-friendly integrations.

Status

v0.8.1 — the core execution contract includes typed agents, evidence-preserving runs — successful and failed alike, via RunError.Partial, with activity counts, the provider's finish reason, and the provider's cache and reasoning token breakdown on every outcome — self-correction, retries with fallback models, streaming on every adapter including Bedrock, structured output, explicit tool deadlines and choice, opt-in ordered parallel tool execution, multimodal input spanning images, documents, audio, and video, thinking content carried end to end with provider signatures, deferred tools that pause a run for human approval or external results, in-tool deliberate run cancellation (&tool.Canceled{}), untrusted-history sanitization with structural reporting (golem.SanitizeHistory), bounded history by count or token budget with explicit repair for damaged conversations, tools that return images and documents as evidence and definitive failures that reach the model, token/request/tool-call/cost usage bounds with optional pre-send input estimation, request tuning, run and conversation identity stamped on events, results, and messages, run events, and agent delegation. The common tools (web fetch, file read, command execution, PDF extraction, multi-format document extraction, agent skills) ship alongside an MCP client that bridges server tools over stdio or streamable HTTP, with provider adapters for OpenAI-compatible APIs (twelve services plus the local Ollama and LM Studio runtimes), Anthropic, Google Gemini, Azure OpenAI, and AWS Bedrock, plus an embeddings port with adapters for OpenAI-compatible APIs (including Azure and the local runtimes) and Gemini, token-counting ports for Anthropic, Gemini, and Bedrock, and user-supplied pricing that reports and bounds a run's dollar cost. The guides publish as a documentation site. The public API remains intentionally small; additive changes only until v1.

Direction

  • Make a useful agent the shortest path: configure a model, declare tools, run with dependencies, get a typed result.
  • Make important behavior explicit: model calls, tool execution, iteration limits, usage, and validation are visible in the run result.
  • Keep infrastructure replaceable: applications choose models, tracing, storage, and transport through narrow interfaces.
  • Prefer Go-native composition over ports of Python metaprogramming.

Read the foundation brief before proposing a new public abstraction. Contributor and coding-agent rules live in AGENTS.md, and the development roadmap lives in docs/ROADMAP.md.

Installation

go get github.com/abubakarsiddik31/golem

Golem needs Go 1.26.5 or newer and depends only on the Go standard library.

Quick start

client, err := openai.New(openai.Config{
    APIKey: os.Getenv("OPENAI_API_KEY"),
    Model:  "gpt-4o-mini",
})
agent, err := golem.New[struct{}, string](client,
    golem.DecodeFunc[string](func(_ context.Context, r model.Response) (string, error) {
        return r.Message.Content, nil
    }),
)
result, err := agent.Run(ctx, golem.RunContext[struct{}]{}, "Reply with exactly the word: pong")

Every run returns the typed output, the full normalized conversation (result.Messages, durable additive-only JSON), and cumulative usage — and fails with a RunError carrying an inspectable stage (model, tool, decode, loop, usage) that preserves the cause for errors.Is and errors.As.

Documentation

Guides are the source of truth for each capability; this README only indexes them.

Guide Covers
Getting started The smallest agent, result shape, error stages
Providers OpenAI-compatible and Anthropic adapters, error classification
Embeddings The embedding.Embedder port: queries, documents, usage
Token counting The tokens.Counter port: budgets, pre-send limits
Tools and dependencies Typed tools, dependencies, and controlled parallel execution
Web fetch The webfetch common tool: URLs as agent-readable text
File read The fileread common tool: workspace files as agent-readable text
Command execution The shell common tool: one command, combined output
PDF extract The pdfextract common tool: PDF documents as structured Markdown with tables and images
Document extract The docextract common tool: Word, Excel, PowerPoint, Markdown, CSV, and multi-format documents
Agent skills The skills common tool: standard SKILL.md folders loaded on demand
MCP client Bridging Model Context Protocol servers into agent tools
Agent delegation One agent exposed as another agent's tool
Tool timeouts Context-aware deadlines for individual tool calls
Conversations and history Multi-turn runs, durable message JSON, history trimming
Multimodal input Images, documents, audio, and video in prompts, per-provider mapping
Structured output Output schemas, tool-mode output, DecodeJSON
Self-correction Output and tool rejection budgets (ModelRetry)
Retries Transient model failures, backoff, fallback models
Streaming RunStream, the streaming capability port, SSE adapters
Thinking Reasoning models: requesting thinking, keeping signatures, replay
Run events Observing attempts, tool calls, and corrections as they happen
Usage limits Bounding tokens, requests, and tool calls
Cost User-supplied pricing: Result.Cost and cost bounds
Testing without a provider Deterministic fakes, contract assertions
Deferred tools Approvals and external results: pausing a run and resuming it

Design decisions live in docs/adr/; each guide links the ADR that decided its behavior.

Examples

Runnable programs live in examples/; provider-backed ones print instructions and exit unless their API key is set.

Example Shows
minimal Smallest agent against an OpenAI-compatible API
embeddings Semantic search over the embedding.Embedder port
token-counting Pre-send limits and budget-bounded history over the tokens.Counter port
cost User-supplied pricing: Result.Cost and cost-bounded runs, offline
tool-results Tools returning parts and definitive failures, offline
history-repair Normalizing a damaged conversation with a report, offline
history-sanitization Sanitizing a client-submitted history at the trust boundary, offline
run-ids Run and conversation identity across chained and forked runs, offline
run-cancellation A tool ending the run deliberately with tool.Canceled, resuming the evidence, offline
tools Typed tool with a run dependency
web-fetch The webfetch common tool fetching a local test page
file-read The fileread common tool reading a workspace file
command-execution The shell common tool running one local command
pdf-extract The pdfextract common tool extracting tables and reading order, offline
doc-extract The docextract common tool extracting Word, Excel, and Markdown, offline
skills The skills common tool loading a standard SKILL.md folder
mcp-client MCP server bridged into agent tools over stdio
mcp-http MCP server bridged over streamable HTTP
delegation A specialist agent delegated to as a tool
structured-output Output schema + JSON decoding
structured-output-tool Tool-mode structured output
streaming RunStream printing fragments as they arrive
run-events WithRunEvents printing the event sequence of a run
partial-evidence A failed run's RunError.Partial evidence resumed with history, offline
thinking Adaptive thinking with reasoning blocks and signatures
conversation Interactive multi-turn chat with history
self-correction Tool rejecting correctable arguments
fallback Primary model with a fallback and a request bound
anthropic Anthropic Messages API adapter
gemini Google Gemini GenerateContent adapter
azure Azure OpenAI deployment adapter
bedrock AWS Bedrock Converse adapter with SigV4
local-models Ollama or LM Studio through the OpenAI-compatible adapter
testing-without-a-provider Scripted fake model, offline and deterministic
OPENAI_API_KEY=sk-... go run ./examples/minimal
GOLEM_LOCAL_BASE_URL=http://localhost:11434/v1 go run ./examples/local-models   # Ollama or LM Studio
go run ./examples/testing-without-a-provider   # no credentials needed
go run ./examples/partial-evidence             # no credentials needed

Package shape

golem/        Agent configuration and typed run API
model/        Provider-neutral model request/response contract
tool/         Tool declarations and execution contracts
webfetch/     Common tool: fetch a URL as agent-readable text
fileread/     Common tool: read a file as agent-readable text
shell/        Common tool: run one command, return combined output
mcp/          Model Context Protocol client bridging servers into tools
providers/    Stdlib-only adapters implementing model.Model
internal/     Execution loop and non-public mechanics
examples/     Runnable programs per capability
docs/guides/  Feature guides (source of truth for behavior)
docs/adr/     Decisions that shape the public contracts

Development

go test ./...
go vet ./...

The guides double as the published documentation site; preview it with mkdocs serve — see docs/website.md. Logo usage guidelines and brand assets live in assets/brand/.

Community

License

Released under the MIT License.

Documentation

Overview

Package golem provides typed building blocks for AI agents in Go.

Index

Examples

Constants

View Source
const (
	// EventModelStart precedes one provider call attempt.
	EventModelStart = runner.EventModelStart
	// EventModelEnd follows one provider call attempt, carrying the
	// attempt's usage and error.
	EventModelEnd = runner.EventModelEnd
	// EventToolStart precedes one tool execution.
	EventToolStart = runner.EventToolStart
	// EventToolEnd follows one tool execution, carrying the result or the
	// error; a correction rejection carries a *model.ModelRetry error.
	EventToolEnd = runner.EventToolEnd
	// EventOutputRejected marks a decoder rejection starting a correction
	// round; its Attempt numbers the round that follows, and turn numbers
	// restart with it.
	EventOutputRejected = runner.EventOutputRejected
	// EventDeferred marks a tool call that deferred instead of executing;
	// the run pauses with the call pending on Result.Pending. It replaces
	// the call's tool-end event.
	EventDeferred = runner.EventDeferred
	// EventCanceled marks the tool call that ended the run with the
	// &tool.Canceled sentinel: the boundary after which nothing else
	// executes. It follows the cancelling call's tool-end event; CallID
	// and ToolName identify the call and Err carries the sentinel. A run
	// that ends this way reports RunError with StageCanceled, evidence on
	// RunError.Partial.
	EventCanceled = runner.EventCanceled
)
View Source
const DefaultMaxIterations = 10

DefaultMaxIterations bounds model turns per run when no explicit limit is configured.

Variables

This section is empty.

Functions

func NewID added in v0.7.6

func NewID() string

NewID mints a unique, time-ordered identifier in UUID version 7 form (RFC 9562): a millisecond timestamp followed by cryptographically secure random bytes, so identifiers sort by creation time. Runs mint their own run identifiers and, when history carries none, their conversation identifiers; NewID is what an application uses to mint its own — pinning a run's identity to a trace ID it already has, or forking a conversation by supplying a fresh identifier to WithConversationID. Uniqueness within one millisecond rests on the random bits, so simultaneous mints are unordered but distinct.

Types

type Agent

type Agent[Deps any, Output any] struct {
	// contains filtered or unexported fields
}

Agent combines a model, instructions, tools, and a typed output boundary. Deps is the dependency value tools receive on every run.

Example

ExampleAgent demonstrates an agent that executes a typed tool with an explicit dependency value and returns the full run evidence.

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"

	"github.com/abubakarsiddik31/golem"
	"github.com/abubakarsiddik31/golem/model"
	"github.com/abubakarsiddik31/golem/tool"
)

// diceModel scripts the tool exchange: it requests the player-name tool
// once, then produces a final answer. Real applications implement
// model.Model with a provider adapter.
type diceModel struct{ requests []model.Request }

func (m *diceModel) Generate(ctx context.Context, request model.Request) (model.Response, error) {
	m.requests = append(m.requests, request)
	for _, message := range request.Messages {
		if message.Role == model.RoleTool {
			return model.Response{
				Message: model.Message{Role: model.RoleAssistant, Content: fmt.Sprintf("winner: %s", message.Content)},
				Usage:   model.Usage{InputTokens: 54, OutputTokens: 2},
			}, nil
		}
	}
	return model.Response{
		Message: model.Message{Role: model.RoleAssistant, ToolCalls: []model.ToolCall{
			{ID: "call-1", Name: "get_player_name", Args: json.RawMessage(`{}`)},
		}},
		Usage: model.Usage{InputTokens: 54, OutputTokens: 2},
	}, nil
}

func main() {
	getPlayerName := tool.MustNew(tool.Tool[string]{
		Name:        "get_player_name",
		Description: "Get the player's name.",
		Schema:      json.RawMessage(`{"type":"object"}`),
		Exec: func(ctx context.Context, playerName string, args json.RawMessage) (tool.Result, error) {
			return tool.Text(playerName), nil
		},
	})

	agent, err := golem.New[string, string](
		&diceModel{},
		golem.DecodeFunc[string](func(ctx context.Context, response model.Response) (string, error) {
			return response.Message.Content, nil
		}),
		golem.WithTools[string, string](getPlayerName),
	)
	if err != nil {
		log.Fatal(err)
	}

	result, err := agent.Run(context.Background(), golem.RunContext[string]{Deps: "Anne"}, "My guess is 4")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.Output)
	fmt.Println(len(result.Messages), "messages,", result.Usage.OutputTokens, "output tokens")
	fmt.Println("requests:", result.Requests, "- tool calls:", result.ToolCalls)
}
Output:
winner: Anne
4 messages, 4 output tokens
requests: 2 - tool calls: 1

func New

func New[Deps any, Output any](
	modelClient model.Model,
	decoder OutputDecoder[Output],
	options ...Option[Deps, Output],
) (*Agent[Deps, Output], error)

New creates an Agent. A model and decoder are both required: Golem never guesses how untrusted model output becomes a typed application value.

func (*Agent[Deps, Output]) AsTool added in v0.6.0

func (a *Agent[Deps, Output]) AsTool(name, description string, options ...AgentToolOption[Deps, Output]) (tool.Tool[Deps], error)

AsTool exposes the agent as a tool another agent can request: the model passes a prompt, the agent runs it with the delegating run's dependency value, and the typed output is rendered to text as the tool result.

Both agents must share the Deps type — the tool carries the delegating run's dependency value into the sub-agent's RunContext unchanged. The sub-agent sees nothing else of the delegating conversation: the prompt argument is its entire input.

A string output is rendered as-is; every other type is JSON-encoded; WithAgentResult replaces either. Malformed or empty prompt arguments are rejected with *model.ModelRetry, so the delegating run's tool retry budget governs correction. Every other sub-agent failure fails the delegating run at the tool stage with the inner RunError preserved in the chain; cancellation keeps its identity through the chain for errors.Is, and a sub-agent run ended by &tool.Canceled cancels the delegating run at the cancellation stage the same way. The sub-agent's own messages and usage are not part of the delegating run's evidence — only the rendered result is.

Example
package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"

	"github.com/abubakarsiddik31/golem"
	"github.com/abubakarsiddik31/golem/model"
)

// delegatingModel hands the question to the researcher tool, then answers
// from its result. It stands in for the planner's provider.
type delegatingModel struct{}

func (m *delegatingModel) Generate(ctx context.Context, request model.Request) (model.Response, error) {
	for _, message := range request.Messages {
		if message.Role == model.RoleTool {
			return model.Response{
				Message: model.Message{Role: model.RoleAssistant,
					Content: fmt.Sprintf("the researcher says: %s", message.Content)},
			}, nil
		}
	}
	return model.Response{
		Message: model.Message{Role: model.RoleAssistant, ToolCalls: []model.ToolCall{
			{ID: "call-1", Name: "researcher", Args: json.RawMessage(`{"prompt":"capital of France?"}`)},
		}},
	}, nil
}

// factModel stands in for the specialist's provider: one run, one fact.
type factModel struct{}

func (m *factModel) Generate(ctx context.Context, request model.Request) (model.Response, error) {
	return model.Response{Message: model.Message{Role: model.RoleAssistant, Content: "Paris"}}, nil
}

func main() {
	specialist, err := golem.New[struct{}, string](&factModel{},
		golem.DecodeFunc[string](func(ctx context.Context, response model.Response) (string, error) {
			return response.Message.Content, nil
		}))
	if err != nil {
		log.Fatal(err)
	}
	research, err := specialist.AsTool("researcher", "Answers one geography question.")
	if err != nil {
		log.Fatal(err)
	}
	planner, err := golem.New[struct{}, string](&delegatingModel{},
		golem.DecodeFunc[string](func(ctx context.Context, response model.Response) (string, error) {
			return response.Message.Content, nil
		}),
		golem.WithTools[struct{}, string](research))
	if err != nil {
		log.Fatal(err)
	}

	result, err := planner.Run(context.Background(), golem.RunContext[struct{}]{}, "I need the capital of France.")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.Output)
}
Output:
the researcher says: Paris

func (*Agent[Deps, Output]) Run

func (a *Agent[Deps, Output]) Run(ctx context.Context, runCtx RunContext[Deps], prompt string, opts ...RunOption) (Result[Output], error)

Run executes the agent: it asks the configured model to answer prompt, executing requested tools along the way, and decodes the final response. Model calls are attempted up to the configured attempt limit; exhausted retries fail with the model stage, preserving the provider cause. Output the decoder rejects with *model.ModelRetry is fed back for correction up to the configured output retry budget, and tool calls a tool rejects with *model.ModelRetry are fed back up to the tool retry budget.

Errors are wrapped in RunError with the failing stage. A run that had begun producing evidence — completed model turns, reported usage, executed tools — carries it as RunError.Partial; a failure before any activity leaves Partial nil. Cancellation and deadline errors are wrapped like every other failure and remain matchable with errors.Is through RunError.Unwrap. A tool that returns &tool.Canceled ends the run deliberately at the cancellation stage instead: not a failure, the sentinel stays reachable with errors.As, and Partial keeps the tool results recorded before the stop in a resume-ready transcript.

func (*Agent[Deps, Output]) RunStream

func (a *Agent[Deps, Output]) RunStream(ctx context.Context, runCtx RunContext[Deps], prompt string, onDelta func(model.Delta) error, opts ...RunOption) (Result[Output], error)

RunStream executes the agent like Run while streaming progress: every model fragment — text, tool-call arguments, and re-streamed correction rounds — is forwarded to onDelta in arrival order, across tool turns. The returned Result is identical in shape to Run's; deltas are advisory progress on top of the canonical run.

The model must implement model.StreamingModel; otherwise RunStream fails up front with a plain error, before any stage runs — there is no silent fallback to non-streaming generation. Streamed model turns are single-attempt: retryable failures fail the run at the model stage instead of being retried, because a retried stream would replay fragments the caller already saw. An error returned from onDelta stops the run and surfaces at the model stage with the original error reachable via errors.Is. A nil onDelta is allowed and discards fragments. Failures carry RunError.Partial evidence like Run's.

Example

ExampleAgent_RunStream shows a run that streams every fragment to the callback while producing the same typed result as Run.

package main

import (
	"context"
	"fmt"
	"log"
	"strings"

	"github.com/abubakarsiddik31/golem"
	"github.com/abubakarsiddik31/golem/model"
)

// morningModel streams its answer as two fragments.
type morningModel struct{}

func (m *morningModel) Generate(ctx context.Context, request model.Request) (model.Response, error) {
	return model.Response{Message: model.Message{Role: model.RoleAssistant, Content: "good morning"}}, nil
}

func (m *morningModel) GenerateStream(ctx context.Context, request model.Request, onDelta func(model.Delta) error) (model.Response, error) {
	for _, fragment := range []string{"good ", "morning"} {
		if err := onDelta(model.Delta{Content: fragment}); err != nil {
			return model.Response{}, err
		}
	}
	return model.Response{Message: model.Message{Role: model.RoleAssistant, Content: "good morning"}}, nil
}

func main() {
	agent, err := golem.New[struct{}, string](&morningModel{},
		golem.DecodeFunc[string](func(ctx context.Context, response model.Response) (string, error) {
			return response.Message.Content, nil
		}))
	if err != nil {
		log.Fatal(err)
	}

	var fragments []string
	result, err := agent.RunStream(context.Background(), golem.RunContext[struct{}]{}, "greet me",
		func(d model.Delta) error {
			fragments = append(fragments, d.Content)
			return nil
		})
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(strings.Join(fragments, "|"))
	fmt.Println(result.Output)
}
Output:
good |morning
good morning

func (*Agent[Deps, Output]) RunStreamWithHistory

func (a *Agent[Deps, Output]) RunStreamWithHistory(ctx context.Context, runCtx RunContext[Deps], history []model.Message, prompt string, onDelta func(model.Delta) error, opts ...RunOption) (Result[Output], error)

RunStreamWithHistory continues a conversation like RunWithHistory while streaming progress; see RunStream for the streaming contract.

func (*Agent[Deps, Output]) RunWithDeferredResults added in v0.7.0

func (a *Agent[Deps, Output]) RunWithDeferredResults(ctx context.Context, runCtx RunContext[Deps], history []model.Message, results DeferredResults, prompt string, opts ...RunOption) (Result[Output], error)

RunWithDeferredResults resumes a run that paused on deferred tool calls. history is the paused run's Result.Messages; results resolves every pending call; prompt optionally continues the conversation with a new user message — an empty prompt resumes on the resolutions alone.

Approved calls re-execute their tool with the approved marker set (see tool.CallApproved) under the configured tool timeout; a re-run that fails — or defers again — fails the resume run at the tool stage, and a re-run that returns &tool.Canceled ends the resume run at the cancellation stage, both before any model call. Denied calls and external results become the calls' tool results, in the model's emission order. The resumed run continues through the ordinary loop and may itself pause again.

func (*Agent[Deps, Output]) RunWithHistory

func (a *Agent[Deps, Output]) RunWithHistory(ctx context.Context, runCtx RunContext[Deps], history []model.Message, prompt string, opts ...RunOption) (Result[Output], error)

RunWithHistory continues a conversation. history — typically the Result.Messages of a previous run — is sent before a fresh user prompt, and the result carries the full reconstructed conversation so runs chain. The agent's current instructions govern the request: any system messages in history are replaced by them, so guidance is re-evaluated per run and never duplicated.

History is repaired before the request is built so it keeps the call/result pairing providers require: a tool call that never received a result — from a crashed or cancelled run, or hand-built history — gets a synthesized result stating no outcome was produced, and a result whose call is absent is dropped.

Example

ExampleAgent_RunWithHistory continues a conversation across two runs: the first result's messages become the second run's history, and the second result carries the full chained conversation.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/abubakarsiddik31/golem"
	"github.com/abubakarsiddik31/golem/model"
)

// conversationModel answers with the last user prompt it has seen.
type conversationModel struct{}

func (m *conversationModel) Generate(ctx context.Context, request model.Request) (model.Response, error) {
	last := ""
	for _, message := range request.Messages {
		if message.Role == model.RoleUser {
			last = message.Content
		}
	}
	return model.Response{
		Message: model.Message{Role: model.RoleAssistant, Content: fmt.Sprintf("heard: %s", last)},
	}, nil
}

func main() {
	agent, err := golem.New[struct{}, string](&conversationModel{},
		golem.DecodeFunc[string](func(ctx context.Context, response model.Response) (string, error) {
			return response.Message.Content, nil
		}))
	if err != nil {
		log.Fatal(err)
	}
	runCtx := golem.RunContext[struct{}]{}

	first, err := agent.Run(context.Background(), runCtx, "hello")
	if err != nil {
		log.Fatal(err)
	}
	second, err := agent.RunWithHistory(context.Background(), runCtx, first.Messages, "goodbye")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(first.Output)
	fmt.Println(second.Output)
	fmt.Println(len(second.Messages), "messages in the chained conversation")
}
Output:
heard: hello
heard: goodbye
4 messages in the chained conversation

type AgentToolOption added in v0.6.0

type AgentToolOption[Deps any, Output any] func(*agentToolConfig[Deps, Output])

AgentToolOption configures a tool built by Agent.AsTool.

func WithAgentResult added in v0.6.0

func WithAgentResult[Deps any, Output any](fn func(ctx context.Context, output Output) (string, error)) AgentToolOption[Deps, Output]

WithAgentResult replaces how a successful sub-agent output is rendered for the delegating model. fn runs inside the tool execution: honor ctx and return an error to fail the delegating run at the tool stage. It is also the hook for capturing the inner run's typed result or evidence.

type Approval added in v0.7.0

type Approval struct {
	// Approved re-executes the tool with the approved marker set, so the
	// gated action happens inside the run that holds the approval.
	Approved bool
	// Reason is shown to the model when Approved is false; empty falls
	// back to a plain denial message. It is ignored on approval.
	Reason string
}

Approval is the decision on one deferred approval request, keyed by call ID in DeferredResults.Approvals.

type DecodeFunc

type DecodeFunc[Output any] func(context.Context, model.Response) (Output, error)

DecodeFunc adapts a function to an OutputDecoder.

func (DecodeFunc[Output]) Decode

func (f DecodeFunc[Output]) Decode(ctx context.Context, response model.Response) (Output, error)

Decode converts response using f.

type DeferredRequests added in v0.7.0

type DeferredRequests struct {
	Approvals []PendingToolCall
	External  []PendingToolCall
}

DeferredRequests enumerates the tool calls that paused a run, grouped by what resolving them requires. Approvals wait for a human decision; External wait for a result produced outside the run.

type DeferredResults added in v0.7.0

type DeferredResults struct {
	// Approvals carries the human decision per approval request.
	Approvals map[string]Approval
	// External carries the result per externally executed call, handed
	// to the model verbatim as the call's tool result.
	External map[string]string
}

DeferredResults resolves the pending calls of a paused run, keyed by call ID. Every pending call must be resolved exactly once and no unknown call ID is accepted; validation fails the resume run before any model call.

type EventKind added in v0.6.0

type EventKind = runner.EventKind

EventKind identifies which RunEvent fields carry meaning.

type HistoryProcessor

type HistoryProcessor func(ctx context.Context, history []model.Message) ([]model.Message, error)

HistoryProcessor rewrites the history of one run before the request is built. It receives the history exactly as the caller supplied it — before validation and repair — and returns the history to send; the returned messages are then part-validated, repaired, and sent. The processor runs once per run; an error fails the run before any model call. Processors must be deterministic enough for their caller's purposes: nothing re-runs them.

func BudgetHistory added in v0.7.6

func BudgetHistory(counter tokens.Counter, maxTokens int) HistoryProcessor

BudgetHistory returns a HistoryProcessor that keeps the newest turns of a conversation whose input-token count fits maxTokens, as reported by the counter. It counts the history it receives — the run's tools and instructions are not included, so leave headroom for them — and, while over budget, drops the oldest message and counts again, always advancing past messages that cannot open a request under the same boundary rule TrimHistory uses. Counting is one call to the counter per dropped message plus one, so prefer a generous budget over a tight one. A nil counter or a budget below 1 fails the run; so does a history whose newest openable turn alone exceeds the budget, or nothing left after the boundary rule.

func TrimHistory

func TrimHistory(maxMessages int) HistoryProcessor

TrimHistory returns a HistoryProcessor that keeps the newest maxMessages messages of a conversation. After the cut it advances past messages that cannot open a request: tool results whose requesting call was trimmed, and assistant turns carrying tool calls whose results were trimmed — repair would otherwise reattach synthesized results, paying tokens for evidence the trim meant to drop. A budget below 1, or a history with nothing left after the boundary rule, fails the run.

type HistoryRepair added in v0.7.6

type HistoryRepair = runner.HistoryRepair

HistoryRepair reports what one NormalizeHistory pass changed, so an application boundary can act on a damaged history instead of discovering it from a provider rejection.

func NormalizeHistory added in v0.7.6

func NormalizeHistory(history []model.Message) ([]model.Message, HistoryRepair)

NormalizeHistory restores the call/result pairing providers require and reports every change. A crashed or cancelled run leaves tool calls without results — each receives a synthesized interrupted result, placed directly after the assistant message that requested it. A context-evicting pipeline leaves results without calls — each is dropped. A stream that died mid-arguments leaves a tool call whose JSON is not an object — reported as truncated; the arguments stay verbatim in the returned history, and adapters make them sendable on the wire.

The pass only adds or removes pairing evidence; it never rewrites messages. Synthesized and Dropped name what this pass changed; Truncated names the damage the pass leaves in place by design — a truncated call stays listed on every pass. The pass is deterministic and idempotent — normalizing a normalized history returns it unchanged — and synthesized results carry no wall-clock data, so repeated passes stay prompt-cache friendly. Runs also self-heal pairing at request build time, silently; NormalizeHistory is the explicit pass for application boundaries that want to see the damage first.

type InstructionsFunc

type InstructionsFunc[Deps any] func(ctx context.Context, runCtx RunContext[Deps]) string

InstructionsFunc builds the instructions of one run. ctx is the caller's run context, so a builder that consults external state can honor cancellation.

type Option

type Option[Deps any, Output any] func(*Agent[Deps, Output])

Option configures an Agent during construction.

func WithHistoryProcessor

func WithHistoryProcessor[Deps any, Output any](processor HistoryProcessor) Option[Deps, Output]

WithHistoryProcessor configures a processor applied to the history of every run, before validation and repair, on Run and its history-aware and streaming variants. See TrimHistory for a builtin.

func WithInstructions

func WithInstructions[Deps any, Output any](instructions string) Option[Deps, Output]

WithInstructions configures stable system instructions for every run.

func WithInstructionsFunc

func WithInstructionsFunc[Deps any, Output any](fn InstructionsFunc[Deps]) Option[Deps, Output]

WithInstructionsFunc configures instructions evaluated at the start of every run, so guidance can depend on runtime state such as the run's dependency value. The result joins static instructions — static text first, separated by a blank line — and an empty result contributes nothing. History system messages are replaced by the resolved instructions of the current run, exactly as for static instructions. Register one function; compose closures when several sources apply.

Example

ExampleWithInstructionsFunc shows instructions resolved per run: the function's result joins the static instructions, and both flow to the model as the run's system guidance.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/abubakarsiddik31/golem"
	"github.com/abubakarsiddik31/golem/model"
)

// instructedModel echoes the instructions it was given, if any.
type instructedModel struct{}

func (m *instructedModel) Generate(ctx context.Context, request model.Request) (model.Response, error) {
	for _, message := range request.Messages {
		if message.Role == model.RoleSystem {
			return model.Response{
				Message: model.Message{Role: model.RoleAssistant, Content: message.Content},
			}, nil
		}
	}
	return model.Response{Message: model.Message{Role: model.RoleAssistant, Content: ""}}, nil
}

func main() {
	type player struct{ Name string }
	agent, err := golem.New[player, string](&instructedModel{},
		golem.DecodeFunc[string](func(ctx context.Context, response model.Response) (string, error) {
			return response.Message.Content, nil
		}),
		golem.WithInstructions[player, string]("Always greet the player."),
		golem.WithInstructionsFunc[player, string](
			func(ctx context.Context, runCtx golem.RunContext[player]) string {
				return "The player's name is " + runCtx.Deps.Name + "."
			}),
	)
	if err != nil {
		log.Fatal(err)
	}

	result, err := agent.Run(context.Background(), golem.RunContext[player]{Deps: player{Name: "Anne"}}, "greet")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.Output)
}
Output:
Always greet the player.

The player's name is Anne.

func WithMaxAttempts

func WithMaxAttempts[Deps any, Output any](attempts int) Option[Deps, Output]

WithMaxAttempts bounds how many times each model call may be attempted, including the first, when the model reports a retryable failure (408, 429, 5xx, transport faults). Tool and decode failures are never retried. The default is 1 — retries are opt-in — and values below 1 fail New.

func WithMaxIterations

func WithMaxIterations[Deps any, Output any](iterations int) Option[Deps, Output]

WithMaxIterations bounds model turns per run. It must be at least 1; otherwise New fails.

func WithOutputRetries

func WithOutputRetries[Deps any, Output any](retries int) Option[Deps, Output]

WithOutputRetries sets how many correction rounds a decoder may request by returning *model.ModelRetry: each round appends the rejection reason to the conversation and asks the model again. The default is 0 — self-correction is opt-in — and negative values fail New.

Example

ExampleWithOutputRetries shows a decoder rejecting a correctable response: the run feeds the rejection back to the model, which answers again within the configured budget.

package main

import (
	"context"
	"fmt"
	"log"
	"strconv"
	"strings"

	"github.com/abubakarsiddik31/golem"
	"github.com/abubakarsiddik31/golem/model"
)

// pickyModel answers with a word first, then with the digit once corrected.
type pickyModel struct{ calls int }

func (m *pickyModel) Generate(ctx context.Context, request model.Request) (model.Response, error) {
	m.calls++
	content := "seven"
	if m.calls > 1 {
		content = "7"
	}
	return model.Response{Message: model.Message{Role: model.RoleAssistant, Content: content}}, nil
}

func main() {
	agent, err := golem.New[struct{}, int](&pickyModel{},
		golem.DecodeFunc[int](func(ctx context.Context, response model.Response) (int, error) {
			value, err := strconv.Atoi(strings.TrimSpace(response.Message.Content))
			if err != nil {
				return 0, &model.ModelRetry{Err: fmt.Errorf("answer must be an integer, got %q", response.Message.Content)}
			}
			return value, nil
		}),
		golem.WithOutputRetries[struct{}, int](2),
	)
	if err != nil {
		log.Fatal(err)
	}

	result, err := agent.Run(context.Background(), golem.RunContext[struct{}]{}, "pick a number")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.Output)
	fmt.Println(len(result.Messages), "messages in the corrected conversation")
}
Output:
7
4 messages in the corrected conversation

func WithOutputSchema

func WithOutputSchema[Deps any, Output any](schema json.RawMessage) Option[Deps, Output]

WithOutputSchema declares the JSON Schema document describing the agent's expected final answer. Adapters that support structured output map it to their native mechanism; adapters that do not ignore it. The schema describes the expected shape to the model — the decoder remains the validation boundary. An empty schema disables the behavior; a non-empty schema that is not valid JSON fails New. Mutually exclusive with WithOutputTool, which expresses the same intent through an output tool call.

Example

ExampleWithOutputSchema pairs a declared output schema — sent to the model as structured-output instructions by adapters that support them — with the JSON decoder that validates the response content.

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"

	"github.com/abubakarsiddik31/golem"
	"github.com/abubakarsiddik31/golem/model"
)

// forecastModel answers with JSON shaped by the output schema.
type forecastModel struct{}

func (m *forecastModel) Generate(ctx context.Context, request model.Request) (model.Response, error) {
	return model.Response{
		Message: model.Message{Role: model.RoleAssistant, Content: `{"city":"Lagos","celsius":31}`},
		Usage:   model.Usage{InputTokens: 20, OutputTokens: 6},
	}, nil
}

func main() {
	type weather struct {
		City    string `json:"city"`
		Celsius int    `json:"celsius"`
	}
	agent, err := golem.New[struct{}, weather](&forecastModel{}, golem.DecodeJSON[weather](),
		golem.WithOutputSchema[struct{}, weather](json.RawMessage(`{
			"type": "object",
			"properties": {"city": {"type": "string"}, "celsius": {"type": "integer"}},
			"required": ["city", "celsius"],
			"additionalProperties": false
		}`)),
	)
	if err != nil {
		log.Fatal(err)
	}

	result, err := agent.Run(context.Background(), golem.RunContext[struct{}]{}, "forecast for Lagos")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s: %d°C\n", result.Output.City, result.Output.Celsius)
}
Output:
Lagos: 31°C

func WithOutputTool

func WithOutputTool[Deps any, Output any](name, description string, schema json.RawMessage) Option[Deps, Output]

WithOutputTool declares tool-mode structured output: schema becomes the parameters of a synthesized output tool offered to the model, and the run ends on the model's first call to it. The call's arguments reach the decoder as the final response content, so DecodeJSON validates them like any other response — the decoder remains the validation boundary.

Tool mode reaches every model with tool calling, including those without native JSON-schema output support. Calls co-emitted with the output call are not executed; they are closed with an interrupted result so the conversation keeps the call/result pairing providers require. The output call itself is closed in the result evidence after decoding: a recorded result on success, a rejection bound to the call when the decoder asks for correction. Mutually exclusive with WithOutputSchema. name must not collide with a registered tool; description may be empty; schema must be a non-empty valid JSON document.

Example

ExampleWithOutputTool declares tool-mode structured output: the schema becomes the parameters of a synthesized output tool, the run ends on the model's first call to it, and the call's arguments reach the decoder as the final response content.

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"

	"github.com/abubakarsiddik31/golem"
	"github.com/abubakarsiddik31/golem/model"
)

// reportingModel calls the output tool with its final arguments.
type reportingModel struct{}

func (m *reportingModel) Generate(ctx context.Context, request model.Request) (model.Response, error) {
	return model.Response{
		Message: model.Message{Role: model.RoleAssistant, ToolCalls: []model.ToolCall{
			{ID: "out-1", Name: "record_weather", Args: json.RawMessage(`{"city":"Lagos","celsius":31}`)},
		}},
		Usage: model.Usage{InputTokens: 20, OutputTokens: 6},
	}, nil
}

func main() {
	type weather struct {
		City    string `json:"city"`
		Celsius int    `json:"celsius"`
	}
	agent, err := golem.New[struct{}, weather](&reportingModel{}, golem.DecodeJSON[weather](),
		golem.WithOutputTool[struct{}, weather]("record_weather",
			"Record the final weather report.", json.RawMessage(`{
				"type": "object",
				"properties": {"city": {"type": "string"}, "celsius": {"type": "integer"}},
				"required": ["city", "celsius"],
				"additionalProperties": false
			}`)),
	)
	if err != nil {
		log.Fatal(err)
	}

	result, err := agent.Run(context.Background(), golem.RunContext[struct{}]{}, "forecast for Lagos")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s: %d°C\n", result.Output.City, result.Output.Celsius)
}
Output:
Lagos: 31°C

func WithParallelToolCalls

func WithParallelToolCalls[Deps any, Output any]() Option[Deps, Output]

WithParallelToolCalls lets independent calls returned in one model response run concurrently. Result messages remain in model emission order. A tool marked Sequential is a barrier: earlier calls finish, it runs alone, then later calls begin. The default is false for compatibility and predictable side effects.

func WithPrice added in v0.7.6

func WithPrice[Deps any, Output any](price model.Price) Option[Deps, Output]

WithPrice wires a model.Price so the run can report and bound cost: Result.Cost and PartialResult.Cost carry the run's cumulative usage priced at these rates, and UsageLimit.Cost turns the price into a post-response bound checked with the other usage limits. Rates are the application's or an adapter package's; Golem ships no price table. A nil price leaves cost unpriced at zero.

func WithRetryBackoff

func WithRetryBackoff[Deps any, Output any](backoff func(attempt int) time.Duration) Option[Deps, Output]

WithRetryBackoff overrides the wait between retried model calls. backoff receives the 1-based number of the attempt that just failed. When attempts are enabled without an explicit backoff, runs wait with exponential backoff: 500 ms doubling, capped at 30 s.

func WithRunEvents added in v0.6.0

func WithRunEvents[Deps any, Output any](onEvent func(RunEvent)) Option[Deps, Output]

WithRunEvents registers an observer invoked for every observable point of each run: provider call attempts — retried attempts included —, tool executions, and decoder correction boundaries. The observer runs inline with execution: it must not block, it cannot fail the run, and an observer that must stop the run cancels the run context. Run and its history and streaming variants emit the same events. Events are advisory observation; the canonical record remains the run Result.

func WithTokenCounter added in v0.7.6

func WithTokenCounter[Deps any, Output any](counter tokens.Counter) Option[Deps, Output]

WithTokenCounter wires a tokens.Counter so the run can price a request before sending it: UsageLimit.PerRequestInputTokens turns the counter into a pre-send bound checked before every model call. A nil counter leaves the agent without one; setting the per-request bound without a counter fails New.

func WithToolChoice

func WithToolChoice[Deps any, Output any](name string) Option[Deps, Output]

WithToolChoice restricts this agent's advertised tools to name. It is a provider-neutral availability boundary: the selected tool is the only function sent to the model, so models that do not support a provider-native forced-choice flag still cannot request another registered tool. An empty or unregistered name fails New.

func WithToolRetries

func WithToolRetries[Deps any, Output any](retries int) Option[Deps, Output]

WithToolRetries sets how many tool rejections a run feeds back to the model: a tool signals correctable arguments by returning an error wrapping *model.ModelRetry, and the run delivers the rejection as the call's tool result so the model can try again. The default is 0 — self-correction is opt-in — and negative values fail New. The budget counts total rejections per run and is additionally bounded by the model turn limit.

Example

ExampleWithToolRetries shows a tool rejecting correctable arguments: the run delivers the rejection as the call's tool result, and the model calls again with fixed arguments within the configured budget.

package main

import (
	"context"
	"encoding/json"
	"fmt"
	"log"
	"strings"

	"github.com/abubakarsiddik31/golem"
	"github.com/abubakarsiddik31/golem/model"
	"github.com/abubakarsiddik31/golem/tool"
)

// learningModel requests the roll tool with an invalid argument first,
// then corrects the call once it sees the rejection come back.
type learningModel struct{ calls int }

func (m *learningModel) Generate(ctx context.Context, request model.Request) (model.Response, error) {
	last := request.Messages[len(request.Messages)-1]
	if last.Role == model.RoleTool && !strings.Contains(last.Content, "rejected") {
		return model.Response{
			Message: model.Message{Role: model.RoleAssistant, Content: fmt.Sprintf("the die %s", last.Content)},
		}, nil
	}
	m.calls++
	n := 0
	if m.calls > 1 {
		n = 4
	}
	return model.Response{Message: model.Message{Role: model.RoleAssistant, ToolCalls: []model.ToolCall{
		{ID: fmt.Sprintf("call-%d", m.calls), Name: "roll", Args: json.RawMessage(fmt.Sprintf(`{"n":%d}`, n))},
	}}}, nil
}

func main() {
	roll := tool.MustNew(tool.Tool[struct{}]{
		Name:        "roll",
		Description: "Roll a die; n must be positive.",
		Schema:      json.RawMessage(`{"type":"object","properties":{"n":{"type":"integer"}}}`),
		Exec: func(ctx context.Context, deps struct{}, args json.RawMessage) (tool.Result, error) {
			var input struct {
				N int `json:"n"`
			}
			if err := json.Unmarshal(args, &input); err != nil {
				return tool.Result{}, err
			}
			if input.N <= 0 {
				return tool.Result{}, &model.ModelRetry{Err: fmt.Errorf("n must be positive, got %d", input.N)}
			}
			return tool.Text(fmt.Sprintf("rolled %d", input.N)), nil
		},
	})

	agent, err := golem.New[struct{}, string](&learningModel{},
		golem.DecodeFunc[string](func(ctx context.Context, response model.Response) (string, error) {
			return response.Message.Content, nil
		}),
		golem.WithTools[struct{}, string](roll),
		golem.WithToolRetries[struct{}, string](2),
	)
	if err != nil {
		log.Fatal(err)
	}

	result, err := agent.Run(context.Background(), golem.RunContext[struct{}]{}, "roll a 4")
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println(result.Output)
	fmt.Println(len(result.Messages), "messages in the corrected run")
}
Output:
the die rolled 4
6 messages in the corrected run

func WithToolTimeout

func WithToolTimeout[Deps any, Output any](timeout time.Duration) Option[Deps, Output]

WithToolTimeout sets the default deadline for one tool execution. A tool's non-zero Timeout takes precedence. The zero value disables the default; negative values fail New. Tools must honor their context so work ends when the deadline expires.

func WithTools

func WithTools[Deps any, Output any](tools ...tool.Tool[Deps]) Option[Deps, Output]

WithTools registers tools the model may request. Tools should be built with tool.New; New rejects invalid or duplicate declarations.

func WithUsageLimit

func WithUsageLimit[Deps any, Output any](limit UsageLimit) Option[Deps, Output]

WithUsageLimit bounds the tokens a single run may consume and the model requests and tool executions it may make, counted across every model turn, retried call, and correction round. The check runs after each model response against the run's cumulative usage: the response that crosses a bound fails the run at the usage stage, even when it would have decoded successfully. Negative values fail New.

Example

ExampleWithUsageLimit shows a run stopped at the usage stage: the response that crosses the bound fails the run, with the crossed dimension inspectable through the typed cause.

package main

import (
	"context"
	"errors"
	"fmt"
	"log"

	"github.com/abubakarsiddik31/golem"
	"github.com/abubakarsiddik31/golem/model"
)

// verboseModel reports heavy usage on every response.
type verboseModel struct{}

func (m *verboseModel) Generate(ctx context.Context, request model.Request) (model.Response, error) {
	return model.Response{
		Message: model.Message{Role: model.RoleAssistant, Content: "an expensive answer"},
		Usage:   model.Usage{InputTokens: 1200, OutputTokens: 800},
	}, nil
}

func main() {
	agent, err := golem.New[struct{}, string](&verboseModel{},
		golem.DecodeFunc[string](func(ctx context.Context, response model.Response) (string, error) {
			return response.Message.Content, nil
		}),
		golem.WithUsageLimit[struct{}, string](golem.UsageLimit{TotalTokens: 1000}),
	)
	if err != nil {
		log.Fatal(err)
	}

	_, err = agent.Run(context.Background(), golem.RunContext[struct{}]{}, "answer")
	var runErr *golem.RunError
	if !errors.As(err, &runErr) {
		log.Fatal(err)
	}
	fmt.Println(runErr.Stage)
	fmt.Println(runErr.Err)
}
Output:
usage
run exceeded the total token limit of 1000 (used 2000)

type OutputDecoder

type OutputDecoder[Output any] interface {
	Decode(ctx context.Context, response model.Response) (Output, error)
}

OutputDecoder validates and converts a provider response to the agent's declared result type. It is the boundary at which model-produced data becomes application data. Returning *model.ModelRetry rejects a response the model can correct; with an output retry budget configured, the run feeds the rejection back to the model.

func DecodeJSON

func DecodeJSON[Output any]() OutputDecoder[Output]

DecodeJSON returns an OutputDecoder that decodes the final response's message content as JSON into Output. Content that is not valid JSON for Output is rejected as *model.ModelRetry — a correctable rejection — so with an output retry budget configured the run asks the model to fix the response instead of failing. Pair it with WithOutputSchema so the model is told the expected shape up front.

type PartialResult added in v0.7.1

type PartialResult struct {
	// Messages is the ordered conversation evidence, ending at the last
	// completed model turn.
	Messages []model.Message
	// Usage sums the provider-reported consumption of completed turns.
	Usage model.Usage
	// FinishReason is the provider's terminal cause of the last
	// completed model turn — FinishLength, for example, when a
	// truncated turn is what made the output undecodable.
	FinishReason model.FinishReason
	// Requests counts model calls the run made, failed attempts included.
	Requests int
	// ToolCalls counts tool executions the run attempted.
	ToolCalls int
	// Cost is the run's cumulative usage priced at the agent's WithPrice
	// rates when one is wired; zero when no price is wired.
	Cost float64
	// RunID and ConversationID are the failed run's identity — the same
	// values its events carried and its Result would have reported — so
	// telemetry can join a failure to the conversation it interrupted
	// even when the failure predates any recoverable evidence.
	RunID          string
	ConversationID string
}

PartialResult is the evidence of a failed run: the conversation through its last completed model turn, the usage completed turns reported, and the run's activity counts. A failure inside a tool batch leaves Messages ending at the assistant turn that requested the batch — its executed results are observable through run events — and any tool call left without a result is repaired on resume, exactly as for a crashed run. A cancellation is the one exception: the tool results recorded before the stop stay in Messages, every unanswered call of the batch is closed with a synthesized no-result result, and the transcript is resumable without repair. Feed Partial.Messages to RunWithHistory to continue a failed conversation.

type PendingToolCall added in v0.7.0

type PendingToolCall struct {
	// CallID identifies the call; resolution results are keyed by it.
	CallID string
	// ToolName and Args identify what the model asked for. Args is
	// model-produced JSON — validate before trusting it.
	ToolName string
	Args     json.RawMessage
	// Reason is the tool's explanation to whoever resolves the request,
	// such as the approval prompt text or a correlation key.
	Reason string
}

PendingToolCall is one deferred call awaiting resolution.

type Result

type Result[Output any] struct {
	Output   Output
	Messages []model.Message
	Usage    model.Usage
	// FinishReason is the provider's terminal cause of the final model
	// turn: FinishStop for an answered run, FinishToolCall when the run
	// ended on tool calls (a paused run, or an output-tool run, both of
	// which the provider saw as tool requests), FinishLength when the
	// provider truncated the turn. Empty when the model reported no
	// cause.
	FinishReason model.FinishReason
	// Requests counts the model calls the run made, retried and failed
	// attempts included — the same count UsageLimit.Requests bounds and
	// RunError.Partial preserves on failure, so a cost ledger never has
	// to infer activity from messages. It counts this run only: a
	// delegated sub-agent's activity stays in the sub-agent's own result.
	Requests int
	// ToolCalls counts the tool executions the run attempted — rejected
	// calls included, because the tool ran; unknown-tool requests,
	// deferred calls, and interrupted output-tool co-emissions do not
	// count. An approved deferred call's re-run on resume does.
	ToolCalls int
	// Cost is the run's cumulative usage priced at the WithPrice rates in
	// US dollars — a best-effort estimate, not a billing figure. Zero
	// when no price is wired or the priced usage costs nothing.
	Cost float64
	// Pending is non-nil when the run paused awaiting deferred tool
	// calls; see DeferredRequests for the resolution contract.
	Pending *DeferredRequests
	// RunID identifies this run: minted fresh per run — never inherited
	// from history — unless WithRunID supplies one. It rides every event
	// the run emits and every message the run added to Messages, so a
	// shared agent's interleaved events stay attributable.
	RunID string
	// ConversationID identifies the conversation the run continued:
	// inherited from the most recent identified message of the supplied
	// history, pinned or forked with WithConversationID, and minted when
	// none applies. Chained RunWithHistory calls share it; messages
	// carrying it on Result.Messages re-identify the conversation after
	// storage round-trips, no session object required.
	ConversationID string
}

Result preserves the typed output and the normalized model evidence that produced it, including every tool-call exchange in execution order. This makes testing and observability possible without a tracing backend.

A run that pauses on deferred tool calls reports Pending and skips decoding: Output is the zero value and Messages ends with the executed calls' results only. Check Pending before relying on Output.

type RunContext

type RunContext[Deps any] struct {
	Deps Deps
}

RunContext carries explicit application dependencies for a run. Its Deps value flows to every tool executed during the run.

type RunError

type RunError struct {
	Stage Stage
	Err   error
	// Partial preserves the evidence the run accumulated before the
	// error ended it; see PartialResult. It is nil when the run failed
	// before producing any: no model turn completed, no usage was
	// reported, and no tool executed.
	Partial *PartialResult
}

RunError adds an inspectable execution stage while preserving the source error for errors.Is and errors.As.

func (*RunError) Error

func (e *RunError) Error() string

func (*RunError) Unwrap

func (e *RunError) Unwrap() error

Unwrap exposes the originating model or decoder error.

type RunEvent added in v0.6.0

type RunEvent = runner.Event

RunEvent is one observation of an executing run: a provider call attempt, a tool execution, or a correction boundary. Events are delivered synchronously and in deterministic execution order — the contract and its ordering rules live with the execution loop and are re-exported here.

type RunOption

type RunOption func(*runOptions)

RunOption customizes a single run. Options are evaluated once, at run start; invalid input fails the run before any model call.

func WithConversationID added in v0.7.6

func WithConversationID(id string) RunOption

WithConversationID pins the identifier of the conversation this run continues; the default resolves it from the supplied history — the most recent message carrying one — and mints a fresh identifier when none applies, starting a new conversation. An empty value resolves like the default. To fork: continue a history under a NEW identity by passing golem.NewID(), which the history's identifiers cannot override.

func WithPromptImageData

func WithPromptImageData(mediaType string, data []byte) RunOption

WithPromptImageData attaches one inline image with its media type, such as "image/png". Data is application-owned: treat it as immutable once attached.

func WithPromptImageURL

func WithPromptImageURL(url string) RunOption

WithPromptImageURL attaches one image reachable at url; the provider fetches it. See the multimodal support each adapter documents — not every provider accepts image URLs.

func WithPromptParts

func WithPromptParts(parts ...model.Part) RunOption

WithPromptParts appends non-text parts — images, documents, audio, video — after the prompt text of this run's user message. Parts must be well-formed (see model.Part.Validate); a malformed part, or parts on a history message other than a user message, fails the run up front. Which kinds and media types an adapter accepts is its own contract: unsupported combinations fail before any request.

func WithRunID added in v0.7.6

func WithRunID(id string) RunOption

WithRunID supplies this run's identifier; the default mints one with golem.NewID. An empty value mints too — pass an identifier only to align a run's identity with one your infrastructure already has, such as a trace or request ID. The identifier rides the run's events, its Result, and every message the run adds to the conversation.

func WithRunObserver added in v0.7.1

func WithRunObserver(onEvent func(RunEvent)) RunOption

WithRunObserver registers a run-scoped event observer for a single run: the same events WithRunEvents delivers, under the same contract, but bound to one run instead of the agent. It is how a shared agent — a server handling many requests — routes each request's events separately without rebuilding the agent. A run's observer composes with the agent's: the construction-scoped observer fires first, then the run's. Accepted by Run and its history, streaming, and deferred-resume variants; a nil observer observes nothing.

type SanitizeReport added in v0.8.0

type SanitizeReport struct {
	// SystemPrompts counts the system messages the pass dropped. Runs
	// never send history system prompts — instructions govern every run
	// — so the count reports an attempt, not a model-visible change.
	SystemPrompts int
	// UnsafeParts lists, in history order, the URL parts dropped for a
	// scheme other than http or https.
	UnsafeParts []UnsafePart
	// Repair is the pairing repair the pass applied — the same report
	// NormalizeHistory produces — so a fabricated dangling call is named
	// here rather than surfacing as a resume-time surprise.
	Repair HistoryRepair
}

SanitizeReport records what one SanitizeHistory pass changed, so an application endpoint can log, reject, or otherwise act on what an untrusted client tried to assert instead of discovering it from a provider rejection or a fabricated approval.

func SanitizeHistory added in v0.8.0

func SanitizeHistory(history []model.Message) ([]model.Message, SanitizeReport)

SanitizeHistory makes an untrusted history safe to run: it drops system messages, drops URL parts whose scheme is not http or https (case-insensitive; unparsable and scheme-relative URLs included), repairs call/result pairing, and reports every change. Use it at the trust boundary — a browser request resuming a conversation, another service's transcript, a client-submitted paused run — before handing the history to Run, RunWithHistory, or RunWithDeferredResults.

The pass never rewrites content: thinking blocks, failure flags, call arguments, and identity stamps pass through untouched, and inline-data parts are left to the existing part validation. A user message left with neither content nor parts — by the drops or as submitted — is removed; tool results are never removed, because their pairing evidence must survive. The pass is deterministic and idempotent: sanitizing a sanitized history returns it unchanged with a zero report.

Sanitization narrows what a fabricated history can reach; it does not make one trustworthy. Authenticate at the transport, scope the toolset to the caller, and re-validate high-stakes effects against server-side state inside the tool — see the conversations guide's trust rules. Runs never sanitize automatically: the boundary is the application's, and trusted server-side history has nothing to strip.

type Stage

type Stage string

Stage identifies the run phase that returned an error.

const (
	// StageModel means the model could not generate a response.
	StageModel Stage = "model"
	// StageDecode means a generated response could not become the declared type.
	StageDecode Stage = "decode"
	// StageTool means a tool execution failed; the run aborted.
	StageTool Stage = "tool"
	// StageLoop means the run exceeded its model-turn limit before producing
	// a final response.
	StageLoop Stage = "loop"
	// StageUsage means the run crossed a configured usage bound.
	StageUsage Stage = "usage"
	// StageCanceled means a tool ended the run on purpose by returning
	// &tool.Canceled — a deliberate stop, not a failure. The sentinel is
	// reachable through RunError.Unwrap with errors.As, and RunError.Partial
	// preserves the evidence, including the tool results recorded before
	// the stop.
	StageCanceled Stage = "canceled"
)

type UnsafePart added in v0.8.0

type UnsafePart struct {
	// MessageIndex is the part's message in the history exactly as it
	// was passed to SanitizeHistory.
	MessageIndex int
	// Kind is the dropped part's kind.
	Kind model.PartKind
	// Scheme is the rejected URL scheme, lowercased; empty when the URL
	// had no parsable scheme.
	Scheme string
}

UnsafePart is one dropped URL part: the threat is where the URL asks the provider to look, not what the part shows.

type UsageLimit

type UsageLimit struct {
	InputTokens  int
	OutputTokens int
	TotalTokens  int
	// PerRequestInputTokens bounds one model request's estimated input,
	// enforced before the request is sent. It requires WithTokenCounter
	// and counts what the wired counter counts — see the tokens package
	// for the per-provider support matrix.
	PerRequestInputTokens int
	// Cost bounds a run's cumulative priced cost in US dollars, computed
	// at the WithPrice rates after each model response. It requires
	// WithPrice; the estimate is only as good as the supplied rates and
	// is not a billing guarantee.
	Cost float64
	// Requests bounds model calls, retried attempts included.
	Requests int
	// ToolCalls bounds tool executions.
	ToolCalls int
}

UsageLimit bounds a run's provider-recorded token consumption and its model-request and tool-execution activity. The zero value disables the limit; each dimension is independent, and zero within a set limit means that dimension is unbounded. Providers that do not report usage count as zero tokens, so a token limit never trips without provider-reported usage; requests and tool executions are counted by the run itself.

type UsageLimitError

type UsageLimitError struct {
	// Kind names the crossed dimension, e.g. "output token".
	Kind string
	// Limit is the configured bound. Token and activity limits are whole
	// numbers; Cost is a USD amount.
	Limit float64
	// Actual is the run's cumulative value when the run failed.
	Actual float64
}

UsageLimitError reports that a run's cumulative usage crossed one of its configured bounds. It is wrapped in a RunError with the usage stage.

func (*UsageLimitError) Error

func (e *UsageLimitError) Error() string

Directories

Path Synopsis
Package docextract provides a high-performance, multi-format document extraction tool and library for Golem.
Package docextract provides a high-performance, multi-format document extraction tool and library for Golem.
Package embedding defines the provider-neutral contract for text embedding services: turning text into dense vectors for semantic search, clustering, and retrieval-augmented generation.
Package embedding defines the provider-neutral contract for text embedding services: turning text into dense vectors for semantic search, clustering, and retrieval-augmented generation.
examples
anthropic command
Command anthropic runs a minimal agent against the Anthropic Messages API: explicit configuration, including the MaxTokens bound the API requires.
Command anthropic runs a minimal agent against the Anthropic Messages API: explicit configuration, including the MaxTokens bound the API requires.
azure command
Command azure runs a minimal agent against Azure OpenAI: the wire format matches OpenAI chat completions, but requests target a named deployment with an explicit API version and the api-key header.
Command azure runs a minimal agent against Azure OpenAI: the wire format matches OpenAI chat completions, but requests target a named deployment with an explicit API version and the api-key header.
bedrock command
Command bedrock runs a minimal agent against the AWS Bedrock Runtime Converse API, with requests signed using AWS Signature Version 4.
Command bedrock runs a minimal agent against the AWS Bedrock Runtime Converse API, with requests signed using AWS Signature Version 4.
command-execution command
Command command-execution shows the shell common tool in action: a scripted fake model asks to run one local command — no network, no credentials, fully deterministic.
Command command-execution shows the shell common tool in action: a scripted fake model asks to run one local command — no network, no credentials, fully deterministic.
conversation command
Command conversation chains runs into a multi-turn chat: each result's messages become the next run's history, and instructions are re-applied per run.
Command conversation chains runs into a multi-turn chat: each result's messages become the next run's history, and instructions are re-applied per run.
cost command
Command cost prices a run's token usage: golem.WithPrice wires a model.Price so Result.Cost reports the cumulative spend and UsageLimit.Cost bounds it, failing the run at the usage stage when the priced total crosses the bound.
Command cost prices a run's token usage: golem.WithPrice wires a model.Price so Result.Cost reports the cumulative spend and UsageLimit.Cost bounds it, failing the run at the usage stage when the priced total crosses the bound.
deferred-tools command
Command deferred-tools runs the full deferred-tool cycle offline against a scripted model: no network, no credentials, deterministic.
Command deferred-tools runs the full deferred-tool cycle offline against a scripted model: no network, no credentials, deterministic.
delegation command
Command delegation runs a planner agent whose only tool is another agent: the model delegates a claim to the fact-checking specialist, Golem runs it as a sub-agent with the shared dependency value, and the planner answers from the rendered result.
Command delegation runs a planner agent whose only tool is another agent: the model delegates a claim to the fact-checking specialist, Golem runs it as a sub-agent with the shared dependency value, and the planner answers from the rendered result.
doc-extract command
Command doc-extract demonstrates the docextract common tool in action: Word documents (.docx), Excel spreadsheets (.xlsx), PowerPoint decks (.pptx), Markdown (.md), CSVs, and PDFs are extracted into clean, agent-readable Markdown.
Command doc-extract demonstrates the docextract common tool in action: Word documents (.docx), Excel spreadsheets (.xlsx), PowerPoint decks (.pptx), Markdown (.md), CSVs, and PDFs are extracted into clean, agent-readable Markdown.
embeddings command
Command embeddings runs a tiny semantic search: three documents are embedded in one call, a query is embedded on the query side of the split, and cosine similarity ranks the documents — the retrieval half of a RAG pipeline, with the vector store left as an exercise.
Command embeddings runs a tiny semantic search: three documents are embedded in one call, a query is embedded on the query side of the split, and cosine similarity ranks the documents — the retrieval half of a RAG pipeline, with the vector store left as an exercise.
fallback command
Command fallback runs a prompt against a primary model with a backup model behind it: when the primary fails with a retryable error — rate limits, 5xx, transport faults — the run continues on the backup instead of failing.
Command fallback runs a prompt against a primary model with a backup model behind it: when the primary fails with a retryable error — rate limits, 5xx, transport faults — the run continues on the backup instead of failing.
file-read command
Command file-read shows the fileread common tool in action: a local temp directory stands in for a workspace and a scripted fake model requests the read — no network, no credentials, fully deterministic.
Command file-read shows the fileread common tool in action: a local temp directory stands in for a workspace and a scripted fake model requests the read — no network, no credentials, fully deterministic.
gemini command
Command gemini runs a minimal agent against the Google Gemini GenerateContent API.
Command gemini runs a minimal agent against the Google Gemini GenerateContent API.
history-repair command
Command history-repair normalizes a damaged conversation the way an application boundary would: a crashed run left a tool call without a result, a pipeline dropped its call, and a stream died mid-arguments.
Command history-repair normalizes a damaged conversation the way an application boundary would: a crashed run left a tool call without a result, a pipeline dropped its call, and a stream died mid-arguments.
history-sanitization command
Command history-sanitization shows the trust boundary every history endpoint needs: a client resubmits a conversation that carries an injected system prompt and a file-scheme image URL alongside its real turns, golem.SanitizeHistory strips both and repairs pairing while the report names every attempt — and the sanitized history runs normally.
Command history-sanitization shows the trust boundary every history endpoint needs: a client resubmits a conversation that carries an injected system prompt and a file-scheme image URL alongside its real turns, golem.SanitizeHistory strips both and repairs pairing while the report names every attempt — and the sanitized history runs normally.
local-models command
Command local-models runs an agent against a local OpenAI-compatible runtime — Ollama or LM Studio — through the standard openai adapter, with one typed tool.
Command local-models runs an agent against a local OpenAI-compatible runtime — Ollama or LM Studio — through the standard openai adapter, with one typed tool.
mcp-client command
Command mcp-client shows the mcp package bridging a Model Context Protocol server into agent tools.
Command mcp-client shows the mcp package bridging a Model Context Protocol server into agent tools.
mcp-http command
Command mcp-http shows the mcp package over the streamable-HTTP transport: a local HTTP server stands in for a remote MCP endpoint — no external network, no credentials, fully deterministic.
Command mcp-http shows the mcp package over the streamable-HTTP transport: a local HTTP server stands in for a remote MCP endpoint — no external network, no credentials, fully deterministic.
minimal command
Command minimal runs the smallest agent: an OpenAI-compatible model, a decoder that takes the response text, and one run.
Command minimal runs the smallest agent: an OpenAI-compatible model, a decoder that takes the response text, and one run.
multimodal-input command
Command multimodal-input attaches an inline image and an inline document to a run's prompt and asks the model to handle both.
Command multimodal-input attaches an inline image and an inline document to a run's prompt and asks the model to handle both.
partial-evidence command
Command partial-evidence shows a failed run keeping its evidence: a model failure after a completed tool turn carries RunError.Partial — the conversation so far, the usage completed turns reported, and the activity counts — and the partial messages resume through RunWithHistory.
Command partial-evidence shows a failed run keeping its evidence: a model failure after a completed tool turn carries RunError.Partial — the conversation so far, the usage completed turns reported, and the activity counts — and the partial messages resume through RunWithHistory.
pdf-extract command
Command pdf-extract demonstrates the pdfextract common tool in action: a generated multi-column PDF with an intact table and an image placeholder is extracted by a scripted agent — fully offline, deterministic, and fast.
Command pdf-extract demonstrates the pdfextract common tool in action: a generated multi-column PDF with an intact table and an image placeholder is extracted by a scripted agent — fully offline, deterministic, and fast.
run-cancellation command
Command run-cancellation shows a tool ending the run deliberately: a guard tool returns &tool.Canceled when the request crosses the budget it polices, the run stops at the canceled stage with the evidence — the executed calls before the stop, the closings after it — on RunError.Partial, and the transcript resumes through RunWithHistory.
Command run-cancellation shows a tool ending the run deliberately: a guard tool returns &tool.Canceled when the request crosses the budget it polices, the run stops at the canceled stage with the evidence — the executed calls before the stop, the closings after it — on RunError.Partial, and the transcript resumes through RunWithHistory.
run-events command
Command run-events observes an executing run through WithRunEvents: every provider call attempt and tool execution reported as it happens.
Command run-events observes an executing run through WithRunEvents: every provider call attempt and tool execution reported as it happens.
run-ids command
Command run-ids shows a conversation's identity: every run mints a fresh run ID, but runs chained through RunWithHistory share one conversation ID — inherited from the history's most recent identified message, so the association survives a storage round-trip with no session object.
Command run-ids shows a conversation's identity: every run mints a fresh run ID, but runs chained through RunWithHistory share one conversation ID — inherited from the history's most recent identified message, so the association survives a storage round-trip with no session object.
self-correction command
Command self-correction shows a tool that rejects correctable arguments: the die roll requires a positive count, and when the model gets it wrong the run feeds the rejection back so the model calls again within the configured budget.
Command self-correction shows a tool that rejects correctable arguments: the die roll requires a positive count, and when the model gets it wrong the run feeds the rejection back so the model calls again within the configured budget.
skills command
Command skills shows the skills common tool in action: a temp directory laid out in the standard .agents/skills shape stands in for a skill pack, and a scripted fake model loads one skill — no network, no credentials, fully deterministic.
Command skills shows the skills common tool in action: a temp directory laid out in the standard .agents/skills shape stands in for a skill pack, and a scripted fake model loads one skill — no network, no credentials, fully deterministic.
streaming command
Command streaming prints a response as it arrives: RunStream forwards every model fragment across tool turns and correction rounds while producing the same Result as Run.
Command streaming prints a response as it arrives: RunStream forwards every model fragment across tool turns and correction rounds while producing the same Result as Run.
structured-output command
Command structured-output extracts a typed value: the agent declares a JSON Schema the adapter sends as structured-output instructions, and DecodeJSON turns the response content into the declared type.
Command structured-output extracts a typed value: the agent declares a JSON Schema the adapter sends as structured-output instructions, and DecodeJSON turns the response content into the declared type.
structured-output-tool command
Command structured-output-tool extracts a typed value through tool-mode structured output: the schema becomes the parameters of a synthesized output tool, and the run ends on the model's first call to it.
Command structured-output-tool extracts a typed value through tool-mode structured output: the schema becomes the parameters of a synthesized output tool, and the run ends on the model's first call to it.
testing-without-a-provider command
Command testing-without-a-provider runs an agent against a scripted fake model: no network, no credentials, fully deterministic.
Command testing-without-a-provider runs an agent against a scripted fake model: no network, no credentials, fully deterministic.
thinking command
Command thinking runs an agent with adaptive thinking enabled and shows where the model's reasoning lands in the run result.
Command thinking runs an agent with adaptive thinking enabled and shows where the model's reasoning lands in the run result.
token-counting command
Command token-counting prices a conversation before sending it: a growing history is bounded by token budget with golem.BudgetHistory, and a run enforces a per-request input ceiling with UsageLimit.PerRequestInputTokens — both over the tokens.Counter port.
Command token-counting prices a conversation before sending it: a growing history is bounded by token budget with golem.BudgetHistory, and a run enforces a per-request input ceiling with UsageLimit.PerRequestInputTokens — both over the tokens.Counter port.
tool-results command
Command tool-results shows tools returning more than text: one tool hands back an image as evidence beside its result, and a definitive failure reaches the model as the tool's result — without consuming the tool's retry budget.
Command tool-results shows tools returning more than text: one tool hands back an image as evidence beside its result, and a definitive failure reaches the model as the tool's result — without consuming the tool's retry budget.
tools command
Command tools runs an agent whose tool receives a typed dependency value: the model requests the lookup, Golem executes it with the run's Deps, and the model answers from the result.
Command tools runs an agent whose tool receives a typed dependency value: the model requests the lookup, Golem executes it with the run's Deps, and the model answers from the result.
web-fetch command
Command web-fetch shows the webfetch common tool in action: a local test server stands in for the web and a scripted fake model requests the fetch — no network, no credentials, fully deterministic.
Command web-fetch shows the webfetch common tool in action: a local test server stands in for the web and a scripted fake model requests the fetch — no network, no credentials, fully deterministic.
Package fileread provides a common tool that reads a file inside a configured root directory and returns its text for the model.
Package fileread provides a common tool that reads a file inside a configured root directory and returns its text for the model.
internal
runner
Package runner orchestrates the sequential model/tool execution loop.
Package runner orchestrates the sequential model/tool execution loop.
Package mcp connects Golem agents to Model Context Protocol servers: it speaks the JSON-RPC 2.0 protocol over a transport, performs the initialize handshake, discovers a server's tools, and bridges them into tool.Tool declarations any agent can register.
Package mcp connects Golem agents to Model Context Protocol servers: it speaks the JSON-RPC 2.0 protocol over a transport, performs the initialize handshake, discovers a server's tools, and bridges them into tool.Tool declarations any agent can register.
Package model defines the provider-neutral contract used by Golem agents.
Package model defines the provider-neutral contract used by Golem agents.
Package pdfextract provides a high-performance, layout-aware PDF extraction tool and library for Golem.
Package pdfextract provides a high-performance, layout-aware PDF extraction tool and library for Golem.
Package providers holds Golem's provider adapters and small shared helpers for configuring them.
Package providers holds Golem's provider adapters and small shared helpers for configuring them.
anthropic
Package anthropic adapts the Anthropic Messages API to Golem's provider-neutral model contract.
Package anthropic adapts the Anthropic Messages API to Golem's provider-neutral model contract.
azure
Package azure adapts Azure OpenAI chat completions to Golem's provider-neutral model contract.
Package azure adapts Azure OpenAI chat completions to Golem's provider-neutral model contract.
bedrock
Package bedrock adapts the AWS Bedrock Runtime Converse API to Golem's provider-neutral model contract.
Package bedrock adapts the AWS Bedrock Runtime Converse API to Golem's provider-neutral model contract.
gemini
Package gemini adapts the Google Gemini GenerateContent API to Golem's provider-neutral model contract.
Package gemini adapts the Google Gemini GenerateContent API to Golem's provider-neutral model contract.
openai
Package openai adapts OpenAI-compatible chat-completions APIs to Golem's provider-neutral model contract.
Package openai adapts OpenAI-compatible chat-completions APIs to Golem's provider-neutral model contract.
Package shell provides a common tool that runs one shell command and returns its combined output for the model.
Package shell provides a common tool that runs one shell command and returns its combined output for the model.
Package skills provides a common tool that loads Agent Skills from standard skill directories and returns a chosen skill's instructions to the model.
Package skills provides a common tool that loads Agent Skills from standard skill directories and returns a chosen skill's instructions to the model.
Package testmodel provides model implementations for testing agents without provider credentials or network access.
Package testmodel provides model implementations for testing agents without provider credentials or network access.
Package tokens defines the provider-neutral contract for input-token counting: asking a provider how many tokens a request would consume before sending it.
Package tokens defines the provider-neutral contract for input-token counting: asking a provider how many tokens a request would consume before sending it.
Package tool defines Golem's typed tool declaration and execution contract.
Package tool defines Golem's typed tool declaration and execution contract.
Package webfetch provides Golem's first common tool: fetch an http or https URL with GET and return the response body as text a model can read.
Package webfetch provides Golem's first common tool: fetch an http or https URL with GET and return the response body as text a model can read.

Jump to

Keyboard shortcuts

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