Skip to content

feat(go): added experimental ways to define tools, interrupts, and streaming - #4797

Merged
apascal07 merged 181 commits into
mainfrom
ap/go-x-tools
Jun 25, 2026
Merged

feat(go): added experimental ways to define tools, interrupts, and streaming#4797
apascal07 merged 181 commits into
mainfrom
ap/go-x-tools

Conversation

@apascal07

@apascal07apascal07 commented Feb 21, 2026

Copy link
Copy Markdown
Collaborator

Adds experimental tool definition APIs (ai/exp) with simplified function signatures, typed interrupt/resume, and a runtime helper package for use inside tool functions.

These APIs are designed to replace the existing DefineTool and DefineMultipartTool in a future major release. The current implementation wraps the existing tool infrastructure for backwards compatibility, but the intent is to invert this -- the new APIs will become the primary implementation and the old ones will be removed.

Examples

Simple Tools

The new experimental tool constructor (genkitx.DefineTool, in the genkit/exp package) replaces both the stable DefineTool and DefineMultipartTool with a single API. The function receives a plain context.Context instead of ai.ToolContext, making tool functions easier to write and test. Multipart responses are handled via tool.AttachParts rather than requiring a separate function signature:

typeWeatherInputstruct {
Citystring`json:"city" jsonschema:"description=city name"`
}
weatherTool:=genkitx.DefineTool(g, "getWeather", "Fetches the weather for a given city",
func(ctx context.Context, inputWeatherInput) (string, error) {
ifinput.City=="Paris" {
return"Sunny, 25°C", nil
}
return"Cloudy, 18°C", nil
},
)
resp, _:=genkit.Generate(ctx, g,
ai.WithPrompt("What's the weather like in Paris?"),
ai.WithTools(weatherTool),
)
fmt.Println(resp.Text())

Use tool.AttachParts to return additional content (images, media) without changing the function signature:

genkitx.DefineTool(g, "screenshot", "Takes a screenshot",
func(ctx context.Context, inputScreenshotInput) (string, error) {
img:=takeScreenshot(input.URL)
tool.AttachParts(ctx, ai.NewMediaPart("image/png", img))
return"Screenshot captured", nil
},
)

Partial Tool Responses (Streaming Progress)

Tools can stream partial responses during execution for client-side display (e.g., progress indicators). Use tool.SendPartial inside any tool function -- it's best-effort and no-ops when streaming isn't available.

Define a status type shared between the tool and client:

typePipelineStatusstruct {
Stepstring`json:"step"`Progressint`json:"progress"`
}

Send partial responses from the tool:

processDataTool:=aix.NewTool("process_data", "Processes data from a source",
func(ctx context.Context, inputProcessInput) (string, error) {
tool.SendPartial(ctx, PipelineStatus{Step: "connecting", Progress: 0})
connect(input.DataSource)
tool.SendPartial(ctx, PipelineStatus{Step: "processing", Progress: 50})
results:=process(input.DataSource)
tool.SendPartial(ctx, PipelineStatus{Step: "done", Progress: 100})
returnfmt.Sprintf("Processed %d records", len(results)), nil
},
)

On the client side, partial tool responses arrive as ModelResponseChunks during streaming. Use Part.IsPartial() to distinguish progress updates from final tool results:

// GenerateStreamforchunk, _:=rangeai.GenerateStream(ctx, r, opts...) {
for_, p:=rangechunk.Chunk.ToolResponses() {
ifp.IsPartial() {
status:=p.ToolResponse.Output.(PipelineStatus)
fmt.Printf("[%s... %d%%]\n", status.Step, status.Progress)
}
}
fmt.Print(sv.Chunk.Text())
}
// Agent flowforchunk, _:=rangeconn.Receive() {
for_, p:=rangechunk.ModelChunk.ToolResponses() {
ifp.IsPartial() {
status:=p.ToolResponse.Output.(PipelineStatus)
fmt.Printf("[%s... %d%%]", status.Step, status.Progress)
}
}
fmt.Print(chunk.ModelChunk.Text())
}

