fix(arize): _set_usage_outputs handles raw OpenAI Pydantic CompletionUsage - #26506
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.
`_set_usage_outputs` called `usage.get(...)` and
`usage.get('output_tokens_details', {}).get('reasoning_tokens')`. These
crash with `AttributeError: 'CompletionUsage' object has no attribute
'get'` when `usage` (or the nested token-details object) is a raw OpenAI
Pydantic model rather than a dict / litellm `Usage` wrapper. Reproduces
on the langfuse_otel + arize Responses API logging paths.
Fixes BerriAI#13672.
Changes:
- Add `_safe_get(obj, key, default)` that prefers dict-style `.get` when
available and otherwise falls back to `getattr`. Works uniformly for
dicts, litellm's `Usage`, and plain Pydantic models like
`openai.types.completion_usage.CompletionUsage` /
`CompletionTokensDetails` / `OutputTokensDetails`.
- Use `_safe_get` for total / completion / prompt / output tokens.
- Look for reasoning tokens in `completion_tokens_details` (Chat
Completions API) before falling back to `output_tokens_details`
(Responses API). Previously reasoning tokens from the Chat Completions
API were silently dropped.
Tests:
- `test_set_usage_outputs_pydantic_completion_usage` — covers the chat
completions path with raw `CompletionUsage` + `CompletionTokensDetails`.
- `test_set_usage_outputs_pydantic_response_api_usage` — covers the
Responses API path with a Pydantic usage object lacking `.get`.
Both tests fail on main before this commit and pass after.
|
alvinttang seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account. You have signed the CLA already but the status is still pending? Let us recheck it. |
Low: No security issuesThis PR adds a Status: 0 open Posted by Veria AI · 2026-04-25T21:08:53.488Z |
Greptile SummaryThis PR fixes a long-standing Confidence Score: 5/5Safe to merge — the fix is backward-compatible, narrowly scoped, and fully covered by new regression tests. No P0 or P1 findings. The No files require special attention.
|
| Filename | Overview |
|---|---|
| litellm/integrations/arize/_utils.py | Adds _safe_get helper to unify dict/pydantic attribute access and updates _set_usage_outputs to use it; also fixes reasoning_tokens lookup order for Chat vs Responses API. |
| tests/test_litellm/integrations/arize/test_arize_utils.py | Adds two regression tests covering the Pydantic CompletionUsage and Responses API paths; no real network calls made. |
| tests/test_litellm/test_utils.py | Extends the JSON schema validator to allow supports_low_reasoning_effort as a valid boolean field. |
| litellm/model_prices_and_context_window_backup.json | Adds azure/gpt-5.5, azure/gpt-5.5-2026-04-23, and azure/gpt-5.5-pro model entries with pricing, context window, and capability flags. |
| model_prices_and_context_window.json | Mirror of backup JSON: adds same azure/gpt-5.5 model family entries. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A["_set_usage_outputs(span, response_obj, span_attrs)"] --> B["usage = response_obj.get('usage')"]
B --> C{usage is falsy?}
C -- yes --> Z[return]
C -- no --> D["_safe_get(usage, 'total_tokens')"]
D --> E["set LLM_TOKEN_COUNT_TOTAL"]
E --> F["_safe_get(usage, 'completion_tokens')\n OR _safe_get(usage, 'output_tokens')"]
F --> G["set LLM_TOKEN_COUNT_COMPLETION"]
G --> H["_safe_get(usage, 'prompt_tokens')\n OR _safe_get(usage, 'input_tokens')"]
H --> I["set LLM_TOKEN_COUNT_PROMPT"]
I --> J["token_details = _safe_get(usage, 'completion_tokens_details')\n OR _safe_get(usage, 'output_tokens_details')"]
J --> K["_safe_get(token_details, 'reasoning_tokens')"]
K --> L["set LLM_TOKEN_COUNT_COMPLETION_DETAILS_REASONING"]
subgraph _safe_get["_safe_get(obj, key, default)"]
S1{obj is None?} -- yes --> S2[return default]
S1 -- no --> S3{obj has callable .get?}
S3 -- yes --> S4["obj.get(key, default)"]
S3 -- no --> S5["getattr(obj, key, default)"]
end
Reviews (2): Last reviewed commit: "Merge branch 'litellm-oss-staging-04-25-..." | Re-trigger Greptile
Merging this PR will not alter performance
Comparing |
| token_details = _safe_get(usage, "completion_tokens_details") or _safe_get( | ||
| usage, "output_tokens_details" | ||
| ) |
There was a problem hiding this comment.
or truthiness check may bypass completion_tokens_details unintentionally
If a CompletionTokensDetails Pydantic model instance defines a custom __bool__ or __len__ that returns falsy (e.g. all fields are zero/None), _safe_get(usage, "completion_tokens_details") or _safe_get(usage, "output_tokens_details") would fall through to output_tokens_details even though completion_tokens_details was explicitly present. Using an explicit None check would be safer:
token_details = _safe_get(usage, "completion_tokens_details")
if token_details is None:
token_details = _safe_get(usage, "output_tokens_details")| token_details = _safe_get(usage, "completion_tokens_details") or _safe_get( | |
| usage, "output_tokens_details" | |
| ) | |
| token_details = _safe_get(usage, "completion_tokens_details") | |
| if token_details is None: | |
| token_details = _safe_get(usage, "output_tokens_details") |
| completion_tokens_details=CompletionTokensDetails(reasoning_tokens=25), | ||
| ) | ||
| assert not hasattr(usage, "get"), "precondition: CompletionUsage must lack .get" | ||
|
|
There was a problem hiding this comment.
Precondition assertion is fragile against library updates
assert not hasattr(usage, "get") hard-codes an assumption about the openai SDK's internal Pydantic model structure. If a future version of openai or Pydantic v2 adds a .get() method (e.g. for dict-compatibility), this assertion will fail and block CI even though the fix itself still works correctly via the callable(getter) branch of _safe_get. Consider removing this precondition or rewriting it as a pytest.skip with a note, so the test degrades gracefully.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
…-otel-pydantic-usage
98a9005
into
BerriAI:litellm-oss-staging-04-25-2026
`_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
…Usage (BerriAI#26506) * [Feat] Day-0 support for GPT-5.5 and GPT-5.5 Pro (BerriAI#26449) * 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. * [Feat] Add azure/gpt-5.5 + azure/gpt-5.5-pro entries (+ dated variants) (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 OpenAI pro entries, but having the schema accept it lets cost-map tests pass on either merge order. * fix(arize/langfuse_otel): handle Pydantic usage objects without `.get` `_set_usage_outputs` called `usage.get(...)` and `usage.get('output_tokens_details', {}).get('reasoning_tokens')`. These crash with `AttributeError: 'CompletionUsage' object has no attribute 'get'` when `usage` (or the nested token-details object) is a raw OpenAI Pydantic model rather than a dict / litellm `Usage` wrapper. Reproduces on the langfuse_otel + arize Responses API logging paths. Fixes BerriAI#13672. Changes: - Add `_safe_get(obj, key, default)` that prefers dict-style `.get` when available and otherwise falls back to `getattr`. Works uniformly for dicts, litellm's `Usage`, and plain Pydantic models like `openai.types.completion_usage.CompletionUsage` / `CompletionTokensDetails` / `OutputTokensDetails`. - Use `_safe_get` for total / completion / prompt / output tokens. - Look for reasoning tokens in `completion_tokens_details` (Chat Completions API) before falling back to `output_tokens_details` (Responses API). Previously reasoning tokens from the Chat Completions API were silently dropped. Tests: - `test_set_usage_outputs_pydantic_completion_usage` — covers the chat completions path with raw `CompletionUsage` + `CompletionTokensDetails`. - `test_set_usage_outputs_pydantic_response_api_usage` — covers the Responses API path with a Pydantic usage object lacking `.get`. Both tests fail on main before this commit and pass after. --------- Co-authored-by: yuneng-jiang <yuneng@berri.ai> Co-authored-by: Mateo Wang <277851410+mateo-berri@users.noreply.github.com> Co-authored-by: alvinttang <alvin@pm.me> Co-authored-by: Krrish Dholakia <krrish+github@berri.ai>
…Usage (BerriAI#26506) * [Feat] Day-0 support for GPT-5.5 and GPT-5.5 Pro (BerriAI#26449) * 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. * [Feat] Add azure/gpt-5.5 + azure/gpt-5.5-pro entries (+ dated variants) (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. * fix(arize/langfuse_otel): handle Pydantic usage objects without `.get` `_set_usage_outputs` called `usage.get(...)` and `usage.get('output_tokens_details', {}).get('reasoning_tokens')`. These crash with `AttributeError: 'CompletionUsage' object has no attribute 'get'` when `usage` (or the nested token-details object) is a raw OpenAI Pydantic model rather than a dict / litellm `Usage` wrapper. Reproduces on the langfuse_otel + arize Responses API logging paths. Fixes BerriAI#13672. Changes: - Add `_safe_get(obj, key, default)` that prefers dict-style `.get` when available and otherwise falls back to `getattr`. Works uniformly for dicts, litellm's `Usage`, and plain Pydantic models like `openai.types.completion_usage.CompletionUsage` / `CompletionTokensDetails` / `OutputTokensDetails`. - Use `_safe_get` for total / completion / prompt / output tokens. - Look for reasoning tokens in `completion_tokens_details` (Chat Completions API) before falling back to `output_tokens_details` (Responses API). Previously reasoning tokens from the Chat Completions API were silently dropped. Tests: - `test_set_usage_outputs_pydantic_completion_usage` — covers the chat completions path with raw `CompletionUsage` + `CompletionTokensDetails`. - `test_set_usage_outputs_pydantic_response_api_usage` — covers the Responses API path with a Pydantic usage object lacking `.get`. Both tests fail on main before this commit and pass after. --------- Co-authored-by: yuneng-jiang <yuneng@berri.ai> Co-authored-by: Mateo Wang <277851410+mateo-berri@users.noreply.github.com> Co-authored-by: alvinttang <alvin@pm.me> Co-authored-by: Krrish Dholakia <krrish+github@berri.ai>
Summary
Fixes #13672 (252 days old, 18 comments).
_set_usage_outputsinlitellm/integrations/arize/_utils.pycallsusage.get("total_tokens")andusage.get("output_tokens_details", {}).get("reasoning_tokens"). When the logger is invoked vialangfuse_otel(or arize on a Chat Completions response wrapped as raw OpenAI Pydantic),usageisopenai.types.completion_usage.CompletionUsage— a Pydantic v2 model with no.getmethod — so the call raisesAttributeError. Same for nestedCompletionTokensDetails/OutputTokensDetails.Latent secondary bug: reasoning_tokens for Chat Completions live in
completion_tokens_details, notoutput_tokens_details, so they were silently dropped on that path.Fix
_safe_get(obj, key, default)helper that prefers dict-style.getwhen callable and falls back togetattrotherwise — uniform for dicts, litellmUsage, and raw OpenAI Pydantic models. Used for total / completion / prompt / output tokens. For reasoning tokens, triescompletion_tokens_details(Chat Completions) first, thenoutput_tokens_details(Responses API). +42 LOC.Test
tests/test_litellm/integrations/arize/test_arize_utils.py:test_set_usage_outputs_pydantic_completion_usage— Chat Completions API path with rawCompletionUsage+CompletionTokensDetails.test_set_usage_outputs_pydantic_response_api_usage— Responses API path with usage object lacking.get.Both fail on main with
AttributeError, pass after the fix. Widerpytestof arize + langfuse integrations: 52/52 (2 pre-existing proxy-import failures unrelated).ruffclean.Risk notes
_safe_getreturns identical values for dicts and litellmUsage(which has.get); only differs on Pydantic-without-.get, where it now succeeds instead of crashing.completion_tokens_detailslookup change is additive — adds reasoning_tokens for Chat Completions that were previously silently dropped; does not regress Responses API.Refs #13672