Documentation
¶
Overview ¶
Package runner executes the ReAct feedback loop for the fino Agent SDK. A Runner holds only configuration; each run owns its own message list, so a single Runner can be reused concurrently across independent runs.
Index ¶
- Variables
- type Approval
- type ApprovalError
- type Input
- type Option
- type PendingToolCall
- type Result
- type RunOption
- type Runner
- func (r *Runner) ResumeApproved(ctx context.Context, a *agent.Agent, suspended SuspendedRun, ...) (*Result, error)
- func (r *Runner) Run(ctx context.Context, a *agent.Agent, input Input, opts ...RunOption) (*Result, error)
- func (r *Runner) Stream(ctx context.Context, a *agent.Agent, input Input, opts ...RunOption) iter.Seq2[model.Event, error]
- type SuspendedRun
- type ToolDeniedError
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNotSuspended is returned by Result.SuspendedRun when the result did not // suspend (it completed normally). It is distinct from ErrInvalidApproval. ErrNotSuspended = errors.New("result is not suspended") // ErrInvalidApproval is the sentinel wrapped by ApprovalError. It reports an // invalid resume input: a malformed SuspendedRun, or an approval set that // does not match the suspended pending calls (missing, unknown, duplicate). ErrInvalidApproval = errors.New("invalid approval") // ErrResumeAgentMismatch indicates ResumeApproved was called with an agent // other than the one active when the run suspended (SuspendedRun.LastAgentName). // Resuming under the wrong agent would resolve tools in the wrong context. ErrResumeAgentMismatch = errors.New("resume agent does not match suspended agent") )
var ( // ErrMaxTurns indicates the run exceeded the configured maximum number of turns. ErrMaxTurns = errors.New("max turns exceeded") // ErrToolNotFound indicates the model requested a tool the current mode does not provide. ErrToolNotFound = errors.New("tool not found") // ErrSystemMessageInHistory indicates the run input contained a system // message; the Runner injects system instructions from the mode instead. ErrSystemMessageInHistory = errors.New("system message in history") // ErrToolDenied is the sentinel wrapped by ToolDeniedError when a Policy denies a tool call. ErrToolDenied = errors.New("tool denied") // ErrStreamContract indicates a model.Model.Stream implementation violated // its event contract: it must yield exactly one TurnMessage per turn and must // not yield FinalMessage (FinalMessage is emitted only by the Runner). ErrStreamContract = errors.New("model stream contract violated") // ErrInvalidToolCallID indicates a tool_use block carried an empty ID. The // Runner rejects the batch before authorizing or executing any tool, because // approval, the idempotency key, and the replay tape all key on the ID. ErrInvalidToolCallID = errors.New("invalid tool call id") // ErrDuplicateToolCallID indicates two tool_use blocks in the same batch // shared an ID, which makes approval matching and the idempotency key // ambiguous. The Runner rejects the batch before executing any tool. ErrDuplicateToolCallID = errors.New("duplicate tool call id") )
Functions ¶
This section is empty.
Types ¶
type Approval ¶ added in v0.4.0
Approval is a human decision about one suspended tool call, matched to the call by CallID (the tool_use block's ID). Approved=true executes the tool; Approved=false rejects it and writes Reason into a model-visible error tool_result so the model can adjust.
type ApprovalError ¶ added in v0.4.0
type ApprovalError struct {
Missing []string // pending CallIDs with no approval
Unknown []string // approval CallIDs not in the pending set
Duplicate []string // CallIDs approved more than once
}
ApprovalError reports that the approvals passed to ResumeApproved did not validly cover the suspended pending calls. It wraps ErrInvalidApproval.
func (*ApprovalError) Error ¶ added in v0.4.0
func (e *ApprovalError) Error() string
Error implements the error interface.
func (*ApprovalError) Unwrap ¶ added in v0.4.0
func (e *ApprovalError) Unwrap() error
Unwrap returns ErrInvalidApproval so errors.Is(err, ErrInvalidApproval) is true.
type Input ¶
type Input struct {
// contains filtered or unexported fields
}
Input is the initial message list for a run. Construct it with Text or Messages rather than building it directly.
type Option ¶
type Option func(*Runner)
Option configures a Runner.
func WithMaxConcurrency ¶
WithMaxConcurrency sets the maximum number of tools the Runner may execute concurrently within a single tool-call batch. A value of n <= 1 (the default) keeps tools serial. A value of n > 1 permits up to n tools at once only when every tool in the batch declares tool.Effects.ParallelSafe; otherwise the whole batch falls back to serial execution. The Runner still authorizes all calls serially in call order, preserves result order, and is fail-fast on the first error.
func WithMaxTurns ¶
WithMaxTurns sets the maximum number of model turns per run.
func WithPolicy ¶
WithPolicy sets the authorization policy consulted before each tool call. A nil policy is ignored, preserving the default AllowAll.
type PendingToolCall ¶ added in v0.3.0
PendingToolCall describes a tool call that a Policy suspended. It carries the tool's static info, the original model tool_use, and the Policy's reason, so a caller can present the call for human approval and later resume it.
type Result ¶
type Result struct {
Message message.Message
Messages []message.Message
LastAgent *agent.Agent
LastMode string
// Suspended reports that a Policy suspended the run before executing a tool
// batch. The history tail is the dangling assistant tool_use message.
Suspended bool
// PendingCalls holds the suspended tool calls (only those whose decision
// resolved to DecisionSuspend), in call order. It is non-empty only when
// Suspended is true.
PendingCalls []PendingToolCall
// contains filtered or unexported fields
}
Result is the outcome of a run. A run terminates in one of two successful shapes: a completed run (Suspended == false) carries the final Message; a suspended run (Suspended == true) carries PendingCalls and leaves Message zero. The two are mutually exclusive (loop-semantics I3).
func (*Result) SuspendedRun ¶ added in v0.4.0
func (r *Result) SuspendedRun() (SuspendedRun, error)
SuspendedRun extracts a value snapshot from a suspended Result. It returns ErrNotSuspended if the result completed normally.
type RunOption ¶
type RunOption func(*runConfig)
RunOption configures a single run.
func WithMode ¶
WithMode selects which mode of the Agent to start the run in. The default is the Agent's default mode.
func WithModelOptions ¶
WithModelOptions appends model options applied after the mode's own defaults.
func WithResumeFromPendingTools ¶ added in v0.2.1
func WithResumeFromPendingTools() RunOption
WithResumeFromPendingTools makes a run first execute any pending tool calls at the tail of the input history before calling the model. Pending calls are the tool_uses of the last message when it is an assistant message that carries at least one tool_use and therefore has no following tool_result message.
This is the minimal seam for mid-batch / human-in-the-loop resume identified by invariant I10 (see docs/spec/loop-semantics.md §7.2): it exposes the ability to continue a run that was interrupted after the model requested tools but before they ran (e.g. while awaiting human approval), without introducing any checkpoint, session, or graph concept. When the seam is off (the default) or the tail has no pending tool_use, it is a no-op and the run starts at the model as usual.
type Runner ¶
type Runner struct {
// contains filtered or unexported fields
}
Runner executes the ReAct loop against an Agent. It holds only configuration (model, turn limit, policy, hooks); each Run owns its own message list.
func New ¶
New creates a Runner with the given model and options. It returns an error if the model is nil or the configured maximum turns is not positive. The defaults are 10 turns and an AllowAll policy.
func (*Runner) ResumeApproved ¶ added in v0.4.0
func (r *Runner) ResumeApproved(ctx context.Context, a *agent.Agent, suspended SuspendedRun, approvals []Approval, opts ...RunOption) (*Result, error)
ResumeApproved continues a suspended run after a human has approved or rejected its pending tool calls. It validates that a matches the agent active at suspend time and that the approvals validly cover the pending calls, then executes the batch in call order — approved (and previously-allowed) calls run their tools; rejected calls produce a model-visible error tool_result — and resumes the ReAct loop from the next model turn. It does not re-consult the Policy: human approval replaces policy authorization for the suspended calls.
func (*Runner) Run ¶
func (r *Runner) Run(ctx context.Context, a *agent.Agent, input Input, opts ...RunOption) (*Result, error)
Run executes the ReAct loop until the model returns a message with no tool calls, the turn limit is reached, or an error occurs. It returns an error if the agent is nil, the input contains a system message, or the selected mode is not found.
func (*Runner) Stream ¶
func (r *Runner) Stream(ctx context.Context, a *agent.Agent, input Input, opts ...RunOption) iter.Seq2[model.Event, error]
Stream executes the ReAct loop like Run but yields semantic events as they occur. Iteration stops after a final message with no tool calls, on the turn limit, or on the first error. Terminal errors are yielded as a model.StreamError alongside a non-nil iterator error. The arguments mirror Run.
type SuspendedRun ¶ added in v0.4.0
type SuspendedRun struct {
Messages []message.Message
LastAgentName string
LastMode string
PendingCalls []PendingToolCall
// RunID is the run-scoped identifier captured at suspend time (empty when
// the original run used no WithRunID). ResumeApproved restores it so
// approved and previously-allowed calls receive the same
// tool.ExecutionContext.IdempotencyKey they would have in the original run
// (loop-semantics I13). Fixtures without it deserialize to "".
RunID string
}
SuspendedRun is a plain value snapshot of a suspended run, extracted from a suspended Result via Result.SuspendedRun. It is not a checkpoint, state machine, or anything the core persists; the caller persists it however they like and later passes it to Runner.ResumeApproved together with the live agent. LastAgentName and LastMode record the agent and mode active at suspend time (a handoff may have switched away from the root agent before the suspend); ResumeApproved checks the agent the caller passes against LastAgentName so the resume re-enters the correct context. The agent itself is code reconstructed by the caller, so the snapshot holds only its name — no live object reference. PendingCalls holds only the suspended calls.
The snapshot contains no live object references, so it is straightforward to serialize; whether json.Marshal succeeds depends on the caller's data — notably tool.Info.Metadata (map[string]any) on each PendingCall, which the core does not sanitize. Keep tool metadata JSON-marshalable if you persist SuspendedRun as JSON.
func SuspendedRunFrom ¶ added in v0.8.0
func SuspendedRunFrom(e model.Suspended) SuspendedRun
SuspendedRunFrom rebuilds a SuspendedRun from a model.Suspended stream event, so a Stream consumer can resume with ResumeApproved exactly as a Run-path caller would from Result.SuspendedRun. The two carry the same data; this is the Stream-side adapter, kept here because model must not import runner (loop-semantics §5).
type ToolDeniedError ¶
ToolDeniedError reports that a Policy denied a tool invocation. It wraps ErrToolDenied and carries the denied tool's Info and the Policy Decision.
func (*ToolDeniedError) Error ¶
func (e *ToolDeniedError) Error() string
Error implements the error interface.
func (*ToolDeniedError) Unwrap ¶
func (e *ToolDeniedError) Unwrap() error
Unwrap returns ErrToolDenied so errors.Is(err, ErrToolDenied) reports true.