appQQQ's picture
chore: upload app/streaming/sse.py
ccad007 verified
Raw
History Blame Contribute Delete
1.08 kB
"""SSE generator 辅助. 提供 ping / retry / error 处理."""
from __future__ import annotations
import asyncio
import json
import logging
from collections.abc import AsyncIterator
from app.streaming.events import sse_format, sse_heartbeat
logger = logging.getLogger(__name__)
async def heartbeat_pinger(interval: float = 15.0) -> AsyncIterator[str]:
"""无限流式发送 SSE 注释保活. 配合 `asyncio.Queue` 一起使用."""
try:
while True:
await asyncio.sleep(interval)
yield sse_heartbeat()
except asyncio.CancelledError:
return
async def event_stream(
queue: asyncio.Queue[tuple[str, dict] | None],
) -> AsyncIterator[str]:
"""从 asyncio.Queue 读取事件并序列化为 SSE. 收到 None 表示结束."""
while True:
item = await queue.get()
if item is None:
break
event, data = item
yield sse_format(event, data)
def safe_json(data: dict) -> str:
"""防御性 JSON 序列化."""
return json.dumps(data, ensure_ascii=False, default=str)