Documentation
¶
Overview ¶
Package llm holds LLM wire types and the Tool contract used across core services and agent execution. Keep this package at the bottom of the dependency graph to avoid import cycles.
Index ¶
- Constants
- func FormatAmount(nano int64) string
- func KnownProvider(name string) bool
- func ParseRate(s string) (int64, error)
- func ProviderNeedsCredential(name string) bool
- func Providers() []string
- func WithCallOrigin(ctx context.Context, origin CallOrigin) context.Context
- type Access
- type AccessDeclarer
- type ArgChecker
- type CallOrigin
- type CallProfile
- type Completion
- type ContentPart
- type Cost
- type GrantScoper
- type LLMClient
- type Message
- type MultimodalTool
- type PolicyProvider
- type Pricing
- type ProviderState
- type Request
- type StreamSink
- type TitleGenerator
- type Tool
- type ToolAction
- type ToolCall
- type ToolDef
- type ToolRegistry
- type ToolResult
- type Usage
Constants ¶
const ( ContentPartText = "text" ContentPartImage = "image" )
Content part kinds.
const ( MessageSourceCommandResult = "command_result" MessageSourceSubagentResult = "subagent_result" MessageSourceMonitorEvent = "monitor_event" )
Message sources for background-event provenance. See Message.Source; the set mirrors docs/design/local-background-jobs.md.
const ( // ProviderOpenAICompatible is OpenAI Chat Completions, spoken by OpenRouter, // LiteLLM, vLLM, and local inference servers. It is the default. ProviderOpenAICompatible = "openai_compatible" // ProviderOpenAI is OpenAI's own Responses API. ProviderOpenAI = "openai" // ProviderAnthropic is the Anthropic Messages API. ProviderAnthropic = "anthropic" // ProviderOllama is Ollama's own /api/chat, spoken by a local daemon. Its // compatibility endpoint would answer ProviderOpenAICompatible, but that // path cannot set the context window the runtime otherwise defaults and // silently truncates to. ProviderOllama = "ollama" )
Provider types name the wire protocol an upstream speaks.
A provider type selects a client implementation; it is not a vendor name. Claude reached through an OpenAI-compatible gateway is ProviderOpenAICompatible, and Claude reached at Anthropic's own endpoint is ProviderAnthropic.
This is the one definition. A configured model entry, a catalog target, and a recorded call all name a protocol, and an operator must read the same word in settings.yaml, in the catalog, and in the call ledger. It lives here because a protocol name is a contract, not something config loads or the gateway resolves.
const NanoUnitsPerUnit = 1_000_000_000
NanoUnitsPerUnit is the fixed-point scale rates and costs are held at: one currency unit is 1e9 of them.
Money is not held in a float here. A rate like $0.30 per million tokens has no exact binary representation, and a run of a few hundred calls accumulates the error into a figure someone will compare against an invoice. Integer nano-units multiply and add exactly, and nine decimal places is finer than any provider prices to.
Variables ¶
This section is empty.
Functions ¶
func FormatAmount ¶
FormatAmount renders nano-units as a decimal string with six places, which is enough to show a single cheap call without reading as zero.
func KnownProvider ¶
KnownProvider reports whether name is a provider type BuildMax implements. An empty name is not known: a caller that has not stated a protocol has not finished describing its upstream. Defaulting an unset one is the configuration boundary's job, not this package's.
func ParseRate ¶
ParseRate reads a decimal price string — "3", "0.30", "3.75" — into nano-units.
Rates are written as decimal strings rather than numbers because that is how every provider publishes them, so a configured value can be compared against a price page without arithmetic, and because a JSON or YAML float would round the value before this package ever saw it.
func ProviderNeedsCredential ¶
ProviderNeedsCredential reports whether an upstream speaking this protocol must carry a secret.
Every hosted protocol does, and one without a credential is a misconfiguration that must fail at the first call rather than send an unauthenticated request. A local runtime has none: what authorizes the call is being able to reach the daemon at all, which is a property of the deployment's network. Demanding a placeholder for it would turn a working setup into a diagnostic failure.
func Providers ¶
func Providers() []string
Providers returns every implemented wire protocol, for help text and error messages that must not drift from the list above.
func WithCallOrigin ¶
func WithCallOrigin(ctx context.Context, origin CallOrigin) context.Context
WithCallOrigin attaches runtime-owned origin metadata to one model call.
Types ¶
type Access ¶
type Access uint8
Access describes what a tool call does to the world. The zero value is AccessWrite so an undeclared tool is treated conservatively. Read by the permission layer and the tool scheduler; see docs/design/tool-permissions.md.
type AccessDeclarer ¶
AccessDeclarer is an optional interface a Tool can implement to classify its own calls. Args are passed so the answer can depend on the call.
AccessReadOnly is a claim about effect only. It does not promise that Execute is safe on several goroutines at once — CallMcpTool reports what a third party says about itself, which is not something this runtime can underwrite. A scheduler must require concurrency safety separately.
type ArgChecker ¶
type ArgChecker interface {
CheckArgs(args map[string]any) ToolAction
}
ArgChecker is an optional interface a Tool can implement to make arg-level policy decisions before Execute is called. Return ToolActionAllow to proceed normally.
type CallOrigin ¶
type CallOrigin struct {
// Surface is where the call began, such as cli, desktop, server, or worker.
Surface string
// ViaGateway says a BuildMax gateway forwarded the call to its provider.
ViaGateway bool
}
CallOrigin is runtime-owned correlation metadata for one model call. It is not sent as prompt content and never carries user, workspace, or session identifiers.
func CallOriginFromContext ¶
func CallOriginFromContext(ctx context.Context) (CallOrigin, bool)
CallOriginFromContext returns the origin metadata for one model call.
type CallProfile ¶
type CallProfile string
CallProfile is what a call is for. It is the caller's statement of intent, not a provider setting: a title and a tool-calling turn send the same shape of request and have nothing in common in how they will be reused.
It exists because prompt caching is charged. A cache write costs more than ordinary input and only repays itself if a later call reads it, so whether to ask for one cannot be decided from the request alone — a one-shot utility call and the first turn of a long run look identical on the wire. Carrying the answer in an untyped context value, or guessing it from prompt text, would hide a billed behavior from the callers and tests that have to reason about it.
const ( // ProfileAgentTurn is one iteration of the agent loop: a large stable // prefix that the next iteration will send again. ProfileAgentTurn CallProfile = "agent_turn" // ProfileTitle is one-shot title generation. ProfileTitle CallProfile = "title" // ProfileCompaction is summarizing history the run is about to discard. ProfileCompaction CallProfile = "compaction" // ProfileEvaluation is a harness call made about a run rather than by one. ProfileEvaluation CallProfile = "evaluation" // ProfileProbe is a single question with no expectation of reuse: a // connectivity check, a tool's own model call. ProfileProbe CallProfile = "probe" )
func (CallProfile) Valid ¶
func (p CallProfile) Valid() bool
Valid reports whether p is a profile this build knows. An unknown profile is refused rather than defaulted, because the default it would fall to is the one that spends money.
type Completion ¶
type Completion struct {
Content string
ToolCalls []ToolCall
Usage Usage
// ProviderState is set only by a protocol that carries reasoning state and
// only when the model produced some.
ProviderState *ProviderState
}
Completion is one model turn: what the assistant said, what it asked to run, what it cost, and any reasoning state the protocol needs back.
It is a struct rather than a longer return list because every capability the contract has gained wanted another slot, and a fifth positional value is where that stops being readable.
func (Completion) AssistantMessage ¶
func (c Completion) AssistantMessage() Message
AssistantMessage is the history entry this completion becomes. The agent loop appends it verbatim, so reasoning state reaches the next request without any layer between here and there having to know it exists.
type ContentPart ¶
type ContentPart struct {
Type string `json:"type"`
// Text is set on a text part.
Text string `json:"text,omitempty"`
// MediaType and Data are set on an image part. Data is base64 with no
// data: prefix, which is the form every protocol here wants.
MediaType string `json:"media_type,omitempty"`
Data string `json:"data,omitempty"`
}
ContentPart is one piece of a message's content.
Parts sit beside Content rather than replacing it. A message with an image still carries text saying what the image is, so token estimation, trimming, compaction, traces, and the terminal renderer keep working unchanged and a protocol that cannot take images still receives a sensible turn.
type Cost ¶
type Cost struct {
Currency string `json:"currency"`
// Uncached is fresh prompt input — the prompt minus what was cached.
Uncached int64 `json:"uncached"`
// CacheRead and CacheWrite are the cached parts of the prompt.
CacheRead int64 `json:"cache_read"`
CacheWrite int64 `json:"cache_write"`
// Output is the generated tokens.
Output int64 `json:"output"`
// Total is the sum of the four above.
Total int64 `json:"total"`
// Baseline is what the same call would have cost with no caching at all:
// every prompt token billed at the fresh input rate. It is the only honest
// way to say whether caching helped, because the alternative — comparing
// against zero — would report a saving on a call that only ever wrote.
Baseline int64 `json:"baseline"`
}
Cost is what one call, or a run of them, is estimated to have cost.
Every field is nano-units of Currency. The breakdown is kept rather than summed away because the parts answer different questions: whether caching paid for itself is Uncached + CacheRead + CacheWrite against Baseline, and what the call cost is Total.
func EstimateCost ¶
EstimateCost prices one usage report, or (Cost{}, false) when it cannot.
It refuses rather than guesses in two cases. Unconfigured rates give no basis for a number at all. Usage a provider never reported gives nothing to multiply: a call with no counts is an unmeasured call, not a free one, and returning a zero cost for it would turn an unknown into a claim.
func (Cost) Add ¶
Add accumulates another call's cost.
Two costs in different currencies are not added, because BuildMax holds no exchange rate and inventing one would produce a total that is wrong in both. The mismatch is reported so a caller can say "unavailable" rather than show a figure that silently dropped half the run.
func (Cost) Saved ¶
Saved is Baseline minus Total, and zero when caching cost more than it saved.
A negative saving is not reported as one. A run that wrote a cache entry nothing went on to read genuinely paid more than it would have without caching, and dressing that up as a small saving is the kind of claim this package exists not to make. Callers that want the loss compare the two fields themselves.
type GrantScoper ¶
GrantScoper is an optional interface a Tool can implement to narrow what a session grant covers. A tool that dispatches to something else — an MCP server, another agent — would otherwise have one approval cover every target it can reach.
type LLMClient ¶
type LLMClient interface {
ChatCompletionBlocking(ctx context.Context, req Request) (Completion, error)
ChatCompletionStreaming(ctx context.Context, req Request, onDelta func(string)) (Completion, error)
ContextWindow() int // 0 = no windowing configured
}
LLMClient can perform chat completions with tools and exposes its configuration.
type Message ¶
type Message struct {
Role string `json:"role"` // "user", "assistant", "system", or "tool"
Content string `json:"content,omitempty"` // message content
ToolCallID string `json:"tool_call_id,omitempty"` // for role "tool": the ID of the tool call this result answers
ToolCalls []ToolCall `json:"tool_calls,omitempty"` // for role "assistant": tool calls made by the model
// Source records non-user provenance for a user-role message. Model
// providers share no portable mid-history event role, so a background
// event travels on the wire as a user message — but the persisted history
// and the trace must not claim the user said it. Empty means genuinely
// user-authored. Wire adapters map fields explicitly and never send it.
Source string `json:"source,omitempty"`
// ProviderState is opaque reasoning state the producing protocol requires
// back on later turns. For role "assistant" only. See ProviderState.
ProviderState *ProviderState `json:"provider_state,omitempty"`
// Parts is non-text content this message carries. Content stays the text
// projection of the same message, so nothing that reads it has to know
// parts exist. See ContentPart.
Parts []ContentPart `json:"parts,omitempty"`
}
Message represents a chat message for the API (user, assistant, or tool).
func (Message) Images ¶
func (m Message) Images() []ContentPart
Images returns the image parts of a message, or nil when it has none.
type MultimodalTool ¶
type MultimodalTool interface {
Tool
ExecuteMultimodal(ctx context.Context, args map[string]any) (ToolResult, error)
}
MultimodalTool is a Tool whose result can carry non-text content.
It is an optional upgrade rather than a change to Execute because exactly one tool needs it — the MCP gateway, which forwards whatever a server returns — and widening the contract would make sixteen text-only tools carry a field they never set. The agent loop asks for it and falls back to Execute.
type PolicyProvider ¶
type PolicyProvider interface {
DefaultAction() ToolAction
}
PolicyProvider is an optional interface a Tool can implement to declare its default action. The agent uses this as a fallback when no arg-level check applies.
type Pricing ¶
type Pricing struct {
// Currency is the ISO 4217 code the rates are quoted in. Rates in different
// currencies are never added: BuildMax does not convert.
Currency string
// InputPerMTok is fresh prompt input, excluding anything cached.
InputPerMTok int64
// CacheReadPerMTok is prompt served from the provider's cache.
CacheReadPerMTok int64
// CacheWritePerMTok is prompt written into it.
CacheWritePerMTok int64
// OutputPerMTok is generated tokens.
OutputPerMTok int64
}
Pricing is what one model charges, as nano-currency-units per million tokens.
The four rates are separate because prompt caching prices them differently: a cache read is cheaper than fresh input and a cache write is dearer, which is the whole reason caching is a decision rather than a free win. Collapsing them into one input rate would make every cached call look mispriced.
A zero rate is a real price — some models genuinely do not charge for cache reads — so "not configured" is the zero Pricing as a whole, reported by Configured, rather than a zero in any single field.
func (Pricing) Configured ¶
Configured reports whether these rates can price a call. A currency alone is not enough, and neither is a rate without one: a cost shown from half a price list is a guess wearing a number.
type ProviderState ¶
type ProviderState struct {
Protocol string `json:"protocol"`
Data json.RawMessage `json:"data"`
}
ProviderState is provider-owned content that a protocol produces and then requires unchanged on subsequent requests: Anthropic thinking blocks, OpenAI Responses reasoning items. Nothing outside the adapter that produced it may interpret it, and nothing rewrites it — a signature over edited content is worse than no state at all.
Protocol names the producer so a session continued under a different one drops what it cannot use, rather than sending a payload that protocol would reject. That is what lets history stay portable while carrying state that is not.
func (*ProviderState) Belongs ¶
func (p *ProviderState) Belongs(protocol string) bool
Belongs reports whether this state was produced by the named protocol. A nil state belongs to none, so callers can test the result directly.
type Request ¶
type Request struct {
Messages []Message
Tools []ToolDef
Profile CallProfile
// CacheScope is an opaque discriminator that keeps unrelated prompt
// populations out of one another's provider cache bucket. Empty means the
// call is not scoped beyond the credential it uses.
//
// It exists because a provider cache key is a routing hint, not an
// authorization boundary: two callers sharing one credential share a bucket
// unless something separates them. For managed inference the gateway sets
// it from the authenticated team, so one team's prefix cannot be bucketed
// with another's. It is never accepted from a client, never persisted, and
// never logged — it is an input to a hash and nothing else.
CacheScope string
}
Request is one completion request.
Profile travels with the messages rather than beside them so a caller cannot forget it at one call site and get a different charge than at another.
type StreamSink ¶
type StreamSink interface {
OnDelta(delta string)
}
StreamSink receives content deltas during streaming. Implementations may write to stdout, send to a TUI, or buffer for SSE.
type TitleGenerator ¶
type TitleGenerator interface {
GenerateTitle(ctx context.Context, input string) (title string, promptTokens, completionTokens int, err error)
}
TitleGenerator generates a short title from an input string, e.g. via LLM. Returns token usage for metering; on error or when nil, callers fall back to truncated input.
type Tool ¶
type Tool interface {
Name() string
Description() string
Parameters() any // JSON schema for arguments (e.g. map[string]any)
Execute(ctx context.Context, args map[string]any) (result string, err error)
}
Tool is a capability the agent can invoke by name. Both the core agent loop (internal/core/agent) and runtime tools (internal/tool) use this interface.
type ToolAction ¶
type ToolAction int
ToolAction is the outcome of a policy check before a tool call executes.
const ( // ToolActionAllow executes the tool unconditionally. ToolActionAllow ToolAction = iota // ToolActionAsk requests interactive approval before execution. ToolActionAsk // ToolActionDeny blocks execution and returns an error result to the model. ToolActionDeny )
type ToolCall ¶
type ToolCall struct {
ID string `json:"id"` // unique id for this call
Name string `json:"name"` // tool name to invoke
Arguments string `json:"arguments,omitempty"` // JSON object of arguments
}
ToolCall is a tool invocation returned by the model.
type ToolDef ¶
type ToolDef struct {
Name string // tool name
Description string // description for the model
Parameters any // JSON schema for arguments (e.g. map[string]any or jsonschema.Definition)
}
ToolDef describes a tool (function) the model can call.
type ToolRegistry ¶
type ToolRegistry struct {
// contains filtered or unexported fields
}
ToolRegistry keeps the tools available to an agent run.
func NewToolRegistry ¶
func NewToolRegistry() ToolRegistry
NewToolRegistry builds an empty registry for the tools available to an agent run.
func (*ToolRegistry) AppendTools ¶
func (r *ToolRegistry) AppendTools(tools ...Tool)
AppendTools adds tools to the registry.
func (ToolRegistry) GetDefs ¶
func (r ToolRegistry) GetDefs() []ToolDef
GetDefs builds the LLM-facing tool definitions.
func (ToolRegistry) Lookup ¶
func (r ToolRegistry) Lookup(name string) Tool
Lookup returns the executable tool matching name, or nil if none match.
func (ToolRegistry) Tools ¶
func (r ToolRegistry) Tools() []Tool
Tools returns the registered tools in append order.
type ToolResult ¶
type ToolResult struct {
Text string
Parts []ContentPart
}
ToolResult is what a tool produces when text alone cannot say it.
Text is always set and is what hooks, traces, the terminal, and token estimation read; Parts carries whatever the text could only describe.
type Usage ¶
type Usage struct {
PromptTokens int `json:"prompt_tokens"`
CompletionTokens int `json:"completion_tokens"`
TotalTokens int `json:"total_tokens"`
// CacheReadTokens is the part of the prompt served from a provider's cache,
// and CacheWriteTokens the part written into it. Both are subsets of the
// prompt: adding them to PromptTokens would count the same tokens twice.
// Zero means the provider reported none, which is also what a provider
// without caching reports.
CacheReadTokens int `json:"cache_read_tokens,omitempty"`
CacheWriteTokens int `json:"cache_write_tokens,omitempty"`
}
Usage holds token counts from the API (same shape for non-stream and stream responses).