mirror of
https://github.com/home-assistant/core.git
synced 2025-07-19 03:07:37 +00:00
Update intent response (#83962)
* Add language to conversation and intent response * Move language to intent response instead of speech * Extend intent response for voice MVP * Add tests for error conditions in conversation/process * Move intent response type data into "data" field * Move intent response error message back to speech * Remove "success" from intent response * Add id to target in intent response * target defaults to None * Update homeassistant/helpers/intent.py * Fix test * Return conversation_id and multiple targets * Clean up git mess * Fix linting errors * Fix more async_handle signatures * Separate conversation_id and IntentResponse * Add unknown error code * Add ConversationResult * Don't set domain on single entity * Language is required for intent response * Add partial_action_done * Default language in almond agent * Remove partial_action_done * Fix linting * Rename success/fail targets Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
parent
da62528526
commit
98eabd2f68
@ -221,8 +221,8 @@ class ServiceIntentHandler(IntentHandler):
|
|||||||
|
|
||||||
response = intent_obj.create_response()
|
response = intent_obj.create_response()
|
||||||
response.async_set_speech(self.speech.format(state.name))
|
response.async_set_speech(self.speech.format(state.name))
|
||||||
response.async_set_targets(
|
response.async_set_results(
|
||||||
[
|
success_results=[
|
||||||
IntentResponseTarget(
|
IntentResponseTarget(
|
||||||
type=IntentResponseTargetType.ENTITY,
|
type=IntentResponseTargetType.ENTITY,
|
||||||
name=state.name,
|
name=state.name,
|
||||||
@ -351,9 +351,9 @@ class IntentResponse:
|
|||||||
self.reprompt: dict[str, dict[str, Any]] = {}
|
self.reprompt: dict[str, dict[str, Any]] = {}
|
||||||
self.card: dict[str, dict[str, str]] = {}
|
self.card: dict[str, dict[str, str]] = {}
|
||||||
self.error_code: IntentResponseErrorCode | None = None
|
self.error_code: IntentResponseErrorCode | None = None
|
||||||
self.targets: list[IntentResponseTarget] = []
|
self.intent_targets: list[IntentResponseTarget] = []
|
||||||
self.success_targets: list[IntentResponseTarget] = []
|
self.success_results: list[IntentResponseTarget] = []
|
||||||
self.failed_targets: list[IntentResponseTarget] = []
|
self.failed_results: list[IntentResponseTarget] = []
|
||||||
|
|
||||||
if (self.intent is not None) and (self.intent.category == IntentCategory.QUERY):
|
if (self.intent is not None) and (self.intent.category == IntentCategory.QUERY):
|
||||||
# speech will be the answer to the query
|
# speech will be the answer to the query
|
||||||
@ -404,20 +404,22 @@ class IntentResponse:
|
|||||||
self.async_set_speech(message)
|
self.async_set_speech(message)
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def async_set_targets(self, targets: list[IntentResponseTarget]) -> None:
|
def async_set_targets(
|
||||||
"""Set response targets."""
|
|
||||||
self.targets = targets
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def async_set_partial_action_done(
|
|
||||||
self,
|
self,
|
||||||
success_targets: list[IntentResponseTarget],
|
intent_targets: list[IntentResponseTarget],
|
||||||
failed_targets: list[IntentResponseTarget],
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Set response targets."""
|
"""Set response targets."""
|
||||||
self.response_type = IntentResponseType.PARTIAL_ACTION_DONE
|
self.intent_targets = intent_targets
|
||||||
self.success_targets = success_targets
|
|
||||||
self.failed_targets = failed_targets
|
@callback
|
||||||
|
def async_set_results(
|
||||||
|
self,
|
||||||
|
success_results: list[IntentResponseTarget],
|
||||||
|
failed_results: list[IntentResponseTarget] | None = None,
|
||||||
|
) -> None:
|
||||||
|
"""Set response results."""
|
||||||
|
self.success_results = success_results
|
||||||
|
self.failed_results = failed_results if failed_results is not None else []
|
||||||
|
|
||||||
@callback
|
@callback
|
||||||
def as_dict(self) -> dict[str, Any]:
|
def as_dict(self) -> dict[str, Any]:
|
||||||
@ -440,18 +442,17 @@ class IntentResponse:
|
|||||||
else:
|
else:
|
||||||
# action done or query answer
|
# action done or query answer
|
||||||
response_data["targets"] = [
|
response_data["targets"] = [
|
||||||
dataclasses.asdict(target) for target in self.targets
|
dataclasses.asdict(target) for target in self.intent_targets
|
||||||
]
|
]
|
||||||
|
|
||||||
if self.response_type == IntentResponseType.PARTIAL_ACTION_DONE:
|
# Add success/failed targets
|
||||||
# Add success/failed targets
|
response_data["success"] = [
|
||||||
response_data["success"] = [
|
dataclasses.asdict(target) for target in self.success_results
|
||||||
dataclasses.asdict(target) for target in self.success_targets
|
]
|
||||||
]
|
|
||||||
|
|
||||||
response_data["failed"] = [
|
response_data["failed"] = [
|
||||||
dataclasses.asdict(target) for target in self.failed_targets
|
dataclasses.asdict(target) for target in self.failed_results
|
||||||
]
|
]
|
||||||
|
|
||||||
response_dict["data"] = response_data
|
response_dict["data"] = response_data
|
||||||
|
|
||||||
|
@ -143,13 +143,13 @@ async def test_http_processing_intent(hass, hass_client, hass_admin_user):
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"language": hass.config.language,
|
"language": hass.config.language,
|
||||||
"data": {"targets": []},
|
"data": {"targets": [], "success": [], "failed": []},
|
||||||
},
|
},
|
||||||
"conversation_id": None,
|
"conversation_id": None,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
async def test_http_partial_action(hass, hass_client, hass_admin_user):
|
async def test_http_failed_action(hass, hass_client, hass_admin_user):
|
||||||
"""Test processing intent via HTTP API with a partial completion."""
|
"""Test processing intent via HTTP API with a partial completion."""
|
||||||
|
|
||||||
class TestIntentHandler(intent.IntentHandler):
|
class TestIntentHandler(intent.IntentHandler):
|
||||||
@ -161,24 +161,24 @@ async def test_http_partial_action(hass, hass_client, hass_admin_user):
|
|||||||
"""Handle the intent."""
|
"""Handle the intent."""
|
||||||
response = handle_intent.create_response()
|
response = handle_intent.create_response()
|
||||||
area = handle_intent.slots["area"]["value"]
|
area = handle_intent.slots["area"]["value"]
|
||||||
|
|
||||||
|
# Mark some targets as successful, others as failed
|
||||||
response.async_set_targets(
|
response.async_set_targets(
|
||||||
[
|
intent_targets=[
|
||||||
intent.IntentResponseTarget(
|
intent.IntentResponseTarget(
|
||||||
type=intent.IntentResponseTargetType.AREA, name=area, id=area
|
type=intent.IntentResponseTargetType.AREA, name=area, id=area
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
response.async_set_results(
|
||||||
# Mark some targets as successful, others as failed
|
success_results=[
|
||||||
response.async_set_partial_action_done(
|
|
||||||
success_targets=[
|
|
||||||
intent.IntentResponseTarget(
|
intent.IntentResponseTarget(
|
||||||
type=intent.IntentResponseTargetType.ENTITY,
|
type=intent.IntentResponseTargetType.ENTITY,
|
||||||
name="light1",
|
name="light1",
|
||||||
id="light.light1",
|
id="light.light1",
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
failed_targets=[
|
failed_results=[
|
||||||
intent.IntentResponseTarget(
|
intent.IntentResponseTarget(
|
||||||
type=intent.IntentResponseTargetType.ENTITY,
|
type=intent.IntentResponseTargetType.ENTITY,
|
||||||
name="light2",
|
name="light2",
|
||||||
@ -186,6 +186,7 @@ async def test_http_partial_action(hass, hass_client, hass_admin_user):
|
|||||||
)
|
)
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
intent.async_register(hass, TestIntentHandler())
|
intent.async_register(hass, TestIntentHandler())
|
||||||
@ -211,7 +212,7 @@ async def test_http_partial_action(hass, hass_client, hass_admin_user):
|
|||||||
|
|
||||||
assert data == {
|
assert data == {
|
||||||
"response": {
|
"response": {
|
||||||
"response_type": "partial_action_done",
|
"response_type": "action_done",
|
||||||
"card": {},
|
"card": {},
|
||||||
"speech": {},
|
"speech": {},
|
||||||
"language": hass.config.language,
|
"language": hass.config.language,
|
||||||
@ -298,13 +299,15 @@ async def test_http_api(hass, init_components, hass_client):
|
|||||||
"language": hass.config.language,
|
"language": hass.config.language,
|
||||||
"response_type": "action_done",
|
"response_type": "action_done",
|
||||||
"data": {
|
"data": {
|
||||||
"targets": [
|
"targets": [],
|
||||||
|
"success": [
|
||||||
{
|
{
|
||||||
"type": "entity",
|
"type": "entity",
|
||||||
"name": "kitchen",
|
"name": "kitchen",
|
||||||
"id": "light.kitchen",
|
"id": "light.kitchen",
|
||||||
},
|
},
|
||||||
]
|
],
|
||||||
|
"failed": [],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"conversation_id": None,
|
"conversation_id": None,
|
||||||
@ -468,7 +471,7 @@ async def test_custom_agent(hass, hass_client, hass_admin_user):
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"language": "test-language",
|
"language": "test-language",
|
||||||
"data": {"targets": []},
|
"data": {"targets": [], "success": [], "failed": []},
|
||||||
},
|
},
|
||||||
"conversation_id": "test-conv-id",
|
"conversation_id": "test-conv-id",
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ async def test_http_handle_intent(hass, hass_client, hass_admin_user):
|
|||||||
},
|
},
|
||||||
"language": hass.config.language,
|
"language": hass.config.language,
|
||||||
"response_type": "action_done",
|
"response_type": "action_done",
|
||||||
"data": {"targets": []},
|
"data": {"targets": [], "success": [], "failed": []},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user