mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-12 23:01:41 +00:00
fix: strip no-content chunks before stream_chunk_builder, keep usage-bearing ones
Remove chunks where choices==[] AND usage is absent — these cause stream_chunk_builder to IndexError on chunks[-1]["choices"][0] and silently drop the partial billing record. Usage-bearing empty-choices chunks (OpenAI stream_options include_usage final chunk) are explicitly preserved so real provider token counts are not replaced by estimates.
This commit is contained in:
parent
924a245edd
commit
e4f66c01a0
1 changed files with 16 additions and 0 deletions
|
|
@ -2465,6 +2465,22 @@ class ProxyBaseLLMRequestProcessing:
|
|||
return
|
||||
elif not isinstance(first_chunk, str) and not hasattr(first_chunk, "choices"):
|
||||
return
|
||||
# Remove pure no-content chunks (choices=[] and no usage payload) that can
|
||||
# cause stream_chunk_builder to IndexError on chunks[-1]["choices"][0].
|
||||
# Usage-bearing empty-choices chunks (OpenAI stream_options include_usage)
|
||||
# are intentionally kept so real token counts are not replaced by estimates.
|
||||
def _is_discardable_chunk(c: Any) -> bool:
|
||||
if isinstance(c, dict):
|
||||
return c.get("choices") == [] and not c.get("usage")
|
||||
return (
|
||||
hasattr(c, "choices")
|
||||
and getattr(c, "choices", None) == []
|
||||
and not getattr(c, "usage", None)
|
||||
)
|
||||
|
||||
chunks = [c for c in chunks if not _is_discardable_chunk(c)]
|
||||
if not chunks:
|
||||
return
|
||||
# Optimization, not a correctness guard: dispatch_success_handlers is the
|
||||
# authoritative de-dup via has_dispatched_final_stream_success. This just
|
||||
# skips the stream_chunk_builder assembly when completion already logged.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue