# CC-Switch API 转换层详细分析报告

## 1. 项目概述

**CC-Switch** 是一个基于 **Tauri (Rust 后端 + Web 前端)** 的桌面应用，用于管理多个大模型编程工具（Claude Code、Codex CLI、Gemini CLI 等）的配置切换。其核心功能之一是**本地 HTTP 代理服务器**，在客户端与上游 LLM API 之间充当协议翻译层，使仅支持特定 API 格式的客户端（如 Claude Code 只支持 Anthropic Messages API）能通过代理访问不同协议的后端服务。

### 核心能力

- 将 **Anthropic Messages API** 请求/响应转换为 **OpenAI Chat Completions**、**OpenAI Responses API**、**Google Gemini Native** 等格式
- 支持**流式 (SSE)** 和**非流式**两种模式的实时协议翻译
- 内置故障转移、熔断器、Thinking 自修复、Copilot 请求优化等企业级特性
- 支持 GitHub Copilot、ChatGPT Plus/Pro (Codex OAuth) 等动态 Token 认证的供应商

### 与同类项目的核心差异

CC-Switch 是唯一的**桌面端**代理工具（非服务端部署），面向个人开发者场景。其协议转换是**单方向锚定**的——以 Anthropic Messages API 为唯一"客户端协议"，向多种上游协议转换。相比 One-API / New-API 的多协议互转、LiteLLM 的 OpenAI-centric 统一格式，CC-Switch 的转换矩阵更聚焦但深度更大（如 Gemini Shadow Store、Copilot Optimizer 等属于独有的复杂机制）。

---

## 2. 技术架构总览

```
┌─────────────────────────────────────────────────────────────────┐
│                    CC-Switch Tauri 桌面应用                       │
├─────────────────────────────────────────────────────────────────┤
│  Web 前端 (React)  │          Rust 后端 (Tauri)                  │
│                    │  ┌──────────────────────────────────────┐   │
│  - 供应商管理      │  │       proxy/ 模块 (Axum HTTP Server)  │   │
│  - 配置切换        │  │                                      │   │
│  - 用量展示        │  │  ┌──────────┐   ┌────────────────┐   │   │
│                    │  │  │ server.rs│──▶│  handlers.rs   │   │   │
│                    │  │  │ (路由层)  │   │  (请求分发器)   │   │   │
│                    │  │  └──────────┘   └───────┬────────┘   │   │
│                    │  │                         │             │   │
│                    │  │  ┌──────────────────────▼─────────┐   │   │
│                    │  │  │       forwarder.rs (转发器)      │   │   │
│                    │  │  │  - 供应商选择 / 故障转移         │   │   │
│                    │  │  │  - 请求体预处理 / 参数过滤       │   │   │
│                    │  │  │  - Thinking 自修复 / Copilot优化  │   │   │
│                    │  │  └──────────────┬─────────────────┘   │   │
│                    │  │                 │                     │   │
│                    │  │  ┌──────────────▼─────────────────┐   │   │
│                    │  │  │     providers/ (适配器层)        │   │   │
│                    │  │  │                                 │   │   │
│                    │  │  │  ┌─────────┐  ┌──────────────┐  │   │   │
│                    │  │  │  │adapter.rs│  │   claude.rs  │  │   │   │
│                    │  │  │  │(Trait定义)│  │ (Claude适配器)│  │   │   │
│                    │  │  │  └─────────┘  ├──────────────┤  │   │   │
│                    │  │  │               │   codex.rs    │  │   │   │
│                    │  │  │               ├──────────────┤  │   │   │
│                    │  │  │               │   gemini.rs   │  │   │   │
│                    │  │  │               └──────────────┘  │   │   │
│                    │  │  └─────────────────────────────────┘   │   │
│                    │  │                                        │   │
│                    │  │  ┌─────────────────────────────────┐   │   │
│                    │  │  │   转换层 (transform/ 模块)        │   │   │
│                    │  │  │                                 │   │   │
│                    │  │  │  transform.rs         (→Chat)   │   │   │
│                    │  │  │  transform_responses.rs (→Resp)  │   │   │
│                    │  │  │  transform_gemini.rs    (→Gemini)│   │   │
│                    │  │  └─────────────────────────────────┘   │   │
│                    │  │                                        │   │
│                    │  │  ┌─────────────────────────────────┐   │   │
│                    │  │  │   流式转换层 (streaming/ 模块)     │   │   │
│                    │  │  │                                 │   │   │
│                    │  │  │  streaming.rs          (Chat)   │   │   │
│                    │  │  │  streaming_responses.rs (Resp)   │   │   │
│                    │  │  │  streaming_gemini.rs    (Gemini) │   │   │
│                    │  │  └─────────────────────────────────┘   │   │
│                    │  │                                        │   │
│                    │  │  ┌─────────────────────────────────┐   │   │
│                    │  │  │   辅助模块                        │   │   │
│                    │  │  │  gemini_shadow.rs  (状态回放)     │   │   │
│                    │  │  │  gemini_schema.rs  (Schema转换)   │   │   │
│                    │  │  │  copilot_optimizer.rs (Copilot优化)│   │   │
│                    │  │  │  thinking_rectifier.rs (Thinking修复)│   │
│                    │  │  │  thinking_budget_rectifier.rs     │   │   │
│                    │  │  │  cache_injector.rs (缓存注入)     │   │   │
│                    │  │  │  body_filter.rs   (私有参数过滤)   │   │   │
│                    │  │  │  model_mapper.rs  (模型名映射)     │   │   │
│                    │  │  │  gemini_url.rs    (Gemini URL构建)│   │   │
│                    │  │  └─────────────────────────────────┘   │   │
│                    │  └──────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────┘
```

**源文件规模**：`proxy/` 目录共 53 个源文件，其中核心转换层 6 个文件合计约 7,700 行，辅助模块 7 个文件合计约 4,000 行，转发器和处理器合计约 3,700 行。

---

## 3. 路由层 (server.rs)

**文件**: `src-tauri/src/proxy/server.rs` (354 行)

代理服务器基于 **Axum** 框架构建，使用手动 hyper HTTP/1.1 accept loop（通过 TCP `peek()` 捕获原始请求头大小写，存入 `OriginalHeaderCases` 扩展供转发器使用）。

### 路由表

| 路由路径 | 处理器 | 协议 |
|---------|--------|------|
| `/v1/messages`, `/claude/v1/messages` | `handle_messages` | Claude (Anthropic Messages API) |
| `/chat/completions`, `/v1/chat/completions`, `/v1/v1/chat/completions`, `/codex/v1/chat/completions` | `handle_chat_completions` | OpenAI Chat Completions |
| `/responses`, `/v1/responses`, `/v1/v1/responses`, `/codex/v1/responses` | `handle_responses` | OpenAI Responses API |
| `/responses/compact`, `/v1/responses/compact`, `/v1/v1/responses/compact`, `/codex/v1/responses/compact` | `handle_responses_compact` | Responses Compact (Codex CLI 远程压缩透传) |
| `/v1beta/*path`, `/gemini/v1beta/*path` | `handle_gemini` | Gemini (Google AI API) |
| `/health` | `health_check` | 健康检查 |
| `/status` | `get_status` | 状态查询 |

**关键设计细节**:
- **多前缀兼容**: 裸路径 (`/v1/...`) 和带供应商前缀 (`/claude/`, `/codex/`, `/gemini/`) 均支持，兼容不同客户端的 `base_url` 配置
- **双前缀容错**: `/v1/v1/...` 路由处理客户端双重前缀的配置错误
- **无前缀路由**: `/chat/completions` 和 `/responses` 不带 `/v1/`，适配 Codex CLI 的特定 base_url 配置
- **请求体限制**: `DefaultBodyLimit::max(200MB)`，支撑大体积上下文和图片请求

---

## 4. 适配器层 (Adapter Pattern)

### 4.1 ProviderAdapter Trait

**文件**: `src-tauri/src/proxy/providers/adapter.rs` (50 行)

所有供应商适配器都实现统一的 `ProviderAdapter` trait：

```rust
pub trait ProviderAdapter: Send + Sync {
    fn name(&self) -> &'static str;
    fn extract_base_url(&self, provider: &Provider) -> Result<String, ProxyError>;
    fn extract_auth(&self, provider: &Provider) -> Option<AuthInfo>;
    fn build_url(&self, base_url: &str, endpoint: &str) -> String;
    fn get_auth_headers(&self, auth: &AuthInfo) -> Vec<(HeaderName, HeaderValue)>;
    fn needs_transform(&self, _provider: &Provider) -> bool;         // 默认 false
    fn transform_request(&self, body: Value, provider: &Provider) -> Result<Value, ProxyError>;  // 默认透传
    fn transform_response(&self, body: Value) -> Result<Value, ProxyError>;  // 默认透传
}
```

其中 `needs_transform()`、`transform_request()`、`transform_response()` 提供默认实现（透传），供应商适配器仅需覆写需要的方法。

### 4.2 供应商类型枚举 (ProviderType)

**文件**: `src-tauri/src/proxy/providers/mod.rs` (515 行)

系统定义 8 种供应商类型，决定认证和请求处理逻辑：

| ProviderType | 说明 | 认证策略 | 是否需要转换 |
|---|---|---|---|
| `Claude` | Anthropic 官方 API | Anthropic (`x-api-key`) | 视 api_format 决定 |
| `ClaudeAuth` | Claude 中转服务 (仅 Bearer) | ClaudeAuth | 视 api_format 决定 |
| `Codex` | OpenAI Codex | Bearer | 否 |
| `Gemini` | Google Gemini API (API Key) | Google (`x-goog-api-key`) | 否 |
| `GeminiCli` | Gemini CLI (OAuth) | GoogleOAuth | 否 |
| `OpenRouter` | OpenRouter 中转 | Bearer | 否 (已支持原生) |
| `GitHubCopilot` | GitHub Copilot | GitHubCopilot (动态 Token) | **是** → OpenAI Chat/Responses |
| `CodexOAuth` | ChatGPT Plus/Pro 反代 | CodexOAuth (动态 Token) | **是** → OpenAI Responses |

**适配器工厂** `get_adapter_for_provider_type()` 的映射逻辑：
- `Claude`, `ClaudeAuth`, `OpenRouter`, `GitHubCopilot`, `CodexOAuth` → `ClaudeAdapter`
- `Codex` → `CodexAdapter`
- `Gemini`, `GeminiCli` → `GeminiAdapter`

### 4.3 API 格式识别

**文件**: `src-tauri/src/proxy/providers/claude.rs` (1313 行)

Claude 适配器通过 `get_claude_api_format()` 函数识别需要使用的 API 格式，优先级为：

