feat(arize): emit prompt-caching tokens as OpenInference span attributes - #26568
feat(arize): emit prompt-caching tokens as OpenInference span attributes#26568restato wants to merge 5 commits into
Conversation
[Infra] Promote internal staging to main
* feat(openai): day-0 support for GPT-5.5 and GPT-5.5 Pro
Add pricing + capability entries for the new GPT-5.5 family launched by
OpenAI on 2026-04-24:
- gpt-5.5 / gpt-5.5-2026-04-23 (chat): $5/$30/$0.50 per 1M
input/output/cached input
- gpt-5.5-pro / gpt-5.5-pro-2026-04-23 (responses-only): $60/$360/$6
per 1M input/output/cached input
Other fees (long-context >272k, flex, batches, priority, cache
discounts) follow the same ratios as GPT-5.4, with context window
retained at 1.05M input / 128K output.
No transformation / classifier code changes are required:
OpenAIGPT5Config.is_model_gpt_5_4_plus_model() already matches 5.5+ via
numeric version parsing, and model registration is driven from the
JSON. The existing responses-API bridge for tools + reasoning_effort
(litellm/main.py:970) already covers gpt-5.5-pro.
Tests:
- GPT5_MODELS regression list now covers gpt-5.5-pro and dated variants
- New test_generic_cost_per_token_gpt55_pro cost-calc test
- Updated test_generic_cost_per_token_gpt55 for long-context fields
* fix(openai): mirror reasoning_effort flags onto gpt-5.5 dated variants
gpt-5.5-2026-04-23 and gpt-5.5-pro-2026-04-23 were missing the
supports_none_reasoning_effort, supports_xhigh_reasoning_effort, and
supports_minimal_reasoning_effort flags that their non-dated
counterparts define. Reasoning-effort routing in OpenAIGPT5Config is
fully capability-driven from these JSON flags — since an absent flag
is treated as False for opt-in levels (xhigh), users pinning to a
dated snapshot would silently lose xhigh support and diverge from the
base alias on logprobs + flexible temperature handling.
Copy the flags onto both dated variants so every dated snapshot
inherits the base model's reasoning-effort capability profile.
Adds a parametrized regression test that asserts
supports_{none,minimal,xhigh}_reasoning_effort parity between each
dated variant and its non-dated counterpart, preventing future drift
when new snapshots are added.
…s) (BerriAI#26361) * feat(azure): add azure/gpt-5.5 + azure/gpt-5.5-pro entries (+ dated variants) Azure variants of OpenAI's GPT-5.5 family. Microsoft has not yet shipped GPT-5.5 on Azure OpenAI (latest GA on the Foundry models page is GPT-5.4 as of 2026-04-24), but adding the entries day-0 mirrors the established precedent for azure/gpt-5.4* (which were in the cost map before the Azure rollout) so cost tracking and capability flags work the moment customers deploy. Schema follows the existing azure/gpt-5.4* shape: - Same base/long-context pricing as openai/gpt-5.5*: $5/$30 chat, $60/$360 pro per 1M, with priority tier 2x base - Azure variants drop the flex/batches keys (Azure has no flex tier) but keep priority pricing, matching gpt-5.4* precedent - mode=chat for the thinking model, mode=responses for pro reasoning_effort capability flags mirror the OpenAI variants exactly since Azure proxies the same API contract: minimal rejection on both chat and pro, low/none rejection on pro. Once BerriAI#26456 (which sets supports_low_reasoning_effort + minimal=false on openai/gpt-5.5*) lands, OpenAI and Azure flag profiles align. Tests pin entry presence + pricing for all four Azure variants and verify the live-API-derived reasoning_effort flags. * test: register supports_low_reasoning_effort in cost-map JSON schema azure/gpt-5.5-pro and azure/gpt-5.5-pro-2026-04-23 added in this branch carry supports_low_reasoning_effort=false. The strict 'additionalProperties: false' schema in test_aaamodel_prices_and_context_window_json_is_valid rejected the new key. Register it alongside the other supports_*_reasoning_effort entries. Note: the runtime side of this flag (code that reads it) lands in BerriAI#26456. Until that PR merges the flag is inert for both Azure and OpenAI pro entries, but having the schema accept it lets cost-map tests pass on either merge order.
[Infra] Promote Internal Staging to main
`_set_usage_outputs` emits only total / completion / prompt / reasoning
tokens, leaving `LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_READ` and
`LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITE` defined-but-unused. As a
result, observability backends (Langfuse, Arize Phoenix) cannot display
the prompt-cache breakdown nor apply correct cost calculation for
Anthropic / Bedrock prompt caching (cache reads at 0.1x, cache writes
at 1.25x of standard input).
Read from `usage.prompt_tokens_details` so the change is provider-agnostic.
LiteLLM's `Usage.__init__` already normalizes provider-specific cache
fields onto this object:
- Anthropic `cache_read_input_tokens` -> prompt_tokens_details.cached_tokens
- DeepSeek `prompt_cache_hit_tokens` -> prompt_tokens_details.cached_tokens
- OpenAI native cached_tokens -> prompt_tokens_details.cached_tokens
- Anthropic `cache_creation_input_tokens` -> prompt_tokens_details.cache_creation_tokens
Mapping:
- prompt_tokens_details.cached_tokens
-> llm.token_count.prompt_details.cache_read
- prompt_tokens_details.cache_creation_tokens
-> llm.token_count.prompt_details.cache_write
Note: PR BerriAI#24112 introduced a similar `cached_tokens` block in
litellm_oss_staging_03_21_2026 but has not yet landed on main; PR BerriAI#26506
on litellm-oss-staging-04-25-2026 refactored the function without
including it. This change brings the emission directly to main with
extended cache_creation coverage.
Tests:
- Anthropic: both cache_read and cache_write emitted
- OpenAI: only cache_read emitted (no cache_write concept)
- DeepSeek: prompt_cache_hit_tokens normalized -> cache_read emitted
- No-cache: neither attribute emitted
Greptile SummaryThis PR restores the Confidence Score: 4/5Safe to merge; the change is a small, isolated additive fix with good test coverage and no breaking changes. Only a single P2 style finding (truthy guard vs. is not None for zero token counts). No logic bugs, no security concerns, no backwards-incompatible changes. Score capped at 4 due to the P2 finding per scoring guidelines. No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/integrations/arize/_utils.py | Adds prompt-cache token emission (cache_read / cache_write) to _set_usage_outputs; dual dict/object access pattern is correct; only nit is truthy-check silencing explicit zeros. |
| tests/test_litellm/integrations/arize/test_arize_utils.py | Adds 4 well-scoped mock-only tests covering Anthropic, OpenAI, DeepSeek, and no-cache paths; no real network calls; assertions are precise and correct. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[_set_usage_outputs] --> B{usage present?}
B -- No --> Z[return]
B -- Yes --> C[emit total / completion / prompt tokens]
C --> D[emit reasoning_tokens if present]
D --> E[get prompt_tokens_details]
E --> F{dict-like? hasattr get}
F -- Yes --> G[dict .get cached_tokens / cache_creation_tokens]
F -- No --> H[getattr cached_tokens / cache_creation_tokens]
G --> I{cached_tokens truthy?}
H --> I
I -- Yes --> J[set LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_READ]
I -- No --> K{cache_creation_tokens truthy?}
J --> K
K -- Yes --> L[set LLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITE]
K -- No --> M[done]
L --> M
Reviews (1): Last reviewed commit: "feat(arize): emit prompt-caching tokens ..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
|
@ryan-crabbe-berri Please review. |
|
🤖 litellm-agent: This PR was marked BLOCKED 7 days ago with no subsequent activity. Closing automatically. |
Relevant issues
Restores
cached_tokensemission that PR #24112 added but PR #26506 inadvertently removed during a refactor for raw OpenAI Pydantic usage support.Adds
cache_creation_tokensemission, which has never been wired up despite the OpenInferenceLLM_TOKEN_COUNT_PROMPT_DETAILS_CACHE_WRITEconstant being defined.Pre-Submission checklist
tests/test_litellm/directory — 4 new tests intests/test_litellm/integrations/arize/test_arize_utils.pytests/test_litellm/integrations/arize/test_arize_utils.py→ 8/8 passing)_set_usage_outputsto emit two additional OpenInference attributes@greptileaiand received a Confidence Score of at least 4/5 before requesting a maintainer reviewType
🐛 Bug Fix
Changes
What
_set_usage_outputsnow emits prompt-cache breakdown as OpenInference span attributes:usage.prompt_tokens_details.cached_tokensllm.token_count.prompt_details.cache_readusage.prompt_tokens_details.cache_creation_tokensllm.token_count.prompt_details.cache_writeWhy
LiteLLM normalizes provider-specific cache fields onto
prompt_tokens_detailsinUsage.__init__:cache_read_input_tokens→prompt_tokens_details.cached_tokensprompt_cache_hit_tokens→prompt_tokens_details.cached_tokensprompt_tokens_details.cached_tokens→ already therecache_creation_input_tokens→prompt_tokens_details.cache_creation_tokensReading from
prompt_tokens_detailskeeps the OTEL emitter provider-agnostic.Without these attributes, observability backends (Langfuse, Arize Phoenix) cannot display the cache breakdown nor apply correct cost calculation — Anthropic prices cache reads at 0.1× and cache writes at 1.25× standard input.
History of regression
cached_tokensemission ✅_set_usage_outputsto handle raw OpenAICompletionUsagePydantic objects, but the new function body dropped thecached_tokensblock. Cache reads stopped reaching observability backends. ❌cached_tokensand addscache_creation_tokens. ✅Tests
tests/test_litellm/integrations/arize/test_arize_utils.py:test_arize_set_attributes_anthropic_cache_tokens— both cache_read and cache_write settest_arize_set_attributes_openai_cached_tokens— only cache_read set (no cache_write concept on OpenAI)test_arize_set_attributes_deepseek_cache_hit_tokens—prompt_cache_hit_tokensnormalized → cache_read settest_arize_set_attributes_no_cache_tokens_omits_attributes— neither attribute set when usage lacks cache fieldsLint
black --checkpasses on changed filesruff checkpasses on_utils.py; pre-existingT201 printwarning inTestArizeLogger(line 469) is unrelated to this PR~