"""Agent foundation for conversation integration.""" from abc import ABC, abstractmethod from dataclasses import dataclass from typing import Any, Literal from homeassistant.core import Context from homeassistant.helpers import intent, llm from .const import DOMAIN @dataclass(frozen=True) class AgentInfo: """Container for conversation agent info.""" id: str name: str supports_streaming: bool @dataclass(slots=True) class ConversationInput: """User input to be processed.""" text: str """User spoken text.""" context: Context """Context of the request.""" conversation_id: str | None """Unique identifier for the conversation.""" device_id: str | None """Unique identifier for the device.""" satellite_id: str | None """Unique identifier for the satellite.""" language: str """Language of the request.""" agent_id: str """Agent to use for processing.""" extra_system_prompt: str | None = None """Extra prompt to provide extra info to LLMs how to understand the command.""" def as_dict(self) -> dict[str, Any]: """Return input as a dict.""" return { "text": self.text, "context": self.context.as_dict(), "conversation_id": self.conversation_id, "device_id": self.device_id, "satellite_id": self.satellite_id, "language": self.language, "agent_id": self.agent_id, "extra_system_prompt": self.extra_system_prompt, } def as_llm_context(self, conversing_domain: str) -> llm.LLMContext: """Return input as an LLM context.""" return llm.LLMContext( platform=conversing_domain, context=self.context, language=self.language, assistant=DOMAIN, device_id=self.device_id, ) @dataclass(slots=True) class ConversationResult: """Result of async_process.""" response: intent.IntentResponse conversation_id: str | None = None continue_conversation: bool = False def as_dict(self) -> dict[str, Any]: """Return result as a dict.""" return { "response": self.response.as_dict(), "conversation_id": self.conversation_id, "continue_conversation": self.continue_conversation, } class AbstractConversationAgent(ABC): """Abstract conversation agent.""" @property @abstractmethod def supported_languages(self) -> list[str] | Literal["*"]: """Return a list of supported languages.""" @abstractmethod async def async_process(self, user_input: ConversationInput) -> ConversationResult: """Process a sentence.""" async def async_reload(self, language: str | None = None) -> None: # noqa: B027 """Clear cached intents for a language.""" async def async_prepare(self, language: str | None = None) -> None: # noqa: B027 """Load intents for a language."""