Load custom sentences from config directory (#85558)

* Load custom sentences from config directory

* Load custom sentences from config directory

* Custom sentences in custom_sentences/<language>/

* Load custom sentences from config directory

* Custom sentences in custom_sentences/<language>/

* Add custom_sentences test
This commit is contained in:
Michael Hansen 2023-01-09 16:48:59 -06:00 committed by GitHub
parent 6970a8a87a
commit 07bd208c7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 92 additions and 0 deletions

View File

@ -3,6 +3,7 @@ from __future__ import annotations
from dataclasses import dataclass
import logging
from pathlib import Path
import re
from typing import Any
@ -10,6 +11,7 @@ from hassil.intents import Intents, SlotList, TextSlotList
from hassil.recognize import recognize
from hassil.util import merge_dict
from home_assistant_intents import get_intents
import yaml
from homeassistant import core, setup
from homeassistant.helpers import area_registry, entity_registry, intent
@ -147,6 +149,32 @@ class DefaultAgent(AbstractConversationAgent):
# Will need to recreate graph
intents_changed = True
_LOGGER.debug(
"Loaded intents component=%s, language=%s", component, language
)
# Check for custom sentences in <config>/custom_sentences/<language>/
if lang_intents is None:
# Only load custom sentences once, otherwise they will be re-loaded
# when components change.
custom_sentences_dir = Path(
self.hass.config.path("custom_sentences", language)
)
if custom_sentences_dir.is_dir():
for custom_sentences_path in custom_sentences_dir.rglob("*.yaml"):
with custom_sentences_path.open(
encoding="utf-8"
) as custom_sentences_file:
# Merge custom sentences
merge_dict(intents_dict, yaml.safe_load(custom_sentences_file))
# Will need to recreate graph
intents_changed = True
_LOGGER.debug(
"Loaded custom sentences language=%s, path=%s",
language,
custom_sentences_path,
)
if not intents_dict:
return None

View File

@ -12,6 +12,19 @@ from homeassistant.setup import async_setup_component
from tests.common import async_mock_service
class OrderBeerIntentHandler(intent.IntentHandler):
"""Handle OrderBeer intent."""
intent_type = "OrderBeer"
async def async_handle(self, intent_obj: intent.Intent) -> intent.IntentResponse:
"""Return speech response."""
beer_style = intent_obj.slots["beer_style"]["value"]
response = intent_obj.create_response()
response.async_set_speech(f"You ordered a {beer_style}")
return response
@pytest.fixture
async def init_components(hass):
"""Initialize relevant components with empty configs."""
@ -314,3 +327,43 @@ async def test_ws_api(hass, hass_ws_client, payload):
},
"conversation_id": payload.get("conversation_id") or ANY,
}
async def test_custom_sentences(hass, hass_client, hass_admin_user):
"""Test custom sentences with a custom intent."""
assert await async_setup_component(hass, "homeassistant", {})
assert await async_setup_component(hass, "conversation", {})
assert await async_setup_component(hass, "intent", {})
# Expecting testing_config/custom_sentences/en/beer.yaml
intent.async_register(hass, OrderBeerIntentHandler())
# Invoke intent via HTTP API
client = await hass_client()
for beer_style in ("stout", "lager"):
resp = await client.post(
"/api/conversation/process",
json={"text": f"I'd like to order a {beer_style}, please"},
)
assert resp.status == HTTPStatus.OK
data = await resp.json()
assert data == {
"response": {
"card": {},
"speech": {
"plain": {
"extra_data": None,
"speech": f"You ordered a {beer_style}",
}
},
"language": hass.config.language,
"response_type": "action_done",
"data": {
"targets": [],
"success": [],
"failed": [],
},
},
"conversation_id": None,
}

View File

@ -0,0 +1,11 @@
language: "en"
intents:
OrderBeer:
data:
- sentences:
- "I'd like to order a {beer_style} [please]"
lists:
beer_style:
values:
- "stout"
- "lager"