Skip to content

Commit 11623cb

Browse files
committed
fix: nullable types with gen ai bump
1 parent 5d9a7e7 commit 11623cb

File tree

3 files changed

+30
-74
lines changed

3 files changed

+30
-74
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ dependencies = [
3939
"google-cloud-spanner>=3.56.0, <4.0.0", # For Spanner database
4040
"google-cloud-speech>=2.30.0, <3.0.0", # For Audio Transcription
4141
"google-cloud-storage>=2.18.0, <3.0.0", # For GCS Artifact service
42-
"google-genai>=1.41.0, <2.0.0", # Google GenAI SDK
42+
"google-genai>=1.44.0, <2.0.0", # Google GenAI SDK
4343
"graphviz>=0.20.2, <1.0.0", # Graphviz for graph rendering
4444
"mcp>=1.8.0, <2.0.0;python_version>='3.10'", # For MCP Toolset
4545
"opentelemetry-api>=1.37.0, <=1.37.0", # OpenTelemetry - limit upper version for sdk and api to not risk breaking changes from unstable _logs package.

src/google/adk/tools/_gemini_schema_util.py

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -74,31 +74,6 @@ def _to_snake_case(text: str) -> str:
7474
return text
7575

7676

77-
def _sanitize_schema_type(schema: dict[str, Any]) -> dict[str, Any]:
78-
if ("type" not in schema or not schema["type"]) and schema.keys().isdisjoint(
79-
schema
80-
):
81-
schema["type"] = "object"
82-
if isinstance(schema.get("type"), list):
83-
nullable = False
84-
non_null_type = None
85-
for t in schema["type"]:
86-
if t == "null":
87-
nullable = True
88-
elif not non_null_type:
89-
non_null_type = t
90-
if not non_null_type:
91-
non_null_type = "object"
92-
if nullable:
93-
schema["type"] = [non_null_type, "null"]
94-
else:
95-
schema["type"] = non_null_type
96-
elif schema.get("type") == "null":
97-
schema["type"] = ["object", "null"]
98-
99-
return schema
100-
101-
10277
def _dereference_schema(schema: dict[str, Any]) -> dict[str, Any]:
10378
"""Resolves $ref pointers in a JSON schema."""
10479

@@ -183,7 +158,11 @@ def _sanitize_schema_formats_for_gemini(
183158
elif field_name in supported_fields and field_value is not None:
184159
snake_case_schema[field_name] = field_value
185160

186-
return _sanitize_schema_type(snake_case_schema)
161+
# If the schema is empty, assume it has the type of object
162+
if not snake_case_schema:
163+
snake_case_schema["type"] = "object"
164+
165+
return snake_case_schema
187166

188167

189168
def _to_gemini_schema(openapi_schema: dict[str, Any]) -> Schema:

tests/unittests/tools/test_gemini_schema_util.py

Lines changed: 24 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ def test_to_gemini_schema_array_string_types(self):
6565
"nonnullable_string": {"type": ["string"]},
6666
"nullable_string": {"type": ["string", "null"]},
6767
"nullable_number": {"type": ["null", "integer"]},
68-
"object_nullable": {"type": "null"},
68+
"object_nullable": {"type": ["object", "null"]},
6969
"multi_types_nullable": {"type": ["string", "null", "integer"]},
7070
"empty_default_object": {},
7171
},
@@ -87,7 +87,10 @@ def test_to_gemini_schema_array_string_types(self):
8787
assert gemini_schema.properties["object_nullable"].type == Type.OBJECT
8888
assert gemini_schema.properties["object_nullable"].nullable
8989

90-
assert gemini_schema.properties["multi_types_nullable"].type == Type.STRING
90+
assert gemini_schema.properties["multi_types_nullable"].any_of == [
91+
Schema(type=Type.STRING),
92+
Schema(type=Type.INTEGER),
93+
]
9194
assert gemini_schema.properties["multi_types_nullable"].nullable
9295

9396
assert gemini_schema.properties["empty_default_object"].type == Type.OBJECT
@@ -146,6 +149,25 @@ def test_to_gemini_schema_any_of(self):
146149
assert gemini_schema.any_of[0].type == Type.STRING
147150
assert gemini_schema.any_of[1].type == Type.INTEGER
148151

152+
def test_to_gemini_schema_any_of_nullable(self):
153+
openapi_schema = {
154+
"anyOf": [{"type": "string"}, {"type": "null"}],
155+
}
156+
gemini_schema = _to_gemini_schema(openapi_schema)
157+
assert gemini_schema.type == Type.STRING
158+
assert gemini_schema.nullable == True
159+
160+
def test_to_gemini_schema_any_of_nullable_multiple_types(self):
161+
openapi_schema = {
162+
"anyOf": [{"type": "string"}, {"type": "integer"}, {"type": "null"}],
163+
}
164+
gemini_schema = _to_gemini_schema(openapi_schema)
165+
assert gemini_schema.any_of == [
166+
Schema(type=Type.STRING),
167+
Schema(type=Type.INTEGER),
168+
]
169+
assert gemini_schema.nullable == True
170+
149171
def test_to_gemini_schema_general_list(self):
150172
openapi_schema = {
151173
"type": "array",
@@ -524,51 +546,6 @@ def test_sanitize_schema_formats_for_gemini(self):
524546
"null",
525547
]
526548

527-
def test_sanitize_schema_formats_for_gemini_nullable(self):
528-
openapi_schema = {
529-
"properties": {
530-
"case_id": {
531-
"description": "The ID of the case.",
532-
"title": "Case Id",
533-
"type": "string",
534-
},
535-
"next_page_token": {
536-
"anyOf": [{"type": "string"}, {"type": "null"}],
537-
"default": None,
538-
"description": (
539-
"The nextPageToken to fetch the next page of results."
540-
),
541-
"title": "Next Page Token",
542-
},
543-
},
544-
"required": ["case_id"],
545-
"title": "list_alerts_by_caseArguments",
546-
"type": "object",
547-
}
548-
openapi_schema = _sanitize_schema_formats_for_gemini(openapi_schema)
549-
assert openapi_schema == {
550-
"properties": {
551-
"case_id": {
552-
"description": "The ID of the case.",
553-
"title": "Case Id",
554-
"type": "string",
555-
},
556-
"next_page_token": {
557-
"any_of": [
558-
{"type": "string"},
559-
{"type": ["object", "null"]},
560-
],
561-
"description": (
562-
"The nextPageToken to fetch the next page of results."
563-
),
564-
"title": "Next Page Token",
565-
},
566-
},
567-
"required": ["case_id"],
568-
"title": "list_alerts_by_caseArguments",
569-
"type": "object",
570-
}
571-
572549
def test_to_gemini_schema_properties_is_none(self):
573550
"""Tests schema conversion when 'properties' field is None."""
574551
openapi_schema = {"type": "object", "properties": None}

0 commit comments

Comments
 (0)