diff --git a/litellm/llms/hosted_vllm/chat/transformation.py b/litellm/llms/hosted_vllm/chat/transformation.py index 29dc485732f..f7a863416ed 100644 --- a/litellm/llms/hosted_vllm/chat/transformation.py +++ b/litellm/llms/hosted_vllm/chat/transformation.py @@ -28,10 +28,11 @@ from ...openai.chat.gpt_transformation import OpenAIGPTConfig class HostedVLLMChatConfig(OpenAIGPTConfig): - def _convert_custom_tools_to_function_tools(self, tools: list[dict[str, Any]]) -> list[dict[str, Any]]: + def _convert_tools_to_function_tools(self, tools: list[dict[str, Any]]) -> list[dict[str, Any]]: """ vLLM chat completions currently accepts only OpenAI function tools. - Convert custom tools into function tools so request validation does not fail. + Convert custom tools and flattened function tools so request validation + does not fail. """ converted_tools: Final[list[dict[str, Any]]] = [] for idx, tool in enumerate(tools): @@ -39,18 +40,22 @@ class HostedVLLMChatConfig(OpenAIGPTConfig): converted_tools.append(tool) continue - if tool.get("type") != "custom": + if tool.get("type") == "custom": + source_tool = tool.get("custom", {}) + if not isinstance(source_tool, dict): + source_tool = {} + tool_name = source_tool.get("name") or tool.get("name") or f"custom_tool_{idx}" + tool_description = source_tool.get("description") or tool.get("description") + tool_parameters = source_tool.get("input_schema") or tool.get("input_schema") + elif tool.get("type") == "function" and not isinstance(tool.get("function"), dict): + source_tool = tool + tool_name = source_tool.get("name") or f"function_tool_{idx}" + tool_description = source_tool.get("description") + tool_parameters = source_tool.get("parameters") + else: converted_tools.append(tool) continue - custom_tool = tool.get("custom", {}) - if not isinstance(custom_tool, dict): - custom_tool = {} - - tool_name = custom_tool.get("name") or tool.get("name") or f"custom_tool_{idx}" - tool_description = custom_tool.get("description") or tool.get("description") - tool_parameters = custom_tool.get("input_schema") or tool.get("input_schema") - if not isinstance(tool_parameters, dict): tool_parameters = { "type": "object", @@ -94,7 +99,7 @@ class HostedVLLMChatConfig(OpenAIGPTConfig): _tools = _remove_additional_properties(_tools) _tools = _remove_strict_from_schema(_tools) if isinstance(_tools, list): - _tools = self._convert_custom_tools_to_function_tools(_tools) + _tools = self._convert_tools_to_function_tools(_tools) if _tools is not None: non_default_params["tools"] = _tools diff --git a/tests/test_litellm/llms/hosted_vllm/chat/test_hosted_vllm_chat_transformation.py b/tests/test_litellm/llms/hosted_vllm/chat/test_hosted_vllm_chat_transformation.py index 82b05601a85..3c077983175 100644 --- a/tests/test_litellm/llms/hosted_vllm/chat/test_hosted_vllm_chat_transformation.py +++ b/tests/test_litellm/llms/hosted_vllm/chat/test_hosted_vllm_chat_transformation.py @@ -1,7 +1,6 @@ import json from unittest.mock import MagicMock, patch - from litellm.constants import ( DEFAULT_REASONING_EFFORT_HIGH_THINKING_BUDGET, DEFAULT_REASONING_EFFORT_LOW_THINKING_BUDGET, @@ -433,3 +432,39 @@ def test_hosted_vllm_custom_tools_use_top_level_input_schema(): assert tools[0]["function"]["name"] == "search" assert tools[0]["function"]["description"] == "Search docs" assert tools[0]["function"]["parameters"] == input_schema + + +def test_hosted_vllm_flattens_function_tools_into_nested_function_shape(): + config = HostedVLLMChatConfig() + input_schema = { + "type": "object", + "properties": {"site_id": {"type": "string"}}, + "required": ["site_id"], + } + + optional_params = config.map_openai_params( + non_default_params={ + "tools": [ + { + "type": "function", + "name": "matomo-matomo_site_list", + "description": "Get Matomo site details", + "parameters": input_schema, + } + ] + }, + optional_params={}, + model="hosted_vllm/gpt-oss-120b", + drop_params=False, + ) + + tools = optional_params["tools"] + assert len(tools) == 1 + assert tools[0] == { + "type": "function", + "function": { + "name": "matomo-matomo_site_list", + "description": "Get Matomo site details", + "parameters": input_schema, + }, + }