Add tool choice mapping for giga chat

This commit is contained in:
Sameer Kankute 2026-01-23 16:29:19 +05:30
parent ebf0beda97
commit acf5ad1155
2 changed files with 192 additions and 7 deletions

View file

@ -158,13 +158,10 @@ class GigaChatConfig(BaseConfig):
# Convert tools to functions format
optional_params["functions"] = self._convert_tools_to_functions(value)
elif param == "tool_choice":
if isinstance(value, dict) and value.get("function"):
optional_params["function_call"] = {"name": value["function"]["name"]}
elif value == "auto":
pass # Default behavior
elif value == "required":
# GigaChat doesn't have 'required', handled differently
pass
# Map OpenAI tool_choice to GigaChat function_call
mapped_choice = self._map_tool_choice(value)
if mapped_choice is not None:
optional_params["function_call"] = mapped_choice
elif param == "functions":
optional_params["functions"] = value
elif param == "function_call":
@ -203,6 +200,48 @@ class GigaChatConfig(BaseConfig):
})
return functions
def _map_tool_choice(
self, tool_choice: Union[str, dict]
) -> Optional[Union[str, dict]]:
"""
Map OpenAI tool_choice to GigaChat function_call format.
OpenAI format:
- "auto": Call zero, one, or multiple functions (default)
- "required": Call one or more functions
- "none": Don't call any functions
- {"type": "function", "function": {"name": "get_weather"}}: Force specific function
GigaChat format:
- "none": Disable function calls
- "auto": Automatic mode (default)
- {"name": "get_weather"}: Force specific function
Args:
tool_choice: OpenAI tool_choice value
Returns:
GigaChat function_call value or None
"""
if tool_choice == "none":
return "none"
elif tool_choice == "auto":
return "auto"
elif tool_choice == "required":
# GigaChat doesn't have a direct "required" equivalent
# Use "auto" as the closest behavior
return "auto"
elif isinstance(tool_choice, dict):
# OpenAI format: {"type": "function", "function": {"name": "func_name"}}
# GigaChat format: {"name": "func_name"}
if tool_choice.get("type") == "function":
func_name = tool_choice.get("function", {}).get("name")
if func_name:
return {"name": func_name}
# Default to None (don't set function_call)
return None
def _upload_image(self, image_url: str) -> Optional[str]:
"""
Upload image to GigaChat and return file_id.

View file

@ -347,3 +347,149 @@ class TestGigaChatSupportedParams:
assert "tools" in supported
assert "response_format" in supported
assert "stream" in supported
class TestGigaChatToolChoiceMapping:
"""Tests for tool_choice -> function_call mapping"""
@pytest.fixture
def config(self):
from litellm.llms.gigachat.chat.transformation import GigaChatConfig
return GigaChatConfig()
def test_tool_choice_none(self, config):
"""tool_choice='none' should map to function_call='none'"""
result = config._map_tool_choice("none")
assert result == "none"
def test_tool_choice_auto(self, config):
"""tool_choice='auto' should map to function_call='auto'"""
result = config._map_tool_choice("auto")
assert result == "auto"
def test_tool_choice_required(self, config):
"""tool_choice='required' should map to function_call='auto' (closest equivalent)"""
result = config._map_tool_choice("required")
assert result == "auto"
def test_tool_choice_forced_function(self, config):
"""tool_choice with forced function should map to function_call with name"""
tool_choice = {
"type": "function",
"function": {"name": "get_weather"}
}
result = config._map_tool_choice(tool_choice)
assert result == {"name": "get_weather"}
def test_tool_choice_forced_function_full(self, config):
"""tool_choice with full function details should extract only name"""
tool_choice = {
"type": "function",
"function": {
"name": "weather_forecast",
"description": "Get weather forecast"
}
}
result = config._map_tool_choice(tool_choice)
assert result == {"name": "weather_forecast"}
def test_tool_choice_invalid_dict(self, config):
"""tool_choice with invalid dict should return None"""
tool_choice = {"type": "tool"} # Missing function
result = config._map_tool_choice(tool_choice)
assert result is None
def test_tool_choice_in_map_openai_params_auto(self, config):
"""tool_choice='auto' should be mapped in map_openai_params"""
params = {"tool_choice": "auto"}
result = config.map_openai_params(
non_default_params=params,
optional_params={},
model="GigaChat",
drop_params=False,
)
assert result["function_call"] == "auto"
def test_tool_choice_in_map_openai_params_none(self, config):
"""tool_choice='none' should be mapped in map_openai_params"""
params = {"tool_choice": "none"}
result = config.map_openai_params(
non_default_params=params,
optional_params={},
model="GigaChat",
drop_params=False,
)
assert result["function_call"] == "none"
def test_tool_choice_in_map_openai_params_required(self, config):
"""tool_choice='required' should be mapped to 'auto' in map_openai_params"""
params = {"tool_choice": "required"}
result = config.map_openai_params(
non_default_params=params,
optional_params={},
model="GigaChat",
drop_params=False,
)
assert result["function_call"] == "auto"
def test_tool_choice_in_map_openai_params_forced(self, config):
"""tool_choice with forced function should be mapped in map_openai_params"""
params = {
"tool_choice": {
"type": "function",
"function": {"name": "weather_forecast"}
}
}
result = config.map_openai_params(
non_default_params=params,
optional_params={},
model="GigaChat",
drop_params=False,
)
assert result["function_call"] == {"name": "weather_forecast"}
def test_tool_choice_with_tools(self, config):
"""tool_choice should work together with tools parameter"""
params = {
"tools": [{
"type": "function",
"function": {
"name": "get_weather",
"description": "Get weather",
"parameters": {"type": "object", "properties": {}}
}
}],
"tool_choice": {
"type": "function",
"function": {"name": "get_weather"}
}
}
result = config.map_openai_params(
non_default_params=params,
optional_params={},
model="GigaChat",
drop_params=False,
)
assert "functions" in result
assert result["function_call"] == {"name": "get_weather"}
def test_transform_request_with_tool_choice(self, config):
"""Full transform_request should include function_call from tool_choice"""
messages = [{"role": "user", "content": "What's the weather?"}]
optional_params = {
"functions": [{
"name": "get_weather",
"description": "Get weather",
"parameters": {"type": "object", "properties": {}}
}],
"function_call": {"name": "get_weather"}
}
result = config.transform_request(
model="gigachat/GigaChat",
messages=messages,
optional_params=optional_params,
litellm_params={},
headers={},
)
assert "function_call" in result
assert result["function_call"] == {"name": "get_weather"}