11 KiB
DashScope 风格兼容对话 API v1 Spec
| 项 | 内容 |
|---|---|
| 状态 | Implemented;已增加拒绝请求的 Origin 诊断日志 |
| 日期 | 2026-09-07 |
| 负责人 | 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: resultSSE;首个事件提供会话 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_ALLOWED_ORIGINS |
空 | 逗号分隔的精确 HTTP(S) 浏览器 Origin;不允许 *、非根路径、查询或片段;单个末尾 / 会被规范化移除 |
兼容路径只有在 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. 请求契约
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:
{
"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:
id: 1
event: result
:HTTP_STATUS/200
data: {"output":{"session_id":"conv_...","finish_reason":"null"},"usage":{},"request_id":"..."}
只有 SuperAgent Adapter 同时验证最终内容、成功 run.completed 和顶层 end 后,才发送终止事件:
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:
{"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 上游失败或超时。 兼容入口的 403 响应仍只返回稳定错误 JSON;服务端日志在拒绝时提供有限诊断信息,便于确认发送方应加入哪一个精确 Origin, 但不会因为诊断而放宽鉴权或预检规则。
8. CORS、Nginx 与 Secret
- 浏览器 Origin 必须精确出现在
FIRE_SAFETY_CHAT_ALLOWED_ORIGINS;多个值用英文逗号分隔,不允许*或 credentials。 - 白名单值的规范形式是
scheme://host[:port],不能带非根路径、查询或片段;单个末尾/会被接受并规范化移除,配置时建议省略。不要把 API URL、Nginx 上游地址或 Token 当作 Origin。 - 预检只允许
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 总运行时限。
8.1 403 Origin 诊断日志
兼容入口只在拒绝场景记录诊断字段:普通 POST 的非空 Origin 不在白名单时记录
result=forbidden_origin 与 origin;CORS 预检拒绝时记录 result=preflight_forbidden、
origin、preflight_method、preflight_headers 和稳定 reason。reason 取值为
origin_missing、origin_not_allowed、method_not_allowed 或 headers_not_allowed。
每个请求 Header 最多保留前 256 个输入字节,超长值追加 [truncated] 后再引用/ASCII 转义,
以确保恶意换行不能伪造日志记录。成功请求不记录 Origin。日志禁止记录 xtoken、Authorization、Cookie、
prompt/body、会话 ID、Provider Session、Provider payload 或其他 Secret。
运维人员只能把日志中的完整 origin 当作排障线索,不能直接执行或无审查复制;若出现 [truncated],应从浏览器
Network 面板确认完整 Origin。确认后再把 scheme://host[:port] 原样加入
FIRE_SAFETY_CHAT_ALLOWED_ORIGINS,多个来源用英文逗号分隔;禁止 *。修改代码需要重新 build 并 recreate,
仅修改环境配置也需要 recreate,单独 restart 不会让运行容器读取新值。
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 字节上限和 ASCII0x21-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。
- Given 普通请求的 Origin 不在白名单,When 兼容入口返回 403,Then 日志记录有界、引用/转义后的
origin,但不记录 Token、Cookie、prompt/body、会话或 Provider 数据。 - Given CORS 预检因来源、方法或请求头名称集合被拒绝,When 返回 403,Then 日志记录有界、引用/转义后的
origin、preflight_method、preflight_headers和稳定reason,且恶意换行不能增加日志行数。 - Given Origin 白名单值被更新,When 服务重新加载配置,Then 精确
scheme://host[:port]值生效,单个末尾/被规范化移除,而*、非根路径、查询和片段不得被接受。
10. 协议来源
- Alibaba Cloud Model Studio Application API reference
- Call a Model Studio application by using an API
外部文档会演进;本项目以本 Spec 和自动化测试定义的受限兼容子集为准。