mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-10-30 11:54:50 +08:00
69 lines
2.0 KiB
Python
69 lines
2.0 KiB
Python
"""
|
||
SSE 事件编码、缓存控制与流式响应构造。
|
||
"""
|
||
|
||
import json
|
||
from collections.abc import AsyncIterable, Iterable
|
||
from typing import Any
|
||
|
||
from fastapi.responses import StreamingResponse
|
||
|
||
SSE_HEADERS = {
|
||
"Cache-Control": "no-cache, no-transform",
|
||
"Connection": "keep-alive",
|
||
"X-Accel-Buffering": "no",
|
||
}
|
||
|
||
|
||
def encode_data_event(payload: dict[str, Any]) -> str:
|
||
"""
|
||
把载荷编码为 OpenAI 使用的无名称 SSE 数据事件。
|
||
|
||
:param payload: 可序列化为 JSON 的数据映射
|
||
:return: 以空行结束的 data 事件文本,保留中文字符
|
||
"""
|
||
return f"data: {json.dumps(payload, ensure_ascii=False)}\n\n"
|
||
|
||
|
||
def encode_named_event(event: str, payload: dict[str, Any]) -> str:
|
||
"""
|
||
把事件名和载荷编码为一个 SSE 事件,不增加业务响应包装。
|
||
|
||
:param event: 协议定义的事件名称,由调用方提供
|
||
:param payload: 可序列化为 JSON 的事件数据
|
||
:return: 包含 event 和 data 行、以空行结束的事件文本
|
||
"""
|
||
return (
|
||
f"event: {event}\n"
|
||
f"data: {json.dumps(payload, ensure_ascii=False)}\n\n"
|
||
)
|
||
|
||
|
||
def build_sse_response(
|
||
content: AsyncIterable[str] | Iterable[str],
|
||
*,
|
||
headers: dict[str, str] | None = None,
|
||
) -> StreamingResponse:
|
||
"""
|
||
构造禁用代理缓冲的 SSE 响应。
|
||
|
||
:param content: 同步或异步事件文本迭代器
|
||
:param headers: 协议附加响应头;同名项覆盖默认头,None 表示使用默认值
|
||
:return: 交给 HTTP 框架消费和关闭的流式响应
|
||
"""
|
||
return StreamingResponse(
|
||
content,
|
||
media_type="text/event-stream",
|
||
headers={**SSE_HEADERS, **(headers or {})},
|
||
)
|
||
|
||
|
||
def build_sse_error_response(payload: str) -> StreamingResponse:
|
||
"""
|
||
用统一的流式传输策略返回单个错误事件。
|
||
|
||
:param payload: 已按对应协议编码的完整 SSE 错误事件
|
||
:return: 只产生该事件的流式响应
|
||
"""
|
||
return build_sse_response(iter([payload]))
|