Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 17 additions & 9 deletions google/genai/_transformers.py
Original file line number Diff line number Diff line change
Expand Up @@ -629,15 +629,23 @@ def handle_null_fields(schema: _common.StringDict) -> None:
schema['nullable'] = True
del schema['type']
elif 'anyOf' in schema:
for item in schema['anyOf']:
if 'type' in item and item['type'] == 'null':
schema['nullable'] = True
schema['anyOf'].remove({'type': 'null'})
if len(schema['anyOf']) == 1:
# If there is only one type left after removing null, remove the anyOf field.
for key, val in schema['anyOf'][0].items():
schema[key] = val
del schema['anyOf']
# Identify null members by their 'type', not by exact dict equality: a null
# member may carry extra keys (e.g. {'type': 'null', 'title': 'N'}), in
# which case `list.remove({'type': 'null'})` raises ValueError. Rebuilding
# the list also avoids mutating it while iterating.
non_null_items = [
item
for item in schema['anyOf']
if not (isinstance(item, dict) and item.get('type') == 'null')
]
if len(non_null_items) != len(schema['anyOf']):
schema['nullable'] = True
schema['anyOf'] = non_null_items
if len(schema['anyOf']) == 1:
# If there is only one type left after removing null, remove the anyOf field.
for key, val in schema['anyOf'][0].items():
schema[key] = val
del schema['anyOf']


def _raise_for_unsupported_schema_type(origin: Any) -> None:
Expand Down
23 changes: 23 additions & 0 deletions google/genai/tests/transformers/test_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,29 @@ def test_schema_with_no_null_fields_is_unchanged():
assert schema_before == schema


def test_handle_null_fields_anyof_null_member_with_extra_keys():
"""A null member of anyOf may carry extra keys (e.g. a 'title').

It must still be detected and removed instead of raising
ValueError: list.remove(x): x not in list. Such schemas are common from
JSON-Schema-based tools (e.g. MCP) rather than plain pydantic.
"""
schema = {
'anyOf': [
{'type': 'integer'},
{'type': 'null', 'title': 'N'},
],
'title': 'Total Area Sq Mi',
}

_transformers.handle_null_fields(schema)

assert schema['nullable'] is True
# Only the integer type remains, so anyOf is flattened away.
assert 'anyOf' not in schema
assert schema['type'] == 'integer'


@pytest.mark.parametrize('use_vertex', [True, False])
def test_schema_with_default_value(client):

Expand Down
Loading