## 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

无。

