"""Base interface adapter for multi-modal interaction.""" from abc import ABC, abstractmethod from enum import Enum from typing import Any, AsyncIterator from pydantic import BaseModel, Field from fusionagi._time import utc_now_iso class ModalityType(str, Enum): """Types of sensory modalities supported.""" TEXT = "text" VOICE = "voice" VISUAL = "visual" HAPTIC = "haptic" GESTURE = "gesture" BIOMETRIC = "biometric" class InterfaceMessage(BaseModel): """Message exchanged through an interface.""" id: str = Field(description="Unique message identifier") modality: ModalityType = Field(description="Sensory modality of this message") content: Any = Field(description="Message content (modality-specific)") metadata: dict[str, Any] = Field(default_factory=dict, description="Additional metadata") timestamp: str = Field( default_factory=utc_now_iso, description="Message timestamp" ) user_id: str | None = Field(default=None, description="User identifier if applicable") session_id: str | None = Field(default=None, description="Session identifier") class InterfaceCapabilities(BaseModel): """Capabilities of an interface adapter.""" supported_modalities: list[ModalityType] = Field(description="Supported sensory modalities") supports_streaming: bool = Field(default=False, description="Supports streaming responses") supports_interruption: bool = Field(default=False, description="Supports mid-response interruption") supports_multimodal: bool = Field(default=False, description="Supports multiple modalities simultaneously") latency_ms: float | None = Field(default=None, description="Expected latency in milliseconds") max_concurrent_sessions: int | None = Field(default=None, description="Max concurrent sessions") class InterfaceAdapter(ABC): """ Abstract base for interface adapters. Interface adapters translate between human sensory modalities and FusionAGI's internal message format. Each adapter handles one or more modalities (voice, visual, haptic, etc.). """ def __init__(self, name: str) -> None: self.name = name @abstractmethod def capabilities(self) -> InterfaceCapabilities: """Return the capabilities of this interface.""" ... @abstractmethod async def send(self, message: InterfaceMessage) -> None: """ Send a message through this interface to the user. Args: message: Message to send (modality-specific content). """ ... @abstractmethod async def receive(self, timeout_seconds: float | None = None) -> InterfaceMessage | None: """ Receive a message from the user through this interface. Args: timeout_seconds: Optional timeout for receiving. Returns: Received message or None if timeout. """ ... async def stream_send(self, messages: AsyncIterator[InterfaceMessage]) -> None: """ Stream messages to the user (for streaming responses). Default implementation sends each message individually. Override for true streaming support. Args: messages: Async iterator of messages to stream. """ async for msg in messages: await self.send(msg) async def initialize(self) -> None: """Initialize the interface (connect, authenticate, etc.).""" pass async def shutdown(self) -> None: """Shutdown the interface gracefully.""" pass def validate_message(self, message: InterfaceMessage) -> bool: """ Validate that a message is compatible with this interface. Args: message: Message to validate. Returns: True if valid, False otherwise. """ caps = self.capabilities() return message.modality in caps.supported_modalities