diff --git a/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py b/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py index 5cd02293f14..51362106000 100644 --- a/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py +++ b/litellm/llms/vertex_ai/gemini/vertex_and_google_ai_studio_gemini.py @@ -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"} diff --git a/litellm/responses/litellm_completion_transformation/transformation.py b/litellm/responses/litellm_completion_transformation/transformation.py index 697b27a4ece..e2afe3cb8ca 100644 --- a/litellm/responses/litellm_completion_transformation/transformation.py +++ b/litellm/responses/litellm_completion_transformation/transformation.py @@ -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: diff --git a/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py b/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py index 0d02521433a..c99b52805fb 100644 --- a/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py +++ b/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_and_google_ai_studio_gemini.py @@ -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). diff --git a/tests/test_litellm/responses/litellm_completion_transformation/test_litellm_completion_responses.py b/tests/test_litellm/responses/litellm_completion_transformation/test_litellm_completion_responses.py index 8fcc1e64220..de2df14367b 100644 --- a/tests/test_litellm/responses/litellm_completion_transformation/test_litellm_completion_responses.py +++ b/tests/test_litellm/responses/litellm_completion_transformation/test_litellm_completion_responses.py @@ -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 = {