Files
fire-safety-ymd/docs/specs/fire-safety-ymd-dashscope-compatible-chat-v1.md
T
2026-09-05 17:36:23 +08:00

8.4 KiB
Raw Blame History

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. 请求契约

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 上游失败或超时。

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. 协议来源

外部文档会演进;本项目以本 Spec 和自动化测试定义的受限兼容子集为准。