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 8dd0dc19b81..84a1f097eba 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,6 +604,7 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): googleMaps: Optional[dict] = None google_maps_retrieval_config: Optional[dict] = None computerUse: Optional[dict] = None + value = deepcopy(value) # remove 'additionalProperties' from tools value = _remove_additional_properties(value) # remove 'strict' from tools diff --git a/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_ai_gemini_transformation.py b/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_ai_gemini_transformation.py index d99c190c6e5..6c9c396259f 100644 --- a/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_ai_gemini_transformation.py +++ b/tests/test_litellm/llms/vertex_ai/gemini/test_vertex_ai_gemini_transformation.py @@ -2097,3 +2097,48 @@ def test_multi_turn_function_calling_roles(): assert ( content["role"] == "user" ), f"Content block {i} with function_response has role='{content['role']}', expected 'user'" + + +def test_map_function_does_not_mutate_caller_tools(): + """Regression test for issue #31343. + + _map_function strips additionalProperties and strict from tool schemas because + Gemini does not accept them. Those fields must be removed from a local copy only; + the original list passed in by the caller must remain unchanged so that a + subsequent OpenAI fallback still receives a valid strict-mode schema. + """ + from litellm.llms.vertex_ai.gemini.vertex_and_google_ai_studio_gemini import ( + VertexGeminiConfig, + ) + + tools = [ + { + "type": "function", + "function": { + "name": "my_tool", + "strict": True, + "parameters": { + "type": "object", + "properties": { + "value": {"type": "string", "description": "Input value."} + }, + "required": ["value"], + "additionalProperties": False, + }, + }, + } + ] + + original_additional_props = tools[0]["function"]["parameters"]["additionalProperties"] + original_strict = tools[0]["function"]["strict"] + + VertexGeminiConfig()._map_function(value=tools, optional_params={}) + + assert tools[0]["function"]["parameters"]["additionalProperties"] == original_additional_props, ( + "_map_function mutated the caller's tools list: additionalProperties was removed. " + "This breaks Gemini-to-OpenAI fallbacks because OpenAI requires additionalProperties=False." + ) + assert tools[0]["function"]["strict"] == original_strict, ( + "_map_function mutated the caller's tools list: strict was removed." + ) +