Intents package combines sentences/responses per language (#109079)

pull/109123/head
Michael Hansen 2024-01-30 05:38:29 -06:00 committed by GitHub
parent a1f36c25d4
commit 9752e70675
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
10 changed files with 107 additions and 119 deletions

View File

@ -6,7 +6,6 @@ from collections import defaultdict
from collections.abc import Awaitable, Callable, Iterable from collections.abc import Awaitable, Callable, Iterable
from dataclasses import dataclass from dataclasses import dataclass
import functools import functools
import itertools
import logging import logging
from pathlib import Path from pathlib import Path
import re import re
@ -28,7 +27,7 @@ from hassil.recognize import (
recognize_all, recognize_all,
) )
from hassil.util import merge_dict from hassil.util import merge_dict
from home_assistant_intents import get_domains_and_languages, get_intents from home_assistant_intents import get_intents, get_languages
import yaml import yaml
from homeassistant import core, setup from homeassistant import core, setup
@ -156,7 +155,7 @@ class DefaultAgent(AbstractConversationAgent):
@property @property
def supported_languages(self) -> list[str]: def supported_languages(self) -> list[str]:
"""Return a list of supported languages.""" """Return a list of supported languages."""
return get_domains_and_languages()["homeassistant"] return get_languages()
async def async_initialize(self, config_intents: dict[str, Any] | None) -> None: async def async_initialize(self, config_intents: dict[str, Any] | None) -> None:
"""Initialize the default agent.""" """Initialize the default agent."""
@ -387,6 +386,7 @@ class DefaultAgent(AbstractConversationAgent):
return maybe_result return maybe_result
# Try again with missing entities enabled # Try again with missing entities enabled
best_num_unmatched_entities = 0
for result in recognize_all( for result in recognize_all(
user_input.text, user_input.text,
lang_intents.intents, lang_intents.intents,
@ -394,20 +394,28 @@ class DefaultAgent(AbstractConversationAgent):
intent_context=intent_context, intent_context=intent_context,
allow_unmatched_entities=True, allow_unmatched_entities=True,
): ):
# Remove missing entities that couldn't be filled from context if result.text_chunks_matched < 1:
for entity_key, entity in list(result.unmatched_entities.items()): # Skip results that don't match any literal text
if isinstance(entity, UnmatchedTextEntity) and ( continue
entity.text == MISSING_ENTITY
): # Don't count missing entities that couldn't be filled from context
result.unmatched_entities.pop(entity_key) num_unmatched_entities = 0
for entity in result.unmatched_entities_list:
if isinstance(entity, UnmatchedTextEntity):
if entity.text != MISSING_ENTITY:
num_unmatched_entities += 1
else:
num_unmatched_entities += 1
if maybe_result is None: if maybe_result is None:
# First result # First result
maybe_result = result maybe_result = result
elif len(result.unmatched_entities) < len(maybe_result.unmatched_entities): best_num_unmatched_entities = num_unmatched_entities
elif num_unmatched_entities < best_num_unmatched_entities:
# Fewer unmatched entities # Fewer unmatched entities
maybe_result = result maybe_result = result
elif len(result.unmatched_entities) == len(maybe_result.unmatched_entities): best_num_unmatched_entities = num_unmatched_entities
elif num_unmatched_entities == best_num_unmatched_entities:
if (result.text_chunks_matched > maybe_result.text_chunks_matched) or ( if (result.text_chunks_matched > maybe_result.text_chunks_matched) or (
(result.text_chunks_matched == maybe_result.text_chunks_matched) (result.text_chunks_matched == maybe_result.text_chunks_matched)
and ("name" in result.unmatched_entities) # prefer entities and ("name" in result.unmatched_entities) # prefer entities
@ -536,14 +544,12 @@ class DefaultAgent(AbstractConversationAgent):
intents_dict = lang_intents.intents_dict intents_dict = lang_intents.intents_dict
language_variant = lang_intents.language_variant language_variant = lang_intents.language_variant
domains_langs = get_domains_and_languages() supported_langs = set(get_languages())
if not language_variant: if not language_variant:
# Choose a language variant upfront and commit to it for custom # Choose a language variant upfront and commit to it for custom
# sentences, etc. # sentences, etc.
all_language_variants = { all_language_variants = {lang.lower(): lang for lang in supported_langs}
lang.lower(): lang for lang in itertools.chain(*domains_langs.values())
}
# en-US, en_US, en, ... # en-US, en_US, en, ...
for maybe_variant in _get_language_variations(language): for maybe_variant in _get_language_variations(language):
@ -558,23 +564,17 @@ class DefaultAgent(AbstractConversationAgent):
) )
return None return None
# Load intents for all domains supported by this language variant # Load intents for this language variant
for domain in domains_langs: lang_variant_intents = get_intents(language_variant, json_load=json_load)
domain_intents = get_intents(
domain, language_variant, json_load=json_load
)
if not domain_intents:
continue
if lang_variant_intents:
# Merge sentences into existing dictionary # Merge sentences into existing dictionary
merge_dict(intents_dict, domain_intents) merge_dict(intents_dict, lang_variant_intents)
# Will need to recreate graph # Will need to recreate graph
intents_changed = True intents_changed = True
_LOGGER.debug( _LOGGER.debug(
"Loaded intents domain=%s, language=%s (%s)", "Loaded intents language=%s (%s)",
domain,
language, language,
language_variant, language_variant,
) )

View File

@ -7,5 +7,5 @@
"integration_type": "system", "integration_type": "system",
"iot_class": "local_push", "iot_class": "local_push",
"quality_scale": "internal", "quality_scale": "internal",
"requirements": ["hassil==1.6.0", "home-assistant-intents==2024.1.2"] "requirements": ["hassil==1.6.0", "home-assistant-intents==2024.1.29"]
} }

View File

@ -29,7 +29,7 @@ hass-nabucasa==0.75.1
hassil==1.6.0 hassil==1.6.0
home-assistant-bluetooth==1.12.0 home-assistant-bluetooth==1.12.0
home-assistant-frontend==20240112.0 home-assistant-frontend==20240112.0
home-assistant-intents==2024.1.2 home-assistant-intents==2024.1.29
httpx==0.26.0 httpx==0.26.0
ifaddr==0.2.0 ifaddr==0.2.0
janus==1.0.0 janus==1.0.0

View File

@ -1056,7 +1056,7 @@ holidays==0.41
home-assistant-frontend==20240112.0 home-assistant-frontend==20240112.0
# homeassistant.components.conversation # homeassistant.components.conversation
home-assistant-intents==2024.1.2 home-assistant-intents==2024.1.29
# homeassistant.components.home_connect # homeassistant.components.home_connect
homeconnect==0.7.2 homeconnect==0.7.2

View File

@ -849,7 +849,7 @@ holidays==0.41
home-assistant-frontend==20240112.0 home-assistant-frontend==20240112.0
# homeassistant.components.conversation # homeassistant.components.conversation
home-assistant-intents==2024.1.2 home-assistant-intents==2024.1.29
# homeassistant.components.home_connect # homeassistant.components.home_connect
homeconnect==0.7.2 homeconnect==0.7.2

View File

