This commit is contained in:
whn 2026-09-16 14:05:41 +08:00 committed by GitHub
commit e62d011f7d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 53 additions and 13 deletions

View file

@ -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

View file

@ -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,
},
}