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 c116960a1ff..ded975ac795 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 @@ -604,14 +604,13 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): openai_function_object = _openai_function_object elif "name" in tool: # functions list - _named_function_object = ChatCompletionToolParamFunctionChunk(**tool) - - if _named_function_object.get("parameters") is None: - _input_schema = tool.get("input_schema") - if isinstance(_input_schema, dict): - _named_function_object["parameters"] = _build_vertex_schema(_input_schema) - - openai_function_object = _named_function_object + _input_schema = tool.get("input_schema") if tool.get("parameters") is None else None + _named_tool = ( + {**tool, "parameters": _build_vertex_schema(_input_schema)} + if isinstance(_input_schema, dict) + else tool + ) + openai_function_object = ChatCompletionToolParamFunctionChunk(**_named_tool) if "type" in tool and tool["type"] == "computer_use": computer_use_config = {k: v for k, v in tool.items() if k != "type"} 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 55559b441ba..1ef46cdb86f 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 @@ -1254,14 +1254,32 @@ def test_vertex_ai_map_tool_input_schema_gets_vertex_schema_conversion(): def test_vertex_ai_map_tool_explicit_parameters_wins_over_input_schema(): """A tool carrying both keys must keep `parameters` — `input_schema` is only a fallback.""" - tool = _anthropic_shaped_tool({"type": "object", "properties": {"ignored": {"type": "string"}}}) - tool["parameters"] = {"type": "object", "properties": {"location": {"type": "string"}}} + tool = { + **_anthropic_shaped_tool({"type": "object", "properties": {"ignored": {"type": "string"}}}), + "parameters": {"type": "object", "properties": {"location": {"type": "string"}}}, + } declaration = _declaration_for(tool) assert declaration["parameters"]["properties"] == {"location": {"type": "string"}} +def test_vertex_ai_map_tool_null_parameters_falls_back_to_input_schema(): + """A JSON `"parameters": null` alongside `input_schema` must take the fallback, not + send a null schema upstream.""" + tool = { + **_anthropic_shaped_tool({"type": "object", "properties": {"location": {"type": "string"}}}), + "parameters": None, + } + + declaration = _declaration_for(tool) + + assert declaration["parameters"] == { + "type": "object", + "properties": {"location": {"type": "string"}}, + } + + def test_vertex_ai_map_tool_without_any_schema_is_unchanged(): """A named tool with no schema at all must still map, without inventing parameters.""" declaration = _declaration_for({"name": "ping", "description": "no args"})