# LLM API Protocol Conversion Layer — Architecture Design

> 语言无关的 HTTP 层 SDK。以 Hub-and-Spoke 架构实现多协议间的双向转换，覆盖完整 HTTP 接口体系，同协议自动透传，为新协议和多模态预留扩展点。

---

## 目录

1. [设计目标与约束](#1-设计目标与约束)
2. [架构总览](#2-架构总览)
3. [接口体系分层](#3-接口体系分层)
4. [Canonical Model（规范模型）](#4-canonical-model规范模型)
5. [Protocol Adapter 接口](#5-protocol-adapter-接口)
6. [Conversion Engine（转换引擎）](#6-conversion-engine转换引擎)
7. [流式转换架构](#7-流式转换架构)
8. [扩展点设计](#8-扩展点设计)
9. [错误处理](#9-错误处理)
10. [附录 A：模块依赖](#附录-a模块依赖)
11. [附录 B：接口速查](#附录-b接口速查)
12. [附录 C：字段晋升规范](#附录-c字段晋升规范)
13. [附录 D：协议适配清单模板](#附录-d协议适配清单模板)
14. [附录 E：协议适配文档索引](#附录-e协议适配文档索引)

---

## 1. 设计目标与约束

### 1.1 核心目标

| 目标 | 说明 |
|------|------|
| **完整 HTTP 接口体系转换** | 覆盖 /models、/embeddings、/rerank 等全部接口的 URL 路由映射、请求头转换、请求体/响应体格式转换 |
| **输入输出解耦** | 客户端协议和服务端协议独立指定，任意组合 |
| **同协议透传** | client == provider 时跳过 Canonical 全量转换，保持协议语义 |
| **尽力转换** | 能对接的参数尽可能对接，不能对接的各自忽略，保障最大覆盖面 |
| **协议可扩展** | 添加新协议只需实现 Adapter，不修改核心引擎 |
| **流式优先** | SSE 流式转换作为核心能力，与非流式同等地位 |
| **Tool Calling 核心** | 工具调用是编程场景的一等公民 |
| **语言无关** | 不绑定编程语言，用伪类型描述接口 |

### 1.2 约束

| 约束 | 说明 |
|------|------|
| 部署形态 | HTTP 层 SDK，处理 HTTP 请求/响应转换（URL + Headers + Body），不包含 HTTP 服务器启动/监听 |
| 当前模态 | 仅文本（含 Tool Calling），接口体系覆盖 /models、/embeddings、/rerank |
| Provider 必传 | 每次转换调用需传入 TargetProvider，提供目标上游的地址、认证、模型名等信息 |
| 适配器注册 | 所有 ProtocolAdapter 通过代码注册，不支持动态增减 |
| 有状态特性 | 初始不实现，架构预留扩展点 |

### 1.3 设计决策溯源

| 决策 | 依据 |
|------|------|
| HTTP 层 SDK | 编程工具启动时调用 /models 等接口，缺失会报错或功能降级 |
| Hub-and-Spoke | 以规范格式为枢纽，将 O(n²) 的协议对转换降为 O(n) |
| 自定义 Canonical Model | 不选用厂商格式，避免语义损失和厂商锁定 |
| 协议识别外部化 | 协议识别是调用方职责，引擎不绑定具体识别方式 |
| 接口分层 + 尽力转换 | 不同接口差异程度不同，分层处理最大化覆盖面 |
| 同协议透传 | 零转换的性能优势已验证 |

---

## 2. 架构总览

### 2.1 分层架构

```
┌─────────────────────────────────────────────────────────────────────────┐
│                         上层 HTTP 框架（用户自选）                          │
│           Express / FastAPI / Axum / Gin / 任意框架                       │
│                                                                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │              协议识别 + 前缀剥离（调用方职责）                      │   │
│  │                                                                 │   │
│  │  入站: /{protocol}/{native_path}                                 │   │
│  │                                                                 │   │
│  │  /openai/chat/completions    → client=openai, /chat/completions │   │
│  │  /anthropic/v1/messages      → client=anthropic, /v1/messages   │   │
│  │                                                                 │   │
│  │  Step 1: 识别 client protocol（URL 前缀 / 配置映射 / 任意方式）   │   │
│  │  Step 2: 剥离前缀 → 得到 nativePath                              │   │
│  │  Step 3: 确定 provider protocol（配置决定）                       │   │
│  └─────────────────────────────┬───────────────────────────────────┘   │
└───────────────────────────────┬─────────────────────────────────────────┘
                                │ nativePath + clientProtocol + providerProtocol
                                ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                         Conversion Engine (SDK)                         │
│                                                                         │
│                    ┌────────────┼────────────┐                          │
│                    ▼            ▼            ▼                          │
│           ┌──────────────┐ ┌─────────┐ ┌──────────┐                   │
│           │  Passthrough  │ │核心接口层│ │扩展接口层 │                   │
│           │  同协议直通   │ │  Chat   │ │ Models   │                   │
│           │  或未知接口   │ │  流式   │ │ Embed    │                   │
│           │  直接转发    │ │ Tool    │ │ Rerank   │                   │
│           └──────────────┘ │ Thinking│ └──────────┘                   │
│                          └────┬────┘                                  │
│                    ┌──────────▼───────────────┐                         │
│                    │     ProtocolAdapter 层    │                         │
│                    │  ┌────────┐  ┌─────────┐  │                         │
│                    │  │Protocol│  │Protocol │  │                         │
│                    │  │  A     │  │  B      │  │                         │
│                    │  └────────┘  └─────────┘  │                         │
│                    └──────────────┬────────────┘                         │
│                    ┌──────────────▼────────────┐                         │
│                    │      Canonical Model       │                         │
│                    └───────────────────────────┘                         │
└─────────────────────────────────────────────────────────────────────────┘
```

### 2.2 URL 路由规则

调用方负责识别协议并剥离前缀，将 `nativePath`、`clientProtocol`、`providerProtocol` 传入引擎。网关只剥离第一段协议前缀，不对剩余路径做版本号归一化；`/v1` 是否存在属于协议原生路径，由对应 ProtocolAdapter 按本地 API reference 识别和映射。

```
入站 URL                                    调用方剥离前缀后        引擎出站
──────────────────────────────────────────────────────────────────────────────
/openai/chat/completions                →  /chat/completions    →  /chat/completions
/openai/models                          →  /models              →  /models
/anthropic/v1/messages                  →  /v1/messages         →  /v1/messages
/anthropic/v1/models                    →  /v1/models           →  /v1/models
```

出站到上游 API 时使用服务端协议原生路径（无网关协议前缀），并由 `provider.BaseURL + providerAdapter.BuildUrl(nativePath, interfaceType)` 组合得到真实 URL。OpenAI `base_url` 配置到版本路径一级（如 `https://api.openai.com/v1`），Anthropic `base_url` 配置到域名级（如 `https://api.anthropic.com`）。

### 2.3 请求处理流程

#### 2.3.1 三车道数据流模型

引擎根据协议匹配情况和参数条件，选择三条数据流通道之一：

```
┌──────────────────────────────────────────────────────────────────────────────────┐
│                            数据流三车道模型                                        │
├────────────────┬─────────────────────────┬────────────────────────────────────────┤
│   透传车道      │    智能透传车道           │         完整转换车道                    │
├────────────────┼─────────────────────────┼────────────────────────────────────────┤
│ 触发条件        │ 同协议                   │ 同协议 + 接口∈{Chat,Embed,Rerank}      │ 不同协议                │
│                │                         │ + Body非空 + ModelName非空             │                         │
│                │                         │                                        │                         │
│ 请求处理        │ 重建Headers              │ 重建Headers                            │ Decode→Middleware→Encode│
│                │ Body原样转发              │ RewriteRequestModelName(body)         │                         │
│                │                         │ （最小化JSON字段手术）                   │                         │
│                │                         │                                        │                         │
│ 响应处理        │ 原样返回                 │ modelOverride非空时                     │ Decode→modelOverride    │
│                │                         │ RewriteResponseModelName(body)         │ →Encode                 │
│                │                         │                                        │                         │
│ 流式处理        │ raw SSE frame原样透传     │ SSE frame→仅改写data JSON中的model       │ Decode→Middleware       │
│                │ 保留[DONE]和frame边界     │ 解析失败则输出原frame继续处理             │ →modelOverride→Encode   │
│                │                         │                                        │                         │
│ 性能开销        │ 最低                     │ 低（仅JSON字段改写）                     │ 高（完整序列化）         │
└────────────────┴─────────────────────────┴────────────────────────────────────────┘
```

**智能透传的设计动机**：同协议场景下，若仅需改写 `model` 字段（如客户端请求模型 "X"，上游需要模型 "Y"），无需进入 Canonical 全量解码/编码。直接在 JSON 层面改写该字段，保持未改写字段的 JSON 内容和类型不变；实现不承诺保留原始字节顺序、空白或对象字段顺序。

#### 2.3.2 完整请求处理流程

```
客户端入站                调用方职责                    SDK 内部处理                    上游出站
┌──────────────┐                                                            ┌──────────────┐
│ URL:          │  调用方完成:                   1. 接口识别                   │ URL:          │
│  /<protocol>/ │  · clientProtocol             2. IsPassthrough?            │  目标协议      │
│  ...          │  · nativePath                    ├─ yes ─┬─ 无 modelOverride → 透传车道    │  原生路径     │
│ Headers:      │  · providerProtocol             │        └─ 有 modelOverride → 智能透传车道│ Headers:      │
│  协议原生格式  │                                 │                                   │  目标协议格式  │
│ Body:         │                                 └─ no → 完整转换车道                 │ Body:         │
│  协议原生格式  │                                                                      │  目标协议格式  │
└──────────────┘                                                            └──────────────┘
```

响应方向同理（含流式）。

**同协议透传**：client == provider 时，仅重建 Header 后原样转发到上游。
**智能透传**：同协议且需改写 model 字段时，最小化 JSON 改写后转发。
**未知接口透传**：无法识别的路径，URL+Header 适配后 Body 原样转发；即使请求体存在顶层 `model`，也不做通用猜测。

---

## 3. 接口体系分层

### 3.1 分层策略

| 层级 | 接口 | 转换方式 |
|------|------|---------|
| **核心层** | Chat 接口（各协议的核心对话接口） | Canonical Model 深度转换 |
| **扩展层** | /models, /models/{model}, /embeddings, /rerank | 轻量级字段映射 |
| **透传层** | /files, /count_tokens, /batches, /fine-tuning, /audio/*, /images/*, /video/*, /moderations 等未知路径 | URL+Header 适配后 Body 原样转发 |

**接口纳入原则**：只有 ≥2 个协议提供相同功能的接口才纳入转换层（核心层或扩展层），其余走透传。

**尽力转换策略**：
- 双方都支持且可映射 → 完整转换（如 /models）
- 服务端协议不支持该接口 → 透传到上游或返回空响应/错误（可配置）
- 未知接口 → 始终透传

**各协议的接口清单**详见各自的协议适配文档（附录 E）。

---

## 4. Canonical Model（规范模型）

协议无关的统一内部表示，Hub-and-Spoke 架构的枢纽。**核心层**使用完整 Canonical Model 深度转换；**扩展层**使用轻量 Canonical Models 做字段映射；**透传层**不使用。

设计原则：超集设计、类型化 discriminated union、前向兼容。Canonical Model 面向当前已适配协议的公共语义进行抽象，字段随协议扩展演进（详见附录 C：字段晋升规范）。

**适配新协议时**：对照本节定义逐字段确认映射关系。协议特有字段不纳入 Canonical，同协议透传时自然保留，跨协议时丢弃（详见附录 C 字段分类规则）。

### 4.1 CanonicalRequest

```
CanonicalRequest {
    model: String
    system: Union<None, String, Array<SystemBlock>>

    SystemBlock {
        text: String
    }

    messages: Array<CanonicalMessage>
    tools: Option<Array<CanonicalTool>>
    tool_choice: Option<ToolChoice>
    parameters: RequestParameters
    thinking: Option<ThinkingConfig>
    stream: Boolean
    user_id: Option<String>
    output_format: Option<OutputFormat>
    parallel_tool_use: Option<Boolean>       // true = 允许并行, false = 禁止并行
}
```

### 4.2 RequestParameters

```
RequestParameters {
    max_tokens: Option<Integer>
    temperature: Option<Float>
    top_p: Option<Float>
    top_k: Option<Integer>
    frequency_penalty: Option<Float>
    presence_penalty: Option<Float>
    stop_sequences: Option<Array<String>>
}
```

### 4.3 CanonicalMessage / ContentBlock

```
CanonicalMessage {
    role: Enum<system, user, assistant, tool>
    content: Array<ContentBlock>
}

ContentBlock = Union<
    TextBlock,          { type: "text", text: String }
    ToolUseBlock,       { type: "tool_use", id: String, name: String, input: Object }
    ToolResultBlock,    { type: "tool_result", tool_use_id: String,
                          content: Union<String, Array<ContentBlock>>,
                          is_error: Option<Boolean> }
    ThinkingBlock,      { type: "thinking", thinking: String }
    ImageBlock,         { type: "image", source: ... }   // Deferred：多模态预留
    AudioBlock,         { type: "audio", source: ... }   // Deferred：多模态预留
    VideoBlock,         { type: "video", source: ... }   // Deferred：多模态预留
    FileBlock           { type: "file", source: ... }    // Deferred：多模态预留
>
```

**当前实现状态**：仅实现了 TextBlock、ToolUseBlock、ToolResultBlock、ThinkingBlock。多模态类型（ImageBlock、AudioBlock、VideoBlock、FileBlock）为预留扩展点。

### 4.4 CanonicalTool / ToolChoice

```
CanonicalTool {
    name: String
    description: Option<String>
    input_schema: Object
}

ToolChoice = Union<
    {type: "auto"},
    {type: "none"},
    {type: "any"},
    {type: "tool", name: String}
>
```

### 4.5 ThinkingConfig

```
ThinkingConfig {
    type: Enum<enabled, disabled>
    budget_tokens: Option<Integer>               // token 级控制
    effort: Option<Enum<low, medium, high, xhigh>> // 抽象级别控制
}
```

### 4.6 OutputFormat

```
OutputFormat = Union<
    { type: "json_object" },
    { type: "json_schema", json_schema: { name: String, schema: Object, strict: Option<Boolean> } },
    { type: "text" }
>
```

### 4.7 CanonicalResponse

```
CanonicalResponse {
    id: String
    model: String
    content: Array<ContentBlock>
    stop_reason: Option<StopReason>       // end_turn | max_tokens | tool_use | stop_sequence | content_filter | refusal
    usage: CanonicalUsage
}

CanonicalUsage {
    input_tokens: Integer
    output_tokens: Integer
    cache_read_tokens: Option<Integer>
    cache_creation_tokens: Option<Integer>
    reasoning_tokens: Option<Integer>
}
```

### 4.8 CanonicalStreamEvent

采用类型化事件模型，具有显式的 start/stop 生命周期，语义明确，便于任意协议间的双向转换：

```
CanonicalStreamEvent = Union<
    MessageStartEvent,         { type: "message_start", message: {id, model, usage} }
    ContentBlockStartEvent,    { type: "content_block_start", index, content_block }
    ContentBlockDeltaEvent,    { type: "content_block_delta", index, delta }
    ContentBlockStopEvent,     { type: "content_block_stop", index }
    MessageDeltaEvent,         { type: "message_delta", delta: {stop_reason}, usage }
    MessageStopEvent,          { type: "message_stop" }
    ErrorEvent,                { type: "error", error: {type, message} }
    PingEvent                  { type: "ping" }
>

content_block: Union<
    {type: "text", text: ""},
    {type: "tool_use", id, name, input: {}},
    {type: "thinking", thinking: ""},
    {type: "image"} | {type: "audio"} | {type: "video"}  // 预留
>

delta: Union<
    {type: "text_delta", text},
    {type: "input_json_delta", partial_json},
    {type: "thinking_delta", thinking},
    {type: "image_delta", data} | {type: "audio_delta", data}  // 预留
>
```

### 4.9 扩展层 Canonical Models

```
// /models
CanonicalModelList { models: Array<CanonicalModel> }
CanonicalModel { id, name, created, owned_by }

// /models/{model}
CanonicalModelInfo { id, name, created, owned_by }

// /embeddings
CanonicalEmbeddingRequest { model, input, encoding_format?, dimensions? }
CanonicalEmbeddingResponse { data: [{index, embedding}], model, usage }

// /rerank
CanonicalRerankRequest { model, query, documents, top_n?, return_documents? }
CanonicalRerankResponse { results: [{index, relevance_score, document?}], model }
```

### 4.10 设计说明

**`system` 为什么独立于 `messages`？**
不同协议对系统消息的处理方式不同：有的用顶层字段，有的嵌入 messages 数组。独立 `system` 语义更清晰，Decoder 提取、Encoder 注入，适配时按各自协议规范处理即可。

**流式事件为什么用类型化事件模型？**
类型化事件有显式的 start/stop 生命周期，语义明确。delta 模型需要状态机推断语义，增加双向转换的复杂度。新协议适配时，如协议原生支持类似生命周期，直接映射；如使用 delta 模型，需在 StreamDecoder 中用状态机转换。

**`user_id`、`output_format`、`parallel_tool_use` 为什么独立为顶层字段？**
这些是多个协议共同表达的公共语义，只是字段名、嵌套位置或语义方向（正/反）有差异。统一为协议中立的顶层字段，避免命名偏向。适配新协议时，对照各字段在 Canonical 中的定义，实现提取（Decoder）和注入（Encoder）即可。

---

## 5. Protocol Adapter 接口

### 5.1 TargetProvider

每次转换调用传入的目标上游信息。Adapter 通过 Provider 获取认证和配置，**不需要理解其他协议的 Header 格式**。

```
TargetProvider {
    base_url: String                          // 上游 API 地址
    api_key: String                           // 上游认证密钥
    model_name: String                        // 目标模型名（调用方完成映射后传入）
    adapter_config: Map<String, Any>          // 协议专属配置
}
```

`adapter_config` 由各 Adapter 自行定义所需的 key，引擎不感知含义，原样透传。各 Adapter 需在协议适配文档中声明自己从 `adapter_config` 读取的 key 列表和默认值。

### 5.2 ProtocolAdapter

每种协议的完整适配器，融入所有接口类型的处理能力。所有 Adapter 通过代码注册，不支持动态增减。新协议可通过空函数（如不支持的扩展层接口直接透传）渐进适配，接口集中定义方便明确所有应实现的内容。

```
interface ProtocolAdapter {
    protocolName(): String
    protocolVersion(): String
    supportsPassthrough(): Boolean        // 同协议透传开关，默认 true

    // HTTP 级别
    detectInterfaceType(nativePath: String): InterfaceType  // 根据协议的 URL 路径识别接口类型
    buildUrl(nativePath: String, interfaceType: InterfaceType): String   // 始终返回有效 URL；未知接口返回 nativePath
    buildHeaders(provider: TargetProvider): Map<String, String>
    supportsInterface(interfaceType: InterfaceType): Boolean

    // 核心层：Chat
    decodeRequest(raw): CanonicalRequest
    encodeRequest(canonical, provider): RawRequest
    decodeResponse(raw): CanonicalResponse
    encodeResponse(canonical): RawResponse

    // 核心层：流式
    createStreamDecoder(): StreamDecoder
    createStreamEncoder(): StreamEncoder

    // 错误编码
    encodeError(error: ConversionError): (body, statusCode)

    // 扩展层
    decodeModelsResponse(raw): CanonicalModelList
    encodeModelsResponse(canonical): RawResponse
    decodeModelInfoResponse(raw): CanonicalModelInfo
    encodeModelInfoResponse(canonical): RawResponse
    decodeEmbeddingRequest(raw): CanonicalEmbeddingRequest
    encodeEmbeddingRequest(canonical, provider): RawRequest
    decodeEmbeddingResponse(raw): CanonicalEmbeddingResponse
    encodeEmbeddingResponse(canonical): RawResponse
    decodeRerankRequest(raw): CanonicalRerankRequest
    encodeRerankRequest(canonical, provider): RawRequest
    decodeRerankResponse(raw): CanonicalRerankResponse
    encodeRerankResponse(canonical): RawResponse

    // 智能透传支持
    extractUnifiedModelID(nativePath: String): (modelID, error)      // 从路径提取统一模型 ID
    extractModelName(body: Raw, interfaceType: InterfaceType): (modelName, error)  // 从请求体提取 model 字段值
    rewriteRequestModelName(body: Raw, newModel: String, interfaceType: InterfaceType): RawRequest   // 最小化改写请求体中的 model 字段
    rewriteResponseModelName(body: Raw, newModel: String, interfaceType: InterfaceType): RawResponse  // 最小化改写响应体中的 model 字段
}
```

**`buildHeaders` 的设计**：Adapter 只需从 `provider` 中提取自己协议需要的认证和配置信息，构建自己的 Header 格式。不再需要理解其他协议的 Header。

**URL 事实来源**：Adapter 的 `detectInterfaceType` 和 `buildUrl` 必须以本地 API reference 为事实来源。OpenAI 参考 `docs/api_reference/openai`（忽略 `responses` 目录），其 nativePath 不包含 `/v1`，例如 `/chat/completions`、`/models`、`/embeddings`。Anthropic 参考 `docs/api_reference/anthropic`，其 nativePath 保留 `/v1`，例如 `/v1/messages`、`/v1/models`。不得根据其他协议是否包含 `/v1` 推断当前协议路径。

**智能透传方法的契约**：
- `rewriteRequestModelName` / `rewriteResponseModelName` 必须**幂等**（多次调用结果相同）
- Rewrite 方法必须**最小化**（仅修改 model 字段，不触碰其他字段）
- Rewrite 失败时，引擎使用宽容策略：记录警告日志，使用原始 body 继续处理
- `extractModelName` 只支持 adapter 明确适配的接口类型：CHAT、EMBEDDINGS、RERANK（这些接口的请求体包含 model 字段）。PASSTHROUGH 或未适配接口返回错误或空结果，调用方按无 model 请求透传，不做顶层 `model` 猜测。

### 5.3 InterfaceType

```
InterfaceType = Enum<
    CHAT, MODELS, MODEL_INFO, EMBEDDINGS, RERANK, PASSTHROUGH
>
```

| 类型 | 说明 |
|------|------|
| CHAT | 核心对话接口（各协议的 Chat/Messages 接口） |
| MODELS | 模型列表接口 |
| MODEL_INFO | 模型详情接口 |
| EMBEDDINGS | 向量嵌入接口 |
| RERANK | 重排序接口 |
| PASSTHROUGH | 未知接口，透传处理 |

### 5.4 StreamDecoder / StreamEncoder

```
interface StreamDecoder {
    processChunk(rawChunk): Array<CanonicalStreamEvent>
    flush(): Array<CanonicalStreamEvent>
}

interface StreamEncoder {
    encodeEvent(event): Array<RawSSEChunk>
    flush(): Array<RawSSEChunk>
}
```

### 5.5 AdapterRegistry

```
interface AdapterRegistry {
    register(adapter: ProtocolAdapter): void
    get(protocolName: String): ProtocolAdapter
    listProtocols(): Array<String>
}
```

---

## 6. Conversion Engine（转换引擎）

### 6.1 ConversionEngine

ConversionEngine 是无状态的格式转换工具，仅做协议间的编解码和字段映射，不持有会话状态，线程安全，可复用。

**协议识别**：`clientProtocol` 和 `providerProtocol` 由调用方确定并传入引擎（详见 §2.2）。

#### 6.1.1 HTTP 规格

```
HTTPRequestSpec {
    url: String                    // 请求路径（不含 base_url）
    method: String                 // HTTP 方法
    headers: Map<String, String>
    body: ByteArray                // 原始请求体
}

HTTPResponseSpec {
    statusCode: Integer
    headers: Map<String, String>
    body: ByteArray                // 原始响应体
}
```

#### 6.1.2 引擎接口

```
class ConversionEngine {
    registry: AdapterRegistry
    middlewareChain: MiddlewareChain

    // 生命周期
    registerAdapter(adapter): void
    use(middleware): void
    getRegistry(): AdapterRegistry

    // 核心转换
    isPassthrough(clientProtocol, providerProtocol): Boolean {
        return clientProtocol == providerProtocol && registry.get(clientProtocol).supportsPassthrough()
    }

    convertHttpRequest(request, clientProtocol, providerProtocol, provider): HTTPRequestSpec
    convertHttpResponse(response, clientProtocol, providerProtocol, interfaceType, modelOverride): HTTPResponseSpec
    createStreamConverter(clientProtocol, providerProtocol, modelOverride, interfaceType): StreamConverter

    // 辅助方法
    detectInterfaceType(nativePath, clientProtocol): InterfaceType
    encodeError(error, clientProtocol): (body, statusCode)
}
```

#### 6.1.3 请求转换流程

```
function convertHttpRequest(request, clientProtocol, providerProtocol, provider):
    nativePath = request.url

    if isPassthrough(clientProtocol, providerProtocol):
        providerAdapter = registry.get(providerProtocol)
        interfaceType = providerAdapter.detectInterfaceType(nativePath)

        // 智能透传：对 Chat/Embeddings/Rerank 接口改写 model 字段
        if interfaceType in {CHAT, EMBEDDINGS, RERANK} and request.body非空 and provider.modelName非空:
            rewrittenBody = providerAdapter.rewriteRequestModelName(request.body, provider.modelName, interfaceType)
            if rewrite失败:
                log.warn("智能透传改写失败，使用原始请求体")
                rewrittenBody = request.body
        else:
            rewrittenBody = request.body

        return HTTPRequestSpec {
            url: provider.base_url + nativePath,
            method: request.method,
            headers: providerAdapter.buildHeaders(provider),
            body: rewrittenBody
        }

    // 完整转换车道
    clientAdapter = registry.get(clientProtocol)
    providerAdapter = registry.get(providerProtocol)
    interfaceType = clientAdapter.detectInterfaceType(nativePath)

    providerUrl = providerAdapter.buildUrl(nativePath, interfaceType)
    providerHeaders = providerAdapter.buildHeaders(provider)
    providerBody = convertBody(interfaceType, clientAdapter, providerAdapter, provider, request.body)

    return HTTPRequestSpec {
        url: provider.base_url + providerUrl,
        method: request.method,
        headers: providerHeaders,
        body: providerBody
    }
```

#### 6.1.4 响应转换流程

```
function convertHttpResponse(response, clientProtocol, providerProtocol, interfaceType, modelOverride):
    if isPassthrough(clientProtocol, providerProtocol):
        // 智能透传：改写响应体中的 model 字段
        if modelOverride非空 and response.body非空:
            adapter = registry.get(clientProtocol)
            rewrittenBody = adapter.rewriteResponseModelName(response.body, modelOverride, interfaceType)
            if rewrite失败:
                log.warn("智能透传改写失败，使用原始响应体")
                return response
            return HTTPResponseSpec { statusCode: response.statusCode, headers: response.headers, body: rewrittenBody }
        return response

    // 完整转换车道
    clientAdapter = registry.get(clientProtocol)
    providerAdapter = registry.get(providerProtocol)
    convertedBody = convertResponseBody(interfaceType, clientAdapter, providerAdapter, response.body, modelOverride)

    return HTTPResponseSpec { statusCode: response.statusCode, headers: response.headers, body: convertedBody }
```

**modelOverride 参数语义**：跨协议场景下，客户端期望看到的模型名。在响应转换时直接覆写 `canonicalResponse.model = modelOverride`，在流式转换时覆写 `event.message.model = modelOverride`。

### 6.2 Body 转换分发

#### 6.2.1 接口类型分发策略

| 接口类型 | 请求转换 | 响应转换 | 中间件 | modelOverride |
|---------|---------|---------|--------|---------------|
| CHAT | Decode→**Middleware**→Encode | Decode→modelOverride→Encode | **应用** | 支持（响应） |
| MODELS | Body透传（GET） | Decode→Encode | 不应用 | 不支持 |
| MODEL_INFO | Body透传（GET） | Decode→Encode | 不应用 | 不支持 |
| EMBEDDINGS | Decode→Encode | Decode→modelOverride→Encode | 不应用 | 支持（响应） |
| RERANK | Decode→Encode | Decode→modelOverride→Encode | 不应用 | 支持（响应） |
| PASSTHROUGH | Body透传 | Body透传 | 不应用 | 不支持 |

**关键说明**：
- **只有 CHAT 接口**走完整的 `decode → middleware → encode` 管道
- **扩展层接口**（EMBEDDINGS、RERANK）**跳过中间件**，直接 decode → encode
- **扩展层响应支持 modelOverride**，在 encode 前直接覆写 canonical 字段

#### 6.2.2 请求体转换

```
function convertBody(interfaceType, clientAdapter, providerAdapter, provider, body):
    switch interfaceType:
        case CHAT:
            canonical = clientAdapter.decodeRequest(body)
            canonical = middlewareChain.apply(canonical)
            return providerAdapter.encodeRequest(canonical, provider)
        case MODELS:
            return body                   // GET 请求，无 body
        case MODEL_INFO:
            return body                   // GET 请求，无 body
        case EMBEDDINGS:
            if !clientAdapter.supportsInterface(EMBEDDINGS)
                || !providerAdapter.supportsInterface(EMBEDDINGS):
                return body               // 尽力转换：不支持则透传
            return providerAdapter.encodeEmbeddingRequest(
                clientAdapter.decodeEmbeddingRequest(body), provider)
        case RERANK:
            // 同 EMBEDDINGS 模式
        default:
            return body                   // 透传层：原样转发
```

#### 6.2.3 响应体转换

```
function convertResponseBody(interfaceType, clientAdapter, providerAdapter, body, modelOverride):
    switch interfaceType:
        case CHAT:
            canonical = providerAdapter.decodeResponse(body)
            if modelOverride非空: canonical.model = modelOverride
            return clientAdapter.encodeResponse(canonical)
        case MODELS:
            if !clientAdapter.supportsInterface(MODELS) || !providerAdapter.supportsInterface(MODELS):
                return body
            return clientAdapter.encodeModelsResponse(providerAdapter.decodeModelsResponse(body))
        case MODEL_INFO:
            // 同 MODELS 模式
        case EMBEDDINGS:
            if !clientAdapter.supportsInterface(EMBEDDINGS) || !providerAdapter.supportsInterface(EMBEDDINGS):
                return body
            canonical = providerAdapter.decodeEmbeddingResponse(body)
            if modelOverride非空: canonical.model = modelOverride
            return clientAdapter.encodeEmbeddingResponse(canonical)
        case RERANK:
            // 同 EMBEDDINGS 模式，支持 modelOverride
        default:
            return body                   // 透传层：原样转发
```

### 6.3 StreamConverter

#### 6.3.1 接口定义

```
interface StreamConverter {
    processChunk(rawChunk): Array<RawSSEChunk>
    flush(): Array<RawSSEChunk>
}
```

#### 6.3.2 三种转换器变体

| 转换器 | 触发条件 | processChunk | flush |
|--------|---------|--------------|-------|
| `PassthroughStreamConverter` | 同协议 + 无 modelOverride | `[rawChunk]` | `[]` |
| `SmartPassthroughStreamConverter` | 同协议 + 有 modelOverride | 按 SSE frame 改写 `data` JSON 中的 model；失败输出原 frame | 输出缓存中的未完整 frame |
| `CanonicalStreamConverter` | 不同协议 | Decode→Middleware→modelOverride→Encode | decoder.flush()→encoder.flush() |

#### 6.3.3 PassthroughStreamConverter

```
class PassthroughStreamConverter implements StreamConverter {
    processChunk(rawChunk): Array<RawSSEChunk> { return [rawChunk] }
    flush(): Array<RawSSEChunk> { return [] }
}
```

#### 6.3.4 SmartPassthroughStreamConverter

```
class SmartPassthroughStreamConverter implements StreamConverter {
    adapter: ProtocolAdapter
    modelOverride: String
    interfaceType: InterfaceType
    buffer: ByteArray

    processChunk(rawChunk): Array<RawSSEChunk> {
        if rawChunk为空: return []
        buffer.append(rawChunk)
        frames = splitCompleteSSEFrames(buffer)
        result = []
        for frame in frames:
            payload = extractDataPayload(frame)
            if payload == "[DONE]":
                result.append(frame)
                continue
            rewrittenPayload = adapter.rewriteResponseModelName(payload, modelOverride, interfaceType)
            if rewrite失败:
                log.warn("智能透传改写失败，使用原始 SSE frame")
                result.append(frame)
            else:
                result.append(rebuildSSEFrameWithData(frame, rewrittenPayload))
        return result
    }
    flush(): Array<RawSSEChunk> {
        if buffer为空: return []
        return [buffer.drain()]  // 未完整 frame 原样输出
    }
}
```

#### 6.3.5 CanonicalStreamConverter

```
class CanonicalStreamConverter implements StreamConverter {
    decoder: StreamDecoder
    encoder: StreamEncoder
    middleware: MiddlewareChain
    context: ConversionContext
    clientProtocol: String
    providerProtocol: String
    modelOverride: String

    processChunk(rawChunk):
        events = decoder.processChunk(rawChunk)
        result = []
        for each event in events:
            // 中间件：转换 canonical 事件
            if middleware != null:
                processed, err = middleware.applyStreamEvent(event, clientProtocol, providerProtocol, context)
                if err != null:
                    continue  // 宽容策略：跳过错误事件，继续处理
                event = processed

            // modelOverride：覆写 model 字段
            if modelOverride非空 and event.message != null:
                event.message.model = modelOverride

            // 编码为目标协议 SSE
            chunks = encoder.encodeEvent(event)
            result.append(chunks)
        return result

    flush():
        events = decoder.flush()
        // 同 processChunk 的中间件 + modelOverride + encode 管道
        result = [经过管道处理的所有事件]
        encoderChunks = encoder.flush()
        result.append(encoderChunks)
        return result
}
```

#### 6.3.6 流式转换器创建

```
function createStreamConverter(clientProtocol, providerProtocol, modelOverride, interfaceType):
    if isPassthrough(clientProtocol, providerProtocol):
        if modelOverride非空:
            adapter = registry.get(clientProtocol)
            // 解析 SSE frame，仅改写 data JSON 中的 model；解析失败输出原 frame
            return new SmartPassthroughStreamConverter(adapter, modelOverride, interfaceType)
        // raw passthrough 保留 SSE frame 边界和 [DONE]
        return new PassthroughStreamConverter()

    providerAdapter = registry.get(providerProtocol)
    clientAdapter = registry.get(clientProtocol)

    return new CanonicalStreamConverter(
        decoder: providerAdapter.createStreamDecoder(),
        encoder: clientAdapter.createStreamEncoder(),
        middleware: middlewareChain,
        modelOverride: modelOverride
    )
```

### 6.4 Middleware

引擎内部的拦截钩子，在 decode → encode 之间对 Canonical 进行变换。

```
interface ConversionMiddleware {
    intercept(canonical, clientProtocol, providerProtocol, context): canonical | error
    interceptStreamEvent(event, clientProtocol, providerProtocol, context): event | error
}

ConversionContext {
    conversionId: String       // 唯一转换 ID（UUID）
    interfaceType: InterfaceType
    timestamp: DateTime
    metadata: Map<String, Any>
}
```

#### 6.4.1 中间件执行规则

- `intercept` 返回修改后的 canonical，或返回 ConversionError 以**中断转换**
- `interceptStreamEvent` 返回修改后的 event，或返回 error
- 多个 Middleware 按注册顺序链式执行，任一中断则后续不再执行

#### 6.4.2 错误处理差异

| 场景 | 错误处理策略 |
|------|-------------|
| 请求中间件 `intercept` 返回 error | **严格模式**：中断整个转换，返回错误 |
| 流式中间件 `interceptStreamEvent` 返回 error | **宽容模式**：跳过该事件，继续处理后续事件 |

**设计动机**：流式场景下，单个事件的错误不应中断整个流。请求场景下，错误请求应被明确拒绝。

### 6.5 使用示例

```
engine = new ConversionEngine()
engine.registerAdapter(new OpenAIAdapter())
engine.registerAdapter(new AnthropicAdapter())

// 场景1: 跨协议 Chat 转换
// 入站: /openai/chat/completions
provider = TargetProvider {
    base_url: "https://api.anthropic.com",
    api_key: "xxx",
    model_name: "claude-3-opus",
    adapter_config: { anthropic_version: "2023-06-01" }
}
out = engine.convertHttpRequest(inRequest, "openai", "anthropic", provider)
// 出站: /v1/messages + Anthropic headers + 转换后的 body

// 场景2: /models 跨协议
out = engine.convertHttpRequest(inRequest, "openai", "anthropic", provider)
// URL: /v1/models, headers 按 Anthropic 格式重建

// 场景3: 同协议透传
out = engine.convertHttpRequest(inRequest, "openai", "openai", provider)
// client == provider → 透传车道：重建 headers 后原样转发

// 场景4: 同协议智能透传（改写 model 字段）
provider = TargetProvider { model_name: "gpt-4-turbo", ... }
out = engine.convertHttpRequest(inRequest, "openai", "openai", provider)
// 智能透传车道：请求体中的 model 字段改写为 "gpt-4-turbo"

// 场景5: 流式转换（从 provider 协议解码，编码为 client 协议）
converter = engine.createStreamConverter("openai", "anthropic", "claude-3-opus", CHAT)
for chunk in upstreamSSE {
    for out in converter.processChunk(chunk) { sendToClient(out) }
}
converter.flush()

// 场景6: 同协议流式智能透传
converter = engine.createStreamConverter("openai", "openai", "gpt-4-turbo", CHAT)
// 使用 SmartPassthroughStreamConverter，按 SSE frame 改写 data JSON 中的 model 字段
```

---

## 7. 流式转换架构

### 7.1 转换管道

```
上游 SSE 流
    │
    ├─ 同协议 + 无 modelOverride: PassthroughStreamConverter
    │     raw SSE frame → [raw SSE frame]
    │
    ├─ 同协议 + 有 modelOverride: SmartPassthroughStreamConverter
    │     SSE frame → data JSON model rewrite → [SSE frame]
    │
    └─ 不同协议: CanonicalStreamConverter
          StreamDecoder                          StreamEncoder
          ┌───────────┐                         ┌───────────┐
          │ SSE Parser│                         │SSE Writer │
          └─────┬─────┘                         └─────▲─────┘
                │                                     │
          ┌─────▼─────┐     CanonicalEvent[]    ┌─────┴─────┐
          │ Event      │──────────────────────▶│ Event      │
          │ Translator │     ┌──────────┐      │ Translator │
          │ (状态机)    │     │Middleware│      │            │
          └───────────┘     │(宽容模式) │      └───────────┘
                              └──────────┘
                                    │
                              ┌─────▼─────┐
                              │modelOverride│
                              │(覆写 model) │
                              └───────────┘
```

**流式中间件错误处理**：`interceptStreamEvent` 返回 error 时，跳过该事件继续处理后续事件（宽容模式），而非中断整个流。

### 7.2 StreamDecoder 通用状态

StreamDecoder 需要跟踪以下通用状态。具体协议的 Decoder 可根据需要扩展：

```
StreamDecoderState {
    messageStarted: Boolean
    openBlocks: Set<Integer>
    currentBlockType: Map<Integer, String>
    currentBlockId: Map<Integer, String>
    utf8Remainder: Option<ByteArray>          // UTF-8 跨 chunk 安全
    accumulatedUsage: Option<CanonicalUsage>
}
```

**协议特有状态**：某些协议的 SSE 格式需要额外状态（如工具调用参数累积、工具调用索引映射等），在各自的协议适配文档中定义。

### 7.3 事件映射概述

StreamDecoder 将协议原生 SSE 事件翻译为 CanonicalStreamEvent，StreamEncoder 做反向翻译。映射复杂度取决于协议的 SSE 格式：

- **类型化事件协议**（事件有明确的 start/delta/stop 生命周期）：与 CanonicalStreamEvent 接近 1:1 映射，状态机最简单
- **Delta 模型协议**（只有增量 delta chunk，无显式生命周期）：需要状态机推断 block 边界，管理工具调用索引和参数累积
- **缓冲策略**：部分协议的 Encoder 需要缓冲 ContentBlockStart 事件，等待首次 ContentBlockDelta 时合并输出

各协议的具体事件映射表详见各自的协议适配文档（附录 E）。

---

## 8. 扩展点设计

### 8.1 新协议接入

1. 实现 ProtocolAdapter（URL 映射 + Header 映射 + 各接口编解码）
2. 注册到 AdapterRegistry
3. 完成

按附录 D 的清单模板逐项确认，所有项目确认后即可与引擎对接。

### 8.2 多模态扩展

**状态**：Deferred（未实现）

Canonical Model 已预留 ImageBlock / AudioBlock / VideoBlock / FileBlock。实现路径：
1. 在各 ProtocolAdapter 中实现多模态块的编解码
2. 在 StreamDecoder/StreamEncoder 中处理多模态增量数据

### 8.3 有状态特性扩展

**状态**：Deferred（未实现）

```
interface StatefulMiddleware extends ConversionMiddleware {
    stateStore: SessionStateStore
}
```

适用场景：跨轮次保留协议特有的状态（如 thinking signature）。

### 8.4 特性降级策略

当 Canonical 公共字段在目标协议中无直接等价物时：
- 有语义等价物 → 自动映射
- 无等价物 → 丢弃，日志 warn
- 有替代方案 → 降级策略处理（如注入合成工具实现 JSON 模式）

各协议的具体降级规则详见各自的协议适配文档（附录 E）。

### 8.5 自定义接口支持

**状态**：Deferred（未实现）

```
interface CustomInterfaceHandler {
    interfaceType(): InterfaceType
    matchUrl(url): Boolean
    convertRequest(client, provider, raw): raw
    convertResponse(client, provider, raw): raw
}
engine.registerCustomHandler(handler)
```

当前实现中，未知接口直接走 PASSTHROUGH 透传。

---

## 9. 错误处理

### 9.1 错误分类

```
ConversionError { code: ErrorCode, message, clientProtocol?, providerProtocol?,
                  interfaceType?, details?, cause? }

ErrorCode = Enum<
    INVALID_INPUT,              // 请求格式不符合协议规范
    MISSING_REQUIRED_FIELD,     // 缺少必填字段
    INCOMPATIBLE_FEATURE,       // 特性在目标协议不可用
    FIELD_MAPPING_FAILURE,      // 字段映射逻辑错误
    TOOL_CALL_PARSE_ERROR,      // 工具调用参数解析失败
    JSON_PARSE_ERROR,           // JSON 解析失败
    STREAM_STATE_ERROR,         // 流式状态机异常
    UTF8_DECODE_ERROR,          // UTF-8 解码错误
    PROTOCOL_CONSTRAINT_VIOLATION, // 违反协议约束
    ENCODING_FAILURE,           // 编码失败
    INTERFACE_NOT_SUPPORTED,    // 目标协议不支持此接口
    UNSUPPORTED_MULTIMODAL      // 多模态内容块暂不支持跨协议转换
>
```

### 9.2 错误处理策略

引擎采用**分层宽容策略**，根据接口层级和场景选择不同的错误处理方式：

```
┌─────────────────────────────────────────────────────────────────────┐
│                      错误处理分层策略                                 │
├─────────────────┬───────────────────────────────────────────────────┤
│ 核心层（CHAT）    │ 严格模式：任何错误返回 ConversionError，中断转换    │
│                 │ - Decode 失败 → 返回 JSON_PARSE_ERROR             │
│                 │ - Middleware 失败 → 返回错误                       │
│                 │ - Encode 失败 → 返回 ENCODING_FAILURE             │
├─────────────────┼───────────────────────────────────────────────────┤
│ 扩展层           │ 宽容模式：记录警告日志，返回原始 body 透传          │
│ (Models/Embed/  │ - Decode 失败 → log.warn + 返回原始 body           │
│  Rerank)        │ - Encode 失败 → log.warn + 返回原始 body           │
├─────────────────┼───────────────────────────────────────────────────┤
│ 流式中间件       │ 宽容模式：跳过错误事件，继续处理后续事件            │
│                 │ - interceptStreamEvent 返回 error → continue       │
├─────────────────┼───────────────────────────────────────────────────┤
│ 智能透传         │ 宽容模式：重写失败则使用原始 body/chunk            │
│                 │ - Rewrite 失败 → log.warn + 返回原始 body/SSE frame│
├─────────────────┼───────────────────────────────────────────────────┤
│ 请求中间件       │ 严格模式：返回错误则中断整个转换                    │
│                 │ - intercept 返回 error → 返回 error               │
└─────────────────┴───────────────────────────────────────────────────┘
```

**设计动机**：
- 核心接口（CHAT）必须保证语义正确性，错误应明确暴露
- 扩展层接口优先保证可用性，错误应降级处理
- 流式场景不应因单个事件错误中断整个流

**不支持接口的处理**（`INTERFACE_NOT_SUPPORTED`）：

| 策略 | 适用场景 | 实现 |
|------|---------|------|
| 透传 | 上游可能有自己的实现 | URL+Header 适配后 Body 原样转发 |
| 返回空响应 | 不影响核心功能 | 返回空列表 `{data: []}` |
| 返回错误 | 客户端明确需要此功能 | 返回 501 或协议格式错误 |

具体策略由 `supportsInterface` 返回值决定：返回 false 时引擎直接透传 body。

**多模态处理**（`UNSUPPORTED_MULTIMODAL`）：Canonical Model 保留 image、audio、video、file 内容块占位，但当前跨协议多模态编解码暂未实现。跨协议完整转换遇到这些内容块时返回 `UNSUPPORTED_MULTIMODAL`；同协议 raw passthrough 和 smart passthrough 不拒绝多模态字段，仍按原协议请求透传或仅改写 model。

### 9.3 错误响应格式

ConversionEngine 的 `encodeError` 是协议 Adapter 的错误编码能力，用于 SDK 内部或非代理场景把 `ConversionError` 编码为客户端协议格式。各协议的错误响应 JSON 结构和 HTTP 状态码映射详见各自的协议适配文档（附录 E）。

ProxyHandler 对外遵循更明确的代理错误边界：

| 场景 | 响应策略 |
|------|---------|
| 网关层错误 | 返回应用统一 JSON：`{"error": "...", "code": "..."}` |
| 上游已返回 HTTP 非 2xx | 透传上游 status、过滤 hop-by-hop 后的 headers、body |
| 未收到上游 HTTP 响应 | 返回 `UPSTREAM_UNAVAILABLE`，HTTP 502 |
| 网关层 JSON 解析失败 | 返回 `INVALID_JSON`，HTTP 400 |
| 路由统一模型失败 | 返回 `MODEL_NOT_FOUND`，HTTP 404 |
| 跨协议转换失败 | 返回 `CONVERSION_FAILED` 或具体转换错误码 |

因此，代理接口中的网关层错误不会再编码成 OpenAI/Anthropic 协议错误格式；只有上游已经返回的错误响应才按上游原样透传。

#### 9.3.1 EncodeError Fallback 行为

当客户端适配器不可用时，引擎使用通用 JSON 错误作为 fallback：

```
function encodeError(error, clientProtocol):
    adapter = registry.get(clientProtocol)
    if adapter不存在:
        // Fallback: 通用 JSON 错误
        return {
            "error": {
                "message": error.message,
                "type": "internal_error"
            }
        }, 500
    return adapter.encodeError(error)
```

---

## 附录 A：模块依赖

```
┌──────────────────────────────────────────────────┐
│                ConversionEngine                   │
│  门面：HTTP 转换 / 透传判断 / 流式转换              │
│  无状态；协议识别见 §2.2                            │
├──────────────────────────────────────────────────┤
│  HTTPRequestSpec / HTTPResponseSpec              │
│  url, method, headers, body / statusCode, ...    │
├──────────────────────────────────────────────────┤
│              TargetProvider                       │
│  base_url / api_key / model_name / adapter_config │
├──────────────────┬───────────────────────────────┤
│ AdapterRegistry  │       MiddlewareChain          │
├──────────────────┴───────────────────────────────┤
│  StreamConverter: Passthrough | SmartPassthrough | Canonical │
├──────────────────────────────────────────────────┤
│  ProtocolAdapter: 各协议实现                      │
│  · buildHeaders(provider) · URL 映射             │
│  · Chat/Models/ModelInfo/Embeddings/Rerank/... 编解码  │
│  · encodeError · StreamDecoder / StreamEncoder    │
│  · rewriteRequestModelName / rewriteResponseModelName (智能透传) │
├──────────────────────────────────────────────────┤
│  Canonical Model (Core + Extended)                │
├──────────────────────────────────────────────────┤
│  Error Handling (分层宽容策略)                     │
├──────────────────────────────────────────────────┤
│  Utility: UTF-8 Buffer / SSE Parser / Detector   │
└──────────────────────────────────────────────────┘
```

---

## 附录 B：接口速查

```
// ─── 核心入口 ───
ConversionEngine
  .registerAdapter(adapter): void
  .use(middleware): void
  .getRegistry(): AdapterRegistry
  .isPassthrough(clientProtocol, providerProtocol): Boolean
  .convertHttpRequest(request, clientProtocol, providerProtocol, provider): HTTPRequestSpec
  .convertHttpResponse(response, clientProtocol, providerProtocol, interfaceType, modelOverride): HTTPResponseSpec
  .createStreamConverter(clientProtocol, providerProtocol, modelOverride, interfaceType): StreamConverter
  .detectInterfaceType(nativePath, clientProtocol): InterfaceType
  .encodeError(error, clientProtocol): (body, statusCode)

// ─── HTTP 规格 ───
HTTPRequestSpec { url, method, headers, body }
HTTPResponseSpec { statusCode, headers, body }

// ─── 目标上游信息 ───
TargetProvider { base_url, api_key, model_name, adapter_config }

// ─── 接口类型 ───
InterfaceType = CHAT | MODELS | MODEL_INFO | EMBEDDINGS | RERANK | PASSTHROUGH

// ─── 协议适配器 ───
ProtocolAdapter
  .protocolName() / .protocolVersion() / .supportsPassthrough()
  .detectInterfaceType(nativePath) / .buildUrl(nativePath, type) / .buildHeaders(provider) / .supportsInterface(type)
  .decodeRequest(raw) / .encodeRequest(canonical, provider)
  .decodeResponse(raw) / .encodeResponse(canonical)
  .createStreamDecoder() / .createStreamEncoder()
  .encodeError(error): (body, statusCode)
  // 扩展层
  .decodeModelsResponse / .encodeModelsResponse
  .decodeModelInfoResponse / .encodeModelInfoResponse
  .decodeEmbeddingRequest / .encodeEmbeddingRequest / ...Response
  .decodeRerankRequest / .encodeRerankRequest / ...Response
  // 智能透传支持
  .extractUnifiedModelID(nativePath)
  .extractModelName(body, interfaceType)
  .rewriteRequestModelName(body, newModel, interfaceType)
  .rewriteResponseModelName(body, newModel, interfaceType)

// ─── 流式处理 ───
StreamConverter: .processChunk(raw) / .flush()
  ├─ PassthroughStreamConverter       [raw] → [raw]
  ├─ SmartPassthroughStreamConverter  [SSE frame] → [rewrite(data JSON)]
  └─ CanonicalStreamConverter         decode → middleware → modelOverride → encode

// ─── 中间件 ───
ConversionMiddleware
  .intercept(req, clientProtocol, providerProtocol, ctx): (req, error)
  .interceptStreamEvent(event, clientProtocol, providerProtocol, ctx): (event, error)

ConversionContext { conversionId, interfaceType, timestamp, metadata }
```

---

## 附录 C：字段晋升规范

### 原则

Canonical Model 是**活的公共契约**，不是固定不变的。其字段集反映的是**当前已适配协议的公共语义**，随协议扩展而演进。

### 字段分类

| 分类 | 判定条件 | 处理方式 |
|------|---------|---------|
| **公共字段** | ≥2 个协议表达相同含义 | 升级为 Canonical Model 的正式字段 |
| **协议特有字段** | 仅 1 个协议使用 | 不在中间层传播；同协议透传时自然保留；跨协议时丢弃 |
| **未知字段** | 新协议带来的新概念 | 先按特有字段处理；当第二个协议也出现时晋升 |

### 晋升流程

```
1. 发现：适配新协议时，识别出无法映射到现有 Canonical 字段的语义
2. 判定：该语义是否已被 ≥1 个现有协议所表达？
   - 是 → 晋升为 Canonical 公共字段
   - 否 → 暂不纳入，记录在适配清单中
3. 设计：确定字段名（协议中立）、类型、在 Canonical 中的位置
4. 更新：修改 Canonical Model 定义，所有现有 Adapter 的 Decoder/Encoder 同步更新
5. 文档：更新本节说明
```

### 晋升示例

| 字段 | 晋升原因 |
|------|---------|
| `user_id` | 多协议均支持用户标识，但字段名和嵌套位置不同 |
| `output_format` | 多协议均支持控制输出格式，但字段名不同 |
| `parallel_tool_use` | 多协议均支持并行工具调用控制，但语义方向（允许/禁止）不同 |
| `top_k` | 多协议（Gemini、OpenAI 兼容厂商、Ollama 等）均支持，但字段名和嵌套位置不同 |
| `frequency_penalty` / `presence_penalty` | OpenAI 生态广泛使用，多协议均支持类似概念 |
| `stop_reason: refusal` | OpenAI 和 Gemini 均有安全拒绝/内容过滤场景，语义一致 |

### 降级规则

当公共字段的目标协议不支持时：
- 有语义等价物 → 自动映射
- 无等价物 → 丢弃，日志 warn
- 有替代方案 → 降级策略处理

---

## 附录 D：协议适配清单模板

适配新协议时，按以下清单逐项确认。所有项目确认后即可与引擎对接。

**方法调用时机**：理解各方法何时被引擎调用，有助于正确实现：
- `decodeRequest` / `encodeRequest`：引擎在 `convertBody(CHAT)` 中调用（§6.2），对每个 Chat 请求执行 decode → middleware → encode 管道
- `decodeResponse` / `encodeResponse`：引擎在 `convertResponseBody(CHAT)` 中调用，对每个 Chat 响应执行反向管道
- `decodeXxxRequest` / `encodeXxxRequest`：扩展层接口仅在 `supportsInterface` 返回 true 时被调用（§6.2 convertBody 分支）；返回 false 时引擎直接透传 body
- `createStreamDecoder` / `createStreamEncoder`：引擎在 `createStreamConverter` 中调用（§6.1），Decoder 来自 provider 协议（解码上游 SSE），Encoder 来自 client 协议（编码给客户端）
- `buildHeaders`：每次请求出站时调用，同协议透传也会调用
- `encodeError`：转换失败或 Middleware 中断时的协议错误编码能力；ProxyHandler 的网关层错误使用应用统一格式，不调用协议错误编码

### D.1 协议基本信息

| 项目 | 说明 |
|------|------|
| 协议名称 | 用于 URL 前缀和 Adapter 注册的唯一标识 |
| 协议版本 | 当前适配的 API 版本 |
| Base URL | API 服务地址 |
| 认证方式 | Header 名称和格式 |

### D.2 接口识别

| 项目 | 说明 |
|------|------|
| URL 路径模式 | 列出所有接口的 URL 路径和对应的 InterfaceType，由 `detectInterfaceType` 实现 |
| 接口能力矩阵 | 每种 InterfaceType 的支持状态（`supportsInterface`） |
| URL 映射表 | 每种 InterfaceType 的目标 URL 路径（`buildUrl`） |

**重要**：`detectInterfaceType` 由各协议 Adapter 实现，因为不同协议有不同的 URL 路径约定。例如：
- OpenAI: `/chat/completions` → CHAT
- Anthropic: `/v1/messages` → CHAT

### D.3 请求头构建

| 项目 | 说明 |
|------|------|
| 认证头 | 如何从 `provider.api_key` 构建认证 Header |
| 必需 Header | 协议要求的固定 Header（如 Content-Type） |
| 可选 Header | 根据功能动态添加的 Header |
| adapter_config 契约 | 定义本 Adapter 从 `provider.adapter_config` 读取的 key 列表和默认值 |

### D.4 核心层 — Chat 请求编解码

逐字段对照 §4.1 CanonicalRequest 和 §4.2 RequestParameters 确认映射关系。

#### Decoder（协议 → Canonical）

| 项目 | 说明 |
|------|------|
| 系统消息 | 如何提取为 `canonical.system`（顶层字段 / messages 中 / 不支持） |
| 消息角色 | 协议角色与 Canonical 角色（§4.3：system/user/assistant/tool）的映射 |
| 内容块 | 逐类型对照 §4.3 ContentBlock 联合体，确认每种类型的解码规则 |
| 工具定义 | 对照 §4.4 CanonicalTool，确认字段名映射 |
| 工具选择 | 对照 §4.4 ToolChoice 联合体，确认各变体的映射规则 |
| 参数映射 | 对照 §4.2 RequestParameters，逐字段确认映射和类型转换 |
| 公共字段 | 逐字段对照 §4.1 中 `user_id`、`output_format`、`parallel_tool_use`、`thinking`，确认提取规则 |
| 协议特有字段 | 仅本协议使用的字段列表，以及处理方式（忽略/记录） |
| 协议约束 | 消息顺序要求、角色交替要求、必填字段等 |

#### Encoder（Canonical → 协议）

| 项目 | 说明 |
|------|------|
| 模型名称 | 使用 `provider.model_name` 覆盖 `canonical.model` |
| 系统消息注入 | 如何将 `canonical.system` 编码为协议格式 |
| 消息编码 | 各角色的编码规则（特别注意 role 映射、content 结构差异） |
| 角色约束处理 | 是否需要 enforceAlternation？具体策略？ |
| 工具编码 | 对照 §4.4 确认 tools/tool_choice 的编码规则 |
| 参数编码 | 对照 §4.2 确认参数映射 |
| 公共字段编码 | 逐字段确认注入规则 |
| 降级处理 | 对照 §8.4 三级降级策略（自动映射/丢弃/替代方案），确认每个不支持字段的处理方式 |

### D.5 核心层 — Chat 响应编解码

逐字段对照 §4.7 CanonicalResponse 确认映射关系。

#### Decoder（协议 → Canonical）

| 项目 | 说明 |
|------|------|
| 响应结构 | 协议响应的顶层结构解析 |
| 内容块解码 | 逐类型对照 §4.3 ContentBlock 联合体，确认解码规则 |
| 停止原因 | 协议原生值与 §4.7 StopReason 枚举的映射表 |
| Token 用量 | 对照 §4.7 CanonicalUsage，逐字段确认映射和命名差异 |
| 推理内容 | ThinkingBlock 的解码规则 |
| 协议特有内容 | 仅本协议返回的特有字段的处理（忽略/丢弃/记录） |

#### Encoder（Canonical → 协议）

| 项目 | 说明 |
|------|------|
| 响应结构 | 如何从 CanonicalResponse 构建协议响应的顶层结构 |
| 内容块编码 | 各 ContentBlock 类型到协议格式的编码规则 |
| 停止原因 | §4.7 StopReason 到协议原生值的映射表 |
| Token 用量 | CanonicalUsage 到协议 usage 字段的编码规则 |
| 推理内容 | ThinkingBlock 的编码规则 |
| 降级处理 | Canonical 中协议不支持的 stop_reason 等值的降级映射 |

### D.6 核心层 — 流式编解码

对照 §4.8 CanonicalStreamEvent 和 §7.2 StreamDecoderState 确认映射和状态设计。

#### StreamDecoder（协议 SSE → Canonical 事件）

| 项目 | 说明 |
|------|------|
| SSE 格式 | 协议的 SSE 事件格式（named events vs delta chunks） |
| 事件映射表 | 每种协议 SSE 事件 → §4.8 CanonicalStreamEvent 的映射规则 |
| 状态机设计 | 在 §7.2 通用状态基础上，声明本协议需要的额外状态 |
| UTF-8 安全 | 是否需要处理跨 chunk 的 UTF-8 截断 |
| 特殊情况 | 工具调用参数乱序、无限空白检测、延迟字段等 |

#### StreamEncoder（Canonical 事件 → 协议 SSE）

| 项目 | 说明 |
|------|------|
| 事件映射表 | §4.8 每个 CanonicalStreamEvent → 协议 SSE chunk 的映射规则 |
| 缓冲策略 | 哪些事件需要缓冲、何时输出 |
| SSE 格式 | event type / data 字段的编码方式 |
| 结束标记 | 如何输出流结束信号（如 `[DONE]`） |

### D.7 扩展层接口

对每种扩展层接口逐项确认。当前扩展层 InterfaceType 见 §5.3 枚举（MODELS、MODEL_INFO、EMBEDDINGS、RERANK），随协议扩展可能增加。

| 项目 | 说明 |
|------|------|
| 接口是否存在 | 该协议是否原生支持此接口 |
| URL 路径 | 接口的 URL 路径 |
| 请求格式 | 对照 §4.9 扩展层 Canonical Models，确认请求体映射 |
| 响应格式 | 对照 §4.9 扩展层 Canonical Models，确认响应体映射 |
| 不支持时策略 | 透传 / 返回空响应 / 返回错误 |

### D.8 错误编码

| 项目 | 说明 |
|------|------|
| 错误响应格式 | 协议的错误响应 JSON 结构 |
| encodeError | §9.1 ConversionError → 协议错误格式的编码规则 |
| HTTP 状态码 | 协议常用的错误状态码映射 |

### D.9 自检清单

| 章节 | 检查项 |
|------|--------|
| D.2 | [ ] `detectInterfaceType(nativePath)` 已实现，所有已知路径已覆盖 |
| D.2 | [ ] 所有 InterfaceType 的 `supportsInterface` 返回值已确定 |
| D.2 | [ ] 所有 InterfaceType 的 `buildUrl` 映射已确定 |
| D.3 | [ ] `buildHeaders(provider)` 已实现，adapter_config 契约已文档化 |
| D.4 | [ ] Chat 请求的 Decoder 和 Encoder 已实现（逐字段对照 §4.1/§4.2） |
| D.4 | [ ] 角色映射和消息顺序约束已处理 |
| D.4 | [ ] 工具调用（tool_use / tool_result）的编解码已处理 |
| D.4 | [ ] 协议特有字段已识别并确定处理方式（忽略/降级） |
| D.5 | [ ] Chat 响应的 Decoder 和 Encoder 已实现（逐字段对照 §4.7） |
| D.5 | [ ] stop_reason 映射表已确认 |
| D.5 | [ ] usage 字段映射已确认 |
| D.6 | [ ] 流式 StreamDecoder 和 StreamEncoder 已实现（对照 §4.8） |
| D.7 | [ ] 扩展层接口的编解码已实现（支持的接口） |
| D.8 | [ ] `encodeError` 已实现 |
| D.10 | [ ] `extractUnifiedModelID(nativePath)` 已实现 |
| D.10 | [ ] `extractModelName(body, interfaceType)` 已实现（覆盖 Chat/Embeddings/Rerank） |
| D.10 | [ ] `rewriteRequestModelName` 已实现（幂等、最小化） |
| D.10 | [ ] `rewriteResponseModelName` 已实现（按接口类型处理 model 字段存在性） |

### D.10 智能透传支持

| 项目 | 说明 |
|------|------|
| extractUnifiedModelID | 从路径提取统一模型 ID 的规则（如 `/models/{provider_id}/{model_name}`） |
| extractModelName | 从请求体提取 model 字段值的规则（按接口类型：Chat/Embeddings/Rerank） |
| rewriteRequestModelName | 请求体 model 字段改写规则（最小化 JSON 手术，仅修改 model 字段） |
| rewriteResponseModelName | 响应体 model 字段改写规则（按接口类型处理 model 字段存在性） |

**rewriteResponseModelName 的接口类型差异**：

| 接口类型 | model 字段处理 |
|---------|---------------|
| CHAT | 存在则改写，不存在则添加（协议要求必须有 model 字段） |
| EMBEDDINGS | 存在则改写，不存在则添加（协议要求必须有 model 字段） |
| RERANK | 存在则改写，不存在则不添加（model 字段可选） |
| 其他 | 直接返回原始 body |

---

## 附录 E：协议适配文档索引

| 协议 | 适配文档 |
|------|---------|
| OpenAI | [conversion_openai.md](./conversion_openai.md) |
| Anthropic | [conversion_anthropic.md](./conversion_anthropic.md) |

新增协议时，按附录 D 模板编撰适配文档，并将链接添加到上表。
