7.9 KiB
Context
当前后端代理入口采用 /{protocol}/*path,ProxyHandler 将剥离协议前缀后的 path 作为 nativePath 传入 adapter。这个模型本身是合理的:OpenAI adapter 应接收 /chat/completions,Anthropic adapter 应接收 /v1/messages。需要修正的是文档中曾把版本路径抽象为网关统一规则,但实际上版本路径是否存在属于协议原生路径的一部分,应由协议 adapter 识别和映射。
流式链路当前由 ProviderClient.SendStream 按 \n\n 拆分 SSE 事件,并把 data: [DONE] 转成 Done 信号。这个设计适合跨协议 decoder,但不适合同协议 raw passthrough:透传路径会丢失 SSE frame 结尾空行,Smart Passthrough 也无法对 data: {...} 中的 JSON 顶层 model 做可靠改写。
错误处理目前混合了协议错误、应用错误和上游错误。新的产品决策是:网关层错误使用应用统一 {error, code} 格式;只要上游已经返回 HTTP 响应,即使是非 2xx,也保持透明代理语义,直接透传上游 status、headers、body。
Goals / Non-Goals
Goals:
- 明确网关只剥离协议前缀,不对剩余 nativePath 做版本号归一化。
- 明确不同协议的 nativePath 形态由协议自身决定:OpenAI 为
/chat/completions,Anthropic 为/v1/messages。 - 保持现有
base_url约定:OpenAI 配置到版本路径一级,Anthropic 配置到域名级。 - 由 adapter 负责识别协议原生 nativePath,并通过 BuildUrl 产出真实上游路径。
- 同协议无 model 改写的流式请求保持 raw SSE 字节透传。
- 同协议需要 model 改写的流式请求按 SSE frame 解析,只改写
dataJSON 中的 model 字段,再重建 SSE frame。 - 网关层错误统一使用应用错误格式;上游非 2xx 响应透明透传。
- 只有 adapter 明确适配的接口才提取 model;未知接口不做通用 model 猜测。
- 跨协议遇到多模态占位内容时明确返回不支持错误,避免静默丢内容。
Non-Goals:
- 不统一不同协议是否使用
/v1版本路径。 - 不调整 OpenAI/Anthropic
base_url配置约定。 - 不调整统计口径和统计数据模型。
- 不实现多模态正式转换。
- 不引入新依赖。
Decisions
Decision 1: nativePath 保持协议原生路径
外部请求 /{protocol}/{path} 只剥离协议前缀,剩余 path 原样作为 client protocol 的 nativePath。OpenAI 请求 /openai/chat/completions 进入 OpenAI adapter 时为 /chat/completions;Anthropic 请求 /anthropic/v1/messages 进入 Anthropic adapter 时为 /v1/messages。
替代方案是由网关统一添加或移除 /v1。该方案会把版本路径从协议知识错误提升为网关通用规则,导致 Anthropic 和 OpenAI 的 base_url 约定被混淆。
Decision 2: 对外路径由协议 adapter 的 nativePath 决定
网关不提供跨协议统一路径规范。OpenAI 对外路径不带 /v1,因为 OpenAI provider 的 base_url 已配置到版本路径一级;Anthropic 对外路径保留 /v1,因为 Anthropic 协议原生路径包含版本段且 base_url 配置到域名级。
替代方案是把所有协议对外路径都改成不带 /v1 或都带 /v1。该方案不符合各协议原生路径,也会让 adapter 的 DetectInterfaceType 失去协议边界。
Decision 3: 上游路径由 adapter.BuildUrl 产生
无论同协议透传、Smart Passthrough 还是跨协议转换,出站 URL 都使用 provider.BaseURL + providerAdapter.BuildUrl(nativePath, interfaceType)。OpenAI adapter 对 /chat/completions 返回 /chat/completions,配合 base_url=http://xxx.com/v1 得到 http://xxx.com/v1/chat/completions。Anthropic adapter 对 /v1/messages 返回 /v1/messages,配合 base_url=http://xxx.com 得到 http://xxx.com/v1/messages。
替代方案是在 engine 中直接拼接 provider.BaseURL + nativePath。该方案当前对部分协议可工作,但绕过了 adapter 的 URL 映射职责,不利于后续协议扩展和特殊路径映射。
Decision 4: 同协议流式分 raw passthrough 和 smart passthrough 两条路径
当 clientProtocol 等于 providerProtocol 且不需要响应 model 改写时,handler/provider client 应直接将上游响应 body 的 SSE 字节写回客户端,不进入 StreamConverter 事件拆分链路。
当需要响应 model 改写时,保留 SSE frame 边界,解析每个 SSE frame 的 data 行:[DONE] 原样输出;JSON payload 调用 adapter 的响应 model 改写逻辑后重新写为 SSE frame;解析失败则按宽容策略输出原 frame。
替代方案是继续让 ProviderClient.SendStream 只暴露去掉 \n\n 的 rawEvent。该方案无法满足 raw passthrough 的字节语义,也无法可靠输出 [DONE]。
Decision 5: 网关层错误与上游错误分离
网关层错误包括路由失败、请求 JSON 错误、转换失败、上游连接失败、跨协议暂不支持能力等,统一返回 {error, code}。上游错误指已经收到上游 HTTP 响应的情况,非 2xx 响应不进入 conversion,直接透传 status、过滤后的 headers 和 body。
替代方案是把所有代理错误都编码为客户端协议错误格式。该方案会隐藏上游原始错误,也与管理接口统一错误格式不一致。
Decision 6: adapter 明确声明可提取 model 的接口边界
Chat、Embeddings、Rerank 等已适配接口由 adapter 的 ExtractModelName 明确解析 model。未知接口即使 body 中存在顶层 model,也不做假设性提取,按无 model 透传处理。
替代方案是在 handler 中统一尝试解析顶层 model。该方案会误判未来协议特有接口的模型字段语义,破坏 adapter 对协议知识的封装。
Decision 7: 多模态占位保留但跨协议拒绝
Canonical 中现有 image/audio/video/file 占位保留,后续多模态实现可继续扩展。同协议 Smart Passthrough 保留请求 JSON 语义,不检查多模态。跨协议完整转换检测到 image/audio/video/file 时返回 UNSUPPORTED_MULTIMODAL 网关错误。
替代方案是继续编码空占位或静默丢弃。该方案会制造数据丢失,调用方难以诊断。
Risks / Trade-offs
- [Risk] 对外路径由协议决定,调用方需要分别记住 OpenAI 和 Anthropic 的路径形态。→ Mitigation: README、backend README 和设计文档明确列出每个协议的路径和 base_url 约定。
- [Risk] raw stream passthrough 可能需要调整 ProviderClient 接口,影响现有测试。→ Mitigation: 将非流式、跨协议流式、同协议 raw 流式分别建模,并补充 E2E 测试。
- [Risk] 上游错误透传可能让 OpenAI 客户端看到 Anthropic 错误体。→ Mitigation: 文档明确透明代理边界;只有网关自身错误保证统一格式。
- [Risk] SSE frame 级 model 改写需要处理 CRLF、多 data 行和
[DONE]。→ Mitigation: 实现轻量 SSE frame parser,覆盖 LF/CRLF、多行 data、解析失败回退原 frame 的测试。 - [Risk] 跨协议多模态拒绝会短期限制能力。→ Mitigation: 保留 Canonical 占位和同协议透传,后续多模态 change 可在此基础上扩展。
Migration Plan
- 更新 OpenSpec 和设计文档,明确协议原生路径、base_url、错误和流式边界。
- 校正 adapter 路径识别和
BuildUrl映射测试,确保 OpenAI 接收/chat/completions、Anthropic 接收/v1/messages。 - 修改
ConversionEngine同协议 URL 构建,统一使用BuildUrl,避免核心逻辑绕过 adapter。 - 调整 ProviderClient/ProxyHandler 流式链路,支持 raw passthrough、SSE frame 级 Smart Passthrough 和非 2xx 透传。
- 调整网关层错误输出,非 2xx 上游响应绕过 conversion。
- 补齐单测、集成测试和 E2E 测试。
- 更新 README、backend README 和
docs/conversion_design.md。
由于应用尚未上线,不提供额外路径别名兼容和数据迁移。回滚策略是恢复旧流式 provider client 和错误处理行为。
Open Questions
无。