diff --git a/homeassistant/components/google_generative_ai_conversation/conversation.py b/homeassistant/components/google_generative_ai_conversation/conversation.py index 2c84249dcb3..168e867d857 100644 --- a/homeassistant/components/google_generative_ai_conversation/conversation.py +++ b/homeassistant/components/google_generative_ai_conversation/conversation.py @@ -64,28 +64,18 @@ async def async_setup_entry( SUPPORTED_SCHEMA_KEYS = { - "min_items", - "example", - "property_ordering", - "pattern", - "minimum", - "default", - "any_of", - "max_length", - "title", - "min_properties", - "min_length", - "max_items", - "maximum", - "nullable", - "max_properties", + # Gemini API does not support all of the OpenAPI schema + # SoT: https://ai.google.dev/api/caching#Schema "type", - "description", - "enum", "format", - "items", + "description", + "nullable", + "enum", + "max_items", + "min_items", "properties", "required", + "items", } @@ -109,9 +99,7 @@ def _format_schema(schema: dict[str, Any]) -> Schema: key = _camel_to_snake(key) if key not in SUPPORTED_SCHEMA_KEYS: continue - if key == "any_of": - val = [_format_schema(subschema) for subschema in val] - elif key == "type": + if key == "type": val = val.upper() elif key == "format": # Gemini API does not support all formats, see: https://ai.google.dev/api/caching#Schema diff --git a/tests/components/google_generative_ai_conversation/snapshots/test_conversation.ambr b/tests/components/google_generative_ai_conversation/snapshots/test_conversation.ambr index 106366fd240..c840f7da324 100644 --- a/tests/components/google_generative_ai_conversation/snapshots/test_conversation.ambr +++ b/tests/components/google_generative_ai_conversation/snapshots/test_conversation.ambr @@ -6,7 +6,7 @@ tuple( ), dict({ - 'config': GenerateContentConfig(http_options=None, system_instruction="Current time is 05:00:00. Today's date is 2024-05-24.\nYou are a voice assistant for Home Assistant.\nAnswer questions about the world truthfully.\nAnswer in plain text. Keep it simple and to the point.\nOnly if the user wants to control a device, tell them to expose entities to their voice assistant in Home Assistant.", temperature=1.0, top_p=0.95, top_k=64.0, candidate_count=None, max_output_tokens=150, stop_sequences=None, response_logprobs=None, logprobs=None, presence_penalty=None, frequency_penalty=None, seed=None, response_mime_type=None, response_schema=None, routing_config=None, safety_settings=[SafetySetting(method=None, category=, threshold=), SafetySetting(method=None, category=, threshold=), SafetySetting(method=None, category=, threshold=), SafetySetting(method=None, category=, threshold=)], tools=[Tool(function_declarations=[FunctionDeclaration(response=None, description='Test function', name='test_tool', parameters=Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=, description=None, enum=None, format=None, items=None, properties={'param1': Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=, description='Test parameters', enum=None, format=None, items=Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=, description=None, enum=None, format=None, items=None, properties=None, required=None), properties=None, required=None), 'param2': Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=[Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=, description=None, enum=None, format=None, items=None, properties=None, required=None), Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=, description=None, enum=None, format=None, items=None, properties=None, required=None)], max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=None, description=None, enum=None, format=None, items=None, properties=None, required=None), 'param3': Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=, description=None, enum=None, format=None, items=None, properties={'json': Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=, description=None, enum=None, format=None, items=None, properties=None, required=None)}, required=[])}, required=[]))], retrieval=None, google_search=None, google_search_retrieval=None, code_execution=None)], tool_config=None, labels=None, cached_content=None, response_modalities=None, media_resolution=None, speech_config=None, audio_timestamp=None, automatic_function_calling=AutomaticFunctionCallingConfig(disable=True, maximum_remote_calls=None, ignore_call_history=None), thinking_config=None), + 'config': GenerateContentConfig(http_options=None, system_instruction="Current time is 05:00:00. Today's date is 2024-05-24.\nYou are a voice assistant for Home Assistant.\nAnswer questions about the world truthfully.\nAnswer in plain text. Keep it simple and to the point.\nOnly if the user wants to control a device, tell them to expose entities to their voice assistant in Home Assistant.", temperature=1.0, top_p=0.95, top_k=64.0, candidate_count=None, max_output_tokens=150, stop_sequences=None, response_logprobs=None, logprobs=None, presence_penalty=None, frequency_penalty=None, seed=None, response_mime_type=None, response_schema=None, routing_config=None, safety_settings=[SafetySetting(method=None, category=, threshold=), SafetySetting(method=None, category=, threshold=), SafetySetting(method=None, category=, threshold=), SafetySetting(method=None, category=, threshold=)], tools=[Tool(function_declarations=[FunctionDeclaration(response=None, description='Test function', name='test_tool', parameters=Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=, description=None, enum=None, format=None, items=None, properties={'param1': Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=, description='Test parameters', enum=None, format=None, items=Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=, description=None, enum=None, format=None, items=None, properties=None, required=None), properties=None, required=None), 'param2': Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=None, description=None, enum=None, format=None, items=None, properties=None, required=None), 'param3': Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=, description=None, enum=None, format=None, items=None, properties={'json': Schema(min_items=None, example=None, property_ordering=None, pattern=None, minimum=None, default=None, any_of=None, max_length=None, title=None, min_length=None, min_properties=None, max_items=None, maximum=None, nullable=None, max_properties=None, type=, description=None, enum=None, format=None, items=None, properties=None, required=None)}, required=[])}, required=[]))], retrieval=None, google_search=None, google_search_retrieval=None, code_execution=None)], tool_config=None, labels=None, cached_content=None, response_modalities=None, media_resolution=None, speech_config=None, audio_timestamp=None, automatic_function_calling=AutomaticFunctionCallingConfig(disable=True, maximum_remote_calls=None, ignore_call_history=None), thinking_config=None), 'history': list([ ]), 'model': 'models/gemini-2.0-flash', diff --git a/tests/components/google_generative_ai_conversation/test_conversation.py b/tests/components/google_generative_ai_conversation/test_conversation.py index 5e887d3cab7..64f71c18bf2 100644 --- a/tests/components/google_generative_ai_conversation/test_conversation.py +++ b/tests/components/google_generative_ai_conversation/test_conversation.py @@ -493,6 +493,26 @@ async def test_escape_decode() -> None: {"type": "string", "enum": ["a", "b", "c"]}, {"type": "STRING", "enum": ["a", "b", "c"]}, ), + ( + {"type": "string", "default": "default"}, + {"type": "STRING"}, + ), + ( + {"type": "string", "pattern": "default"}, + {"type": "STRING"}, + ), + ( + {"type": "string", "maxLength": 10}, + {"type": "STRING"}, + ), + ( + {"type": "string", "minLength": 10}, + {"type": "STRING"}, + ), + ( + {"type": "string", "title": "title"}, + {"type": "STRING"}, + ), ( {"type": "string", "format": "enum", "enum": ["a", "b", "c"]}, {"type": "STRING", "format": "enum", "enum": ["a", "b", "c"]}, @@ -517,6 +537,10 @@ async def test_escape_decode() -> None: {"type": "number", "format": "hex"}, {"type": "NUMBER"}, ), + ( + {"type": "number", "minimum": 1}, + {"type": "NUMBER"}, + ), ( {"type": "integer", "format": "int32"}, {"type": "INTEGER", "format": "int32"}, @@ -535,21 +559,7 @@ async def test_escape_decode() -> None: ), ( {"anyOf": [{"type": "integer"}, {"type": "number"}]}, - {"any_of": [{"type": "INTEGER"}, {"type": "NUMBER"}]}, - ), - ( - { - "any_of": [ - {"any_of": [{"type": "integer"}, {"type": "number"}]}, - {"any_of": [{"type": "integer"}, {"type": "number"}]}, - ] - }, - { - "any_of": [ - {"any_of": [{"type": "INTEGER"}, {"type": "NUMBER"}]}, - {"any_of": [{"type": "INTEGER"}, {"type": "NUMBER"}]}, - ] - }, + {}, ), ({"type": "string", "format": "lower"}, {"type": "STRING"}), ({"type": "boolean", "format": "bool"}, {"type": "BOOLEAN"}), @@ -570,7 +580,15 @@ async def test_escape_decode() -> None: }, ), ( - {"type": "object", "additionalProperties": True}, + {"type": "object", "additionalProperties": True, "minProperties": 1}, + { + "type": "OBJECT", + "properties": {"json": {"type": "STRING"}}, + "required": [], + }, + ), + ( + {"type": "object", "additionalProperties": True, "maxProperties": 1}, { "type": "OBJECT", "properties": {"json": {"type": "STRING"}}, @@ -581,6 +599,20 @@ async def test_escape_decode() -> None: {"type": "array", "items": {"type": "string"}}, {"type": "ARRAY", "items": {"type": "STRING"}}, ), + ( + { + "type": "array", + "items": {"type": "string"}, + "minItems": 1, + "maxItems": 2, + }, + { + "type": "ARRAY", + "items": {"type": "STRING"}, + "min_items": 1, + "max_items": 2, + }, + ), ], ) async def test_format_schema(openapi, genai_schema) -> None: