Streaming
Set "stream": true on any gateway request. The response is a server-sent events
stream in the ingress format’s native protocol — the same events the format’s official
SDKs already parse.
Per-format protocols
Chat Completions
chat.completion.chunk objects, terminated by data: [DONE]:
data: {"id":"gen-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"role":"assistant","content":""}}]}
data: {"id":"gen-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{"content":"Hello"}}]}
data: {"id":"gen-…","object":"chat.completion.chunk","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: {"id":"gen-…","object":"chat.completion.chunk","choices":[],"usage":{"prompt_tokens":12,"completion_tokens":7,"total_tokens":19}}
data: [DONE]hi.tool_event — server-tool lifecycle frames (opt-in)
When a server tool runs during a stream, the default stream stays byte-identical to a request with no tools — the tool’s work is invisible on the wire and you just get the final answer. Opt in to lifecycle frames to watch each tool call as it happens:
{ "plugins": [{ "id": "web_search", "events": true }] }…or opt in to every tool at once with a top-level flag:
{ "hi_tool_events": true }Either opt-in makes the gateway emit a compact start → result (or start →
error) pair around each tool execution. The frame body:
{
"object": "hi.tool_event",
"tool": "web_search",
"phase": "start",
"id": "call_srv1",
"payload": { "query": "hyperinfer" }
}object— always"hi.tool_event".tool— which server tool:web_search,web_fetch,datetime, orimage.phase—"start"(before execution),"result"(success), or"error".id— the tool-call id; astartand itsresult/errorshare it so you can fold them client-side.payload— a compact, tool-specific object (the query, the result count, the cost). Heavy content (full page text, image bytes) is never in the payload — it goes to the model, not the stream.
Per-format wire shape
Chat Completions / Responses
A plain data: line, no event: header — interleaved with the normal
chat.completion.chunk / response.* frames:
data: {"object":"hi.tool_event","tool":"web_search","phase":"start","id":"call_srv1","payload":{"query":"hyperinfer"}}
data: {"object":"hi.tool_event","tool":"web_search","phase":"result","id":"call_srv1","payload":{"query":"hyperinfer","resultCount":1,"costMicro":1798}}Default stream is unchanged
If you do not opt in, no hi.tool_event frames are emitted and the stream is
byte-for-byte identical to a request without server tools — existing SDKs and
proxies are unaffected. The opt-in is strictly additive.
Usage in streams — always
Token usage is reported on every request, streamed or not. For streaming, the final
usage chunk is always emitted — you do not need to send
stream_options: {"include_usage": true} (Chat Completions ingress accepts it for
compatibility; the behavior is always on). Usage includes prompt, completion, and
reasoning tokens, with cached vs. uncached prompt tokens broken out — see
Usage & Credits.
Keep-alives, timeouts, aborts
- SSE keep-alive comments every 15 seconds, so load balancers and proxies never idle-close a healthy stream.
- Time to first token up to 5 minutes; 120 seconds inter-chunk idle; 60 minutes absolute stream cap. Details in Limits & Timeouts.
- If your client aborts mid-stream, the tokens generated up to that point are still metered, billed, and recorded — the partial usage settles when the stream closes.
Errors that occur before any byte has streamed return a normal HTTP error response in your ingress format’s error shape. We never retry mid-stream.