Documentation
¶
Overview ¶
Package exp provides experimental AI primitives for Genkit.
APIs in this package are under active development and may change in any minor version release.
Index ¶
- Variables
- func ApplyPatch(document any, patch JSONPatch) (any, error)
- func NewSessionContext[State any](ctx context.Context, s *Session[State]) context.Context
- func ValidateResumeAgainstHistory(resume *ToolResume, history []*ai.Message) error
- type Agent
- func DefineAgent[State any](r api.Registry, name string, prompt InlinePrompt, opts ...AgentOption[State]) *Agent[State]
- func DefineCustomAgent[State any](r api.Registry, name string, fn AgentFunc[State], opts ...AgentOption[State]) *Agent[State]
- func DefinePromptAgent[State any](r api.Registry, name string, opts ...PromptAgentOption[State]) *Agent[State]
- func NewCustomAgent[State any](name string, fn AgentFunc[State], opts ...AgentOption[State]) *Agent[State]
- func (a *Agent[State]) Abort(ctx context.Context, snapshotID string) (SnapshotStatus, error)
- func (a *Agent[State]) AbortAction() api.Action
- func (a *Agent[State]) Connect(ctx context.Context, opts ...InvocationOption[State]) (*AgentConnection[State], error)
- func (a *Agent[State]) ConnectJSON(ctx context.Context, opts *api.BidiJSONOptions) (api.BidiJSONConnection, error)
- func (a *Agent[State]) Desc() api.ActionDesc
- func (a *Agent[State]) GetLatestSnapshot(ctx context.Context, sessionID string, opts ...SnapshotReadOption) (*SessionSnapshot[State], error)
- func (a *Agent[State]) GetSnapshot(ctx context.Context, snapshotID string, opts ...SnapshotReadOption) (*SessionSnapshot[State], error)
- func (a *Agent[State]) GetSnapshotAction() api.Action
- func (a *Agent[State]) Handle() *AgentHandle
- func (a *Agent[State]) Name() string
- func (a *Agent[State]) Ref() AgentRef
- func (a *Agent[State]) Register(r api.Registry)
- func (a *Agent[State]) Run(ctx context.Context, input *AgentInput, opts ...InvocationOption[State]) (*AgentOutput[State], error)
- func (a *Agent[State]) RunBidiJSON(ctx context.Context, input json.RawMessage, ...) (*api.ActionRunResult[json.RawMessage], error)
- func (a *Agent[State]) RunDetached(ctx context.Context, input *AgentInput, opts ...InvocationOption[State]) (*DetachedTask[State], error)
- func (a *Agent[State]) RunJSON(ctx context.Context, input json.RawMessage, ...) (json.RawMessage, error)
- func (a *Agent[State]) RunJSONWithTelemetry(ctx context.Context, input json.RawMessage, ...) (*api.ActionRunResult[json.RawMessage], error)
- func (a *Agent[State]) RunText(ctx context.Context, text string, opts ...InvocationOption[State]) (*AgentOutput[State], error)
- func (a *Agent[State]) Store() SessionStore[State]
- func (a *Agent[State]) Task(snapshotID string) *DetachedTask[State]
- func (a *Agent[State]) WaitForSnapshot(ctx context.Context, snapshotID string) (*SessionSnapshot[State], error)
- func (a *Agent[State]) WaitForSnapshotAction() api.Action
- type AgentAbortRequest
- type AgentAbortResponse
- type AgentConnection
- func (c *AgentConnection[State]) Close() error
- func (c *AgentConnection[State]) Custom() (State, error)
- func (c *AgentConnection[State]) Detach() error
- func (c *AgentConnection[State]) Done() <-chan struct{}
- func (c *AgentConnection[State]) Output() (*AgentOutput[State], error)
- func (c *AgentConnection[State]) Receive() iter.Seq2[*AgentStreamChunk, error]
- func (c *AgentConnection[State]) Send(input *AgentInput) error
- func (c *AgentConnection[State]) SendMessage(message *ai.Message) error
- func (c *AgentConnection[State]) SendResume(resume *ToolResume) error
- func (c *AgentConnection[State]) SendText(text string) error
- type AgentFinishReason
- type AgentFunc
- type AgentHandle
- func (h *AgentHandle) Abort(ctx context.Context, snapshotID string) (SnapshotStatus, error)
- func (h *AgentHandle) GetLatestSnapshot(ctx context.Context, sessionID string, opts ...SnapshotReadOption) (*SessionSnapshot[json.RawMessage], error)
- func (h *AgentHandle) GetSnapshot(ctx context.Context, snapshotID string, opts ...SnapshotReadOption) (*SessionSnapshot[json.RawMessage], error)
- func (h *AgentHandle) Metadata() *AgentMetadata
- func (h *AgentHandle) Name() string
- func (h *AgentHandle) Run(ctx context.Context, input *AgentInput, ...) (*AgentOutput[json.RawMessage], error)
- func (h *AgentHandle) RunDetached(ctx context.Context, input *AgentInput, ...) (*DetachedTask[json.RawMessage], error)
- func (h *AgentHandle) RunText(ctx context.Context, text string, opts ...InvocationOption[json.RawMessage]) (*AgentOutput[json.RawMessage], error)
- func (h *AgentHandle) Task(snapshotID string) *DetachedTask[json.RawMessage]
- func (h *AgentHandle) WaitForSnapshot(ctx context.Context, snapshotID string) (*SessionSnapshot[json.RawMessage], error)
- type AgentInit
- type AgentInput
- type AgentMetadata
- type AgentOption
- func WithDescription[State any](description string) AgentOption[State]
- func WithSessionStore[State any](store SessionStore[State]) AgentOption[State]
- func WithStateTransform[State any](transform StateTransform[State]) AgentOption[State]
- func WithStreamTransform[State any](transform StreamTransform) AgentOption[State]
- type AgentOutput
- type AgentRef
- type AgentResult
- type AgentStateManagement
- type AgentStreamChunk
- type Artifact
- type ArtifactStore
- type DetachedTask
- func (t *DetachedTask[State]) Abort(ctx context.Context) (SnapshotStatus, error)
- func (t *DetachedTask[State]) Poll(ctx context.Context, opts ...SnapshotReadOption) (*SessionSnapshot[State], error)
- func (t *DetachedTask[State]) SnapshotID() string
- func (t *DetachedTask[State]) Wait(ctx context.Context) (*SessionSnapshot[State], error)
- type GetSnapshotRequest
- type InlinePrompt
- type InterruptibleTool
- type InterruptibleToolFunc
- type InvocationOption
- type JSONPatch
- type JSONPatchOp
- type JSONPatchOperation
- type PromptAgentOption
- type Responder
- type Session
- func (s *Session[State]) AddArtifacts(artifacts ...*Artifact)
- func (s *Session[State]) AddMessages(messages ...*ai.Message)
- func (s *Session[State]) Artifacts() []*Artifact
- func (s *Session[State]) Custom() State
- func (s *Session[State]) Messages() []*ai.Message
- func (s *Session[State]) SessionID() string
- func (s *Session[State]) SetMessages(messages []*ai.Message)
- func (s *Session[State]) State() *SessionState[State]
- func (s *Session[State]) UpdateArtifacts(fn func([]*Artifact) []*Artifact)
- func (s *Session[State]) UpdateCustom(fn func(State) State)
- func (s *Session[State]) UpdateMessages(fn func([]*ai.Message) []*ai.Message)
- type SessionRunner
- type SessionSnapshot
- type SessionState
- type SessionStore
- type SnapshotMetadataReader
- type SnapshotReadOption
- type SnapshotReader
- type SnapshotStatus
- type SnapshotSubscriber
- type SnapshotWriter
- type StateTransform
- type StreamTransform
- type Tool
- func (t *Tool[In, Out]) Definition() *ai.ToolDefinition
- func (t *Tool[In, Out]) Name() string
- func (t *Tool[In, Out]) Register(r api.Registry)
- func (t *Tool[In, Out]) Respond(toolReq *ai.Part, outputData any, opts *ai.RespondOptions) *ai.Part
- func (t *Tool[In, Out]) Restart(toolReq *ai.Part, opts *ai.RestartOptions) *ai.Part
- func (t *Tool[In, Out]) RunRaw(ctx context.Context, input any) (any, error)
- func (t *Tool[In, Out]) RunRawMultipart(ctx context.Context, input any) (*ai.MultipartToolResponse, error)
- type ToolFunc
- type ToolResume
- type TurnContext
- type TurnEnd
- type TurnResult
Constants ¶
This section is empty.
Variables ¶
var ( // ErrSnapshotNotFound means no snapshot exists under the given ID, or the // session has none yet. ErrSnapshotNotFound = status.ErrNotFound.Subtype("snapshot not found") // ErrSessionStoreNotConfigured means the operation needs server-managed // state but the agent was defined without WithSessionStore. It describes a // gap in how the agent was set up, not anything the caller sent, which is // why it is a failed precondition rather than an invalid argument. ErrSessionStoreNotConfigured = status.ErrFailedPrecondition.Subtype("session store not configured") // ErrSessionIDRequired means a snapshot reached a store without a session ID. // Every store implementation rejects this: a snapshot with no session cannot // be resolved back to a conversation. ErrSessionIDRequired = status.ErrInvalidArgument.Subtype("session ID is required") )
Failure modes agents and session stores report. Match them with errors.Is.
Functions ¶
func ApplyPatch ¶
ApplyPatch applies an RFC 6902 JSON Patch to document and returns the new value. The input is not mutated; a normalized clone is patched and returned. Operating on the root pointer ("") replaces or removes the whole document.
It is lenient to keep streaming robust: an add or replace whose parent container is missing initializes it as an object, and a remove or replace of a missing member is a no-op. A test operation is honored and returns an error on mismatch. Other unknown operations also return an error.
func NewSessionContext ¶
NewSessionContext returns a new context with the session attached.
It also publishes a type-erased view of the session's custom state so prompt rendering can inject it into templates as {{@state}}. go/ai cannot import this package (this package imports go/ai), so the custom state is exposed through a getter in internal/base, evaluated at render time so templates see the latest values.
func ValidateResumeAgainstHistory ¶
func ValidateResumeAgainstHistory(resume *ToolResume, history []*ai.Message) error
ValidateResumeAgainstHistory ensures every restart and respond entry on a resume payload references a tool request that is actually pending, so a caller cannot drive a tool the model never asked for and interrupted on. For restart entries it additionally checks the input is unchanged from the original request, preventing a client from forging tool inputs on the interrupted call.
Only the most recent model message in history is searched, and within it newest-first. Resume handling resolves exactly that response's tool requests, so an entry aimed at an earlier turn does nothing downstream; worse, because refs are only made unique within a single response, a tool name + ref recurs across turns, and searching further back would let a client validate a restart whose input was forged from an already-settled call. An entry naming an earlier turn is rejected as stale, separately from one naming a tool request that never existed. On a violation it returns an INVALID_ARGUMENT error.
The prompt-backed agent loop (DefineAgent) calls this automatically. A custom agent (DefineCustomAgent) that accepts an AgentInput.Resume from untrusted callers should call it before forwarding the payload to the model:
if input.Resume != nil {
if err := ValidateResumeAgainstHistory(input.Resume, sess.Messages()); err != nil {
return nil, err
}
}
Types ¶
type Agent ¶
type Agent[State any] struct { // contains filtered or unexported fields }
Agent is a bidirectional streaming agent with automatic snapshot management.
Agent implements api.BidiAction, so generic transports accept it directly (e.g. pass it to genkit.Handler to serve it over HTTP, one turn per request). Agent.Run, Agent.RunText, and Agent.Connect are typed conveniences over the same underlying action.
Server-managed agents (those with a SessionStore configured) also register companion actions for the snapshot lifecycle, available via Agent.GetSnapshotAction, Agent.WaitForSnapshotAction, and Agent.AbortAction for serving alongside the agent, and expose the store itself via Agent.Store.
func DefineAgent ¶
func DefineAgent[State any]( r api.Registry, name string, prompt InlinePrompt, opts ...AgentOption[State], ) *Agent[State]
DefineAgent defines an agent backed by an inline prompt and registers it. The prompt is defined from prompt's ai.PromptOption values and registered under the agent's name; each turn renders it, appends conversation history, calls the model with streaming, and updates session state.
The prompt is an InlinePrompt, a list of ai.PromptOption values:
agent := DefineAgent(r, "pirate",
InlinePrompt{
ai.WithModelName("googleai/gemini-flash-latest"),
ai.WithSystem("You are a sarcastic pirate."),
},
WithSessionStore(store),
)
State is inferred from the typed agent options (e.g. WithSessionStore, WithStateTransform); pass an explicit [State] only when no typed option is provided. A typed option that disagrees with the inferred State fails at compile time.
To back an agent with a prompt already in the registry (e.g. one from a .prompt file), use DefinePromptAgent. For full control over the per-turn loop, use DefineCustomAgent.
func DefineCustomAgent ¶
func DefineCustomAgent[State any]( r api.Registry, name string, fn AgentFunc[State], opts ...AgentOption[State], ) *Agent[State]
DefineCustomAgent defines an agent with full control over the conversation loop and registers it (and any companion actions) with the registry. fn receives a Responder for streaming output and a SessionRunner for turn and state management; call SessionRunner.Run to enter the per-turn loop.
It is NewCustomAgent followed by Agent.Register. To build an agent without registering it, use NewCustomAgent directly. For agents backed by a prompt, use DefineAgent instead.
func DefinePromptAgent ¶
func DefinePromptAgent[State any]( r api.Registry, name string, opts ...PromptAgentOption[State], ) *Agent[State]
DefinePromptAgent defines a prompt-backed agent and registers it, sourcing its prompt from the registry by name. Each turn renders the prompt, appends conversation history, calls the model with streaming, and updates session state, exactly like DefineAgent.
By default the agent uses the prompt registered under its own name (e.g. one defined via ai.DefinePrompt or loaded from a .prompt file), so no source option is required. Pass WithNamedPrompt to reference a differently named prompt and supply its render input from code, so a single prompt can back many agents.
It is the registry-backed counterpart of DefineAgent: where DefineAgent defines the prompt inline, DefinePromptAgent points at a prompt already in the registry. The prompt source is a typed option (WithNamedPrompt) rather than a positional argument, so it composes with the other agent options (WithSessionStore, WithStateTransform, WithStreamTransform, WithDescription) in a single variadic. For full control over the per-turn loop, use DefineCustomAgent.
State is inferred from the typed agent options; pass an explicit [State] only when no typed option provides it (e.g. only WithNamedPrompt and WithDescription, whose State cannot be deduced from their arguments).
func NewCustomAgent ¶
func NewCustomAgent[State any]( name string, fn AgentFunc[State], opts ...AgentOption[State], ) *Agent[State]
NewCustomAgent creates an agent with full control over the conversation loop without registering it. Register it later with the registry (e.g. genkit.RegisterAction), which also registers its companion actions; see Agent.Register. fn receives a Responder for streaming output and a SessionRunner for turn and state management; call SessionRunner.Run to enter the per-turn loop.
This is the agent counterpart of core.NewStreamingActionOf: use it when the agent must outlive or precede a registry (e.g. built in a library, registered conditionally, or moved between registries). For the common case, DefineCustomAgent creates and registers in one step.
There is no NewAgent for prompt-backed agents: a prompt is bound to the registry it renders against, so it cannot be built before one exists. For prompt-like behavior without registration, render and generate with your own [genkit.Genkit] inside a custom fn.
func (*Agent[State]) Abort ¶
Abort aborts the detached invocation behind a pending snapshot by flipping it to SnapshotStatusAborting; the runtime observes the flip, cancels the background work, and lands the row as SnapshotStatusAborted with its state once the work drains. A caller that has only a store (no agent) aborts through the abort companion action instead. It is a no-op on a missing snapshot (returns "") or an already-settled one (returns the existing status), and idempotent on a row already aborting.
It returns FAILED_PRECONDITION on a client-managed agent and INVALID_ARGUMENT when snapshotID is empty.
func (*Agent[State]) AbortAction ¶
AbortAction returns the agent's abort companion action, which asks the background work behind a pending snapshot (e.g. a detached invocation) to stop (input AgentAbortRequest, output AgentAbortResponse). It returns nil when the agent has no SessionStore or the store does not implement SnapshotSubscriber.
Use it to expose aborting over a transport (e.g. mount it with genkit.Handler next to the agent itself); local Go code aborts with Agent.Abort; a store-only caller uses this companion action.
func (*Agent[State]) Connect ¶
func (a *Agent[State]) Connect( ctx context.Context, opts ...InvocationOption[State], ) (*AgentConnection[State], error)
Connect starts a new agent invocation with bidirectional streaming. Use this for multi-turn interactions where you need to send multiple inputs and receive streaming chunks. For single-turn usage, see Run and RunText; for a single turn that keeps working after the call returns, RunDetached.
func (*Agent[State]) ConnectJSON ¶
func (a *Agent[State]) ConnectJSON(ctx context.Context, opts *api.BidiJSONOptions) (api.BidiJSONConnection, error)
ConnectJSON starts a bidirectional streaming session using JSON-encoded messages. Local Go callers should prefer the typed Agent.Connect.
func (*Agent[State]) Desc ¶
func (a *Agent[State]) Desc() api.ActionDesc
Desc returns the descriptor of the agent's run action.
func (*Agent[State]) GetLatestSnapshot ¶
func (a *Agent[State]) GetLatestSnapshot(ctx context.Context, sessionID string, opts ...SnapshotReadOption) (*SessionSnapshot[State], error)
GetLatestSnapshot fetches a session's most recently created snapshot (whatever its status) through the agent, with the same transform, shaping, and SnapshotReadOption projections as Agent.GetSnapshot. It is the transform-applying counterpart to SnapshotReader.GetLatestSnapshot and backs resume-by-session lookups.
It returns FAILED_PRECONDITION on a client-managed agent and INVALID_ARGUMENT when sessionID is empty; an unknown session is NOT_FOUND.
func (*Agent[State]) GetSnapshot ¶
func (a *Agent[State]) GetSnapshot(ctx context.Context, snapshotID string, opts ...SnapshotReadOption) (*SessionSnapshot[State], error)
GetSnapshot fetches a session snapshot by ID through the agent, applying the configured WithStateTransform and the same read-time shaping the getSnapshot companion action performs (a pending or aborting row whose heartbeat went stale is surfaced as SnapshotStatusExpired; an empty status or zero UpdatedAt is defaulted). Prefer it to reading Agent.Store directly, which returns raw, untransformed state. Pass WithMetadataOnly to read the shaped metadata without the state.
It returns FAILED_PRECONDITION on a client-managed agent (no store) and INVALID_ARGUMENT when snapshotID is empty; a missing snapshot is NOT_FOUND.
func (*Agent[State]) GetSnapshotAction ¶
GetSnapshotAction returns the agent's getSnapshot companion action, which fetches a session snapshot by ID (input GetSnapshotRequest, output SessionSnapshot). It returns nil when the agent is client-managed (no SessionStore configured): there is no server-side snapshot to fetch.
Use it to expose snapshot polling over a transport (e.g. mount it with genkit.Handler next to the agent itself); local Go code should use Agent.GetSnapshot, which applies the configured state transform.
func (*Agent[State]) Handle ¶ added in v1.13.0
func (a *Agent[State]) Handle() *AgentHandle
Handle returns the agent's untyped caller-side view: the same surface LookupAgent builds for callers that hold only the agent's name. Use it to hand the agent to code written against AgentHandle (orchestration helpers, middleware) without exposing the typed Agent. It performs no registry lookup, so it also works for an unregistered agent (see NewCustomAgent).
func (*Agent[State]) Name ¶
Name returns the agent's registered name. This is also the name under which any inline-defined prompt and companion actions (getSnapshot, waitForSnapshot, abort) are registered.
func (*Agent[State]) Ref ¶
Ref returns an AgentRef for this agent, capturing its name and description so callers can reference it without restating either, and without a name string that can drift from the agent. Resolution remains by name, so the agent must be registered (as DefineAgent does) wherever the ref is used.
func (*Agent[State]) Register ¶
Register registers the agent's run action and any companion actions (getSnapshot, waitForSnapshot, abort) with the registry. Agents defined via DefineAgent or DefineCustomAgent are already registered; this exists so an agent can travel to another registry as a unit. An inline-defined prompt does not travel: the agent holds it directly, so execution is unaffected, but the prompt action stays in the registry it was defined in.
func (*Agent[State]) Run ¶
func (a *Agent[State]) Run( ctx context.Context, input *AgentInput, opts ...InvocationOption[State], ) (*AgentOutput[State], error)
Run starts a single-turn agent invocation with the given input. It sends the input, waits for the agent to complete, and returns the output. For multi-turn interactions or streaming, use Connect instead.
In-band failures (e.g. a failed turn) resolve as a failed AgentOutput rather than an error; a rejected init payload fails with an error, since the invocation never starts. See AgentConnection.Output.
func (*Agent[State]) RunBidiJSON ¶
func (a *Agent[State]) RunBidiJSON(ctx context.Context, input json.RawMessage, cb func(context.Context, json.RawMessage) error, opts *api.BidiJSONOptions) (*api.ActionRunResult[json.RawMessage], error)
RunBidiJSON runs a one-shot invocation whose session init (the wire counterpart of the InvocationOption values) rides in opts: input is delivered as the only chunk on the input stream and outgoing chunks are forwarded to cb.
func (*Agent[State]) RunDetached ¶ added in v1.13.0
func (a *Agent[State]) RunDetached( ctx context.Context, input *AgentInput, opts ...InvocationOption[State], ) (*DetachedTask[State], error)
RunDetached launches input as a detached (background) invocation and returns the task tracking it. It is the one-shot counterpart of AgentConnection.Detach: the input is delivered with AgentInput.Detach set, whatever the caller set it to; the runtime persists a pending snapshot and keeps working on a context decoupled from ctx; and the returned DetachedTask polls, waits on, or aborts that snapshot, with custom state typed as State. The pending snapshot is the durable record: record DetachedTask.SnapshotID and rehydrate with Agent.Task to pick the work up later, including from another process.
The launch is rejected with FAILED_PRECONDITION when the agent cannot support detach: it has no session store, or the store does not implement SnapshotSubscriber. The rejection is the invocation's failed output (AgentOutput.Error) surfaced as the returned error; match it by status.
An agent may also settle the invocation synchronously, before the runtime observes the detach directive (e.g. a custom agent whose fn returns without consuming the input). When a turn committed, RunDetached returns a task over its snapshot, which is already terminal, so Poll and Wait resolve immediately; when nothing was recorded, it fails with FAILED_PRECONDITION naming the finish reason, since there is no durable record to track.
func (*Agent[State]) RunJSON ¶
func (a *Agent[State]) RunJSON(ctx context.Context, input json.RawMessage, cb func(context.Context, json.RawMessage) error) (json.RawMessage, error)
RunJSON runs a one-shot invocation with no init (a fresh session): input is the turn's AgentInput and the result is the final AgentOutput. To supply a session source, use Agent.RunBidiJSON.
func (*Agent[State]) RunJSONWithTelemetry ¶
func (a *Agent[State]) RunJSONWithTelemetry(ctx context.Context, input json.RawMessage, cb func(context.Context, json.RawMessage) error) (*api.ActionRunResult[json.RawMessage], error)
RunJSONWithTelemetry is Agent.RunJSON with trace information on the result.
func (*Agent[State]) RunText ¶
func (a *Agent[State]) RunText( ctx context.Context, text string, opts ...InvocationOption[State], ) (*AgentOutput[State], error)
RunText is a convenience method that starts a single-turn agent invocation with a user text message. It is equivalent to calling Run with an AgentInput whose Message is a user text message.
func (*Agent[State]) Store ¶
func (a *Agent[State]) Store() SessionStore[State]
Store returns the SessionStore the agent was configured with via WithSessionStore, or nil when the agent is client-managed (no store).
For reads and aborts prefer the typed facade Agent.GetSnapshot, Agent.GetLatestSnapshot, and Agent.Abort: they apply the configured WithStateTransform and read-time shaping. Store exposes the raw backend for advanced use; a direct SnapshotReader.GetSnapshot returns untransformed state.
The store is returned as the SessionStore interface, not its concrete type; a caller needing a store-specific capability (e.g. SnapshotSubscriber) type-asserts for it.
func (*Agent[State]) Task ¶ added in v1.13.0
func (a *Agent[State]) Task(snapshotID string) *DetachedTask[State]
Task returns the task tracking a snapshot ID recorded earlier (e.g. by a prior process that called Agent.RunDetached), with custom state typed as State. It performs no I/O and does not verify the snapshot exists; the first DetachedTask.Poll or DetachedTask.Wait surfaces NOT_FOUND for an unknown ID.
func (*Agent[State]) WaitForSnapshot ¶ added in v1.13.0
func (a *Agent[State]) WaitForSnapshot(ctx context.Context, snapshotID string) (*SessionSnapshot[State], error)
WaitForSnapshot fetches a session snapshot by ID and blocks until it settles, returning the terminal snapshot with the same transform and shaping as Agent.GetSnapshot. An already-terminal snapshot returns at once. It is how an owner follows a detached invocation to its end; a caller holding only the agent's name waits through AgentHandle.WaitForSnapshot instead.
A snapshot that failed, aborted, or expired is returned like any other, so a non-nil error means the wait itself could not proceed: reads failed past the wait's transient-retry budget, or ctx ended and its error is returned. Bound the wait with context.WithTimeout and read the snapshot afterwards to learn where it stands.
It returns FAILED_PRECONDITION on a client-managed agent (no store) and INVALID_ARGUMENT when snapshotID is empty; a missing snapshot is NOT_FOUND.
func (*Agent[State]) WaitForSnapshotAction ¶ added in v1.13.0
WaitForSnapshotAction returns the agent's waitForSnapshot companion action, which resolves a snapshot the same way getSnapshot does and returns once it settles (input GetSnapshotRequest, output SessionSnapshot). It returns nil when the agent is client-managed (no SessionStore configured).
Use it to expose following a detached invocation over a transport (e.g. mount it with genkit.Handler next to the agent itself), so a remote caller waits in one request instead of a read per tick; local Go code should use Agent.WaitForSnapshot, which applies the configured state transform.
type AgentAbortRequest ¶
type AgentAbortRequest struct {
// SnapshotID identifies the snapshot whose invocation should be aborted.
SnapshotID string `json:"snapshotId"`
}
AgentAbortRequest is the input for the abort companion action.
type AgentAbortResponse ¶
type AgentAbortResponse struct {
// SnapshotID identifies the snapshot the abort attempt targeted.
SnapshotID string `json:"snapshotId"`
// Status is the snapshot's status after the abort attempt. For a pending
// snapshot this is [SnapshotStatusAborting]: the flip that stops the work
// landed, and the finalize that stamps the state and settles the row as
// [SnapshotStatusAborted] follows. For an already-settled snapshot this is
// the existing status (the abort is a no-op), and for a row already aborting
// it is [SnapshotStatusAborting] again.
Status SnapshotStatus `json:"status,omitempty"`
}
AgentAbortResponse is the output of the abort companion action.
type AgentConnection ¶
type AgentConnection[State any] struct { // contains filtered or unexported fields }
AgentConnection is an active agent invocation with bidirectional streaming, adding agent-specific Send helpers (SendMessage, SendText, SendResume, Detach) over the core connection.
It also tracks custom state live: as AgentConnection.Receive yields chunks, it applies each chunk's AgentStreamChunk.CustomPatch to an internal copy, exposed by AgentConnection.Custom, so callers see custom state as it streams without applying patches themselves.
func (*AgentConnection[State]) Close ¶
func (c *AgentConnection[State]) Close() error
Close signals that no more inputs will be sent.
func (*AgentConnection[State]) Custom ¶
func (c *AgentConnection[State]) Custom() (State, error)
Custom returns the conversation's custom state as tracked from the streamed patches observed via AgentConnection.Receive. It reflects the deltas consumed so far, so reading it as a turn streams shows the live state; before any patch arrives it returns the zero value. The authoritative final state is on AgentOutput.State (client-managed) or the turn-end snapshot (server-managed).
func (*AgentConnection[State]) Detach ¶
func (c *AgentConnection[State]) Detach() error
Detach asks the server to write a pending snapshot, close the connection, and continue processing any already-buffered inputs in the background. Output() returns the pending snapshot ID; the client can later call Abort to stop the background work or GetSnapshot to observe its progression. The pending snapshot is finalized with the cumulative final state once the queued inputs are processed.
Chunks emitted after detach are not forwarded over the wire, but their session-level side effects still apply: an artifact sent via Responder.SendArtifact still lands in the final snapshot's state.
To send a final input in the same wire message, call Send(&AgentInput{Detach: true, Message: ...}) directly.
func (*AgentConnection[State]) Done ¶
func (c *AgentConnection[State]) Done() <-chan struct{}
Done returns a channel closed when the connection completes.
func (*AgentConnection[State]) Output ¶
func (c *AgentConnection[State]) Output() (*AgentOutput[State], error)
Output finalizes the connection and returns the agent's result. It closes the input side, drains any chunks not consumed via Receive, and blocks until the agent finalizes. It is idempotent: later calls return the same value, and the returned pointer is shared, so treat it as read-only.
In-band failures resolve rather than error. A failed turn returns an AgentOutput with AgentFinishReasonFailed, the error on AgentOutput.Error, and the last-good state on AgentOutput.State (client-managed) or behind AgentOutput.SnapshotID (server-managed), so a failure costs only the failed turn, not the session. A detached invocation resolves with the pending snapshot ID.
A run the caller stopped returns both: the error that stopped it, and an AgentOutput with AgentFinishReasonAborted naming the same resume point, the way ai.Generate hands back a partial response beside its error. Check the output even when the error is non-nil, or the work up to the stop is stranded. It is nil when the invocation never started (a rejected init payload), and when an agent function that ignores its context has not settled within settleGrace, since there is nothing yet to report.
Do not call Output concurrently with a goroutine iterating Receive; both consume the stream and would split chunks between them. Finish Receive first.
func (*AgentConnection[State]) Receive ¶
func (c *AgentConnection[State]) Receive() iter.Seq2[*AgentStreamChunk, error]
Receive returns an iterator for receiving stream chunks. Breaking out of the iterator does not cancel the connection; multi-turn callers routinely break on TurnEnd, send the next input, then call Receive again to consume the next batch. Call AgentConnection.Output to finish the invocation, or cancel the ctx passed to Connect to abort it.
Each yielded chunk's AgentStreamChunk.CustomPatch is applied to the connection's tracked custom state before the chunk is yielded, so AgentConnection.Custom reflects every delta observed so far.
func (*AgentConnection[State]) Send ¶
func (c *AgentConnection[State]) Send(input *AgentInput) error
Send sends an AgentInput to the agent. The input must not be nil.
Once the invocation has resolved (e.g. a failed turn ended it), Send fails with an error matching core.ErrActionCompleted; the outcome is on AgentConnection.Output. The same applies to the SendMessage, SendText, SendResume, and Detach helpers.
func (*AgentConnection[State]) SendMessage ¶
func (c *AgentConnection[State]) SendMessage(message *ai.Message) error
SendMessage sends a message to the agent for one turn.
func (*AgentConnection[State]) SendResume ¶
func (c *AgentConnection[State]) SendResume(resume *ToolResume) error
SendResume sends a resume payload to continue an interrupted generation. Construct the payload with ai.ToolAction.RestartWith or ai.ToolAction.RespondWith parts.
func (*AgentConnection[State]) SendText ¶
func (c *AgentConnection[State]) SendText(text string) error
SendText sends a user text message to the agent.
type AgentFinishReason ¶
type AgentFinishReason string
AgentFinishReason is why an agent turn or invocation finished.
The first group mirrors the model-level ai.FinishReason so a turn backed by a single generate call can forward its reason verbatim: AgentFinishReasonStop, AgentFinishReasonLength, AgentFinishReasonBlocked, AgentFinishReasonInterrupted, AgentFinishReasonOther and AgentFinishReasonUnknown.
The remaining values are agent-specific outcomes that never arise from forwarding a model finish reason: AgentFinishReasonAborted, AgentFinishReasonDetached and AgentFinishReasonFailed. The model-level namesakes of aborted and failed accompany generate errors, whose turns the agent reports as failed rather than forwarding a reason.
const ( // AgentFinishReasonStop indicates the model stopped naturally. AgentFinishReasonStop AgentFinishReason = "stop" // AgentFinishReasonLength indicates generation hit the token limit. AgentFinishReasonLength AgentFinishReason = "length" // AgentFinishReasonBlocked indicates generation was blocked (e.g. safety). AgentFinishReasonBlocked AgentFinishReason = "blocked" // AgentFinishReasonInterrupted indicates the model paused on a tool request // awaiting input (e.g. human approval). The turn can be resumed by sending an // [AgentInput] with a Resume payload. AgentFinishReasonInterrupted AgentFinishReason = "interrupted" // AgentFinishReasonOther indicates generation stopped for some other reason. AgentFinishReasonOther AgentFinishReason = "other" // AgentFinishReasonUnknown indicates the reason was unspecified. AgentFinishReasonUnknown AgentFinishReason = "unknown" // AgentFinishReasonAborted indicates the turn or invocation was aborted // (e.g. a detached invocation aborted via the abort companion action). AgentFinishReasonAborted AgentFinishReason = "aborted" // AgentFinishReasonDetached indicates the invocation was moved to the // background because the client detached. The returned [AgentOutput] reports // this reason; the persisted snapshot is later finalized with how the // background work actually ended. AgentFinishReasonDetached AgentFinishReason = "detached" // AgentFinishReasonFailed indicates the turn or invocation terminated with an // error. AgentFinishReasonFailed AgentFinishReason = "failed" )
func (AgentFinishReason) CarriesResult ¶ added in v1.13.0
func (r AgentFinishReason) CarriesResult() bool
CarriesResult reports whether a turn that ended for this reason produced an answer its caller can use. True for the reasons that mean the agent spoke and stopped: AgentFinishReasonStop, the two catch-alls, and the empty reason a turn may report when it names none. Every other reason ended the turn with nothing to hand back.
Callers use it to decide whether a turn's final message is the answer or an account of why there is none. That question has one answer per reason, so it belongs on the reason: an agent that reports a turn's outcome and one that folds it into a tool result would otherwise each keep their own list, and a reason added later would be an answer to one of them and not the other.
It names the reasons that do carry a result rather than the ones that do not, so a reason added later defaults to "no answer". That default is the safe one: mistaking an explanation for an answer hands a caller partial work as though it were final, while the reverse only asks it to look at the text.
type AgentFunc ¶
type AgentFunc[State any] = func(ctx context.Context, resp Responder, sess *SessionRunner[State]) (*AgentResult, error)
AgentFunc is the function signature for custom agents. The State type parameter is the shape of the conversation's custom state (see SessionState.Custom). The agent streams output through resp and reads or mutates state through sess; mutating custom state via Session.UpdateCustom automatically streams a AgentStreamChunk.CustomPatch delta to the client.
type AgentHandle ¶ added in v1.13.0
type AgentHandle struct {
// contains filtered or unexported fields
}
AgentHandle is the untyped caller-side view of an agent: one turn, plus the snapshot lifecycle behind a detached one (read, follow, abort), addressed with the custom-state type fixed to json.RawMessage. It is how code that does not hold the defining Agent value calls an agent it knows only by name: orchestrators, middleware, and tools resolve one with LookupAgent (or the genkit/exp package's LookupAgent), and a typed owner hands one out with Agent.Handle.
A handle adds no capability over the agent it names; it only removes the wire plumbing (JSON marshaling, companion-action lookup and dispatch) that name-resolved callers would otherwise repeat. Custom state on every surface is json.RawMessage: unmarshal SessionState.Custom into the agent's own state type when the caller knows it.
Reaching the agent is an [agentTransport]'s job, so where the agent lives is not part of this surface. Both constructors bind the in-process transport today; the seam is what will let one bind an agent behind an HTTP endpoint without moving a method.
func LookupAgent ¶ added in v1.13.0
func LookupAgent(r api.Registry, name string) *AgentHandle
LookupAgent resolves the agent registered under name and returns its AgentHandle, or nil when name resolves to no agent. It is the caller-side counterpart of DefineAgent and friends: definition hands back the typed Agent, and a later caller that holds only the name recovers this untyped view, companion actions included. Callers holding a genkit instance should use the genkit/exp package's LookupAgent instead.
Nil covers both misses, matching every other Lookup in the framework: no action under the agent key, and an action registered there that is not an agent. Neither is worth distinguishing to a caller, because the answer to both is the same and the caller knows the name it asked for. Guard the result, or call AgentHandle.Run and let its nil check report it.
func (*AgentHandle) Abort ¶ added in v1.13.0
func (h *AgentHandle) Abort(ctx context.Context, snapshotID string) (SnapshotStatus, error)
Abort asks the background work behind a pending snapshot to stop, through the agent's abort companion action, and returns the snapshot's status after the attempt: SnapshotStatusAborting when the row was pending (the stop landed, and the row settles as SnapshotStatusAborted once the worker's finalize stamps the state on) or already aborting, or the existing terminal status (the abort was a no-op) when it had already settled. A missing snapshot is NOT_FOUND, matching the companion action remote callers use (unlike Agent.Abort, which reports it as "").
It returns FAILED_PRECONDITION when the agent has no session store (ErrSessionStoreNotConfigured) or the store cannot observe aborts (no SnapshotSubscriber; see AgentMetadata.Abortable), and INVALID_ARGUMENT when snapshotID is empty.
func (*AgentHandle) GetLatestSnapshot ¶ added in v1.13.0
func (h *AgentHandle) GetLatestSnapshot(ctx context.Context, sessionID string, opts ...SnapshotReadOption) (*SessionSnapshot[json.RawMessage], error)
GetLatestSnapshot fetches a session's most recently created snapshot (whatever its status) through the agent's getSnapshot companion action, with the same shaping and the same SnapshotReadOption projections as AgentHandle.GetSnapshot. It is Agent.GetLatestSnapshot with custom state as raw JSON.
It returns FAILED_PRECONDITION (ErrSessionStoreNotConfigured) when the agent has no session store and INVALID_ARGUMENT when sessionID is empty; an unknown session is NOT_FOUND.
func (*AgentHandle) GetSnapshot ¶ added in v1.13.0
func (h *AgentHandle) GetSnapshot(ctx context.Context, snapshotID string, opts ...SnapshotReadOption) (*SessionSnapshot[json.RawMessage], error)
GetSnapshot fetches a session snapshot by ID through the agent's getSnapshot companion action, so the read is shaped exactly as remote callers see it: the configured WithStateTransform applies and a stale-heartbeat pending row surfaces as SnapshotStatusExpired. It is Agent.GetSnapshot with custom state as raw JSON. Pass WithMetadataOnly to read the shaped metadata without loading the state.
It returns FAILED_PRECONDITION (ErrSessionStoreNotConfigured) when the agent has no session store and INVALID_ARGUMENT when snapshotID is empty; a missing snapshot is NOT_FOUND.
func (*AgentHandle) Metadata ¶ added in v1.13.0
func (h *AgentHandle) Metadata() *AgentMetadata
Metadata returns the agent's capability metadata (who manages state, whether background work can be aborted), or nil when the agent's descriptor carries none or did not decode. The handle holds one copy, detached from the descriptor but shared across calls, so treat it as read-only.
func (*AgentHandle) Name ¶ added in v1.13.0
func (h *AgentHandle) Name() string
Name returns the agent's registered name.
func (*AgentHandle) Run ¶ added in v1.13.0
func (h *AgentHandle) Run(ctx context.Context, input *AgentInput, opts ...InvocationOption[json.RawMessage]) (*AgentOutput[json.RawMessage], error)
Run starts a single-turn invocation with the given input and returns the final output, with custom state as raw JSON. It is Agent.Run for callers holding only the handle; the InvocationOption values instantiate at json.RawMessage (e.g. WithSessionID[json.RawMessage](id)).
In-band failures (e.g. a failed turn) resolve as a failed AgentOutput rather than an error, exactly like Agent.Run; a non-nil error means the invocation never started (a rejected init payload) or could not run to a result.
func (*AgentHandle) RunDetached ¶ added in v1.13.0
func (h *AgentHandle) RunDetached(ctx context.Context, input *AgentInput, opts ...InvocationOption[json.RawMessage]) (*DetachedTask[json.RawMessage], error)
RunDetached launches input as a detached (background) invocation and returns the task tracking it. It is Agent.RunDetached for callers holding only the handle, with the task's snapshots read as raw JSON: the input is delivered with AgentInput.Detach set whatever the caller set it to, a launch the agent cannot support (no session store, or one without SnapshotSubscriber; check AgentMetadata.Abortable to pre-flight) fails with FAILED_PRECONDITION, and an invocation the agent settled before observing the detach yields a task over its committed snapshot.
The rejection is decoded from the invocation's failed output, which keeps only the status name, so match it with status.Of rather than errors.Is.
func (*AgentHandle) RunText ¶ added in v1.13.0
func (h *AgentHandle) RunText(ctx context.Context, text string, opts ...InvocationOption[json.RawMessage]) (*AgentOutput[json.RawMessage], error)
RunText is AgentHandle.Run with a user text message as the input, the way Agent.RunText is for Agent.Run.
func (*AgentHandle) Task ¶ added in v1.13.0
func (h *AgentHandle) Task(snapshotID string) *DetachedTask[json.RawMessage]
Task returns the task tracking a snapshot ID recorded earlier (e.g. by a prior process that called AgentHandle.RunDetached), with custom state as raw JSON. It performs no I/O and does not verify the snapshot exists; the first DetachedTask.Poll or DetachedTask.Wait surfaces NOT_FOUND for an unknown ID.
func (*AgentHandle) WaitForSnapshot ¶ added in v1.13.0
func (h *AgentHandle) WaitForSnapshot(ctx context.Context, snapshotID string) (*SessionSnapshot[json.RawMessage], error)
WaitForSnapshot fetches a session snapshot by ID and blocks until it settles, through the agent's waitForSnapshot companion action, returning the terminal snapshot shaped exactly as AgentHandle.GetSnapshot shapes a read. An already-terminal snapshot returns at once. It is Agent.WaitForSnapshot with custom state as raw JSON, and it is how a caller that holds only actions follows a detached invocation: one dispatch for the whole wait, rather than a read per tick.
A snapshot that failed, aborted, or expired is returned like any other, so a non-nil error means the wait itself could not proceed: reads failed past the wait's transient-retry budget, or ctx ended and its error is returned. Bound the wait with context.WithTimeout, then call AgentHandle.GetSnapshot to learn where the task stands.
It returns FAILED_PRECONDITION (ErrSessionStoreNotConfigured) when the agent has no session store and INVALID_ARGUMENT when snapshotID is empty; a missing snapshot is NOT_FOUND.
type AgentInit ¶
type AgentInit[State any] struct { // SessionID identifies the session (conversation) to resume or start. Only // valid when the agent is server-managed (a session store is configured); // mutually exclusive with State. Alone, it resumes the session's latest // snapshot, rejected only if that snapshot is [SnapshotStatusPending] and so // still being written to. A [SnapshotStatusFailed] or [SnapshotStatusAborted] // snapshot resumes with what the last turn to finish committed. If the session // has no snapshots yet, a fresh conversation // starts under this caller-chosen ID. Combined with SnapshotID, it asserts // the snapshot belongs to that session. SessionID string `json:"sessionId,omitempty"` // SnapshotID loads state from a persisted snapshot. Only valid when the // agent is server-managed (a session store is configured). May be // combined with SessionID to validate that the snapshot belongs to that // session. Mutually exclusive with State. SnapshotID string `json:"snapshotId,omitempty"` // State provides direct state for the invocation. Only valid when the agent // is client-managed (no session store). The conversation's identity rides // inside it ([SessionState.SessionID]). Mutually exclusive with SessionID and // SnapshotID. State *SessionState[State] `json:"state,omitempty"` }
AgentInit is the input for starting an agent invocation. SessionID, SnapshotID, and State are competing conversation sources, and which are valid depends on the agent's state management:
- Server-managed state (a session store is configured): callers may use SessionID (resume the session's latest snapshot), SnapshotID (resume a specific snapshot), or both (the session ID is asserted against the snapshot); sending State is rejected.
- Client-managed state (no session store): callers may use State, which carries the conversation's identity inside it (SessionState.SessionID); sending SessionID or SnapshotID is rejected.
Sending no fields starts a fresh invocation with empty state.
type AgentInput ¶
type AgentInput struct {
// Detach signals the client will disconnect after this input is accepted. The
// server writes a pending snapshot, returns [AgentOutput] with its ID, and
// keeps processing any already-buffered inputs in the background. The pending
// snapshot is finalized with the cumulative final state once the queued inputs
// are processed (or the invocation is aborted).
Detach bool `json:"detach,omitempty"`
// Message is the user's input for this turn.
Message *ai.Message `json:"message,omitempty"`
// Resume provides options for resuming an interrupted generation.
// Construct using [ai.ToolAction.RestartWith] / [ai.ToolAction.RespondWith]
// parts. When set, the generate call resumes with these parts instead
// of treating Message as a tool response.
Resume *ToolResume `json:"resume,omitempty"`
}
AgentInput is the input sent to an agent during a conversation turn.
An input with neither Message nor Resume continues the conversation from where it stands, which is how a failed turn is re-attempted: resume the SnapshotStatusFailed snapshot, send an empty input, and the agent runs the turn again on the messages it committed. It is rejected on a conversation with no messages to continue.
type AgentMetadata ¶
type AgentMetadata struct {
// Abortable reports whether the agent's invocations can be aborted
// (true when the store implements [SnapshotSubscriber]).
Abortable bool `json:"abortable,omitempty"`
// StateManagement reports who owns session state.
StateManagement AgentStateManagement `json:"stateManagement,omitempty"`
// StateSchema is the JSON schema for the agent's custom session state
// (the Custom field of [SessionState]), inferred from the agent's state
// type. It lets the Dev UI and other reflective callers render or
// validate state without the agent describing it separately. Nil when the
// state type carries no schema to infer (e.g. an unstructured any state).
StateSchema map[string]any `json:"stateSchema,omitempty"`
}
AgentMetadata is the value placed under metadata["agent"] on an agent's action descriptor. It exposes capability information so the Dev UI and other reflective callers can render the right surface (e.g. hide the Abort button when the store doesn't support it).
type AgentOption ¶
type AgentOption[State any] interface { // contains filtered or unexported methods }
AgentOption configures an agent at definition time. It is accepted by DefineAgent, DefineCustomAgent, and DefinePromptAgent as a typed variadic, so a State mismatch fails at compile time.
Every AgentOption is also a PromptAgentOption: the shared options (WithSessionStore, WithStateTransform, WithStreamTransform, WithDescription) configure all three constructors. The converse does not hold. A prompt-source option such as WithNamedPrompt is a PromptAgentOption but not an AgentOption, so passing it to DefineAgent or DefineCustomAgent is a compile-time error.
func WithDescription ¶
func WithDescription[State any](description string) AgentOption[State]
WithDescription sets a human-readable description of the agent, stored on its action descriptor (read back via Agent.Desc and shown in the Dev UI).
func WithSessionStore ¶
func WithSessionStore[State any](store SessionStore[State]) AgentOption[State]
WithSessionStore sets the store for persisting snapshots. The store must implement SnapshotReader and SnapshotWriter at minimum. Detach support also requires SnapshotSubscriber; detach attempts on a store that lacks that interface are rejected at runtime.
func WithStateTransform ¶
func WithStateTransform[State any](transform StateTransform[State]) AgentOption[State]
WithStateTransform registers a StateTransform applied to session state on its way out to a client (via the getSnapshot companion action or AgentOutput.State). Typical uses are PII redaction and stripping secrets.
func WithStreamTransform ¶
func WithStreamTransform[State any](transform StreamTransform) AgentOption[State]
WithStreamTransform registers a StreamTransform applied to every AgentStreamChunk on its way out to a client. Typical uses are redacting streamed model tokens and stripping fields a client should not receive; it is the stream-side counterpart to WithStateTransform.
A chunk is not parameterized by the state type, so State cannot be inferred from the transform and must be supplied explicitly to match the agent's, e.g. WithStreamTransform[MyState](fn).
type AgentOutput ¶
type AgentOutput[State any] struct { // Artifacts contains artifacts produced during the session. Artifacts []*Artifact `json:"artifacts,omitempty"` // Error is the structured failure information when the invocation ended in // failure (FinishReason is [AgentFinishReasonFailed]). Its Status preserves // the original error category (e.g. INVALID_ARGUMENT, FAILED_PRECONDITION, // INTERNAL) so callers can still branch on it. Nil otherwise. Error *status.Error `json:"error,omitempty"` // FinishReason is why the invocation finished. It is // [AgentFinishReasonDetached] when the client detached and the work continues // in the background, or [AgentFinishReasonFailed] when the invocation ended // in failure (see [AgentOutput.Error]); otherwise it is the last turn's // reason (or the value a custom agent set on its [AgentResult]). FinishReason AgentFinishReason `json:"finishReason,omitempty"` // Message is the last model response message from the conversation. Message *ai.Message `json:"message,omitempty"` // SessionID is the ID of the session this invocation belongs to, assigned by // the framework when the invocation starts and stable across resumes. Pass it // to [WithSessionID] to resume a server-managed session; with client-managed // state it also rides inside [AgentOutput.State] ([SessionState.SessionID]). SessionID string `json:"sessionId,omitempty"` // SnapshotID is the ID of the most recent turn-end snapshot for this // invocation. Empty when no store is configured or no turn committed. When // FinishReason is [AgentFinishReasonDetached] it is the pending detach // snapshot. When [AgentFinishReasonFailed], it is the resume point: the // failed turn's own [SnapshotStatusFailed] snapshot when the turn committed // anything, otherwise the last committed turn's snapshot. SnapshotID string `json:"snapshotId,omitempty"` // State contains the final conversation state. // Only populated when state is client-managed (no store configured). // When FinishReason is [AgentFinishReasonFailed], it is the resume point: // what the failed turn committed, or the last-good state through the last // successful turn when the turn failed before committing anything. State *SessionState[State] `json:"state,omitempty"` }
AgentOutput is the output when an agent invocation completes. It wraps AgentResult with framework-managed fields.
type AgentRef ¶
type AgentRef struct {
// Name identifies the agent, resolved as /agent/<Name>. Required.
Name string `json:"name" jsonschema_description:"Name of the agent. Resolved as /agent/<Name>. Required."`
// Description is a human-readable description used by consumers that list
// agents (e.g. the agents middleware's system prompt). [Agent.Ref] fills it
// from the agent's descriptor. Optional.
Description string `` /* 172-byte string literal not displayed */
}
AgentRef refers to an agent by name, optionally carrying a description. It is the agent analog of ai.ModelRef / ai.ToolRef: a small, JSON-serializable value that names an agent for resolution against a registry. Like those, it resolves by name (the path the Dev UI, HTTP serving, and ListAgents all use), so the referenced agent must be registered wherever the ref is consumed.
Build one by name with a struct literal, or derive it from an agent value with Agent.Ref, which fills in the name and description so callers need not restate either:
aix.AgentRef{Name: "researcher"}
coderAgent.Ref()
type AgentResult ¶
type AgentResult struct {
// Artifacts contains artifacts produced during the session.
Artifacts []*Artifact `json:"artifacts,omitempty"`
// FinishReason is why the invocation finished. A custom agent may set it to
// override the default (the last turn's reason); leave it empty to accept the
// default.
FinishReason AgentFinishReason `json:"finishReason,omitempty"`
// Message is the last model response message from the conversation.
Message *ai.Message `json:"message,omitempty"`
}
AgentResult is the return value from an AgentFunc. It contains the user-specified outputs of the agent invocation.
type AgentStateManagement ¶
type AgentStateManagement string
AgentStateManagement enumerates who owns session state for an agent: "server" (a SessionStore is configured and snapshots are persisted server-side) or "client" (no store; state flows through invocation init / output).
const ( // AgentStateManagementServer indicates the agent is wired with // a [SessionStore] and persists snapshots server-side. AgentStateManagementServer AgentStateManagement = "server" // AgentStateManagementClient indicates the agent has no store; // session state is client-managed and round-trips through invocation // init and output. AgentStateManagementClient AgentStateManagement = "client" )
type AgentStreamChunk ¶
type AgentStreamChunk struct {
// Artifact contains a newly produced artifact.
Artifact *Artifact `json:"artifact,omitempty"`
// CustomPatch is an RFC 6902 JSON Patch describing a delta to the session's
// custom state, emitted automatically whenever the agent mutates it (e.g. via
// [Session.UpdateCustom]). Pointers are rooted at the custom document (e.g.
// "/agentStatus"), with no "/custom" prefix. The first patch of each turn is a
// whole-document replace at the root pointer ("") to re-base the client; later
// patches are incremental diffs. Apply it with [ApplyPatch] to keep a local
// copy of custom state live as the turn streams.
CustomPatch JSONPatch `json:"customPatch,omitempty"`
// ModelChunk contains generation tokens from the model.
ModelChunk *ai.ModelResponseChunk `json:"modelChunk,omitempty"`
// TurnEnd is non-nil when the agent has finished processing the current
// input. It groups all turn-end signals (snapshot ID, etc.) so callers can
// check a single field. When set, the client should stop iterating and may
// send the next input.
TurnEnd *TurnEnd `json:"turnEnd,omitempty"`
}
AgentStreamChunk represents a single item in the agent's output stream. Multiple fields can be populated in a single chunk.
type Artifact ¶
type Artifact struct {
// Metadata contains additional artifact-specific data.
Metadata map[string]any `json:"metadata,omitempty"`
// Name identifies the artifact (e.g., "generated_code.go", "diagram.png").
Name string `json:"name,omitempty"`
// Parts contains the artifact content (text, media, etc.).
Parts []*ai.Part `json:"parts"`
}
Artifact represents a named collection of parts produced during a session. Examples: generated files, images, code snippets, diagrams, etc.
type ArtifactStore ¶
type ArtifactStore interface {
// Artifacts returns a snapshot of the session's current artifacts.
Artifacts() []*Artifact
// AddArtifacts adds artifacts, replacing any existing artifact of the same
// name.
AddArtifacts(artifacts ...*Artifact)
}
ArtifactStore is the State-agnostic view of a session's artifact collection. Every Session satisfies it regardless of its State type, since artifact operations do not touch custom state. Middleware and tools that work with artifacts without knowing the agent's State type use it via ArtifactStoreFromContext, where SessionFromContext cannot help because it requires the concrete State.
func ArtifactStoreFromContext ¶
func ArtifactStoreFromContext(ctx context.Context) ArtifactStore
ArtifactStoreFromContext returns the active session's artifacts as a State-agnostic ArtifactStore, or nil if there is no active session in ctx. Unlike SessionFromContext it does not require knowing the session's State type, so it is the accessor for middleware and tools.
type DetachedTask ¶ added in v1.13.0
type DetachedTask[State any] struct { // contains filtered or unexported fields }
DetachedTask tracks a detached (background) agent invocation through its pending snapshot, with custom state typed as State: the agent's own state type for a task the typed Agent minted, json.RawMessage for one an AgentHandle minted. Obtain one from Agent.RunDetached or AgentHandle.RunDetached when launching, or rehydrate one from a recorded snapshot ID with Agent.Task or AgentHandle.Task; the two are equivalent, because the snapshot is the only state a task has.
func (*DetachedTask[State]) Abort ¶ added in v1.13.0
func (t *DetachedTask[State]) Abort(ctx context.Context) (SnapshotStatus, error)
Abort asks the task's background work to stop and returns the snapshot's status after the attempt, as Agent.Abort or AgentHandle.Abort reports it.
func (*DetachedTask[State]) Poll ¶ added in v1.13.0
func (t *DetachedTask[State]) Poll(ctx context.Context, opts ...SnapshotReadOption) (*SessionSnapshot[State], error)
Poll fetches the task's snapshot once, without waiting. The snapshot's Status says where the task stands: SnapshotStatusPending while the work runs, a terminal status once it settles (see SnapshotStatus.Terminal), including SnapshotStatusExpired when the worker stopped heartbeating and is presumed dead. A terminal snapshot carries the cumulative session state; a poll that only dispatches on where the task stands can pass WithMetadataOnly to skip that payload.
func (*DetachedTask[State]) SnapshotID ¶ added in v1.13.0
func (t *DetachedTask[State]) SnapshotID() string
SnapshotID returns the ID of the snapshot tracking the task: pending while the background work runs, finalized in place when it settles. It is the task's durable identity; record it to pick the task up later with Agent.Task or AgentHandle.Task.
func (*DetachedTask[State]) Wait ¶ added in v1.13.0
func (t *DetachedTask[State]) Wait(ctx context.Context) (*SessionSnapshot[State], error)
Wait blocks until the task settles and returns its terminal snapshot: the waiting happens next to the store that knows when the work finished, so the caller neither picks a cadence nor pays a read per tick. A task that failed, aborted, or expired still returns its snapshot rather than an error (inspect SessionSnapshot.Status and SessionSnapshot.Error), so a non-nil error means the wait itself could not proceed: reads failed past the wait's transient-retry budget, or ctx ended and its error is returned.
Use context.WithTimeout to bound the wait; on the deadline the wait returns ctx's error, and DetachedTask.Poll then reports where the task stands.
type GetSnapshotRequest ¶
type GetSnapshotRequest struct {
// MetadataOnly returns the snapshot's metadata only: status, finish reason,
// parent, session, timestamps, and error, with no state payload. The read is
// shaped exactly as a full read (status defaulting and heartbeat expiry need
// only the metadata), and a store that implements [SnapshotMetadataReader]
// answers without loading the state at all; any other store is read in full
// and the state dropped. For callers that dispatch on where a task stands,
// this skips serializing a potentially large conversation history.
MetadataOnly bool `json:"metadataOnly,omitempty"`
// SessionID identifies the session whose latest snapshot to fetch.
// Optional when SnapshotID is given. The latest snapshot is the session's
// most recently updated row regardless of status (pending, failed, or
// aborted included).
SessionID string `json:"sessionId,omitempty"`
// SnapshotID identifies the snapshot to fetch. Optional when SessionID is
// given; when both are present the fetched snapshot must belong to that
// session.
SnapshotID string `json:"snapshotId,omitempty"`
}
GetSnapshotRequest is the input for an agent's getSnapshot companion action, available when the agent has a session store configured. Intended for Dev UI and client reconnect flows, it returns the stored SessionSnapshot with WithStateTransform applied to its state if configured.
At least one of SnapshotID or SessionID must be set. SnapshotID fetches a specific snapshot; SessionID alone fetches the session's latest snapshot (whatever its status). When both are set, the fetched snapshot must belong to that session, or the request is rejected.
type InlinePrompt ¶
type InlinePrompt []ai.PromptOption
InlinePrompt is an inline prompt definition for an agent: the list of ai.PromptOption values that configure the agent's prompt. Pass one to DefineAgent, which registers the prompt under the agent's name:
agent := DefineAgent(r, "pirate",
InlinePrompt{
ai.WithModelName("googleai/gemini-flash-latest"),
ai.WithSystem("You are a sarcastic pirate."),
},
WithSessionStore(store),
)
Any option [genkit.DefinePrompt] accepts is valid here; see its documentation for the full list. To give the template a default render input, include ai.WithInputType among the options. For an agent backed by a prompt already in the registry (e.g. one defined via ai.DefinePrompt or loaded from a .prompt file), use DefinePromptAgent instead, which takes no InlinePrompt.
The session's conversation is handed to the prompt on every turn, under the usual placement rules: a prompt that sets ai.WithMessages, ai.WithMessagesTemplate, or ai.WithMessagesFn must place it with {{history}} or ai.HistoryFromContext, which is what lets an agent trim or summarize its own history. One that sets none of them, the common case, has it placed between the system message and the user prompt.
type InterruptibleTool ¶
InterruptibleTool is a Tool that supports typed interrupt/resume. The Res type parameter is the type of data the caller sends back when resuming the tool after an interrupt.
func NewInterruptibleTool ¶
func NewInterruptibleTool[In, Out, Res any]( name, description string, fn InterruptibleToolFunc[In, Out, Res], opts ...ai.ToolOption, ) *InterruptibleTool[In, Out, Res]
NewInterruptibleTool creates a new unregistered interruptible tool.
func (*InterruptibleTool[In, Out, Resume]) Respond ¶
Respond creates a tool response ai.Part for an interrupted tool request. Instead of re-executing the tool (as [Resume] does), this provides a pre-computed result directly.
Unlike tool.Respond, this method validates that the interrupted part belongs to this tool and accepts a strongly-typed output.
func (*InterruptibleTool[In, Out, Resume]) Resume ¶
Resume creates a restart part for resuming this interrupted tool with typed data. The data will be deserialized into the *Res parameter of the tool function when it is re-executed.
Res must serialize to a JSON object (a struct or a map), since it is carried as structured metadata on the restart part; see tool.Interrupt.
Unlike tool.Resume, this method also validates that the interrupted part belongs to this tool.
type InterruptibleToolFunc ¶
type InterruptibleToolFunc[In, Out, Resume any] = func(ctx context.Context, input In, res *Resume) (Out, error)
InterruptibleToolFunc is the function signature for tools created with [DefineInterruptibleTool] and NewInterruptibleTool. The resumed parameter is non-nil when the tool is being re-executed after an interrupt.
type InvocationOption ¶
type InvocationOption[State any] interface { // contains filtered or unexported methods }
InvocationOption configures an agent invocation (Connect, Run, or RunText).
func WithSessionID ¶
func WithSessionID[State any](id string) InvocationOption[State]
WithSessionID resumes the conversation with the given ID from its latest snapshot (see SnapshotReader.GetLatestSnapshot). Use it when the caller tracks the conversation rather than individual snapshots; the ID is assigned on the conversation's first invocation (see AgentOutput.SessionID) and stays stable across resumes.
Valid only for server-managed agents, and so mutually exclusive with WithState (a client-managed conversation carries its identity inside the state). Combined with WithSnapshotID, the snapshot picks the resume point and the session ID is validated against it.
The resume is rejected only if the latest snapshot is pending, meaning a detached invocation is still writing to it: wait for it to finalize, or abort it. A failed or aborted tip resumes with what the last turn to finish committed, and an input with no payload of its own re-attempts from there. If history was forked, the most recently created branch wins; use WithSnapshotID for a specific branch. An empty ID is an error, not a no-op.
func WithSnapshotID ¶
func WithSnapshotID[State any](id string) InvocationOption[State]
WithSnapshotID loads state from a persisted snapshot by ID. Use this for server-managed state where snapshots are stored. Combine with WithSessionID to assert which session the snapshot belongs to; a mismatch is rejected. Mutually exclusive with WithState.
func WithState ¶
func WithState[State any](state *SessionState[State]) InvocationOption[State]
WithState sets the initial state for the invocation, for client-managed agents where the client sends state directly. The conversation's identity rides inside the state (SessionState.SessionID); the framework mints it on the first invocation and echoes it on the output, so resending the state keeps the identity. The state is deep-copied at the start, so the caller may reuse the object freely. Mutually exclusive with WithSessionID and WithSnapshotID.
type JSONPatch ¶
type JSONPatch []*JSONPatchOperation
JSONPatch is an RFC 6902 JSON Patch: an ordered list of operations applied in sequence. Use Diff to compute the patch between two values and ApplyPatch to apply one to a document.
func Diff ¶
Diff computes an RFC 6902 JSON Patch that transforms from into to.
The diff is rooted at the document, so pointers are bare (e.g. "/agentStatus", "/items/0"). Only add, remove, and replace operations are emitted. Object members recurse (add for new keys, remove for deleted keys); arrays diff by index, appending with the end-of-array token "/-" and removing from the tail backwards so indices stay valid. A root-level change that cannot be expressed member-by-member (object↔array↔primitive) collapses to a single whole-document replace at path "".
Object keys are visited in sorted order, so the patch is deterministic.
type JSONPatchOp ¶
type JSONPatchOp string
JSONPatchOp is the kind of a JSON Patch operation (RFC 6902): one of JSONPatchOpAdd, JSONPatchOpRemove, JSONPatchOpReplace, JSONPatchOpMove, JSONPatchOpCopy, or JSONPatchOpTest.
const ( JSONPatchOpAdd JSONPatchOp = "add" JSONPatchOpRemove JSONPatchOp = "remove" JSONPatchOpReplace JSONPatchOp = "replace" JSONPatchOpMove JSONPatchOp = "move" JSONPatchOpCopy JSONPatchOp = "copy" JSONPatchOpTest JSONPatchOp = "test" )
type JSONPatchOperation ¶
type JSONPatchOperation struct {
// From is a JSON Pointer to the source location; required for "move" and "copy".
From string `json:"from,omitempty"`
// Op is the operation to perform.
Op JSONPatchOp `json:"op"`
// Path is a JSON Pointer (RFC 6901) to the target location, e.g. "/agentStatus".
// The empty pointer "" refers to the whole document.
Path string `json:"path"`
// Value is the operand for "add", "replace", and "test"; an explicit null is a
// valid operand. Ignored for "remove", "move", and "copy".
Value any `json:"value"`
}
JSONPatchOperation is a single RFC 6902 (JSON Patch) operation. A JSONPatch applies an ordered list of these to transform one JSON document into another.
type PromptAgentOption ¶
type PromptAgentOption[State any] interface { // contains filtered or unexported methods }
PromptAgentOption configures a prompt-backed agent defined via DefinePromptAgent. It is the wider set that additionally admits the prompt-source option WithNamedPrompt; every AgentOption satisfies it, so the shared agent options compose with WithNamedPrompt in a single variadic.
func WithNamedPrompt ¶
func WithNamedPrompt[State any](name string, input any) PromptAgentOption[State]
WithNamedPrompt points a DefinePromptAgent at the prompt registered under name, rendered with input on every turn (pass nil for the prompt's own default input). name need not match the agent's name, so a single registered prompt can back many agents with different inputs; pass "" to keep the default same-named lookup while still supplying a custom input.
Without this option a prompt agent uses the prompt registered under its own name. This option lets a single registered prompt back many agents, each rendered with its own input, and composes with the other agent options in one variadic.
input is rendered through the prompt once at definition time as a smoke check, so an input that fails the prompt's schema panics there rather than on the first invocation.
This option applies only to DefinePromptAgent. Passing it to DefineAgent or DefineCustomAgent is a compile-time error.
type Responder ¶
type Responder struct {
// contains filtered or unexported fields
}
Responder is an agent's output channel to the client. Its Send methods are fire-and-forget: they return no error, and the agent function should stop producing once its own context is cancelled.
A Send applies its session-level side effects synchronously, so a state read (SessionRunner.Result, Session.Artifacts) or turn-end snapshot taken afterward observes them. Only the forward to the client is asynchronous, and it is dropped once the work context is cancelled (client disconnect, abort, or agent completion) or the run has detached; the side effects still apply.
func (Responder) SendArtifact ¶
SendArtifact streams an artifact to the client and adds it to the session, replacing any existing artifact with the same name. The session keeps a deep copy, so later mutations of artifact do not affect session state.
func (Responder) SendModelChunk ¶
func (r Responder) SendModelChunk(chunk *ai.ModelResponseChunk)
SendModelChunk sends a generation chunk (token-level streaming).
type Session ¶
type Session[State any] struct { // contains filtered or unexported fields }
Session holds conversation state and provides thread-safe read/write access to messages, custom state, and artifacts.
func SessionFromContext ¶
SessionFromContext retrieves the current session from context. Returns nil if no session is in context or if the type doesn't match.
func (*Session[State]) AddArtifacts ¶
AddArtifacts adds artifacts to the session. If an artifact with the same name already exists, it is replaced.
func (*Session[State]) AddMessages ¶
AddMessages appends messages to the conversation history.
func (*Session[State]) Artifacts ¶
Artifacts returns the current artifacts. The returned slice is a fresh copy, but its elements point at the live artifacts held by the session: treat them as read-only, or deep-copy before mutating. Session.State returns a fully independent copy.
func (*Session[State]) Custom ¶
func (s *Session[State]) Custom() State
Custom returns the current user-defined custom state.
func (*Session[State]) Messages ¶
Messages returns the current conversation history. The returned slice is a fresh copy, but its elements point at the live messages held by the session: treat them as read-only, or deep-copy before mutating. Session.State returns a fully independent copy.
func (*Session[State]) SessionID ¶
SessionID returns the ID of the session this conversation belongs to. The agent runtime settles it before the agent function runs and keeps it stable for the invocation's lifetime, stamping it on every snapshot persisted. It is safe to use as a key for external resources tied to the conversation, including from code that retrieves the session via SessionFromContext.
func (*Session[State]) SetMessages ¶
SetMessages replaces the conversation history with the given messages.
func (*Session[State]) State ¶
func (s *Session[State]) State() *SessionState[State]
State returns a copy of the current state.
func (*Session[State]) UpdateArtifacts ¶
UpdateArtifacts atomically reads the current artifacts, applies the given function, and writes the result back. fn runs while the session's internal lock is held: it must not call other Session methods or send on a Responder, or it will deadlock.
func (*Session[State]) UpdateCustom ¶
func (s *Session[State]) UpdateCustom(fn func(State) State)
UpdateCustom atomically reads the current custom state, applies the given function, and writes the result back. fn runs while the session's internal lock is held: it must not call other Session methods or send on a Responder, or it will deadlock.
When the session is driven by an agent invocation, the mutation is streamed to the client as an AgentStreamChunk.CustomPatch describing the delta (the runtime computes and emits it after fn returns). Agents therefore just mutate state; they never hand-craft patches.
func (*Session[State]) UpdateMessages ¶
UpdateMessages atomically reads the current messages, applies the given function, and writes the result back. fn runs while the session's internal lock is held: it must not call other Session methods or send on a Responder, or it will deadlock.
type SessionRunner ¶
SessionRunner extends Session with agent-runtime functionality: turn management, snapshot persistence, and input channel handling.
func (*SessionRunner[State]) Result ¶
func (s *SessionRunner[State]) Result() *AgentResult
Result returns an AgentResult populated from the current session state: the last message in the conversation history and all artifacts. The returned value is independent of the session; callers may mutate it without affecting session state. An artifact sent through the Responder is visible here as soon as the Send call returns.
It is a convenience for custom agents that don't need to construct the result manually.
func (*SessionRunner[State]) Run ¶
func (s *SessionRunner[State]) Run(ctx context.Context, fn func(ctx context.Context, input *AgentInput) (*TurnResult, error)) error
Run loops over the input channel, calling fn for each turn. Each turn is wrapped in a trace span for observability. Input messages are automatically added to the session before fn is called. After fn returns successfully, a snapshot check is triggered and a TurnEnd chunk (carrying any new snapshot's ID) is sent.
fn may return a TurnResult to report how the turn ended (e.g. its finish reason); returning nil reports nothing. The reason rides the turn's TurnEnd chunk and is persisted on the turn-end snapshot.
When fn returns an error, Run records the failure, stops looping, and returns the error. What it does with the turn's state depends on what fn returned beside the error: a TurnResult commits the turn, which snapshots the session under SnapshotStatusFailed with the error on the row, and nil rolls it back, leaving the previous snapshot as the resume point. A prompt-backed agent commits whenever the generate call produced a partial response, because that partial ends at a turn seam and is a conversation the caller can continue from; a custom agent commits when it knows the same of its own state.
A custom agent may then recover (e.g. call Run again to keep processing inputs) or propagate the error out of the agent function, which resolves the invocation with a failed AgentOutput carrying the error and the resume point rather than failing the action.
type SessionSnapshot ¶
type SessionSnapshot[State any] struct { // CreatedAt is when the snapshot was created. CreatedAt time.Time `json:"createdAt"` // Error is the structured failure information for a snapshot in // [SnapshotStatusFailed]. Nil otherwise. Error *status.Error `json:"error,omitempty"` // FinishReason is the semantic reason the turn or invocation captured here // ended (e.g. [AgentFinishReasonStop], [AgentFinishReasonInterrupted], // [AgentFinishReasonFailed], [AgentFinishReasonAborted]). It complements // [SessionSnapshot.Status] (the persistence lifecycle) so a resumed or // background task can report how it ended without re-deriving it from the // messages. FinishReason AgentFinishReason `json:"finishReason,omitempty"` // HeartbeatAt is refreshed periodically while a detached (background) turn is // in flight, so a reader can detect a dead background worker: if a pending // snapshot's heartbeat goes stale (older than the configured timeout), reads // surface its status as [SnapshotStatusExpired] (the dead worker can no longer // persist a terminal status itself). Refreshing it does not advance // [SessionSnapshot.UpdatedAt], so liveness stays distinct from state changes. Nil // on snapshots that never detached. HeartbeatAt *time.Time `json:"heartbeatAt,omitempty"` // ParentID is the ID of the previous snapshot in this timeline. It is // informational lineage (for debugging and UI history trees) and plays // no part in resolving a session's latest snapshot. ParentID string `json:"parentId,omitempty"` // SessionID is the ID of the session this snapshot belongs to. Assigned by the // framework on the conversation's first invocation and stamped on every later // snapshot in the chain, across resumed invocations; stores preserve it across // rewrites. SessionID string `json:"sessionId,omitempty"` // SnapshotID is the unique identifier for this snapshot (UUID). SnapshotID string `json:"snapshotId"` // State is the conversation state captured at this point. Nil on a // pending snapshot (the live state is not yet committed; the background // invocation is still processing queued inputs); populated on terminal // snapshots with the cumulative final state. State *SessionState[State] `json:"state,omitempty"` // Status is the lifecycle state of this snapshot. Empty is treated as // [SnapshotStatusCompleted] for backwards compatibility. Status SnapshotStatus `json:"status,omitempty"` // UpdatedAt is when the snapshot's state was last written. A heartbeat refresh on // a pending snapshot deliberately does not advance it, so a pending snapshot's // UpdatedAt equals CreatedAt until the snapshot is finalized, whose terminal write // advances it. UpdatedAt time.Time `json:"updatedAt,omitempty"` }
SessionSnapshot is a persisted point-in-time capture of session state. It is the canonical record written to and read from a SessionStore.
type SessionState ¶
type SessionState[State any] struct { // Artifacts are named collections of parts produced during the conversation. Artifacts []*Artifact `json:"artifacts,omitempty"` // Custom is the user-defined state associated with this conversation. Custom State `json:"custom,omitempty"` // Messages is the conversation history (user/model exchanges). // Does NOT include prompt-rendered messages — those are rendered fresh each turn. Messages []*ai.Message `json:"messages,omitempty"` // SessionID is the ID of the session (conversation) this state belongs to. // Framework-owned: assigned on the conversation's first invocation and // re-stamped on outbound state, so client-managed callers can round-trip the // state object opaquely. For server-managed agents the snapshot row's // [SessionSnapshot.SessionID] is canonical and this field mirrors it. SessionID string `json:"sessionId,omitempty"` }
SessionState is the portable conversation state that flows between client and server. It contains only the data needed for conversation continuity.
type SessionStore ¶
type SessionStore[State any] interface { SnapshotReader[State] SnapshotWriter[State] }
SessionStore is the minimum store interface required by WithSessionStore. Status-change observation is layered as the optional SnapshotSubscriber capability and checked at runtime: a store wired into an agent that intends to support detach must also implement SnapshotSubscriber, or the runtime will reject detach attempts.
type SnapshotMetadataReader ¶ added in v1.13.0
type SnapshotMetadataReader[State any] interface { // GetSnapshotMetadata is [SnapshotReader.GetSnapshot] without the state: // the returned row carries every other field as stored, with State nil, // and the same nil-if-not-found contract. GetSnapshotMetadata(ctx context.Context, snapshotID string) (*SessionSnapshot[State], error) // GetLatestSnapshotMetadata is [SnapshotReader.GetLatestSnapshot] without // the state: the same resolution, the same nil-if-none and // empty-session-ID contract, and a row with State nil. GetLatestSnapshotMetadata(ctx context.Context, sessionID string) (*SessionSnapshot[State], error) }
SnapshotMetadataReader is the optional capability layered on SessionStore that answers a metadata-only read without loading the row's state. The runtime takes it for every read that only needs to know where a snapshot stands (GetSnapshotRequest.MetadataOnly, WithMetadataOnly): its status, finish reason, parent, session, timestamps, error, and heartbeat. A store that does not implement it is still correct, since the runtime reads the row in full and drops the state, but it pays to load a conversation history it will not use. Both bundled local stores and the Firestore store implement it. Implement both methods or neither: the capability is detected as a whole.
type SnapshotReadOption ¶ added in v1.13.0
type SnapshotReadOption interface {
// contains filtered or unexported methods
}
SnapshotReadOption configures how a snapshot read projects its result. It applies to the reads that answer at once: Agent.GetSnapshot, Agent.GetLatestSnapshot, their AgentHandle twins, and DetachedTask.Poll.
func WithMetadataOnly ¶ added in v1.13.0
func WithMetadataOnly() SnapshotReadOption
WithMetadataOnly reads a snapshot's metadata only: the returned snapshot carries the shaped status, finish reason, parent, session, timestamps, and error, and its State is nil. The shaping is identical to a full read (status defaulting and heartbeat expiry need only the metadata), and a store that implements SnapshotMetadataReader answers without loading the state at all; any other store is read in full and the state dropped. Use it to dispatch on where a task stands without serializing a potentially large conversation history; the state transform does not run, exactly as on a full read of a stateless row.
type SnapshotReader ¶
type SnapshotReader[State any] interface { // GetSnapshot retrieves a snapshot by ID. Returns nil if not found. GetSnapshot(ctx context.Context, snapshotID string) (*SessionSnapshot[State], error) // GetLatestSnapshot returns the session's most recently created // snapshot, whatever its status: a pending, failed, or aborted row is // returned like any other, and the caller applies its own policy. // Returns nil if the session has no rows, and an error if sessionID is // empty. // // "Most recently created" means the greatest [SessionSnapshot.CreatedAt]; // break ties deterministically (e.g. by SnapshotID). This is a plain // max-timestamp lookup, implementable as a single indexed query (e.g. // WHERE sessionId = ? ORDER BY createdAt DESC LIMIT 1). A later rewrite of // an older row (e.g. a detach finalize) does not move it ahead of a // newer-created sibling, since CreatedAt is preserved across rewrites. // ParentID is informational lineage and plays no part in resolution: when // history forks, the most recently created branch wins. GetLatestSnapshot(ctx context.Context, sessionID string) (*SessionSnapshot[State], error) }
SnapshotReader retrieves snapshots. The minimum any session store must implement to be used with WithSessionStore.
type SnapshotStatus ¶
type SnapshotStatus string
SnapshotStatus describes the lifecycle state of a snapshot. A synchronous turn writes SnapshotStatusCompleted on success (an empty value is also treated as completed for backwards compatibility), SnapshotStatusFailed when something inside it breaks, and SnapshotStatusAborted when the caller stopped it instead.
When a client sets AgentInput.Detach, the server writes a single snapshot with SnapshotStatusPending (and empty state) and returns its ID immediately. Background processing then rewrites that snapshot with the state through the last committed turn and SnapshotStatusCompleted / SnapshotStatusFailed when the agent finishes. An abort in the meantime flips the row to SnapshotStatusAborting to stop the work, and the same finalize then lands it as SnapshotStatusAborted with the state. A detached run's settled rows are therefore shaped like a synchronous one's: the same statuses mean the same things whichever way the invocation ran.
SnapshotStatusExpired is never persisted: it is computed on read for a pending or aborting snapshot whose background worker is presumed dead (its heartbeat went stale), surfacing the orphan rather than leaving it in flight forever.
const ( // SnapshotStatusPending indicates a detached invocation is still // processing the queued inputs. The snapshot will be rewritten with a // terminal status once the background work finishes. SnapshotStatusPending SnapshotStatus = "pending" // SnapshotStatusAborting indicates the abort companion action stopped a // detached invocation and its worker is winding down toward the finalize // that stamps the state onto the row. The flip is what cancels the work; the // finalize that follows writes [SnapshotStatusAborted] with the state through // the last committed turn. It is not terminal, so a wait keeps waiting, and it // is not resumable: the row carries no state yet, and resuming from the // parent snapshot would fork away from the work the finalize is about to // commit. The worker keeps refreshing [SessionSnapshot.HeartbeatAt] while it // drains, so a stale beat means it died between the two writes and a read // surfaces the row as [SnapshotStatusExpired]. SnapshotStatusAborting SnapshotStatus = "aborting" // SnapshotStatusCompleted indicates the snapshot captures a settled state. SnapshotStatusCompleted SnapshotStatus = "completed" // SnapshotStatusAborted indicates the caller stopped the invocation rather // than something inside it breaking: it called the abort companion action on a // detached run, it ended the context under an attached one (its own cancel, the // transport closing, a deadline it set expiring), or the run reached a limit it // set such as ai.WithMaxTurns. // // Its state is what the run committed before the stop, trimmed back to the // last turn that finished, so resume is permitted the same way // [SnapshotStatusFailed] permits it. A tool that ran inside the turn that did // not finish has its response discarded with that turn, so re-sending the // conversation runs it again. A detached run reaches this status through // [SnapshotStatusAborting]: the abort flips the row to stop the work, and the // finalize that follows writes this status together with the state. SnapshotStatusAborted SnapshotStatus = "aborted" // SnapshotStatusFailed indicates a turn ended with an error. The snapshot's // Error field describes the failure, and its state is what the turn // committed before it: the conversation trimmed back to the last completed // tool round. Resume is permitted, so whether the failure is worth another // attempt is the client's call, taken from the error's status. A turn the // caller stopped writes [SnapshotStatusAborted] instead. SnapshotStatusFailed SnapshotStatus = "failed" // SnapshotStatusExpired indicates a pending or aborting snapshot whose detached // background worker is presumed dead: its [SessionSnapshot.HeartbeatAt] went // stale. It is computed on read (never persisted), so the raw store row stays // [SnapshotStatusPending] or [SnapshotStatusAborting] while a read surfaces it // as expired. SnapshotStatusExpired SnapshotStatus = "expired" )
func (SnapshotStatus) Terminal ¶ added in v1.13.0
func (s SnapshotStatus) Terminal() bool
Terminal reports whether the status is settled: no further transition will happen on its own. SnapshotStatusPending and SnapshotStatusAborting are the non-terminal statuses, the two rows a live worker is still owed a write for (an empty status counts as SnapshotStatusCompleted, matching the documented default), so waiters stop on completed, failed, aborted, and expired alike. An expired snapshot's raw store row is still pending or aborting, but its worker is presumed dead, so nothing will finalize it.
type SnapshotSubscriber ¶
type SnapshotSubscriber interface {
// OnSnapshotStatusChange returns a channel that yields the snapshot's
// status whenever it changes. The first value (if any) reflects the
// status at subscription time. The channel is closed when ctx is
// cancelled. If the snapshot does not exist when the subscription is
// established, the channel is closed without yielding a value.
//
// Implementations may push changes from a transaction log or CDC feed,
// or poll internally.
OnSnapshotStatusChange(ctx context.Context, snapshotID string) <-chan SnapshotStatus
}
SnapshotSubscriber is the optional capability layered on SessionStore that lets the agent runtime observe a snapshot's status changes without polling. It is what makes a detached invocation abortable: aborting is an ordinary SnapshotWriter.SaveSnapshot that flips a pending row to SnapshotStatusAborting, and the runtime reacts to that flip through this subscription, promptly cancelling the background work context.
A store that does not implement it cannot support detach (there is no way to signal the background work to stop); see the runtime's detach precondition check.
type SnapshotWriter ¶
type SnapshotWriter[State any] interface { // SaveSnapshot atomically reads the snapshot at id (if any), applies // fn, and persists the result largely verbatim. The store owns only // identity; the caller (fn) owns the lifecycle timestamps and status: // // - SnapshotID: if id is empty, the store generates a fresh ID; // otherwise the store uses id (any SnapshotID populated by fn is // overridden). // - SessionID: the ID of the session (chain of snapshots) the row // belongs to: preserved from the existing row on update (a row's // session never changes once set), otherwise taken from fn's row // as-is. Stores never mint or infer session IDs. // - CreatedAt / UpdatedAt: caller-managed. The store persists whatever fn // returns and never stamps them. fn sets CreatedAt and UpdatedAt to the // current time on a new row, preserves CreatedAt and advances UpdatedAt // on a state-changing rewrite, and preserves both on a non-state write // (e.g. a heartbeat refresh, which carries the existing snapshot through // unchanged but for HeartbeatAt). Keeping timestamps with the caller is // what lets a heartbeat advance liveness without registering as a state // change - the store has no special heartbeat path. // - Status: if the snapshot returned by fn has Status="", it is // defaulted to [SnapshotStatusCompleted] (the common case for // synchronous turn-end writes). Callers writing a pending row must // set Status explicitly. // // fn receives the existing snapshot (or nil if id is empty or the // row does not exist) and returns the snapshot to commit, or // (nil, nil) to skip the write without changing the row. // // Under contention, stores that use optimistic concurrency or // transaction retries may call fn multiple times. fn must therefore // be a pure function of its input: no side effects (channel sends, // logging, external I/O) inside fn. // // Returns the snapshot as persisted (with the store-owned fields // populated), or nil if fn declined to write. SaveSnapshot( ctx context.Context, snapshotID string, fn func(existing *SessionSnapshot[State]) (*SessionSnapshot[State], error), ) (*SessionSnapshot[State], error) }
SnapshotWriter persists snapshots. The minimum any session store must implement to be used with WithSessionStore.
type StateTransform ¶
type StateTransform[State any] = func(ctx context.Context, state *SessionState[State]) (*SessionState[State], error)
StateTransform rewrites session state on its way out to a client: it is applied to the state returned by the getSnapshot companion action and to AgentOutput.State for client-managed agents, but not to state persisted in the store or passed to the agent function. Typical uses are PII redaction and stripping secrets.
state is a fresh deep copy the transform owns. It returns the state to expose and a nil error, with two special outcomes:
- A nil state omits state from the response. This is an intentional, successful outcome: the stored snapshot and the agent's own view keep the data, only the outbound copy is dropped.
- A non-nil error fails closed: the operation being shaped aborts rather than exposing anything. A snapshot read returns the error; the final AgentOutput of an invocation resolves as a failed output carrying it. Use it when redaction cannot be performed safely (e.g. an RBAC policy lookup failed), where omitting (nil) would be a silent under-redaction and returning raw state would leak. The error's status code (such as PERMISSION_DENIED) propagates to the caller.
ctx is the request or invocation context, carrying deadlines and values such as the caller's identity for RBAC-aware redaction.
type StreamTransform ¶
type StreamTransform = func(ctx context.Context, chunk *AgentStreamChunk) (*AgentStreamChunk, error)
StreamTransform rewrites an AgentStreamChunk on its way out to a client: it runs at the wire boundary on every chunk the runtime forwards (model chunks, artifacts, custom-state patches, and turn-end signals), after the chunk's in-process side effects have already been applied. So, like StateTransform, it shapes only what the client sees, not session state, persisted snapshots, or anything passed to the agent function. It also leaves the final AgentOutput untouched; it is a stream-only hook. Typical uses are redacting streamed model tokens and stripping fields a client should not receive.
chunk is a fresh deep copy the transform owns. It returns the chunk to forward and a nil error, with two special outcomes:
- A nil chunk drops it from the stream. This is an intentional, successful outcome and is wire-only: the chunk's side effects (an artifact recorded on the session) and the final AgentOutput keep the underlying data. Dropping a chunk whose AgentStreamChunk.TurnEnd is set withholds the turn-end signal a client uses to pace the conversation, so reshape such a chunk rather than dropping it.
- A non-nil error fails closed: the whole invocation fails, so no unshaped chunk reaches the client and the offending chunk's side effects do not surface in the final output either. Use it when a chunk cannot be shaped safely; a panic in the transform is treated the same way.
ctx is the request context, carrying deadlines and values such as the caller's identity for RBAC-aware redaction.
To redact custom state, prefer WithStateTransform: the runtime applies it before diffing, so the custom-patch stream stays internally consistent. Rewriting an AgentStreamChunk.CustomPatch delta here can desync a client that reconstructs custom state from the patch sequence.
Unlike StateTransform it is not parameterized by State: a chunk carries no state type, so WithStreamTransform cannot infer State from the transform and takes it as an explicit type argument.
type Tool ¶
type Tool[In, Out any] struct { // contains filtered or unexported fields }
Tool wraps an ai.Tool with experimental x package features such as a plain context.Context function signature and tool.AttachParts.
DEPRECATED(breaking): With breaking changes, Tool would not wrap ai.ToolAction. It would be the primary tool type, backed directly by core.NewActionOf, eliminating the inner field and all delegation methods below.
func NewTool ¶
func NewTool[In, Out any]( name, description string, fn ToolFunc[In, Out], opts ...ai.ToolOption, ) *Tool[In, Out]
NewTool creates a new unregistered tool with a simple function signature. Use tool.AttachParts inside the function to return additional content parts.
func (*Tool[In, Out]) Definition ¶
func (t *Tool[In, Out]) Definition() *ai.ToolDefinition
Definition returns the ai.ToolDefinition for this tool.
The inner tool is built on ai.NewMultipartTool, whose function returns *ai.MultipartToolResponse, so the inner definition would advertise that envelope as the output schema. We override OutputSchema with the schema inferred from the Out type parameter, making the definition equivalent to what ai.NewTool exposes (the real output type) rather than leaking the multipart envelope to the model and Dev UI. Genkit infers schemas with DoNotReference, so the result is fully inlined and needs no registry resolution.
A custom schema from ai.WithOutputSchema or ai.WithOutputSchemaName (which require Out to be 'any') passes through from the inner tool untouched: with Out being 'any' there is no inferred schema to override it.
func (*Tool[In, Out]) Respond ¶
Respond creates a tool response part for an interrupted tool request.
func (*Tool[In, Out]) Restart ¶
Restart creates a restart part using the legacy ai.RestartOptions.
DEPRECATED(breaking): Remove entirely. Superseded by InterruptibleTool.Resume.
func (*Tool[In, Out]) RunRawMultipart ¶
func (t *Tool[In, Out]) RunRawMultipart(ctx context.Context, input any) (*ai.MultipartToolResponse, error)
RunRawMultipart runs the tool with raw input and returns the full multipart response.
type ToolResume ¶
type ToolResume struct {
// Respond contains tool response parts to send to the model when resuming.
Respond []*ai.Part `json:"respond,omitempty"`
// Restart contains tool request parts to restart when resuming.
Restart []*ai.Part `json:"restart,omitempty"`
}
ToolResume holds the parts that resume an interrupted agent turn. Mirrors ai.GenerateActionResume but is named for the tool-call callsite where it is set on an AgentInput.
type TurnContext ¶
type TurnContext struct {
// SnapshotID is the ID the turn-end snapshot will be saved under, minted
// before the turn runs so the handler knows it in advance. A server-managed
// turn persists its snapshot under this ID, whether it succeeded or failed.
// It is empty for a client-managed agent (no store, so no snapshot) and is
// the reserved-but-unused ID for a turn that writes none (one that failed
// before committing anything, or one whose snapshots a detach suspended).
SnapshotID string
// ParentSnapshotID is the ID of the snapshot this turn continues from: the
// previous turn's snapshot, or the snapshot the invocation resumed from. It
// is empty on the first turn of a fresh conversation and for a
// client-managed agent.
ParentSnapshotID string
// TurnIndex is the zero-based index of this turn within the invocation.
TurnIndex int
}
TurnContext carries per-turn metadata the runtime exposes to the per-turn function through its context. Retrieve it with TurnContextFromContext.
Its SnapshotID is reserved before the turn runs (for a server-managed agent), so a handler can name external, snapshot-correlated resources (e.g. a git worktree named after the snapshot) up front and have them line up with the snapshot the turn persists at its end.
func TurnContextFromContext ¶
func TurnContextFromContext(ctx context.Context) *TurnContext
TurnContextFromContext returns the TurnContext for the turn currently executing, or nil if ctx is not a per-turn context (e.g. it was called outside the SessionRunner.Run callback). The returned pointer is read-only; the runtime owns it.
type TurnEnd ¶
type TurnEnd struct {
// FinishReason is why this turn finished (e.g. [AgentFinishReasonStop],
// [AgentFinishReasonInterrupted]). It lets a caller react to a turn boundary
// (such as pausing on an interrupt) without scanning the message content.
// Empty when the turn reported no reason.
//
// [AgentFinishReasonFailed] reports a failed turn; unless the agent
// recovers and keeps processing, the invocation then resolves with a failed
// [AgentOutput] carrying the error and the resume point, and further
// sends fail with [core.ErrActionCompleted].
FinishReason AgentFinishReason `json:"finishReason,omitempty"`
// SnapshotID is the ID of the snapshot persisted at the end of this turn,
// whether it succeeded or failed. Empty if no snapshot was written (no store
// configured, a turn that failed before committing anything, or snapshots
// were suspended after detach).
SnapshotID string `json:"snapshotId,omitempty"`
}
TurnEnd groups the signals emitted when an agent turn finishes. A TurnEnd value is emitted exactly once per turn, regardless of whether a snapshot was persisted.
type TurnResult ¶
type TurnResult struct {
// FinishReason is why this turn ended (e.g. [AgentFinishReasonStop],
// [AgentFinishReasonInterrupted]). Empty to report no reason.
FinishReason AgentFinishReason
}
TurnResult is the optional return value of a SessionRunner.Run per-turn callback. It lets a custom agent report how the turn ended; the framework forwards the reason on the turn's TurnEnd chunk, persists it on the turn-end snapshot, and uses it to default the invocation's finish reason.
Returning nil (or a zero TurnResult) omits the reason: the framework performs no implicit inference. A prompt-backed agent populates it automatically from the underlying generate response.
Returned alongside an error, it also says the failed turn left state worth continuing from, which is what makes the turn snapshot and the session resumable; see SessionRunner.Run.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package localstore provides single-process exp.SessionStore implementations suitable for local development, tests, and single-instance apps (CLI tools, desktop apps, local web services).
|
Package localstore provides single-process exp.SessionStore implementations suitable for local development, tests, and single-instance apps (CLI tools, desktop apps, local web services). |
|
Package tool provides runtime helpers for use inside tool functions.
|
Package tool provides runtime helpers for use inside tool functions. |