diff --git a/pyproject.toml b/pyproject.toml index ed1b641b..1c37e764 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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. diff --git a/src/google/adk/tools/_gemini_schema_util.py b/src/google/adk/tools/_gemini_schema_util.py index 3d7fbaec..d2ed560e 100644 --- a/src/google/adk/tools/_gemini_schema_util.py +++ b/src/google/adk/tools/_gemini_schema_util.py @@ -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 diff --git a/tests/unittests/tools/test_gemini_schema_util.py b/tests/unittests/tools/test_gemini_schema_util.py index 9bc4d1a2..ffbc80b8 100644 --- a/tests/unittests/tools/test_gemini_schema_util.py +++ b/tests/unittests/tools/test_gemini_schema_util.py @@ -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",