1. **Codex OAuth 强制**: `meta.provider_type == "codex_oauth"` → `openai_responses`
2. **meta.apiFormat (SSOT)**: `meta.api_format` 字段 → `openai_chat` / `openai_responses` / `gemini_native`
3. **settings_config.api_format**: 旧版兼容
4. **openrouter_compat_mode**: 旧版 OpenRouter 兼容开关 → `openai_chat`
5. **默认**: `anthropic` (直接透传)

### 4.4 认证策略

**文件**: `src-tauri/src/proxy/providers/auth.rs` (259 行)

7 种认证策略覆盖所有上游服务：

| 策略 | Header 注入 |
|------|-----------|
| `Anthropic` | `x-api-key: <key>` + `anthropic-version: 2023-06-01` (版本由转发器注入) |
| `ClaudeAuth` | `Authorization: Bearer <key>` (无 `x-api-key`) |
| `Bearer` | `Authorization: Bearer <key>` |
| `Google` | `x-goog-api-key: <key>` |
| `GoogleOAuth` | `Authorization: Bearer <token>` + `x-goog-client: GeminiCLI/1.0` |
| `GitHubCopilot` | `Authorization: Bearer <token>` + 多个 Copilot 专有 Header (`openai-intent`, `x-initiator`, `x-interaction-type`, `x-request-id`, `x-agent-task-id` 等约 10 个) |
| `CodexOAuth` | `Authorization: Bearer <token>` + `originator: cc-switch` + `chatgpt-account-id` |

**动态认证机制**: GitHub Copilot 和 Codex OAuth 的 Token 是动态获取的，通过 `CopilotAuthManager` 和 `CodexOAuthAuth` 分别管理 Token 的刷新和多账户绑定（通过 `meta.authBinding` 实现供应商到账户的映射）。

---

## 5. 转换层详解 — 核心协议转换

### 5.1 Anthropic ↔ OpenAI Chat Completions

**文件**: `src-tauri/src/proxy/providers/transform.rs` (1193 行)

#### 请求转换: `anthropic_to_openai(body) → Value`

将 Anthropic Messages API 请求转换为 OpenAI Chat Completions 请求。

**字段映射**:

| Anthropic 字段 | OpenAI 字段 | 说明 |
|---|---|---|
| `system` (string) | `messages[0].role = "system"` | System prompt 提升为 system role message |
| `system` (array) | `messages[0].role = "system"` (合并) | 多个 system message 合并，冲突的 `cache_control` 被丢弃 |
| `messages` | `messages` | 按角色转换内容格式 |
| `max_tokens` | `max_tokens` / `max_completion_tokens` | o-series 模型使用 `max_completion_tokens` |
| `temperature` | `temperature` | 直接透传 |
| `top_p` | `top_p` | 直接透传 |
| `stop_sequences` | `stop` | 直接透传 |
| `tools[].input_schema` | `tools[].function.parameters` | Schema 格式转换，过滤 `BatchTool`，移除 `format: "uri"` |
| `tool_choice` | `tool_choice` | 直接透传 |
| `thinking` + `output_config.effort` | `reasoning_effort` | 仅对支持推理的模型注入 (o-series, GPT-5+) |

**消息内容转换**:
- `text` → `{type: "text", text: "..."}`
- `image` → `{type: "image_url", image_url: {url: "data:...;base64,..."}}` (data URI)
- `tool_use` → `tool_calls[{id, type: "function", function: {name, arguments}}]`
- `tool_result` → `{role: "tool", tool_call_id: "...", content: "..."}` (独立 tool role 消息)
- `thinking` → 丢弃

**推理强度 (reasoning_effort) 映射规则**:

| Anthropic thinking 配置 | OpenAI reasoning_effort |
|---|---|
| `type: "adaptive"` | `"xhigh"` |
| `type: "enabled"` + budget < 4000 | `"low"` |
| `type: "enabled"` + budget 4000-15999 | `"medium"` |
| `type: "enabled"` + budget >= 16000 | `"high"` |
| `type: "enabled"` + 无 budget | `"high"` |
| `output_config.effort: "max"` | `"xhigh"` |

**特殊处理**:
- 多个 system message 自动合并，冲突的 `cache_control` 被丢弃
- 支持 `cache_control` 在 system message、text block、tool 上的透传
- `clean_schema()` 清理 JSON Schema 中 OpenAI 不支持的 `format: "uri"` 等字段

#### 响应转换: `openai_to_anthropic(body) → Value`

| OpenAI 字段 | Anthropic 字段 |
|---|---|
| `choices[0].message.content` | `content[{type: "text"}]` |
| `choices[0].message.tool_calls` | `content[{type: "tool_use", id, name, input}]` |
| `choices[0].message.function_call` | `content[{type: "tool_use"}]` (旧格式兼容) |
| `choices[0].message.refusal` | `content[{type: "text", text: refusal}]` |
| `choices[0].finish_reason` | `stop_reason` (stop→end_turn, length→max_tokens, tool_calls→tool_use) |
| `usage.prompt_tokens` | `usage.input_tokens` |
| `usage.completion_tokens` | `usage.output_tokens` |
| `usage.prompt_tokens_details.cached_tokens` | `usage.cache_read_input_tokens` |

---

### 5.2 Anthropic ↔ OpenAI Responses API

**文件**: `src-tauri/src/proxy/providers/transform_responses.rs` (1329 行)

Responses API 是 OpenAI 2025 年推出的新一代 API，采用扁平化的 input/output 结构。

#### 请求转换: `anthropic_to_responses(body, cache_key, is_codex_oauth) → Value`

