Low-level Streaming
Direct AIEngine.queryStream and provider stream() AsyncIterables for raw token streaming without the chat tool layer.
When to use this
Most apps want Chat API — it streams text and dispatches tool calls in one pass. Reach for the lower-level streaming surface here when you need raw tokens without the chat tool layer: side panels, summarize-this buttons, off-graph generation, or tests.
AIEngine.queryStream()
High-level streaming with automatic context building from the current graph. Yields { type: 'text' | 'done' | 'error', content } chunks. No tool calls, no graph mutation — pure model output.
import { useState } from 'react';
import { GraphStore, QueryEngine, AIEngine } from '@inferagraph/core/data';
import { AnthropicProvider } from '@inferagraph/anthropic-provider';
const store = new GraphStore();
const provider = new AnthropicProvider({ apiKey: 'sk-ant-...' });
const ai = new AIEngine(store, new QueryEngine(store));
ai.setProvider(provider);
function StreamingChat() {
const [response, setResponse] = useState('');
const [streaming, setStreaming] = useState(false);
const handleQuery = async () => {
setResponse('');
setStreaming(true);
for await (const chunk of ai.queryStream('Tell me about Abraham's journey to Canaan')) {
switch (chunk.type) {
case 'text':
setResponse(prev => prev + chunk.content);
break;
case 'done':
setStreaming(false);
break;
case 'error':
console.error('Error:', chunk.content);
setStreaming(false);
break;
}
}
};
return (
<div>
<button onClick={handleQuery} disabled={streaming}>Ask</button>
<p>{response}</p>
</div>
);
}Provider-level stream()
Lower still: every LLMProvider exposes a native stream() AsyncIterable. Bypass AIEngine entirely when you want to control the prompt yourself or stream from contexts that don't have a graph (e.g., a detail panel summarizing a single node's content).
Providers that don't override stream() automatically fall back to complete(), yielding a single text chunk followed by done.
import { useState } from 'react';
import { LLMProvider } from '@inferagraph/core/data';
import { AnthropicProvider } from '@inferagraph/anthropic-provider';
const provider = new AnthropicProvider({ apiKey: 'sk-ant-...' });
function ProviderStream() {
const [result, setResult] = useState('');
const handleStream = async () => {
setResult('');
for await (const chunk of provider.stream({
messages: [{ role: 'user', content: 'Describe the Exodus' }],
})) {
if (chunk.type === 'text')
setResult(prev => prev + chunk.content);
}
};
return (
<div>
<button onClick={handleStream}>Stream</button>
<p>{result}</p>
</div>
);
}
// Custom provider: override stream() or use default fallback
class MyProvider extends LLMProvider {
async *stream(request) {
yield { type: 'text', content: 'Jacob had twelve sons...' };
yield { type: 'done', content: '' };
}
}