Drop homeassistant agent and assist_pipeline migration code (#147968)

Co-authored-by: Franck Nijhof <git@frenck.dev>
Co-authored-by: Paulus Schoutsen <balloob@gmail.com>
This commit is contained in:
Allen Porter 2025-07-10 08:30:54 -07:00 committed by GitHub
parent 4f27058a68
commit d15baf9f9f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 26 additions and 301 deletions

View File

@ -38,8 +38,6 @@ from .pipeline import (
async_create_default_pipeline,
async_get_pipeline,
async_get_pipelines,
async_migrate_engine,
async_run_migrations,
async_setup_pipeline_store,
async_update_pipeline,
)
@ -61,7 +59,6 @@ __all__ = (
"WakeWordSettings",
"async_create_default_pipeline",
"async_get_pipelines",
"async_migrate_engine",
"async_pipeline_from_audio_stream",
"async_setup",
"async_update_pipeline",
@ -87,7 +84,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
hass.data[DATA_LAST_WAKE_UP] = {}
await async_setup_pipeline_store(hass)
await async_run_migrations(hass)
async_register_websocket_api(hass)
return True

View File

@ -3,7 +3,6 @@
DOMAIN = "assist_pipeline"
DATA_CONFIG = f"{DOMAIN}.config"
DATA_MIGRATIONS = f"{DOMAIN}_migrations"
DEFAULT_PIPELINE_TIMEOUT = 60 * 5 # seconds

View File

@ -13,7 +13,7 @@ from pathlib import Path
from queue import Empty, Queue
from threading import Thread
import time
from typing import TYPE_CHECKING, Any, Literal, cast
from typing import TYPE_CHECKING, Any, cast
import wave
import hass_nabucasa
@ -49,7 +49,6 @@ from .const import (
CONF_DEBUG_RECORDING_DIR,
DATA_CONFIG,
DATA_LAST_WAKE_UP,
DATA_MIGRATIONS,
DOMAIN,
MS_PER_CHUNK,
SAMPLE_CHANNELS,
@ -2059,50 +2058,6 @@ async def async_setup_pipeline_store(hass: HomeAssistant) -> PipelineData:
return PipelineData(pipeline_store)
@callback
def async_migrate_engine(
hass: HomeAssistant,
engine_type: Literal["conversation", "stt", "tts", "wake_word"],
old_value: str,
new_value: str,
) -> None:
"""Register a migration of an engine used in pipelines."""
hass.data.setdefault(DATA_MIGRATIONS, {})[engine_type] = (old_value, new_value)
# Run migrations when config is already loaded
if DATA_CONFIG in hass.data:
hass.async_create_background_task(
async_run_migrations(hass), "assist_pipeline_migration", eager_start=True
)
async def async_run_migrations(hass: HomeAssistant) -> None:
"""Run pipeline migrations."""
if not (migrations := hass.data.get(DATA_MIGRATIONS)):
return
engine_attr = {
"conversation": "conversation_engine",
"stt": "stt_engine",
"tts": "tts_engine",
"wake_word": "wake_word_entity",
}
updates = []
for pipeline in async_get_pipelines(hass):
attr_updates = {}
for engine_type, (old_value, new_value) in migrations.items():
if getattr(pipeline, engine_attr[engine_type]) == old_value:
attr_updates[engine_attr[engine_type]] = new_value
if attr_updates:
updates.append((pipeline, attr_updates))
for pipeline, attr_updates in updates:
await async_update_pipeline(hass, pipeline, **attr_updates)
@dataclass
class PipelineConversationData:
"""Hold data for the duration of a conversation."""

View File

@ -51,7 +51,6 @@ from .const import (
DATA_DEFAULT_ENTITY,
DOMAIN,
HOME_ASSISTANT_AGENT,
OLD_HOME_ASSISTANT_AGENT,
SERVICE_PROCESS,
SERVICE_RELOAD,
ConversationEntityFeature,
@ -65,7 +64,6 @@ from .trace import ConversationTraceEventType, async_conversation_trace_append
__all__ = [
"DOMAIN",
"HOME_ASSISTANT_AGENT",
"OLD_HOME_ASSISTANT_AGENT",
"AssistantContent",
"AssistantContentDeltaDict",
"ChatLog",
@ -270,15 +268,6 @@ async def async_setup(hass: HomeAssistant, config: ConfigType) -> bool:
hass, entity_component, config.get(DOMAIN, {}).get("intents", {})
)
# Temporary migration. We can remove this in 2024.10
from homeassistant.components.assist_pipeline import ( # noqa: PLC0415
async_migrate_engine,
)
async_migrate_engine(
hass, "conversation", OLD_HOME_ASSISTANT_AGENT, HOME_ASSISTANT_AGENT
)
async def handle_process(service: ServiceCall) -> ServiceResponse:
"""Parse text into commands."""
text = service.data[ATTR_TEXT]

View File

@ -12,12 +12,7 @@ from homeassistant.core import Context, HomeAssistant, async_get_hass, callback
from homeassistant.exceptions import HomeAssistantError
from homeassistant.helpers import config_validation as cv, intent, singleton
from .const import (
DATA_COMPONENT,
DATA_DEFAULT_ENTITY,
HOME_ASSISTANT_AGENT,
OLD_HOME_ASSISTANT_AGENT,
)
from .const import DATA_COMPONENT, DATA_DEFAULT_ENTITY, HOME_ASSISTANT_AGENT
from .entity import ConversationEntity
from .models import (
AbstractConversationAgent,
@ -54,7 +49,7 @@ def async_get_agent(
hass: HomeAssistant, agent_id: str | None = None
) -> AbstractConversationAgent | ConversationEntity | None:
"""Get specified agent."""
if agent_id is None or agent_id in (HOME_ASSISTANT_AGENT, OLD_HOME_ASSISTANT_AGENT):
if agent_id is None or agent_id == HOME_ASSISTANT_AGENT:
return hass.data[DATA_DEFAULT_ENTITY]
if "." in agent_id:

View File

@ -16,7 +16,6 @@ if TYPE_CHECKING:
DOMAIN = "conversation"
DEFAULT_EXPOSED_ATTRIBUTES = {"device_class"}
HOME_ASSISTANT_AGENT = "conversation.home_assistant"
OLD_HOME_ASSISTANT_AGENT = "homeassistant"
ATTR_TEXT = "text"
ATTR_LANGUAGE = "language"

View File

@ -4,7 +4,7 @@ from __future__ import annotations
from typing import Literal
from homeassistant.components import assist_pipeline, conversation
from homeassistant.components import conversation
from homeassistant.config_entries import ConfigEntry, ConfigSubentry
from homeassistant.const import CONF_LLM_HASS_API, MATCH_ALL
from homeassistant.core import HomeAssistant
@ -57,9 +57,6 @@ class GoogleGenerativeAIConversationEntity(
async def async_added_to_hass(self) -> None:
"""When entity is added to Home Assistant."""
await super().async_added_to_hass()
assist_pipeline.async_migrate_engine(
self.hass, "conversation", self.entry.entry_id, self.entity_id
)
conversation.async_set_agent(self.hass, self.entry, self)
async def async_will_remove_from_hass(self) -> None:

View File

@ -4,7 +4,7 @@ from __future__ import annotations
from typing import Literal
from homeassistant.components import assist_pipeline, conversation
from homeassistant.components import conversation
from homeassistant.config_entries import ConfigSubentry
from homeassistant.const import CONF_LLM_HASS_API, MATCH_ALL
from homeassistant.core import HomeAssistant
@ -52,9 +52,6 @@ class OllamaConversationEntity(
async def async_added_to_hass(self) -> None:
"""When entity is added to Home Assistant."""
await super().async_added_to_hass()
assist_pipeline.async_migrate_engine(
self.hass, "conversation", self.entry.entry_id, self.entity_id
)
conversation.async_set_agent(self.hass, self.entry, self)
async def async_will_remove_from_hass(self) -> None:

View File

@ -2,7 +2,7 @@
from typing import Literal
from homeassistant.components import assist_pipeline, conversation
from homeassistant.components import conversation
from homeassistant.config_entries import ConfigSubentry
from homeassistant.const import CONF_LLM_HASS_API, MATCH_ALL
from homeassistant.core import HomeAssistant
@ -57,9 +57,6 @@ class OpenAIConversationEntity(
async def async_added_to_hass(self) -> None:
"""When entity is added to Home Assistant."""
await super().async_added_to_hass()
assist_pipeline.async_migrate_engine(
self.hass, "conversation", self.entry.entry_id, self.entity_id
)
conversation.async_set_agent(self.hass, self.entry, self)
async def async_will_remove_from_hass(self) -> None:

View File

@ -140,8 +140,6 @@ IGNORE_VIOLATIONS = {
("websocket_api", "lovelace"),
("websocket_api", "shopping_list"),
"logbook",
# Temporary needed for migration until 2024.10
("conversation", "assist_pipeline"),
}

View File

@ -12,7 +12,7 @@ import hass_nabucasa
import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components import assist_pipeline, stt
from homeassistant.components import assist_pipeline, conversation, stt
from homeassistant.components.assist_pipeline.const import (
BYTES_PER_CHUNK,
CONF_DEBUG_RECORDING_DIR,
@ -116,7 +116,7 @@ async def test_pipeline_from_audio_stream_legacy(
await client.send_json_auto_id(
{
"type": "assist_pipeline/pipeline/create",
"conversation_engine": "homeassistant",
"conversation_engine": conversation.HOME_ASSISTANT_AGENT,
"conversation_language": "en-US",
"language": "en",
"name": "test_name",
@ -184,7 +184,7 @@ async def test_pipeline_from_audio_stream_entity(
await client.send_json_auto_id(
{
"type": "assist_pipeline/pipeline/create",
"conversation_engine": "homeassistant",
"conversation_engine": conversation.HOME_ASSISTANT_AGENT,
"conversation_language": "en-US",
"language": "en",
"name": "test_name",
@ -252,7 +252,7 @@ async def test_pipeline_from_audio_stream_no_stt(
await client.send_json_auto_id(
{
"type": "assist_pipeline/pipeline/create",
"conversation_engine": "homeassistant",
"conversation_engine": conversation.HOME_ASSISTANT_AGENT,
"conversation_language": "en-US",
"language": "en",
"name": "test_name",

View File

@ -29,7 +29,6 @@ from homeassistant.components.assist_pipeline.pipeline import (
async_create_default_pipeline,
async_get_pipeline,
async_get_pipelines,
async_migrate_engine,
async_update_pipeline,
)
from homeassistant.const import MATCH_ALL
@ -162,12 +161,6 @@ async def test_loading_pipelines_from_storage(
hass: HomeAssistant, hass_storage: dict[str, Any]
) -> None:
"""Test loading stored pipelines on start."""
async_migrate_engine(
hass,
"conversation",
conversation.OLD_HOME_ASSISTANT_AGENT,
conversation.HOME_ASSISTANT_AGENT,
)
id_1 = "01GX8ZWBAQYWNB1XV3EXEZ75DY"
hass_storage[STORAGE_KEY] = {
"version": STORAGE_VERSION,
@ -176,7 +169,7 @@ async def test_loading_pipelines_from_storage(
"data": {
"items": [
{
"conversation_engine": conversation.OLD_HOME_ASSISTANT_AGENT,
"conversation_engine": conversation.HOME_ASSISTANT_AGENT,
"conversation_language": "language_1",
"id": id_1,
"language": "language_1",
@ -668,43 +661,6 @@ async def test_update_pipeline(
}
@pytest.mark.usefixtures("init_supporting_components")
async def test_migrate_after_load(hass: HomeAssistant) -> None:
"""Test migrating an engine after done loading."""
assert await async_setup_component(hass, "assist_pipeline", {})
pipeline_data: PipelineData = hass.data[DOMAIN]
store = pipeline_data.pipeline_store
assert len(store.data) == 1
assert (
await async_create_default_pipeline(
hass,
stt_engine_id="bla",
tts_engine_id="bla",
pipeline_name="Bla pipeline",
)
is None
)
pipeline = await async_create_default_pipeline(
hass,
stt_engine_id="test",
tts_engine_id="test",
pipeline_name="Test pipeline",
)
assert pipeline is not None
async_migrate_engine(hass, "stt", "test", "stt.test")
async_migrate_engine(hass, "tts", "test", "tts.test")
await hass.async_block_till_done(wait_background_tasks=True)
pipeline_updated = async_get_pipeline(hass, pipeline.id)
assert pipeline_updated.stt_engine == "stt.test"
assert pipeline_updated.tts_engine == "tts.test"
def test_fallback_intent_filter() -> None:
"""Test that we filter the right things."""
assert (
@ -1364,7 +1320,7 @@ async def test_stt_language_used_instead_of_conversation_language(
await client.send_json_auto_id(
{
"type": "assist_pipeline/pipeline/create",
"conversation_engine": "homeassistant",
"conversation_engine": conversation.HOME_ASSISTANT_AGENT,
"conversation_language": MATCH_ALL,
"language": "en",
"name": "test_name",
@ -1440,7 +1396,7 @@ async def test_tts_language_used_instead_of_conversation_language(
await client.send_json_auto_id(
{
"type": "assist_pipeline/pipeline/create",
"conversation_engine": "homeassistant",
"conversation_engine": conversation.HOME_ASSISTANT_AGENT,
"conversation_language": MATCH_ALL,
"language": "en",
"name": "test_name",
@ -1516,7 +1472,7 @@ async def test_pipeline_language_used_instead_of_conversation_language(
await client.send_json_auto_id(
{
"type": "assist_pipeline/pipeline/create",
"conversation_engine": "homeassistant",
"conversation_engine": conversation.HOME_ASSISTANT_AGENT,
"conversation_language": MATCH_ALL,
"language": "en",
"name": "test_name",

View File

@ -327,37 +327,6 @@
}),
})
# ---
# name: test_http_processing_intent[homeassistant]
dict({
'continue_conversation': False,
'conversation_id': <ANY>,
'response': dict({
'card': dict({
}),
'data': dict({
'failed': list([
]),
'success': list([
dict({
'id': 'light.kitchen',
'name': 'kitchen',
'type': 'entity',
}),
]),
'targets': list([
]),
}),
'language': 'en',
'response_type': 'action_done',
'speech': dict({
'plain': dict({
'extra_data': None,
'speech': 'Turned on the light',
}),
}),
}),
})
# ---
# name: test_ws_api[payload0]
dict({
'continue_conversation': False,

View File

@ -108,37 +108,6 @@
}),
})
# ---
# name: test_turn_on_intent[None-turn kitchen on-homeassistant]
dict({
'continue_conversation': False,
'conversation_id': <ANY>,
'response': dict({
'card': dict({
}),
'data': dict({
'failed': list([
]),
'success': list([
dict({
'id': 'light.kitchen',
'name': 'kitchen',
'type': <IntentResponseTargetType.ENTITY: 'entity'>,
}),
]),
'targets': list([
]),
}),
'language': 'en',
'response_type': 'action_done',
'speech': dict({
'plain': dict({
'extra_data': None,
'speech': 'Turned on the light',
}),
}),
}),
})
# ---
# name: test_turn_on_intent[None-turn on kitchen-None]
dict({
'continue_conversation': False,
@ -201,37 +170,6 @@
}),
})
# ---
# name: test_turn_on_intent[None-turn on kitchen-homeassistant]
dict({
'continue_conversation': False,
'conversation_id': <ANY>,
'response': dict({
'card': dict({
}),
'data': dict({
'failed': list([
]),
'success': list([
dict({
'id': 'light.kitchen',
'name': 'kitchen',
'type': <IntentResponseTargetType.ENTITY: 'entity'>,
}),
]),
'targets': list([
]),
}),
'language': 'en',
'response_type': 'action_done',
'speech': dict({
'plain': dict({
'extra_data': None,
'speech': 'Turned on the light',
}),
}),
}),
})
# ---
# name: test_turn_on_intent[my_new_conversation-turn kitchen on-None]
dict({
'continue_conversation': False,
@ -294,37 +232,6 @@
}),
})
# ---
# name: test_turn_on_intent[my_new_conversation-turn kitchen on-homeassistant]
dict({
'continue_conversation': False,
'conversation_id': <ANY>,
'response': dict({
'card': dict({
}),
'data': dict({
'failed': list([
]),
'success': list([
dict({
'id': 'light.kitchen',
'name': 'kitchen',
'type': <IntentResponseTargetType.ENTITY: 'entity'>,
}),
]),
'targets': list([
]),
}),
'language': 'en',
'response_type': 'action_done',
'speech': dict({
'plain': dict({
'extra_data': None,
'speech': 'Turned on the light',
}),
}),
}),
})
# ---
# name: test_turn_on_intent[my_new_conversation-turn on kitchen-None]
dict({
'continue_conversation': False,
@ -387,34 +294,3 @@
}),
})
# ---
# name: test_turn_on_intent[my_new_conversation-turn on kitchen-homeassistant]
dict({
'continue_conversation': False,
'conversation_id': <ANY>,
'response': dict({
'card': dict({
}),
'data': dict({
'failed': list([
]),
'success': list([
dict({
'id': 'light.kitchen',
'name': 'kitchen',
'type': <IntentResponseTargetType.ENTITY: 'entity'>,
}),
]),
'targets': list([
]),
}),
'language': 'en',
'response_type': 'action_done',
'speech': dict({
'plain': dict({
'extra_data': None,
'speech': 'Turned on the light',
}),
}),
}),
})
# ---

View File

@ -8,7 +8,10 @@ import pytest
from syrupy.assertion import SnapshotAssertion
from homeassistant.components.conversation import default_agent
from homeassistant.components.conversation.const import DATA_DEFAULT_ENTITY
from homeassistant.components.conversation.const import (
DATA_DEFAULT_ENTITY,
HOME_ASSISTANT_AGENT,
)
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
from homeassistant.const import ATTR_FRIENDLY_NAME
from homeassistant.core import HomeAssistant
@ -22,8 +25,6 @@ from tests.typing import ClientSessionGenerator, WebSocketGenerator
AGENT_ID_OPTIONS = [
None,
# Old value of conversation.HOME_ASSISTANT_AGENT,
"homeassistant",
# Current value of conversation.HOME_ASSISTANT_AGENT,
"conversation.home_assistant",
]
@ -187,7 +188,7 @@ async def test_http_api_wrong_data(
},
{
"text": "Test Text",
"agent_id": "homeassistant",
"agent_id": HOME_ASSISTANT_AGENT,
},
],
)

View File

@ -14,7 +14,10 @@ from homeassistant.components.conversation import (
async_handle_sentence_triggers,
default_agent,
)
from homeassistant.components.conversation.const import DATA_DEFAULT_ENTITY
from homeassistant.components.conversation.const import (
DATA_DEFAULT_ENTITY,
HOME_ASSISTANT_AGENT,
)
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
from homeassistant.core import Context, HomeAssistant
from homeassistant.exceptions import HomeAssistantError
@ -28,8 +31,6 @@ from tests.typing import ClientSessionGenerator
AGENT_ID_OPTIONS = [
None,
# Old value of conversation.HOME_ASSISTANT_AGENT,
"homeassistant",
# Current value of conversation.HOME_ASSISTANT_AGENT,
"conversation.home_assistant",
]
@ -205,8 +206,8 @@ async def test_get_agent_info(
"""Test get agent info."""
agent_info = conversation.async_get_agent_info(hass)
# Test it's the default
assert conversation.async_get_agent_info(hass, "homeassistant") == agent_info
assert conversation.async_get_agent_info(hass, "homeassistant") == snapshot
assert conversation.async_get_agent_info(hass, HOME_ASSISTANT_AGENT) == agent_info
assert conversation.async_get_agent_info(hass, HOME_ASSISTANT_AGENT) == snapshot
assert (
conversation.async_get_agent_info(hass, mock_conversation_agent.agent_id)
== snapshot
@ -223,7 +224,7 @@ async def test_get_agent_info(
default_agent = conversation.async_get_agent(hass)
default_agent._attr_supports_streaming = True
assert (
conversation.async_get_agent_info(hass, "homeassistant").supports_streaming
conversation.async_get_agent_info(hass, HOME_ASSISTANT_AGENT).supports_streaming
is True
)