Move default response out of sentence trigger registration and into agent (#109317)

* Move default response out of trigger and into agent

* Add test
This commit is contained in:
Michael Hansen 2024-02-01 13:40:29 -06:00 committed by GitHub
parent ed726db974
commit c2c98bd04c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 69 additions and 3 deletions

View file

@ -238,7 +238,10 @@ class DefaultAgent(AbstractConversationAgent):
) )
) )
# Use last non-empty result as response # Use last non-empty result as response.
#
# There may be multiple copies of a trigger running when editing in
# the UI, so it's critical that we filter out empty responses here.
response_text: str | None = None response_text: str | None = None
for trigger_response in trigger_responses: for trigger_response in trigger_responses:
response_text = response_text or trigger_response response_text = response_text or trigger_response
@ -246,7 +249,7 @@ class DefaultAgent(AbstractConversationAgent):
# Convert to conversation result # Convert to conversation result
response = intent.IntentResponse(language=language) response = intent.IntentResponse(language=language)
response.response_type = intent.IntentResponseType.ACTION_DONE response.response_type = intent.IntentResponseType.ACTION_DONE
response.async_set_speech(response_text or "") response.async_set_speech(response_text or "Done")
return ConversationResult(response=response) return ConversationResult(response=response)

View file

@ -98,7 +98,12 @@ async def async_attach_trigger(
# mypy does not understand the type narrowing, unclear why # mypy does not understand the type narrowing, unclear why
return automation_result.conversation_response # type: ignore[return-value] return automation_result.conversation_response # type: ignore[return-value]
return "Done" # It's important to return None here instead of a string.
#
# When editing in the UI, a copy of this trigger is registered.
# If we return a string from here, there is a race condition between the
# two trigger copies for who will provide a response.
return None
default_agent = await _get_agent_manager(hass).async_get_agent(HOME_ASSISTANT_AGENT) default_agent = await _get_agent_manager(hass).async_get_agent(HOME_ASSISTANT_AGENT)
assert isinstance(default_agent, DefaultAgent) assert isinstance(default_agent, DefaultAgent)

View file

@ -7,6 +7,7 @@ from homeassistant.helpers import trigger
from homeassistant.setup import async_setup_component from homeassistant.setup import async_setup_component
from tests.common import async_mock_service from tests.common import async_mock_service
from tests.typing import WebSocketGenerator
@pytest.fixture @pytest.fixture
@ -99,6 +100,63 @@ async def test_response(hass: HomeAssistant, setup_comp) -> None:
assert service_response["response"]["speech"]["plain"]["speech"] == response assert service_response["response"]["speech"]["plain"]["speech"] == response
async def test_subscribe_trigger_does_not_interfere_with_responses(
hass: HomeAssistant, setup_comp, hass_ws_client: WebSocketGenerator
) -> None:
"""Test that subscribing to a trigger from the websocket API does not interfere with responses."""
websocket_client = await hass_ws_client()
await websocket_client.send_json(
{
"id": 5,
"type": "subscribe_trigger",
"trigger": {"platform": "conversation", "command": ["test sentence"]},
}
)
service_response = await hass.services.async_call(
"conversation",
"process",
{
"text": "test sentence",
},
blocking=True,
return_response=True,
)
# Default response, since no automations with responses are registered
assert service_response["response"]["speech"]["plain"]["speech"] == "Done"
# Now register a trigger with a response
assert await async_setup_component(
hass,
"automation",
{
"automation test1": {
"trigger": {
"platform": "conversation",
"command": ["test sentence"],
},
"action": {
"set_conversation_response": "test response",
},
}
},
)
service_response = await hass.services.async_call(
"conversation",
"process",
{
"text": "test sentence",
},
blocking=True,
return_response=True,
)
# Response will now come through
assert service_response["response"]["speech"]["plain"]["speech"] == "test response"
async def test_same_trigger_multiple_sentences( async def test_same_trigger_multiple_sentences(
hass: HomeAssistant, calls, setup_comp hass: HomeAssistant, calls, setup_comp
) -> None: ) -> None: