Streaming

Chat must stream: a model can take tens of seconds, and users need tokens immediately. AI SDK Core uses streamText for that. Batch jobs with no UI use generateText (see Quick start).

streamText starts as soon as you call it. Errors are swallowed by default so they do not crash the Node process—always pass onError. The stream is backpressured: if you do not read it, the model will not keep producing tokens.


Minimal streamText loop (Node)

import { streamText } from 'ai';

const result = streamText({
  model: 'openai/gpt-4.1-mini',
  prompt: 'Invent a holiday in three sentences.',
  onError({ error }) {
    console.error(error);
  },
});

for await (const textPart of result.textStream) {
  process.stdout.write(textPart);
}

result.textStream is both a ReadableStream and an AsyncIterable. After the stream ends you can await result.text, result.usage, result.finishReason, and result.toolCalls.

System text goes in instructions, not the older system field.


Wiring useChat: UI Message Stream

In the Next.js App Router, convert the full result.stream (tools, step boundaries) to the UI protocol:

import {
  convertToModelMessages,
  createUIMessageStreamResponse,
  streamText,
  toUIMessageStream,
  type UIMessage,
} from 'ai';

export const maxDuration = 30;

export async function POST(req: Request) {
  const { messages }: { messages: UIMessage[] } = await req.json();

  const result = streamText({
    model: 'openai/gpt-4.1-mini',
    instructions: 'You are a helpful assistant.',
    messages: await convertToModelMessages(messages),
  });

  return createUIMessageStreamResponse({
    stream: toUIMessageStream({ stream: result.stream }),
  });
}

This is the 7.x default for useChat (SSE). Custom backends should send header x-vercel-ai-ui-message-stream: v1. Events include text-delta, tool-input-*, start-step / finish-step, finish, and data: [DONE]. You do not write those by hand—use the helpers.


Text streams (weaker)

When you only need a string and no tools:

import { createTextStreamResponse, toTextStream } from 'ai';

return createTextStreamResponse({
  stream: toTextStream({ stream: result.stream }),
});

Client:

import { useChat } from '@ai-sdk/react';
import { TextStreamChatTransport } from 'ai';

useChat({ transport: new TextStreamChatTransport({ api: '/api/chat' }) });

Do not use a text stream if you have tools—tool calls will not arrive. Chat assistants should always use the UI Message Stream.


Other pipes

HelperUse for
createUIMessageStreamResponse + toUIMessageStreamApp Router + useChat (default)
pipeUIMessageStreamToResponseWriting a Node-style ServerResponse
createTextStreamResponse + toTextStreamPlain text
result.textStreamfor await in a script

Structured streams

streamText accepts output: Output.object({ schema }). Read incomplete objects from partialOutputStream; the client uses useObject. Partial JSON cannot be validated against the full schema. See Generating Structured Data in the official docs.


Debug checklist

  1. UI stuck on submitted: the route is not returning a UI stream, or result.stream is unused.
  2. Text only, empty tool UI: the client ignores parts, or the server used the text protocol.
  3. Cut off at ~30s on Vercel: set export const maxDuration (see Next.js).
  4. No tokens and no thrown error: add onError; streamText does not rethrow by default.

Next

评论