| Anthropic 字段 | Responses API 字段 | 说明 |
|---|---|---|
| `system` | `instructions` | System prompt 转为 instructions 字段 |
| `messages` | `input` | 消息数组转为扁平化的 input items |
| `max_tokens` | `max_output_tokens` | 统一使用 max_output_tokens |
| `tools[].input_schema` | `tools[].parameters` | Schema 格式转换，移除 `cache_control` |
| `tool_choice.type = "any"` | `tool_choice = "required"` | 映射工具选择策略 |
| `tool_choice.type = "tool"` | `tool_choice = {type: "function", name}` | 强制指定工具 |
| `thinking` | `reasoning.effort` | 推理强度映射 |

**消息结构转换核心差异** — 消息从"嵌套在 role message 中"变为"独立 top-level item"：
- `tool_use` 从 assistant message 中**提升**为独立的 `function_call` item:
  ```json
  // Anthropic
  {"role": "assistant", "content": [{"type": "tool_use", "id": "call_1", ...}]}
  // Responses API
  {"type": "function_call", "call_id": "call_1", ...}
  ```
- `tool_result` 从 user message 中**提升**为独立的 `function_call_output` item:
  ```json
  // Anthropic
  {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "call_1", ...}]}
  // Responses API
  {"type": "function_call_output", "call_id": "call_1", ...}
  ```
- 文本类型区分: user 的 text → `input_text`, assistant 的 text → `output_text`

**Codex OAuth (ChatGPT Plus/Pro 反代) 特殊协议约束**:

当 `is_codex_oauth = true` 时，需满足 ChatGPT 后端的协议限制：
- `store: false` — 不允许服务端持久化
- `include: ["reasoning.encrypted_content"]` — 保持多轮 reasoning 上下文（ChatGPT 后端通过加密 reasoning blob 维持推理状态）
- 删除 `max_output_tokens`、`temperature`、`top_p` (ChatGPT 后端不支持这些参数)
- 兜底注入 `instructions`("")、`tools`([])、`parallel_tool_calls`(false) 默认值
- 强制 `stream: true` (CC-Switch SSE 解析层只支持流式)

#### 响应转换: `responses_to_anthropic(body) → Value`

| Responses API 字段 | Anthropic 字段 |
|---|---|
| `output[type="message"].content[type="output_text"]` | `content[{type: "text"}]` |
| `output[type="function_call"]` | `content[{type: "tool_use", id: call_id, name, input}]` |
| `output[type="reasoning"].summary` | `content[{type: "thinking", thinking: ...}]` |
| `output[type="message"].content[type="refusal"]` | `content[{type: "text"}]` |
| `status = "completed"` (无 tool_use) | `stop_reason: "end_turn"` |
| `status = "completed"` (有 function_call) | `stop_reason: "tool_use"` |
| `status = "incomplete"` (reason: max_output_tokens) | `stop_reason: "max_tokens"` |
| `usage.input_tokens` | `usage.input_tokens` |
| `usage.input_tokens_details.cached_tokens` | `usage.cache_read_input_tokens` |

---

### 5.3 Anthropic ↔ Google Gemini Native

**文件**: `src-tauri/src/proxy/providers/transform_gemini.rs` (2152 行)

将 Anthropic Messages 请求转换为 Gemini `generateContent` 格式。这是三个转换中最复杂的，涉及有状态管理。

#### 请求转换: `anthropic_to_gemini_with_shadow(body, shadow_store, provider_id, session_id) → Value`

| Anthropic 字段 | Gemini 字段 | 说明 |
|---|---|---|
| `system` | `systemInstruction.parts[{text}]` | System prompt 转为 systemInstruction |
| `messages` | `contents[{role, parts}]` | role: assistant→model, 其他→user |
| `max_tokens` | `generationConfig.maxOutputTokens` | |
| `temperature` | `generationConfig.temperature` | |
| `top_p` | `generationConfig.topP` | |
| `stop_sequences` | `generationConfig.stopSequences` | |
| `tools[].input_schema` | `tools[].functionDeclarations[]` | 转为 Gemini FunctionDeclaration |
| `tool_choice` | `toolConfig.functionCallingConfig` | auto→AUTO, none→NONE, any→ANY, tool→ANY+allowedFunctionNames |

**消息内容转换**:
- `text` → `{text: "..."}`
- `image` (base64) → `{inlineData: {mimeType, data}}`
- `document` (base64) → `{inlineData: {mimeType, data}}`
- `tool_use` → `{functionCall: {name, args, id}}` (仅 assistant)
- `tool_result` → `{functionResponse: {name, response, id}}` (通过 id→name 映射解析)
- `thinking` / `redacted_thinking` → 丢弃

#### Shadow Store (状态回放机制)

**文件**: `src-tauri/src/proxy/providers/gemini_shadow.rs` (389 行)

Gemini 的 `thoughtSignature` 和 `functionCall.id` 是有状态的，需要跨轮次保留。CC-Switch 实现了 `GeminiShadowStore` 来解决此问题：

- **存储结构**: 以 `(provider_id, session_id)` 为键，存储 `GeminiAssistantTurn`（包含 assistant_content + tool_calls 及 thoughtSignature）
- **容量限制**: 默认 200 个 session，每 session 64 轮对话，超出后 LRU 淘汰最旧 session
- **回放机制**: 在后续请求中，将存储的 shadow turn（包含 thoughtSignature 和原始 Gemini 格式内容）替换到对应 assistant 消息的位置，确保多轮对话的 `functionResponse` 和 `thoughtSignature` 能正确传递
- **线程安全**: 使用 `std::sync::RwLock`（非 tokio async lock），因为 shadow store 操作很快

#### 工具参数修正 (Tool Call Rectification)

Gemini 模型有时会将工具调用的参数结构"展平"或混淆（如将 `{skill: "git-commit"}` 发为 `{name: "git-commit"}`）。系统通过 `AnthropicToolSchemaHints` 机制：
1. 从请求中的 `tools[].input_schema` 提取预期参数结构
2. 在响应中检测并修正参数名映射 (如 `name` → `skill`)
3. 处理 `parameters` 嵌套展平问题
4. 通过 `rectify_tool_call_parts()` 实现类型强制转换（如 string `"123"` → integer `123`）

#### 合成 ID 机制

Gemini 2.x 并行调用常省略 `functionCall.id`。系统处理：
- `synthesize_tool_call_id()` 生成 `gemini_synth_<uuid>` 格式的合成 ID，在 Anthropic 侧使用
- **不会**转发回 Gemini（在构建 Gemini 请求时被过滤）
- 流式场景支持 "synth → real id upgrade"：当后续 chunk 携带真实 ID 时替换合成 ID

#### Gemini Schema 构建

**文件**: `src-tauri/src/proxy/providers/gemini_schema.rs` (338 行)

工具定义的 Schema 转换使用双通道策略：
- **`parameters` 通道**: 使用受限的 Gemini Schema 格式（去掉 `$ref`, `$defs`, `additionalProperties`, `oneOf`, `allOf`, `const`, `not`, `if/then/else` 等不支持的关键字）
- **`parametersJsonSchema` 通道**: 使用完整 JSON Schema 格式（当 Schema 包含 Gemini 不支持的关键字时自动升级）
- `build_gemini_function_parameters()` 自动根据 Schema 复杂度选择通道

#### 响应转换: `gemini_to_anthropic_with_shadow_and_hints(body, ...) → Value`

| Gemini 字段 | Anthropic 字段 |
|---|---|
| `candidates[0].content.parts[{text}]` | `content[{type: "text"}]` |
| `candidates[0].content.parts[{functionCall}]` | `content[{type: "tool_use", id, name, input}]` |
| `candidates[0].content.parts[{thought: true}]` | 丢弃 (Gemini thinking) |
| `candidates[0].finishReason = "STOP"` | `stop_reason: "end_turn"` / `"tool_use"` |
| `candidates[0].finishReason = "MAX_TOKENS"` | `stop_reason: "max_tokens"` |
| `candidates[0].finishReason = "SAFETY"` 等 | `stop_reason: "refusal"` |
| `promptFeedback.blockReason` | `stop_reason: "refusal"` + 错误文本 |
| `usageMetadata.promptTokenCount` | `usage.input_tokens` |
| `usageMetadata.cachedContentTokenCount` | `usage.cache_read_input_tokens` |

---

## 6. 流式转换层

### 6.1 OpenAI Chat Completions SSE → Anthropic SSE

**文件**: `src-tauri/src/proxy/providers/streaming.rs` (821 行)

**转换状态机**:

```
OpenAI SSE chunk ──▶ 状态解析 ──▶ Anthropic SSE event
                                    │
    choices[0].delta.content ──────▶ content_block_start (type: "text")
                                    content_block_delta (type: "text_delta")
                                    content_block_stop
                                    
    choices[0].delta.reasoning ────▶ content_block_start (type: "thinking")
                                    content_block_delta (type: "thinking_delta")
                                    content_block_stop
                                    
    choices[0].delta.tool_calls ───▶ content_block_start (type: "tool_use")
                                    content_block_delta (type: "input_json_delta")
                                    content_block_stop
                                    
    choices[0].finish_reason ──────▶ message_delta (stop_reason)
    [DONE] ───────────────────────▶ message_stop
```

**特殊处理**:
- **UTF-8 安全**: 处理 TCP 分包导致的 UTF-8 多字节字符截断，使用 `utf8_remainder` 缓冲区确保不会产生 U+FFFD 替换字符
- **Copilot 无限空白 Bug 检测**: 跟踪连续空白字符数，超过 20 个时强制中止 tool call 流（Copilot 有时会无限输出空白字符的已知 Bug）
- **延迟工具调用**: 处理 `id/name` 在 `arguments` 之后到达的乱序情况

### 6.2 OpenAI Responses API SSE → Anthropic SSE

**文件**: `src-tauri/src/proxy/providers/streaming_responses.rs` (1070 行)

Responses API 使用**命名事件** (named events) 的生命周期模型，与 Chat Completions 的 delta chunk 模型完全不同。

**事件映射**:

| Responses API SSE Event | Anthropic SSE Event |
|---|---|
| `response.created` | `message_start` |
| `response.content_part.added` (output_text) | `content_block_start(type:text)` |
| `response.output_text.delta` | `content_block_delta(text_delta)` |
| `response.refusal.delta` | `content_block_delta(text_delta)` |
| `response.output_text.done` | `content_block_stop` |
| `response.output_item.added` (function_call) | `content_block_start(type:tool_use)` |
| `response.function_call_arguments.delta` | `content_block_delta(input_json_delta)` |
| `response.function_call_arguments.done` | `content_block_stop` |
| `response.reasoning.delta` | `content_block_start(type:thinking)` + `content_block_delta(thinking_delta)` |
| `response.reasoning.done` | `content_block_stop` |
| `response.completed` | `message_delta` (stop_reason + usage) + `message_stop` |

**状态管理**: 维护 `content_part_key` (item_id + content_index 复合键)、`index_by_key` (content part → Anthropic index 映射)、`tool_index_by_item_id` (item_id → index 映射)、`open_indices` (未关闭的 block 索引集合)。

### 6.3 Gemini SSE → Anthropic SSE

**文件**: `src-tauri/src/proxy/providers/streaming_gemini.rs` (1054 行)

Gemini 的 `streamGenerateContent?alt=sse` 每个 chunk 都是完整的 `GenerateContentResponse`，需要增量合并。

**核心机制**:
- **累积快照模型**: 每个 SSE chunk 包含完整的 candidates，系统通过比较 `functionCall.name` 和位置匹配来去重和合并跨 chunk 的工具调用
- **工具调用 ID 去重合并**: `merge_tool_call_snapshots()` 处理跨 chunk 的工具调用累积（支持合成 ID → 真实 ID 升级）
- **Shadow Store 记录**: 在流结束时记录 assistant turn 到 shadow store（包括 `thoughtSignature`）
- **Tool Call Rectification**: 流式场景下同样应用工具参数修正
- **thoughtSignature 提取**: 从 text parts 中提取 `thoughtSignature` 用于后续回放

---

## 7. 请求处理流程

### 7.1 主处理链

以 Claude Code → CC-Switch → 上游 API 为例的完整流程：

```
Claude Code 发送 Anthropic Messages API 请求
         │
         ▼
[server.rs] 路由匹配 → /v1/messages → handle_messages()
         │
         ▼
[handlers.rs] handle_messages():
  1. 解析请求体
  2. 创建 RequestContext (识别 AppType::Claude, 提取 session_id)
  3. 调用 forwarder.forward_with_retry()
         │
         ▼
[forwarder.rs] RequestForwarder.forward_with_retry():
  遍历供应商列表（故障转移队列）:
  1. 检查熔断器状态
  2. forward() 内部处理:
     a. get_adapter(AppType::Claude) → ClaudeAdapter
     b. 提取 base_url，应用 model_mapper 映射模型名
     c. 规范化 thinking 类型
     d. 确定端点路径（Copilot 动态端点 / 格式化重写）
     e. Copilot Optimizer: 分类请求、清理孤立 tool_result、合并 tool_result、热身降级、确定性 ID
     f. 解析 API 格式: get_claude_api_format()
     g. 如需转换: transform_claude_request_for_api_format()
        - openai_chat → anthropic_to_openai()
        - openai_responses → anthropic_to_responses()
        - gemini_native → anthropic_to_gemini_with_shadow()
     h. body_filter: 过滤 `_` 前缀的私有参数
     i. 获取动态认证 Token (Copilot/CodexOAuth)
     j. 构建有序 HeaderMap
     k. 发送请求 (hyper for HTTP/CONNECT proxy, reqwest for SOCKS5 proxy)
  3. 成功: 记录结果、更新状态、触发 UI 切换
  4. 失败: 检查 thinking_rectifier / thinking_budget_rectifier，各自最多触发一次修复后重试
         │
         ▼
[handlers.rs] 响应处理:
  if needs_transform:
    流式: 根据格式选择:
      - openai_responses → create_anthropic_sse_stream_from_responses()
      - gemini_native → create_anthropic_sse_stream_from_gemini()
      - 其他 → create_anthropic_sse_stream()
    非流式: 读取完整响应，应用:
      - responses_to_anthropic()
      - gemini_to_anthropic_with_shadow_and_hints()
      - openai_to_anthropic()
  else:
    透传响应
```

### 7.2 Copilot Optimizer

**文件**: `src-tauri/src/proxy/copilot_optimizer.rs` (1251 行)

专用于 GitHub Copilot 的请求优化系统，在转发器内部对请求进行预处理：

| 优化策略 | 说明 |
|---------|------|
| **请求分类** | 识别 initiator (user/agent)、热身请求、压缩请求、子代理请求 |
| **孤立 tool_result 清理** | 清理无对应 tool_use 的 tool_result（Claude Code 偶尔产生） |
| **tool_result 合并** | 将多个 tool_result 合并为一个，减少 Copilot 高级计费 |
| **热身降级** | 检测到热身请求时降级为小模型，节省成本 |
| **确定性请求 ID** | 基于 session 生成确定性 `x-request-id` 和 `x-interaction-id`，提高 Copilot 稳定性 |
| **子代理头修改** | 子代理请求时修改 `x-initiator`、`x-interaction-type` 为 agent |

### 7.3 Thinking 自修复机制

**文件**: `src-tauri/src/proxy/thinking_rectifier.rs` (609 行), `thinking_budget_rectifier.rs` (307 行)

两个独立的自修复 rectifier，在 Anthropic API 返回特定错误时自动修复并重试：

| Rectifier | 触发条件 | 修复动作 | 触发次数限制 |
|-----------|---------|---------|------------|
| **Signature Rectifier** | Anthropic API 报告 thinking signature 无效 | 移除 `thinking`/`redacted_thinking` blocks 和 `signature` 字段 | 每请求 1 次 |
| **Budget Rectifier** | Anthropic API 报告 thinking budget 错误 | 调整 `thinking.budget_tokens` | 每请求 1 次 |

修复后通过 `release_permit_neutral()` 释放熔断器的 HalfOpen 探测槽位，不影响健康统计。

### 7.4 其他辅助机制

