Add language scores websocket command (#140480)

* Add language scores websocket command

* Don't store language scores in snapshot

* Add language/country args for preferred lang

* Bump intents to 2025.3.24 for dash lang code
This commit is contained in:
Michael Hansen 2025-03-26 08:07:15 -05:00 committed by GitHub
parent 3eda5333b0
commit 8db91623ec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 95 additions and 5 deletions

View File

@ -3,11 +3,13 @@
from __future__ import annotations from __future__ import annotations
from collections.abc import Iterable from collections.abc import Iterable
from dataclasses import asdict
from typing import Any from typing import Any
from aiohttp import web from aiohttp import web
from hassil.recognize import MISSING_ENTITY, RecognizeResult from hassil.recognize import MISSING_ENTITY, RecognizeResult
from hassil.string_matcher import UnmatchedRangeEntity, UnmatchedTextEntity from hassil.string_matcher import UnmatchedRangeEntity, UnmatchedTextEntity
from home_assistant_intents import get_language_scores
import voluptuous as vol import voluptuous as vol
from homeassistant.components import http, websocket_api from homeassistant.components import http, websocket_api
@ -38,6 +40,7 @@ def async_setup(hass: HomeAssistant) -> None:
websocket_api.async_register_command(hass, websocket_list_agents) websocket_api.async_register_command(hass, websocket_list_agents)
websocket_api.async_register_command(hass, websocket_list_sentences) websocket_api.async_register_command(hass, websocket_list_sentences)
websocket_api.async_register_command(hass, websocket_hass_agent_debug) websocket_api.async_register_command(hass, websocket_hass_agent_debug)
websocket_api.async_register_command(hass, websocket_hass_agent_language_scores)
@websocket_api.websocket_command( @websocket_api.websocket_command(
@ -336,6 +339,36 @@ def _get_unmatched_slots(
return unmatched_slots return unmatched_slots
@websocket_api.websocket_command(
{
vol.Required("type"): "conversation/agent/homeassistant/language_scores",
vol.Optional("language"): str,
vol.Optional("country"): str,
}
)
@websocket_api.async_response
async def websocket_hass_agent_language_scores(
hass: HomeAssistant,
connection: websocket_api.ActiveConnection,
msg: dict[str, Any],
) -> None:
"""Get support scores per language."""
language = msg.get("language", hass.config.language)
country = msg.get("country", hass.config.country)
scores = await hass.async_add_executor_job(get_language_scores)
matching_langs = language_util.matches(language, scores.keys(), country=country)
preferred_lang = matching_langs[0] if matching_langs else language
result = {
"languages": {
lang_key: asdict(lang_scores) for lang_key, lang_scores in scores.items()
},
"preferred_language": preferred_lang,
}
connection.send_result(msg["id"], result)
class ConversationProcessView(http.HomeAssistantView): class ConversationProcessView(http.HomeAssistantView):
"""View to process text.""" """View to process text."""

View File

@ -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==2.2.3", "home-assistant-intents==2025.3.23"] "requirements": ["hassil==2.2.3", "home-assistant-intents==2025.3.24"]
} }

View File

@ -39,7 +39,7 @@ hass-nabucasa==0.94.0
hassil==2.2.3 hassil==2.2.3
home-assistant-bluetooth==1.13.1 home-assistant-bluetooth==1.13.1
home-assistant-frontend==20250306.0 home-assistant-frontend==20250306.0
home-assistant-intents==2025.3.23 home-assistant-intents==2025.3.24
httpx==0.28.1 httpx==0.28.1
ifaddr==0.2.0 ifaddr==0.2.0
Jinja2==3.1.6 Jinja2==3.1.6

2
requirements_all.txt generated
View File

@ -1160,7 +1160,7 @@ holidays==0.69
home-assistant-frontend==20250306.0 home-assistant-frontend==20250306.0
# homeassistant.components.conversation # homeassistant.components.conversation
home-assistant-intents==2025.3.23 home-assistant-intents==2025.3.24
# homeassistant.components.homematicip_cloud # homeassistant.components.homematicip_cloud
homematicip==1.1.7 homematicip==1.1.7

View File

@ -987,7 +987,7 @@ holidays==0.69
home-assistant-frontend==20250306.0 home-assistant-frontend==20250306.0
# homeassistant.components.conversation # homeassistant.components.conversation
home-assistant-intents==2025.3.23 home-assistant-intents==2025.3.24
# homeassistant.components.homematicip_cloud # homeassistant.components.homematicip_cloud
homematicip==1.1.7 homematicip==1.1.7

View File

@ -25,7 +25,7 @@ RUN --mount=from=ghcr.io/astral-sh/uv:0.6.10,source=/uv,target=/bin/uv \
-c /usr/src/homeassistant/homeassistant/package_constraints.txt \ -c /usr/src/homeassistant/homeassistant/package_constraints.txt \
-r /usr/src/homeassistant/requirements.txt \ -r /usr/src/homeassistant/requirements.txt \
stdlib-list==0.10.0 pipdeptree==2.25.1 tqdm==4.67.1 ruff==0.11.0 \ stdlib-list==0.10.0 pipdeptree==2.25.1 tqdm==4.67.1 ruff==0.11.0 \
PyTurboJPEG==1.7.5 go2rtc-client==0.1.2 ha-ffmpeg==3.2.2 hassil==2.2.3 home-assistant-intents==2025.3.23 mutagen==1.47.0 pymicro-vad==1.0.1 pyspeex-noise==1.0.2 PyTurboJPEG==1.7.5 go2rtc-client==0.1.2 ha-ffmpeg==3.2.2 hassil==2.2.3 home-assistant-intents==2025.3.24 mutagen==1.47.0 pymicro-vad==1.0.1 pyspeex-noise==1.0.2
LABEL "name"="hassfest" LABEL "name"="hassfest"
LABEL "maintainer"="Home Assistant <hello@home-assistant.io>" LABEL "maintainer"="Home Assistant <hello@home-assistant.io>"

View File

@ -536,3 +536,60 @@ async def test_ws_hass_agent_debug_sentence_trigger(
# Trigger should not have been executed # Trigger should not have been executed
assert len(calls) == 0 assert len(calls) == 0
async def test_ws_hass_language_scores(
hass: HomeAssistant, init_components, hass_ws_client: WebSocketGenerator
) -> None:
"""Test getting language support scores."""
client = await hass_ws_client(hass)
await client.send_json_auto_id(
{"type": "conversation/agent/homeassistant/language_scores"}
)
msg = await client.receive_json()
assert msg["success"]
# Sanity check
result = msg["result"]
assert result["languages"]["en-US"] == {
"cloud": 3,
"focused_local": 2,
"full_local": 3,
}
async def test_ws_hass_language_scores_with_filter(
hass: HomeAssistant, init_components, hass_ws_client: WebSocketGenerator
) -> None:
"""Test getting language support scores with language/country filter."""
client = await hass_ws_client(hass)
# Language filter
await client.send_json_auto_id(
{"type": "conversation/agent/homeassistant/language_scores", "language": "de"}
)
msg = await client.receive_json()
assert msg["success"]
# German should be preferred
result = msg["result"]
assert result["preferred_language"] == "de-DE"
# Language/country filter
await client.send_json_auto_id(
{
"type": "conversation/agent/homeassistant/language_scores",
"language": "en",
"country": "GB",
}
)
msg = await client.receive_json()
assert msg["success"]
# GB English should be preferred
result = msg["result"]
assert result["preferred_language"] == "en-GB"