> ## Documentation Index > Fetch the complete documentation index at: https://openrouter.ai/docs/llms.txt > Use this file to discover all available pages before exploring further. # Streaming > Stream responses in real-time with multiple consumption patterns. All streams are built on a reusable stream architecture that supports concurrent consumers. ## Text Streaming ### getTextStream() Stream text content as it's generated: ```typescript lines theme={null} import { OpenRouter } from '@openrouter/agent'; const openrouter = new OpenRouter({ apiKey: process.env.OPENROUTER_API_KEY, }); const result = openrouter.callModel({ model: 'openai/gpt-5-nano', input: 'Write a short poem about the ocean.', }); for await (const delta of result.getTextStream()) { process.stdout.write(delta); } ``` Each iteration yields a small chunk of text (typically a few characters or a word). ## Reasoning Streaming ### getReasoningStream() For models that support reasoning (like o1 or Claude with thinking), stream the reasoning process: ```typescript lines theme={null} const result = openrouter.callModel({ model: 'openai/o1-preview', input: 'Solve this step by step: If x + 5 = 12, what is x?', }); console.log('Reasoning:'); for await (const delta of result.getReasoningStream()) { process.stdout.write(delta); } console.log('\n\nFinal answer:'); const text = await result.getText(); console.log(text); ``` ## Items Streaming ### getItemsStream() Stream complete items as they update. This is the **recommended way** to handle streaming when you need structured access to all output types (messages, tool calls, reasoning, etc.). See [Working with Items](/docs/agent-sdk/call-model/items) for the full paradigm explanation. ```typescript expandable lines theme={null} import type { StreamableOutputItem } from '@openrouter/agent'; const result = openrouter.callModel({ model: 'anthropic/claude-sonnet-4', input: 'Hello!', tools: [myTool], }); for await (const item of result.getItemsStream()) { switch (item.type) { case 'message': console.log('Message:', item.content); break; case 'function_call': console.log('Tool call:', item.name, item.arguments); break; case 'reasoning': console.log('Thinking:', item.summary); break; case 'function_call_output': console.log('Tool result:', item.output); break; } } ``` **Key insight**: Each iteration yields a **complete item** with the same ID but updated content. Replace items by ID rather than accumulating deltas. This stream yields all item types: | Type | Description | | ----------------------- | ---------------------------------- | | `message` | Assistant text responses | | `function_call` | Tool invocations with arguments | | `reasoning` | Model thinking (extended thinking) | | `web_search_call` | Web search operations | | `file_search_call` | File search operations | | `image_generation_call` | Image generation operations | | `function_call_output` | Results from executed tools | ## Message Streaming (Deprecated) ### getNewMessagesStream() `getNewMessagesStream()` is deprecated. Use `getItemsStream()` instead, which includes all item types and follows the items-based paradigm. Stream cumulative message snapshots in the OpenResponses format: ```typescript lines theme={null} // Deprecated - use getItemsStream() instead const result = openrouter.callModel({ model: 'openai/gpt-5-nano', input: 'Hello!', tools: [myTool], }); for await (const message of result.getNewMessagesStream()) { if (message.type === 'message') { console.log('Assistant message:', message.content); } else if (message.type === 'function_call_output') { console.log('Tool result:', message.output); } } ``` This stream yields: * `ResponsesOutputMessage` - Assistant text/content updates * `OpenResponsesFunctionCallOutput` - Tool execution results (after tools complete) ## Full Event Streaming ### getFullResponsesStream() Stream all response events including tool preliminary results: ```typescript expandable lines theme={null} const result = openrouter.callModel({ model: 'openai/gpt-5-nano', input: 'Search for documents', tools: [searchTool], // Generator tool with eventSchema }); for await (const event of result.getFullResponsesStream()) { switch (event.type) { case 'response.output_text.delta': process.stdout.write(event.delta); break; case 'response.function_call_arguments.delta': console.log('Tool argument delta:', event.delta); break; case 'response.completed': console.log('Response complete'); break; case 'tool.preliminary_result': // Intermediate progress from generator tools console.log('Progress:', event.result); break; case 'tool.result': // Final result when tool execution completes console.log('Tool completed:', event.toolCallId); console.log('Result:', event.result); // Access any preliminary results that were emitted if (event.preliminaryResults) { console.log('Preliminary results:', event.preliminaryResults); } break; } } ``` ### Event Types The full stream includes these event types: | Event Type | Description | | ---------------------------------------- | --------------------------------------------------- | | `response.created` | Response object created | | `response.in_progress` | Generation started | | `response.output_text.delta` | Text content chunk | | `response.output_text.done` | Text content complete | | `response.reasoning.delta` | Reasoning content chunk | | `response.reasoning.done` | Reasoning complete | | `response.function_call_arguments.delta` | Tool call argument chunk | | `response.function_call_arguments.done` | Tool call arguments complete | | `response.completed` | Full response complete | | `tool.preliminary_result` | Progress from generator tools (intermediate yields) | | `tool.result` | Final result from tool execution | ## Tool Call Streaming ### getToolCallsStream() Stream structured tool calls as they complete: ```typescript lines theme={null} const result = openrouter.callModel({ model: 'openai/gpt-5-nano', input: 'What is the weather in Paris and Tokyo?', tools: [weatherTool], maxToolRounds: 0, // Don't auto-execute, just get tool calls }); for await (const toolCall of result.getToolCallsStream()) { console.log(`Tool: ${toolCall.name}`); console.log(`Arguments:`, toolCall.arguments); console.log(`ID: ${toolCall.id}`); } ``` ### getToolStream() Stream tool deltas and preliminary results: ```typescript lines theme={null} const result = openrouter.callModel({ model: 'openai/gpt-5-nano', input: 'Search for TypeScript tutorials', tools: [searchTool], // Generator tool }); for await (const event of result.getToolStream()) { if (event.type === 'delta') { // Raw argument deltas process.stdout.write(event.content); } else if (event.type === 'preliminary_result') { // Progress from generator tools console.log(`\nProgress (${event.toolCallId}):`, event.result); } } ``` ## Concurrent Consumers Multiple consumers can read from the same result: ```typescript expandable lines theme={null} const result = openrouter.callModel({ model: 'openai/gpt-5-nano', input: 'Write a story.', }); // Start both consumers concurrently const [text, response] = await Promise.all([ // Consumer 1: Collect text (async () => { let text = ''; for await (const delta of result.getTextStream()) { text += delta; } return text; })(), // Consumer 2: Get full response result.getResponse(), ]); console.log('Text length:', text.length); console.log('Token usage:', response.usage); ``` The underlying `ReusableReadableStream` ensures each consumer receives all events. ## Cancellation Cancel a stream to stop generation: ```typescript expandable lines theme={null} const result = openrouter.callModel({ model: 'openai/gpt-5-nano', input: 'Write a very long essay...', }); // Start streaming const streamPromise = (async () => { let charCount = 0; for await (const delta of result.getTextStream()) { process.stdout.write(delta); charCount += delta.length; // Cancel after 500 characters if (charCount > 500) { await result.cancel(); break; } } })(); await streamPromise; console.log('\nCancelled!'); ``` ## Streaming with UI Frameworks ### React Example ```tsx expandable lines theme={null} import { useState, useEffect } from 'react'; function ChatResponse({ prompt }: { prompt: string }) { const [text, setText] = useState(''); const [isStreaming, setIsStreaming] = useState(true); useEffect(() => { const openrouter = new OpenRouter({ apiKey: API_KEY }); const result = openrouter.callModel({ model: 'openai/gpt-5-nano', input: prompt, }); (async () => { for await (const delta of result.getTextStream()) { setText(prev => prev + delta); } setIsStreaming(false); })(); return () => { result.cancel(); }; }, [prompt]); return (

{text}

{isStreaming && |}
); } ``` ### Server-Sent Events (SSE) ```typescript expandable lines theme={null} import { Hono } from 'hono'; import { streamSSE } from 'hono/streaming'; const app = new Hono(); app.get('/stream', (c) => { return streamSSE(c, async (stream) => { const result = openrouter.callModel({ model: 'openai/gpt-5-nano', input: c.req.query('prompt') || 'Hello!', }); for await (const delta of result.getTextStream()) { await stream.writeSSE({ data: JSON.stringify({ delta }), event: 'delta', }); } await stream.writeSSE({ data: JSON.stringify({ done: true }), event: 'done', }); }); }); ``` ## Next Steps * **[Working with Items](/docs/agent-sdk/call-model/items)** - Understand the items-based streaming paradigm * **[Tools](/docs/agent-sdk/call-model/tools)** - Create tools and multi-turn streaming with tools