@ -48,14 +48,14 @@
'card': dict({ 'card': dict({
}), }),
'data': dict({ 'data': dict({
'code': 'no_valid_targets', 'code': 'no_intent_match',
}), }),
'language': 'en', 'language': 'en',
'response_type': 'error', 'response_type': 'error',
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named test transcript', 'speech': "Sorry, I couldn't understand that",
}), }),
}), }),
}), }),
@ -67,7 +67,7 @@
'data': dict({ 'data': dict({
'engine': 'test', 'engine': 'test',
'language': 'en-US', 'language': 'en-US',
'tts_input': 'No device or entity named test transcript', 'tts_input': "Sorry, I couldn't understand that",
'voice': 'james_earl_jones', 'voice': 'james_earl_jones',
}), }),
'type': <PipelineEventType.TTS_START: 'tts-start'>, 'type': <PipelineEventType.TTS_START: 'tts-start'>,
@ -75,9 +75,9 @@
dict({ dict({
'data': dict({ 'data': dict({
'tts_output': dict({ 'tts_output': dict({
'media_id': 'media-source://tts/test?message=No+device+or+entity+named+test+transcript&language=en-US&voice=james_earl_jones', 'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&voice=james_earl_jones",
'mime_type': 'audio/mpeg', 'mime_type': 'audio/mpeg',
'url': '/api/tts_proxy/e5e8e318b536f0a5455f993243a34521e7ad4d6d_en-us_031e2ec052_test.mp3', 'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_031e2ec052_test.mp3',
}), }),
}), }),
'type': <PipelineEventType.TTS_END: 'tts-end'>, 'type': <PipelineEventType.TTS_END: 'tts-end'>,
@ -137,14 +137,14 @@
'card': dict({ 'card': dict({
}), }),
'data': dict({ 'data': dict({
'code': 'no_valid_targets', 'code': 'no_intent_match',
}), }),
'language': 'en-US', 'language': 'en-US',
'response_type': 'error', 'response_type': 'error',
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named test transcript', 'speech': "Sorry, I couldn't understand that",
}), }),
}), }),
}), }),
@ -156,7 +156,7 @@
'data': dict({ 'data': dict({
'engine': 'test', 'engine': 'test',
'language': 'en-US', 'language': 'en-US',
'tts_input': 'No device or entity named test transcript', 'tts_input': "Sorry, I couldn't understand that",
'voice': 'Arnold Schwarzenegger', 'voice': 'Arnold Schwarzenegger',
}), }),
'type': <PipelineEventType.TTS_START: 'tts-start'>, 'type': <PipelineEventType.TTS_START: 'tts-start'>,
@ -164,9 +164,9 @@
dict({ dict({
'data': dict({ 'data': dict({
'tts_output': dict({ 'tts_output': dict({
'media_id': 'media-source://tts/test?message=No+device+or+entity+named+test+transcript&language=en-US&voice=Arnold+Schwarzenegger', 'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&voice=Arnold+Schwarzenegger",
'mime_type': 'audio/mpeg', 'mime_type': 'audio/mpeg',
'url': '/api/tts_proxy/e5e8e318b536f0a5455f993243a34521e7ad4d6d_en-us_2657c1a8ee_test.mp3', 'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_2657c1a8ee_test.mp3',
}), }),
}), }),
'type': <PipelineEventType.TTS_END: 'tts-end'>, 'type': <PipelineEventType.TTS_END: 'tts-end'>,
@ -226,14 +226,14 @@
'card': dict({ 'card': dict({
}), }),
'data': dict({ 'data': dict({
'code': 'no_valid_targets', 'code': 'no_intent_match',
}), }),
'language': 'en-US', 'language': 'en-US',
'response_type': 'error', 'response_type': 'error',
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named test transcript', 'speech': "Sorry, I couldn't understand that",
}), }),
}), }),
}), }),
@ -245,7 +245,7 @@
'data': dict({ 'data': dict({
'engine': 'test', 'engine': 'test',
'language': 'en-US', 'language': 'en-US',
'tts_input': 'No device or entity named test transcript', 'tts_input': "Sorry, I couldn't understand that",
'voice': 'Arnold Schwarzenegger', 'voice': 'Arnold Schwarzenegger',
}), }),
'type': <PipelineEventType.TTS_START: 'tts-start'>, 'type': <PipelineEventType.TTS_START: 'tts-start'>,
@ -253,9 +253,9 @@
dict({ dict({
'data': dict({ 'data': dict({
'tts_output': dict({ 'tts_output': dict({
'media_id': 'media-source://tts/test?message=No+device+or+entity+named+test+transcript&language=en-US&voice=Arnold+Schwarzenegger', 'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&voice=Arnold+Schwarzenegger",
'mime_type': 'audio/mpeg', 'mime_type': 'audio/mpeg',
'url': '/api/tts_proxy/e5e8e318b536f0a5455f993243a34521e7ad4d6d_en-us_2657c1a8ee_test.mp3', 'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_2657c1a8ee_test.mp3',
}), }),
}), }),
'type': <PipelineEventType.TTS_END: 'tts-end'>, 'type': <PipelineEventType.TTS_END: 'tts-end'>,
@ -338,14 +338,14 @@
'card': dict({ 'card': dict({
}), }),
'data': dict({ 'data': dict({
'code': 'no_valid_targets', 'code': 'no_intent_match',
}), }),
'language': 'en', 'language': 'en',
'response_type': 'error', 'response_type': 'error',
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named test transcript', 'speech': "Sorry, I couldn't understand that",
}), }),
}), }),
}), }),
@ -357,7 +357,7 @@
'data': dict({ 'data': dict({
'engine': 'test', 'engine': 'test',
'language': 'en-US', 'language': 'en-US',
'tts_input': 'No device or entity named test transcript', 'tts_input': "Sorry, I couldn't understand that",
'voice': 'james_earl_jones', 'voice': 'james_earl_jones',
}), }),
'type': <PipelineEventType.TTS_START: 'tts-start'>, 'type': <PipelineEventType.TTS_START: 'tts-start'>,
@ -365,9 +365,9 @@
dict({ dict({
'data': dict({ 'data': dict({
'tts_output': dict({ 'tts_output': dict({
'media_id': 'media-source://tts/test?message=No+device+or+entity+named+test+transcript&language=en-US&voice=james_earl_jones', 'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&voice=james_earl_jones",
'mime_type': 'audio/mpeg', 'mime_type': 'audio/mpeg',
'url': '/api/tts_proxy/e5e8e318b536f0a5455f993243a34521e7ad4d6d_en-us_031e2ec052_test.mp3', 'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_031e2ec052_test.mp3',
}), }),
}), }),
'type': <PipelineEventType.TTS_END: 'tts-end'>, 'type': <PipelineEventType.TTS_END: 'tts-end'>,

View File

@ -46,14 +46,14 @@
'card': dict({ 'card': dict({
}), }),
'data': dict({ 'data': dict({
'code': 'no_valid_targets', 'code': 'no_intent_match',
}), }),
'language': 'en', 'language': 'en',
'response_type': 'error', 'response_type': 'error',
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named test transcript', 'speech': "Sorry, I couldn't understand that",
}), }),
}), }),
}), }),
@ -64,16 +64,16 @@
dict({ dict({
'engine': 'test', 'engine': 'test',
'language': 'en-US', 'language': 'en-US',
'tts_input': 'No device or entity named test transcript', 'tts_input': "Sorry, I couldn't understand that",
'voice': 'james_earl_jones', 'voice': 'james_earl_jones',
}) })
# --- # ---
# name: test_audio_pipeline.6 # name: test_audio_pipeline.6
dict({ dict({
'tts_output': dict({ 'tts_output': dict({
'media_id': 'media-source://tts/test?message=No+device+or+entity+named+test+transcript&language=en-US&voice=james_earl_jones', 'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&voice=james_earl_jones",
'mime_type': 'audio/mpeg', 'mime_type': 'audio/mpeg',
'url': '/api/tts_proxy/e5e8e318b536f0a5455f993243a34521e7ad4d6d_en-us_031e2ec052_test.mp3', 'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_031e2ec052_test.mp3',
}), }),
}) })
# --- # ---
@ -127,14 +127,14 @@
'card': dict({ 'card': dict({
}), }),
'data': dict({ 'data': dict({
'code': 'no_valid_targets', 'code': 'no_intent_match',
}), }),
'language': 'en', 'language': 'en',
'response_type': 'error', 'response_type': 'error',
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named test transcript', 'speech': "Sorry, I couldn't understand that",
}), }),
}), }),
}), }),
@ -145,16 +145,16 @@
dict({ dict({
'engine': 'test', 'engine': 'test',
'language': 'en-US', 'language': 'en-US',
'tts_input': 'No device or entity named test transcript', 'tts_input': "Sorry, I couldn't understand that",
'voice': 'james_earl_jones', 'voice': 'james_earl_jones',
}) })
# --- # ---
# name: test_audio_pipeline_debug.6 # name: test_audio_pipeline_debug.6
dict({ dict({
'tts_output': dict({ 'tts_output': dict({
'media_id': 'media-source://tts/test?message=No+device+or+entity+named+test+transcript&language=en-US&voice=james_earl_jones', 'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&voice=james_earl_jones",
'mime_type': 'audio/mpeg', 'mime_type': 'audio/mpeg',
'url': '/api/tts_proxy/e5e8e318b536f0a5455f993243a34521e7ad4d6d_en-us_031e2ec052_test.mp3', 'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_031e2ec052_test.mp3',
}), }),
}) })
# --- # ---
@ -220,14 +220,14 @@
'card': dict({ 'card': dict({
}), }),
'data': dict({ 'data': dict({
'code': 'no_valid_targets', 'code': 'no_intent_match',
}), }),
'language': 'en', 'language': 'en',
'response_type': 'error', 'response_type': 'error',
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named test transcript', 'speech': "Sorry, I couldn't understand that",
}), }),
}), }),
}), }),
@ -238,16 +238,16 @@
dict({ dict({
'engine': 'test', 'engine': 'test',
'language': 'en-US', 'language': 'en-US',
'tts_input': 'No device or entity named test transcript', 'tts_input': "Sorry, I couldn't understand that",
'voice': 'james_earl_jones', 'voice': 'james_earl_jones',
}) })
# --- # ---
# name: test_audio_pipeline_with_enhancements.6 # name: test_audio_pipeline_with_enhancements.6
dict({ dict({
'tts_output': dict({ 'tts_output': dict({
'media_id': 'media-source://tts/test?message=No+device+or+entity+named+test+transcript&language=en-US&voice=james_earl_jones', 'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&voice=james_earl_jones",
'mime_type': 'audio/mpeg', 'mime_type': 'audio/mpeg',
'url': '/api/tts_proxy/e5e8e318b536f0a5455f993243a34521e7ad4d6d_en-us_031e2ec052_test.mp3', 'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_031e2ec052_test.mp3',
}), }),
}) })
# --- # ---
@ -421,14 +421,14 @@
'card': dict({ 'card': dict({
}), }),
'data': dict({ 'data': dict({
'code': 'no_valid_targets', 'code': 'no_intent_match',
}), }),
'language': 'en', 'language': 'en',
'response_type': 'error', 'response_type': 'error',
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named test transcript', 'speech': "Sorry, I couldn't understand that",
}), }),
}), }),
}), }),
@ -439,16 +439,16 @@
dict({ dict({
'engine': 'test', 'engine': 'test',
'language': 'en-US', 'language': 'en-US',
'tts_input': 'No device or entity named test transcript', 'tts_input': "Sorry, I couldn't understand that",
'voice': 'james_earl_jones', 'voice': 'james_earl_jones',
}) })
# --- # ---
# name: test_audio_pipeline_with_wake_word_no_timeout.8 # name: test_audio_pipeline_with_wake_word_no_timeout.8
dict({ dict({
'tts_output': dict({ 'tts_output': dict({
'media_id': 'media-source://tts/test?message=No+device+or+entity+named+test+transcript&language=en-US&voice=james_earl_jones', 'media_id': "media-source://tts/test?message=Sorry,+I+couldn't+understand+that&language=en-US&voice=james_earl_jones",
'mime_type': 'audio/mpeg', 'mime_type': 'audio/mpeg',
'url': '/api/tts_proxy/e5e8e318b536f0a5455f993243a34521e7ad4d6d_en-us_031e2ec052_test.mp3', 'url': '/api/tts_proxy/dae2cdcb27a1d1c3b07ba2c7db91480f9d4bfd8f_en-us_031e2ec052_test.mp3',
}), }),
}) })
# --- # ---
@ -778,7 +778,7 @@
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No area named are', 'speech': 'Sorry, I am not aware of any area called are',
}), }),
}), }),
}), }),

