mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-27 06:13:07 +00:00
fix(sap): default FunctionObj.arguments for streaming tool calls
Streaming tool-call deltas from Claude/SAP often omit `arguments` on intermediate chunks (name only). FunctionObj required the field, so pydantic ValidationError crashed the proxy on multi-turn streamed tool use. Default arguments to "" and add unit tests covering FunctionObj, MessageToolCall, and SAPAssistantMessage. Fixes #46405
This commit is contained in:
parent
79abdb66e6
commit
09c4ea4648
2 changed files with 57 additions and 1 deletions
|
|
@ -65,7 +65,9 @@ class ImageContent(LiteLLMBaseModel):
|
|||
|
||||
class FunctionObj(LiteLLMBaseModel):
|
||||
name: str
|
||||
arguments: str
|
||||
# Default empty string: streaming tool-call deltas often omit arguments
|
||||
# until the final chunk (OpenAI/Anthropic streaming semantics).
|
||||
arguments: str = ""
|
||||
|
||||
|
||||
class FunctionTool(LiteLLMBaseModel):
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ import pytest
|
|||
from pydantic import ValidationError
|
||||
|
||||
from litellm.llms.sap.chat.models import (
|
||||
FunctionObj,
|
||||
MessageToolCall,
|
||||
SAPAssistantMessage,
|
||||
SAPMessage,
|
||||
SAPToolChatMessage,
|
||||
|
|
@ -168,6 +170,58 @@ class TestSAPAssistantMessage:
|
|||
assert msg.content.cache_control.type == "ephemeral"
|
||||
|
||||
|
||||
class TestFunctionObj:
|
||||
def test_name_and_arguments_accepted(self):
|
||||
obj = FunctionObj.model_validate({"name": "get_weather", "arguments": '{"city":"Berlin"}'})
|
||||
assert obj.name == "get_weather"
|
||||
assert obj.arguments == '{"city":"Berlin"}'
|
||||
|
||||
def test_missing_arguments_defaults_to_empty_string(self):
|
||||
# Streaming intermediate tool-call chunks often omit arguments (name only).
|
||||
obj = FunctionObj.model_validate({"name": "get_weather"})
|
||||
assert obj.name == "get_weather"
|
||||
assert obj.arguments == ""
|
||||
|
||||
def test_explicit_empty_arguments_accepted(self):
|
||||
obj = FunctionObj.model_validate({"name": "get_weather", "arguments": ""})
|
||||
assert obj.arguments == ""
|
||||
|
||||
def test_missing_name_rejected(self):
|
||||
with pytest.raises(ValidationError):
|
||||
FunctionObj.model_validate({"arguments": "{}"})
|
||||
|
||||
|
||||
class TestMessageToolCall:
|
||||
def test_tool_call_with_arguments(self):
|
||||
tc = MessageToolCall.model_validate({
|
||||
"id": "call_1",
|
||||
"type": "function",
|
||||
"function": {"name": "get_weather", "arguments": "{}"},
|
||||
})
|
||||
assert tc.function.name == "get_weather"
|
||||
assert tc.function.arguments == "{}"
|
||||
|
||||
def test_tool_call_without_arguments_for_streaming_delta(self):
|
||||
tc = MessageToolCall.model_validate({
|
||||
"id": "call_1",
|
||||
"type": "function",
|
||||
"function": {"name": "get_weather"},
|
||||
})
|
||||
assert tc.function.arguments == ""
|
||||
|
||||
def test_assistant_message_streaming_tool_call_without_arguments(self):
|
||||
msg = SAPAssistantMessage.model_validate({
|
||||
"tool_calls": [{
|
||||
"id": "call_1",
|
||||
"type": "function",
|
||||
"function": {"name": "get_weather"},
|
||||
}]
|
||||
})
|
||||
assert len(msg.tool_calls) == 1
|
||||
assert msg.tool_calls[0].function.name == "get_weather"
|
||||
assert msg.tool_calls[0].function.arguments == ""
|
||||
|
||||
|
||||
class TestSAPToolChatMessage:
|
||||
def test_role_is_always_tool(self):
|
||||
msg = SAPToolChatMessage.model_validate({"tool_call_id": "call_1", "content": "ok"})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue