Guard namespace tools without schemas

This commit is contained in:
kigland 2026-06-08 16:11:40 +08:00
parent 1e1f377e3c
commit 816e86b79d
4 changed files with 40 additions and 2 deletions

View file

@ -680,6 +680,13 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig):
f"Gemini: Transforming OpenAI-style '{tool['type']}' tool to googleSearch"
)
tool = {VertexToolName.GOOGLE_SEARCH.value: {}}
elif tool.get("type") == "namespace" and (
not tool.get("description") or not tool.get("parameters")
):
raise ValueError(
"Namespace tools require description and parameters before "
"converting to Gemini function declarations"
)
# Handle tools with 'type' field (OpenAI spec compliance) Ignore this field -> https://github.com/BerriAI/litellm/issues/14644#issuecomment-3342061838
elif "type" in tool:
tool = {k: tool[k] for k in tool if k != "type"}

View file

@ -1388,6 +1388,14 @@ class LiteLLMCompletionResponsesConfig:
)
elif tool.get("type") in ("function", "namespace"):
typed_tool = cast(FunctionToolParam, tool)
if tool.get("type") == "namespace" and (
not typed_tool.get("description")
or not typed_tool.get("parameters")
):
raise ValueError(
"Responses API namespace tools require description and "
"parameters before converting to Chat Completion tools"
)
# Ensure parameters has "type": "object" as required by providers like Anthropic
parameters = dict(typed_tool.get("parameters", {}) or {})
if not parameters or "type" not in parameters:

View file

@ -2917,6 +2917,16 @@ def test_vertex_ai_function_declarations_with_other_tools_separate():
assert func_tool["function_declarations"][0]["name"] == "get_weather"
def test_vertex_ai_namespace_tool_without_schema_raises():
v = VertexGeminiConfig()
with pytest.raises(ValueError, match="Namespace tools require description"):
v._map_function(
value=[{"type": "namespace", "name": "mcp__node_repl"}],
optional_params={},
)
def test_vertex_ai_single_tool_type_still_works():
"""
Test that single tool type usage still works correctly (backward compatibility).

View file

@ -1,6 +1,8 @@
import os
import sys
import pytest
sys.path.insert(
0, os.path.abspath("../../..")
) # Adds the parent directory to the system path
@ -1222,11 +1224,22 @@ class TestToolTransformation:
assert result_tool["type"] == "function"
assert result_tool["function"]["name"] == "mcp__node_repl"
assert (
result_tool["function"]["description"]
== "Run JavaScript in the node REPL"
result_tool["function"]["description"] == "Run JavaScript in the node REPL"
)
assert result_tool["function"]["parameters"] == namespace_tool["parameters"]
def test_transform_namespace_tools_without_schema_raises(self):
"""Name-only namespace tools cannot be converted into usable functions."""
namespace_tool = {
"type": "namespace",
"name": "mcp__node_repl",
}
with pytest.raises(ValueError, match="namespace tools require description"):
LiteLLMCompletionResponsesConfig.transform_responses_api_tools_to_chat_completion_tools(
tools=[namespace_tool]
)
def test_transform_code_execution_tools(self):
"""Test that code_execution tools are passed through as-is"""
code_execution_tool = {