mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-07 08:26:10 +00:00
Merge 5197f50570 into 44d84360fb
This commit is contained in:
commit
2c7e32679c
2 changed files with 90 additions and 1 deletions
|
|
@ -467,7 +467,11 @@ class OllamaChatCompletionResponseIterator(BaseModelResponseIterator):
|
|||
tool_calls: Final = chunk["message"].get("tool_calls")
|
||||
if tool_calls is not None:
|
||||
for tool_call in tool_calls:
|
||||
function_args = tool_call.get("function").get("arguments")
|
||||
function = tool_call.get("function") or {}
|
||||
function_index = function.get("index")
|
||||
if function_index is not None:
|
||||
tool_call["index"] = function_index
|
||||
function_args = function.get("arguments")
|
||||
if function_args is not None and len(function_args) > 0:
|
||||
is_function_call_complete = self._is_function_call_complete(function_args)
|
||||
if is_function_call_complete:
|
||||
|
|
|
|||
|
|
@ -616,6 +616,91 @@ class TestOllamaFinishReasonLength:
|
|||
), f"Expected 'stop' for natural finish, got '{result.choices[0].finish_reason}'"
|
||||
|
||||
|
||||
class TestOllamaStreamingToolCallIndex:
|
||||
"""Streaming: parallel tool calls must preserve Ollama's per-call function.index."""
|
||||
|
||||
def test_parallel_tool_calls_preserve_distinct_index(self):
|
||||
"""
|
||||
Ollama streams one tool call per chunk with the correct index nested at
|
||||
function.index (0, then 1). Previously LiteLLM dropped it and every delta
|
||||
was emitted with index=0, so clients that accumulate arguments by index
|
||||
merged parallel calls into one malformed tool call.
|
||||
"""
|
||||
iterator = OllamaChatCompletionResponseIterator(
|
||||
streaming_response=iter([]),
|
||||
sync_stream=True,
|
||||
)
|
||||
|
||||
first_chunk = {
|
||||
"model": "qwen3:14b",
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"function": {
|
||||
"index": 0,
|
||||
"name": "read_file",
|
||||
"arguments": {"path": "a.rs"},
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"done": False,
|
||||
}
|
||||
second_chunk = {
|
||||
"model": "qwen3:14b",
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"function": {
|
||||
"index": 1,
|
||||
"name": "read_file",
|
||||
"arguments": {"path": "b.rs"},
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"done": False,
|
||||
}
|
||||
|
||||
first_result = iterator.chunk_parser(first_chunk)
|
||||
second_result = iterator.chunk_parser(second_chunk)
|
||||
|
||||
assert first_result.choices[0].delta.tool_calls[0].index == 0
|
||||
assert second_result.choices[0].delta.tool_calls[0].index == 1
|
||||
|
||||
def test_missing_function_index_falls_back_to_positional(self):
|
||||
"""When Ollama omits function.index, Delta still assigns a positional index."""
|
||||
iterator = OllamaChatCompletionResponseIterator(
|
||||
streaming_response=iter([]),
|
||||
sync_stream=True,
|
||||
)
|
||||
|
||||
chunk = {
|
||||
"model": "qwen3:14b",
|
||||
"message": {
|
||||
"role": "assistant",
|
||||
"content": "",
|
||||
"tool_calls": [
|
||||
{
|
||||
"function": {
|
||||
"name": "read_file",
|
||||
"arguments": {"path": "a.rs"},
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
"done": False,
|
||||
}
|
||||
|
||||
result = iterator.chunk_parser(chunk)
|
||||
|
||||
assert result.choices[0].delta.tool_calls[0].index == 0
|
||||
|
||||
|
||||
class TestOllamaReasoningContentStreaming:
|
||||
"""Test that reasoning_content is properly extracted from all thinking chunks."""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue