How the mode is chosen
In order:- A route with
stream: truealways streams;stream: falsealways buffers. - A path matching one of the relay’s
streaming.pathsglobs always streams. - Otherwise the upstream’s reply decides: a
Content-Typeoftext/event-streamis streamed, anything else is read in full and priced.
Accept: text/event-stream or a "stream": true field in the body changes nothing on its own. A reply that is not an event stream is always read and priced, so a caller can never route around response-side billing by asking for a stream.
- Web UI
- config.json
- Edit Relay Config → Advanced → Always-Stream Paths: one route glob per line, such as
/v1/chat/**. - Add Route / Edit Route → Streaming: Auto, Always stream or Never stream.
- Edit Relay Config → Network → Stream Idle Timeout: how long a stream may stay silent.
What the caller receives
Bytes are forwarded unchanged with the upstream’s status and headers (minus framing headers), plusCache-Control: no-cache and X-Accel-Buffering: no so intermediaries do not buffer. The relay never rewrites events.
The wait for the upstream’s headers is bounded by network.timeout_seconds; once the reply is open, only the idle timeout governs it. A stream that falls silent for longer than stream_idle_timeout_in_seconds ends with a timeout error.
How a stream is priced
A streamed call is counted when its route is billable and the upstream’s status is2xx. The usage is read as the bytes pass:
- SSE replies are framed on the side, and only events that carry one of the mapped fields (token paths,
cost_path, a response-sideunits_path) are parsed. Each field keeps the last value any event reported, the running-total shape OpenAI and Anthropic use, never a sum of per-event increments. A stream that reports input tokens on its first event and output tokens on its last is billed for both. - Replies priced by their size (
units_measureofbody_bytesorbody_charswithunits_source: response) are counted as they are forwarded, so a streamed audio reply bills the same bytes a buffered one would. - Request-side units (
units_source: request) are known before the upstream answers and apply to streams as to buffered calls. - A non-SSE stream that is not priced by size is forwarded unread: nothing is taken from its body.
cancelled.
Latency on a streamed row is split as for LLM streams: the time blocked waiting for upstream chunks counts as upstream latency, and Bifrost’s overhead is its own measured work.
Next steps
- Usage mapping and pricing — The fields a stream’s events are read with.
- Relay Logs — Streamed rows carry a Streamed badge and the bytes forwarded.

