Files
lijiaoqiao/docs/subapi_connector_contract_v1_2026-03-17.md
2026-03-26 16:04:46 +08:00

9.2 KiB
Raw Blame History

Subapi Connector 契约清单 v1

  • 版本v1.0
  • 日期2026-03-17
  • 适用阶段S1-S2subapi 外部服务模块化接入 + 客户迁移)
  • 契约目标:为我方 Router Core 与 subapi 之间的调用定义“稳定接口 + 稳定语义 + 可回归验证”。

1. 设计目标与边界

1.1 目标

  1. subapi 视为“可替换外部能力模块”,而不是核心业务真相源。
  2. 固定北向协议与字段语义,避免上游快速迭代导致我方主路径抖动。
  3. 对请求、响应、错误、流式事件建立统一归一模型,支撑审计、计费、告警。

1.2 非目标

  1. 不在本契约覆盖 subapi 后台管理 API/admin/*)。
  2. 不在本契约定义我方控制面业务模型租户、账务、RBAC的内部存储结构。
  3. 不将 subapi 私有实现细节(调度算法内部参数)暴露到我方公共 API。

2. 接入范围(协议与端点)

2.1 Canonical 端点Connector 只使用这一组)

协议域 Method Path 说明
Anthropic 兼容 POST /v1/messages 统一消息入口
Anthropic 兼容 POST /v1/messages/count_tokens 仅计数,不计费记录
OpenAI 兼容 POST /v1/chat/completions Chat Completions
OpenAI 兼容 POST /v1/responses Responses
OpenAI 兼容 POST /v1/responses/*subpath Responses 子资源
OpenAI 兼容WS GET /v1/responses WebSocket 升级入口
通用 GET /v1/models 模型目录
通用 GET /v1/usage 用量/额度信息
Gemini 原生 GET /v1beta/models 模型列表
Gemini 原生 GET /v1beta/models/:model 模型详情
Gemini 原生 POST /v1beta/models/*modelAction generateContent/streamGenerateContent

2.2 Alias 端点处理

  1. subapi 提供不带 /v1 的别名(如 /responses/chat/completions)。
  2. Connector 禁止调用 alias统一走 canonical 端点,避免路由歧义。

3. 认证与请求头契约

3.1 认证头优先级

  1. /v1/*
    • Authorization: Bearer <key> > x-api-key > x-goog-api-key
  2. /v1beta/*
    • x-goog-api-key > Authorization: Bearer <key> > x-api-key > key(query仅兼容)

3.2 禁止项

  1. 禁止通过 query 传 key/api_key(仅保留兼容读取,不作为标准路径)。
  2. Connector 默认不发送 query key。

3.3 会话亲和相关头

  1. OpenAI 兼容:支持 session_idconversation_id,请求体支持 prompt_cache_key
  2. Gemini CLI支持 x-gemini-api-privileged-user-id + 请求体 tmp 路径哈希会话识别。
  3. Anthropic 兼容:可使用 metadata.user_id 补充分组内会话亲和。

3.4 北向/南向边界(安全强约束,新增)

  1. 北向(客户 -> 我方网关):
    • 禁止接收任何 query keykey/api_key),统一要求 header 鉴权。
    • 外部携带 query key 的请求必须被拒绝并记录审计事件。
  2. 南向(我方网关 -> subapi connector
    • 仅允许 header 方式传递凭证(Authorization/x-api-key/x-goog-api-key)。
    • Connector 默认不透传 query key。
  3. 历史兼容策略:
    • 若为极少数遗留客户端保留“内部改写”能力,必须在北向入口完成 query->header 改写,且该兼容规则需要白名单与审计日志。
    • 该兼容规则默认关闭,并纳入版本化淘汰计划。

4. 请求体契约(最小强约束)

4.1 通用约束

  1. Body 必须是非空 JSON。
  2. model 必须为非空字符串。
  3. stream 若出现,必须是布尔值。

4.2 OpenAI Responses 额外约束

  1. previous_response_id 若存在,必须是 resp_* 风格,不可传 message id。
  2. input.type=function_call_output 场景必须满足 call 上下文约束(否则应返回 400 invalid_request_error)。

4.3 Gemini Action 路径约束

  1. *modelAction 支持两种格式:{model}:{action}{model}/{action}
  2. streamGenerateContent 视为流式请求。

5. 响应归一契约Connector 内部模型)

Connector 必须将不同协议响应归一为以下内部结构:

{
  "protocol": "openai|anthropic|gemini",
  "request_id": "string",
  "upstream_request_id": "string",
  "model_requested": "string",
  "model_actual": "string",
  "stream": true,
  "usage": {
    "input_tokens": 0,
    "output_tokens": 0,
    "cache_creation_input_tokens": 0,
    "cache_read_input_tokens": 0,
    "total_tokens": 0
  },
  "first_token_ms": 0,
  "duration_ms": 0,
  "client_disconnect": false
}

5.1 字段规则

  1. upstream_request_id:优先从响应头 x-request-id 提取。
  2. request_id:若协议返回 body request id则保留否则由 Connector 生成稳定 ID。
  3. usage.total_tokens:若上游未提供,按 input + output 计算;均缺失时置 0。
  4. usage 缺失不视为失败,但必须打 usage_extracted=false 观测标签(用于后续补偿分析)。

6. 错误归一契约

6.1 上游原生错误格式(三类)

  1. OpenAI 风格:{"error":{"type":"...","message":"..."}}
  2. Anthropic 风格:{"type":"error","error":{"type":"...","message":"..."}}
  3. Google 风格:{"error":{"code":403,"message":"...","status":"PERMISSION_DENIED"}}

6.2 Connector 统一错误结构

{
  "http_status": 429,
  "category": "auth|billing|rate_limit|upstream|validation|internal",
  "code": "RATE_LIMIT_EXCEEDED",
  "message": "human readable",
  "retryable": true
}

6.3 默认映射规则

  1. 401/403 上游鉴权类错误 -> category=authretryable=false
  2. 429 -> category=rate_limitretryable=true
  3. 500/502/503/504/529 -> category=upstreamretryable=true
  4. 计费检查失败(余额/订阅/额度) -> category=billingretryable 取决于具体码。
  5. JSON/字段校验失败 -> category=validationretryable=false

7. 流式契约SSE/WS

7.1 SSE

  1. 流开始后如发生错误,按事件帧返回,不再回写普通 JSON 错误。
  2. 一旦已向客户端写出任何流内容Connector 禁止触发“同请求 failover 重放”。
  3. 必须记录首字时延 first_token_ms 与终止类型(正常结束/上游错误/客户端断开)。

7.2 WebSocketOpenAI Responses

  1. 首帧必须包含合法 JSON 且含 model
  2. 若首帧不合法,立即关闭连接并返回协议错误。
  3. 多轮 turn 必须重新获取并发槽位(避免长连接长期占槽)。

8. 重试与回退契约Connector 侧)

  1. 仅在“未输出任何字节”时允许请求级重试。
  2. 流式一旦开始输出,禁止自动重试。
  3. 推荐重试上限:
    • 非流式:最多 2 次(指数退避)
    • 流式0 次(依赖上游内部 failover
  4. HTTP 429/503 可进入短退避重试4xx 校验/鉴权错误直接失败。

9. 版本与兼容治理

9.1 版本锁定

  1. 生产固定 subapi 精确版本(vX.Y.Z),不允许漂移到未验证版本。
  2. 升级必须进入周级升级窗口,禁止临时直升生产。

9.2 契约测试门槛(每次升级必须通过)

  1. OpenAI /v1/chat/completions 非流/流式基础场景。
  2. OpenAI /v1/responses + previous_response_id 校验场景。
  3. Anthropic /v1/messages + count_tokens 场景。
  4. Gemini /v1beta/models/* 普通与流式场景。
  5. 三类错误格式的归一验证OpenAI/Anthropic/Google
  6. 流式中断与客户端断开场景(保证不重放、不漏记)。

10. 与 S2 目标的对齐(执行约束)

  1. S2 结束时:
    • 全供应商主路径由自研 Router Core 接管率 >= 60%
    • 国内 LLM 供应商主路径接管率 = 100%
  2. subapi 在 S2 后只承担:
    • 长尾协议兼容
    • 备用回退通道

11. 证据来源(本地代码)

  1. 路由范围与协议入口:
    /home/long/project/立交桥/llm-gateway-competitors/sub2api-tar/backend/internal/server/routes/gateway.go
  2. OpenAI/Anthropic 错误与流式处理:
    /home/long/project/立交桥/llm-gateway-competitors/sub2api-tar/backend/internal/handler/openai_gateway_handler.go
  3. Claude 兼容入口、models/usage/count_tokens
    /home/long/project/立交桥/llm-gateway-competitors/sub2api-tar/backend/internal/handler/gateway_handler.go
  4. Gemini 原生入口与错误格式:
    /home/long/project/立交桥/llm-gateway-competitors/sub2api-tar/backend/internal/handler/gemini_v1beta_handler.go
  5. 认证优先级与禁用 query key
    /home/long/project/立交桥/llm-gateway-competitors/sub2api-tar/backend/internal/server/middleware/api_key_auth.go
    /home/long/project/立交桥/llm-gateway-competitors/sub2api-tar/backend/internal/server/middleware/api_key_auth_google.go
  6. 会话哈希与 header 语义:
    /home/long/project/立交桥/llm-gateway-competitors/sub2api-tar/backend/internal/service/openai_gateway_service.go
    /home/long/project/立交桥/llm-gateway-competitors/sub2api-tar/backend/internal/service/gateway_service.go

12. 下一步v1 -> v1.1

  1. 把本契约转成机器可执行测试清单YAML + golden cases
  2. 为每个端点补充“最小请求样例 + 最小响应样例”文件。
  3. 将错误映射表下沉为配置化规则,减少硬编码发布频率。