Skip to content

devlog/api-plumbing/the-last-stream-chunk-is-missing-from-your-cost-log-482.md

The Last Stream Chunk Is Missing From Your Cost Log

Your terminal finished printing, but accounting did not

I want streamed text in my terminal immediately. I also want the request's token totals in my logs. Those requirements collide when the rendering loop exits as soon as it sees a finish reason. On some streaming APIs, the usage summary arrives afterward, in a chunk with no text to display.

That missing chunk makes dashboards look cheap while invoices keep growing. The money-saving fix starts with separating display completion from accounting completion: stop rendering when appropriate, but keep consuming the stream until its protocol terminates.

Keep the SSE reader separate from the text renderer

A network read is not an SSE event. A read can contain half an event, several events, or part of a multibyte character. Use an incremental UTF-8 decoder, retain incomplete lines, and assemble complete events before parsing JSON. A proper SSE parser also handles multiple data lines and line-ending variations.

After parsing, route each event independently to your renderer and usage collector. An empty text delta is not an empty event. Neither is an empty choices array. For OpenAI-compatible Chat Completions streams that support it, request stream_options.include_usage as true. The additional usage chunk can have an empty choices array and arrive before the [DONE] sentinel.

  • Ignore keepalive comments without discarding buffered, partially received event data.
  • Inspect every parsed event for usage before checking displayable text.
  • Treat protocol termination separately from a model's reported finish reason.
  • Record interrupted streams as incomplete instead of assigning zero tokens.

Normalize usage fields without multiplying cumulative totals

Keep adapters for each API surface. OpenAI-compatible Chat Completions commonly reports prompt_tokens, completion_tokens, and total_tokens. Native Claude streams expose usage through message events: message_start supplies input-side information, while message_delta can supply cumulative output usage. Adding every cumulative output value will overcount. Update the latest reported total instead.

Other API surfaces use different names and nesting. Build a normalized record with nullable fields for input tokens, output tokens, cache reads, and cache writes, plus the raw usage object. Null means unknown; zero means the provider explicitly reported none. Preserve that distinction when a connection drops.

When comparing Claude Sonnet 5.5 and GPT-6 Astra, choose the adapter from the endpoint and response format. A model's display name does not tell your parser which usage schema it received.

Cache reads need a separate accounting bucket

Cache counters are where apparently sensible cost formulas break. In OpenAI-style usage, cached input tokens can be a subset of prompt_tokens. Where that contract applies, calculate ordinary input as prompt tokens minus cached input, then price each bucket once. Do not charge all prompt tokens at the ordinary rate and add cached input again.

Native Claude usage separates ordinary input, cache creation input, and cache read input. Preserve those categories and any supplied cache-write duration breakdown. Apply the documented rates for the actual endpoint and model; do not transplant one provider's formula into another adapter.

Fluxo bills per real token, including cache reads and writes, with no monthly fee. That makes cache-aware records useful for reconciling spend. Keep pricing versioned alongside the model identifier and timestamp, and label locally calculated costs as estimates.

Log each retry before optimizing the prompt

Give every network attempt its own local identifier, linked to one logical task. Store provider request identifiers when available, the endpoint, model, usage, completion status, and retry reason. A failed attempt may still have consumed billable tokens. Retrying should create another record, not overwrite the first.

Start with three fixtures: a final usage chunk without choices, cumulative output updates, and a disconnect before final usage. Then inspect repeated prompts and retry storms. Reliable accounting tells you whether shortening context, preserving a stable cacheable prefix, or fixing reconnect behavior will actually save money.

Carry the usage ledger across editor and CLI sessions

Fluxo provides one key and one base URL, https://api.fluxorelay.com, for Claude, GPT, Gemini, DeepSeek, Kimi, GLM, Qwen, Grok and more. It works with Claude Code, Codex, Gemini CLI, and any OpenAI SDK. Keep the same accounting discipline across those workflows, while checking which usage details each client exposes.

If you want to try this workflow, create a Fluxo account and capture usage from one small streamed request first. Users also earn a referral share when people they invite pay; record that separately from request costs so your engineering measurements stay useful.

Next in the buffer

Sign up in a minute and get credit to try every model mentioned here. Sign up →