fix(streaming): guard against empty choices in _simple_text_part

When upstream providers emit trailing usage chunks with choices: [],
_stream_choice_delta returns an empty tuple, causing an IndexError on
deltas[0]. Added guards to return None when choices or deltas is empty.
This commit is contained in:
Himanshu Joshi 2026-09-27 00:39:57 +05:30
parent 41070b1363
commit f0fbcf7867
2 changed files with 20 additions and 0 deletions

View file

@ -8952,7 +8952,11 @@ def _delta_carries_more_than_text(delta: Mapping[str, object]) -> bool:
def _simple_text_part(choices: Sequence[object]) -> str | None:
if not choices:
return None
deltas: Final = tuple(_stream_choice_delta(choice) for choice in choices)
if not deltas:
return None
if any(_delta_carries_more_than_text(delta) for delta in deltas):
return None
content: Final = deltas[0].get("content")

View file

@ -895,3 +895,19 @@ def test_grok_bug(load_env):
litellm.set_verbose = True
_, LLAMA3_3 = load_env
execute_completion(LLAMA3_3)
def test_stream_chunk_builder_empty_choices_guard():
"""
Test that stream_chunk_builder does not crash with IndexError when
streaming chunks contain empty choices lists (e.g. trailing usage chunks).
"""
chunks = [
{"id": "chat-test-1", "model": "gpt-4o", "choices": [{"index": 0, "delta": {"role": "assistant", "content": "Hello"}}]},
{"id": "chat-test-1", "model": "gpt-4o", "choices": [{"index": 0, "delta": {"content": " world"}}]},
{"id": "chat-test-1", "model": "gpt-4o", "choices": [], "usage": {"prompt_tokens": 10, "completion_tokens": 2, "total_tokens": 12}},
]
res = litellm.stream_chunk_builder(chunks)
assert res is not None
assert res["choices"][0]["message"]["content"] == "Hello world"