This commit is contained in:
duanhongyi 2026-08-27 18:08:35 -05:00 • committed by GitHub
commit 47cb8e8a16
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 75 additions and 1 deletions

View file

@ -324,7 +324,15 @@ class ChunkProcessor:
system_fingerprint: Final = chunk.get("system_fingerprint", None)
first_chunk_with_choices: Final = next((c for c in chunks if c.get("choices")), chunk)
role: Final = first_chunk_with_choices["choices"][0]["delta"]["role"]
if (
first_chunk_with_choices.get("choices")
and len(first_chunk_with_choices["choices"]) > 0
and first_chunk_with_choices["choices"][0].get("delta")
and "role" in first_chunk_with_choices["choices"][0]["delta"]
):
role: Final = first_chunk_with_choices["choices"][0]["delta"]["role"]
else:
role = "assistant"
finish_reason = "stop"
for chunk in chunks:
if "choices" in chunk and len(chunk["choices"]) > 0:

View file

@ -0,0 +1,66 @@
import os
import sys
sys.path.insert(
0, os.path.abspath("../../..")
) # Adds the parent directory to the system path
from litellm.litellm_core_utils.streaming_chunk_builder_utils import ChunkProcessor
def test_build_base_response_all_chunks_empty_choices():
"""
Regression test for the IndexError in ChunkProcessor.build_base_response
when every chunk has choices=[] (e.g. Anthropic usage-only events).
``next((c for c in chunks if c.get("choices")), chunk)`` skips every chunk
(empty list is falsy) and falls back to ``chunk``, whose ``choices`` is also
``[]`` — the old code then did ``choices[0]`` and raised IndexError.
build_base_response should default to role='assistant'.
"""
chunks = [
{
"id": "chatcmpl-all-empty-choices",
"object": "chat.completion.chunk",
"created": 1234567890,
"model": "gpt-4.1-mini",
"choices": [],
},
{
"id": "chatcmpl-all-empty-choices",
"object": "chat.completion.chunk",
"created": 1234567890,
"model": "gpt-4.1-mini",
"choices": [],
},
]
processor = ChunkProcessor(chunks=chunks)
response = processor.build_base_response(chunks)
assert response.choices[0].message.role == "assistant"
assert response.choices[0].message.content == ""
def test_build_base_response_falls_back_to_assistant_when_role_missing():
"""
When a chunk has non-empty choices but its delta omits the role key,
build_base_response should still produce a valid response and default
to 'assistant'.
"""
chunks = [
{
"id": "chatcmpl-no-role",
"object": "chat.completion.chunk",
"created": 1234567890,
"model": "gpt-4.1-mini",
"choices": [
{"index": 0, "delta": {"content": "hello"}}
],
}
]
processor = ChunkProcessor(chunks=chunks)
response = processor.build_base_response(chunks)
assert response.choices[0].message.role == "assistant"
assert response.choices[0].message.content == ""