This commit is contained in:
kacper 2026-03-04 08:20:42 -05:00
parent 133b557512
commit ed629ff60e
7 changed files with 948 additions and 525 deletions

View file

@ -1,8 +1,20 @@
import asyncio
import contextlib
import os
from dataclasses import dataclass, field
from datetime import datetime, timezone
def _positive_int_env(name: str, default: int) -> int:
raw_value = os.getenv(name, "").strip()
if not raw_value:
return default
try:
return max(1, int(raw_value))
except ValueError:
return default
@dataclass(slots=True)
class WisperEvent:
role: str
@ -19,19 +31,29 @@ class WisperBus:
def __init__(self) -> None:
self._subscribers: set[asyncio.Queue[WisperEvent]] = set()
self._lock = asyncio.Lock()
self._subscriber_snapshot: tuple[asyncio.Queue[WisperEvent], ...] = ()
self._subscriber_queue_size = _positive_int_env(
"WISPER_SUBSCRIBER_QUEUE_SIZE", 512
)
async def subscribe(self) -> asyncio.Queue[WisperEvent]:
queue: asyncio.Queue[WisperEvent] = asyncio.Queue()
queue: asyncio.Queue[WisperEvent] = asyncio.Queue(
maxsize=self._subscriber_queue_size
)
async with self._lock:
self._subscribers.add(queue)
self._subscriber_snapshot = tuple(self._subscribers)
return queue
async def unsubscribe(self, queue: asyncio.Queue[WisperEvent]) -> None:
async with self._lock:
self._subscribers.discard(queue)
self._subscriber_snapshot = tuple(self._subscribers)
async def publish(self, event: WisperEvent) -> None:
async with self._lock:
subscribers = list(self._subscribers)
for queue in subscribers:
queue.put_nowait(event)
for queue in self._subscriber_snapshot:
if queue.full():
with contextlib.suppress(asyncio.QueueEmpty):
queue.get_nowait()
with contextlib.suppress(asyncio.QueueFull):
queue.put_nowait(event)