Documentation
¶
Overview ¶
Package exp provides in-preview A2UI ("Agent to UI") support for Genkit agents.
A2UI is a transport-agnostic, JSON-based streaming UI protocol (https://a2ui.org/). An A2UI-enabled agent can stream not just prose, but rich, interactive UI "surfaces" that a client renders incrementally.
The whole server-side integration is the ai.Middleware, added to a github.com/firebase/genkit/go/ai.Generate call via github.com/firebase/genkit/go/ai.WithUse. It injects the catalog's capabilities into the system prompt, then intercepts model output (streamed chunks and the final message), extracts a2ui fenced blocks, validates them against the catalog, and rewrites them into a2ui data parts.
Examples here import this package as a2uix ("github.com/firebase/genkit/go/plugins/a2ui/exp"), the alias used across the Genkit docs and samples.
APIs in this package are under active development and may change in any minor version release.
Index ¶
- Constants
- Variables
- func IsPart(p *ai.Part) bool
- func LoadCatalog(g *genkit.Genkit, catalog *Catalog) error
- func RegisterBasicCatalog(g *genkit.Genkit) error
- func RenderCatalogInstructions(catalog *Catalog) string
- type A2UI
- type Catalog
- type CatalogComponent
- type Envelope
- type Surfaces
- type ValidateMode
Constants ¶
const ( // InstructionsSystem appends A2UI capabilities to the system prompt // (default). InstructionsSystem = "system" // InstructionsNone injects nothing (useful if you supply your own // instructions). InstructionsNone = "none" )
Instruction placement options for Surfaces.Instructions.
const A2UIMimeType = "application/a2ui+json"
A2UIMimeType identifies an A2UI payload. It is stamped onto the metadata.mimeType of the Genkit data part that carries A2UI envelopes, matching the A2A binding of the A2UI spec exactly.
const BasicCatalogID = "https://a2ui.org/specification/v0_9/catalogs/basic/catalog.json"
BasicCatalogID is the catalog id of the A2UI "Basic Catalog" (v0.9). Surfaces created with the basic catalog reference this id, and the client renderer registers a catalog under the same id.
const CatalogValueType = "a2ui-catalog"
CatalogValueType is the registry value type under which A2UI catalogs are stored (key `/a2ui-catalog/<id>`), so the middleware can look them up by id and tooling (e.g. the Dev UI's GET /api/values?type=a2ui-catalog) can list them. Matches the JS plugin's value type.
const DefaultCatalogID = "basic"
DefaultCatalogID is the id used when Surfaces specifies neither Catalog nor CatalogID. It resolves to the bundled basic catalog.
const DefaultVersion = "v0.9"
DefaultVersion is the default A2UI protocol version stamped on emitted envelopes.
Variables ¶
var SupportedVersions = []string{"v0.9", "v0.9.1"}
SupportedVersions is the set of A2UI protocol versions the plugin can stamp on emitted envelopes. Surfaces.Version is validated against it so a typo cannot stamp a version the renderer will reject at runtime. Matches the JS plugin's SUPPORTED_VERSIONS.
Functions ¶
func IsPart ¶
IsPart reports whether p is an a2ui data part (mime application/a2ui+json carrying an "envelopes" array).
func LoadCatalog ¶
LoadCatalog registers an A2UI catalog in the Genkit registry under the key `/a2ui-catalog/<id>` (using the catalog's own ID), so the ai.Middleware can resolve it by id via Surfaces.CatalogID, and tooling such as the Dev UI can enumerate catalogs (GET /api/values?type=a2ui-catalog). This mirrors the JS and Dart plugins, keeping the catalog representation identical across runtimes.
Re-registering the same id is idempotent (the existing registration is kept), so calling it more than once, or registering the basic catalog twice, is safe. Registering a different catalog under an existing id keeps the original and logs a warning, so an edited catalog re-loaded under the same id is not silently ignored. It uses a register-if-absent primitive, so concurrent callers racing on the same id cannot panic.
Example:
a2uix.LoadCatalog(g, myCatalog)
// ... then reference it by id:
ai.WithUse(&a2uix.Surfaces{CatalogID: myCatalog.ID})
func RegisterBasicCatalog ¶
RegisterBasicCatalog registers the bundled BasicCatalog in the registry so it appears alongside custom catalogs in tooling. The middleware falls back to the basic catalog even without this call; register it explicitly to surface it in the Dev UI. Idempotent.
func RenderCatalogInstructions ¶
RenderCatalogInstructions renders a catalog into model-facing instructions describing the A2UI protocol and the available components. It is injected into the system prompt by the middleware when Instructions is InstructionsSystem.
Types ¶
type A2UI ¶
type A2UI struct{}
A2UI provides the Surfaces middleware as a Genkit plugin, so it appears in the Dev UI and can be referenced by name (for example from a prompt file's `use:` list). Registering the plugin is optional: the middleware works when passed directly to ai.WithUse. Register it with github.com/firebase/genkit/go/genkit.WithPlugins during Init:
g := genkit.Init(ctx, genkit.WithPlugins(&a2uix.A2UI{}))
The plugin carries no settings; every option lives on the per-call Surfaces.
func (*A2UI) Init ¶
Init implements api.Plugin. A2UI registers no actions.
func (*A2UI) Middlewares ¶
Middlewares implements ai.MiddlewarePlugin, exposing the Surfaces middleware descriptor to Genkit.
type Catalog ¶
type Catalog struct {
// ID is a globally-unique catalog id (also used as catalogId on
// createSurface).
ID string `json:"id"`
// Components are the components available in this catalog.
Components []CatalogComponent `json:"components"`
}
Catalog pins the set of components a surface may render. The Surfaces middleware uses it to tell the model what it may render (prompt injection) and to validate that emitted envelopes only reference known components. The renderer on the client registers a matching @a2ui/* catalog under the same ID.
func BasicCatalog ¶
func BasicCatalog() *Catalog
BasicCatalog returns the A2UI "Basic Catalog" (v0.9), mirroring the components published by @a2ui/web_core's basic catalog. Use it to render standard UI without defining your own design system.
func LoadCatalogFile ¶
LoadCatalogFile reads an A2UI catalog from a JSON file and registers it with LoadCatalog. The file must contain an object with an "id" string and a "components" array (see Catalog).
type CatalogComponent ¶
type CatalogComponent struct {
// Name is the component type name, e.g. "Text". It must match the renderer
// type.
Name string `json:"name"`
// Description is a one-line summary of what the component renders and when
// to use it.
Description string `json:"description"`
// Props is a short, model-facing description of the component's props. Kept
// as plain text (rather than a JSON Schema) to keep the injected prompt
// compact.
Props string `json:"props"`
}
CatalogComponent is a component the model may use, plus a short description of its props.
type Envelope ¶
Envelope is a single A2UI envelope message (e.g. createSurface, updateComponents, updateDataModel, deleteSurface). It is represented as a generic JSON object because the protocol is open-ended and versioned; the middleware only inspects a few well-known keys.
A component within an updateComponents envelope is a single entry in an A2UI adjacency list: UI is a flat list of components, and the tree is reconstructed via id references, with exactly one component having id "root". Beyond component/id, every component carries catalog-specific props, so components are handled as generic map[string]any objects rather than a dedicated type.
func EnvelopesFromParts ¶
EnvelopesFromParts extracts all A2UI envelopes carried by the given parts. Pass a message's, chunk's, or response's content. Returns nil for content that carries no a2ui parts (e.g. plain prose).
type Surfaces ¶
type Surfaces struct {
// Catalog describes what the agent may render, provided inline. When set it
// takes precedence over CatalogID. Not serialized, so it is only honored for
// code-defined use (not JSON/Dev-UI dispatch); prefer CatalogID with
// [LoadCatalog] for a registry-backed catalog that also survives dispatch
// and appears in the Dev UI.
Catalog *Catalog `json:"-"`
// CatalogID references a catalog registered with [LoadCatalog] by its id.
// The middleware resolves it from the registry at call time. Defaults to
// [DefaultCatalogID] (the bundled basic catalog) when neither Catalog nor
// CatalogID is set.
CatalogID string `` /* 187-byte string literal not displayed */
// Instructions controls where the catalog's capabilities are injected.
// InstructionsSystem (default) appends A2UI instructions to the system
// prompt; InstructionsNone injects nothing.
Instructions string `` /* 245-byte string literal not displayed */
// Validate controls validation of emitted envelopes against the catalog.
// ValidateWarn (default) logs and drops bad blocks; ValidateStrict returns
// an error; ValidateOff skips checking. An unrecognized value is rejected by
// New rather than silently downgraded.
//
// This validates envelope structure and component type names against the
// catalog only. It is a well-formedness check, not sanitization: even under
// ValidateStrict, model-controlled values (an Image's url, a Text's inline
// Markdown, any other prop) pass through untouched. Prop sanitization is the
// renderer/catalog's responsibility, and hosts should CSP-restrict remote
// sources. See the "Security and the trust boundary" section of the README.
Validate ValidateMode `` /* 325-byte string literal not displayed */
// SurfaceID sets the surface-id policy. Provide a fixed id to reuse for
// every surface; leave empty for a fresh UUID per surface.
SurfaceID string `` /* 126-byte string literal not displayed */
// Version is the protocol version stamped on emitted envelopes. Defaults to
// [DefaultVersion].
Version string `json:"version,omitempty" jsonschema_description:"Protocol version stamped on emitted envelopes. Defaults to \"v0.9\"."`
}
Surfaces is the A2UI ai.Middleware: it lets the model stream UI surfaces drawn from a catalog. Add it to a generate call with github.com/firebase/genkit/go/ai.WithUse.
Example:
resp, err := genkit.Generate(ctx, g,
ai.WithModel(m),
ai.WithPrompt("show me the weather in Tokyo"),
ai.WithUse(&a2uix.Surfaces{}), // defaults to the bundled basic catalog
)
Middleware ordering: A2UI keeps per-turn streaming state (a stream parser and its minted surface ids) for the model call it wraps. Place any retrying or fallback middleware (which re-invokes the model) OUTSIDE A2UI so each attempt gets a fresh A2UI turn, i.e. WithUse(retry, &a2uix.Surfaces{}) rather than WithUse(&a2uix.Surfaces{}, retry). WithUse(A, B) means A wraps B.
Every field is per-call configuration; the A2UI plugin only registers the middleware by name and carries no settings of its own.
type ValidateMode ¶
type ValidateMode string
ValidateMode controls how the parser handles malformed or invalid envelopes.
const ( // ValidateStrict throws (returns an error) on malformed JSON or unknown // components. ValidateStrict ValidateMode = "strict" // ValidateWarn logs a warning and drops the offending block/envelope, // keeping the rest of the turn alive. This is the default. ValidateWarn ValidateMode = "warn" // ValidateOff passes envelopes through unchecked. ValidateOff ValidateMode = "off" )