fix: nullable types with gen ai bump

Merge https://github.com/google/adk-python/pull/3280

Fixes: #3281
COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/3280 from MarlzRana:marlzrana/fix-nullable-types-w-gen-ai-bump ad28b82cbdbb7980406086787e098bbe6478a354
PiperOrigin-RevId: 824659954
This commit is contained in:
Marlin Ranasinghe
2025-10-27 13:43:02 -07:00
committed by Copybara-Service
parent e194ebb33c
commit ab00c41fc9
3 changed files with 26 additions and 70 deletions
+1 -1
View File
@@ -39,7 +39,7 @@ dependencies = [
"google-cloud-spanner>=3.56.0, <4.0.0", # For Spanner database
"google-cloud-speech>=2.30.0, <3.0.0", # For Audio Transcription
"google-cloud-storage>=2.18.0, <3.0.0", # For GCS Artifact service
"google-genai>=1.41.0, <2.0.0", # Google GenAI SDK
"google-genai>=1.45.0, <2.0.0", # Google GenAI SDK
"graphviz>=0.20.2, <1.0.0", # Graphviz for graph rendering
"mcp>=1.8.0, <2.0.0;python_version>='3.10'", # For MCP Toolset
"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.
+6 -29
View File
@@ -74,33 +74,6 @@ def _to_snake_case(text: str) -> str:
return text
def _sanitize_schema_type(schema: dict[str, Any]) -> dict[str, Any]:
if not schema:
schema["type"] = "object"
if isinstance(schema.get("type"), list):
types_no_null = [t for t in schema["type"] if t != "null"]
nullable = len(types_no_null) != len(schema["type"])
if "array" in types_no_null:
non_null_type = "array"
else:
non_null_type = types_no_null[0] if types_no_null else "object"
if nullable:
schema["type"] = [non_null_type, "null"]
else:
schema["type"] = non_null_type
elif schema.get("type") == "null":
schema["type"] = ["object", "null"]
schema_type = schema.get("type")
is_array = schema_type == "array" or (
isinstance(schema_type, list) and "array" in schema_type
)
if is_array and "items" not in schema:
schema["items"] = {"type": "string"}
return schema
def _dereference_schema(schema: dict[str, Any]) -> dict[str, Any]:
"""Resolves $ref pointers in a JSON schema."""
@@ -185,11 +158,15 @@ def _sanitize_schema_formats_for_gemini(
elif field_name in supported_fields and field_value is not None:
snake_case_schema[field_name] = field_value
return _sanitize_schema_type(snake_case_schema)
# If the schema is empty, assume it has the type of object
if not snake_case_schema:
snake_case_schema["type"] = "object"
return snake_case_schema
def _to_gemini_schema(openapi_schema: dict[str, Any]) -> Schema:
"""Converts an OpenAPI schema dictionary to a Gemini Schema object."""
"""Converts an OpenAPI v3.1. schema dictionary to a Gemini Schema object."""
if openapi_schema is None:
return None
@@ -65,14 +65,10 @@ class TestToGeminiSchema:
"nonnullable_string": {"type": ["string"]},
"nullable_string": {"type": ["string", "null"]},
"nullable_number": {"type": ["null", "integer"]},
"object_nullable": {"type": "null"},
"nullable_object": {"type": ["object", "null"]},
"multi_types_nullable": {"type": ["string", "null", "integer"]},
"only_null": {"type": "null"},
"empty_default_object": {},
"empty_list_type": {"type": []},
"multi_type_with_array_nullable": {
"type": ["string", "array", "null"]
},
"multi_type_with_array_nonnullable": {"type": ["integer", "array"]},
},
}
gemini_schema = _to_gemini_schema(openapi_schema)
@@ -89,32 +85,21 @@ class TestToGeminiSchema:
assert gemini_schema.properties["nullable_number"].type == Type.INTEGER
assert gemini_schema.properties["nullable_number"].nullable
assert gemini_schema.properties["object_nullable"].type == Type.OBJECT
assert gemini_schema.properties["object_nullable"].nullable
assert gemini_schema.properties["nullable_object"].type == Type.OBJECT
assert gemini_schema.properties["nullable_object"].nullable
assert gemini_schema.properties["multi_types_nullable"].type == Type.STRING
assert gemini_schema.properties["multi_types_nullable"].any_of == [
Schema(type=Type.STRING),
Schema(type=Type.INTEGER),
]
assert gemini_schema.properties["multi_types_nullable"].nullable
assert gemini_schema.properties["only_null"].type is None
assert gemini_schema.properties["only_null"].nullable
assert gemini_schema.properties["empty_default_object"].type == Type.OBJECT
assert gemini_schema.properties["empty_default_object"].nullable is None
assert gemini_schema.properties["empty_list_type"].type == Type.OBJECT
assert not gemini_schema.properties["empty_list_type"].nullable
assert (
gemini_schema.properties["multi_type_with_array_nullable"].type
== Type.ARRAY
)
assert gemini_schema.properties["multi_type_with_array_nullable"].nullable
assert (
gemini_schema.properties["multi_type_with_array_nonnullable"].type
== Type.ARRAY
)
assert not gemini_schema.properties[
"multi_type_with_array_nonnullable"
].nullable
def test_to_gemini_schema_nested_objects(self):
openapi_schema = {
"type": "object",
@@ -159,20 +144,6 @@ class TestToGeminiSchema:
gemini_schema = _to_gemini_schema(openapi_schema)
assert gemini_schema.items.properties["name"].type == Type.STRING
def test_to_gemini_schema_array_without_items_gets_default(self):
openapi_schema = {"type": "array"}
gemini_schema = _to_gemini_schema(openapi_schema)
assert gemini_schema.type == Type.ARRAY
assert not gemini_schema.nullable
assert gemini_schema.items.type == Type.STRING
def test_to_gemini_schema_nullable_array_without_items_gets_default(self):
openapi_schema = {"type": ["array", "null"]}
gemini_schema = _to_gemini_schema(openapi_schema)
assert gemini_schema.type == Type.ARRAY
assert gemini_schema.nullable
assert gemini_schema.items.type == Type.STRING
def test_to_gemini_schema_any_of(self):
openapi_schema = {
"anyOf": [{"type": "string"}, {"type": "integer"}],
@@ -182,6 +153,14 @@ class TestToGeminiSchema:
assert gemini_schema.any_of[0].type == Type.STRING
assert gemini_schema.any_of[1].type == Type.INTEGER
def test_to_gemini_schema_any_of_nullable(self):
openapi_schema = {
"anyOf": [{"type": "string"}, {"type": "null"}],
}
gemini_schema = _to_gemini_schema(openapi_schema)
assert gemini_schema.type == Type.STRING
assert gemini_schema.nullable
def test_to_gemini_schema_general_list(self):
openapi_schema = {
"type": "array",