From 412a283569d575425e5eca3a62eef8dbecdc4b90 Mon Sep 17 00:00:00 2001 From: Sameer Kankute Date: Thu, 12 Mar 2026 18:26:11 +0530 Subject: [PATCH] Revert "fix(vertex): skip harmful schema transforms for Gemini 2.0+ tool parameters" This reverts commit a9c3095cc539c446884649fe6855b44861182c96. --- litellm/llms/vertex_ai/common_utils.py | 22 ----- .../vertex_and_google_ai_studio_gemini.py | 24 ++--- .../vertex_ai/test_vertex_ai_common_utils.py | 91 ------------------- 3 files changed, 6 insertions(+), 131 deletions(-) diff --git a/litellm/llms/vertex_ai/common_utils.py b/litellm/llms/vertex_ai/common_utils.py index ad1e70f2ce..c02d63414c 100644 --- a/litellm/llms/vertex_ai/common_utils.py +++ b/litellm/llms/vertex_ai/common_utils.py @@ -520,28 +520,6 @@ def _build_vertex_schema(parameters: dict, add_property_ordering: bool = False): return parameters -def _build_vertex_schema_for_gemini_2(parameters: dict) -> dict: - """ - Minimal schema builder for Gemini 2.0+ tool parameters. - - Gemini 2.0+ accepts standard JSON Schema natively in tool parameters, - including lowercase types, anyOf with null, and bare {} (TYPE_UNSPECIFIED). - The only transformation needed is resolving $ref/$defs, which Gemini does - NOT support in tool parameters (returns 400). - - This avoids the harmful transforms in _build_vertex_schema that break - JsonValue/Any semantics by coercing {} to {"type": "object"}. - """ - valid_schema_fields = set(get_type_hints(Schema).keys()) - - defs = parameters.pop("$defs", {}) - unpack_defs(parameters, defs) - - parameters = filter_schema_fields(parameters, valid_schema_fields) - - return parameters - - def _build_json_schema(parameters: dict) -> dict: """ Build a JSON Schema for use with Gemini's responseJsonSchema parameter. 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 df7a4a6511..6cd430d6cb 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 @@ -97,7 +97,6 @@ from ..common_utils import ( VertexAIError, _build_json_schema, _build_vertex_schema, - _build_vertex_schema_for_gemini_2, supports_response_json_schema, ) from ..vertex_llm_base import VertexBase @@ -468,7 +467,7 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): return None def _map_function( # noqa: PLR0915 - self, value: List[dict], optional_params: dict, model: str = "" + self, value: List[dict], optional_params: dict ) -> List[Tools]: """ Map OpenAI-style tools/functions to Vertex AI format. @@ -511,21 +510,10 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): "parameters" in _openai_function_object and _openai_function_object["parameters"] is not None and isinstance(_openai_function_object["parameters"], dict) - ): - if supports_response_json_schema(model): - # Gemini 2.0+: minimal transform (resolve $ref only) - _openai_function_object["parameters"] = ( - _build_vertex_schema_for_gemini_2( - _openai_function_object["parameters"] - ) - ) - else: - # Gemini 1.5: full OpenAPI-style transform - _openai_function_object["parameters"] = ( - _build_vertex_schema( - _openai_function_object["parameters"] - ) - ) + ): # OPENAI accepts JSON Schema, Google accepts OpenAPI schema. + _openai_function_object["parameters"] = _build_vertex_schema( + _openai_function_object["parameters"] + ) openai_function_object = _openai_function_object @@ -1063,7 +1051,7 @@ class VertexGeminiConfig(VertexAIBaseConfig, BaseConfig): ): # Pass optional_params so _map_function can add toolConfig if needed mapped_tools = self._map_function( - value=value, optional_params=optional_params, model=model + value=value, optional_params=optional_params ) optional_params = self._add_tools_to_optional_params( optional_params, mapped_tools diff --git a/tests/test_litellm/llms/vertex_ai/test_vertex_ai_common_utils.py b/tests/test_litellm/llms/vertex_ai/test_vertex_ai_common_utils.py index a39c7da2c7..94323e0690 100644 --- a/tests/test_litellm/llms/vertex_ai/test_vertex_ai_common_utils.py +++ b/tests/test_litellm/llms/vertex_ai/test_vertex_ai_common_utils.py @@ -11,7 +11,6 @@ sys.path.insert( ) # Adds the parent directory to the system path from litellm.llms.vertex_ai.common_utils import ( - _build_vertex_schema_for_gemini_2, _get_vertex_url, convert_anyof_null_to_nullable, get_vertex_location_from_url, @@ -1403,93 +1402,3 @@ def test_add_object_type_does_not_add_type_when_anyof_present(): # Verify type was not added (anyOf handles the type) assert "type" not in input_schema, "type should not be added when anyOf is present" - - -class TestBuildVertexSchemaForGemini2: - """Tests for _build_vertex_schema_for_gemini_2 — minimal transform for Gemini 2.0+ tools.""" - - def test_jsonvalue_standalone_preserved(self): - """JsonValue (bare {}) should NOT be coerced to {"type": "object"}.""" - schema = { - "type": "object", - "properties": { - "name": {"type": "string"}, - "value": {}, - }, - "required": ["name", "value"], - } - result = _build_vertex_schema_for_gemini_2(schema) - assert result["properties"]["value"] == {} - - def test_optional_jsonvalue_anyof_preserved(self): - """Optional[JsonValue] anyOf with null should be preserved, not converted to nullable.""" - schema = { - "type": "object", - "properties": { - "name": {"type": "string"}, - "value": { - "anyOf": [ - {"type": "array", "items": {}}, - {}, - {"type": "null"}, - ] - }, - }, - "required": ["name"], - } - result = _build_vertex_schema_for_gemini_2(schema) - value_schema = result["properties"]["value"] - assert "anyOf" in value_schema - assert len(value_schema["anyOf"]) == 3 - assert {"type": "null"} in value_schema["anyOf"] - assert {} in value_schema["anyOf"] - - def test_ref_defs_resolved(self): - """$ref/$defs should be resolved since Gemini doesn't support them in tool params.""" - schema = { - "type": "object", - "properties": { - "value": {"$ref": "#/$defs/JsonValue"}, - }, - "$defs": {"JsonValue": {}}, - } - result = _build_vertex_schema_for_gemini_2(schema) - assert "$ref" not in result["properties"]["value"] - assert "$defs" not in result - assert result["properties"]["value"] == {} - - def test_unsupported_fields_stripped(self): - """Fields not in Vertex Schema TypedDict should be removed.""" - schema = { - "type": "object", - "properties": { - "name": {"type": "string", "additionalProperties": False}, - }, - "additionalProperties": False, - "$schema": "http://json-schema.org/draft-07/schema#", - } - result = _build_vertex_schema_for_gemini_2(schema) - assert "additionalProperties" not in result - assert "$schema" not in result - - def test_no_type_coercion(self): - """Schemas without type should NOT have type: object added.""" - schema = { - "type": "object", - "properties": { - "data": {"description": "Any data"}, - }, - } - result = _build_vertex_schema_for_gemini_2(schema) - assert "type" not in result["properties"]["data"] - - def test_items_empty_preserved(self): - """items: {} should NOT be coerced to items: {"type": "object"}.""" - schema = { - "type": "object", - "properties": { - "values": {"type": "array", "items": {}}, - }, - } - result = _build_vertex_schema_for_gemini_2(schema) - assert result["properties"]["values"]["items"] == {}