Repository navigation
v0.90.0 — Consumer field bindings with key lookup, gRPC model gateway, and accepted-run snapshots
Latestv0.90.0 — Consumer field bindings with key lookup, gRPC model gateway, and accepted-run snapshots
Goa-AI's dsl, codegen/agent, features/model/gateway, runtime/agent/runtime, and registry packages let service authors declare AI agents and toolsets in Goa designs, generate typed schemas and executors, route model calls through provider adapters or remote gateways, execute durable multi-turn agent workflows, and coordinate distributed tool catalogs across services. This release adds BindField and Lookup to bind or translate consumer-owned tool fields across an entire toolset, introduces a typed Goa-generated gRPC model gateway (features/model/gateway) with complete vs. partial token-usage accounting, serves run snapshots immediately after workflow acceptance before the first worker activity starts, and eliminates redundant registry and runtime decoding work across turns.
Registry wire protocol 15 and run suspension checkpoint format goa-ai.run-suspension.v14 are unchanged from v0.89.0. Existing v0.89.0 workers, providers, and registry replicas can roll forward to v0.90.0 without draining in-flight runs.
Toolset-wide consumer field bindings and key lookups (BindField and Lookup)
The dsl and codegen/agent packages define how an agent consumes a Toolset—a named group of callable tools—and generate the JSON schemas advertised to the language model alongside the Go functions that decode model arguments into provider payloads.
Previously, when multiple tools in a consumed toolset shared an application-supplied field (such as a tenant scope or workspace ID), authors had to repeat BindInput("tool_name", "field") on every tool. In addition, when a provider required an internal Go struct or identifier that the model could only refer to by a human-readable string key (such as choosing a region by name), applications had to write custom wrapper tools to expose a string argument and translate it into the provider's type.
Now, Use blocks support BindField and Lookup (#483, #515):
BindFieldacross a consumed toolset:BindField("scope")omitsscopefrom the model-facing input schema of every tool in the toolset whose payload declares that top-level field, and generates per-tool and toolset-wideWrapExecuteWithScopehelpers that inject the application's value before calling the provider. When matched tools use different Go types for the same field name,codegen/agentemits the per-tool wrappers and omits the conflicting toolset-wide wrapper (#515).Lookupfor model-facing string keys: NestingLookup(String, ...)insideBindFieldkeeps the field in the model-facing JSON schema as a validated string key instead of hiding it. Generation emitsDecode<Tool>PayloadWith<Field>LookupandWrapExecuteWith<Field>Lookuphelpers that validate the model's string key against the advertised schema, resolve it through a caller-suppliedmap[string]<ProviderFieldType>table, and return a structuredplanner.RetryHintif the key is missing.- Complete
tool_schemas.jsoninspection catalog: Generated per-agentspecs/tool_schemas.jsoncatalogs now includelookup_input_schemaandtext_only_input_schemaalongsideinput_schemaso design and evaluation tooling can inspect every model-facing schema projection (#516).
Before (BindInput repeated per tool, no key-to-struct lookup):
Use(RecordsToolset, func() {
BindInput("list_records", "scope")
BindInput("search_records", "scope")
})After (BindField with optional Lookup across all matching tools):
Use(RecordsToolset, func() {
BindField("scope", func() {
Lookup(String, "Human-readable scope name.", func() {
MinLength(1)
Example("north-america")
})
})
})Typed gRPC model gateway and token-usage completeness
The features/model/gateway, runtime/agent/model, and features/model/middleware packages sit between agent planners and model providers (Anthropic, AWS Bedrock, OpenAI, and Google Vertex/Gemini), enforcing request contracts, stream ordering, and adaptive token-bucket rate limiting.
Previously, features/model/gateway provided in-process server and remote-client hooks (NewServer, NewRemoteClient) but left wire serialization to caller-written callbacks. In addition, model.Response and model.RetainUsage could not distinguish a provider response that legitimately consumed zero tokens (such as a 100% prompt-cache hit) from a response that omitted usage counters, or distinguish complete invocation totals from partial counts captured before a mid-stream or multi-round tool-search failure. As a result, AdaptiveRateLimiter could refund reserved token capacity when a failed call reported only partial token usage.
- Goa-generated gRPC model gateway (
features/model/gateway):gateway.RegisterGRPCServerandgateway.NewGRPCClientexpose unaryComplete, server-streamingStream, andCountTokensRPCs over a typed Goa gRPC contract (features/model/gateway/design). The error codec encodes error trees and shared directed acyclic graphs (DAGs) through a typedOneOfunion (GatewayErrorNodeDetail), preservingmodel.OutputValidationError(including boundedResponseEvidenceandRecoveryCorrection),model.RequestValidationError,model.ProviderError, sentinel errors, and retained token usage across the process boundary. - Complete vs. incomplete token usage:
model.ResponseaddsUsageReportedandUsageIncompleteflags plusHasUsage()andHasObservedUsage()helpers, andmodeladdsmodel.RetainIncompleteUsage(err, usage)andmodel.UsageIncompleteFromError(err). Provider adapters (anthropic,bedrock,openai,vertex) mark partial stream or multi-round search counts as incomplete.AdaptiveRateLimitercharges overruns when usage is incomplete without refunding unmeasured work. - Token counting through runtime middleware: Wrapping a
model.Clientwithruntime.WithModelClientormiddleware.NewAdaptiveRateLimiternow preserves optionalmodel.TokenCounter(CountTokens) support (#484).
srv, err := gateway.NewServer(gateway.WithClient(providerClient))
if err != nil {
return err
}
gateway.RegisterGRPCServer(grpcRegistrar, srv)
client, err := gateway.NewGRPCClient(grpcConn)Accepted-run snapshots, live compaction events, and worker routing
The runtime/agent/runtime and runtime/agent/engine packages manage the lifecycle of an agent run from initial submission (Start / StartOneShot) through planner turns, transcript compaction, tool execution, and cancellation.
Previously, calling GetRunSnapshot immediately after Start or StartOneShot returned an error if a worker had not yet executed the first storage activity (StartRootRun), forcing HTTP/SSE gateways to poll or treat newly accepted runs as missing. During long conversations, history compression ran silently inside the planner turn without emitting a live status event or recovering cleanly if a later model call in the same activity failed before producing output.
Now (#496, #501, #504, #512, #513, #516):
- Accepted-run snapshots before first activity execution:
engine.AcceptedRunQuerier(QueryAcceptedRun, implemented by bothtemporal.Engineandinmem.Engine) letsRuntime.GetRunSnapshotsynthesize arun.PhaseAccepted(run.StatusPending) snapshot directly from the workflow engine's accepted execution metadata whenStartRootRunhas not yet been recorded in the session store (#516). - Live history-compaction status and recovery: When the runtime compresses older conversation turns into a summary, it emits a live compaction status event, uses a hardened summary prompt, and records the summary model invocation separately in the activity journal so a retryable pre-output failure on the subsequent planner model call can still recover while retaining summary token usage (#513, #516).
- Batched tool lifecycle storage and current-worker continuation routing: Tool batch execution coalesces lifecycle event appends, skips unused child-run history reads, and deduplicates result reminders (#512). New continuations and active-cancellation observer workflows route to current workers while existing runs keep their accepted worker targets (#496).
- Early workflow payload size checks and preparation spans: The runtime checks workflow record payload bounds during encoding (#504) and emits tracing spans across preparation stages before planner execution begins (#501).
Registry catalog resilience and lower per-turn overhead
The registry and runtime/toolregistry packages maintain the cluster-wide catalog of toolsets published by domain services and route tool invocations over Pulse streams.
Previously, rolling out a tool description update or switching pagination providers could invalidate in-progress cursors or unreadable historical conversations, and health sweeps or turn initialization repeatedly decoded unchanged toolset schemas and retired state keys.
Now (#477, #486–#495, #497–#500, #502, #503, #505, #507–#511, #514):
- Stable paging and historical transcript reads across provider updates: Saved conversations and active pagination cursors remain valid when a tool's description or paging provider changes, and retired catalog declarations remain readable for historical runs (#489, #497, #500, #502, #503).
- Leaner registry storage and health sweeps: The registry expires completed result-stream lifecycle records, deduplicates persisted toolset definitions (#511), streams fingerprint JSON directly into SHA-256 (#495), and skips retired and native state reads during health sweeps and listing (#514).
- Precompiled declarations and zero-copy tool specs: Static registry declarations and text-only tool definitions are compiled and memoized on first use rather than at package init or on every turn (#486, #487, #490, #492, #498, #505, #507–#510).
Required user action
No action is required for most applications upgrading from v0.89.0:
- Wire protocol and storage compatibility (no drain required): Registry wire protocol 15 and suspension checkpoint format
goa-ai.run-suspension.v14are unchanged fromv0.89.0. Mixedv0.89.0andv0.90.0services, workers, and registry nodes can run together during a rolling deployment. - Regenerate with
goa genwhen adopting new DSL or schema catalog features: Upgradegoa.design/goa-aitov0.90.0(withgoa.design/goa/v3v3.35.1-0.20261028010903-3d7ec7ca9b4eand Go1.27.3+) and rungoa genif you useBindField/Lookup, updatedAGENTS_QUICKSTART.mdcontract guardrails, or the expandedspecs/tool_schemas.jsoncatalog. - Custom
engine.Engineimplementations only: If you maintain a custom implementation ofruntime/agent/engine.Engineoutside the built-intemporalandinmemengines, remove the trailingtarget *WorkerDeploymentVersionparameter fromStartActiveCancellationWorkflow(ctx context.Context, ownerRunID string, request CancellationRequest) errorand optionally implementengine.AcceptedRunQuerier.
References
- DSL Reference (
BindFieldandLookup) - Architecture and Design (
DESIGN.md) - Add
BindFieldandLookupconsumer input binding DSL and codegen (#483, #515) - Add gRPC model gateway, accepted-run snapshots, usage completeness, and quickstart contract guardrails (#516)
- Upgrade module dependencies and align OpenAI tool-search types (#517)