Skip to main content

Module usage

Module usage 

Source
Expand description

Real provider-reported token usage extraction.

The proxy already rewrites requests; this module reads the response so the dashboard/terminal can show measured cost (the user’s real provider bill) instead of an estimate. All three providers report the exact model and the billed token breakdown — including prompt-cache reads/writes — in the final event of a stream (or the body of a non-streaming response):

  • Anthropic: message_start carries model + input/cache tokens, the final message_delta carries output_tokens. Non-streaming: one usage object.
  • OpenAI Responses: the response.completed event nests response.usage.
  • OpenAI Chat Completions: the final chunk carries usage (needs stream_options.include_usage, which the proxy injects).
  • Gemini: every chunk carries usageMetadata; the last one has the totals.

RealUsage normalizes every provider onto the four billable buckets that crate::core::gain::model_pricing::ModelCost::estimate_usd prices: uncached input, output (incl. reasoning/thoughts), cache-read, cache-write.

Structs§

RealUsage
One LLM turn’s real, provider-reported usage, normalized to billable buckets.
Scanner
Incrementally extracts RealUsage from a response stream (or a full body).
WireContext
Request-side context the forward path knows and the response scanner does not: who sent the request (gateway identity, enterprise#11), what the proxy saved on the wire, and the counterfactual-baseline inputs (enterprise#18). Travels inside RealUsage so usage_meter::record stays the single choke-point through which every measured turn flows.

Enums§

Provider
LLM provider whose response shape a Scanner understands.

Functions§

gemini_model_from_path
Extracts the model from a Gemini request path (/v1beta/models/{model}:generateContent). Returns None for other paths.
tee_stream
Wraps a response byte stream so every chunk is forwarded byte-for-byte while a Scanner observes it; on stream end the merged usage is recorded. Memory overhead is one buffered SSE line, never the whole response.