modify: 流式输出

This commit is contained in:
2026-08-21 14:40:36 +08:00
parent 94c50353f4
commit cc8713d5e7
8 changed files with 1132 additions and 23 deletions
+92
View File
@@ -264,6 +264,93 @@ async def upload(file: UploadFile = File(...)):
> **约定**:插件 `__init__.py` 中导入 `router` 实例(`from .routes import router`),网关通过 `getattr(module, "router", None)` 自动发现。
### 流式端点(SSE)
基于 Zenoh Query/Reply 原生"多回复"实现流式输出:Worker 的 `@rpc_method` 返回生成器时,
框架逐块 `query.reply()` 推送(见 `utils/worker` README「流式端点」),网关以 SSE
(`text/event-stream`)逐块转发给 HTTP 客户端。**无额外端口、无额外进程、无新依赖**;
未声明 `stream` 的端点行为与之前完全一致。
#### TOML 配置
在方法节中声明 `stream = true`(示例 `config/workers/example/stream_chat.toml`):
```toml
[POST]
summary = "模拟 AI 流式对话(SSE,逐 token 输出)"
auth_required = false
stream = true
timeout = 300.0 # 流总寿命上限(秒,zenoh query deadline 硬上限)
stream_idle_timeout = 10.0 # 网关侧连续无数据块的最大间隔(秒)
params = ["prompt: str", "tokens: int"]
```
| 字段 | 默认值 | 说明 |
| --- | --- | --- |
| `stream` | `false` | 流式端点:以 SSE(`text/event-stream`)输出 |
| `timeout` | `3.0` | 流总寿命上限(秒)。**流式端点必须配大**(如 300s),到期后 zenoh 丢弃后续回复 |
| `stream_idle_timeout` | `10.0` | 空闲超时(秒):超过该间隔无新数据块则网关终止流 |
> `timeout`(总寿命硬上限)与 `stream_idle_timeout`(空闲超时)是两回事,流式场景两个都要配。
#### 请求与响应
- 请求与普通端点一致(GET 走 query/path 参数,POST/PUT/PATCH 走 body 模型),鉴权依赖同样生效
- 响应为 `text/event-stream`,每块一个 `data: {json}\n\n` 事件(SSE 标准分帧)
- 响应头:`Cache-Control: no-cache`、`X-Accel-Buffering: no`(配合反代关闭缓冲)
- OpenAPI 不声明流式端点的响应 schema(逐块 JSON dict,无法用单一模型描述)
```bash
curl -N -X POST localhost:8000/api/v1/calc/stream_chat \
-H 'Content-Type: application/json' -d '{"prompt":"你好","tokens":3}'
# data: {"token":"你好·1","index":0}
# data: {"token":"你好·2","index":1}
# data: {"token":"你好·3","index":2}
# data: {"done":true}
```
#### 错误语义
| 场景 | 表现 |
| --- | --- |
| 无可用 worker(首个数据块前) | HTTP 503(与普通端点一致) |
| Worker 中途异常 / 流式端点初始化失败 | SSE error 事件 `data: {"error":"..."}` 收尾(HTTP 200 已提交,无法再改状态码) |
| 空闲超时 | SSE error 事件 `data: {"error":"流式空闲超时 ..."}` 收尾 |
| 客户端断开 | 网关终止迭代,Worker 侧 reply 失败自动停流,不空转 |
#### 部署注意
生产环境经 nginx 反代时需关闭缓冲,否则 SSE 会被整段缓冲、失去逐块即时性:
```nginx
location /api/v1/ {
proxy_buffering off;
}
```
#### 本地联调前注意
流式能力在 `chongming-worker` 框架(`utils/worker/`,独立仓库)。**本地未 push 的框架改动
不会自动生效**——venv 里安装的是 `uv sync` 从 git 源拉取的旧版副本,旧版 `@rpc_method`
不认 `stream` 参数,会报 `TypeError: rpc_method() got an unexpected keyword argument 'stream'`
并连锁导致契约校验失败(启动终止)。本地联调时先以可编辑方式装入 venv:
```bash
uv pip install -e utils/worker
```
正式发布仍走「开发与发布检查清单」流程(工具包 push → `uv lock --upgrade-package
chongming-worker` → `uv sync`,可编辑安装会被锁文件版本替换)。
#### 测试
```
uv run python test_stream_worker.py # Worker 框架流式回复单测
uv run python test_stream_gateway.py # 网关 call_stream 单测
uv run python test_stream_e2e_zenoh.py # 真实 Zenoh 会话端到端(peer 模式,无需 router)
uv run python test_stream_route.py # 路由层 SSE 输出与兼容性回归(非流式端点不变)
```
---
## 进程插件与混合部署
@@ -450,6 +537,9 @@ python publisher.py example add '{"a":3,"b":5}' # 调用 add
5. 重启服务
```
流式端点(SSE)在此基础上额外两步:TOML 方法节声明 `stream = true` 并配大 `timeout`;
`@rpc_method(..., stream=True)` 函数返回生成器(yield 逐块),详见上文「流式端点(SSE)」。
### 常用命令
```bash
@@ -566,6 +656,8 @@ uv pip install -e utils/<name> # 安装/更新工具包子包
| `internal` | `<操作>.toml` | `true` 则仅限内部 RPC,不注册 HTTP 路由 |
| `cacheable` | `<操作>.toml` | 端点是否可缓存 |
| `summary` | `<操作>.toml` | 端点描述(用于 OpenAPI / 日志) |
| `stream` | `<操作>.toml` | `true` 则流式输出(SSE),需同时配大 `timeout` |
| `stream_idle_timeout`| `<操作>.toml` | 流式空闲超时(秒,默认 10),超时无新数据块则终止流 |
---