Real-Time Streaming
The Chaos AI SDK streams responses incrementally. Instead of waiting for the full response, you can process each message as it arrives using the onStreamEvent callback. This lets you build responsive UIs that show progress in real time.
How streaming works
When you call chaos.chat.responses.create(), the SDK opens an HTTP connection and reads NDJSON lines as they stream in. Each line is parsed into a ChaosSDKMessage and delivered to your onStreamEvent callback. After the stream completes, the method returns a ChatCreateResponse containing all collected messages.
The onStreamEvent callback
Add onStreamEvent to your ChatCreateRequestParams to receive each message as it arrives:
import { Chaos, WALLET_MODEL } from '@chaoslabs/ai-sdk';
const chaos = new Chaos({
apiKey: process.env.CHAOS_API_KEY!,
});
const response = await chaos.chat.responses.create({
model: WALLET_MODEL,
input: [
{ type: 'message', role: 'user', content: 'Analyze my DeFi positions' },
],
metadata: {
user_id: 'user-123',
session_id: 'session-abc',
wallets: [{ address: '0xYourWallet', chain: 'ethereum' }],
},
onStreamEvent: (message) => {
console.log(`[${message.type}]`, message.id);
},
});
// response.messages contains all the messages that were streamed
console.log('Total messages:', response.messages?.length);ChaosSDKMessage types
Every streamed message has a kind of "message" and a type that tells you what it contains. The five message types are:
type | Interface | Description |
|---|---|---|
"status" | AgentStatusSDKMessage | Agent status change ("processing" or "done") |
"text" | AgentTextSDKMessage | Incremental text content |
"block" | BlockSDKMessage | A structured block (table, chart, action, etc.) |
"follow_up_suggestions" | FollowUpSuggestionsSDKMessage | Suggested follow-up queries |
"input" | UserInputSDKMessage | A disambiguation prompt with options |
Handling each message type
Here is a complete handler that processes every message type:
import { Chaos, WALLET_MODEL } from '@chaoslabs/ai-sdk';
import type { ChaosSDKMessage } from '@chaoslabs/ai-sdk';
const chaos = new Chaos({
apiKey: process.env.CHAOS_API_KEY!,
});
function handleStreamEvent(message: ChaosSDKMessage): void {
switch (message.type) {
case 'status':
// Agent status: 'processing' or 'done'
console.log(`Status: ${message.data.status}`);
break;
case 'text':
// Incremental text content
process.stdout.write(message.data.text);
break;
case 'block':
// Structured block (table, chart, action, markdown, etc.)
const block = message.data.block;
console.log(`\n[Block: ${block.type}]`);
if ('title' in block && block.title) {
console.log(` Title: ${block.title}`);
}
break;
case 'follow_up_suggestions':
// Suggested next questions
console.log('\nSuggested follow-ups:');
for (const suggestion of message.data.suggestions) {
console.log(` - ${suggestion}`);
}
break;
case 'input':
// Disambiguation prompt
console.log(`\nInput needed: ${message.data.prompt}`);
for (const option of message.data.options) {
console.log(` - ${option.name}`);
}
break;
}
}
const response = await chaos.chat.responses.create({
model: WALLET_MODEL,
input: [
{ type: 'message', role: 'user', content: 'What are my top positions?' },
],
metadata: {
user_id: 'user-123',
session_id: 'session-abc',
wallets: [{ address: '0xYourWallet', chain: 'ethereum' }],
},
onStreamEvent: handleStreamEvent,
});Building a progress display
A common pattern is to show a status indicator while the agent is processing, then render the content as it streams in:
import { Chaos, WALLET_MODEL, extractText, extractBlocks } from '@chaoslabs/ai-sdk';
import type { ChaosSDKMessage } from '@chaoslabs/ai-sdk';
const chaos = new Chaos({
apiKey: process.env.CHAOS_API_KEY!,
});
let isProcessing = false;
let textChunks: string[] = [];
let blockCount = 0;
function onStreamEvent(message: ChaosSDKMessage): void {
if (message.type === 'status') {
if (message.data.status === 'processing') {
isProcessing = true;
console.log('Thinking...');
} else if (message.data.status === 'done') {
isProcessing = false;
console.log('\nDone.');
}
}
if (message.type === 'text') {
textChunks.push(message.data.text);
// Print text as it arrives
process.stdout.write(message.data.text);
}
if (message.type === 'block') {
blockCount++;
const block = message.data.block;
console.log(`\nReceived ${block.type} block`);
}
}
const response = await chaos.chat.responses.create({
model: WALLET_MODEL,
input: [
{ type: 'message', role: 'user', content: 'Show me my Aave positions' },
],
metadata: {
user_id: 'user-123',
session_id: 'session-abc',
wallets: [{ address: '0xYourWallet', chain: 'ethereum' }],
},
onStreamEvent,
});
console.log(`\nStreamed ${textChunks.length} text chunks, ${blockCount} blocks`);
console.log(`Final response status: ${response.status}`);Streaming event order
Events typically arrive in this order:
statuswithdata.status === "processing"-- the agent has started.textmessages -- incremental text content.blockmessages -- structured data blocks (tables, charts, transactions).follow_up_suggestions-- suggested next queries.statuswithdata.status === "done"-- the agent has finished.
Text and block messages can interleave. Blocks may arrive before all text is complete.
Cancelling a request
You can cancel an in-progress streaming request by calling cancel() on the responses object:
// Start a request
const responsePromise = chaos.chat.responses.create({
model: WALLET_MODEL,
input: [
{ type: 'message', role: 'user', content: 'Detailed analysis of all my positions' },
],
metadata: {
user_id: 'user-123',
session_id: 'session-abc',
wallets: [{ address: '0xYourWallet', chain: 'ethereum' }],
},
onStreamEvent: (message) => {
console.log(`[${message.type}]`);
},
});
// Cancel after 5 seconds
setTimeout(() => {
chaos.chat.responses.cancel();
}, 5000);
try {
const response = await responsePromise;
console.log('Completed:', response.status);
} catch (err) {
console.log('Request was cancelled');
}Error handling
If the connection fails or times out, the create() promise rejects with a ChaosError or ChaosTimeoutError:
import { Chaos, WALLET_MODEL, ChaosError, ChaosTimeoutError } from '@chaoslabs/ai-sdk';
const chaos = new Chaos({
apiKey: process.env.CHAOS_API_KEY!,
timeout: 60000, // 60-second timeout
});
try {
const response = await chaos.chat.responses.create({
model: WALLET_MODEL,
input: [
{ type: 'message', role: 'user', content: 'What is my portfolio?' },
],
metadata: {
user_id: 'user-123',
session_id: 'session-abc',
wallets: [{ address: '0xYourWallet', chain: 'ethereum' }],
},
onStreamEvent: (message) => {
if (message.type === 'text') {
process.stdout.write(message.data.text);
}
},
});
} catch (error) {
if (error instanceof ChaosTimeoutError) {
console.error('Request timed out');
} else if (error instanceof ChaosError) {
console.error('API error:', error.message, 'status:', error.status);
} else {
console.error('Unexpected error:', error);
}
}Next steps
- Wallet Configuration -- Set up multi-chain wallets and context overrides for deterministic testing.
- Quick Start -- Review the basics if you skipped ahead.