| 模块 | 文件 | 行数 | 说明 |
|------|------|------|------|
| **私有参数过滤** | `body_filter.rs` | 261 | 过滤 `_` 前缀的参数，允许客户端传递控制参数而不发送到上游 |
| **缓存注入** | `cache_injector.rs` | 329 | 为 Bedrock 等支持 prompt 缓存的供应商注入缓存控制标记 |
| **Thinking 优化** | `thinking_optimizer.rs` | 230 | Bedrock 场景下的 thinking 参数优化 |
| **模型名映射** | `model_mapper.rs` | 312 | 将客户端请求的模型名映射为上游实际模型名 |
| **Gemini URL** | `gemini_url.rs` | 583 | 处理 Gemini API 的复杂 URL 构建逻辑 |
| **SSE 基础** | `sse.rs` | 294 | SSE 事件解析和生成的基础工具 |

---

## 8. 熔断器与故障转移

### 8.1 熔断器

**文件**: `src-tauri/src/proxy/circuit_breaker.rs` (482 行)

三态熔断器保护上游服务：

| 参数 | 默认值 |
|------|-------|
| `failure_threshold` | 4 (连续失败次数) |
| `success_threshold` | 2 (连续成功次数) |
| `timeout_seconds` | 60 (Open 状态持续时间) |
| `error_rate_threshold` | 0.6 (错误率阈值) |
| `min_requests` | 10 (最小请求数) |

**状态转换**: Closed (正常) → Open (熔断) → HalfOpen (探测，最多 1 个并发探测请求) → Closed/Open

### 8.2 供应商路由

**文件**: `src-tauri/src/proxy/provider_router.rs` (512 行)

- 故障转移关闭时：仅使用当前供应商
- 故障转移开启时：返回按优先级排列的故障转移队列，跳过熔断中的供应商

---

## 9. 支持的协议转换矩阵

```
                    ┌──────────────────────────────────────────────┐
                    │              CC-Switch 代理                    │
                    │                                              │
                    │  ┌──────────┐  ┌──────────┐  ┌──────────┐   │
 Claude Code ──────▶│  │ Claude   │  │ Codex    │  │ Gemini   │   │
 (Anthropic        │  │ Handler  │  │ Handler  │  │ Handler  │   │
  Messages API)    │  └────┬─────┘  └────┬─────┘  └────┬─────┘   │
                    │       │             │              │         │
                    │  ┌────▼─────────────▼──────────────▼─────┐  │
                    │  │          Forwarder (转发器)              │  │
                    │  │  - 供应商选择 / 故障转移 / Copilot优化   │  │
                    │  │  - Thinking自修复 / 参数过滤             │  │
                    │  └────────────────┬───────────────────────┘  │
                    │                   │                          │
                    │       ┌───────────┼───────────┐              │
                    │       ▼           ▼           ▼              │
                    │  ┌─────────┐ ┌─────────┐ ┌──────────┐      │
                    │  │OpenAI   │ │OpenAI   │ │  Gemini  │      │
                    │  │Chat     │ │Responses│ │  Native  │      │
                    │  │Complet. │ │API      │ │          │      │
                    │  └────┬────┘ └────┬────┘ └────┬─────┘      │
                    │       │           │           │             │
                    └───────┼───────────┼───────────┼─────────────┘
                            ▼           ▼           ▼
                    ┌───────────┐ ┌──────────┐ ┌──────────────┐
                    │OpenAI/    │ │ChatGPT/  │ │ Google AI    │
                    │OpenRouter/│ │OpenAI/   │ │ Gemini API   │
                    │Copilot    │ │Codex OAuth│ │              │
                    └───────────┘ └──────────┘ └──────────────┘
```

### 转换路径汇总

| 转换路径 | 请求转换 | 响应转换 | 流式转换 | 使用场景 |
|---------|---------|---------|---------|---------|
| Anthropic → OpenAI Chat | `anthropic_to_openai()` | `openai_to_anthropic()` | `streaming.rs` | GitHub Copilot, OpenRouter (旧版) |
| Anthropic → OpenAI Responses | `anthropic_to_responses()` | `responses_to_anthropic()` | `streaming_responses.rs` | Codex OAuth (ChatGPT Plus/Pro) |
| Anthropic → Gemini | `anthropic_to_gemini_with_shadow()` | `gemini_to_anthropic_with_shadow_and_hints()` | `streaming_gemini.rs` | Gemini 模型直连 |
| Anthropic → Anthropic (透传) | 直接转发 | 直接转发 | 直接转发 | Anthropic 官方, OpenRouter (新版) |

---

## 10. 文件索引

### 核心转换文件

| 文件路径 | 功能 | 代码行数 |
|---------|------|---------|
| `src-tauri/src/proxy/providers/transform.rs` | Anthropic ↔ OpenAI Chat Completions 转换 | 1193 |
| `src-tauri/src/proxy/providers/transform_responses.rs` | Anthropic ↔ OpenAI Responses API 转换 | 1329 |
| `src-tauri/src/proxy/providers/transform_gemini.rs` | Anthropic ↔ Gemini Native 转换 | 2152 |
| `src-tauri/src/proxy/providers/streaming.rs` | OpenAI Chat SSE → Anthropic SSE 流式转换 | 821 |
| `src-tauri/src/proxy/providers/streaming_responses.rs` | Responses SSE → Anthropic SSE 流式转换 | 1070 |
| `src-tauri/src/proxy/providers/streaming_gemini.rs` | Gemini SSE → Anthropic SSE 流式转换 | 1054 |

### 适配器与认证

