[Bugfix] Fix tool parsing crash with non-function tool types (e.g. WebSearchTool) (#45147)

Signed-off-by: Ben Browning <bbrownin@redhat.com>
This commit is contained in:
Ben Browning
2026-06-10 17:17:16 +00:00
committed by GitHub
parent 29026682cb
commit d1bcb4b44c
2 changed files with 48 additions and 3 deletions
+39 -1
View File
@@ -5,13 +5,17 @@ from copy import deepcopy
import pytest
import regex as re
from openai.types.responses import FunctionTool, WebSearchTool
from pydantic import TypeAdapter
from vllm.entrypoints.openai.chat_completion.protocol import (
ChatCompletionToolsParam,
)
from vllm.tool_parsers.streaming import extract_required_tool_call_streaming
from vllm.tool_parsers.utils import get_json_schema_from_tools
from vllm.tool_parsers.utils import (
find_tool_properties,
get_json_schema_from_tools,
)
pytestmark = pytest.mark.cpu_test
@@ -354,3 +358,37 @@ def test_streaming_output_valid_with_trailing_extra_data():
previous_text = current_text
assert len(messages) > 0
FUNCTION_TOOL = FunctionTool(
type="function",
name="get_weather",
parameters={
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"],
},
)
WEB_SEARCH_TOOL = WebSearchTool(type="web_search")
class TestNonFunctionToolsSkipped:
"""Non-function tools (web_search, etc.) must be silently skipped
by the tool-schema utilities instead of raising TypeError."""
def test_find_tool_properties_skips_web_search(self):
tools = [WEB_SEARCH_TOOL, FUNCTION_TOOL]
props = find_tool_properties(tools, "get_weather")
assert props == {"city": {"type": "string"}}
def test_find_tool_properties_only_non_function_tools(self):
props = find_tool_properties([WEB_SEARCH_TOOL], "get_weather")
assert props == {}
def test_get_json_schema_with_mixed_tools(self):
tools = [WEB_SEARCH_TOOL, FUNCTION_TOOL]
schema = get_json_schema_from_tools(tools=tools, tool_choice="required")
assert isinstance(schema, dict)
any_of = schema["items"]["anyOf"]
assert len(any_of) == 1
assert any_of[0]["properties"]["name"]["enum"] == ["get_weather"]
+9 -2
View File
@@ -151,6 +151,10 @@ def consume_space(i: int, s: str) -> int:
return i
def _is_function_tool(tool: Tool) -> bool:
return isinstance(tool, (FunctionTool, ChatCompletionToolsParam))
def _extract_tool_info(
tool: Tool,
) -> tuple[str, dict[str, Any] | None]:
@@ -170,6 +174,8 @@ def find_tool_properties(
if not tools:
return {}
for tool in tools:
if not _is_function_tool(tool):
continue
name, params = _extract_tool_info(tool)
if name == tool_name:
return (params or {}).get("properties", {})
@@ -210,15 +216,16 @@ def _get_tool_schema_defs(
def _get_json_schema_from_tools(
tools: list[Tool],
) -> dict:
fn_tools = [t for t in tools if _is_function_tool(t)]
json_schema = {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"anyOf": [_get_tool_schema_from_tool(tool) for tool in tools],
"anyOf": [_get_tool_schema_from_tool(tool) for tool in fn_tools],
},
}
json_schema_defs = _get_tool_schema_defs(tools)
json_schema_defs = _get_tool_schema_defs(fn_tools)
if json_schema_defs:
json_schema["$defs"] = json_schema_defs
return json_schema