Highlights
A2UI: streaming, interactive UI protocol
This release introduces A2UI (Agent-to-UI) support via the new genkit_a2ui package. An A2UI-enabled agent streams not just prose, but rich, interactive UI surfaces (components, layouts, forms) that a client (Flutter or Web) renders incrementally. The package ships the a2ui() middleware, a bundled basicCatalog, an incremental stream parser with configurable validation, and browser/Flutter-safe client helpers. A full runnable sample lives in testapps/a2ui (Shelf server plus a Flutter chat client rendering surfaces in real time). (#342, #401, #403, #404)
final uiAgent = ai.defineAgent(
name: 'uiAgent',
model: googleAI.gemini('gemini-flash-latest'),
system: 'You are a helpful assistant. Render UI when a result is clearer shown than told.',
use: [a2ui()],
);Breaking Changes
Redesigned tool API around ToolResult and multipart (#350)
Tools now return a ToolResult<Output> instead of a bare value, using Dart dot-shorthand for ergonomics, and support multipart responses (structured output plus media parts). The stringly-typed actionType is now a typed ActionType. See the PR description for the full migration guide.
// BEFORE
fn: (input, ctx) async {
if (ctx.resumed == null) {
ctx.interrupt({'requiresConfirmation': true});
}
return 'Sunny in ${input.location}';
}
// AFTER
fn: (input, ctx) async {
if (ctx.resumed == null) {
return .interrupt({'requiresConfirmation': true});
}
return .response('Sunny in ${input.location}');
}Key points:
- Tools are now registered and resolved under
tool.v2; anything running a tool by key must use/tool.v2/<name>. ctx.interrupt(...)still works but is soft-deprecated.- Reading a tool's direct result (e.g.
tool.call) now yields aToolResult: read(result as ToolResponseResult).output. actionTypefields and params (includingRegistry.lookupAction) are now typed asActionType; custom types viaActionType('my-type')still work.
New Features
promptPartsparameter acrossgenerateandgenerateStream(lite API,GenkitAI, andgenerateHelper), letting you build a user message from a list ofPartobjects (text and media) as an alternative to a stringprompt. (#398)
final response = await ai.generate(
promptParts: [
TextPart(text: 'Describe this image:'),
MediaPart(media: Media(url: 'data:image/png;base64,abc123')),
],
);- Curated Gemini catalog wired into the googleAI plugin:
resolve()andlist()now pick up curatedModelInfo(label, stage, capabilities), with typedGoogleAiModelsrefs and graceful fallback to dynamic discovery. (#329) - Curated Claude catalog for the Anthropic plugin: known Claude models resolve with typed
ModelInfo, extended to 10 models with two-tier capability profiles, plus per-model thinking defaults. Unknown model names still resolve dynamically. (#322, #391, #396)
Bug Fixes
genkit (core)
- Register formatters in
lite.generateso the JSON formatter'sconstraineddefault andoutputInstructionsare applied to lite calls. (#377) - Map the tool role to
userfor Gemini API compatibility. (#340)
genkit_openai
- Stop silently dropping tools and mark o-series models as tool-capable; a model that truly lacks tools now errors loudly from the API. (#370)
- Populate usage metadata (
ModelResponse.usage) for both streaming and non-streaming responses. (#373) - Reject non-object tool input schemas with a clear
INVALID_ARGUMENTerror instead of a cryptic server 400. (#374)
genkit_google_genai
- Gate
responseJsonSchemaon constrained JSON mode and omitresponseMimeTypewhen unset. Also inherited bygenkit_vertexai. (#375) - Drop safety settings with an unset category instead of sending
HARM_CATEGORY_UNSPECIFIED(which the API rejects with 400). Also inherited bygenkit_vertexai. (#392)
genkit_anthropic
- Send PDF media as document blocks instead of
image/png, parse the mime from the data URL, and reject malformed input locally. (#390)
New Contributors
Full Changelog: genkit-v0.15.1...genkit-v0.16.0