| 文件路径 | 功能 | 代码行数 |
|---------|------|---------|
| `src-tauri/src/proxy/providers/adapter.rs` | ProviderAdapter trait 定义 | 50 |
| `src-tauri/src/proxy/providers/auth.rs` | AuthInfo / AuthStrategy 认证类型定义 | 259 |
| `src-tauri/src/proxy/providers/claude.rs` | Claude 适配器 (API 格式检测, URL 构建, 认证) | 1313 |
| `src-tauri/src/proxy/providers/codex.rs` | Codex 适配器 | 305 |
| `src-tauri/src/proxy/providers/gemini.rs` | Gemini 适配器 | 442 |
| `src-tauri/src/proxy/providers/mod.rs` | ProviderType 枚举, get_adapter() 工厂函数 | 515 |
| `src-tauri/src/proxy/providers/copilot_auth.rs` | Copilot 动态 Token 管理 | 1820 |
| `src-tauri/src/proxy/providers/codex_oauth_auth.rs` | Codex OAuth 动态 Token 管理 | 1132 |
| `src-tauri/src/proxy/providers/gemini_shadow.rs` | Gemini Shadow Store (状态回放) | 389 |
| `src-tauri/src/proxy/providers/gemini_schema.rs` | Gemini Schema 构建 (双通道策略) | 338 |

### 请求处理链

| 文件路径 | 功能 | 代码行数 |
|---------|------|---------|
| `src-tauri/src/proxy/server.rs` | Axum HTTP 服务器, 路由注册 | 354 |
| `src-tauri/src/proxy/handlers.rs` | 请求处理器 (分发 + Claude 格式转换入口) | 643 |
| `src-tauri/src/proxy/forwarder.rs` | 请求转发器 (供应商选择, 故障转移, 请求预处理) | 2169 |
| `src-tauri/src/proxy/copilot_optimizer.rs` | Copilot 请求优化 | 1251 |
| `src-tauri/src/proxy/thinking_rectifier.rs` | Thinking Signature 自修复 | 609 |
| `src-tauri/src/proxy/thinking_budget_rectifier.rs` | Thinking Budget 自修复 | 307 |
| `src-tauri/src/proxy/thinking_optimizer.rs` | Bedrock Thinking 优化 | 230 |
| `src-tauri/src/proxy/cache_injector.rs` | Prompt 缓存注入 | 329 |
| `src-tauri/src/proxy/body_filter.rs` | 私有参数过滤 (`_` 前缀) | 261 |
| `src-tauri/src/proxy/model_mapper.rs` | 模型名映射 | 312 |
| `src-tauri/src/proxy/gemini_url.rs` | Gemini URL 构建 | 583 |
| `src-tauri/src/proxy/sse.rs` | SSE 基础工具 | 294 |
| `src-tauri/src/proxy/provider_router.rs` | 供应商路由 (故障转移队列) | 512 |
| `src-tauri/src/proxy/circuit_breaker.rs` | 熔断器实现 | 482 |
| `src-tauri/src/proxy/handler_context.rs` | RequestContext 构建 | 246 |
| `src-tauri/src/proxy/response_processor.rs` | 响应处理 (透传/日志/usage) | 915 |
| `src-tauri/src/proxy/session.rs` | 会话管理 | 565 |
| `src-tauri/src/proxy/hyper_client.rs` | Hyper HTTP 客户端 | 626 |
| `src-tauri/src/proxy/http_client.rs` | Reqwest HTTP 客户端 (SOCKS5) | 392 |
| `src-tauri/src/proxy/error.rs` | 错误类型定义 | 177 |
| `src-tauri/src/proxy/error_mapper.rs` | 错误映射 | 99 |

---

## 11. 关键设计特点

### 11.1 单方向锚定转换

所有转换以 Anthropic Messages API 为锚点，仅实现 Anthropic ↔ X 的双向转换。客户端协议只有一种（Anthropic Messages API），上游协议有三种（OpenAI Chat、OpenAI Responses、Gemini Native）加一种透传（Anthropic 直连）。

### 11.2 流式 + 非流式双轨支持

每种协议转换都同时实现了非流式 (JSON-to-JSON) 和流式 (SSE 事件逐事件翻译，基于 `async_stream` 异步流)。

### 11.3 有状态转换 (Gemini Shadow Store)

Gemini 的 `thoughtSignature` 是有状态的，系统通过 Shadow Store 在内存中记录每轮 assistant 的原始 Gemini 响应，在后续请求中回放。这是 CC-Switch 独有的复杂机制。

### 11.4 自修复能力 (Thinking Rectifiers)

两个独立 rectifier 在 API 错误时自动修复请求并重试，大幅提高了对 Anthropic API thinking 功能变更的容错能力。

### 11.5 Copilot 深度适配

Copilot Optimizer 针对性地解决了 Copilot 的多个实际问题（无限空白 Bug、请求不稳定、高级计费优化等），是与供应商深度绑定的典型案例。

### 11.6 请求头大小写保留

通过手动 hyper accept loop + TCP `peek()` 捕获原始请求头大小写，转发给上游时保持一致（部分上游 API 对 header 大小写敏感）。

### 11.7 代理协议支持

支持 HTTP/CONNECT 和 SOCKS5 两种代理协议，分别使用 hyper 和 reqwest 客户端。

### 11.8 UTF-8 安全

流式转换中处理 TCP 分包导致的 UTF-8 多字节字符截断，使用 `utf8_remainder` 缓冲区确保不会产生 U+FFFD 替换字符。
