fix(streaming): preserve OpenAI SDK usage details in stream chunk builder - #36370
Conversation
0ad3811 to
1d69405
Compare
Greptile SummaryThe PR normalizes raw Pydantic usage objects before streamed usage calculation so provider-reported token details remain available.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| litellm/litellm_core_utils/streaming_chunk_builder_utils.py | Normalizes raw Pydantic usage models into LiteLLM’s usage representation while preserving nested token details. |
| tests/test_litellm/litellm_core_utils/test_streaming_chunk_builder_utils.py | Adds a representative OpenAI SDK usage regression test using a newly constructed, non-mutated chunk dictionary. |
Reviews (3): Last reviewed commit: "test(streaming): construct usage fixture..." | Re-trigger Greptile
| chunk["usage"] = CompletionUsage( | ||
| prompt_tokens=1714, | ||
| completion_tokens=14, | ||
| total_tokens=1728, | ||
| prompt_tokens_details={"cached_tokens": 1664}, | ||
| completion_tokens_details={"reasoning_tokens": 12}, | ||
| ) |
There was a problem hiding this comment.
Avoid mutating the test fixture
The test adds usage by mutating the dumped chunk after construction, contrary to the repository's immutability convention and making the complete fixture shape less explicit.
| chunk["usage"] = CompletionUsage( | |
| prompt_tokens=1714, | |
| completion_tokens=14, | |
| total_tokens=1728, | |
| prompt_tokens_details={"cached_tokens": 1664}, | |
| completion_tokens_details={"reasoning_tokens": 12}, | |
| ) | |
| chunk = { | |
| **chunk, | |
| "usage": CompletionUsage( | |
| prompt_tokens=1714, | |
| completion_tokens=14, | |
| total_tokens=1728, | |
| prompt_tokens_details={"cached_tokens": 1664}, | |
| completion_tokens_details={"reasoning_tokens": 12}, | |
| ), | |
| } |
Context Used: CLAUDE.md (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
1d69405 to
cc84566
Compare
cc84566 to
c3db280
Compare
|
Note: the |
TLDR
Problem this solves:
CompletionUsageobjects silently loseprompt_tokens_detailsandcompletion_tokens_detailsHow it solves it:
Usageat the stream chunk extraction boundaryopenai.CompletionUsageshapeUser Flow
Before: a developer streams chat completions through an OpenAI-compatible endpoint and their cost dashboard never sees cached-token or reasoning-token detail.
POST /v1/chat/completionswith"stream": trueand"stream_options": {"include_usage": true}prompt_tokens/completion_tokens, butprompt_tokens_detailsis missingAfter: the same request keeps the provider-reported usage details.
POST /v1/chat/completionswith"stream": trueand"stream_options": {"include_usage": true}prompt_tokens_details.cached_tokensandcompletion_tokens_details.reasoning_tokensRelevant issues
Related: #34801 (upstream #34812 fixed the generic stream-reassembly path; this covers raw OpenAI SDK pydantic usage shapes that still lost the details).
Linear ticket
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaito re-request a review after pushing changes)Delays in PR merge?
If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).
Screenshots / Proof of Fix
E2E proof against the real DeepSeek API through a litellm proxy, streamed chat completions with
stream_options.include_usage.Before (v1.97.0-rc.1 without this patch, second call with a shared prefix):
After (same proxy image plus this patch; original capture at commit
96af89842c, rebased ontolitellm_internal_stagingasc3db2800e3with no production code changes):The regression test also fails on unpatched
litellm_internal_staging:Type
🐛 Bug Fix
Changes
litellm/litellm_core_utils/streaming_chunk_builder_utils.py: in_extract_usage_chunk(), convert raw pydantic usage objects (e.g. OpenAI SDKCompletionUsage) to litellmUsageviamodel_dump()before the chunk builder reads them.tests/test_litellm/litellm_core_utils/test_streaming_chunk_builder_utils.py: add a regression test using a rawopenai.CompletionUsageon a stream chunk, assertingcached_tokensandreasoning_tokenssurvivecalculate_usage().Final Attestation