genkit-aisdk-converter
v0.1.0
Published
Convert between Genkit and AI SDK UI message stream protocols
Readme
genkit-aisdk-converter
Convert between Genkit generateStream output and the AI SDK UI message stream protocol. Use Genkit on the server for generation while staying compatible with AI SDK clients (useChat, AIChatAgent, etc.).
Overview
- Incoming (client → server): AI SDK UI messages → Genkit
MessageData[]so your endpoint can pass them into Genkit. - Outgoing (server → client): Genkit
GenerateResponseChunkstream → AI SDK UI message stream so the same endpoint can return aResponsethatuseChat()and other AI SDK clients consume.
Installation
npm install genkit-aisdk-converterPeer dependencies: ai (≥6.0.0) and @genkit-ai/ai (≥1.0.0). Install them if not already present:
npm install ai @genkit-ai/aiUsage
Outgoing: Genkit stream → AI SDK response
Turn a Genkit generateStream() result into a streaming Response for useChat():
import { ai } from "@genkit-ai/ai";
import {
genkitStreamToUIMessageStreamResponse,
uiMessagesToGenkitMessages,
} from "genkit-aisdk-converter";
// In your route/handler:
const genkitStream = await ai.generateStream({
model: "your-model",
messages: uiMessagesToGenkitMessages(messages), // convert client messages
});
const response = genkitStreamToUIMessageStreamResponse(
genkitStream
);
return response;To get a ReadableStream<UIMessageChunk> instead of a Response (e.g. to wrap with createUIMessageStreamResponse() yourself):
import { genkitChunksToUIMessageStream } from "genkit-aisdk-converter";
import { createUIMessageStreamResponse } from "ai";
const stream = genkitChunksToUIMessageStream(genkitStream, options);
return createUIMessageStreamResponse(stream);Incoming: AI SDK messages → Genkit messages
Convert messages from useChat (or AIChatAgent) to Genkit MessageData[] before calling ai.generateStream():
import { uiMessagesToGenkitMessages } from "genkit-aisdk-converter";
const genkitMessages = uiMessagesToGenkitMessages(messages, {
ignoreIncompleteToolCalls: true, // omit tool parts still streaming input
convertDataPart: (part) => {
// Optional: AI SDK "data" parts (e.g. type "data-custom") are arbitrary JSON.
// Genkit only accepts text/media, so by default they are skipped. Return
// { text } or { media } to include them in the Genkit message.
if (part.data?.type === "caption") return { text: part.data.text };
return undefined;
},
});Handled UI part types: text, file, reasoning, tool (with ref/name/input/output and approval/denied), data (with optional convertDataPart), step-start, source-url, source-document.
API
| Export | Description |
|--------|-------------|
| uiMessagesToGenkitMessages(messages, options?) | AI SDK UIMessage[] → Genkit MessageData[]. |
| genkitChunksToUIMessageStream(genkitStream, options?) | AsyncIterable<GenerateResponseChunk> → ReadableStream<UIMessageChunk>. |
| genkitStreamToUIMessageStreamResponse(genkitStream, init?, options?) | Same stream converted into a streaming Response for AI SDK clients. |
Options
UiMessagesToGenkitOptions (incoming)
convertDataPart?(part)– AI SDK "data" parts (e.g.data-custom) are arbitrary JSON and are skipped by default. Return{ text }or{ media }to include them in Genkit messages; returnundefinedto keep skipping.ignoreIncompleteToolCalls?– Whentrue, omit tool parts ininput-streaming/input-available(aligns withconvertToModelMessages).
GenkitStreamToUIMessageOptions (outgoing)
responsePromise?– e.g.genkitStream.response; when provided, the real Genkit finish reason is used on the finalfinishchunk (otherwise it defaults to"stop").messageMetadata?– Attached tostartandfinishchunks (e.g. for tracing).dataPartTypeName?– Name for custom/data chunks (default'custom'); alphanumeric and hyphen only; producesdata-<name>.toolResponseToChunk?– Optional mapper to emittool-output-errorortool-output-deniedinstead oftool-output-available.
Protocol mapping
- Genkit chunks:
{ index, role, content: Part[] }withPart={ text }|{ toolRequest }|{ reasoning }|{ media }| custom, etc. - AI SDK UI stream:
start→text-start/text-delta/text-end,reasoning-start/reasoning-delta/reasoning-end,tool-input-start/tool-input-available/tool-output-available,finish-step,finish→[DONE].
License
MIT · Invertase
