"""Abstract base class for message channels.""" from abc import ABC, abstractmethod from dataclasses import dataclass from datetime import datetime from typing import Any, Optional @dataclass class ChannelHealth: ok: bool message: str = "" @dataclass class SendResult: ok: bool question_msg_id: Optional[str] = None raw_response: Optional[dict[str, Any]] = None error: Optional[str] = None @dataclass class Reply: question_msg_id: str content: Any sender_name: Optional[str] = None msg_time: Optional[datetime] = None raw_message: Optional[dict[str, Any]] = None class EvalChannel(ABC): """Abstract message channel used to interact with an evaluation target. All methods are async so push-based channels (WebSocket, SSE) and poll-based channels (REST) share the same interface. """ @abstractmethod async def health_check(self) -> ChannelHealth: """Verify the channel can reach the target.""" ... @abstractmethod async def send(self, content: str, **kwargs: Any) -> SendResult: """Send a message to the target and return its question message id.""" ... @abstractmethod async def poll_reply( self, question_msg_id: str, timeout: float = 30.0, poll_interval: float = 1.0, ) -> Optional[Reply]: """Wait for a reply to a previously sent message. Implementations may poll (REST) or await a push event (WebSocket). """ ...