
Genkit Dart
- 38 installs
- 154 repo stars
- Updated July 29, 2026
- genkit-ai/genkit-dart
Build AI-powered Dart applications with Genkit Dart — integrating LLMs (Gemini, Claude, OpenAI), structured outputs, tool calling, flows, and agentic workflows.
About
Genkit Dart is an AI SDK for Dart that provides a unified interface for text generation, structured outputs, tool calling, agentic workflows, and embeddings. It supports multiple LLM providers through a plugin system including Google Gemini, Anthropic Claude, OpenAI, Firebase AI, and MCP, with type-safe schema management via the schemantic library. Reach for this skill when building AI-powered Dart or Flutter applications that need flexible multi-provider LLM integration.
- Unified interface for text generation, tool calling, flows, and agents
- Plugins for Gemini, Claude, OpenAI, Firebase AI, and MCP
- Type-safe schemas via schemantic library
- Genkit CLI developer UI with execution tracing
- Streaming and embeddings support
Genkit Dart by the numbers
- 38 all-time installs (skills.sh)
- Ranked #8,450 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Jul 30, 2026 (Skillselion catalog sync)
npx skills add https://github.com/genkit-ai/genkit-dart --skill genkit-dartAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 38 |
|---|---|
| repo stars | ★ 154 |
| Last updated | July 29, 2026 |
| Repository | genkit-ai/genkit-dart ↗ |
What it does
Build AI-powered Dart applications with Genkit Dart — integrating LLMs (Gemini, Claude, OpenAI), structured outputs, tool calling, flows, and agentic workflows.
Who is it for?
Dart and Flutter developers adding LLM capabilities to their apps
Skip if: Non-Dart projects or pure Python/JavaScript AI workflows
What you get
- Dart AI application
- configured Genkit flows
- LLM integration code
Files
Genkit Dart
Genkit Dart is an AI SDK for Dart that provides a unified interface for code generation, structured outputs, tools, flows, and AI agents.
Core Features and Usage
If you need help with initializing Genkit (Genkit()), Generation (ai.generate), Tooling (ai.defineTool), Flows (ai.defineFlow), Embeddings (ai.embedMany), streaming, or calling remote flow endpoints, please load the core framework reference: references/genkit.md
Genkit CLI (recommended)
The Genkit CLI provides a local development UI for running Flow, tracing executions, playing with models, and evaluating outputs.
check if the user has it installed: genkit --version
Installation:
curl -sL cli.genkit.dev | bash # Native CLI
# OR
npm install -g genkit-cli # Via npmUsage: Wrap your run command with genkit start to attach the Genkit developer UI and tracing:
genkit start -- dart run main.dartPlugin Ecosystem
Genkit relies on a large suite of plugins to perform generative AI actions, interface with external LLMs, or host web servers.
When asked to use any given plugin, always verify usage by referring to its corresponding reference below. You should load the reference when you need to know the specific initialization arguments, tools, models, and usage patterns for the plugin:
| Plugin Name | Reference Link | Description |
|---|---|---|
genkit_google_genai | references/genkit_google_genai.md | Load for Google Gemini plugin interface usage. |
genkit_anthropic | references/genkit_anthropic.md | Load for Anthropic plugin interface for Claude models. |
genkit_openai | references/genkit_openai.md | Load for OpenAI plugin interface for GPT models, Groq, and custom compatible endpoints. |
genkit_middleware | references/genkit_middleware.md | Load for Tooling for specific agentic behavior: filesystem, skills, and toolApproval interrupts. |
genkit_mcp | references/genkit_mcp.md | Load for Model Context Protocol integration (Server, Host, and Client capabilities). |
genkit_chrome | references/genkit_chrome.md | Load for Running Gemini Nano locally inside the Chrome browser using the Prompt API. |
genkit_shelf | references/genkit_shelf.md | Load for Integrating Genkit Flow actions over HTTP using Dart Shelf. |
genkit_firebase_ai | references/genkit_firebase_ai.md | Load for Firebase AI plugin interface (Gemini API via Vertex AI). |
External Dependencies
Whenever you define schemas mapping inside of Tools, Flows, and Prompts, you must use the schemantic library. To learn how to use schemantic, ensure you read the schemantic skill for how to implement type safe generated Dart code. This is particularly relevant when you encounter symbols like @Schema(), SchemanticType, or classes with the $ prefix. Genkit Dart uses schemantic for all of its data models so it's a CRITICAL skill to understand for using Genkit Dart.
Best Practices
- Always check that code cleanly compiles using
dart analyzebefore generating the final response. - Always use the Genkit CLI for local development and debugging.
Genkit Anthropic Plugin (genkit_anthropic)
The Anthropic plugin for Genkit Dart, used for interacting with the Claude models.
Usage
Requires ANTHROPIC_API_KEY to be passed to the init block.
import 'dart:io';
import 'package:genkit/genkit.dart';
import 'package:genkit_anthropic/genkit_anthropic.dart';
void main() async {
final ai = Genkit(
plugins: [anthropic(apiKey: Platform.environment['ANTHROPIC_API_KEY']!)],
);
final response = await ai.generate(
model: anthropic.model('claude-sonnet-4-5'),
prompt: 'Tell me a joke about a developer.',
);
print(response.text);
}Claude Thinking Configurations
Provides specific configurations for utilizing Claude 3.7+ "thinking" model capabilities.
final response = await ai.generate(
model: anthropic.model('claude-sonnet-4-5'),
prompt: 'Solve this 24 game: 2, 3, 10, 10',
config: AnthropicOptions(thinking: ThinkingConfig(budgetTokens: 2048)),
);
// The thinking content is available in the message parts
print(response.message?.content);Genkit Chrome AI Plugin (genkit_chrome)
Chrome Built-in AI (Gemini Nano) plugin for Genkit Dart, allowing local offline execution within a Chrome application.
Usage
import 'package:genkit/genkit.dart';
import 'package:genkit_chrome/genkit_chrome.dart';
void main() async {
final ai = Genkit(plugins: [ChromeAIPlugin()]);
final stream = ai.generateStream(
model: modelRef('chrome/gemini-nano'),
prompt: 'Write a story about a robot.',
);
await for (final chunk in stream) {
print(chunk.text);
}
}Genkit Firebase AI Plugin (genkit_firebase_ai)
The Firebase AI plugin for Genkit Dart, used for interacting with Gemini APIs through Firebase AI Logic.
Usage
import 'package:genkit/genkit.dart';
import 'package:genkit_firebase_ai/genkit_firebase_ai.dart';
void main() async {
// Initialize Genkit with the Firebase AI plugin
final ai = Genkit(plugins: [firebaseAI()]);
// Generate text
final response = await ai.generate(
model: firebaseAI.gemini('gemini-2.5-flash'),
prompt: 'Tell me a joke about a developer.',
);
print(response.text);
}Genkit Google GenAI Plugin (genkit_google_genai)
The Google AI plugin provides an interface against the official Google AI Gemini API.
Usage
import 'package:genkit/genkit.dart';
import 'package:genkit_google_genai/genkit_google_genai.dart';
void main() async {
// Initialize Genkit with the Google AI plugin
final ai = Genkit(plugins: [googleAI()]);
// Generate text
final response = await ai.generate(
model: googleAI.gemini('gemini-2.5-flash'),
prompt: 'Tell me a joke about a developer.',
);
print(response.text);
}Embeddings
final embeddings = await ai.embedMany(
embedder: googleAI.textEmbedding('text-embedding-004'),
documents: [
DocumentData(content: [TextPart(text: 'Hello world')]),
],
);Image Generation
The plugin also supports image generation models such as gemini-2.5-flash-image.
Example (Nano Banana)
// Define an image generation flow
ai.defineFlow(
name: 'imageGenerator',
inputSchema: .string(defaultValue: 'A banana riding a bike'),
outputSchema: Media.$schema,
fn: (input, context) async {
final response = await ai.generate(
model: googleAI.gemini('gemini-2.5-flash-image'),
prompt: input,
);
if (response.media == null) {
throw Exception('No media generated');
}
return response.media!;
},
);
The media (url field) contain base64 encoded data uri. You can decode it and save it as a file.
## Text-to-Speech (TTS)
You can use text-to-speech models to generate audio from text. The generated `Media` object will contain base64 encoded PCM audio in its data URI.
// Define a TTS flow ai.defineFlow( name: 'textToSpeech', inputSchema: .string(defaultValue: 'Genkit is an amazing AI framework!'), outputSchema: Media.$schema, fn: (prompt, _) async { final response = await ai.generate( model: googleAI.gemini('gemini-2.5-flash-preview-tts'), prompt: prompt, config: GeminiTtsOptions( responseModalities: ['AUDIO'], speechConfig: SpeechConfig( voiceConfig: VoiceConfig( prebuiltVoiceConfig: PrebuiltVoiceConfig(voiceName: 'Puck'), ), ), ), );
if (response.media != null) { return response.media!; } throw Exception('No audio generated'); }, );
Google AI also supports multi-speaker TTS by configuring a `MultiSpeakerVoiceConfig` inside `SpeechConfig`.
Genkit MCP (genkit_mcp)
MCP (Model Context Protocol) integration for Genkit Dart.
MCP Host (Recommended)
Connect to one or more MCP servers and aggregate their capabilities into the Genkit registry automatically.
import 'package:genkit/genkit.dart';
import 'package:genkit_mcp/genkit_mcp.dart';
void main() async {
final ai = Genkit();
final host = defineMcpHost(
ai,
McpHostOptionsWithCache(
name: 'my-host',
mcpServers: {
'fs': McpServerConfig(
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-filesystem', '.'],
),
},
),
);
// Tools can be discovered and executed dynamically using a wildcard...
final response = await ai.generate(
model: 'gemini-2.5-flash',
prompt: 'Summarize the contents of README.md',
toolNames: ['my-host:tool/fs/*'],
);
// ...or by specifying the exact tool name
final exactResponse = await ai.generate(
model: 'gemini-2.5-flash',
prompt: 'Read README.md',
toolNames: ['my-host:tool/fs/read_file'],
);
}MCP Client (Advanced / Single Server)
Connecting to a single MCP server with a client object is an advanced usecase for when you need manual control over the client lifecycle. Standalone clients do not automatically register tools into the registry, so they must be passed into generate or defineDynamicActionProvider manually.
import 'package:genkit/genkit.dart';
import 'package:genkit_mcp/genkit_mcp.dart';
void main() async {
final ai = Genkit();
final client = createMcpClient(
McpClientOptions(
name: 'my-client',
mcpServer: McpServerConfig(
command: 'npx',
args: ['-y', '@modelcontextprotocol/server-filesystem', '.'],
),
),
);
await client.ready();
// Retrieve the tools from the connected client
final tools = await client.getActiveTools(ai);
final response = await ai.generate(
model: 'gemini-2.5-flash',
prompt: 'Read the contents of README.md',
tools: tools,
);
}MCP Server
Expose Genkit actions (tools, prompts, resources) over MCP.
import 'package:genkit/genkit.dart';
import 'package:genkit_mcp/genkit_mcp.dart';
void main() async {
final ai = Genkit();
ai.defineTool(
name: 'add',
description: 'Add two numbers together',
inputSchema: .map(.string(), .dynamicSChema()),
fn: (input, _) async => (input['a'] + input['b']).toString(),
);
ai.defineResource(
name: 'my-resource',
uri: 'my://resource',
fn: (_, _) async => ResourceOutput(content: [TextPart(text: 'my resource')]),
);
// Stdio transport by default
final server = createMcpServer(ai, McpServerOptions(name: 'my-server'));
await server.start();
}Streamable HTTP Transport
import 'dart:io';
final transport = await StreamableHttpServerTransport.bind(
address: InternetAddress.loopbackIPv4,
port: 3000,
);
await server.start(transport);Genkit Middleware (genkit_middleware)
A collection of useful middleware for Genkit Dart to enhance your agent's capabilities. Register plugins when initializing Genkit:
import 'package:genkit/genkit.dart';
import 'package:genkit_middleware/genkit_middleware.dart';
void main() {
final ai = Genkit(
plugins: [
FilesystemPlugin(),
SkillsPlugin(),
ToolApprovalPlugin(),
],
);
}Filesystem Middleware
Allows the agent to list, read, write, and search/replace files within a restricted root directory.
final response = await ai.generate(
prompt: 'Check the logs in the current directory.',
use: [
filesystem(rootDirectory: '/path/to/secure/workspace'),
],
);Tools Provided:
list_files,read_file,write_file,search_and_replace
Skills Middleware
Injects specialized instructions (skills) into the system prompt from SKILL.md files located in specified directories.
final response = await ai.generate(
prompt: 'Help me debug this issue.',
use: [
skills(skillPaths: ['/path/to/skills']),
],
);Tools Provided:
use_skill: Retrieve the full content of a skill by name.
Tool Approval Middleware
Intercepts tool execution for specified tools and requires explicit approval. Returns FinishReason.interrupted.
final response = await ai.generate(
prompt: 'Delete the database.',
use: [
// Require approval for all tools EXCEPT those below
toolApproval(approved: ['read_file', 'list_files']),
],
);
if (response.finishReason == FinishReason.interrupted) {
final interrupt = response.interrupts.first;
// Ask user for approval
final isApproved = await askUser();
if (isApproved) {
final resumeResponse = await ai.generate(
messages: response.messages, // Pass history
toolChoice: ToolChoice.none, // Prevent immediate re-call
interruptRestart: [
ToolRequestPart(
toolRequest: interrupt.toolRequest,
metadata: {
...?interrupt.metadata,
'tool-approved': true
},
),
],
);
}
}Genkit OpenAI Plugin (genkit_openai)
OpenAI-compatible API plugin for Genkit Dart. Supports OpenAI models and other compatible APIs (xAI, DeepSeek, Together AI, Groq, etc.).
Basic Usage
import 'dart:io';
import 'package:genkit/genkit.dart';
import 'package:genkit_openai/genkit_openai.dart';
void main() async {
final ai = Genkit(plugins: [
openAI(apiKey: Platform.environment['OPENAI_API_KEY']),
]);
final response = await ai.generate(
model: openAI.model('gpt-4o'),
prompt: 'Tell me a joke.',
);
}Options
OpenAIOptions allows configuring sampling temperature, nucleus sampling, token generation, seed, etc: config: OpenAIOptions(temperature: 0.7, maxTokens: 100)
Groq API override
Specify custom baseUrl and custom models to integrate with third-party providers.
final ai = Genkit(plugins: [
openAI(
apiKey: Platform.environment['GROQ_API_KEY'],
baseUrl: 'https://api.groq.com/openai/v1',
models: [
CustomModelDefinition(
name: 'llama-3.3-70b-versatile',
info: ModelInfo(
label: 'Llama 3.3 70B',
supports: {'multiturn': true, 'tools': true, 'systemRole': true},
),
),
],
),
]);
final response = await ai.generate(
model: openAI.model('llama-3.3-70b-versatile'),
prompt: 'Hello!',
);Genkit Shelf Plugin (genkit_shelf)
Shelf integration for Genkit Dart, used to serve Genkit Flows.
Standalone Server
Serve Genkit Flows easily on an isolated HTTP server using startFlowServer.
import 'package:genkit/genkit.dart';
import 'package:genkit_shelf/genkit_shelf.dart';
void main() async {
final ai = Genkit();
final flow = ai.defineFlow(
name: 'myFlow',
inputSchema: .string(),
outputSchema: .string(),
fn: (String input, _) async => 'Hello $input',
);
await startFlowServer(
flows: [flow],
port: 8080,
);
}Existing Shelf Application
Mount Genkit Flow endpoints directly to an existing Shelf Router using shelfHandler.
import 'package:genkit/genkit.dart';
import 'package:genkit_shelf/genkit_shelf.dart';
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_router/shelf_router.dart';
void main() async {
final ai = Genkit();
final flow = ai.defineFlow(
name: 'myFlow',
inputSchema: .string(),
outputSchema: .string(),
fn: (String input, _) async => 'Hello $input',
);
final router = Router();
// Mount the flow handler at a specific path
router.post('/myFlow', shelfHandler(flow));
// Start the server
await io.serve(router.call, 'localhost', 8080);
}Access deployed flows using genkit client libraries (from Dart or JS).
Genkit Core Framework
Genkit Dart is an AI SDK for Dart that provides a unified interface for text generation, structured output, tool calling, and agentic workflows.
Initialization
import 'package:genkit/genkit.dart';
import 'package:genkit_google_genai/genkit_google_genai.dart'; // Or any other plugin
void main() async {
// Pass plugins to use into the Genkit constructor
final ai = Genkit(plugins: [googleAI()]);
}Generate Text
final response = await ai.generate(
model: googleAI.gemini('gemini-2.5-flash'), // Needs a model reference from a plugin
prompt: 'Explain quantum computing in simple terms.',
);
print(response.text);Stream Responses
final stream = ai.generateStream(
model: googleAI.gemini('gemini-2.5-flash'),
prompt: 'Write a short story about a robot learning to paint.',
);
await for (final chunk in stream) {
print(chunk.text);
}Embed Text
final embeddings = await ai.embedMany(
documents: [
DocumentData(content: [TextPart(text: 'Hello world')]),
],
embedder: googleAI.textEmbedding('text-embedding-004'),
);
print(embeddings.first.embedding);Define Tools
Models can use define actions and access external data via custom defined tools. Requires the schemantic library for schema definitions.
import 'package:schemantic/schemantic.dart';
@Schema()
abstract class $WeatherInput {
String get location;
}
final weatherTool = ai.defineTool(
name: 'getWeather',
description: 'Gets the current weather for a location',
inputSchema: WeatherInput.$schema,
fn: (input, _) async {
// Call your weather API here
return 'Weather in ${input.location}: 72°F and sunny';
},
);
final response = await ai.generate(
model: googleAI.gemini('gemini-2.5-flash'),
prompt: 'What\'s the weather like in San Francisco?',
toolNames: ['getWeather'], // Use the tools
);Structured Output
You can ensure the generative model returns a typed JSON object by providing an outputSchema.
@Schema()
abstract class $Person {
String get name;
int get age;
}
// ... inside main ...
final response = await ai.generate(
model: googleAI.gemini('gemini-2.5-flash'),
prompt: 'Generate a person named John Doe, age 30',
outputSchema: Person.$schema, // Force the model to return this schema
);
final person = response.output; // Typed Person object
print('Name: ${person.name}, Age: ${person.age}');Define Flows
Wrap your AI logic in flows for better observability, testing, and deployment:
final jokeFlow = ai.defineFlow(
name: 'tellJoke',
inputSchema: .string(),
outputSchema: .string(),
fn: (topic, _) async {
final response = await ai.generate(
model: googleAI.gemini('gemini-2.5-flash'),
prompt: 'Tell me a joke about $topic',
);
return response.text; // Value return
},
);
final joke = await jokeFlow('programming');
print(joke);Streaming Flows
Stream data from your flows using context.sendChunk(...) and returning the final value:
final streamStory = ai.defineFlow(
name: 'streamStory',
inputSchema: .string(),
outputSchema: .string(),
streamSchema: .string(),
fn: (topic, context) async {
final stream = ai.generateStream(
model: googleAI.gemini('gemini-2.5-flash'),
prompt: 'Write a story about $topic',
);
await for (final chunk in stream) {
context.sendChunk(chunk.text); // Stream the chunks
}
return 'Story complete'; // Value return
},
);Calling remote Flows from a dart client
The genkit package provides package:genkit/client.dart representing remote Genkit actions that can be invoked or streamed using type-safe definitions.
1. Defines a remote action
import 'package:genkit/client.dart';
final stringAction = defineRemoteAction(
url: 'http://localhost:3400/my-flow',
inputSchema: .string(),
outputSchema: .string(),
);2. Call the Remote Action (Non-streaming)
final response = await stringAction(input: 'Hello from Dart!');
print('Flow Response: $response');3. Call the Remote Action (Streaming) Use the .stream() method on the action flow, and access stream.onResult to wait on the async return value.
final streamAction = defineRemoteAction(
url: 'http://localhost:3400/stream-story',
inputSchema: .string(),
outputSchema: .string(),
streamSchema: .string(),
);
final stream = streamAction.stream(
input: 'Tell me a short story about a Dart developer.',
);
await for (final chunk in stream) {
print('Chunk: $chunk');
}
final finalResult = await stream.onResult;
print('\nFinal Response: $finalResult');Calling remote Flows from a Javascript client
Install genkit npm package:
npm install genkit1. Call a remote flow (non-streaming)
import { runFlow } from 'genkit/beta/client';
async function callHelloFlow() {
try {
const result = await runFlow({
url: 'http://127.0.0.1:3400/helloFlow', // Replace with your deployed flow's URL
input: { name: 'Genkit User' },
});
console.log('Non-streaming result:', result.greeting);
} catch (error) {
console.error('Error calling helloFlow:', error);
}
}
callHelloFlow();2. Call a remote flow (streaming)
import { streamFlow } from 'genkit/beta/client';
async function streamHelloFlow() {
try {
const result = streamFlow({
url: 'http://127.0.0.1:3400/helloFlow', // Replace with your deployed flow's URL
input: { name: 'Streaming User' },
});
// Process the stream chunks as they arrive
for await (const chunk of result.stream) {
console.log('Stream chunk:', chunk);
}
// Get the final complete response
const finalOutput = await result.output;
console.log('Final streaming output:', finalOutput.greeting);
} catch (error) {
console.error('Error streaming helloFlow:', error);
}
}
streamHelloFlow();Data Models
Genkit uses standard data models for representing prompts (messages & parts) and responses. These classes are implemented using schemantic library.
import 'package:genkit/genkit.dart';
import 'package:schemantic/schemantic.dart';
@Schema()
abstract class $MyDataModel {
// uses Genkit's Message schema (not schemantic's Message)
List<$Message> get messages;
List<$Part> get parts;
}
void example() {
// --- Parts ---
// A Text part
final textPart = TextPart(text: 'some text', metadata: {'foo': 'bar'});
// A Media/Image part
final mediaPart = MediaPart(
media: Media(url: 'https://...', contentType: 'image/png'),
metadata: {'foo': 'bar'},
);
// A Tool Request initiated by the model
final toolRequestPart = ToolRequestPart(
toolRequest: ToolRequest(
name: 'get_weather',
ref: 'abc',
input: {'location': 'Paris, France'},
),
metadata: {'foo': 'bar'},
);
// The resulting data from a Tool execution
final toolResponsePart = ToolResponsePart(
toolResponse: ToolResponse(
name: 'get_weather',
ref: 'abc',
output: {'temperature': '20C'},
),
metadata: {'foo': 'bar'},
);
// Model reasoning (e.g. for Claude's "thinking" models)
final reasoningPart = ReasoningPart(
reasoning: 'thinking...',
metadata: {'foo': 'bar'},
);
// A custom fallback part
final customPart = CustomPart(
custom: {'provider': {'specific': 'data'}},
metadata: {'foo': 'bar'},
);
// --- Messages ---
final systemMessage = Message(
role: Role.system,
content: [textPart, mediaPart],
metadata: {'foo': 'bar'},
);
final userMessage = Message(
role: Role.user,
content: [textPart, mediaPart], // Can contain media (multimodal)
);
final modelMessage = Message(
role: Role.model,
// Models can emit text, tool requests, reasoning, or custom parts
content: [textPart, toolRequestPart, reasoningPart, customPart],
);
// --- Ergonomic Data Access (schema_extensions.dart) ---
// The Genkit SDK provides extensions on `Message` and `Part` to easily access fields
// without needing to cast them manually.
// Get concatenated text from all TextParts in a Message
print(modelMessage.text);
// Get the first Media object from a Message
print(modelMessage.media?.url);
// Iterate over tool requests in a Message
for (final toolReq in modelMessage.toolRequests) {
print(toolReq.name);
}
// Inspect individual parts
for (final part in modelMessage.content) {
if (part.isText) print(part.text);
if (part.isMedia) print(part.media?.url);
if (part.isToolRequest) print(part.toolRequest?.name);
if (part.isToolResponse) print(part.toolResponse?.name);
if (part.isReasoning) print(part.reasoning);
if (part.isCustom) print(part.custom);
}
// --- Streaming Chunks ---
// Data emitted by ai.generateStream() calls
final generateResponseChunk = ModelResponseChunk(
content: [textPart],
index: 0, // Index of the message this chunk belongs to
aggregated: false,
);
// Chunks also have text and media accessors
print(generateResponseChunk.text);
// --- Advanced: Schemas ---
// Use Genkit type schemas directly in Schemantic validations
final messageSchema = Message.$schema;
final partSchema = Part.$schema;
final mySchema = SchemanticType.map(
.string(),
.list(Message.$schema), // Requires a list of Messages
);
// --- Generate Response ---
// ai.generate() returns a GenerateResponseHelper which provides ergonomic getters
// over the underlying ModelResponse:
final response = await ai.generate(...);
print(response.text); // Concatenated text
print(response.media?.url); // First media part
print(response.toolRequests); // All tool requests
print(response.interrupts); // Tool requests that triggered an interrupt
print(response.messages); // Full history of the conversation, including the request and response
print(response.output); // Structured typed output (if outputSchema was used)
}