Documentation
¶
Overview ¶
Package agentturn is a composable agent loop over Open Responses: one loop, one transcript type, one tool contract, hooks and queues. Everything else is a front that feeds prompts in and consumes events out, or a subscriber.
The transcript is openresponses.Items. What a session stores, what the model receives and what a front renders are the same bytes. The model is any openresponses.Streamer: a remote server through openresponses.Client.AsAdapter, a local adapter, or another agent served by front/responses.
Vocabulary ¶
A run is one Run, Continue, Agent.Prompt or Agent.Continue until the agent goes idle. A turn is one model call plus the tool executions it requested.
Transcript and openresponses.Items are one type under two names, and the name says what a value is. A Transcript is a whole conversation: what Run starts from, what Filter and Transform see and return, what a hook or a tool reads from its context. Items is a fragment: the prompts of a run, the outputs of a batch, the items a run added.
Low-level loop ¶
Run and Continue are observational: they yield Event values in order and the loop does not wait for the consumer between phases. The RunEnd is always the last event and says how the run ended.
for ev := range agentturn.Run(ctx, transcript, prompts, cfg) {
switch e := ev.(type) {
case *agentturn.ItemUpdate:
if d, ok := e.Stream.(*openresponses.OutputTextDeltaEvent); ok {
fmt.Print(d.Delta)
}
case *agentturn.RunEnd:
if e.Err != nil { ... }
}
}
Agent ¶
Agent adds queues, subscribers and run control over the same loop. Subscribers are awaited in registration order and every event is a barrier: tool preflight for a turn does not start until every subscriber has returned for the assistant item, and Agent.Prompt settles only after the run_end subscribers finish.
Hooks ¶
Every hook on Config is one field, so two layers that each want one silently lose an assignment to each other. ChainBeforeModelCall and its siblings join them in one place, with the order written where a reader can see it.
Composition ¶
The loop never learns a sub-agent concept. It knows a Model and a list of agenttool.Tool, and every composition is one of those two things: front/responses serves a loop as a model, tools/agent wraps a config as a tool, and the A2A and MCP adapters do the same across a protocol.
Index ¶
- Constants
- Variables
- func CanContinue(t Transcript) bool
- func ChainBeforeModelCall(fns ...func(context.Context, *openresponses.Request) error) func(context.Context, *openresponses.Request) error
- func ChainBeforeToolCall(fns ...func(context.Context, ToolCallInfo) (*ToolDecision, error)) func(context.Context, ToolCallInfo) (*ToolDecision, error)
- func ChainBeforeTurn(fns ...func(context.Context, TurnStartInfo) (openresponses.Items, error)) func(context.Context, TurnStartInfo) (openresponses.Items, error)
- func ChainOutputGuard(fns ...func(context.Context, OutputInfo) (*openresponses.Message, error)) func(context.Context, OutputInfo) (*openresponses.Message, error)
- func ChainShouldStopAfterTurn(fns ...func(context.Context, TurnInfo) (bool, error)) func(context.Context, TurnInfo) (bool, error)
- func ContextWithDeciders(ctx context.Context, by map[string]string) context.Context
- func ContextWithRunID(ctx context.Context, runID string) context.Context
- func ContextWithTranscript(ctx context.Context, t Transcript) context.Context
- func ContextWithTrigger(ctx context.Context, t Trigger) context.Context
- func Continue(ctx context.Context, t Transcript, cfg Config) iter.Seq[Event]
- func DeciderFromContext(ctx context.Context, callID string) string
- func DefaultBackoff(attempt int, err error) time.Duration
- func DefaultRetryable(err error) bool
- func Hidden(item openresponses.Item) openresponses.Item
- func Invoke(ctx context.Context, name string, args json.RawMessage) (agenttool.Result, error)
- func PendingCalls(pending []PendingCall) []*openresponses.FunctionCall
- func Run(ctx context.Context, t Transcript, prompts openresponses.Items, cfg Config) iter.Seq[Event]
- func RunIDFromContext(ctx context.Context) string
- func Unhide(item openresponses.Item) (openresponses.Item, bool)
- func VisibleFilter(visible ...string) func(Transcript) Transcript
- type Agent
- func (a *Agent) Abort()
- func (a *Agent) AbortCause(cause error)
- func (a *Agent) Config() Config
- func (a *Agent) Continue(ctx context.Context) (*RunEnd, error)
- func (a *Agent) FollowUp(items ...openresponses.Item)
- func (a *Agent) Prompt(ctx context.Context, items ...openresponses.Item) (*RunEnd, error)
- func (a *Agent) Resume(ctx context.Context, answers ...Answer) (*RunEnd, error)
- func (a *Agent) SetConfig(cfg Config) error
- func (a *Agent) SetTranscript(t Transcript) error
- func (a *Agent) State() State
- func (a *Agent) Steer(items ...openresponses.Item)
- func (a *Agent) Subscribe(fn func(context.Context, Event) error) (unsubscribe func())
- func (a *Agent) WaitForIdle(ctx context.Context) error
- type Answer
- type Config
- type Event
- type ExecutionMode
- type ItemEnd
- type ItemStart
- type ItemUpdate
- type Model
- type ModelBlocked
- type ModelRetry
- type Option
- type OutputInfo
- type PendingCall
- type PendingReason
- type QueueMode
- type Queued
- type Reason
- type ResponseEnd
- type Retry
- type RunEnd
- type RunStart
- type Source
- type State
- type StopCause
- type ToolAction
- type ToolCallInfo
- type ToolDecision
- type ToolDispatch
- type ToolEnd
- type ToolOverride
- type ToolResultInfo
- type ToolStart
- type ToolUpdate
- type Transcript
- type Trigger
- type TurnEnd
- type TurnInfo
- type TurnStart
- type TurnStartInfo
Constants ¶
const ( EventRunStart = "run_start" EventTurnStart = "turn_start" EventModelRetry = "model_retry" EventModelBlocked = "model_blocked" EventResponseEnd = "response_end" EventItemStart = "item_start" EventItemUpdate = "item_update" EventItemEnd = "item_end" EventToolStart = "tool_start" EventToolDispatch = "tool_dispatch" EventToolUpdate = "tool_update" EventToolEnd = "tool_end" EventTurnEnd = "turn_end" EventRunEnd = "run_end" EventQueued = "queued" )
Event type names.
const EventBuffer = 256
EventBuffer is how many events the loop can run ahead of the consumer of Run or Continue before it blocks; Agent delivers every event synchronously and has no buffer.
Variables ¶
var ( // ErrRunning is returned when Prompt, Continue, Resume, SetConfig or // SetTranscript is called while a run is active. ErrRunning = errors.New("agentturn: agent is already running") // ErrInputRequired is returned when the last run left calls // unanswered, deferred to the caller or cut off by an abort or a // failure; answer them through [Agent.Resume], or open the next // [Agent.Prompt] with their outputs. The typed errors of tools/agent // and tools/a2a match it with errors.Is, so a host can ask "does any // sub-agent need input" once. ErrInputRequired = errors.New("agentturn: pending tool calls must be resumed before continuing") // ErrNotPending is returned when Resume was given an answer for a // call that is not pending, left a pending call unanswered, or was // called with nothing pending, and when a Prompt opens with an // output for a call that is not pending. ErrNotPending = errors.New("agentturn: output does not answer a pending call") )
Errors returned by Agent.
var ( // ErrNoModel is returned when Config.Model is nil. ErrNoModel = errors.New("agentturn: config has no model") // ErrCannotContinue is returned when the transcript does not end // with a user message or a function call output, so there is // nothing for the model to answer. ErrCannotContinue = errors.New("agentturn: transcript must end with a user message or a function call output to continue") // ErrNoPrompt is returned when Run was called with no prompt items. ErrNoPrompt = errors.New("agentturn: no prompt items") // ErrGuard is what a ShouldStopAfterTurn hook wraps to end the run // as a policy stop rather than a failure: ReasonStopped with // StopGuard, the error on RunEnd.Err. ErrGuard = errors.New("agentturn: guard stopped the run") )
Errors returned before a run starts. Every error the package produces, sentinel or wrapped, begins with "agentturn:".
var ErrNoInvoker = errors.New("agentturn: no loop on the context to invoke a tool through")
ErrNoInvoker is returned by Invoke outside a tool call of a loop.
Functions ¶
func CanContinue ¶ added in v0.0.4
func CanContinue(t Transcript) bool
CanContinue reports whether the model has something to answer: the transcript ends with a user message or a function call output.
func ChainBeforeModelCall ¶ added in v0.0.7
func ChainBeforeModelCall(fns ...func(context.Context, *openresponses.Request) error) func(context.Context, *openresponses.Request) error
ChainBeforeModelCall runs each hook on the request in order, so each sees what the ones before it left. The first error stops the chain and is returned, which ends the run with a ModelBlocked event as a single hook's error does.
func ChainBeforeToolCall ¶ added in v0.0.7
func ChainBeforeToolCall(fns ...func(context.Context, ToolCallInfo) (*ToolDecision, error)) func(context.Context, ToolCallInfo) (*ToolDecision, error)
ChainBeforeToolCall folds the decisions of each hook, in order, into one: the strictest action wins, Block over Defer over Allow, which is deny over ask over allow as a permission policy reads it. A Block ends the chain, since nothing after it can make the call run; a Defer does not, so a later layer still sees the call and can refuse it. Among decisions of one action the first reason and decider stand; a decision that takes the fold to a stricter action brings its own, because the reason has to explain the decision that stands and a block's reason is what the model reads. The first note stands, and Terminate is set when any decision sets it. Arguments a decision rewrites are passed to the hooks after it, so each sees what the call will actually run with and the last rewrite is what the tool receives. The first error stops the chain and fails the turn.
A nil result from every hook is a nil decision, which allows the call as a single hook's nil does.
func ChainBeforeTurn ¶ added in v0.0.7
func ChainBeforeTurn(fns ...func(context.Context, TurnStartInfo) (openresponses.Items, error)) func(context.Context, TurnStartInfo) (openresponses.Items, error)
ChainBeforeTurn runs each hook in order and appends what they return, in that order, so every layer contributes its items to the turn. The first error stops the chain and is returned; the items of the hooks that already ran are dropped with the turn.
func ChainOutputGuard ¶ added in v0.0.7
func ChainOutputGuard(fns ...func(context.Context, OutputInfo) (*openresponses.Message, error)) func(context.Context, OutputInfo) (*openresponses.Message, error)
ChainOutputGuard runs each guard on the message in order, each seeing what the ones before it left: a guard that returns a replacement hands that replacement to the next. The last replacement is what reaches the transcript, and a chain in which no guard replaced anything keeps the model's own message. The first error stops the chain and fails the turn.
func ChainShouldStopAfterTurn ¶ added in v0.0.7
func ChainShouldStopAfterTurn(fns ...func(context.Context, TurnInfo) (bool, error)) func(context.Context, TurnInfo) (bool, error)
ChainShouldStopAfterTurn runs each hook in order until one stops the run: the first true ends the run, and the hooks after it do not run, since there is no turn left for them to judge. The first error stops the chain and is returned, so an error wrapping ErrGuard from any hook ends the run as a guard stop with that error on RunEnd.Err. A layer that must see every turn, a meter for one, belongs in a subscriber rather than here.
func ContextWithDeciders ¶ added in v0.0.7
ContextWithDeciders attaches who decided the answer to each pending call, by call ID, in the session format's terms ("human", "policy", "agent"). Agent.Resume does it from the Answer.By of the answers it was given, so a subscriber writing the record of an output the caller supplied, which raises no tool_start to carry a decision, can say who wrote it. A host driving the low-level Run with the outputs as prompts attaches it itself. The loop reads nothing from it.
func ContextWithRunID ¶ added in v0.0.5
ContextWithRunID attaches a run ID to ctx. The loop does this with its own for everything a run calls, the transform, the hooks, the model and the tools, so a tool that composes another agent, and whatever observes that child, can tell which run it was called from.
func ContextWithTranscript ¶ added in v0.0.4
func ContextWithTranscript(ctx context.Context, t Transcript) context.Context
ContextWithTranscript attaches a transcript to ctx. The loop does this with its working transcript before running a turn's hooks and tools, so a tool that composes another agent can seed it from the conversation without the host threading anything through.
func ContextWithTrigger ¶ added in v0.0.6
ContextWithTrigger attaches a Trigger to ctx. A run started with that context, through Run, Continue or an Agent, carries it on its RunStart, so a recorder can write what caused the run without the loop learning what a cron job or a channel is.
func Continue ¶
Continue drives the loop from the transcript as it stands, which must satisfy CanContinue and answer every function call it holds. Events arrive as for Run.
func DeciderFromContext ¶ added in v0.0.7
DeciderFromContext returns who the caller named as the decider of the answer for callID, or "" when nobody was named.
func DefaultBackoff ¶ added in v0.0.4
DefaultBackoff is the Retry.Backoff used when none is set: the Retry-After header of an openresponses error when it names a number of seconds, otherwise 500ms doubled per retry and capped at 30s, without jitter.
func DefaultRetryable ¶ added in v0.0.4
DefaultRetryable is the Retry.Retryable used when none is set: an openresponses error with status 408, 409, 429 or 5xx, a stream that ended before its terminal event, and transport failures (net.Error, an unexpected EOF, a reset or refused connection). Everything else, a 4xx in particular, is final.
func Hidden ¶ added in v0.0.7
func Hidden(item openresponses.Item) openresponses.Item
Hidden marks item as part of the model's context that a renderer should hide: a stream rule's interrupt report, an advisory, a notice a keyword added, a subagent's result. The model still sees it, the transcript holds it and a request carries it; only its item events say it is hidden, and a session recorder writes its entry with visible false, which is the format's word for exactly this.
Use it wherever the loop appends an item the caller supplied: Agent.Prompt, Agent.Steer, Agent.FollowUp, the prompts of Run and the items Config.BeforeTurn returns. The loop unwraps it as it appends, so nothing downstream has to know the wrapper exists; a hidden item still in a queue, which Agent.State hands back, is wrapped, so a host that re-queues what it read keeps the mark and one that type-switches on it should call Unhide first.
An item the filter in force hides from the model is a different thing: it is out of the model's context, a recorder writes it as a custom entry, and marking it hidden adds nothing.
func Invoke ¶ added in v0.0.7
Invoke runs one of the turn's tools as if the model had asked for it under the call in flight: Config.BeforeToolCall decides, tool_start and tool_end are emitted with Parent naming the call that made it, Config.AfterToolCall may override the result, and a session recorder writes it. It is what a tool that lets its code reach the agent's other tools, a code-execution kernel over a loopback bridge, calls instead of holding an agenttool.Set of its own, where the policy, the events and the record would all be absent.
The result is the one the model would have seen, with the error beside it: a tool that failed, a name no tool has, arguments that are not an object, or a call the hook refused, whose Reason is the error. A hook that defers the call refuses it instead, since a nested call cannot be handed to the caller: it belongs to a tool that is running. Nothing is appended to the transcript, so a nested call costs no items and a Terminate on its result means nothing to the loop.
The call it is made under comes from agenttool.CallFrom, which agenttool.New puts on every typed tool's context; a tool that implements the interface itself and wants the parent named passes agenttool.WithCall. Outside a loop, Invoke returns ErrNoInvoker.
Call it from a tool, with the context the tool was given, and not from a hook or a subscriber: BeforeToolCall and AfterToolCall take one call at a time, and a subscriber is called while the agent holds delivery, so either would wait for something it is itself holding.
func PendingCalls ¶ added in v0.0.6
func PendingCalls(pending []PendingCall) []*openresponses.FunctionCall
PendingCalls returns the calls of pending, in order.
func Run ¶
func Run(ctx context.Context, t Transcript, prompts openresponses.Items, cfg Config) iter.Seq[Event]
Run appends prompts to the transcript and drives the loop until the agent goes idle. Events are yielded in order and the RunEnd is the single terminal: it is always the last event, its Reason says how the run ended and its Err carries the failure when Reason is ReasonError, the context error when Reason is ReasonAborted, and nil otherwise. A misuse before the run starts (ErrNoPrompt, ErrCannotContinue, ErrNoModel, a duplicate tool name, or ErrInputRequired for a transcript with a function call that neither the transcript nor the prompts' leading outputs answer) yields one RunEnd with ReasonError and no other event.
The loop runs ahead of the consumer by up to EventBuffer events and applies backpressure after that. Breaking out of the loop cancels the run and blocks until it has wound down.
The transcript is not modified; the items the run appended are on the RunEnd.
func RunIDFromContext ¶ added in v0.0.5
RunIDFromContext returns the ID of the run whose hook or tool call the context belongs to, or "" outside a loop.
func Unhide ¶ added in v0.0.7
func Unhide(item openresponses.Item) (openresponses.Item, bool)
Unhide returns the item Hidden wrapped and whether it was hidden. An item that was never hidden comes back unchanged.
func VisibleFilter ¶
func VisibleFilter(visible ...string) func(Transcript) Transcript
VisibleFilter returns a filter like DefaultFilter that keeps the listed extension item types.
Types ¶
type Agent ¶
type Agent struct {
// contains filtered or unexported fields
}
Agent is the stateful loop: a transcript, queues, subscribers and run control over Run. One run at a time; a second Prompt while one is active returns ErrRunning. The zero Agent has no model and is idle; use New.
Subscribers are called synchronously, in registration order, for every event, so every event is a barrier: the loop does not move to the next phase until each subscriber has returned, a tool that raises an event with Invoke waits for them, and so does a caller queueing an item with Agent.Steer. One event is delivered at a time, whichever goroutine raised it, so a subscriber is never entered from two goroutines at once. A subscriber that steers or prompts the agent from inside an event does so with the context it was handed, which carries the delivery it already holds, or from another goroutine. A subscriber that returns an error ends the run with ReasonError. Every event is delivered with a context whose cancellation is lifted: Agent.Abort reaches the model stream and the running tools, and the events that follow it, the tool_end of a cut-off call and the run_end among them, still reach a subscriber that writes durable state with a context it can use. A subscriber that fails while the run is being aborted ends it with ReasonAborted and its error joined to the context error on RunEnd.Err.
func (*Agent) Abort ¶
func (a *Agent) Abort()
Abort cancels the active run, if any. The model stream and running tools see the cancellation through their context and the run ends with ReasonAborted and context.Canceled on RunEnd.Err. The queues are untouched: anything steered or queued and not yet appended goes to the next run.
func (*Agent) AbortCause ¶ added in v0.0.7
AbortCause is Agent.Abort with a reason: cause is what context.Cause reports to everything the run called, and what the run ends with on RunEnd.Err, so a recorder writes it as the run's end and a product can count why its runs were cut. A stream rule that matched, an advisor that raised a blocker, a coordinator that cancelled a job and a user pressing Esc are four things a session otherwise records identically as "context canceled". A nil cause is Agent.Abort.
A cause that a caller wants errors.Is(err, context.Canceled) to keep matching should wrap it; the tools of the run see context.Canceled from their own context either way, since that is what ctx.Err reports.
func (*Agent) Continue ¶
Continue runs from the transcript as it stands, which must satisfy CanContinue, and returns as Agent.Prompt does.
func (*Agent) FollowUp ¶
func (a *Agent) FollowUp(items ...openresponses.Item)
FollowUp queues items to be injected when the run would otherwise end, so the agent keeps going instead of going idle. The queue has the same life, the same Queued event and the same caveat about subscribers as Agent.Steer.
func (*Agent) Prompt ¶
Prompt appends items and runs until idle. It returns when the run_end subscribers have returned, with the RunEnd that says how the run ended: done, stopped, input_required with the pending calls, or aborted with the context error on RunEnd.Err. The error is set only when the run could not start (ErrNoPrompt, ErrRunning, ErrInputRequired, ErrNotPending, ErrNoModel) or ended with ReasonError, in which case it is RunEnd.Err and the RunEnd is returned alongside. A Trigger attached to ctx with ContextWithTrigger is carried on the run's RunStart.
While calls are pending, a prompt that opens with a function_call_output for each of them is accepted: the outputs are appended with their item events ahead of the message, so the next model call sees the answers and the message together, which is what a front wants after an abort when the user's next line is the next prompt. A leading output for a call that is not pending returns ErrNotPending; a prompt that leaves a pending call unanswered returns ErrInputRequired.
func (*Agent) Resume ¶
Resume answers the calls the last run left pending and continues, whether they were deferred to the caller or cut off by an abort or a failure. Every pending call must have exactly one answer, and no answer may name a call that is not pending. An answer is an output or an approval: a caller that refuses a call answers it with the refusal as text, which the model then sees; a caller that approves a deferred call lets the loop run it. Answer.By says who decided, for the record.
The outputs are appended with their item events first, then the notes of the answers that carry one, as user messages. The approved calls then run as one batch as the loop runs any batch, with BeforeToolCall skipped because the decision has been made: tool_start, tool_update and tool_end are emitted with Turn 0, Sequential and MaxParallelTools apply, AfterToolCall runs, the outputs are appended in the calls' transcript order and their notes after them, and anything steered in meanwhile follows. A batch whose results set Terminate ends the run with ReasonStopped without calling the model, and so does any answer built with Refuse. Otherwise the model is called and the run returns as Agent.Prompt does. With nothing pending, Resume returns ErrNotPending.
func (*Agent) SetConfig ¶ added in v0.0.4
SetConfig replaces the configuration for the next run: model, instructions, reasoning, tools, hooks, all of it. It returns ErrRunning while a run is active. Subscribers and the queues are kept, so anything steered or queued under the old configuration goes to the next run under the new one; a session recorder attached to the agent sees the change as a config delta on the next turn.
func (*Agent) SetTranscript ¶ added in v0.0.4
func (a *Agent) SetTranscript(t Transcript) error
SetTranscript replaces the transcript, as when switching to another branch of a session. It returns ErrRunning while a run is active. The pending calls are derived from the new transcript as WithTranscript derives them, so whatever the old transcript was waiting on is forgotten and whatever the new one is waiting on must be answered through Agent.Resume. Queued Steer and FollowUp items are kept and go to the next run on the new transcript; a host that does not want them there reads them from Agent.State first.
func (*Agent) Steer ¶
func (a *Agent) Steer(items ...openresponses.Item)
Steer queues items to be injected after the current tool batch, before the next model call. When the agent is idle they are consumed by the next run at the same point.
Each item is reported to the subscribers as a Queued event, so a host writing what it accepted has it before the run appends it and can tell an item it accepted from one a run produced. The report is not the accept: the item is queued when this returns, and the event is delivered by whichever goroutine owns delivery, at its next event. A run in flight reports it before its next event; an idle agent reports it at the start of the next run, so a host that must not lose an input writes it before calling here rather than from the event.
It never blocks on the delivery barrier, so steering from inside a subscriber is safe: the item is queued and the event follows on the next one.
The queues live in memory: an item accepted here is in no record until a run appends it or a subscriber writes it, and it survives Agent.Abort, [SetConfig] and [SetTranscript] but not the process. A host reads the queues back from Agent.State and queues them again after a restart.
A steered item is appended with its own item events, which reach every subscriber. A subscriber that steers in reaction to an event the steered item itself produces, an item_end during a run for instance, feeds the run forever, and nothing reports it: the loop cannot tell a reaction from a fresh input. Steer from a front, a monitor or another goroutine, or from a subscriber only on events it can tell apart from its own items, such as a tool_end or a specific item type it never steers.
type Answer ¶ added in v0.0.5
type Answer struct {
// CallID names the pending call.
CallID string
// Output, when set, answers the call without running it: a refusal
// as text, or the result of a call the caller ran itself.
Output *openresponses.FunctionCallOutput
// Args, for an approval, replaces the arguments the tool receives,
// as ToolDecision.Args does. nil keeps the call's own.
Args json.RawMessage
// Note is what the user said when answering: it is appended as a
// user message after the outputs of every answer of the Resume, so
// the model reads the result and the note together, in that order,
// in the same turn.
Note string
// Terminate ends the run with ReasonStopped and StopRefused once
// every answer is in, without calling the model, for a refusal that
// should end the turn so the user can say what to do instead. The
// outputs are still appended, so the transcript stays a valid input.
Terminate bool
// By names who decided this answer, for the record: the session
// format knows "human" for a person at a prompt, "policy" for a
// rule that answered on its own and "agent" for another model. It
// rides on the ToolDecision the loop synthesises for an approval,
// and a session recorder writes it on the decision for an answer of
// either kind. There is no default: a policy engine answers through
// Resume as often as a person does, so an answer that names nobody
// is recorded as an anonymous decision rather than guessed at.
By string
}
Answer resolves one pending call for Agent.Resume: an output the caller produced, or an approval that runs the call inside the loop. Build one with Output, Approve, ApproveWith or Refuse, attach what the user said with Answer.WithNote and who said it with Answer.WithBy.
func ApproveWith ¶ added in v0.0.5
func ApproveWith(callID string, args json.RawMessage) Answer
ApproveWith runs the pending call with args in place of the model's.
func Output ¶ added in v0.0.5
func Output(out *openresponses.FunctionCallOutput) Answer
Output answers a pending call with out.
func Refuse ¶ added in v0.0.6
func Refuse(out *openresponses.FunctionCallOutput) Answer
Refuse answers a pending call with out and ends the run instead of calling the model: Output with Terminate set.
type Config ¶
type Config struct {
// Name is how the agent names itself when composed: the tool name in
// tools/agent, the server name in front/mcp, the agent card in
// front/a2a.
Name string
// Description is one paragraph about the agent, used the same way.
Description string
// Model streams responses. Required.
Model Model
// ModelName is the model field of every request.
ModelName string
// Instructions is the instructions field of every request.
Instructions string
// Tools the model may call. The loop reads it at the start of each
// turn.
Tools []agenttool.Tool
// ToolProvider, when set, supplies the tools for each turn in place
// of Tools, so a source whose tool list changes, such as a remote
// MCP server, is picked up on the next turn. It is called once per
// turn, before the model call, and the same list serves the turn's
// tool batch, so a call resolves against the tools the model was
// offered.
//
// A provider that returns a snapshot offers a change one turn late
// when the change is still in flight as the turn starts: a tool
// whose result announces a new tool returns before the refresh that
// fetches it has completed. A provider that must offer the change
// on the very next call waits for it here, bounded by ctx, as
// mcpclient's Await does:
//
// cfg.ToolProvider = func(ctx context.Context) []agenttool.Tool {
// ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
// defer cancel()
// _ = remote.Await(ctx)
// return remote.Tools()
// }
ToolProvider func(ctx context.Context) []agenttool.Tool
// Reasoning is the reasoning field of every request: effort and
// summary. The zero value leaves the request's own.
Reasoning openresponses.ReasoningConfig
// Text is the text field of every request: output format and
// verbosity. The zero value leaves the request's own.
Text openresponses.TextConfig
// Request is the base of every request the loop sends: tool_choice,
// parallel_tool_calls, max_output_tokens, temperature, truncation,
// include, safety_identifier, prompt_cache_key, service_tier and
// every other member are copied from it. The loop owns input, tools,
// store, stream and previous_response_id, and ModelName,
// Instructions, Reasoning and Text take precedence over the same
// members here when set. The loop adds nothing else: the run and
// turn IDs are on every event, not on the request, so the settings
// the model sees change only when this config does.
Request openresponses.Request
// BeforeTurn runs at the start of each turn, before the request is
// built, and returns items the loop appends to the transcript with
// their item events, as it appends a queued message: the time, a
// reminder due, the state of the environment, whatever the model
// needs this turn. The items are then facts about the transcript, so
// a recorded session rebuilds the request they were part of, which
// injection through Transform cannot give. nil or no items appends
// nothing. A guard on the request itself belongs in BeforeModelCall.
//
// It is one field and several layers want it. Assigning it twice
// keeps the second assignment and loses the first with no error and
// no sign, so a product with more than one layer joins them with
// [ChainBeforeTurn], which appends what each returns in order.
BeforeTurn func(context.Context, TurnStartInfo) (openresponses.Items, error)
// BeforeModelCall runs on the fully built request of each turn, just
// before it is sent and before turn_start reports it. It may change
// anything. A change to Input is one the record cannot describe, so
// a session recorder writes that call without a request hash; a
// change to a setting is recorded as a config delta. A hook that
// refuses the call ends the run with ReasonError after a
// [ModelBlocked] event carrying the request as built; a guard that
// calls a model is on the critical path of every first token, since
// the request is not final until the hook returns.
//
// This is the most contested field in the package: a memory that
// re-renders its block into the instructions and a guard that
// inspects what is about to be sent both want it, and assigning it
// twice keeps the second assignment silently. Join them with
// [ChainBeforeModelCall], in the order they must run: a hook that
// edits the request before one that inspects it.
BeforeModelCall func(context.Context, *openresponses.Request) error
// OutputGuard runs on each assistant message as the stream completes
// it, after output_item.done and before the message is appended to
// the transcript and delivered as item_end, and may replace it with
// another message, a placeholder for one that must not reach the
// user, the transcript or the record. nil keeps the message. The
// deltas of the original have already been delivered as item_update,
// so a front that must not show withheld text renders on item_end.
// Function calls, reasoning and every other output item never reach
// it, so a replay still has what it needs; a guard that also wants
// to end the run returns an error wrapping [ErrGuard] from
// ShouldStopAfterTurn, which sees the turn with TurnInfo.Final set.
// Several guards are joined with [ChainOutputGuard], each seeing
// what the one before it left.
OutputGuard func(context.Context, OutputInfo) (*openresponses.Message, error)
// Retry is the policy for transient model failures. The zero value
// retries nothing; see [Retry].
Retry Retry
// ToolExecution selects parallel (default) or sequential batches.
ToolExecution ExecutionMode
// MaxParallelTools bounds a parallel batch; zero means
// agenttool.DefaultMaxParallel.
MaxParallelTools int
// ToolRecorder, when set, is agenttool.Executor.Recorder for every
// batch: installed on each call's context, so a tool that writes a
// record while it runs with agenttool.WriteRecord reaches the host
// without the host threading a context through Prompt. A session
// recorder offers one as session.Recorder.RecordFunc. nil leaves a
// recorder already on the run's context in place and installs
// nothing else, so under nil a tool's WriteRecord is a no-op.
ToolRecorder agenttool.RecordFunc
// MaxTurns stops a run after this many turns with ReasonStopped;
// zero means no limit.
MaxTurns int
// Filter drops app-only items before each model call and returns
// the conversation the model sees. nil means [DefaultFilter].
Filter func(Transcript) Transcript
// Transform runs before each model call on the whole transcript and
// may return a shorter or otherwise edited one for that call only:
// prune, compact, inject context. It receives a copy of the slice
// and must not mutate the items. The working transcript is not
// replaced.
Transform func(context.Context, Transcript) (Transcript, error)
// BeforeToolCall runs once per call, in the model's order, before any
// call of the batch executes. A nil decision allows the call.
// Several policies are joined with [ChainBeforeToolCall], which
// folds their decisions deny over ask over allow; assigning the
// field twice keeps only the second policy.
//
// One call at a time reaches it, a nested call made with [Invoke]
// from a tool's own goroutine included, so a policy may keep state
// without a lock of its own. Calling Invoke from inside the hook
// waits for the hook to return, which it cannot do.
BeforeToolCall func(context.Context, ToolCallInfo) (*ToolDecision, error)
// AfterToolCall runs when a call completes and may replace its
// result. A nil override keeps the result. An override replaces the
// result before tool_end is delivered and before the output is
// appended, so no subscriber and no recorder sees what the tool
// returned: bytes cut here exist nowhere afterwards. A cap on tool
// output therefore belongs in the tool, the only place the whole
// output exists, which can cut the middle so both ends survive and
// leave the full bytes where the model can read them, naming that
// place in the text it returns; a Transform, which shapes one call
// and never replaces the transcript, is the other placement that
// keeps the record whole. This hook is for a policy on the result
// the model sees, not for saving space. One result at a time
// reaches it, whichever goroutine finished the call, as for
// BeforeToolCall.
AfterToolCall func(context.Context, ToolResultInfo) (*ToolOverride, error)
// ShouldStopAfterTurn ends the run after a turn even when the model
// requested tools: true ends it with ReasonStopped and StopHook. An
// error wrapping [ErrGuard] ends it with ReasonStopped, StopGuard
// and the error on RunEnd.Err, so a policy that stops a run is told
// apart from a failure; any other error ends it with ReasonError.
//
// A guard chain and a token budget both want this field. Join them
// with [ChainShouldStopAfterTurn], which stops at the first hook
// that stops the run, so the error on RunEnd.Err is that hook's and
// says which one fired.
ShouldStopAfterTurn func(context.Context, TurnInfo) (bool, error)
// RequestExtra is passed through as Request.Extra on every call.
RequestExtra map[string]any
}
Config describes an agent. The zero value is not usable: Model is required.
func (Config) BaseRequest ¶
func (c Config) BaseRequest(ctx context.Context) openresponses.Request
BaseRequest returns the request every turn starts from: Config.Request with the loop-owned transport members set (store false, stream true, no previous_response_id), ModelName, Instructions, Reasoning and Text applied over it, RequestExtra merged over its Extra, and the tool definitions of the moment. Only Input is added by the loop; a recorder uses this to write the initial settings.
func (Config) ResolveTools ¶ added in v0.0.4
ResolveTools returns the tools of the moment: ToolProvider's answer when it is set, Tools otherwise. The loop consults it once per turn, before the model call; a front that builds its own request uses it so the provider fallback lives in one place.
type Event ¶
type Event interface {
EventType() string
}
Event is one step of a run. Concrete types are RunStart, TurnStart, ModelRetry, ModelBlocked, ItemStart, ItemUpdate, ItemEnd, ResponseEnd, ToolStart, ToolDispatch, ToolUpdate, ToolEnd, TurnEnd and RunEnd, and Queued, which belongs to no run. Decoded values are pointers, so switch on *ItemUpdate and so on.
type ExecutionMode ¶
type ExecutionMode int
ExecutionMode selects how a batch of tool calls runs.
const ( // ExecParallel runs the calls of a batch concurrently, bounded by // [Config.MaxParallelTools], unless a tool in the batch is // agenttool.Sequential. ExecParallel ExecutionMode = iota // ExecSequential runs every batch one call at a time in the model's // order. ExecSequential )
type ItemEnd ¶
type ItemEnd struct {
RunID string
Turn int
Item openresponses.Item
// ResponseID is the ID of the response that produced the item, and
// empty for an item the loop appended itself.
ResponseID string
// Hidden is set for an item the caller marked with [Hidden]: it is
// in the model's context and a renderer should not show it. A
// session recorder writes its entry with visible false.
Hidden bool
}
ItemEnd carries a completed item. For an assistant item it is emitted only after output_item.done; partial items never arrive here. The item is in the transcript when this event is delivered.
type ItemStart ¶
type ItemStart struct {
RunID string
Turn int
Item openresponses.Item
// ResponseID is the ID of the response streaming the item, and empty
// for an item the loop appended itself.
ResponseID string
// Hidden is set for an item the caller marked with [Hidden]: it is
// in the model's context and a renderer should not show it. The
// Item is the item itself, unwrapped.
Hidden bool
}
ItemStart announces an item entering the transcript: a prompt or queued message, an assistant item as the stream opens it, or a function call output. For an assistant item the Item is the live accumulated value and fills in as updates arrive.
An item the model completes before its attempt commits, a reasoning summary before the first token, is announced here and reaches the transcript when the answer begins or the response arrives (see Retry). An attempt that ends without its response drops what it held, so a front that renders from item_start drops what it was rendering when a ModelRetry, or a run_end with an error or an abort, follows with no item_end for it.
type ItemUpdate ¶
type ItemUpdate struct {
RunID string
Turn int
Item openresponses.Item
Stream openresponses.StreamEvent
ResponseID string
}
ItemUpdate carries one wire event for an assistant item. Stream is the openresponses event verbatim, so a front switches on the same concrete types it would use against a remote server. Item is the accumulated item so far.
func (*ItemUpdate) EventType ¶
func (*ItemUpdate) EventType() string
EventType returns "item_update".
type Model ¶
type Model = openresponses.Streamer
Model is anything that can stream an Open Responses response.
type ModelBlocked ¶ added in v0.0.6
type ModelBlocked struct {
RunID string
Turn int
Request openresponses.Request
Err error
}
ModelBlocked reports that Config.BeforeModelCall refused the turn's request, so no call was made: Request is the request as built when the hook ran and Err is the hook's error. It is the last event before the run ends with ReasonError, in place of the turn_start the call would have had, so a recorder can write the call that was refused as distinct from one that was made and failed.
func (*ModelBlocked) EventType ¶ added in v0.0.6
func (*ModelBlocked) EventType() string
EventType returns "model_blocked".
type ModelRetry ¶ added in v0.0.4
type ModelRetry struct {
RunID string
Turn int
// Attempt is the number of the attempt that failed, from 1; the
// next attempt is Attempt+1.
Attempt int
Err error
Delay time.Duration
// Request is what the next attempt will send: the turn's request,
// or what [Retry.Revise] made of it. A recorder settles on it as it
// does on turn_start, so a fallback to another model reaches the
// path as a config delta.
Request openresponses.Request
}
ModelRetry reports that a model call failed and will be attempted again after Delay, under Config.Retry. It follows the turn_start of the turn; the failed attempt never began an answer, and anything it completed on the way was dropped with it.
func (*ModelRetry) EventType ¶ added in v0.0.4
func (*ModelRetry) EventType() string
EventType returns "model_retry".
type Option ¶
type Option func(*Agent)
Option configures a new Agent.
func WithTranscript ¶
func WithTranscript(t Transcript) Option
WithTranscript starts the agent from an existing transcript, as when resuming a session. Function calls anywhere in it that have no output are pending, with PendingUnknown as their reason since the loop cannot say whether they ran, as they would be after the run that made them: Prompt and Continue return ErrInputRequired until Agent.Resume has answered them, or a Prompt opens with their outputs.
type OutputInfo ¶ added in v0.0.6
type OutputInfo struct {
RunID string
Turn int
ResponseID string
// Message is the message as the model produced it.
Message *openresponses.Message
}
OutputInfo describes an assistant message the stream has completed, for Config.OutputGuard.
type PendingCall ¶ added in v0.0.6
type PendingCall struct {
Call *openresponses.FunctionCall
Reason PendingReason
}
PendingCall is a function call with no output and the reason it has none.
type PendingReason ¶ added in v0.0.6
type PendingReason string
PendingReason says why a call has no output.
const ( // PendingDeferred: BeforeToolCall handed the call to the caller and // nothing has answered it. The tool did not run. PendingDeferred PendingReason = "deferred" // PendingAborted: the run was aborted or failed while the call was // in flight, after its tool_start. The tool may have run to // completion, so its side effect may have happened. PendingAborted PendingReason = "aborted" // PendingUnknown: the call was found without an output in a // transcript the agent was seeded with, so the loop cannot say // whether it ran. A session recorded with dispatch entries can. PendingUnknown PendingReason = "unknown" )
Pending reasons.
type QueueMode ¶ added in v0.0.7
type QueueMode string
QueueMode says which queue an item was accepted into.
const ( // QueueSteer is [Agent.Steer]: the item joins the run after the // current tool batch, before the next model call. QueueSteer QueueMode = "steer" // QueueFollowUp is [Agent.FollowUp]: the item joins the run when it // would otherwise end. QueueFollowUp QueueMode = "follow_up" )
Queue modes.
type Queued ¶ added in v0.0.7
type Queued struct {
RunID string
Item openresponses.Item
Mode QueueMode
// Hidden is set for an item the caller marked with [Hidden].
Hidden bool
}
Queued reports an item Agent.Steer or Agent.FollowUp accepted into a queue, before any run appends it, so a host writing what it accepted can tell an item it was given from one a run produced. It belongs to no run: the goroutine that owns delivery reports it at its next event, which is why it is not the accept itself and a subscriber's error cannot refuse the item.
RunID names the run that was in flight when the item was accepted, and is empty when the agent was idle.
type Reason ¶
type Reason string
Reason says why a run ended.
const ( // ReasonDone means the model produced a final answer with no tool // calls and no queued follow-ups. ReasonDone Reason = "done" // ReasonStopped means the loop chose not to call the model again: // ShouldStopAfterTurn, a terminating tool result, MaxTurns or a // refusal on Resume. RunEnd.Cause says which. ReasonStopped Reason = "stopped" // ReasonInputRequired means BeforeToolCall deferred one or more // calls to the caller; RunEnd.Pending lists them and the run // continues once their outputs are appended (see Agent.Resume). ReasonInputRequired Reason = "input_required" // ReasonAborted means the context was cancelled. RunEnd.Err is // context.Cause, the reason [Agent.AbortCause] or the host's own // context gave, and context.Canceled when there was none. A tool // batch cut off by the abort leaves its calls on RunEnd.Pending. ReasonAborted Reason = "aborted" // ReasonError means the model, a hook or a subscriber failed; // RunEnd.Err says which. ReasonError Reason = "error" )
Run end reasons.
type ResponseEnd ¶
type ResponseEnd struct {
RunID string
Turn int
Response *openresponses.Response
}
ResponseEnd carries the folded response of a turn, usage included, as soon as the stream ends and before any tool of the turn runs. Its output items have all been delivered with item_end. A response that failed is delivered here too, before the run ends with the error, so a recorder can write it.
func (*ResponseEnd) EventType ¶
func (*ResponseEnd) EventType() string
EventType returns "response_end".
type Retry ¶ added in v0.0.4
type Retry struct {
// MaxAttempts is the number of attempts per turn, the first
// included. Zero or one means no retry.
MaxAttempts int
// Backoff returns how long to wait before the next attempt, given
// the number of the attempt that failed and its error. nil means
// [DefaultBackoff].
Backoff func(attempt int, err error) time.Duration
// Retryable reports whether err is worth another attempt. nil means
// [DefaultRetryable].
Retryable func(error) bool
// Revise, when set, may change the request the next attempt sends:
// another model, a lower effort, a smaller max_output_tokens. It is
// called after the attempt numbered attempt failed with err, with a
// copy of the request that failed; it may edit that copy in place
// and return nil, or return a request of its own. The copy shares
// the slices and maps of the original, so a hook that changes the
// input or the tools builds a new one rather than appending to
// what it was given.
//
// The revised request is on the [ModelRetry] event, and a session
// recorder takes its settings, so a fallback to another model is a
// config delta on the path and the record names the model that
// answered rather than the one that did not. A fallback chain
// written as a Streamer under the loop still works and still says
// nothing.
Revise func(attempt int, req *openresponses.Request, err error) *openresponses.Request
}
Retry says when a failed model call is attempted again. A retry happens inside the turn: the same request is sent again after a delay, a ModelRetry event tells subscribers, and the turn_start and response_end of the turn are delivered once.
Only an attempt that has not committed is retried. An attempt commits when the model begins its answer, which is a message or a function call item opening, or when the server answers with a failed response; after that a failure is final, because the transcript or a recorder may already hold part of the answer. An error event the server sends before the answer opens is a failed attempt like a cut stream, and is retried as one. An item the model completed before that point, the reasoning summary a reasoning model writes before its first token, is held rather than appended: it reaches subscribers as item_start and item_update, so a front renders thinking live, and it is appended with its item_end when the answer begins or when the response arrives. Only an attempt that ends without its response, a transport failure, a cut stream or a failed response, drops what it held. A 503 between the reasoning summary and the first token is therefore retried and leaves nothing in the transcript or the record, where before it was final and left a transcript ending in a reasoning item that no server accepts as input before a user message. Abort cuts a delay short.
type RunEnd ¶
type RunEnd struct {
RunID string
Items Transcript
Reason Reason
// Cause says what stopped the run when Reason is ReasonStopped, and
// is empty otherwise.
Cause StopCause
// Err is set when Reason is ReasonError; to context.Cause of the
// run's context when Reason is ReasonAborted, which is the cause
// [Agent.AbortCause] or the host's own context carried and
// context.Canceled when there was none, wrapping the failure of a
// subscriber or a hook when one failed for a reason of its own
// while the run was being aborted; and to the guard's error when
// Reason is ReasonStopped with Cause StopGuard.
Err error
// Pending lists the function calls in the transcript with no
// function_call_output, in transcript order, each with why: the
// calls a deferred decision handed to the caller when Reason is
// ReasonInputRequired, and the calls an abort or a failure cut off
// before their outputs were appended. It is empty for ReasonDone
// and ReasonStopped. The transcript is a valid input again once
// each has an output, which Agent.Resume appends.
Pending []PendingCall
}
RunEnd closes a run. Exactly one is emitted per run and nothing follows it. Items are the items the run appended to the transcript. A run refused before it started (ErrNoPrompt, ErrCannotContinue, ErrNoModel) is one RunEnd with ReasonError, an empty RunID and no other event.
type RunStart ¶
RunStart opens a run. Source says whether the run answers pending calls or begins from input; Trigger is what the caller attached to the context with ContextWithTrigger, zero when nothing was.
type Source ¶ added in v0.0.6
type Source string
Source says what a run began from, in the terms the session format records: an input, or the answers to calls an earlier run left pending.
const ( // SourceInput is a run that begins with new input, or with nothing: // Prompt, Continue, and the low-level Run and Continue. SourceInput Source = "input" // SourceResume is a run that begins by answering a call that was // pending when it started: Resume, and a Prompt that opens with the // outputs of the pending calls. SourceResume Source = "resume" )
Run sources.
type State ¶
type State struct {
// Transcript is a copy of the slice; items are shared.
Transcript Transcript
Running bool
RunID string
Turn int
// Steering and FollowUps count the queued items; Steered and Queued
// are the items themselves, copies of the queues, so a host can
// persist what it accepted and queue it again after a restart.
Steering int
FollowUps int
Steered openresponses.Items
Queued openresponses.Items
// Pending lists the calls awaiting outputs, each with why: deferred,
// cut off by an abort or a failure, or found unanswered in a seeded
// transcript. Continue refuses until Resume has answered them, and
// Prompt unless it opens with their outputs.
Pending []PendingCall
}
State is a snapshot of the agent.
type StopCause ¶ added in v0.0.6
type StopCause string
StopCause says what ended a run with ReasonStopped.
const ( // StopMaxTurns: Config.MaxTurns was reached with tools still being // called. StopMaxTurns StopCause = "max_turns" // StopHook: ShouldStopAfterTurn returned true. StopHook StopCause = "hook" // StopGuard: ShouldStopAfterTurn returned an error wrapping // [ErrGuard]; the error is on RunEnd.Err. StopGuard StopCause = "guard" // StopTerminate: every result of the batch set Terminate, so the // tools answered on the model's behalf. StopTerminate StopCause = "terminate" // StopPartialTerminate: some results of the batch set Terminate and // others did not. The run ends so the host can act on the call that // asked to end it; the other calls ran and their outputs are in the // transcript. StopPartialTerminate StopCause = "partial_terminate" // StopRefused: an answer built with [Refuse] ended the run instead // of calling the model. StopRefused StopCause = "refused" )
Stop causes.
type ToolAction ¶ added in v0.0.4
type ToolAction int
ToolAction is what BeforeToolCall decides for a call.
const ( // Allow runs the call. It is the zero value, so a decision that only // rewrites Args or sets Terminate allows the call. Allow ToolAction = iota // Block refuses the call. The model sees ToolDecision.Reason as the // error output. Block // Defer hands the call to the caller instead of running it: the // other calls of the batch proceed, no output is appended for this // one, and the run ends with ReasonInputRequired listing it. The // caller appends the output later and continues. Defer )
type ToolCallInfo ¶
type ToolCallInfo struct {
RunID string
Turn int
// Call is the function_call item as the model produced it.
Call *openresponses.FunctionCall
// Tool is the tool that will run, or nil when no tool has that
// name; the call then fails with an error output unless the hook
// blocks it first.
Tool agenttool.Tool
// Args are the arguments as raw JSON.
Args json.RawMessage
// Batch is every call of the turn in the model's order and Index is
// this call's position in it. BeforeToolCall runs for each call of
// the batch, in order, before any call executes, so a hook that
// defers one call can defer the rest of the batch and hold all of
// it for the answer.
Batch []*openresponses.FunctionCall
Index int
}
ToolCallInfo describes a call before it runs.
type ToolDecision ¶
type ToolDecision struct {
// Action allows, blocks or defers the call.
Action ToolAction
// Reason is the message the model sees when Action is Block, and
// the reason a session recorder writes on the decision whatever the
// action: which rule raised the prompt for a Defer, which one
// refused the call for a Block. The model never sees the reason of
// a deferred call; it is for the record and for the front that asks
// the user.
Reason string
// Terminate hints the loop to stop after the batch, as a tool result
// would. It composes with Allow and Block.
Terminate bool
// Args, when non-nil, replaces the arguments the tool receives.
Args json.RawMessage
// By names who decided, for the record: the session format knows
// "human" for a person the hook waited on, "policy" for a rule it
// evaluated on its own and "agent" for another model. Empty is read
// as policy for a decision about a call nothing was holding, and as
// nobody for the approval of a held call, where [Answer.By] is what
// says who answered. The loop does not use it.
By string
// Note is text the model sees with the result: it is appended after
// the batch's outputs as a developer message, so the model reads the
// result and the note together, in that order, in the same turn. It
// applies to an allowed call; a blocked call carries its Reason.
Note string
}
ToolDecision is a hook's verdict on a call.
type ToolDispatch ¶ added in v0.0.9
ToolDispatch reports that a call has been handed to its tool: it has taken a slot in the batch's bound and its turn in its chain, and the tool is about to run. tool_start says the call was decided; this says it started, which for a batch wider than the bound or serial by a tool's request is later, and for a call cut off in between never. It is where a session recorder writes the dispatch entry, so a call cut off before it reads as never started and one cut off after as possibly run. It is raised on the call's own goroutine, serialised with the run's events, and a subscriber that fails on it stops the call: it ends with that error and the tool does not run, so a dispatch that could not be made durable is never followed by a side effect the record cannot see. Parent is set for a nested call.
func (*ToolDispatch) EventType ¶ added in v0.0.9
func (*ToolDispatch) EventType() string
EventType returns "tool_dispatch".
type ToolEnd ¶
type ToolEnd struct {
RunID string
Turn int
CallID string
Name string
Result agenttool.Result
Err error
Blocked bool
Deferred bool
// Parent is the ID of the call whose tool made this one with
// [Invoke], and empty for a call the model made.
Parent string
}
ToolEnd carries a finished call, in completion order. Err is set when the tool failed, the arguments were invalid or no tool had the name; Blocked is set when BeforeToolCall refused the call. In both cases Result holds the error output the model sees. Deferred is set when BeforeToolCall handed the call to the caller: nothing ran, Result is empty and no output is appended. A call cancelled by an abort ends with the context error as Err; its output is not appended either, and the call is listed on RunEnd.Pending.
type ToolOverride ¶
ToolOverride replaces the result of a call.
type ToolResultInfo ¶
type ToolResultInfo struct {
RunID string
Turn int
Call *openresponses.FunctionCall
Tool agenttool.Tool
Args json.RawMessage
Result agenttool.Result
Err error
}
ToolResultInfo describes a completed call.
type ToolStart ¶
type ToolStart struct {
RunID string
Turn int
CallID string
Name string
Args json.RawMessage
Decision *ToolDecision
// Parent is the ID of the call whose tool made this one with
// [Invoke], and empty for a call the model made. A nested call has
// no function_call item in the transcript: it is the work of the
// call that made it.
Parent string
}
ToolStart announces a tool call after preflight, in the model's order. Args are the arguments the tool receives, which a decision may have rewritten; the function_call item in the transcript keeps the model's. Decision is what BeforeToolCall returned for the call, nil when there was no hook or it returned nil; for a call approved through Agent.Resume it carries the caller's arguments, when they gave any, and nothing else.
type ToolUpdate ¶
ToolUpdate carries progress from a running tool.
func (*ToolUpdate) EventType ¶
func (*ToolUpdate) EventType() string
EventType returns "tool_update".
type Transcript ¶
type Transcript = openresponses.Items
Transcript is the conversation as the model sees it, plus app-only items that the Config.Filter removes before each call.
func DefaultFilter ¶
func DefaultFilter(t Transcript) Transcript
DefaultFilter drops every item whose type carries a slug prefix such as "agentturn:note", which marks an app-only extension item, and nil items. It is the Filter when Config.Filter is nil.
func TranscriptFromContext ¶ added in v0.0.4
func TranscriptFromContext(ctx context.Context) (Transcript, bool)
TranscriptFromContext returns the transcript the loop attached to the context of a hook or a tool call: the working transcript as it stood when the batch started, the calls of the batch included. It is a snapshot; do not mutate it.
type Trigger ¶ added in v0.0.6
Trigger names what caused a run, in the caller's own terms: a cron job, a channel message, a user's turn. The loop learns nothing from it; it carries the value from ContextWithTrigger to RunStart so a recorder can write it. Kind is the category and Ref the instance; a recorder joins them as "kind:ref" when both are set.
func TriggerFromContext ¶ added in v0.0.6
TriggerFromContext returns the trigger attached to ctx, or the zero Trigger.
type TurnEnd ¶
type TurnEnd struct {
RunID string
Turn int
Response *openresponses.Response
ToolResults []agenttool.Result
}
TurnEnd closes a turn with the folded response, usage included, and the tool results in the model's order.
type TurnInfo ¶
type TurnInfo struct {
RunID string
Turn int
Response *openresponses.Response
// ToolResults are the results of this turn's calls in the model's
// order, empty when the model called no tools.
ToolResults []agenttool.Result
// Final is set when the model called no tools, so the response is
// the run's answer unless a queued follow-up keeps it going.
Final bool
// Transcript is the working transcript after the turn. It is the
// loop's live slice; do not mutate it or keep it past the hook.
Transcript Transcript
}
TurnInfo describes a finished turn.
type TurnStart ¶
type TurnStart struct {
RunID string
Turn int
Request openresponses.Request
Inputs openresponses.Items
}
TurnStart opens a turn and carries the exact request sent to the model. Inputs are the items appended to the transcript since the previous turn's response, or since the run started for the first turn: the tool outputs, the steered and queued messages, the items BeforeTurn added. They are what this turn's response answers, so a front that routes replies to the message that caused them reads them here rather than counting item events between turns.
type TurnStartInfo ¶ added in v0.0.6
type TurnStartInfo struct {
RunID string
Turn int
// Transcript is the working transcript as the turn starts. It is the
// loop's live slice; do not mutate it or keep it past the hook.
Transcript Transcript
}
TurnStartInfo describes a turn about to start, for Config.BeforeTurn.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package compact is the reference compaction Transform for agentturn: when a transcript grows past a token budget, the older part is folded into a shorter form and the recent part is kept verbatim.
|
Package compact is the reference compaction Transform for agentturn: when a transcript grows past a token budget, the older part is folded into a shorter form and the recent part is kept verbatim. |
|
front
|
|
|
responses
Package responses serves an agent as an Open Responses server: the loop as an openresponses.Adapter.
|
Package responses serves an agent as an Open Responses server: the loop as an openresponses.Adapter. |
|
a2a
module
|
|
|
session
module
|
|
|
tools
|
|
|
agent
Package agent wraps an agent configuration as a tool, so one loop can delegate to another in process.
|
Package agent wraps an agent configuration as a tool, so one loop can delegate to another in process. |
|
a2a
module
|