Skip to content

feat(js/plugins/a2ui): add a2ui plugin#5795

Open
pavelgj wants to merge 12 commits into
mainfrom
pj/a2ui
Open

feat(js/plugins/a2ui): add a2ui plugin#5795
pavelgj wants to merge 12 commits into
mainfrom
pj/a2ui

Conversation

@pavelgj

@pavelgj pavelgj commented Jul 22, 2026

Copy link
Copy Markdown
Member

Adds @genkit-ai/a2ui, a Genkit plugin that lets an agent stream A2UI ("Agent to UI") surfaces — rich, interactive UI rendered incrementally by the client — instead of just prose. Also adds a runnable js/testapps/a2ui sample (Express backend + Vite/Lit frontend).

Status: proof-of-concept / exploration.

How it works

A2UI travels on its own channel: a Genkit data part with mime type application/a2ui+json whose payload is an array of A2UI envelope messages (1:1 with the A2UI spec's A2A binding). The whole server integration is a single model middleware, a2ui(), added to an agent's use array. 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.

Server usage

import { genkit } from 'genkit/beta';
import { googleAI } from '@genkit-ai/google-genai';
import { a2ui, basicCatalog } from '@genkit-ai/a2ui';

const ai = genkit({ plugins: [googleAI()] });

export const uiAgent = ai.defineAgent({
  name: 'uiAgent',
  model: 'googleai/gemini-flash-latest',
  system: 'You help users. Render UI when it is clearer than prose.',
  use: [a2ui()], // <- the only A2UI-specific line
});

Client usage

@genkit-ai/a2ui/client is browser-safe. Pull envelopes off each chunk with a2uiEnvelopes and feed them to a renderer (e.g. @a2ui/lit); send user actions (button presses, form submits) back with actionToMessage:

import { a2uiEnvelopes, actionToMessage } from '@genkit-ai/a2ui/client';
import { MessageProcessor } from '@a2ui/web_core/v0_9';
import { basicCatalog } from '@a2ui/lit/v0_9';
import { remoteAgent } from 'genkit/beta/client';

const chat = remoteAgent({ url: '/api/uiAgent' }).chat();
const processor = new MessageProcessor([basicCatalog], (action) =>
  runTurn({ message: actionToMessage(action) })
);

async function runTurn(input) {
  const turn = chat.sendStream(input);
  for await (const chunk of turn.stream) {
    if (chunk.text) appendProse(chunk.text);
    const envelopes = a2uiEnvelopesFromParts(chunk.raw.modelChunk.content);
    if (envelopes.length) processor.processMessages(envelopes);
  }
}

runTurn('weather in Tokyo');

What's included

  • Plugin (js/plugins/a2ui): a2ui() middleware, envelope parser/validator, the basic catalog + model-facing instructions, and the browser-safe /client entrypoint (a2uiEnvelopes, actionToMessage, streamA2uiAgent). Options: catalog (required), instructions ('system'), validate ('strict'), surfaceId, version ('v0.9'). Unit tests for the middleware and parser.
  • Testapp (js/testapps/a2ui): Express server exposing the agent + a Vite/Lit chat UI that renders surfaces, with a getWeather demo tool and suggested prompts.

Testing

  • pnpm -C js/plugins/a2ui test
  • Manual: cd js/testapps/a2ui && genkit start -- pnpm start, open http://localhost:8080 — tried weather, comparisons, and a signup form (values round-trip back to the agent).

Add new @genkit-ai/a2ui plugin with corresponding testapp including web
frontend using @a2ui/lit components. Update pnpm-lock.yaml with new
workspace dependencies and bump genkit version to 1.40.1.
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@github-actions github-actions Bot added docs Improvements or additions to documentation js config labels Jul 22, 2026
pavelgj added 4 commits July 22, 2026 18:48
Change the a2ui middleware to accept a catalog id resolved from the Genkit
registry instead of a catalog object, defaulting to the bundled 'basic'
catalog. Add `loadCatalog` to register in-memory or file-based catalogs
under the new `a2ui-catalog` registry value type, and export
`HasRegistry` from genkit registry. Update README to document catalog
registration and usage.
Replace unsafe `any` type assertions with proper type guards and
typed interfaces throughout the a2ui plugin. Introduce `isTextPart`
type guard for narrowing parts, add `SummarizableEnvelope` interface
for envelope summarization, and use `node:crypto` `randomUUID` instead
of a custom fallback UUID generator.
Introduce a new 'warn' validation option that logs warnings and drops
offending blocks/envelopes instead of throwing, keeping the turn alive.
Update README and status to experimental, and replace hardcoded MIME
type with the A2UI_MIME_TYPE constant.
@pavelgj

pavelgj commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

/gemini review

2 similar comments
@pavelgj

pavelgj commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

/gemini review

@pavelgj

pavelgj commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

/gemini review

Generate style tips, examples, and forms guidance based on the components
a catalog actually provides, preventing custom catalogs from being told to
emit components they lack (which would fail `validate: 'strict'`). Also
clarify that middleware validates component types but not individual Icon
name values.
@pavelgj

pavelgj commented Jul 23, 2026

Copy link
Copy Markdown
Member Author

/gemini review

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces the @genkit-ai/a2ui plugin, which enables Genkit agents to stream rich, interactive UI surfaces using the A2UI protocol. It includes the server-side a2ui() middleware, browser-safe client helpers, a streaming parser, and a complete runnable test application. The review feedback highlights several critical robustness issues where the code lacks defensive checks when handling message content and parts, which could lead to runtime TypeErrors or data corruption if the input structures are not arrays or valid objects.

Comment thread js/plugins/a2ui/src/middleware.ts
Comment thread js/plugins/a2ui/src/middleware.ts
Comment thread js/plugins/a2ui/src/client.ts Outdated
Comment thread js/plugins/a2ui/src/middleware.ts
Comment thread js/plugins/a2ui/src/client.ts
pavelgj added 5 commits July 23, 2026 12:06
Change the a2ui data part to carry `{ envelopes }` instead of a bare
array, aligning the payload shape across client, middleware, and part
helpers. Also simplify the `surfaceId` option to accept only a fixed
string, removing the non-serializable factory function support.
Replace the hard `node:crypto` import with `globalThis.crypto.randomUUID()`
to keep the middleware portable across runtimes like Cloudflare Workers.
Change the default `validate` option from `'strict'` to `'warn'` so
malformed blocks are dropped instead of throwing, keeping turns alive.
Update README and inline docs to reflect the new defaults and behavior.
Replace low-level streamFlow with the higher-level remoteAgent client for
consuming A2UI-enabled agents. This simplifies session handling via chat
sessions and extracts text and A2UI envelopes directly from stream chunks.
Add validation to prevent runtime errors when action is undefined in
actionToMessage, and ensure isTextPart safely handles null or non-object
parts before accessing properties.
@pavelgj
pavelgj marked this pull request as ready for review July 24, 2026 16:31
Rename the envelope extraction helper to `a2uiEnvelopesFromParts` and
update it to accept model chunk content parts directly. Also add
`SupportedVersion` type casts in the stream parser for stronger typing.
Update README, client, and index exports accordingly.
@pavelgj
pavelgj requested review from apascal07 and cabljac July 24, 2026 19:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

config docs Improvements or additions to documentation js

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant