149 lines
8.4 KiB
Markdown
149 lines
8.4 KiB
Markdown
# DashScope 风格兼容对话 API v1 Spec
|
||
|
||
| 项 | 内容 |
|
||
| --- | --- |
|
||
| 状态 | Implemented |
|
||
| 日期 | 2026-09-05 |
|
||
| 负责人 | fire-safety-ymd 后端 |
|
||
| 需求来源 | 复用既有客户端的 `/api/v1/apps/{app_id}/completion`、`xtoken` 与 `event: result` 契约 |
|
||
|
||
## 1. 背景
|
||
|
||
既有用户端按照 DashScope Application HTTP 形式调用固定应用路径,并以 SSE `result` 事件读取 `output.session_id`、`finish_reason` 和 `text`。本项目原生 `/api/chat` 的请求体与事件名称不同。为避免客户端一次性重写,本 checkpoint 在同一 Chat Service 之上增加一个受限兼容适配层,并保留原生接口。
|
||
|
||
本契约参考阿里云官方 Application API 的路径、`input.prompt`、`input.session_id` 和 SSE 结果外形,但不是 DashScope 全量代理或完整复刻。
|
||
|
||
## 2. 目标
|
||
|
||
- 新增可选的 `POST /api/v1/apps/{app_id}/completion`。
|
||
- 接收既有客户端的 `xtoken`、`input.prompt`、可选 `input.session_id` 和 `parameters` 空对象。
|
||
- 返回 `event: result` SSE;首个事件提供会话 ID,严格成功事件以 `finish_reason: "stop"` 终止。
|
||
- 复用 `/api/chat` 的会话容量、TTL、同会话并发排斥、SuperAgent 严格完成判定和错误分类。
|
||
- Nginx 只终止 TLS、限制路径、限流并反代本 Go 服务,不保存或注入任何应用 Secret。
|
||
|
||
## 3. 非目标与兼容边界
|
||
|
||
- 不移除或改变原生 `POST /api/chat`。
|
||
- 不实现 DashScope 的 `messages`、`biz_params`、文件、图像、知识库、思考过程或任意 `parameters`。
|
||
- 不代理到 DashScope,也不把兼容 `app_id` 当成真正的 SuperAgent Profile ID。
|
||
- 不向客户端暴露 Provider Session、Run、Trace、工具参数/结果或半截回答。
|
||
- 不提供非流式 JSON 模式;即使请求未带 `X-DashScope-SSE: enable`,本兼容路径也始终返回 SSE,以匹配现有客户端。
|
||
- `parameters.incremental_output` 仅为输入兼容而接受。v1 只有严格完成后的一个正文结果,因此 `true` 与 `false` 不改变正文分片方式。
|
||
- 静态 `xtoken` 只用于受控联调,不是最终用户身份或动态授权。
|
||
|
||
## 4. 配置与路由
|
||
|
||
| 环境变量 | 默认值 | 说明 |
|
||
| --- | --- | --- |
|
||
| `FIRE_SAFETY_CHAT_COMPAT_APP_ID` | 空 | 非空时注册兼容路径;1 至 128 个 ASCII 字母、数字、下划线或连字符 |
|
||
| `FIRE_SAFETY_CHAT_AUTH_TOKEN` | 空 | 兼容路径期望的 `xtoken`;默认至少 32 个可打印 ASCII 字符,兼容开关开启时允许已交付的旧短凭证 |
|
||
| `FIRE_SAFETY_CHAT_ALLOW_LEGACY_SHORT_TOKEN` | `false` | 仅为已交付旧客户端短凭证的受控测试/迁移临时兼容开关;新环境不得开启 |
|
||
|
||
兼容路径只有在 `FIRE_SAFETY_CHAT_ENABLED=true` 且 App ID 非空时注册。App ID 是用于路径匹配的公开标识,不是 Secret;不匹配的路径返回 404。
|
||
|
||
`FIRE_SAFETY_CHAT_ALLOW_LEGACY_SHORT_TOKEN` 默认为 `false`。只有在旧客户端已经拿到、且无法立即更换的短 `xtoken` 时,才可在受控测试或迁移窗口显式设置为 `true`。无论开关取值如何,Chat 凭证都必须非空、不超过 4096 字节,并且每个字符都在 ASCII `0x21-0x7e` 范围内(不得包含空格、控制字符或 Unicode);开关同时适用于原生 `/api/chat` 的 Bearer 和本兼容路径的 `xtoken`,不放宽 MCP Token 至少 32 个可打印 ASCII 字符的门禁。Chat、SuperAgent Open API 和 MCP 三种凭证仍必须使用不同值。凭证轮换完成后必须恢复 `false`,新环境不得开启该开关。开关启用时,服务启动日志会记录不含 Secret 的安全 warning,便于后续清理。
|
||
|
||
## 5. 请求契约
|
||
|
||
```http
|
||
POST /api/v1/apps/fire-safety-public-app/completion
|
||
xtoken: <FIRE_SAFETY_CHAT_AUTH_TOKEN>
|
||
Content-Type: application/json
|
||
Accept: text/event-stream
|
||
|
||
{
|
||
"input": {
|
||
"prompt": "杨家盘瞭望哨 3 公里内的水源?"
|
||
},
|
||
"parameters": {}
|
||
}
|
||
```
|
||
|
||
后续轮次把前一轮成功响应的 `output.session_id` 放回 `input`:
|
||
|
||
```json
|
||
{
|
||
"input": {
|
||
"prompt": "再说明这些候选的限制",
|
||
"session_id": "conv_..."
|
||
},
|
||
"parameters": {
|
||
"incremental_output": true
|
||
},
|
||
"debug": {}
|
||
}
|
||
```
|
||
|
||
约束:
|
||
|
||
- `input.prompt` 必填,非空,并服从现有消息大小上限。
|
||
- `input.session_id` 只能是本服务此前返回的本地会话 ID;服务端仍负责映射 Provider Session。
|
||
- `parameters` 可省略、为空对象,或只包含布尔型 `incremental_output`。
|
||
- `debug` 可省略或只能是空对象。
|
||
- 未知字段、`null`、错误类型、尾随 JSON 和超大请求体均拒绝。
|
||
- 兼容 DTO 不接收用户身份、角色、区域、租户、Provider Session 或 metadata。
|
||
|
||
## 6. 成功 SSE
|
||
|
||
Prepare 成功后先发送会话事件。`finish_reason` 的初始值按官方示例使用字符串 `"null"`,不是 JSON `null`:
|
||
|
||
```text
|
||
id: 1
|
||
event: result
|
||
:HTTP_STATUS/200
|
||
data: {"output":{"session_id":"conv_...","finish_reason":"null"},"usage":{},"request_id":"..."}
|
||
```
|
||
|
||
只有 SuperAgent Adapter 同时验证最终内容、成功 `run.completed` 和顶层 `end` 后,才发送终止事件:
|
||
|
||
```text
|
||
id: 2
|
||
event: result
|
||
:HTTP_STATUS/200
|
||
data: {"output":{"session_id":"conv_...","finish_reason":"stop","text":"..."},"usage":{"models":[{"input_tokens":12,"output_tokens":34,"model_id":"qwen-plus-latest"}]},"request_id":"..."}
|
||
```
|
||
|
||
- 两个事件的 `session_id` 和 `request_id` 必须相同。
|
||
- `session_id` 是本地随机会话 ID,不是 Provider Session。
|
||
- `model_id` 在上游未提供有效模型名时省略;token 数量是当前 Provider 返回的单轮聚合值。
|
||
- 等待期间每 15 秒可发送不含业务数据的 SSE comment heartbeat。
|
||
- 客户端只有看到 `event: result` 且 `output.finish_reason == "stop"` 才能把本轮视为成功。
|
||
|
||
## 7. 错误
|
||
|
||
SSE 开始前使用 HTTP 状态和 DashScope 风格安全 JSON:
|
||
|
||
```json
|
||
{"code":"CHAT_AUTH_INVALID","message":"Chat authentication failed.","request_id":"..."}
|
||
```
|
||
|
||
SSE 开始后的失败发送 `event: error`,只包含稳定 code、通用 message、request ID 和本地 session ID;不发送 `finish_reason: "stop"`,也不返回任何已接收的部分回答。
|
||
|
||
沿用原生 Chat 的主要状态:400 输入错误、401 token 错误、403 Origin 错误、404 App/会话不存在、409 会话忙、503 容量不足,以及 502/504 上游失败或超时。
|
||
|
||
## 8. CORS、Nginx 与 Secret
|
||
|
||
- 浏览器 Origin 必须精确出现在 `FIRE_SAFETY_CHAT_ALLOWED_ORIGINS`;不允许 `*` 或 credentials。
|
||
- 预检只允许 `POST` 以及 `Content-Type`、`xtoken`、`X-DashScope-SSE`、`X-Request-ID`。
|
||
- Nginx 示例只公开精确兼容路径、`/mcp` 和可选 `/health`,其余路径返回 404。
|
||
- Nginx 不比较或注入 `xtoken`、SuperAgent Open API Key、MCP Bearer 或数据库凭证;Header 原样交给 Go 验证。
|
||
- 对话 SSE 必须关闭代理缓冲、缓存、gzip 和上游自动重试,并让代理超时覆盖 Chat 总运行时限。
|
||
|
||
## 9. 验收标准
|
||
|
||
- Given App ID 未配置,When 请求兼容路径,Then 路由返回 404,原生 `/api/chat` 行为不变。
|
||
- Given App ID 或 `xtoken` 错误,When 请求,Then 在读取问题和调用 SuperAgent 前拒绝。
|
||
- Given `FIRE_SAFETY_CHAT_ALLOW_LEGACY_SHORT_TOKEN` 未开启,When 已交付的旧 `xtoken` 少于 32 个字符,Then 服务启动配置校验失败。
|
||
- Given `FIRE_SAFETY_CHAT_ALLOW_LEGACY_SHORT_TOKEN=true`,When 旧短 `xtoken` 用于受控测试/迁移,Then 兼容入口允许该凭证但仍执行非空、4096 字节上限和 ASCII `0x21-0x7e` 校验,并记录不含 Secret 的安全 warning;MCP Token 的至少 32 字符门禁不变。
|
||
- Given 首轮请求成功,When 读取 SSE,Then 先收到字符串 `"null"` 会话事件,再收到同会话 `"stop"` 最终正文。
|
||
- Given 后续请求携带成功返回的 `session_id`,When 调用,Then 复用同一服务端会话映射。
|
||
- Given Provider 流失败,When SSE 已开始,Then 收到安全 `error`,不收到部分正文或 `stop`。
|
||
- Given Nginx 配置生效,When 请求未列出的路径,Then 不会转发到 Go 或外部 DashScope。
|
||
|
||
## 10. 协议来源
|
||
|
||
- [Alibaba Cloud Model Studio Application API reference](https://www.alibabacloud.com/help/en/model-studio/application-api-reference)
|
||
- [Call a Model Studio application by using an API](https://www.alibabacloud.com/help/en/model-studio/application-calling-guide)
|
||
|
||
外部文档会演进;本项目以本 Spec 和自动化测试定义的受限兼容子集为准。
|