"""WebSocket manager for broadcasting real-time evaluation progress.""" import json from collections import defaultdict from typing import Any from fastapi import WebSocket class ConnectionManager: """Manages WebSocket connections grouped by run_id.""" def __init__(self): self._connections: dict[str, list[WebSocket]] = defaultdict(list) async def connect(self, run_id: str, websocket: WebSocket): await websocket.accept() self._connections[run_id].append(websocket) def disconnect(self, run_id: str, websocket: WebSocket): conns = self._connections.get(run_id, []) if websocket in conns: conns.remove(websocket) if not conns: self._connections.pop(run_id, None) async def broadcast(self, run_id: str, data: dict[str, Any]): conns = self._connections.get(run_id, []) message = json.dumps(data, ensure_ascii=False, default=str) dead = [] for ws in conns: try: await ws.send_text(message) except Exception: dead.append(ws) for ws in dead: self.disconnect(run_id, ws) async def emit(self, run_id: str, event: str, data: dict[str, Any]) -> None: """Async progress-callback compatible with EvalEngine's awaitable callback.""" await self.broadcast(run_id, {"event": event, **data}) ws_manager = ConnectionManager()