LLM SDK

streamText()

Stream text responses in real-time

Stream text responses in real-time. Returns helpers to consume the stream or convert it to HTTP responses.

Using with Copilot SDK? Use toDataStreamResponse() instead of toTextStreamResponse(). See Response Types below.

import { streamText } from '@yourgpt/llm-sdk';
import { openai } from '@yourgpt/llm-sdk/openai';

const result = await streamText({
  model: openai('gpt-4o'),
  prompt: 'Tell me a story.',
});

// Option 1: Iterate over text chunks
for await (const chunk of result.textStream) {
  process.stdout.write(chunk);
}

// Option 2: Get complete text (waits for stream to finish)
const text = await result.text;

// Option 3: Return as HTTP Response (for Copilot SDK)
return result.toDataStreamResponse();

Parameters

const result = await streamText({
  // Required: Language model to use
  model: openai('gpt-4o'),

  // Content (at least one required)
  prompt: 'Hello',              // Simple prompt
  messages: [...],              // Chat history
  system: 'Be concise.',        // System instruction

  // Optional: Tools for function calling
  tools: { ... },
  maxSteps: 5,                  // Max LLM calls for tool loops

  // Optional: Generation settings
  temperature: 0.7,
  maxTokens: 4096,

  // Optional: Structured JSON output (provider-translated)
  responseFormat: {
    type: 'json_schema',
    json_schema: {
      name: 'response',
      schema: { type: 'object', properties: { ... }, required: [...] },
      strict: true,
    },
  },
});

responseFormat works across all supported providers — each adapter translates to the provider's native field. See Structured Output for per-provider details and gotchas.


Response Object

const result = await streamText({ ... });

// Streams
result.textStream     // AsyncIterable<string> - text only
result.fullStream     // AsyncIterable<StreamPart> - all events

// Promises (resolve when stream completes)
result.text           // Promise<string> - full text
result.usage          // Promise<TokenUsage> - token counts
result.finishReason   // Promise<FinishReason>
result.response       // Promise<{ messages: CoreMessage[] }>
result.providerMetadata // Promise<ProviderMetadata | undefined>

// HTTP Response helpers
result.toTextStreamResponse()   // text/plain stream
result.toDataStreamResponse()   // SSE with structured events

API Route Examples

app/api/chat/route.ts
import { streamText } from '@yourgpt/llm-sdk';
import { openai } from '@yourgpt/llm-sdk/openai';

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

  const result = await streamText({
    model: openai('gpt-4o'),
    system: 'You are helpful.',
    messages,
  });

  // Use toDataStreamResponse() for Copilot SDK compatibility
  return result.toDataStreamResponse();
}
server.ts
import { createServer } from 'http';
import { streamText } from '@yourgpt/llm-sdk';
import { openai } from '@yourgpt/llm-sdk/openai';

createServer(async (req, res) => {
  const body = await getBody(req);
  const { messages } = JSON.parse(body);

  const result = await streamText({
    model: openai('gpt-4o'),
    system: 'You are helpful.',
    messages,
  });

  // Use toDataStreamResponse() for Copilot SDK compatibility
  const response = result.toDataStreamResponse();
  res.writeHead(200, Object.fromEntries(response.headers));

  const reader = response.body!.getReader();
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    res.write(value);
  }
  res.end();
}).listen(3001);

function getBody(req: any): Promise<string> {
  return new Promise((resolve) => {
    let data = '';
    req.on('data', (chunk: any) => data += chunk);
    req.on('end', () => resolve(data));
  });
}

For Copilot SDK: Consider using the Runtime API instead of streamText() directly. The Runtime provides a higher-level API with built-in support for tools, persistence, and more.


Response Types

SSE format with structured events. Required for Copilot SDK compatibility.

return result.toDataStreamResponse();
Content-Type: text/event-stream

data: {"type":"text-delta","text":"Hello"}
data: {"type":"text-delta","text":"!"}
data: {"type":"tool-call-complete","toolCall":{...}}
data: {"type":"tool-result","toolCallId":"...","result":{...}}
data: {"type":"finish","finishReason":"stop","usage":{...}}
data: [DONE]

Text Stream Response

Simple text streaming. Not compatible with Copilot SDK (use for direct streaming to other clients).

return result.toTextStreamResponse();
Content-Type: text/plain; charset=utf-8

Hello! How can I help you?

Copilot SDK Compatibility: The Copilot SDK expects SSE format. Use toDataStreamResponse() for Copilot SDK, or toTextStreamResponse() only for direct streaming to non-Copilot clients.


With Tools

import { streamText, tool } from '@yourgpt/llm-sdk';
import { z } from 'zod';

const result = await streamText({
  model: openai('gpt-4o'),
  prompt: 'What is the weather in Tokyo?',
  tools: {
    getWeather: tool({
      description: 'Get weather for a city',
      parameters: z.object({ city: z.string() }),
      execute: async ({ city }) => ({ temp: 22, condition: 'sunny' }),
    }),
  },
  maxSteps: 5,
});

return result.toDataStreamResponse();

Use toDataStreamResponse() with tools to receive tool call and result events.


Consuming the Full Stream

For custom handling of all events:

for await (const part of result.fullStream) {
  switch (part.type) {
    case 'text-delta':
      console.log('Text:', part.text);
      break;
    case 'tool-call-complete':
      console.log('Tool called:', part.toolCall.name);
      break;
    case 'tool-result':
      console.log('Tool result:', part.result);
      break;
    case 'step-start':
      console.log('Starting step:', part.step);
      break;
    case 'step-finish':
      console.log('Step done:', part.finishReason);
      break;
    case 'finish':
      console.log('Complete:', part.usage);
      break;
    case 'error':
      console.error('Error:', part.error);
      break;
  }
}

Stream Part Types

TypeDescriptionProperties
text-deltaText chunktext: string
tool-call-completeTool was calledtoolCall: { id, name, args }
tool-resultTool returnedtoolCallId, result
step-startNew step beganstep: number
step-finishStep completedstep, finishReason
finishStream donefinishReason, usage
errorError occurrederror: Error

Custom Response Headers

return result.toDataStreamResponse({
  status: 200,
  headers: {
    'X-Custom-Header': 'value',
  },
});

Abort Handling

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

  const result = await streamText({
    model: openai('gpt-4o'),
    messages,
    signal: req.signal, // Pass abort signal
  });

  return result.toDataStreamResponse();
}

Continuation and shared execution

The optional openai request setting selects the OpenAI transport. Stream readers, response helpers, and result promises share one model execution. Reading textStream and then awaiting response or providerMetadata does not send a second request.

Preserve (await result.response).messages intact for continuation, including tool outputs and opaque provider metadata. Failed/incomplete Responses streams reject instead of being treated as a successful completed turn. A plain text HTTP response cannot carry all replay fields; use the structured stream/history integration when your client needs them.


Next Steps

On this page