37 lines
1.3 KiB
Python
37 lines
1.3 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
from collections import defaultdict
|
|
|
|
from app.core.contracts import RuntimeEvent
|
|
from app.events.event_bus import EventBus
|
|
|
|
|
|
class StreamingManager:
|
|
"""Simple in-process projection from event bus to websocket consumers."""
|
|
|
|
def __init__(self, event_bus: EventBus) -> None:
|
|
self._event_bus = event_bus
|
|
self._subscribers: dict[str, list[asyncio.Queue[RuntimeEvent]]] = defaultdict(list)
|
|
self._event_bus.subscribe(self._on_event)
|
|
|
|
def replay_events(self, task_id: str) -> list[RuntimeEvent]:
|
|
return self._event_bus.list_for_task(task_id)
|
|
|
|
def subscribe(self, task_id: str) -> asyncio.Queue[RuntimeEvent]:
|
|
queue: asyncio.Queue[RuntimeEvent] = asyncio.Queue()
|
|
self._subscribers[task_id].append(queue)
|
|
return queue
|
|
|
|
def unsubscribe(self, task_id: str, queue: asyncio.Queue[RuntimeEvent]) -> None:
|
|
listeners = self._subscribers.get(task_id, [])
|
|
if queue in listeners:
|
|
listeners.remove(queue)
|
|
if not listeners and task_id in self._subscribers:
|
|
del self._subscribers[task_id]
|
|
|
|
def _on_event(self, event: RuntimeEvent) -> None:
|
|
for queue in self._subscribers.get(event.task_id, []):
|
|
queue.put_nowait(event)
|
|
|