View File

@ -352,14 +352,14 @@
'card': dict({ 'card': dict({
}), }),
'data': dict({ 'data': dict({
'code': 'no_valid_targets', 'code': 'no_intent_match',
}), }),
'language': 'en', 'language': 'en',
'response_type': 'error', 'response_type': 'error',
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named do something', 'speech': "Sorry, I couldn't understand that",
}), }),
}), }),
}), }),
@ -519,7 +519,7 @@
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named late added alias', 'speech': 'Sorry, I am not aware of any device or entity called late added alias',
}), }),
}), }),
}), }),
@ -539,7 +539,7 @@
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named kitchen light', 'speech': 'Sorry, I am not aware of any device or entity called kitchen light',
}), }),
}), }),
}), }),
@ -679,7 +679,7 @@
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named late added light', 'speech': 'Sorry, I am not aware of any device or entity called late added light',
}), }),
}), }),
}), }),
@ -759,7 +759,7 @@
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named kitchen light', 'speech': 'Sorry, I am not aware of any device or entity called kitchen light',
}), }),
}), }),
}), }),
@ -779,7 +779,7 @@
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named my cool light', 'speech': 'Sorry, I am not aware of any device or entity called my cool light',
}), }),
}), }),
}), }),
@ -919,7 +919,7 @@
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named kitchen light', 'speech': 'Sorry, I am not aware of any device or entity called kitchen light',
}), }),
}), }),
}), }),
@ -969,7 +969,7 @@
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named renamed light', 'speech': 'Sorry, I am not aware of any device or entity called renamed light',
}), }),
}), }),
}), }),
@ -1252,14 +1252,14 @@
'card': dict({ 'card': dict({
}), }),
'data': dict({ 'data': dict({
'code': 'no_valid_targets', 'code': 'no_intent_match',
}), }),
'language': 'en', 'language': 'en',
'response_type': 'error', 'response_type': 'error',
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named test text', 'speech': "Sorry, I couldn't understand that",
}), }),
}), }),
}), }),
@ -1292,14 +1292,14 @@
'card': dict({ 'card': dict({
}), }),
'data': dict({ 'data': dict({
'code': 'no_valid_targets', 'code': 'no_intent_match',
}), }),
'language': 'en', 'language': 'en',
'response_type': 'error', 'response_type': 'error',
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named test text', 'speech': "Sorry, I couldn't understand that",
}), }),
}), }),
}), }),
@ -1312,14 +1312,14 @@
'card': dict({ 'card': dict({
}), }),
'data': dict({ 'data': dict({
'code': 'no_valid_targets', 'code': 'no_intent_match',
}), }),
'language': 'en', 'language': 'en',
'response_type': 'error', 'response_type': 'error',
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named test text', 'speech': "Sorry, I couldn't understand that",
}), }),
}), }),
}), }),
@ -1352,14 +1352,14 @@
'card': dict({ 'card': dict({
}), }),
'data': dict({ 'data': dict({
'code': 'no_valid_targets', 'code': 'no_intent_match',
}), }),
'language': 'en', 'language': 'en',
'response_type': 'error', 'response_type': 'error',
'speech': dict({ 'speech': dict({
'plain': dict({ 'plain': dict({
'extra_data': None, 'extra_data': None,
'speech': 'No device or entity named test text', 'speech': "Sorry, I couldn't understand that",
}), }),
}), }),
}), }),
@ -1510,29 +1510,7 @@
'unmatched_slots': dict({ 'unmatched_slots': dict({
}), }),
}), }),
dict({ None,
'details': dict({
'domain': dict({
'name': 'domain',
'text': '',
'value': 'scene',
}),
}),
'intent': dict({
'name': 'HassTurnOn',
}),
'match': False,
'sentence_template': '[activate|<turn>] <name> [scene] [on]',
'slots': dict({
'domain': 'scene',
}),
'source': 'builtin',
'targets': dict({
}),
'unmatched_slots': dict({
'name': 'this will not match anything',
}),
}),
]), ]),
}) })
# --- # ---

