Documentation
¶
Index ¶
- Constants
- Variables
- func CallIDFromContext(ctx context.Context) (string, bool)
- func Register(r *resource.Registry) error
- func WithCallID(ctx context.Context, id string) context.Context
- func WithSession(ctx context.Context, s Session) context.Context
- type Assembly
- func (a *Assembly) Catalog() Catalog
- func (a *Assembly) Close() error
- func (a *Assembly) Dispatcher() Dispatcher
- func (a *Assembly) Execute(ctx context.Context, call message.ToolCall) message.ToolResult
- func (a *Assembly) ExecuteAll(ctx context.Context, calls []message.ToolCall) []message.ToolResult
- func (a *Assembly) NewSession() Session
- type AssemblyFactory
- type AssemblyOption
- type AssemblySettings
- type Budget
- type Catalog
- type ConflictPolicy
- type DiscoverOutcome
- type DiscoverResult
- type DiscoveryPolicy
- type Dispatch
- type Dispatcher
- type Executor
- type Exposure
- type LazyTool
- type Middleware
- type Option
- type Policy
- type Registrar
- type Registry
- type RegistryAttacher
- type RegistryDetacher
- type RegistryFactory
- type SearchHit
- type SearchTool
- type Session
- type Source
- type Tool
- type ToolMeta
- type ToolMetadata
Constants ¶
const AssemblyKind = "tool.Assembly"
AssemblyKind is the deployment resource kind of the standard tool assembly (registry + executor + optional dynamic injection).
const RegistryKind = "tool.Registry"
RegistryKind is the deployment resource kind of a tool registry.
const ToolName = "tool_search"
ToolName is the built-in tool_search tool name. The assembly registers it with ExposureAlways when dynamic injection is enabled, so the model always has a discovery path.
Variables ¶
var DefaultBudget = Budget{
MaxDefinitions: 32,
MaxBytes: 16 * 1024,
}
DefaultBudget is the built-in budget: 32 tools or 16 KiB of definitions per turn, whichever is hit first.
Functions ¶
func CallIDFromContext ¶ added in v0.1.30
CallIDFromContext returns the active tool call id carried by ctx. ok=false means the caller is outside a tool execution (or the id was empty); consumers should treat the zero value as "unknown" rather than an error.
func WithCallID ¶ added in v0.1.30
WithCallID returns a derived context carrying id as the active tool call id.
Types ¶
type Assembly ¶
type Assembly struct {
// contains filtered or unexported fields
}
Assembly is the standard tool resource value: the aggregated registry (read surface), the executor (execution surface), and — when dynamic injection is configured — the injection policy that session views are created from.
func NewAssembly ¶
func NewAssembly(sources []Source, opts ...AssemblyOption) (*Assembly, error)
NewAssembly builds an assembly from sources. With dynamic enabled, the built-in tool_search tool is appended as a source so it is part of the registry from construction (no post-build mutation).
func (*Assembly) Dispatcher ¶
func (a *Assembly) Dispatcher() Dispatcher
Dispatcher returns the executor. Assembly itself also implements Dispatcher by delegation, so callers can take either form.
func (*Assembly) ExecuteAll ¶
ExecuteAll implements Dispatcher.
func (*Assembly) NewSession ¶
NewSession creates one per-run / per-conversation injection view. Without dynamic injection it returns a static session showing every tool.
type AssemblyFactory ¶
type AssemblyFactory struct{}
AssemblyFactory builds the standard tool assembly resource without middleware: the executor runs a bare chain. Deployments that need middleware use the tool.Assembly/middleware impl.
func (AssemblyFactory) Spec ¶
func (AssemblyFactory) Spec() resource.Spec
Spec implements resource.Factory.
type AssemblyOption ¶
type AssemblyOption func(*assemblyOptions)
AssemblyOption configures NewAssembly.
func WithConflict ¶
func WithConflict(p ConflictPolicy) AssemblyOption
WithConflict sets the duplicate-name policy for the underlying registry.
func WithDynamic ¶
func WithDynamic(p Policy) AssemblyOption
WithDynamic enables dynamic tool injection with policy p. Zero fields fall back to DefaultPolicy; tool_search is registered with ExposureAlways automatically.
func WithMiddleware ¶
func WithMiddleware(mws ...Middleware) AssemblyOption
WithMiddleware appends middleware to the executor chain, outermost first.
type AssemblySettings ¶
type AssemblySettings struct {
Dynamic *Policy `json:"dynamic,omitempty"`
}
AssemblySettings is the strict settings subtree of the memory assembly factory: the dynamic injection policy only. Middleware settings belong to the tool.Assembly/middleware impl (see core/tool/middleware); the memory impl rejects the "middlewares" key via strict decoding.
type Budget ¶
type Budget struct {
// MaxDefinitions caps the number of definitions. Zero falls back
// to DefaultBudget.MaxDefinitions.
MaxDefinitions int `json:"max_definitions,omitempty"`
// MaxBytes caps the total serialized size of the definitions. Zero
// falls back to DefaultBudget.MaxBytes.
MaxBytes int64 `json:"max_bytes,omitempty"`
}
Budget caps how many definitions reach the model per turn.
type Catalog ¶
type Catalog interface {
// Get returns the tool registered under name.
Get(name string) (Tool, bool)
// Definitions returns the Definition of every tool in the catalog.
Definitions() []message.ToolDefinition
}
Catalog is the read-side view of a tool collection: lookup plus model-facing definitions. A Registry implements it; read-side wrappers (e.g. the dynamic injection catalog) implement it over a base registry without being execution surfaces.
type ConflictPolicy ¶
type ConflictPolicy int
ConflictPolicy decides what happens when two sources contribute a tool with the same name.
const ( // ConflictError fails construction on any duplicate name. This is // the default: a silent overwrite usually means two sources // disagree about who owns a tool. ConflictError ConflictPolicy = iota // ConflictOverwrite lets a later source (in the explicit dep // order) replace an earlier tool with the same name. Use it when // a deployment intentionally overrides a base tool. ConflictOverwrite )
type DiscoverOutcome ¶ added in v0.3.1
type DiscoverOutcome struct {
// Results carries one entry per requested name, in input order.
Results []DiscoverResult `json:"results"`
// Evicted lists pool entries dropped by budget pressure (LRU)
// while fitting the newly discovered names.
Evicted []string `json:"evicted,omitempty"`
}
DiscoverOutcome summarizes one Discover call.
type DiscoverResult ¶ added in v0.3.1
type DiscoverResult struct {
Name string `json:"name"`
// Exposed reports whether the tool will appear in the next
// Definitions call: pooled and within the per-round budget.
Exposed bool `json:"exposed"`
// Reason explains a name that is not exposed:
// "unknown" | "not_discoverable" | "over_budget" | "visible_budget".
Reason string `json:"reason,omitempty"`
}
DiscoverResult reports one tool's discovery outcome.
type DiscoveryPolicy ¶ added in v0.3.1
type DiscoveryPolicy struct {
// MaxTools caps the number of pool entries. Zero falls back to
// the assembly's effective Budget.MaxDefinitions, so the default
// pool is never wider than the visible set; set it explicitly to
// keep more tools loaded than one round can show.
MaxTools int `json:"max_tools,omitempty"`
// MaxBytes caps the total serialized definition size across pool
// entries, measured with definitionBytes. Zero falls back to the
// assembly's effective Budget.MaxBytes, so the default pool fits
// the visible set; set it explicitly to keep a larger loaded cache.
MaxBytes int64 `json:"max_bytes,omitempty"`
// IdleRounds evicts an entry that has not been used or refreshed
// within this many rounds. Zero falls back to 10.
IdleRounds int `json:"idle_rounds,omitempty"`
}
DiscoveryPolicy bounds the session-level discovery pool: tools the model has found via tool_search or used, kept loaded across rounds. The pool budget is independent from the per-round Budget: Budget caps what reaches the model this turn, Discovery caps what the session remembers.
func DefaultDiscoveryPolicy ¶ added in v0.3.1
func DefaultDiscoveryPolicy() DiscoveryPolicy
DefaultDiscoveryPolicy returns the recommended discovery pool bounds for hosts that keep the default per-round budget: the same size as that budget, with a 10-round idle horizon. Discovery fields left unset are normalized against the assembly's effective Policy.Budget instead, so raising budget.* raises the pool with it.
type Dispatch ¶
Dispatch is the function form of "execute one tool call and return its result". Middleware operates on this signature.
Implementations should treat the input call as immutable and must always return a Result — errors are reported via Result.IsError, never returned out-of-band.
type Dispatcher ¶
type Dispatcher interface {
Execute(context.Context, message.ToolCall) message.ToolResult
ExecuteAll(context.Context, []message.ToolCall) []message.ToolResult
}
Dispatcher is the execution contract consumers depend on: run one tool call, or several concurrently. *Executor is the canonical implementation, but tests and adapters may substitute their own.
type Executor ¶
type Executor struct {
// contains filtered or unexported fields
}
Executor dispatches tool calls against a Catalog through a middleware chain. The un-decorated core only looks the tool up and invokes it; every cross-cutting concern — recovery, telemetry, concurrency limits, timeouts, approval, audit — is middleware, declared at construction. The chain is immutable afterwards, so an Executor is safe for concurrent use by construction.
func NewExecutor ¶
func NewExecutor(catalog Catalog, mws ...Middleware) *Executor
NewExecutor builds an Executor over catalog. Nil middleware values are skipped. catalog must be non-nil.
func (*Executor) Execute ¶
Execute runs one call through the middleware chain. All failures — unknown tool, tool error — surface as Result with IsError=true, never as a Go error.
func (*Executor) ExecuteAll ¶
ExecuteAll runs every call concurrently through the chain and returns results in input order. Concurrency is unbounded by default; put a concurrency middleware in the chain to cap it.
type Exposure ¶
type Exposure string
Exposure controls whether a tool appears in the model-visible Definitions. It is host policy, never part of message.ToolDefinition.
const ( // ExposureAlways marks a tool that appears in Definitions every // turn. Keep this set small; it is the stable baseline. ExposureAlways Exposure = "always" // ExposureDirect marks a tool that appears only when it is // RequiredByName or sits in the discovery pool (found via // tool_search or used recently). ExposureDirect Exposure = "direct" // ExposureDeferred marks a tool that is hidden until tool_search // surfaces it; discovered tools stay visible while they are used // and expire after idle rounds or discovery-budget eviction. ExposureDeferred Exposure = "deferred" // ExposureHidden marks a tool that never appears in Definitions // (not even via search). RequiredByName can still surface it, and // the tool remains callable by exact name at all times. ExposureHidden Exposure = "hidden" )
type LazyTool ¶
type LazyTool struct {
Name string
Placeholder message.ToolDefinition
Load func(ctx context.Context) (Tool, error)
}
LazyTool is a deferred tool descriptor contributed by a Source. Name must equal Placeholder.Name; Load performs any network or process work the tool needs.
type Middleware ¶
Middleware wraps a Dispatch. mws[0] is outermost (sees the call first and the result last), matching agent.ComposeHost.
type Option ¶
type Option func(*options)
Option configures a Registry.
func WithConflictPolicy ¶
func WithConflictPolicy(p ConflictPolicy) Option
WithConflictPolicy sets the duplicate-name policy. The default is ConflictError; pass ConflictOverwrite to let later sources win.
type Policy ¶
type Policy struct {
// Default is the exposure applied to registered tools that have no
// explicit entry in Exposures. Empty means ExposureDeferred.
Default Exposure `json:"default,omitempty"`
// Exposures maps tool names to explicit exposure levels.
Exposures map[string]Exposure `json:"exposures,omitempty"`
// Budget caps the visible set. Zero uses DefaultBudget.
Budget Budget `json:"budget,omitempty"`
// SearchWithLoad makes tool_search load every deferred source
// before ranking, so hits are computed over real metadata instead
// of placeholder declarations. Default is false (lazy search).
SearchWithLoad bool `json:"search_with_load,omitempty"`
// Discovery bounds the persistent discovery pool. Zero fields fall
// back to DefaultDiscoveryPolicy.
Discovery DiscoveryPolicy `json:"discovery,omitempty"`
// SelectedRetention is deprecated: when Discovery.IdleRounds is
// unset it seeds the idle horizon, then it is ignored. Zero means
// "unset".
SelectedRetention int `json:"selected_retention,omitempty"`
// RecentWindow is deprecated: when Discovery.IdleRounds is unset
// and SelectedRetention is not set either, it seeds the idle
// horizon. It no longer drives a separate recency window. Zero
// means "unset".
RecentWindow int `json:"recent_window,omitempty"`
}
Policy is the host-declared injection policy for one assembly. Zero values mean "use DefaultPolicy" for every field except Exposures, so a partial settings subtree is sufficient.
func DefaultPolicy ¶
func DefaultPolicy() Policy
DefaultPolicy returns the recommended policy: everything deferred by default, the per-round budget, and the default discovery pool.
type Registrar ¶ added in v0.1.8
type Registrar interface {
// Add registers one tool. Duplicate names follow the registry's
// ConflictPolicy; adding to a closed registry returns an error.
Add(t Tool) error
// Remove unregisters the named tool, if present.
Remove(name string)
}
Registrar is the runtime mutation surface of a Registry. Sources that discover tools after construction — an MCP server that connects in the background, a refresh that re-lists remote tools — use it to publish tools as they become available.
type Registry ¶
type Registry struct {
// contains filtered or unexported fields
}
Registry aggregates tools from many Source values. It implements Catalog; the underlying execution surface for an Executor is the same value. The tool set is fixed at construction — sources are a build-time concern — and changes at runtime only through the Registrar surface (Add/Remove), which deferred sources use to publish tools discovered after construction.
A Registry is safe for concurrent use. Close releases every contributed tool that implements io.Closer (deferred proxies included), forbids further lazy loads, and detaches the sources that were attached at construction.
func NewRegistry ¶
NewRegistry builds a registry from sources in order. With the default ConflictError policy a duplicate name is an error; with ConflictOverwrite the later source wins. Sources that implement RegistryAttacher receive the new registry's Registrar before NewRegistry returns, so tools they discover after construction (a background MCP connect, a refresh) still reach it; sources that implement RegistryDetacher are notified when the registry closes.
func (*Registry) Add ¶ added in v0.1.8
Add registers one tool at runtime. It follows the same duplicate policy as construction and fails after Close.
func (*Registry) Close ¶
Close releases every tool that implements io.Closer (in reverse registration order), marks the registry closed so deferred tools can no longer load, and notifies every attached RegistryDetacher. It is idempotent.
func (*Registry) Definitions ¶
func (r *Registry) Definitions() []message.ToolDefinition
Definitions implements Catalog, sorted by name.
func (*Registry) Get ¶
Get implements Catalog. A deferred tool returns its proxy, which serves the placeholder definition until first Execute.
func (*Registry) Remove ¶ added in v0.1.8
Remove unregisters the named tool at runtime. If the removed tool implements io.Closer, it is closed so runtime removals (e.g. a shrunk MCP tool projection) release whatever resources the tool holds; the close happens outside the registry lock and is best-effort. Unknown names are ignored; after Close it is a no-op.
type RegistryAttacher ¶ added in v0.1.8
type RegistryAttacher interface {
Attach(r Registrar)
}
RegistryAttacher is an optional Source capability: NewRegistry hands the new Registry's Registrar to sources that need to publish tools after construction (background connects, refresh).
type RegistryDetacher ¶ added in v0.5.4
type RegistryDetacher interface {
// Detach reports that r, the registry this source was attached to,
// has closed and released its tools. A source attached to several
// registries is told about each as it closes.
Detach(r Registrar)
}
RegistryDetacher is an optional RegistryAttacher counterpart: the registry calls Detach when it closes, so the attacher can drop the registrar it was handed. Without it, a source that outlives the registries it is attached to (a host ToolSet attached once per runtime generation) keeps every closed registry, and the tools it holds, reachable from a process-lifetime object.
type RegistryFactory ¶
type RegistryFactory struct{}
RegistryFactory builds a memory registry from its many tool.Source deps. The dep order (GetAll's sorted key order) is the explicit source order used by ConflictOverwrite.
func (RegistryFactory) Spec ¶
func (RegistryFactory) Spec() resource.Spec
Spec implements resource.Factory.
type SearchHit ¶
type SearchHit struct {
Name string `json:"name"`
Description string `json:"description"`
Score float64 `json:"score"`
}
SearchHit is one tool_search result.
type SearchTool ¶
type SearchTool struct{}
SearchTool is the model-facing discovery tool. It resolves the session from context (see WithSession) so one instance can be registered on the shared registry for all sessions.
func NewSearchTool ¶
func NewSearchTool() SearchTool
NewSearchTool returns the tool_search implementation.
func (SearchTool) Definition ¶
func (SearchTool) Definition() message.ToolDefinition
type Session ¶
type Session interface {
Catalog
// Require adds names to the RequiredByName set. Idempotent.
Require(names ...string)
// Discover adds names to the discovery pool (or refreshes their
// recency). The pool is bounded by Policy.Discovery: entries are
// evicted least-recently-used when the pool is over budget and
// swept after idle rounds. A discovered tool must be loaded before
// the next Definitions call, otherwise the model would see its
// placeholder schema — tool_search loads each name before
// discovering it. Names are ranked: the first one wins when a
// same-round tie is pruned. DiscoverResult.Exposed reports whether
// the name will actually appear in the next Definitions call, so a
// pooled tool that loses the per-round budget comes back with
// Reason "visible_budget" instead of a false positive.
Discover(names ...string) DiscoverOutcome
// RecordCall records that the model called name this round,
// refreshing its discovery-pool recency. Always and Hidden tools
// are skipped: the former are always visible, the latter must
// never surface through use.
RecordCall(call message.ToolCall)
// AdvanceTurn moves to the next round, expiring discovery entries
// that went idle and dropping names no longer in the catalog. Call
// it once per inference round.
AdvanceTurn()
// Search ranks searchable definitions against query with BM25.
// Search never loads deferred tools.
Search(ctx context.Context, query string, limit int) ([]SearchHit, error)
// SearchWithLoad loads every deferred tool before ranking, so hits
// are computed over real metadata. It is the explicit opt-in for
// hosts that need the complete tool set; tool_search itself follows
// Policy.SearchWithLoad instead.
SearchWithLoad(ctx context.Context, query string, limit int) ([]SearchHit, error)
// Load eagerly loads every deferred tool. One failing source does
// not stop the others; errors are joined.
Load(ctx context.Context) error
// EnsureLoaded forces the deferred load of the named tools so the
// next Definitions call sees their real schemas. Unknown or
// already-concrete tools are skipped.
EnsureLoaded(ctx context.Context, names ...string) error
}
Session is the per-run / per-conversation injection view over a Catalog. An Engine creates one at run start, feeds the model Definitions() each round, records calls, and advances turns.
When the assembly was built without dynamic injection, the session is static: Definitions() shows every tool, and the stateful operations (Require/Discover/RecordCall/AdvanceTurn) are no-ops while Search returns NotAvailable.
type Source ¶
Source is a multi-tool contributor to a Registry: builtin Go functions, a sandbox-backed exec set, an MCP server, a YAML document, a host-provided collection. Sources are resources in the deployment DAG; a registry aggregates them through a Many dep.
Tools() are eager — available immediately without I/O. LazyTools() are deferred: the registry stores a proxy serving LazyTool.Placeholder as its definition, and the real implementation is loaded on first Execute (never at build time).
type Tool ¶
type Tool interface {
Definition() message.ToolDefinition
Execute(ctx context.Context, arguments string) (message.Content, error)
}
Tool is the interface that LLM-callable tools must implement.
Execute returns provider-neutral content, not a string: a text-only tool returns a single text part (message.NewTextContent), while a tool that produces images, audio, files, or structured data keeps those parts intact all the way to the wire. Errors are transport or execution failures; a failure the model should read and recover from is better expressed as an error result at the dispatch layer, which preserves the distinction for middleware and telemetry.
Returning zero parts with a nil error is allowed and means "nothing to report": dispatch normalizes it to one empty text part before the result is stored. Content that fails message.Content.Validate is a contract violation and comes back as an error result naming the tool.
type ToolMeta ¶
type ToolMeta struct {
// RateLimit is the maximum number of executions per second this
// tool can sustain. Zero means "no claim" (no rate limit applied).
// A negative value is treated as zero.
RateLimit float64
// MutatesState declares that this tool has side effects beyond
// returning a result (writes, posts, sends mail, ...). Zero value
// (false) is the conservative default in the opposite direction:
// callers that don't know better should assume the tool MAY mutate
// state.
MutatesState bool
// SelfTimeout declares that the tool already bounds its own
// execution time, so the timeout middleware should not impose its
// default deadline on top.
SelfTimeout bool
}
ToolMeta carries optional, execution-relevant metadata about a Tool.
All fields are advisory; a zero ToolMeta means "no claims, treat conservatively" (no rate limit, assume the tool may mutate state so retries are unsafe).
func MetadataOf ¶
MetadataOf returns the ToolMeta declared by t, or a zero ToolMeta when t does not implement ToolMetadata. Safe on nil values.
type ToolMetadata ¶
type ToolMetadata interface {
Metadata() ToolMeta
}
ToolMetadata is an optional interface a Tool may implement to declare execution-relevant metadata. Tools that do not implement it are treated as if they returned a zero ToolMeta.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package mcp attaches Model Context Protocol servers to a [tool.Registry], turning every tool a server exposes into an ordinary [tool.Tool].
|
Package mcp attaches Model Context Protocol servers to a [tool.Registry], turning every tool a server exposes into an ordinary [tool.Tool]. |
|
Package tooltest provides reusable fixtures and contract suites for core/tool implementations.
|
Package tooltest provides reusable fixtures and contract suites for core/tool implementations. |