Stream responses in real-time with multiple consumption patterns. All streams are built on a reusable stream architecture that supports concurrent consumers.
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).
For models that support reasoning (like o1 or Claude with thinking), stream the
reasoning process:
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);
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 for the full
paradigm explanation.
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:
Stream all response events including tool preliminary results:
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; }}
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}`);}