1
0
Files
nex/openspec/changes/refine-conversion-proxy-behavior/design.md

101 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
## 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 解析,只改写 `data` JSON 中的 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
1. 更新 OpenSpec 和设计文档明确协议原生路径、base_url、错误和流式边界。
2. 校正 adapter 路径识别和 `BuildUrl` 映射测试,确保 OpenAI 接收 `/chat/completions`、Anthropic 接收 `/v1/messages`
3. 修改 `ConversionEngine` 同协议 URL 构建,统一使用 `BuildUrl`,避免核心逻辑绕过 adapter。
4. 调整 ProviderClient/ProxyHandler 流式链路,支持 raw passthrough、SSE frame 级 Smart Passthrough 和非 2xx 透传。
5. 调整网关层错误输出,非 2xx 上游响应绕过 conversion。
6. 补齐单测、集成测试和 E2E 测试。
7. 更新 README、backend README 和 `docs/conversion_design.md`
由于应用尚未上线,不提供额外路径别名兼容和数据迁移。回滚策略是恢复旧流式 provider client 和错误处理行为。
## Open Questions
无。