Repository navigation
fix(llm): keep choices of OpenAI streaming chunks that also carry usage - #9394
Ayush Suryawanshi (GloballyOptimized) wants to merge 2 commits into
Conversation
The Chat Completions streaming handlers (sync, async, and async response_model) did `continue` as soon as a chunk had `usage`, so any `choices` on that same chunk were dropped: the final text delta, the last tool-call argument fragment, and the finish_reason. OpenAI-compatible servers such as DeepSeek send exactly such a terminal chunk. Record usage and then fall through to normal choice handling. Chunks with `choices=[]` (OpenAI's separate usage trailer) are still skipped by the existing empty-choices check, and usage is still tracked once. Fixes crewAIInc#8775 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Strix Security ReviewWarning This pull request has 2 commits after the last Strix review ( ✅ No open security findings. PR summary: Removes three `continue` statements that made the OpenAI (and OpenAI-compatible) streaming handlers skip a chunk's `choices` whenever the same chunk also carried token `usage`, so terminal text deltas, tool-call argument fragments and `finish_reason` are no longer silently dropped. Adds a test module covering the sync, async and async structured-output streaming paths. Review summaryThis PR removes the I traced the newly-reachable code after each of the three hunks (sync streaming, async streaming, and async streaming with The added test module is test-only and contains no real credentials. No security issues found in the changed code. Ruled out (4)
Review 1 on this PR · Reviewed by Strix |
Related issue
Fixes #8775
Summary
The Chat Completions streaming loops
continueas soon as a chunk hasusage, so anything else in that chunk (the last text delta, the last tool-call arguments, orfinish_reason) gets dropped. DeepSeek's final chunk carries both usage andfinish_reason, so DeepSeek users always lose the finish reason.This removes the
continuein the three streaming paths (sync, async, async withresponse_model). OpenAI's separate usage-only chunk (choices=[]) is still skipped by the existing empty-choices check, and usage is still tracked once at the end.Verification
New tests in
tests/llms/openai/test_streaming_usage_chunks.pyfeed synthetic chunks through a stub client (no network). They cover text, tool-call andfinish_reasoncases for sync and async, plus structured output and controls for the normal OpenAI shape. They fail on main and pass with this change. The existing OpenAI, OpenAI-compatible and Snowflake tests pass, and ruff/mypy are clean.Additional context
Supersedes #8776, which was closed unmerged.
This PR was prepared with AI assistance (Claude Code); I reviewed and tested it. Per CONTRIBUTING it needs the
llm-generatedlabel, which I can't add as an external contributor. Could a maintainer please apply it?🤖 Generated with Claude Code