Add intelligent language matching for Google Assistant SDK Agents (#112600)

Co-authored-by: Erik Montnemery <erik@montnemery.com>
This commit is contained in:
t0bst4r 2024-06-08 09:50:15 +02:00 committed by GitHub
parent f605c10f42
commit deac59f1ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 85 additions and 4 deletions

View File

@ -26,11 +26,18 @@ from homeassistant.helpers.config_entry_oauth2_flow import (
)
from homeassistant.helpers.typing import ConfigType
from .const import DATA_MEM_STORAGE, DATA_SESSION, DOMAIN, SUPPORTED_LANGUAGE_CODES
from .const import (
CONF_LANGUAGE_CODE,
DATA_MEM_STORAGE,
DATA_SESSION,
DOMAIN,
SUPPORTED_LANGUAGE_CODES,
)
from .helpers import (
GoogleAssistantSDKAudioView,
InMemoryStorage,
async_send_text_commands,
best_matching_language_code,
)
SERVICE_SEND_TEXT_COMMAND = "send_text_command"
@ -164,9 +171,16 @@ class GoogleAssistantConversationAgent(conversation.AbstractConversationAgent):
if not session.valid_token:
await session.async_ensure_token_valid()
self.assistant = None
if not self.assistant or user_input.language != self.language:
language = best_matching_language_code(
self.hass,
user_input.language,
self.entry.options.get(CONF_LANGUAGE_CODE),
)
if not self.assistant or language != self.language:
credentials = Credentials(session.token[CONF_ACCESS_TOKEN]) # type: ignore[no-untyped-call]
self.language = user_input.language
self.language = language
self.assistant = TextAssistant(credentials, self.language)
resp = await self.hass.async_add_executor_job(
@ -174,7 +188,7 @@ class GoogleAssistantConversationAgent(conversation.AbstractConversationAgent):
)
text_response = resp[0] or "<empty response>"
intent_response = intent.IntentResponse(language=user_input.language)
intent_response = intent.IntentResponse(language=language)
intent_response.async_set_speech(text_response)
return conversation.ConversationResult(
response=intent_response, conversation_id=user_input.conversation_id

View File

@ -113,6 +113,33 @@ def default_language_code(hass: HomeAssistant) -> str:
return DEFAULT_LANGUAGE_CODES.get(hass.config.language, "en-US")
def best_matching_language_code(
hass: HomeAssistant, assist_language: str, agent_language: str | None = None
) -> str:
"""Get the best matching language, based on the preferred assist language and the configured agent language."""
# Use the assist language if supported
if assist_language in SUPPORTED_LANGUAGE_CODES:
return assist_language
language = assist_language.split("-")[0]
# Use the agent language if assist and agent start with the same language part
if agent_language is not None and agent_language.startswith(language):
return best_matching_language_code(hass, agent_language)
# If assist and agent are not matching, try to find the default language
default_language = DEFAULT_LANGUAGE_CODES.get(language)
if default_language is not None:
return default_language
# If no default agent is available, use the agent language
if agent_language is not None:
return best_matching_language_code(hass, agent_language)
# Fallback to the system default language
return default_language_code(hass)
class InMemoryStorage:
"""Temporarily store and retrieve data from in memory storage."""

View File

@ -3,6 +3,7 @@
from homeassistant.components.google_assistant_sdk.const import SUPPORTED_LANGUAGE_CODES
from homeassistant.components.google_assistant_sdk.helpers import (
DEFAULT_LANGUAGE_CODES,
best_matching_language_code,
default_language_code,
)
from homeassistant.core import HomeAssistant
@ -46,3 +47,42 @@ def test_default_language_code(hass: HomeAssistant) -> None:
hass.config.language = "el"
hass.config.country = "GR"
assert default_language_code(hass) == "en-US"
def test_best_matching_language_code(hass: HomeAssistant) -> None:
"""Test best_matching_language_code."""
hass.config.language = "es"
hass.config.country = "MX"
# Assist Language is supported
assert best_matching_language_code(hass, "de-DE", "en-AU") == "de-DE"
assert best_matching_language_code(hass, "de-DE") == "de-DE"
# Assist Language is not supported, but agent language has the same "lang" part, and is supported
assert best_matching_language_code(hass, "en", "en-AU") == "en-AU"
assert best_matching_language_code(hass, "en-XYZ", "en-AU") == "en-AU"
# Assist Language is not supported, but agent language has the same "lang" part, but is not supported
assert best_matching_language_code(hass, "en", "en-XYZ") == "en-US"
assert best_matching_language_code(hass, "en-XYZ", "en-ABC") == "en-US"
# Assist Language is not supported, agent is not matching or available, falling back to the default of assist lang
assert best_matching_language_code(hass, "de", "en-AU") == "de-DE"
assert best_matching_language_code(hass, "de-XYZ", "en-AU") == "de-DE"
assert best_matching_language_code(hass, "de") == "de-DE"
assert best_matching_language_code(hass, "de-XYZ") == "de-DE"
# Assist language is not existing at all, agent is supported
assert best_matching_language_code(hass, "abc-XYZ", "en-AU") == "en-AU"
# Assist language is not existing at all, agent is not supported, falling back to the agent default
assert best_matching_language_code(hass, "abc-XYZ", "de-XYZ") == "de-DE"
# Assist language is not existing at all, agent is not existing or available, falling back to system default
assert best_matching_language_code(hass, "abc-XYZ", "def-XYZ") == "es-MX"
assert best_matching_language_code(hass, "abc-XYZ") == "es-MX"
# Assist language is not existing at all, agent is not existing or available, system default is not supported
hass.config.language = "el"
hass.config.country = "GR"
assert best_matching_language_code(hass, "abc-XYZ", "def-XYZ") == "en-US"
assert best_matching_language_code(hass, "abc-XYZ") == "en-US"