core/homeassistant/components/conversation/models.py

106 lines
2.8 KiB
Python

"""Agent foundation for conversation integration."""
from __future__ import annotations
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."""
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,
"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:
"""Clear cached intents for a language."""
async def async_prepare(self, language: str | None = None) -> None:
"""Load intents for a language."""