mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-07 08:26:10 +00:00
Merge 5ab11fd2c3 into 44d84360fb
This commit is contained in:
commit
12bafbe553
3 changed files with 127 additions and 3 deletions
|
|
@ -433,10 +433,10 @@ class LiteLLMCompletionStreamingIterator(ResponsesAPIStreamingIterator):
|
|||
if "text" in self.responses_api_request:
|
||||
response_created_event_data["text"] = self.responses_api_request["text"]
|
||||
if "tool_choice" in self.responses_api_request:
|
||||
# Transform tool_choice from dict format (e.g., {"type": "auto"}) to string format
|
||||
response_created_event_data["tool_choice"] = (
|
||||
LiteLLMCompletionResponsesConfig._transform_tool_choice(self.responses_api_request["tool_choice"])
|
||||
or "auto"
|
||||
LiteLLMCompletionResponsesConfig._transform_tool_choice_for_response(
|
||||
self.responses_api_request["tool_choice"]
|
||||
)
|
||||
)
|
||||
else:
|
||||
response_created_event_data["tool_choice"] = "auto"
|
||||
|
|
|
|||
|
|
@ -255,6 +255,41 @@ class LiteLLMCompletionResponsesConfig:
|
|||
# Return as-is for unknown formats
|
||||
return tool_choice
|
||||
|
||||
@staticmethod
|
||||
def _transform_tool_choice_for_response(
|
||||
tool_choice: Any,
|
||||
) -> str | dict[str, Any]:
|
||||
"""
|
||||
Normalize tool_choice into a shape valid for ResponsesAPIResponse.tool_choice
|
||||
when echoing the request back on response events.
|
||||
|
||||
Unlike _transform_tool_choice (which targets the Chat Completions request and
|
||||
emits the nested {"type": "function", "function": {"name": ...}} shape), the
|
||||
Responses API response object only accepts a string or the flat Responses API
|
||||
shape {"type": "function", "name": "..."}, so a forced named tool must keep the
|
||||
flat shape here to avoid a ResponsesAPIResponse ValidationError.
|
||||
"""
|
||||
if tool_choice is None:
|
||||
return "auto"
|
||||
|
||||
if isinstance(tool_choice, str):
|
||||
return tool_choice
|
||||
|
||||
if isinstance(tool_choice, dict):
|
||||
tool_choice_type = tool_choice.get("type")
|
||||
|
||||
if tool_choice_type == "function":
|
||||
function_name = tool_choice.get("name") or tool_choice.get("function", {}).get("name")
|
||||
if function_name:
|
||||
return {"type": "function", "name": function_name}
|
||||
return "required"
|
||||
if tool_choice_type in ("auto", "none", "required"):
|
||||
return tool_choice_type
|
||||
if tool_choice_type in ("tool", "any"):
|
||||
return "required"
|
||||
|
||||
return tool_choice
|
||||
|
||||
@staticmethod
|
||||
def _should_drop_derived_web_search_options(model: str, custom_llm_provider: str | None) -> bool:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1288,6 +1288,95 @@ class TestToolChoiceTransformation:
|
|||
assert result == "required"
|
||||
|
||||
|
||||
class TestToolChoiceForResponseTransformation:
|
||||
"""Regression tests for issue #33689: forcing a named tool call on the
|
||||
litellm_completion bridge with stream=true crashed with a
|
||||
ResponsesAPIResponse ValidationError because the response event echoed the
|
||||
nested Chat Completions tool_choice shape instead of the flat Responses API one"""
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"tool_choice, expected",
|
||||
[
|
||||
("auto", "auto"),
|
||||
("none", "none"),
|
||||
("required", "required"),
|
||||
(None, "auto"),
|
||||
({"type": "function", "name": "get_weather"}, {"type": "function", "name": "get_weather"}),
|
||||
(
|
||||
{"type": "function", "function": {"name": "get_weather"}},
|
||||
{"type": "function", "name": "get_weather"},
|
||||
),
|
||||
({"type": "function"}, "required"),
|
||||
({"type": "function", "name": ""}, "required"),
|
||||
({"type": "auto"}, "auto"),
|
||||
({"type": "none"}, "none"),
|
||||
({"type": "tool"}, "required"),
|
||||
({"type": "any"}, "required"),
|
||||
],
|
||||
)
|
||||
def test_transform_tool_choice_for_response_shapes(self, tool_choice, expected):
|
||||
result = LiteLLMCompletionResponsesConfig._transform_tool_choice_for_response(
|
||||
tool_choice
|
||||
)
|
||||
assert result == expected
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"tool_choice",
|
||||
[
|
||||
"auto",
|
||||
"none",
|
||||
"required",
|
||||
None,
|
||||
{"type": "function", "name": "get_weather"},
|
||||
{"type": "function", "function": {"name": "get_weather"}},
|
||||
{"type": "function"},
|
||||
{"type": "auto"},
|
||||
{"type": "tool"},
|
||||
],
|
||||
)
|
||||
def test_transform_tool_choice_for_response_is_valid_on_response_object(
|
||||
self, tool_choice
|
||||
):
|
||||
"""Every normalized value must construct a ResponsesAPIResponse without raising"""
|
||||
from litellm.types.llms.openai import ResponsesAPIResponse
|
||||
|
||||
result = LiteLLMCompletionResponsesConfig._transform_tool_choice_for_response(
|
||||
tool_choice
|
||||
)
|
||||
ResponsesAPIResponse(id="resp_1", created_at=1, output=[], tool_choice=result)
|
||||
|
||||
def test_forced_named_tool_response_created_event_does_not_raise(self):
|
||||
"""End-to-end guard: building the response.created event for a forced named
|
||||
tool call must produce a ResponsesAPIResponse with the flat function shape"""
|
||||
from litellm.responses.litellm_completion_transformation.streaming_iterator import (
|
||||
LiteLLMCompletionStreamingIterator,
|
||||
)
|
||||
|
||||
iterator = LiteLLMCompletionStreamingIterator.__new__(
|
||||
LiteLLMCompletionStreamingIterator
|
||||
)
|
||||
iterator.model = "claude-3-5-sonnet-latest"
|
||||
iterator._sequence_number = 0
|
||||
iterator._cached_response_id = None
|
||||
iterator.responses_api_request = {
|
||||
"tool_choice": {"type": "function", "name": "get_weather"},
|
||||
"tools": [
|
||||
{
|
||||
"type": "function",
|
||||
"name": "get_weather",
|
||||
"parameters": {"type": "object", "properties": {}},
|
||||
}
|
||||
],
|
||||
}
|
||||
|
||||
event = iterator.create_response_created_event()
|
||||
|
||||
assert event.response.tool_choice == {
|
||||
"type": "function",
|
||||
"name": "get_weather",
|
||||
}
|
||||
|
||||
|
||||
class TestContentTypeTransformation:
|
||||
"""Test content type transformation from Responses API to Chat Completion format"""
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue