feat(prometheus): emit per-token-type detail metrics (LIT-3220) - #28372
Conversation
[Infra] Promote internal staging to main
[Infra] Promote internal staging to main
chore(ci): promote internal staging to main
Adds five sparse counter metrics that break out the token detail fields providers already report in `usage.prompt_tokens_details` and `usage.completion_tokens_details`: - litellm_input_cached_tokens_metric (provider prompt-cache reads) - litellm_input_cache_creation_tokens_metric (Anthropic prompt-cache writes) - litellm_input_audio_tokens_metric (audio input tokens) - litellm_output_reasoning_tokens_metric (reasoning tokens) - litellm_output_audio_tokens_metric (audio output tokens) These are additive — existing input/output/total counters are unchanged, so no dashboards break. Each new counter is only incremented when the underlying detail is populated and > 0, keeping scrape output sparse for providers that don't report a given field. Data is read from the canonical Usage dict that `get_standard_logging_object_payload` already attaches at `standard_logging_payload["metadata"]["usage_object"]`, so no new plumbing through the logging pipeline is required. Tests: 10 new unit tests covering registration, label-set parity, all-types increment, zero/None/negative skip behaviour, and the no-metadata/no-usage_object no-op paths. Closes LIT-3220 Co-Authored-By: Claude <noreply@anthropic.com>
|
|
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
Greptile SummaryAdds five sparse Prometheus counters (
Confidence Score: 4/5This is a purely additive change with no modifications to existing counters or the request hot path; it is safe to merge. The new helper reads from a pre-existing metadata field, all counters are sparse (only emitted when the value is > 0), and existing token metrics are unchanged. The isinstance(…, dict) guards inside the detail-metrics list are technically reachable only when a Pydantic wrapper object bypasses the or {} fallback, which silently drops the metric rather than causing an error. The binary PNG committed to the source tree is a process concern with no runtime impact. The proof/lit-3220-metrics.png binary should not live in the main repo; the isinstance(prompt_details, dict) guards in _increment_token_detail_metrics warrant a second look for the Pydantic-object edge case.
|
| Filename | Overview |
|---|---|
| litellm/integrations/prometheus.py | Registers five new sparse Prometheus counters for per-token-type detail metrics and adds _increment_token_detail_metrics; the implementation is clean and follows the existing _inc_labeled_counter pattern correctly. |
| litellm/types/integrations/prometheus.py | Adds five new metric names to the DEFINED_PROMETHEUS_METRICS Literal and assigns label sets in PrometheusMetricLabels by aliasing the existing input/output label lists; straightforward and consistent with the existing pattern. |
| tests/test_litellm/integrations/test_prometheus_token_detail_metrics.py | New unit test file with 10 mock-only tests covering registration, label parity, zero/None/negative skip paths, and full increment; tests correctly bind to the real _increment_token_detail_metrics implementation via unbound method call. |
| proof/lit-3220-metrics.png | Binary screenshot committed into the main repo; documentation artifacts like this belong in the litellm-docs repo or hosted as PR attachments, not in the source tree. |
Comments Outside Diff (1)
-
proof/lit-3220-metrics.png, line 1 (link)Binary proof artifact committed to main repo
Per the team's documentation guidelines, artifacts like screenshots and visual proof should live in the
litellm-docsrepo or be attached directly to the PR (GitHub renders images pasted or dragged into PR descriptions without requiring a committed file). Committing a binary PNG to the source tree inflates repo clone size for every developer and CI runner going forward, with no runtime benefit.Rule Used: Prevent documentation from being added - needs to ... (source)
Reviews (1): Last reviewed commit: "feat(prometheus): emit per-token-type de..." | Re-trigger Greptile
| prompt_details = usage_object.get("prompt_tokens_details") or {} | ||
| completion_details = usage_object.get("completion_tokens_details") or {} |
There was a problem hiding this comment.
isinstance(…, dict) guards are unreachable after the or {} fallback
prompt_details and completion_details are assigned via … or {}, which means they are either a plain dict (from model_dump() or JSON parsing) or a truthy non-dict value such as a PromptTokensDetailsWrapper / CompletionTokensDetailsWrapper Pydantic object. In the Pydantic-object case isinstance(…, dict) returns False and every field silently resolves to None, so no detail metric fires — even though both wrapper classes expose a compatible .get() method. Using .get() directly (which works for both dicts and the wrapper models) after the or {} guard would be both simpler and more defensive.
31b1d96
into
BerriAI:litellm_shin_may20
Adds a "Token type detail metrics" subsection to docs/proxy/prometheus.md covering the five new sparse counters added in BerriAI/litellm#28372 (LIT-3220): - litellm_input_cached_tokens_metric - litellm_input_cache_creation_tokens_metric - litellm_input_audio_tokens_metric - litellm_output_reasoning_tokens_metric - litellm_output_audio_tokens_metric Includes: - Source field on the Usage object for each metric - Typical providers that populate each field - PromQL examples for cache-hit ratio and reasoning-token share - Note distinguishing provider-side cached input tokens from LiteLLM's own response-cache `litellm_cached_tokens_metric` Co-authored-by: Krrish Dholakia <krrishdholakia@berri.ai>
…) (#28378) * feat(prometheus): emit per-token-type detail metrics (LIT-3220) (#28372) Adds five sparse counter metrics that break out the token detail fields providers already report in `usage.prompt_tokens_details` and `usage.completion_tokens_details`: - litellm_input_cached_tokens_metric (provider prompt-cache reads) - litellm_input_cache_creation_tokens_metric (Anthropic prompt-cache writes) - litellm_input_audio_tokens_metric (audio input tokens) - litellm_output_reasoning_tokens_metric (reasoning tokens) - litellm_output_audio_tokens_metric (audio output tokens) These are additive — existing input/output/total counters are unchanged, so no dashboards break. Each new counter is only incremented when the underlying detail is populated and > 0, keeping scrape output sparse for providers that don't report a given field. Data is read from the canonical Usage dict that `get_standard_logging_object_payload` already attaches at `standard_logging_payload["metadata"]["usage_object"]`, so no new plumbing through the logging pipeline is required. Tests: 10 new unit tests covering registration, label-set parity, all-types increment, zero/None/negative skip behaviour, and the no-metadata/no-usage_object no-op paths. Closes LIT-3220 Co-authored-by: shin-berri <shin-laptop@berri.ai> Co-authored-by: yuneng-jiang <yuneng@berri.ai> Co-authored-by: Krrish Dholakia <krrishdholakia@berri.ai> Co-authored-by: Claude <noreply@anthropic.com> * chore: remove proof folder image --------- Co-authored-by: oss-agent-shin <ext-agent-shin@berri.ai> Co-authored-by: shin-berri <shin-laptop@berri.ai> Co-authored-by: yuneng-jiang <yuneng@berri.ai> Co-authored-by: Krrish Dholakia <krrishdholakia@berri.ai> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Ishaan Jaffer <ishaanjaffer0324@gmail.com>
…iAI#28372) (BerriAI#28378) * feat(prometheus): emit per-token-type detail metrics (LIT-3220) (BerriAI#28372) Adds five sparse counter metrics that break out the token detail fields providers already report in `usage.prompt_tokens_details` and `usage.completion_tokens_details`: - litellm_input_cached_tokens_metric (provider prompt-cache reads) - litellm_input_cache_creation_tokens_metric (Anthropic prompt-cache writes) - litellm_input_audio_tokens_metric (audio input tokens) - litellm_output_reasoning_tokens_metric (reasoning tokens) - litellm_output_audio_tokens_metric (audio output tokens) These are additive — existing input/output/total counters are unchanged, so no dashboards break. Each new counter is only incremented when the underlying detail is populated and > 0, keeping scrape output sparse for providers that don't report a given field. Data is read from the canonical Usage dict that `get_standard_logging_object_payload` already attaches at `standard_logging_payload["metadata"]["usage_object"]`, so no new plumbing through the logging pipeline is required. Tests: 10 new unit tests covering registration, label-set parity, all-types increment, zero/None/negative skip behaviour, and the no-metadata/no-usage_object no-op paths. Closes LIT-3220 Co-authored-by: shin-berri <shin-laptop@berri.ai> Co-authored-by: yuneng-jiang <yuneng@berri.ai> Co-authored-by: Krrish Dholakia <krrishdholakia@berri.ai> * chore: remove proof folder image --------- Co-authored-by: oss-agent-shin <ext-agent-shin@berri.ai> Co-authored-by: shin-berri <shin-laptop@berri.ai> Co-authored-by: yuneng-jiang <yuneng@berri.ai> Co-authored-by: Krrish Dholakia <krrishdholakia@berri.ai> Co-authored-by: Ishaan Jaffer <ishaanjaffer0324@gmail.com>
Summary
Adds five sparse Prometheus counter metrics that break out the token detail fields providers already report in
usage.prompt_tokens_detailsandusage.completion_tokens_details:litellm_input_cached_tokens_metricprompt_tokens_details.cached_tokens(OpenAI prompt cache, Anthropiccache_read_input_tokens)litellm_input_cache_creation_tokens_metricprompt_tokens_details.cache_creation_tokens(Anthropiccache_creation_input_tokens)litellm_input_audio_tokens_metricprompt_tokens_details.audio_tokenslitellm_output_reasoning_tokens_metriccompletion_tokens_details.reasoning_tokenslitellm_output_audio_tokens_metriccompletion_tokens_details.audio_tokensNo breaking changes — existing
litellm_input_tokens_metric/litellm_output_tokens_metric/litellm_total_tokens_metricare untouched. The new counters are additive and only emit when the underlying value is > 0, keeping scrape output sparse for providers that don't report a given field. Each new counter reuses the same label set as its parent input/output token counter, so dashboards can join cleanly.Data is read from the canonical
Usagedict thatget_standard_logging_object_payloadalready attaches atstandard_logging_payload["metadata"]["usage_object"]— no new plumbing through the logging pipeline.Closes LIT-3220.
Visual proof — /metrics output after driving both an Anthropic-shaped and an OpenAI-shaped Usage payload
For the Anthropic call (
cached_tokens=800,cache_creation_tokens=200, no audio/reasoning) and the OpenAI audio call (cached_tokens=100,audio_in=50,reasoning=220,audio_out=40), the new counters appear with the expected values and stay sparse for fields the provider didn't report (nocache_creationline for the OpenAI call, noaudio/reasoninglines for the Anthropic call):The same artifact is committed at
proof/lit-3220-metrics.pngon this branch.Test plan
tests/test_litellm/integrations/test_prometheus_token_detail_metrics.pycovering registration, label-set parity, all-types increment, and zero/None/negative/missing-metadata skip pathstest_prometheus_cache_metrics.py/test_prometheus_labels.py/test_prometheus_metric_name_consistency.pycontinue to pass (33/33 green)uv run black .cleanPrometheusLogger._increment_token_metricswith Anthropic- and OpenAI-shaped payloads, scraped viaprometheus_client.generate_latest(), confirmed correct values + sparse behavior (see screenshot)🤖 Generated with Claude Code