mirror of
https://github.com/home-assistant/core.git
synced 2025-07-27 07:07:28 +00:00
Don't prioritize "name" slot if it's a wildcard in default conversation agent (#117518)
* Don't prioritize "name" slot if it's a wildcard * Fix typing error
This commit is contained in:
parent
f31873a846
commit
daee3d8db0
@ -418,7 +418,9 @@ class DefaultAgent(ConversationEntity):
|
|||||||
language: str,
|
language: str,
|
||||||
) -> RecognizeResult | None:
|
) -> RecognizeResult | None:
|
||||||
"""Search intents for a match to user input."""
|
"""Search intents for a match to user input."""
|
||||||
maybe_result: RecognizeResult | None = None
|
name_result: RecognizeResult | None = None
|
||||||
|
best_results: list[RecognizeResult] = []
|
||||||
|
best_text_chunks_matched: int | None = None
|
||||||
for result in recognize_all(
|
for result in recognize_all(
|
||||||
user_input.text,
|
user_input.text,
|
||||||
lang_intents.intents,
|
lang_intents.intents,
|
||||||
@ -426,18 +428,33 @@ class DefaultAgent(ConversationEntity):
|
|||||||
intent_context=intent_context,
|
intent_context=intent_context,
|
||||||
language=language,
|
language=language,
|
||||||
):
|
):
|
||||||
if "name" in result.entities:
|
if ("name" in result.entities) and (
|
||||||
return result
|
not result.entities["name"].is_wildcard
|
||||||
|
):
|
||||||
|
name_result = result
|
||||||
|
|
||||||
# Keep looking in case an entity has the same name
|
if (best_text_chunks_matched is None) or (
|
||||||
maybe_result = result
|
result.text_chunks_matched > best_text_chunks_matched
|
||||||
|
):
|
||||||
|
# Only overwrite if more literal text was matched.
|
||||||
|
# This causes wildcards to match last.
|
||||||
|
best_results = [result]
|
||||||
|
best_text_chunks_matched = result.text_chunks_matched
|
||||||
|
elif result.text_chunks_matched == best_text_chunks_matched:
|
||||||
|
# Accumulate results with the same number of literal text matched.
|
||||||
|
# We will resolve the ambiguity below.
|
||||||
|
best_results.append(result)
|
||||||
|
|
||||||
if maybe_result is not None:
|
if name_result is not None:
|
||||||
|
# Prioritize matches with entity names above area names
|
||||||
|
return name_result
|
||||||
|
|
||||||
|
if best_results:
|
||||||
# Successful strict match
|
# Successful strict match
|
||||||
return maybe_result
|
return best_results[0]
|
||||||
|
|
||||||
# Try again with missing entities enabled
|
# Try again with missing entities enabled
|
||||||
best_num_unmatched_entities = 0
|
maybe_result: RecognizeResult | None = None
|
||||||
for result in recognize_all(
|
for result in recognize_all(
|
||||||
user_input.text,
|
user_input.text,
|
||||||
lang_intents.intents,
|
lang_intents.intents,
|
||||||
|
@ -311,9 +311,9 @@ def _get_debug_targets(
|
|||||||
|
|
||||||
def _get_unmatched_slots(
|
def _get_unmatched_slots(
|
||||||
result: RecognizeResult,
|
result: RecognizeResult,
|
||||||
) -> dict[str, str | int]:
|
) -> dict[str, str | int | float]:
|
||||||
"""Return a dict of unmatched text/range slot entities."""
|
"""Return a dict of unmatched text/range slot entities."""
|
||||||
unmatched_slots: dict[str, str | int] = {}
|
unmatched_slots: dict[str, str | int | float] = {}
|
||||||
for entity in result.unmatched_entities_list:
|
for entity in result.unmatched_entities_list:
|
||||||
if isinstance(entity, UnmatchedTextEntity):
|
if isinstance(entity, UnmatchedTextEntity):
|
||||||
if entity.text == MISSING_ENTITY:
|
if entity.text == MISSING_ENTITY:
|
||||||
|
@ -6,5 +6,5 @@
|
|||||||
"documentation": "https://www.home-assistant.io/integrations/conversation",
|
"documentation": "https://www.home-assistant.io/integrations/conversation",
|
||||||
"integration_type": "system",
|
"integration_type": "system",
|
||||||
"quality_scale": "internal",
|
"quality_scale": "internal",
|
||||||
"requirements": ["hassil==1.6.1", "home-assistant-intents==2024.4.24"]
|
"requirements": ["hassil==1.7.1", "home-assistant-intents==2024.4.24"]
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ ha-av==10.1.1
|
|||||||
ha-ffmpeg==3.2.0
|
ha-ffmpeg==3.2.0
|
||||||
habluetooth==3.0.1
|
habluetooth==3.0.1
|
||||||
hass-nabucasa==0.78.0
|
hass-nabucasa==0.78.0
|
||||||
hassil==1.6.1
|
hassil==1.7.1
|
||||||
home-assistant-bluetooth==1.12.0
|
home-assistant-bluetooth==1.12.0
|
||||||
home-assistant-frontend==20240501.1
|
home-assistant-frontend==20240501.1
|
||||||
home-assistant-intents==2024.4.24
|
home-assistant-intents==2024.4.24
|
||||||
|
@ -1047,7 +1047,7 @@ hass-nabucasa==0.78.0
|
|||||||
hass-splunk==0.1.1
|
hass-splunk==0.1.1
|
||||||
|
|
||||||
# homeassistant.components.conversation
|
# homeassistant.components.conversation
|
||||||
hassil==1.6.1
|
hassil==1.7.1
|
||||||
|
|
||||||
# homeassistant.components.jewish_calendar
|
# homeassistant.components.jewish_calendar
|
||||||
hdate==0.10.8
|
hdate==0.10.8
|
||||||
|
@ -858,7 +858,7 @@ habluetooth==3.0.1
|
|||||||
hass-nabucasa==0.78.0
|
hass-nabucasa==0.78.0
|
||||||
|
|
||||||
# homeassistant.components.conversation
|
# homeassistant.components.conversation
|
||||||
hassil==1.6.1
|
hassil==1.7.1
|
||||||
|
|
||||||
# homeassistant.components.jewish_calendar
|
# homeassistant.components.jewish_calendar
|
||||||
hdate==0.10.8
|
hdate==0.10.8
|
||||||
|
@ -1122,3 +1122,57 @@ async def test_device_id_in_handler(hass: HomeAssistant, init_components) -> Non
|
|||||||
)
|
)
|
||||||
assert result.response.response_type == intent.IntentResponseType.ACTION_DONE
|
assert result.response.response_type == intent.IntentResponseType.ACTION_DONE
|
||||||
assert handler.device_id == device_id
|
assert handler.device_id == device_id
|
||||||
|
|
||||||
|
|
||||||
|
async def test_name_wildcard_lower_priority(
|
||||||
|
hass: HomeAssistant, init_components
|
||||||
|
) -> None:
|
||||||
|
"""Test that the default agent does not prioritize a {name} slot when it's a wildcard."""
|
||||||
|
|
||||||
|
class OrderBeerIntentHandler(intent.IntentHandler):
|
||||||
|
intent_type = "OrderBeer"
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.triggered = False
|
||||||
|
|
||||||
|
async def async_handle(
|
||||||
|
self, intent_obj: intent.Intent
|
||||||
|
) -> intent.IntentResponse:
|
||||||
|
self.triggered = True
|
||||||
|
return intent_obj.create_response()
|
||||||
|
|
||||||
|
class OrderFoodIntentHandler(intent.IntentHandler):
|
||||||
|
intent_type = "OrderFood"
|
||||||
|
|
||||||
|
def __init__(self) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self.triggered = False
|
||||||
|
|
||||||
|
async def async_handle(
|
||||||
|
self, intent_obj: intent.Intent
|
||||||
|
) -> intent.IntentResponse:
|
||||||
|
self.triggered = True
|
||||||
|
return intent_obj.create_response()
|
||||||
|
|
||||||
|
beer_handler = OrderBeerIntentHandler()
|
||||||
|
food_handler = OrderFoodIntentHandler()
|
||||||
|
intent.async_register(hass, beer_handler)
|
||||||
|
intent.async_register(hass, food_handler)
|
||||||
|
|
||||||
|
# Matches OrderBeer because more literal text is matched ("a")
|
||||||
|
result = await conversation.async_converse(
|
||||||
|
hass, "I'd like to order a stout please", None, Context(), None
|
||||||
|
)
|
||||||
|
assert result.response.response_type == intent.IntentResponseType.ACTION_DONE
|
||||||
|
assert beer_handler.triggered
|
||||||
|
assert not food_handler.triggered
|
||||||
|
|
||||||
|
# Matches OrderFood because "cookie" is not in the beer styles list
|
||||||
|
beer_handler.triggered = False
|
||||||
|
result = await conversation.async_converse(
|
||||||
|
hass, "I'd like to order a cookie please", None, Context(), None
|
||||||
|
)
|
||||||
|
assert result.response.response_type == intent.IntentResponseType.ACTION_DONE
|
||||||
|
assert not beer_handler.triggered
|
||||||
|
assert food_handler.triggered
|
||||||
|
@ -4,8 +4,14 @@ intents:
|
|||||||
data:
|
data:
|
||||||
- sentences:
|
- sentences:
|
||||||
- "I'd like to order a {beer_style} [please]"
|
- "I'd like to order a {beer_style} [please]"
|
||||||
|
OrderFood:
|
||||||
|
data:
|
||||||
|
- sentences:
|
||||||
|
- "I'd like to order {food_name:name} [please]"
|
||||||
lists:
|
lists:
|
||||||
beer_style:
|
beer_style:
|
||||||
values:
|
values:
|
||||||
- "stout"
|
- "stout"
|
||||||
- "lager"
|
- "lager"
|
||||||
|
food_name:
|
||||||
|
wildcard: true
|
||||||
|
Loading…
x
Reference in New Issue
Block a user