mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-08 03:08:45 +00:00
Clients send tool_choice as {"type": "auto"} (Cursor on chat completions,
Claude Code's Anthropic tool_choice shape). validate_chat_completion_tool_choice
recognized that shape but returned it verbatim, and the chat -> Responses API
bridge only normalized {"type": "function"}, so the wrapper reached OpenAI and
the whole call failed with:
Invalid value: 'auto'. Supported values are: 'code_interpreter', ...,
'web_search_preview', ... (param: tool_choice.type)
That broke every tool call, web search included, on responses-mode models.
Unwrap {"type": "auto"|"none"|"required"} to the bare string at both layers:
the chat completions validation boundary where the shape is first accepted,
and the Responses API bridge that owns the Responses tool_choice contract.
No OpenAI surface accepts the object form for these values, so the previous
passthrough only deferred the 400 to the provider.
68 lines
2.5 KiB
Python
68 lines
2.5 KiB
Python
import pytest
|
|
import sys
|
|
import os
|
|
|
|
sys.path.insert(0, os.path.abspath("../.."))
|
|
|
|
from litellm.utils import validate_chat_completion_tool_choice
|
|
|
|
|
|
def test_validate_tool_choice_none():
|
|
"""Test that None is returned as-is."""
|
|
result = validate_chat_completion_tool_choice(None)
|
|
assert result is None
|
|
|
|
|
|
def test_validate_tool_choice_string():
|
|
"""Test that string values are returned as-is."""
|
|
assert validate_chat_completion_tool_choice("auto") == "auto"
|
|
assert validate_chat_completion_tool_choice("none") == "none"
|
|
assert validate_chat_completion_tool_choice("required") == "required"
|
|
|
|
|
|
def test_validate_tool_choice_standard_dict():
|
|
"""Test standard OpenAI format with function."""
|
|
tool_choice = {"type": "function", "function": {"name": "my_function"}}
|
|
result = validate_chat_completion_tool_choice(tool_choice)
|
|
assert result == tool_choice
|
|
|
|
|
|
def test_validate_tool_choice_cursor_format():
|
|
"""Cursor IDE format {"type": "auto"} must be unwrapped to the bare string.
|
|
|
|
No OpenAI surface accepts the object form of these values. Forwarding it
|
|
verbatim makes the provider reject the call with
|
|
"Invalid value: 'auto' ... param: tool_choice.type".
|
|
"""
|
|
assert validate_chat_completion_tool_choice({"type": "auto"}) == "auto"
|
|
assert validate_chat_completion_tool_choice({"type": "none"}) == "none"
|
|
assert validate_chat_completion_tool_choice({"type": "required"}) == "required"
|
|
|
|
|
|
def test_validate_tool_choice_invalid_dict():
|
|
"""Test that invalid dict formats raise exceptions."""
|
|
# Missing both type and function
|
|
with pytest.raises(Exception) as exc_info:
|
|
validate_chat_completion_tool_choice({})
|
|
assert "Invalid tool choice" in str(exc_info.value)
|
|
|
|
# Invalid type value
|
|
with pytest.raises(Exception) as exc_info:
|
|
validate_chat_completion_tool_choice({"type": "invalid"})
|
|
assert "Invalid tool choice" in str(exc_info.value)
|
|
|
|
# Has type but missing function when type is "function"
|
|
with pytest.raises(Exception) as exc_info:
|
|
validate_chat_completion_tool_choice({"type": "function"})
|
|
assert "Invalid tool choice" in str(exc_info.value)
|
|
|
|
|
|
def test_validate_tool_choice_invalid_type():
|
|
"""Test that invalid types raise exceptions."""
|
|
with pytest.raises(Exception) as exc_info:
|
|
validate_chat_completion_tool_choice(123)
|
|
assert "Got=<class 'int'>" in str(exc_info.value)
|
|
|
|
with pytest.raises(Exception) as exc_info:
|
|
validate_chat_completion_tool_choice([])
|
|
assert "Got=<class 'list'>" in str(exc_info.value)
|