Interruptible Tools

DefineInterruptibleTool adds typed interrupt/resume for human-in-the-loop workflows. The function receives a *Res parameter that is non-nil when the tool is being re-executed after an interrupt. Like DefineTool, multipart responses are supported via tool.AttachParts:

typeTransferInputstruct {
ToAccountstring`json:"toAccount"`Amountfloat64`json:"amount"`
}
typeTransferInterruptstruct {
Reasonstring`json:"reason"`Amountfloat64`json:"amount"`
}
typeConfirmationstruct {
Approvedbool`json:"approved"`
}
transferTool:=genkitx.DefineInterruptibleTool(g, "transfer",
"Transfers money to another account.",
func(ctx context.Context, inputTransferInput, confirm*Confirmation) (string, error) {
ifconfirm!=nil&&!confirm.Approved {
return"cancelled", nil
}
ifconfirm==nil&&input.Amount>100 {
return"", tool.Interrupt(TransferInterrupt{
Reason: "large_amount",
Amount: input.Amount,
})
}
return"completed", nil
},
)

Handle the interrupt on the caller side. Use transferTool.Resume for typed, compile-time-checked resume data:

resp, _:=genkit.Generate(ctx, g,
ai.WithPrompt("Transfer $200 to Alice"),
ai.WithTools(transferTool),
)
for_, interrupt:=rangeresp.Interrupts() {
meta, _:= tool.InterruptAs[TransferInterrupt](interrupt)
fmt.Printf("Transfer of $%.2f requires confirmation (reason: %s)\n", meta.Amount, meta.Reason)
// Resume: re-execute the tool with typed resume data.restart, _:=transferTool.Resume(interrupt, Confirmation{Approved: true})
resp, _=genkit.Generate(ctx, g,
ai.WithMessages(resp.History()...),
ai.WithTools(transferTool),
ai.WithToolRestarts(restart),
)
}

Or respond with a pre-computed result instead of re-executing the tool:

// Respond: provide the tool's output directly, skipping re-execution.response, _:=transferTool.Respond(interrupt, "cancelled by user")
resp, _=genkit.Generate(ctx, g,
ai.WithMessages(resp.History()...),
ai.WithTools(transferTool),
ai.WithToolResponses(response),
)

When the caller does not have access to the tool definition (e.g., in a generic interrupt handler), use the definition-free helpers tool.Resume and tool.Respond:

restart, _:=tool.Resume(interrupt, Confirmation{Approved: true})
// orresponse, _:=tool.Respond(interrupt, "cancelled by user")

Interrupts in an Agent Conversation

Interruptible tools work the same way inside an agent (genkit/exp): the agent streams the model's reply and pauses when a tool interrupts, and the client resumes over the same connection. Back a prompt agent with the transfer tool from above, drive a turn, and resume until it settles (a resumed turn can interrupt again):

agent:=genkitx.DefinePromptAgent[any](g, "banker",
aix.WithSessionStore(localstore.NewInMemorySessionStore[any]()),
)
conn, _:=agent.Connect(ctx)
deferconn.Close()
conn.SendText("Transfer $200 to Alice")
for {
// Stream one turn: print the reply and collect any tool interrupts.varinterrupts []*ai.Partvarend*aix.TurnEndforchunk, err:=rangeconn.Receive() {
iferr!=nil {
log.Fatal(err)
}
ifchunk.ModelChunk!=nil {
fmt.Print(chunk.ModelChunk.Text())
interrupts=append(interrupts, chunk.ModelChunk.Interrupts()...)
}
ifchunk.TurnEnd!=nil { // FinishReason is "interrupted" when pausedend=chunk.TurnEndbreak
}
}
ifend==nil||len(interrupts) ==0 {
break// the turn settled on its own (or the stream closed)
}
// Resolve each interrupt and resume. tool.Resume re-executes the tool with// typed data; tool.Respond would instead supply a result without re-running.resume:=&aix.ToolResume{}
for_, interrupt:=rangeinterrupts {
meta, _:= tool.InterruptAs[TransferInterrupt](interrupt)
fmt.Printf("\n[%s] $%.2f -- approve?\n", meta.Reason, meta.Amount)
restart, _:=transferTool.Resume(interrupt, Confirmation{Approved: true})
resume.Restart=append(resume.Restart, restart)
}
conn.SendResume(resume) // the resumed turn streams on the next Receive
}

API Reference

Runtime Helpers (ai/exp/tool -- experimental)

The tool package provides functions for use inside tool functions:

// Interrupt pauses tool execution and sends data to the caller.funcInterrupt(dataany) error// InterruptAs extracts typed interrupt data from an interrupted tool request part.funcInterruptAs[Tany](p*ai.Part) (T, bool)
// Resume creates a restart part for resuming an interrupted tool call.// Does not require access to the tool definition.funcResume[Resany](interruptedPart*ai.Part, dataRes) (*ai.Part, error)
// Respond creates a tool response part for an interrupted tool request.// Provides a pre-computed result instead of re-executing the tool.// Does not require access to the tool definition.funcRespond(interruptedPart*ai.Part, outputany) (*ai.Part, error)
// AttachParts attaches additional content parts (e.g., media) to the tool's// response without changing the function signature.funcAttachParts(ctx context.Context, parts...*ai.Part)
// OriginalInput returns the original input if the caller replaced it during restart.funcOriginalInput[Inany](ctx context.Context) (In, bool)
// SendPartial streams a partial tool response during execution (e.g., progress).// Best-effort: no-ops when streaming is not available.funcSendPartial(ctx context.Context, outputany)
// SendChunk streams a raw ModelResponseChunk during tool execution.// Unlike SendPartial (which wraps data as a partial tool response),// this gives the tool full control over the chunk contents.// Best-effort: no-ops when streaming is not available.funcSendChunk(ctx context.Context, chunk*ai.ModelResponseChunk)

Partial Tool Response Helpers (ai package)

// Part.IsPartial reports whether a Part is a partial (streaming) tool response.func (p*Part) IsPartial() bool// NewPartialToolResponsePart creates a Part marked as a partial tool response.funcNewPartialToolResponsePart(r*ToolResponse) *Part// ModelResponseChunk.ToolResponses returns tool response parts from a chunk.// Use Part.IsPartial() to distinguish progress updates from final results.func (c*ModelResponseChunk) ToolResponses() []*Part

Tool Definitions (ai/exp -- experimental)

Define & Register

// DefineTool creates a tool with a plain context.Context signature and registers it.funcDefineTool[In, Outany](
rRegistry, name, descriptionstring,
fnToolFunc[In, Out],
opts...ai.ToolOption,
) *Tool[In, Out]
// DefineInterruptibleTool creates a tool with typed interrupt/resume and registers it.funcDefineInterruptibleTool[In, Out, Resany](
rRegistry, name, descriptionstring,
fnInterruptibleToolFunc[In, Out, Res],
opts...ai.ToolOption,
) *InterruptibleTool[In, Out, Res]

Create Without Registering

// NewTool creates an unregistered tool.funcNewTool[In, Outany](name, descriptionstring, fnToolFunc[In, Out], opts...ai.ToolOption) *Tool[In, Out]
// NewInterruptibleTool creates an unregistered interruptible tool.funcNewInterruptibleTool[In, Out, Resumeany](name, descriptionstring, fnInterruptibleToolFunc[In, Out, Resume], opts...ai.ToolOption) *InterruptibleTool[In, Out, Resume]

Genkit Convenience Functions (genkit/exp -- experimental)

The genkit/exp package (aliased genkitx) wraps the registry-level constructors above to register through a *genkit.Genkit instead of a raw Registry. The registry stays internal; these reach it through an internal bridge, so no public registry accessor is added. The same package also hosts the experimental agent constructors (DefineAgent, DefinePromptAgent, DefineCustomAgent).

// DefineTool registers a simple tool via the Genkit instance.funcDefineTool[In, Outany](g*genkit.Genkit, name, descriptionstring, fn aix.ToolFunc[In, Out], opts...ai.ToolOption) *aix.Tool[In, Out]
// DefineInterruptibleTool registers an interruptible tool via the Genkit instance.funcDefineInterruptibleTool[In, Out, Resumeany](g*genkit.Genkit, name, descriptionstring, fn aix.InterruptibleToolFunc[In, Out, Resume], opts...ai.ToolOption) *aix.InterruptibleTool[In, Out, Resume]

Function Signatures

// ToolFunc -- simple tools.typeToolFunc[In, Outany] =func(ctx context.Context, inputIn) (Out, error)
// InterruptibleToolFunc -- tools with typed resume.// The resumed parameter is non-nil when the tool is re-executed after an interrupt.typeInterruptibleToolFunc[In, Out, Resumeany] =func(ctx context.Context, inputIn, res*Resume) (Out, error)

InterruptibleTool[In, Out, Res]

// Resume creates a restart part for this tool with typed data.// Validates that the interrupted part belongs to this tool.func (*InterruptibleTool) Resume(interruptedPart*ai.Part, dataRes) (*ai.Part, error)
// Respond creates a tool response part for this tool with typed output.// Provides a pre-computed result instead of re-executing the tool.// Validates that the interrupted part belongs to this tool.func (*InterruptibleTool) Respond(interruptedPart*ai.Part, outputOut) (*ai.Part, error)

Tool[In, Out]

Implements ai.Tool -- usable anywhere a tool is accepted (ai.WithTools, etc.).

func (*Tool) Name() stringfunc (*Tool) Definition() *ai.ToolDefinitionfunc (*Tool) RunRaw(ctx context.Context, inputany) (any, error)
func (*Tool) RunRawMultipart(ctx context.Context, inputany) (*ai.MultipartToolResponse, error)
func (*Tool) Respond(toolReq*ai.Part, outputDataany, opts*ai.RespondOptions) *ai.Partfunc (*Tool) Register(r api.Registry)

Resolve conflict in go/samples/basic-agents/main.go: keep the four-agent
(banker + hooks/agentEntry) description from ap/go-x-tools while adopting
the StreamBidi -> Connect rename from ap/go-session-flow.
decodeInit decoded the JSON init payload straight into the typed Init,
so any field the InitSchema did not declare was silently dropped before
validation ever saw it: an agent could be started with a misspelled or
bogus init field and get a fresh session with no error. Route init
through base.UnmarshalAndNormalize against the resolved InitSchema, the
same pipeline the unary input path uses, so the raw payload is validated
(additionalProperties rejects unknown fields) and normalized (e.g.
integer widening) before the typed decode.
Add coverage for unknown-field rejection on ConnectJSON/RunBidiJSON at
both the core BidiAction layer and the agent layer (real inferred
AgentInit schema), plus a guard that init is normalized, not just
validated.
Stamp the agent's root action span with the session ID under
"genkit:metadata:agent:sessionId" so traces from the same conversation
can be correlated. This mirrors the JS agent (PR #5251), which sets the
attribute once on the action span via setCustomMetadataAttributes; Go has
no such helper, so the genkit:metadata: prefix is inlined.
Align the per-turn span shape with JS's run() too: name "runTurn-N"
(1-indexed) with type flowStep and no subtype, replacing "agent/turn/N".
Add two construction options to localstore.FileSessionStore, mirroring
the JS FileSessionStore (PR #5251):
- WithMaxPersistedChainLength(n): on each save, walk the new snapshot's
parentId chain and unlink rows past the newest n, capping
per-conversation disk use. n must be >= 1; 0 or negative is rejected.
- WithSnapshotPathPrefix(fn): derive a per-call subdirectory from
context for tenant isolation. The prefix may nest via "/" and is
sanitized against directory escape.
Snapshots remain flat by id within the prefix
(<dir>/<prefix>/<snapshotId>.json), so resume-by-snapshot, heartbeat,
finalize, abort, and status subscription stay O(1) direct opens, while
GetLatestSnapshot scans the prefix directory. With no options configured
the on-disk layout is unchanged.
Options follow the ai/option.go interface pattern (in a new option.go)
and error if set more than once.
…tore
FileSessionStore.OnSnapshotStatusChange previously reflected only status
changes written through the same store instance, so a detached turn running
in one process could not be aborted by another process sharing the
directory.
Add an internal poller that re-reads subscribed snapshot files on an
interval (default 1s, configurable via WithPollInterval; <=0 disables it)
and delivers any change. The instant in-process fast path is kept for
same-process delivery; both paths funnel through a single per-snapshot
dedup gate, so a change is delivered exactly once regardless of which
observes it first. The poller is started lazily on the first subscriber and
stopped when the last unsubscribes, so a store with no subscriptions pays
nothing.
Polling (rather than fsnotify) matches the SnapshotSubscriber contract,
adds no dependency, and is immune to the temp-file+rename inode swap.
…pt/NamedPrompt
Replace the FromInline/FromPrompt agent-source constructors with three
clearer forms:
- InlinePrompt(opts...) defines the prompt inline (was FromInline)
- SameNamedPrompt() references the prompt named like the agent
- NamedPrompt(name, input) references any registered prompt by name,
rendered with an input supplied from code
NamedPrompt decouples the prompt's lookup name from the agent's name, so a
single prompt can back many agents with different inputs. The old
FromPrompt(defaultInput...) variadic-of-one is gone; per-turn input now
rides on NamedPrompt.
Updates the wrapper docs, both samples (chef's personality moves to the
.prompt frontmatter default), the README, and the tests, and adds
TestPromptAgent_NamedPromptSharedAcrossAgents covering the shared-prompt
path. Breaking change to the experimental ai/exp API.
Split the prompt-backed agent constructors so each has one clear job:
- DefineAgent(r, name, prompt, opts...) defines the prompt inline; the
prompt is an InlinePrompt, a
[]ai.PromptOption slice passed
positionally
- DefinePromptAgent(r, name, opts...) sources a prompt from the registry,
defaulting to the agent's own name
DefinePromptAgent uses the same-named prompt by default; WithNamedPrompt(name,
input) points it at a different registered prompt rendered with a code-supplied
input, so one prompt can back many agents.
The prompt source is split across a compile-time-validated option set mirroring
ai/option.go: the shared options (WithSessionStore, WithStateTransform,
WithDescription) are AgentOptions valid on every constructor, while
WithNamedPrompt is a PromptAgentOption accepted only by DefinePromptAgent.
Passing it to DefineAgent or DefineCustomAgent fails to compile.
Making the inline prompt a required positional argument means an inline agent
cannot be defined without one. Removes the AgentSource abstraction and the
SameNamedPrompt/NamedPrompt sources.
Updates the wrapper docs, both samples, and the tests. Breaking change to the
experimental ai/exp API.
The "Load the Prompt from a File" path now uses DefinePromptAgent (default
same-named lookup) with WithNamedPrompt for shared prompts, and the inline
example uses the InlinePrompt slice literal.
…pans
Each runTurn-N span now records the committed session state at turn end as
its genkit:output, shaped as {state: <session state>}, and for
server-managed agents carries the turn-end snapshot's ID under
genkit:metadata:agent:snapshotId. The session ID stays on the root action
span as before.
The state is raw: StateTransform shapes only client-facing surfaces, not
telemetry or persisted state, so the span output matches the snapshot its
ID points to.
With the turn span output now derived from session state, the per-turn
chunk collection is gone: removed SessionRunner.collectTurnOutput,
chunkRouter.collectTurnChunks and its turnChunks/turnMu fields, and the
accumulation branch in applySideEffects (now artifact-only).
…rror
WithStreamTransform is the stream-side counterpart to WithStateTransform, rewriting each AgentStreamChunk on its way to the client. Both StateTransform and StreamTransform now return (value, error): a nil value omits the state or drops the chunk (wire-only), while a non-nil error (or a panic) fails the read or invocation closed with the transform's status preserved. Updates go/README.md and the genkit.DefineAgent option docs.
The abortSnapshot companion action's response carried only status, so a caller could not correlate the result with the snapshot it aborted. Add snapshotId (matching the abortSnapshot request) and populate it from the request.
Authored in the shared zod schema (genkit-tools/common/src/types/agent.ts) and regenerated across genkit-schema.json, the Go bindings (go/ai/exp/gen.go), and the Python typings (_typing.py); the Go doc and noomitempty come from schemas.config.
The operation aborts a turn by marking its pending snapshot aborted for the
active turn to observe; it does not abort a snapshot. Name it for that
turn-level intent.
- Agent.AbortSnapshot -> Agent.Abort; AbortSnapshotAction -> AbortAction
- HTTP route POST /agents/{name}/abortSnapshot -> /agents/{name}/abort
- Wire types AbortSnapshotRequest/Response -> AgentAbortRequest/Response,
regenerated into genkit-schema.json, go gen.go, and py _typing.py
The unexported abortPendingSnapshot helper keeps its name: it names the
mechanism (flip the pending snapshot to aborted), distinct from the
turn-level abort.
BREAKING CHANGE: the Agent.Abort* Go API, the /abort HTTP route, and the
AgentAbort{Request,Response} schema types (Go, Python, JS) replace the
former AbortSnapshot* names.
A per-turn handler could not learn its snapshot ID until after the turn (on the TurnEnd chunk), so durable agents had no way to name snapshot-correlated external resources (e.g. a git worktree) before doing the turn's work. Reserve the turn's snapshot ID at turn start (the runtime now mints it rather than the store) and surface it, the parent snapshot ID, and the turn index on a TurnContext. The turn-end snapshot persists under that reserved ID, so the ID the handler reads up front is the ID the snapshot lands under.
The TurnContext rides on the per-turn fn's context.Context (TurnContextFromContext) rather than the SessionRunner.Run callback signature, so existing custom agents compile unchanged. Empty SnapshotID for client-managed agents and for turns that write no snapshot. The detach pending row stays store-minted.
Base automatically changed from ap/go-session-flow to mainJune 24, 2026 17:00
@apascal07
apascal07 requested a review from a team as a code ownerJune 24, 2026 17:00
…nkit/exp
The experimental Define* surface (DefineXTool, DefineInterruptibleTool, DefineAgent, DefinePromptAgent, DefineCustomAgent, ListAgents) moves out of the stable genkit package into genkit/exp, alongside the agent route builders and DefineStreamingFlow already there. The package qualifier lets DefineXTool drop the X workaround and become exp.DefineTool without colliding with the stable genkit.DefineTool.
The registry stays internal: genkit/exp reaches it through a new internal/genkitbridge hook that genkit installs in its init, so no public registry accessor is added and external code cannot import the bridge. ListAgents is reimplemented over the bridge. Agent-handler serving tests move to an external genkit_test package to avoid the genkit -> genkit/exp import cycle. Samples and go/README.md are updated to the genkitx.Define* surface.
BREAKING CHANGE: genkit.DefineXTool, genkit.DefineInterruptibleTool, genkit.DefineAgent, genkit.DefinePromptAgent, genkit.DefineCustomAgent, and genkit.ListAgents are removed in favor of the genkit/exp equivalents. These APIs are experimental and unshipped.
@github-actionsgithub-actionsBot added docs Improvements or additions to documentation js tooling python Python labels Jun 24, 2026
PR #4462 squash-landed the agent foundation that this branch also builds, so most conflicts were add/add on identical content. Resolution:
- 23 add/add files differed only by copyright year (2025 vs 2026); took main's 2026 copies.
- go/ai/generate.go: kept the branch's nil-guard on Interrupts() and the new ToolResponses() method (pure additions; main had neither).
- go/genkit/exp/*, README, and basic-agents samples: kept the branch's design (experimental definers in genkit/exp; the banker tool-interrupt/resume demo).
- go/genkit/servers_test.go: dropped TestHandlerAgent/TestHandlerAgentRef; their coverage moved to go/genkit/exp/routes_test.go when DefineAgent left package genkit.
Verified: go build ./..., go vet ./..., and tests for go/genkit, go/genkit/exp, go/ai/exp, go/ai, go/core all pass.
wrapSimpleFunc and wrapInterruptibleFunc duplicated their entire body
(parts-collector context setup, original-input plumbing, interrupt-error
conversion, and multipart-response assembly); only the user-function call
differed. Extract that scaffolding into a shared runToolFunc helper so the
two wrappers stay thin adapters.
Behavior-preserving: the interruptible wrapper's MapToStruct error now
routes through convertInterruptError, which is a no-op for non-InterruptError
values.
…l tool APIs
Tools run on concurrent goroutines, so tool.SendPartial / tool.SendChunk could
race on the wrapped stream callback (shared role/index state and the single
sink). Route every tool-originated send through one mutex in handleToolRequests;
log and drop best-effort sink errors and guard the chunk type assertion against
a stray value.
Add the missing test coverage for the experimental tool surface:
- ai/exp/tool: unit tests for Interrupt/Resume/Respond/InterruptAs/AttachParts/
OriginalInput/SendPartial/SendChunk.
- ai/exp: integration over DefineTool/DefineInterruptibleTool including the
typed interrupt/resume round-trip and a -race regression for the streaming
serialization above.
- ai: units for NewPartialToolResponsePart/IsPartial, ToolResponses, and the
corrected ModelResponseChunk.Text behavior.
Interrupt and resume payloads must serialize to a JSON object: return a clear,
typed error instead of an opaque json failure, and document the constraint on
the relevant helpers.
Bump touched-file copyright headers to 2026.
@apascal07
apascal07 merged commit d892b44 into mainJun 25, 2026
15 checks passed
@apascal07
apascal07 deleted the ap/go-x-tools branch June 25, 2026 05:45
apascal07 added a commit that referenced this pull request Jun 25, 2026
…ddleware
Resolve conflicts from main absorbing the agent foundation (#4462, #4797,
#4387, #5576) via separate squashed PRs:
- Adopt main's renames (AgentAbort{Request,Response}, Agent.Abort/AbortAction,
abort routes) and its turn-context feature across go/ai/exp + schema.
- Preserve this branch's net-new middleware surface: ArtifactStore, AgentRef,
WithContextFunc context seeding, and the orchestrator sample.
Per review direction, consolidate the agent constructors into the genkit/exp
(genkitx) surface that main migrated docs/samples/tests to:
- Move the genkit-instance seeding into genkitx.DefineAgent/DefinePromptAgent/
DefineCustomAgent via a new genkitbridge.SeedContext hook, so middleware
resolves sub-agents through the documented exp constructors.
- Remove the now-dead genkit.DefineAgent/DefinePromptAgent/DefineCustomAgent/
ListAgents duplicates from package genkit.
- Reconcile the basic-agents sample to keep both the banker (interrupt/resume)
and orchestrator (middleware delegation) demos on genkitx.
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

configdocsImprovements or additions to documentationgojspythonPythonroottooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@apascal07@pavelgj@MichaelDoyle