Skip to main content

toAISdkStream()

Converts Mastra streams (agent, network, or workflow) to AI SDK-compatible streams. Use this function when you need to manually transform Mastra streams for use with AI SDK's createUIMessageStream() and createUIMessageStreamResponse().

This is useful when building custom streaming endpoints outside Mastra's provided route helpers such as chatRoute() or workflowRoute().

Usage example
Direct link to Usage example

Next.js App Router example:

app/api/chat/route.ts
import { mastra } from "../../mastra";
import { createUIMessageStream, createUIMessageStreamResponse } from "ai";
import { toAISdkStream } from "@mastra/ai-sdk";

export async function POST(req: Request) {
const { messages } = await req.json();
const myAgent = mastra.getAgent("weatherAgent");
const stream = await myAgent.stream(messages);

const uiMessageStream = createUIMessageStream({
originalMessages: messages,
execute: async ({ writer }) => {
for await (const part of toAISdkStream(stream, { from: "agent" })) {
await writer.write(part);
}
},
});

return createUIMessageStreamResponse({
stream: uiMessageStream,
});
}
tip

Pass messages to originalMessages in createUIMessageStream() to avoid duplicated assistant messages in the UI. See Troubleshooting: Repeated Assistant Messages for details.

Parameters
Direct link to Parameters

The first parameter is the Mastra stream to convert. It can be one of:

  • MastraModelOutput - An agent stream from agent.stream()
  • MastraAgentNetworkStream - A network stream from agent.network()
  • MastraWorkflowStream or WorkflowRunOutput - A workflow stream

The second parameter is an options object:

from:

'agent' | 'network' | 'workflow'
= 'agent'
The type of Mastra stream being converted.

lastMessageId?:

string
(Agent only) The ID of the last message in the conversation.

sendStart?:

boolean
= true
(Agent only) Whether to send start events in the stream.

sendFinish?:

boolean
= true
(Agent only) Whether to send finish events in the stream.

sendReasoning?:

boolean
= false
(Agent only) Whether to include reasoning-delta chunks in the stream. Set to true to stream reasoning content from models that support extended thinking.

sendSources?:

boolean
= false
(Agent only) Whether to include source citations in the output.

includeTextStreamParts?:

boolean
= true
(Workflow only) Whether to include text stream parts in the output.

messageMetadata?:

(options: { part: UIMessageStreamPart }) => Record<string, unknown> | undefined
(Agent only) A function that receives the current stream part and returns metadata to attach to start and finish chunks.

onError?:

(error: unknown) => string
(Agent only) A function to handle errors during stream conversion. Receives the error and should return a string representation.

Examples
Direct link to Examples

Converting a workflow stream
Direct link to Converting a workflow stream

app/api/workflow/route.ts
import { mastra } from "../../mastra";
import { createUIMessageStream, createUIMessageStreamResponse } from "ai";
import { toAISdkStream } from "@mastra/ai-sdk";

export async function POST(req: Request) {
const { input } = await req.json();
const workflow = mastra.getWorkflow("myWorkflow");
const run = workflow.createRun();
const stream = await run.stream({ inputData: input });

const uiMessageStream = createUIMessageStream({
execute: async ({ writer }) => {
for await (const part of toAISdkStream(stream, { from: "workflow" })) {
await writer.write(part);
}
},
});

return createUIMessageStreamResponse({
stream: uiMessageStream,
});
}

Converting a network stream
Direct link to Converting a network stream

app/api/network/route.ts
import { mastra } from "../../mastra";
import { createUIMessageStream, createUIMessageStreamResponse } from "ai";
import { toAISdkStream } from "@mastra/ai-sdk";

export async function POST(req: Request) {
const { messages } = await req.json();
const routingAgent = mastra.getAgent("routingAgent");
const stream = await routingAgent.network(messages);

const uiMessageStream = createUIMessageStream({
execute: async ({ writer }) => {
for await (const part of toAISdkStream(stream, { from: "network" })) {
await writer.write(part);
}
},
});

return createUIMessageStreamResponse({
stream: uiMessageStream,
});
}

Converting an agent stream with reasoning enabled
Direct link to Converting an agent stream with reasoning enabled

app/api/reasoning/route.ts
import { mastra } from "../../mastra";
import { createUIMessageStream, createUIMessageStreamResponse } from "ai";
import { toAISdkStream } from "@mastra/ai-sdk";

export async function POST(req: Request) {
const { messages } = await req.json();
const reasoningAgent = mastra.getAgent("reasoningAgent");
const stream = await reasoningAgent.stream(messages, {
providerOptions: {
openai: { reasoningEffort: "high" },
},
});

const uiMessageStream = createUIMessageStream({
originalMessages: messages,
execute: async ({ writer }) => {
for await (const part of toAISdkStream(stream, {
from: "agent",
sendReasoning: true,
})) {
await writer.write(part);
}
},
});

return createUIMessageStreamResponse({
stream: uiMessageStream,
});
}

Using messageMetadata
Direct link to Using messageMetadata

app/api/chat-with-metadata/route.ts
import { mastra } from "../../mastra";
import { createUIMessageStream, createUIMessageStreamResponse } from "ai";
import { toAISdkStream } from "@mastra/ai-sdk";

export async function POST(req: Request) {
const { messages } = await req.json();
const myAgent = mastra.getAgent("weatherAgent");
const stream = await myAgent.stream(messages);

const uiMessageStream = createUIMessageStream({
originalMessages: messages,
execute: async ({ writer }) => {
for await (const part of toAISdkStream(stream, {
from: "agent",
messageMetadata: ({ part }) => ({
timestamp: Date.now(),
partType: part.type,
}),
})) {
await writer.write(part);
}
},
});

return createUIMessageStreamResponse({
stream: uiMessageStream,
});
}

Client-side stream transformation
Direct link to Client-side stream transformation

If you're using the Mastra client SDK (@mastra/client-js) on the client side and want to convert streams to AI SDK format:

client-stream-to-ai-sdk.ts
import { MastraClient } from "@mastra/client-js";
import { createUIMessageStream } from "ai";
import { toAISdkStream } from "@mastra/ai-sdk";
import type { ChunkType, MastraModelOutput } from "@mastra/core/stream";

const client = new MastraClient({
baseUrl: "http://localhost:4111",
});

const agent = client.getAgent("weatherAgent");
const response = await agent.stream("What is the weather in Tokyo?");

// Convert the client SDK stream to a ReadableStream<ChunkType>
const chunkStream = new ReadableStream<ChunkType>({
async start(controller) {
await response.processDataStream({
onChunk: async (chunk) => {
controller.enqueue(chunk);
},
});
controller.close();
},
});

// Transform to AI SDK format
const uiMessageStream = createUIMessageStream({
execute: async ({ writer }) => {
for await (const part of toAISdkStream(
chunkStream as unknown as MastraModelOutput,
{ from: "agent" }
)) {
await writer.write(part);
}
},
});

for await (const part of uiMessageStream) {
console.log(part);
}