Skip to content

Commit cf429e7

Browse files
Merge pull request #14587 from timelfrink/fix/streaming-tool-call-indices
Fix: Streaming tool call index assignment for multiple tool calls
2 parents 197e98b + d4540d3 commit cf429e7

File tree

2 files changed

+51
-1
lines changed

2 files changed

+51
-1
lines changed

litellm/types/utils.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -757,10 +757,12 @@ def __init__(
757757
self.function_call = function_call
758758
if tool_calls is not None and isinstance(tool_calls, list):
759759
self.tool_calls = []
760+
current_index = 0
760761
for tool_call in tool_calls:
761762
if isinstance(tool_call, dict):
762763
if tool_call.get("index", None) is None:
763-
tool_call["index"] = 0
764+
tool_call["index"] = current_index
765+
current_index += 1
764766
self.tool_calls.append(ChatCompletionDeltaToolCall(**tool_call))
765767
elif isinstance(tool_call, ChatCompletionDeltaToolCall):
766768
self.tool_calls.append(tool_call)

tests/litellm_utils_tests/test_utils.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2328,6 +2328,54 @@ def test_get_whitelisted_models():
23282328
print("whitelisted_models written to whitelisted_bedrock_models.txt")
23292329

23302330

2331+
def test_delta_tool_calls_sequential_indices():
2332+
"""
2333+
Test that multiple tool calls without explicit indices receive sequential indices.
2334+
2335+
When providers don't include index fields in tool calls, the Delta class
2336+
should automatically assign sequential indices (0, 1, 2, ...) instead of
2337+
defaulting all tool calls to index=0.
2338+
"""
2339+
import json
2340+
from litellm.types.utils import Delta
2341+
2342+
# Simulate tool calls from streaming responses without explicit indices
2343+
tool_calls_without_indices = [
2344+
{
2345+
"id": "call_1",
2346+
"function": {
2347+
"name": "get_weather_for_dallas",
2348+
"arguments": json.dumps({})
2349+
},
2350+
"type": "function",
2351+
# Note: no "index" field - simulates provider response
2352+
},
2353+
{
2354+
"id": "call_2",
2355+
"function": {
2356+
"name": "get_weather_precise",
2357+
"arguments": json.dumps({"location": "Dallas, TX"})
2358+
},
2359+
"type": "function",
2360+
# Note: no "index" field - simulates provider response
2361+
}
2362+
]
2363+
2364+
# Create Delta object as LiteLLM would when processing streaming response
2365+
delta = Delta(
2366+
content=None,
2367+
tool_calls=tool_calls_without_indices
2368+
)
2369+
2370+
# Verify tool calls have sequential indices
2371+
assert delta.tool_calls is not None, "Tool calls should not be None"
2372+
assert len(delta.tool_calls) == 2
2373+
assert delta.tool_calls[0].index == 0, f"First tool call should have index 0, got {delta.tool_calls[0].index}"
2374+
assert delta.tool_calls[1].index == 1, f"Second tool call should have index 1, got {delta.tool_calls[1].index}"
2375+
2376+
# Verify tool call details are preserved
2377+
assert delta.tool_calls[0].function.name == "get_weather_for_dallas"
2378+
assert delta.tool_calls[1].function.name == "get_weather_precise"
23312379

23322380
def test_completion_with_no_model():
23332381
"""

0 commit comments

Comments
 (0)