Revert "fix(vertex): drop bare {} schemas from anyOf before adding nullable=True (#23060)"

This reverts commit 3ad9a536d3.
This commit is contained in:
Ishaan Jaffer 2026-03-07 17:35:38 -08:00
parent 906288a1b2
commit ee18e39bf0
2 changed files with 7 additions and 57 deletions

View file

@ -712,20 +712,6 @@ def convert_anyof_null_to_nullable(schema, depth=0):
)
if contains_null:
# Drop any-type schemas (bare {}) from anyOf before adding nullable=True.
# Adding nullable=True to {} produces {"nullable": True} with no type field,
# which Gemini rejects as an anyOf entry without a concrete type.
for atype in list(anyof):
if isinstance(atype, dict) and _is_any_type_schema(atype):
anyof.remove(atype)
if len(anyof) == 0:
# All remaining entries were any-type schemas (e.g. anyOf: [{}, null]).
# This means "any nullable value" — collapse anyOf and mark parent nullable.
del schema["anyOf"]
schema["nullable"] = True
return
# set all types to nullable following guidance found here: https://cloud.google.com/vertex-ai/generative-ai/docs/samples/generativeaionvertexai-gemini-controlled-generation-response-schema-3#generativeaionvertexai_gemini_controlled_generation_response_schema_3-python
for atype in anyof:
# Remove items field if type is array and items is empty

View file

@ -1451,15 +1451,11 @@ def test_add_object_type_preserves_any_type_schema():
assert schema["type"] == "object"
def test_convert_anyof_pure_any_type_collapses_to_nullable_parent():
"""
anyOf: [{}, null] means "any nullable value".
The empty schema {} (any-type) inside anyOf cannot receive nullable=True and be
sent to Gemini Gemini rejects {"nullable": True} with no type field as an anyOf
entry. Instead we collapse the anyOf and set nullable=True on the parent.
"""
def test_convert_anyof_preserves_any_type_members():
"""Test convert_anyof_null_to_nullable does NOT coerce empty anyOf members to object."""
from litellm.llms.vertex_ai.common_utils import convert_anyof_null_to_nullable
# anyOf with empty schema and null — empty should be preserved
schema = {
"anyOf": [
{},
@ -1467,42 +1463,10 @@ def test_convert_anyof_pure_any_type_collapses_to_nullable_parent():
]
}
convert_anyof_null_to_nullable(schema)
# anyOf should be removed; parent gets nullable=True to represent "any nullable value"
assert "anyOf" not in schema
assert schema.get("nullable") is True
def test_convert_anyof_drops_empty_schema_when_concrete_type_present():
"""
Regression test for tool schemas with anyOf: [array, {}, null].
The bare {} (any-type) inside anyOf must be dropped Gemini rejects
{"nullable": True} with no type field. The concrete array type should
remain with nullable=True.
Broken by da941e4261c5 (preserve type schema semantics for JsonValue fields).
"""
from litellm.llms.vertex_ai.common_utils import convert_anyof_null_to_nullable
schema = {
"properties": {
"callbacks": {
"anyOf": [
{"items": {}, "type": "array"},
{},
{"type": "null"},
]
}
}
}
convert_anyof_null_to_nullable(schema)
callbacks_anyof = schema["properties"]["callbacks"]["anyOf"]
# {} and {"type": "null"} should both be gone; only the array entry remains
assert len(callbacks_anyof) == 1
assert callbacks_anyof[0].get("type") == "array"
assert callbacks_anyof[0].get("nullable") is True
# empty items should have been stripped too
assert "items" not in callbacks_anyof[0]
# null should be removed, empty schema should be preserved (not coerced to object)
assert len(schema["anyOf"]) == 1
assert "type" not in schema["anyOf"][0] or schema["anyOf"][0].get("type") != "object"
assert schema["anyOf"][0].get("nullable") is True
def test_build_vertex_schema_jsonvalue():