Provider streaming API
Custom Streaming API
For providers with non-standard APIs, implementstreamSimple. Study the existing API implementations before writing your own:
Reference implementations:
Atomic uses provider implementations from its installed @bastani/pi-ai dependency. The streaming implementations behind the api field live under node_modules/@bastani/pi-ai/dist/api/, including:
anthropic-messages.d.ts/anthropic-messages.js- Anthropic Messages APImistral-conversations.d.ts/mistral-conversations.js- Mistral Conversations/Chat streamingopenai-completions.d.ts/openai-completions.js- OpenAI Chat Completionsopenai-responses.d.ts/openai-responses.js- OpenAI Responses APIgoogle-generative-ai.d.ts/google-generative-ai.js- Google Generative AIbedrock-converse-stream.d.ts/bedrock-converse-stream.js- Amazon Bedrock Converse API Per-vendor provider configurations (base URLs, auth, model catalogs) live underdist/providers/, for exampleanthropic.d.ts/anthropic.jsandmistral.d.ts/mistral.js.
Stream Pattern
All providers follow the same pattern:Event Types
Push events viastream.push() in this order:
-
{ type: "start", partial: output }- Stream started -
Content events (repeatable, track
contentIndexfor each block):{ type: "text_start", contentIndex, partial }- Text block started{ type: "text_delta", contentIndex, delta, partial }- Text chunk{ type: "text_end", contentIndex, content, partial }- Text block ended{ type: "thinking_start", contentIndex, partial }- Thinking started{ type: "thinking_delta", contentIndex, delta, partial }- Thinking chunk{ type: "thinking_end", contentIndex, content, partial }- Thinking ended{ type: "toolcall_start", contentIndex, partial }- Tool call started{ type: "toolcall_delta", contentIndex, delta, partial }- Tool call JSON chunk{ type: "toolcall_end", contentIndex, toolCall, partial }- Tool call ended
-
{ type: "done", reason, message }or{ type: "error", reason, error }- Stream ended
partial field in each event contains the current AssistantMessage state. Update output.content as you receive data, then include output as the partial.
Stop Reasons
StopReason is "pending" | "stop" | "length" | "toolUse" | "error" | "aborted".
Start the partial message at "pending". It is the reason every in-flight message carries, and it says the terminal event has not arrived yet — it is not a default standing in for "stop". Set the real reason when the provider says the turn ended, then push done with it.
Two checks belong immediately before done:
- a stream that reached the end while still
"pending"never received a terminal event, so raise rather than report a stop that did not happen; "error"and"aborted"are failures, so raise them withoutput.errorMessageand let thecatchpush anerrorevent.
done accepts only "stop", "length", and "toolUse", which is exactly what those two checks leave, so the as "stop" | "length" | "toolUse" cast older implementations used is no longer needed.
Map each raw reason your provider can send onto one of the five terminal values, and raise on one you do not recognise rather than falling back to "stop". This is what the built-in providers do: an unmapped reason becomes a provider error naming the raw value, so a new truncation or safety signal is visible instead of arriving as a turn that looks like it finished normally. The optional rawStopReason field on AssistantMessage is where the provider’s own string belongs when you want to keep it.
Content Blocks
Add content blocks tooutput.content as they arrive:
Tool Calls
Tool calls require accumulating JSON and parsing:Usage and Cost
Update usage from API response and calculate cost:calculateCost() selects one rate set for the whole request. Aggregate input is usage.input + usage.cacheRead + usage.cacheWrite; a tier applies only when that sum is strictly greater than inputTokensAbove, and the matching tier with the highest threshold wins. Every tier must provide complete input, output, cacheRead, and cacheWrite rates. Extension-registered models preserve these tiers, and matching models.json modelOverrides use the same replacement rules described in Custom Models.