Custom data stream example code
```ts
import {
createUIMessageStream,
pipeUIMessageStreamToResponse,
streamText,
toUIMessageStream,
} from 'ai';
import express, { Request, Response } from 'express';
const app = express();
app.post('/custom-data-parts', async (req: Request, res: Response) => {
pipeUIMessageStreamToResponse({
response: res,
stream: createUIMessageStream({
execute: async ({ writer }) => {
writer.write({ type: 'start' });
writer.write({
type: 'data-custom',
data: {
custom: 'Hello, world!',
},
});
const result = streamText({
model: 'openai/gpt-4o',
prompt: 'Invent a new holiday and describe its traditions.',
});
writer.merge(
toUIMessageStream({ stream: result.stream, sendStart: false }),
);
},
}),
});
});
app.listen(8080, () => {
console.log(`Example app listening on port ${8080}`);
});
```
This example demonstrates how to use createUIMessageStream to send custom data to the client alongside AI-generated text.
Text Stream using pipeTextStreamToResponse
To send a plain text stream to the client, use pipeTextStreamToResponse with a stream generated by toTextStream. Pass the result.stream from streamText to toTextStream and pipe it to the response object.
Text stream example code
```ts
import { pipeTextStreamToResponse, streamText, toTextStream } from 'ai';
import express, { Request, Response } from 'express';
const app = express();
app.post('/', async (req: Request, res: Response) => {
const result = streamText({
model: 'openai/gpt-4o',
prompt: 'Invent a new holiday and describe its traditions.',
});
pipeTextStreamToResponse({
response: res,
stream: toTextStream({ stream: result.stream }),
});
});
app.listen(8080, () => {
console.log(`Example app listening on port ${8080}`);
});
```
This example shows how to stream plain text using pipeTextStreamToResponse.
AI SDK Express examples repository
A full working example of using the AI SDK with Express is available at github.com/vercel/ai/examples/express.
toUIMessageStream sendStart option
When using `createUIMessageStream` with `writer.merge()` to combine multiple streams, you can pass `{ sendStart: false }` to `toUIMessageStream` to prevent sending duplicate start messages if custom data is already being written.
AI SDK Fastify example repository
A full working example of the AI SDK with Fastify is available at github.com/vercel/ai/tree/main/examples/fastify.
Fastify direct textStream piping
For simple text streaming without UI message wrapping, access the `textStream` property directly from the `streamText` result and send it in the Fastify reply. Set the Content-Type header to 'text/plain; charset=utf-8'. This bypasses message stream formatting and pipes raw text directly to the response.
Fastify server setup for AI SDK
Fastify servers using the AI SDK should listen on port 8080 by default for examples. Initialize Fastify with `Fastify({ logger: true })`, then define POST routes that handle streaming responses. Use `fastify.listen({ port: 8080 })` to start the server. The AI Gateway API key should be set in the `AI_GATEWAY_API_KEY` environment variable.
Fastify createUIMessageStream for custom data
The `createUIMessageStream` function allows streaming custom data to the client. It takes an `execute` function that receives a `writer` object with methods `write()` to add data and `merge()` to combine streams. Provide data objects with a `type` field (e.g., 'data-custom') and optional `data` property. The `onError` callback allows customizing error messages sent to the client. Set Content-Type header to 'text/plain; charset=utf-8'.
Fastify streamText with toUIMessageStream
To stream text from a Fastify POST endpoint, use the `streamText` function with `toUIMessageStream` helper to convert the result stream to a UI message stream. Set the Content-Type header to 'text/plain; charset=utf-8' and send the converted stream in the reply. The `streamText` call takes a model and prompt, returning a result object with a stream property that can be passed to `toUIMessageStream`.
UI Message Stream code example
```ts
import { serve } from '@hono/node-server';
import {
createUIMessageStreamResponse,
streamText,
toUIMessageStream,
} from 'ai';
import { Hono } from 'hono';
const app = new Hono();
app.post('/', async c => {
const result = streamText({
model: 'openai/gpt-4o',
prompt: 'Invent a new holiday and describe its traditions.',
});
return createUIMessageStreamResponse({
stream: toUIMessageStream({ stream: result.stream }),
});
});
serve({ fetch: app.fetch, port: 8080 });
```
This example creates a Hono server that streams UI messages in response to POST requests.
Hono server setup with AI SDK
The AI SDK can be used in a Hono server to generate and stream text and objects to the client. A basic Hono server listens on port 8080. The Vercel AI Gateway API key must be set in the AI_GATEWAY_API_KEY environment variable.
UI Message Stream in Hono example
Create a UI message stream response using createUIMessageStreamResponse with toUIMessageStream. Import serve from @hono/node-server, and createUIMessageStreamResponse, streamText, toUIMessageStream from ai, and Hono from hono. In a POST route, call streamText with model and prompt parameters, then return createUIMessageStreamResponse with the toUIMessageStream-wrapped result stream. Start the server with serve({ fetch: app.fetch, port: 8080 }).
Text Stream in Hono example
Create a plain text stream response using createTextStreamResponse with toTextStream. In a POST route, call streamText with model and prompt parameters, then return createTextStreamResponse with the toTextStream-wrapped result stream.
Text Stream code example
```ts
import { serve } from '@hono/node-server';
import { createTextStreamResponse, streamText, toTextStream } from 'ai';
import { Hono } from 'hono';
const app = new Hono();
app.post('/text', async c => {
const result = streamText({
model: 'openai/gpt-4o',
prompt: 'Write a short poem about coding.',
});
return createTextStreamResponse({
stream: toTextStream({ stream: result.stream }),
});
});
serve({ fetch: app.fetch, port: 8080 });
```
This example creates a Hono server that streams plain text in response to POST requests.
Sending custom data in UI message stream
Use createUIMessageStream to build custom streaming responses that include both AI-generated text and custom application data. The execute function receives a writer object that has write() and merge() methods. Call writer.write() to send individual data chunks with a type property, and writer.merge() to combine multiple streams. Set sendStart: false in toUIMessageStream options to avoid duplicate start messages when merging.
Custom data streaming code example
```ts
import { serve } from '@hono/node-server';
import {
createUIMessageStream,
createUIMessageStreamResponse,
streamText,
toUIMessageStream,
} from 'ai';
import { Hono } from 'hono';
const app = new Hono();
app.post('/stream-data', async c => {
const stream = createUIMessageStream({
execute: ({ writer }) => {
writer.write({ type: 'start' });
writer.write({
type: 'data-custom',
data: {
custom: 'Hello, world!',
},
});
const result = streamText({
model: 'openai/gpt-4o',
prompt: 'Invent a new holiday and describe its traditions.',
});
writer.merge(
toUIMessageStream({
stream: result.stream,
sendStart: false,
onError: error => {
return error instanceof Error ? error.message : String(error);
},
}),
);
},
});
return createUIMessageStreamResponse({ stream });
});
serve({ fetch: app.fetch, port: 8080 });
```
This example shows how to send custom data alongside AI-generated text in a Hono server.
toUIMessageStream error masking option
The toUIMessageStream function accepts an onError callback to handle error messages. Error messages are masked by default for security reasons. To expose error messages to the client, define the onError callback to return error.message for Error instances or String(error) for other error types.
Nest.js example repository reference
A full working example of AI SDK integration with Nest.js is available at github.com/vercel/ai/examples/nest
AI SDK Nest.js integration overview
The AI SDK supports building Nest.js servers that generate and stream text and objects to clients. Use helpers like pipeUIMessageStreamToResponse, pipeTextStreamToResponse, streamText, toUIMessageStream, toTextStream, and createUIMessageStream to implement streaming endpoints.
Nest.js text stream example
Use pipeTextStreamToResponse helper with toTextStream to stream plain text to the response. Call streamText with model and prompt options, then pipe the result stream through toTextStream and pipeTextStreamToResponse to the Express Response object.
Nest.js send custom data with UI message stream
Use createUIMessageStream with pipeUIMessageStreamToResponse to send custom data alongside AI-generated content. The execute function receives a writer object. Call writer.write() to send custom data objects with a type field. Use writer.merge() to include a text stream from toUIMessageStream, passing sendStart: false and an onError handler that returns masked error messages by default but can expose full error messages if needed for client error handling.
Nest.js controller UI message stream example
Use the pipeUIMessageStreamToResponse helper with streamText and toUIMessageStream in a Nest.js controller to stream text to the client. Import Controller, Post, Res from @nestjs/common and pipeUIMessageStreamToResponse, streamText, toUIMessageStream from 'ai'. Define a Post route handler that takes a Response parameter, calls streamText with model and prompt options, then pipes the result to the response using pipeUIMessageStreamToResponse and toUIMessageStream.
RSC text streaming code example
Server action (app/actions.ts):
```typescript
'use server';
import { streamText } from 'ai';
import { createStreamableValue } from '@ai-sdk/rsc';
export async function generate(input: string) {
const stream = createStreamableValue('');
(async () => {
const { textStream } = streamText({
model: 'openai/gpt-5.4',
prompt: input,
});
for await (const delta of textStream) {
stream.update(delta);
}
stream.done();
})();
return { output: stream.value };
}
```
Client component (app/page.tsx):
```tsx
'use client';
import { useState } from 'react';
import { generate } from './actions';
import { readStreamableValue } from '@ai-sdk/rsc';
export const maxDuration = 30;
export default function Home() {
const [generation, setGeneration] = useState<string>('');
return (
<div>
<button
onClick={async () => {
const { output } = await generate('Why is the sky blue?');
for await (const delta of readStreamableValue(output)) {
setGeneration(currentGeneration => `${currentGeneration}${delta}`);
}
}}
>
Ask
</button>
<div>{generation}</div>
</div>
);
}
```
streamText function parameters
The `streamText` function from the `ai` package takes a configuration object with at least `model` and `prompt` properties. The `model` parameter specifies which AI model to use (e.g., 'openai/gpt-5.4'). The function returns an object containing `textStream` which is an async iterable of text deltas.
Stream text in RSC with streamText and createStreamableValue
To stream text generation in React Server Components, use the `streamText` function from the `ai` package on the server and wrap it with `createStreamableValue` from `@ai-sdk/rsc`. On the client, consume the stream with `readStreamableValue` in a for-await loop to update state incrementally. The server should call `stream.update(delta)` for each generated chunk and `stream.done()` when complete. Set `maxDuration` to at least 30 seconds to allow streaming responses.
partialOutputStream for iterating partial objects
The partialOutputStream from streamText provides an async iterator over partial objects as they are generated. Iterate with for-await-of to receive updates during generation.
Stream object with React Server Components
Object generation can be streamed to the client in real-time using React Server Components. This allows displaying generated objects as they are being produced rather than waiting for completion. Use streamText with Output.object on the server and readStreamableValue on the client to iterate over partial objects.
createStreamableValue for server streaming
The createStreamableValue function from @ai-sdk/rsc creates a streamable value on the server. Call stream.update() in a loop to send partial objects to the client, and stream.done() when streaming is complete.
maxDuration for streaming responses
Set maxDuration export to control the maximum time allowed for streaming responses. For example, export const maxDuration = 30; allows streaming for up to 30 seconds.
Stream object example with notifications schema
This example shows streaming object generation with a schema for notifications. Client code: Call generate('Messages during finals week.'), then use for-await with readStreamableValue(object) to iterate partialObject and update state with JSON.stringify(partialObject.notifications, null, 2). Server code: Use streamText with Output.object({schema: z.object({notifications: z.array(z.object({name: z.string().describe('Name of a fictional person.'), message: z.string().describe('Do not use emojis or links.'), minutesAgo: z.number()}))})}) in a 'use server' function, then for-await over partialOutputStream calling stream.update(partialObject) and stream.done() when done.
Output.object for structured object streaming
Use Output.object() in streamText to specify a Zod schema for streaming structured data. This generates objects that conform to the schema while streaming partial results to the client.
streamUI with token usage recording example
Example showing how to use streamUI with onFinish to record token usage:
const result = await streamUI({
model: 'openai/gpt-5.4',
messages: [...history.get(), { role: 'user', content: input }],
text: ({ content, done }) => {
if (done) {
history.done((messages: ServerMessage[]) => [
...messages,
{ role: 'assistant', content },
]);
}
return <div>{content}</div>;
},
tools: {
deploy: {
description: 'Deploy repository to vercel',
inputSchema: z.object({
repositoryName: z
.string()
.describe('The name of the repository, example: vercel/ai-chatbot'),
}),
generate: async function* ({ repositoryName }) {
yield <div>Cloning repository {repositoryName}...</div>;
await new Promise(resolve => setTimeout(resolve, 3000));
yield <div>Building repository {repositoryName}...</div>;
await new Promise(resolve => setTimeout(resolve, 2000));
return <div>{repositoryName} deployed!</div>;
},
},
},
onFinish: ({ usage }) => {
const { promptTokens, completionTokens, totalTokens } = usage;
console.log('Prompt tokens:', promptTokens);
console.log('Completion tokens:', completionTokens);
console.log('Total tokens:', totalTokens);
},
});
streamUI onFinish callback signature and usage fields
The onFinish callback receives an object with a usage property. The usage object contains: promptTokens (number of tokens in the prompt), completionTokens (number of tokens in the completion), and totalTokens (sum of prompt and completion tokens). Example: onFinish: ({ usage }) => { const { promptTokens, completionTokens, totalTokens } = usage; }
Record token usage with streamUI onFinish callback
The streamUI function accepts an onFinish callback that is invoked when the stream completes. The callback receives an object with a usage property containing promptTokens, completionTokens, and totalTokens fields. This allows you to record token usage for billing purposes after streaming UI responses.
Set maxDuration for streaming responses
Export a `maxDuration` constant in client components that stream responses to allow the response to continue streaming for that duration in seconds. For example, `export const maxDuration = 30;` allows streaming for up to 30 seconds.
Stream text with chat prompt using AI SDK
To stream chat completions with the AI SDK, use the `streamText` function on the server with a model identifier and message history. The function returns a textStream that can be iterated with `for await` to get text deltas. Use `createStreamableValue` to wrap the stream for transfer to the client, then call `stream.update()` for each delta and `stream.done()` when complete.
streamText API parameters
The streamText function accepts the following parameters: `model` (string, required) - the model identifier like 'openai/gpt-5.4'; `system` (string, optional) - system prompt to guide the model behavior; `messages` (array, required) - message history with role and content properties.
generateText include parameter
The include parameter is optional and controls inclusion of data in step results: requestBody (boolean, default false, can be large with images/files), requestMessages (boolean, default false, can be large with images/files), responseBody (boolean, default false). By default these are excluded to reduce memory usage.
GenerateTextStartEvent structure
GenerateTextStartEvent contains: provider (string like 'openai'), modelId (string like 'gpt-4o'), instructions (Instructions|undefined), messages (Array<ModelMessage>), tools (TOOLS|undefined), toolChoice (ToolChoice<TOOLS>|undefined), activeTools (ActiveTools<TOOLS>), toolOrder (ToolOrder<TOOLS>), maxOutputTokens, temperature, topP, topK, presencePenalty, frequencyPenalty, stopSequences, seed, maxRetries, timeout, headers, providerOptions, output (OUTPUT|undefined), abortSignal, include, runtimeContext, and toolsContext.
generateText onLanguageModelCallEnd callback
The onLanguageModelCallEnd callback is optional and is called after model response is normalized and parsed, before client-side tool execution. Signature: (event: LanguageModelCallEndEvent) => PromiseLike<void> | void. Errors are silently caught.
GenerateTextStepStartEvent structure
GenerateTextStepStartEvent contains: stepNumber (zero-based index), provider, modelId, instructions, messages (user-facing ModelMessage format, may be overridden by prepareStep), tools, toolChoice (LanguageModelV4ToolChoice|undefined), activeTools, toolOrder, steps (ReadonlyArray<StepResult<TOOLS>>), providerOptions, timeout, headers, stopWhen, output, abortSignal, include, runtimeContext, and toolsContext.
generateText onLanguageModelCallStart callback
The onLanguageModelCallStart callback is optional and is called immediately before provider model call begins. Unlike onStepStart, it excludes later client-side tool execution. Signature: (event: LanguageModelCallStartEvent) => PromiseLike<void> | void. Errors are silently caught.
LanguageModelCallStartEvent structure
LanguageModelCallStartEvent contains: callId (string, unique identifier), provider, modelId, instructions (Instructions|undefined), messages (Array<ModelMessage>), and tools (ReadonlyArray<Record<string, unknown>>|undefined, prepared tool definitions).
generateText onStepStart callback
The onStepStart callback is optional and is called when a step (LLM call) begins, before the provider is called. Signature: (event: GenerateTextStepStartEvent) => PromiseLike<void> | void. Errors are silently caught.
LanguageModelCallEndEvent structure partial
LanguageModelCallEndEvent contains: callId (string), provider, modelId (provider-returned), finishReason (FinishReason), usage (LanguageModelUsage), content (ReadonlyArray<ContentPart<TOOLS>>, text/reasoning/files/tool calls), responseId (string, provider-returned), providerMetadata (ProviderMetadata|undefined), and performance object with: responseTimeMs, effectiveOutputTokensPerSecond, outputTokensPerSecond (undefined for generateText), inputTokensPerSecond, effectiveTotalTokensPerSecond, timeToFirstOutputMs, and timeBetweenOutputChunksMs.
generateText import and basic usage
The generateText function is imported from the 'ai' package and is used to generate text and call tools for a given prompt using a language model. It is ideal for non-interactive use cases such as automation tasks and for agents that use tools.
generateText basic example
import { generateText } from 'ai';
__PROVIDER_IMPORT__;
const { text } = await generateText({
model: __MODEL__,
prompt: 'Invent a new holiday and describe its traditions.',
});
console.log(text);
generateText required parameters
generateText requires: model (LanguageModel), and either prompt (string or array of messages) or messages (array of messages). The prompt parameter accepts a string or Array<SystemModelMessage | UserModelMessage | AssistantModelMessage | ToolModelMessage>. The messages parameter is a list of messages that represent a conversation and automatically converts UI messages from the useChat hook.
generateText message types and structures
generateText accepts messages with roles: 'system' (SystemModelMessage), 'user' (UserModelMessage), 'assistant' (AssistantModelMessage), and 'tool' (ToolModelMessage). SystemModelMessage has role='system' and content=string. UserModelMessage has role='user' and content=string|Array<TextPart|ImagePart|FilePart>. AssistantModelMessage has role='assistant' and content=string|Array<TextPart|FilePart|ReasoningPart|ReasoningFilePart|ToolCallPart>. ToolModelMessage has role='tool' and content=Array<ToolResultPart>.
ReasoningPart message structure
ReasoningPart has type='reasoning' and text (string) properties. It represents reasoning output from the assistant model.
ReasoningFilePart message structure
ReasoningFilePart has type='reasoning-file', data (string|Uint8Array|Buffer|ArrayBuffer|URL), and mediaType (string, required) properties. It represents file-based reasoning output.
generateText allowSystemInMessages parameter
The allowSystemInMessages parameter is optional (boolean) and defaults to false. When false, system messages are not allowed in the prompt or messages fields (they are only allowed in the instructions option). Setting to true can create a prompt injection risk for user-controlled messages.
generateText sampling parameters
generateText accepts optional sampling parameters: temperature (number), topP (number), topK (number). It is recommended to set either temperature or topP but not both. Values are passed through to the provider and ranges depend on the provider and model.
generateText penalty parameters
generateText accepts optional presencePenalty (number) affecting likelihood of repeating information in the prompt, and frequencyPenalty (number) affecting likelihood of repeatedly using the same words or phrases. Values are passed through to the provider.
generateText token and generation control
generateText accepts optional: maxOutputTokens (number, maximum tokens to generate), stopSequences (string[] that stop generation), seed (number, for deterministic results when supported), and reasoning ('provider-default'|'none'|'minimal'|'low'|'medium'|'high'|'xhigh' controlling model reasoning before generating response).
generateText retry and timeout parameters
generateText accepts optional: maxRetries (number, default 2, set to 0 to disable), abortSignal (AbortSignal for cancellation), and timeout (number in milliseconds or object with totalMs, stepMs, toolMs, and tools properties for per-tool timeouts like {toolName}Ms).
generateText timeout configuration details
The timeout parameter can be: a number (milliseconds for total timeout), or an object with: totalMs (total timeout for entire call), stepMs (timeout for each individual step/LLM call), toolMs (default timeout for all tool executions), and tools (per-tool timeout overrides using pattern {toolName}Ms like weatherMs or slowApiMs). If a tool exceeds its timeout, it aborts and returns a tool-error allowing the model to respond or retry.
generateText telemetry configuration
The telemetry parameter is optional and accepts TelemetryOptions. TelemetryOptions has: isEnabled (boolean, enabled by default), recordInputs (boolean, enabled by default), recordOutputs (boolean, enabled by default), functionId (string for grouping telemetry by function), includeRuntimeContext (object specifying which runtime context properties to include, excluded unless set to true), includeToolsContext (object specifying per-tool context properties to include), and integrations (Telemetry|Telemetry[] for per-call telemetry integrations replacing global ones).
generateText stopWhen parameter
The stopWhen parameter is optional and accepts StopCondition<TOOLS> or Array<StopCondition<TOOLS>>. It specifies a condition for stopping generation when there are tool results in the last step. When an array, any of the conditions can be met to stop. Default: isStepCount(1).