初始化提交
This commit is contained in:
commit
887c1f449e
963 files changed
+166869
No files matched your search
@@ -0,0 +1,55 @@
|
||||
"""进程内 SSE 事件总线。
|
||||
|
||||
后台任务 ``append_event`` 写入 DB(持久化)后,同时 publish 到本总线;SSE generator
|
||||
从内存队列实时读取并转发给前端,避免轮询 DB 带来的延迟与开销。
|
||||
|
||||
约束:仅进程内有效,进程重启即失。多进程 / 多副本部署需替换为 Redis Pub/Sub 等共享
|
||||
通道;本地开发(单进程 uvicorn)适用。事件历史仍以 DB 为持久事实源,总线仅承担实时转发。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections import defaultdict
|
||||
|
||||
from app.schemas.runtime import EventResponse
|
||||
|
||||
_TERMINAL_EVENT_TYPES = {"run.completed", "run.failed", "run.cancelled"}
|
||||
|
||||
|
||||
class RunEventBus:
|
||||
"""按 run_id 缓存事件并广播给订阅者。"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._events: dict[str, list[EventResponse]] = defaultdict(list)
|
||||
self._subscribers: dict[str, list[asyncio.Queue[EventResponse]]] = defaultdict(list)
|
||||
|
||||
def publish(self, run_id: str, event: EventResponse) -> None:
|
||||
self._events[run_id].append(event)
|
||||
for queue in self._subscribers.get(run_id, []):
|
||||
queue.put_nowait(event)
|
||||
|
||||
def list_after(self, run_id: str, *, after_sequence: int) -> list[EventResponse]:
|
||||
"""返回 run 内 sequence 大于 after_sequence 的事件(用于断线重连补发)。"""
|
||||
events = self._events.get(run_id, [])
|
||||
if after_sequence <= 0:
|
||||
return list(events)
|
||||
return [event for event in events if event.sequence > after_sequence]
|
||||
|
||||
def subscribe(self, run_id: str) -> asyncio.Queue[EventResponse]:
|
||||
queue: asyncio.Queue[EventResponse] = asyncio.Queue()
|
||||
self._subscribers[run_id].append(queue)
|
||||
return queue
|
||||
|
||||
def unsubscribe(self, run_id: str, queue: asyncio.Queue[EventResponse]) -> None:
|
||||
subscribers = self._subscribers.get(run_id)
|
||||
if subscribers is None:
|
||||
return
|
||||
self._subscribers[run_id] = [q for q in subscribers if q is not queue]
|
||||
|
||||
@staticmethod
|
||||
def is_terminal(event: EventResponse) -> bool:
|
||||
return event.type in _TERMINAL_EVENT_TYPES
|
||||
|
||||
|
||||
bus = RunEventBus()
|
||||
Reference in new issue
Block a user