Paulus Schoutsen 90fc6ffdbf
Add support for continue conversation in Assist Pipeline (#139480)
* Add support for continue conversation in Assist Pipeline

* Also forward to ESPHome

* Update snapshot

* And mobile app
2025-02-28 13:15:31 -06:00

108 lines
3.3 KiB
Python

"""Tests for the conversation component."""
from __future__ import annotations
from collections.abc import AsyncGenerator
from dataclasses import dataclass, field
from typing import Literal
from unittest.mock import patch
import pytest
from homeassistant.components import conversation
from homeassistant.components.conversation.models import (
ConversationInput,
ConversationResult,
)
from homeassistant.components.homeassistant.exposed_entities import (
DATA_EXPOSED_ENTITIES,
async_expose_entity,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import chat_session, intent
class MockAgent(conversation.AbstractConversationAgent):
"""Test Agent."""
def __init__(
self, agent_id: str, supported_languages: list[str] | Literal["*"]
) -> None:
"""Initialize the agent."""
self.agent_id = agent_id
self.calls = []
self.response = "Test response"
self._supported_languages = supported_languages
@property
def supported_languages(self) -> list[str]:
"""Return a list of supported languages."""
return self._supported_languages
async def async_process(self, user_input: ConversationInput) -> ConversationResult:
"""Process some text."""
self.calls.append(user_input)
response = intent.IntentResponse(language=user_input.language)
response.async_set_speech(self.response)
return ConversationResult(
response=response, conversation_id=user_input.conversation_id
)
@pytest.fixture
async def mock_chat_log(hass: HomeAssistant) -> AsyncGenerator[MockChatLog]:
"""Return mock chat logs."""
# pylint: disable-next=contextmanager-generator-missing-cleanup
with (
patch(
"homeassistant.components.conversation.chat_log.ChatLog",
MockChatLog,
),
chat_session.async_get_chat_session(hass, "mock-conversation-id") as session,
conversation.async_get_chat_log(hass, session) as chat_log,
):
yield chat_log
@dataclass
class MockChatLog(conversation.ChatLog):
"""Mock chat log."""
_mock_tool_results: dict = field(default_factory=dict)
def mock_tool_results(self, results: dict) -> None:
"""Set tool results."""
self._mock_tool_results = results
@property
def llm_api(self):
"""Return LLM API."""
return self._llm_api
@llm_api.setter
def llm_api(self, value):
"""Set LLM API."""
self._llm_api = value
if not value:
return
async def async_call_tool(tool_input):
"""Call tool."""
if tool_input.id not in self._mock_tool_results:
raise ValueError(f"Tool {tool_input.id} not found")
return self._mock_tool_results[tool_input.id]
self._llm_api.async_call_tool = async_call_tool
def expose_new(hass: HomeAssistant, expose_new: bool) -> None:
"""Enable exposing new entities to the default agent."""
exposed_entities = hass.data[DATA_EXPOSED_ENTITIES]
exposed_entities.async_set_expose_new_entities(conversation.DOMAIN, expose_new)
def expose_entity(hass: HomeAssistant, entity_id: str, should_expose: bool) -> None:
"""Expose an entity to the default agent."""
async_expose_entity(hass, conversation.DOMAIN, entity_id, should_expose)