View File

@ -147,8 +147,8 @@ async def test_conversation_agent(
conversation.HOME_ASSISTANT_AGENT conversation.HOME_ASSISTANT_AGENT
) )
with patch( with patch(
"homeassistant.components.conversation.default_agent.get_domains_and_languages", "homeassistant.components.conversation.default_agent.get_languages",
return_value={"homeassistant": ["dwarvish", "elvish", "entish"]}, return_value=["dwarvish", "elvish", "entish"],
): ):
assert agent.supported_languages == ["dwarvish", "elvish", "entish"] assert agent.supported_languages == ["dwarvish", "elvish", "entish"]
@ -440,7 +440,7 @@ async def test_error_missing_entity(hass: HomeAssistant, init_components) -> Non
assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS
assert ( assert (
result.response.speech["plain"]["speech"] result.response.speech["plain"]["speech"]
== "No device or entity named missing entity" == "Sorry, I am not aware of any device or entity called missing entity"
) )
@ -452,7 +452,10 @@ async def test_error_missing_area(hass: HomeAssistant, init_components) -> None:
assert result.response.response_type == intent.IntentResponseType.ERROR assert result.response.response_type == intent.IntentResponseType.ERROR
assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS
assert result.response.speech["plain"]["speech"] == "No area named missing area" assert (
result.response.speech["plain"]["speech"]
== "Sorry, I am not aware of any area called missing area"
)
async def test_error_no_exposed_for_domain( async def test_error_no_exposed_for_domain(
@ -467,7 +470,8 @@ async def test_error_no_exposed_for_domain(
assert result.response.response_type == intent.IntentResponseType.ERROR assert result.response.response_type == intent.IntentResponseType.ERROR
assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS
assert ( assert (
result.response.speech["plain"]["speech"] == "kitchen does not contain a light" result.response.speech["plain"]["speech"]
== "Sorry, I am not aware of any light in the kitchen area"
) )
@ -483,7 +487,8 @@ async def test_error_no_exposed_for_device_class(
assert result.response.response_type == intent.IntentResponseType.ERROR assert result.response.response_type == intent.IntentResponseType.ERROR
assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS
assert ( assert (
result.response.speech["plain"]["speech"] == "bedroom does not contain a window" result.response.speech["plain"]["speech"]
== "Sorry, I am not aware of any window in the bedroom area"
) )
@ -596,5 +601,5 @@ async def test_all_domains_loaded(
assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS assert result.response.error_code == intent.IntentResponseErrorCode.NO_VALID_TARGETS
assert ( assert (
result.response.speech["plain"]["speech"] result.response.speech["plain"]["speech"]
== "No device or entity named test light" == "Sorry, I am not aware of any device or entity called test light"
) )

View File

@ -581,6 +581,7 @@ async def test_http_api_no_match(
assert data == snapshot assert data == snapshot
assert data["response"]["response_type"] == "error" assert data["response"]["response_type"] == "error"
assert data["response"]["data"]["code"] == "no_intent_match"
async def test_http_api_handle_failure( async def test_http_api_handle_failure(
@ -738,6 +739,7 @@ async def test_ws_api(
assert msg["success"] assert msg["success"]
assert msg["result"] == snapshot assert msg["result"] == snapshot
assert msg["result"]["response"]["data"]["code"] == "no_intent_match"
@pytest.mark.parametrize("agent_id", AGENT_ID_OPTIONS) @pytest.mark.parametrize("agent_id", AGENT_ID_OPTIONS)
@ -1180,7 +1182,7 @@ async def test_ws_hass_agent_debug(
"turn my cool light off", "turn my cool light off",
"turn on all lights in the kitchen", "turn on all lights in the kitchen",
"how many lights are on in the kitchen?", "how many lights are on in the kitchen?",
"this will not match anything", # unmatched in results "this will not match anything", # None in results
], ],
} }
) )
@ -1190,6 +1192,9 @@ async def test_ws_hass_agent_debug(
assert msg["success"] assert msg["success"]
assert msg["result"] == snapshot assert msg["result"] == snapshot
# Last sentence should be a failed match
assert msg["result"]["results"][-1] is None
# Light state should not have been changed # Light state should not have been changed
assert len(on_calls) == 0 assert len(on_calls) == 0
assert len(off_calls) == 0 assert len(off_calls) == 0