Revert "fix(vertex): skip harmful schema transforms for Gemini 2.0+ tool parameters"

This reverts commit a9c3095cc5.
This commit is contained in:
Sameer Kankute
2026-03-12 18:26:11 +05:30
parent 72c98489d1
commit 412a283569
3 changed files with 6 additions and 131 deletions
-22
View File
@@ -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.
@@ -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
@@ -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"] == {}