1
0

docs: 添加 API 参考文档和技术分析文档

This commit is contained in:
2026-04-19 01:43:02 +08:00
parent 2b1c5e96c3
commit b92974716f
14 changed files with 32227 additions and 0 deletions

View File

@@ -0,0 +1,722 @@
# 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_resultClaude 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 替换字符。

View File

@@ -0,0 +1,575 @@
# LiteLLM 大模型 API 代理转换层深度分析报告
## 1. 项目概述
LiteLLM 是一个统一的 LLM API 网关和 SDK 项目,核心能力是将 100+ 种不同来源的大模型 API 统一管理,并以 **OpenAI 格式** 作为统一接口标准提供给用户。
### 整体请求流向
```
OpenAI SDK (客户端) ──▶ LiteLLM AI Gateway (proxy/) ──▶ LiteLLM SDK (litellm/) ──▶ LLM API
Anthropic SDK (客户端) ──▶ LiteLLM AI Gateway (proxy/) ──▶ LiteLLM SDK (litellm/) ──▶ LLM API
任意 HTTP 客户端 ──▶ LiteLLM AI Gateway (proxy/) ──▶ LiteLLM SDK (litellm/) ──▶ LLM API
```
- **AI Gateway代理层**:在 SDK 之上提供认证、速率限制、预算管理和路由功能
- **SDK核心层**:处理实际的 LLM 提供商调用、请求/响应转换和流式传输
### 与同类项目的核心差异
LiteLLM 是**覆盖最广**的 LLM 网关,以 **OpenAI 格式为唯一内部规范格式**Canonical Format所有转换都围绕 OpenAI ↔ 提供商格式展开。同时支持 **Anthropic Messages API 直通模式**`/v1/messages` 端点),使其也能充当 Anthropic 协议的代理网关。SDK 可独立使用Python 包),也可配合 Gateway 作为服务部署。
---
## 2. 转换层技术架构
### 2.1 架构设计模式
采用 **策略模式 + 工厂模式 + 模板方法** 组合设计:
- **策略模式**:每个 LLM 提供商都有一个独立的 `Config` 类,继承自统一的 `BaseConfig` 抽象基类,实现各自的转换方法
- **工厂模式**`ProviderConfigManager` 作为中心工厂,根据模型名称和提供商类型动态解析出正确的 Config 实例
- **模板方法**`BaseConfig` 定义统一的转换接口(抽象方法),子类实现具体的提供商差异
### 2.2 核心抽象基类
**文件**: `litellm/llms/base_llm/chat/transformation.py` (466 行)
```python
class BaseConfig(ABC):
@abstractmethod
def get_supported_openai_params(self, model: str) -> list:
"""返回该提供商支持的 OpenAI 格式参数列表"""
@abstractmethod
def map_openai_params(self, non_default_params, optional_params, model, drop_params) -> dict:
"""将 OpenAI 格式参数映射为提供商特定参数"""
@abstractmethod
def validate_environment(self, headers, model, messages, optional_params, ...) -> dict:
"""验证并构建请求头"""
@abstractmethod
def transform_request(self, model, messages, optional_params, litellm_params, headers) -> dict:
"""将 OpenAI 格式请求转换为提供商格式"""
@abstractmethod
def transform_response(self, model, raw_response, model_response, ...) -> ModelResponse:
"""将提供商响应转换为统一的 OpenAI ModelResponse 格式"""
@abstractmethod
def get_error_class(self, error_message, status_code, headers) -> BaseLLMException:
"""返回提供商特定的错误类"""
# 以下为有默认实现的重要方法:
def get_complete_url(self, api_base, api_key, model, ...) -> str: ... # URL 构建
def sign_request(self, headers, optional_params, request_data, ...) -> ...: ... # 请求签名
def get_model_response_iterator(self, ...) -> BaseModelResponseIterator: ... # 流式迭代器
def should_fake_stream(self, ...) -> bool: ... # 是否需要模拟流式
def is_thinking_enabled(self, ...) -> bool: ... # 思考模式检测
def calculate_additional_costs(self, ...) -> float: ... # 自定义成本计算
def post_stream_processing(self, ...) -> ...: ... # 流式后处理钩子
```
**6 个抽象方法**必须实现,其余方法提供默认实现。
### 2.3 中央 HTTP 编排器
**文件**: `litellm/llms/custom_httpx/llm_http_handler.py` (12,161 行)
`BaseLLMHTTPHandler` 是所有提供商共用的 HTTP 编排器,支持**多种模态**(不仅限于 chat
```
completion() 编排流程:
1. provider_config.validate_environment() → 验证环境、构建请求头
2. provider_config.get_complete_url() → 构建 API URL
3. provider_config.transform_request() → 转换请求体
4. provider_config.sign_request() → 签名请求(如 AWS Bedrock
5. 发送 HTTP 请求到提供商 API
6. provider_config.transform_response() → 转换响应为 ModelResponse
```
**额外支持的模态**: embedding、rerank、audio_transcription、OCR、vector_store、files、batches、image_generation、video_generation、responses API 等。
**关键设计**: 编排器本身不需要修改,所有提供商的差异都封装在各自的 Config 类中。此外编排器还支持 **HTTP 错误重试**模式 — 通过 `should_retry_llm_api_inside_llm_translation_on_http_error()` 在特定错误时自动修复并重试。
### 2.4 请求入口与提供商解析
**入口文件**: `litellm/main.py``completion()` 函数(约 7,807 行,`completion()` 起始于 line 1052
**提供商解析文件**: `litellm/litellm_core_utils/get_llm_provider_logic.py``get_llm_provider()` 函数 (958 行)
解析优先级:
1. **LiteLLM Proxy 默认**: 检查是否应使用 litellm_proxy
2. **litellm_params**: 从 router 参数获取
3. **Azure 特殊处理**: Azure 非 OpenAI 模型
4. **Cohere/Anthropic 文本模型重定向**
5. **OpenRouter 前缀剥离**
6. **JSON 配置提供商**: `JSONProviderRegistry` 动态注册的 OpenAI 兼容提供商
7. **前缀提供商**: `model.split("/")[0]` 在提供商列表中匹配
8. **API Base 匹配**: ~40 个已知 API 端点域名匹配
9. **已知模型名查找**: `"gpt-4"``"openai"``"claude-3"``"anthropic"`
10. **字符串前缀回退**: `model.startswith("bytez/")``"bytez"`
11. **错误**: 无法解析则抛出 `BadRequestError`
**Config 解析**: `litellm/utils.py``ProviderConfigManager`line 8013通过 `_PROVIDER_CONFIG_MAP` 懒加载字典进行 O(1) 查找,约 90 个提供商映射。
**派发模式**: `main.py` 中仍保留一个较大的 `if/elif` 链用于提供商分派。Azure 和 OpenAI 仍使用专用处理路径,约 30 个提供商通过 `base_llm_http_handler.completion()` 处理。
### 2.5 多模态工厂
`ProviderConfigManager` 提供多个工厂方法:
| 工厂方法 | 用途 | 提供商数量 |
|---------|------|-----------|
| `get_provider_chat_config()` | Chat Completions | ~90 |
| `get_provider_embedding_config()` | Embedding 向量 | ~25 |
| `get_provider_rerank_config()` | Rerank 重排序 | ~13 |
| `get_provider_anthropic_messages_config()` | Anthropic Messages 直通 | 4 |
---
## 3. 支持的协议/提供商总览
### 3.1 支持规模
| 维度 | 数量 |
|------|------|
| LLM 提供商枚举值 (`LlmProviders`) | 133 |
| 搜索提供商枚举值 (`SearchProviders`) | 12 |
| 实现目录数 (`litellm/llms/` 下) | ~118 |
| `base_llm/` 子模态目录 | 29 (chat, embedding, rerank, responses, audio, OCR, image, video, etc.) |
### 3.2 主要提供商分类
| 分类 | 提供商 | 转换方式 |
|------|--------|----------|
| **OpenAI 及兼容** | OpenAI, Azure OpenAI, DeepSeek, Groq, Together AI, Fireworks, Perplexity, OpenRouter 等 | 几乎直通,参数过滤 |
| **Anthropic 系列** | Anthropic 原生, Bedrock Claude, Vertex AI Claude | 深度转换(消息格式、工具调用、思考模式) |
| **Google 系列** | Gemini (AI Studio), Vertex AI Gemini | 深度转换contents/parts 格式、角色映射) |
| **AWS 系列** | Bedrock Converse, Bedrock Invoke | Converse 统一格式 或 按提供商分派转换 |
| **开源/本地** | Ollama, vLLM, LM Studio, llamafile | 参数重映射 + 消息格式适配 |
| **其他云厂商** | Cohere, Mistral, Databricks, WatsonX, OCI, Snowflake 等 | 各自独立转换 |
| **直通模式** | 所有提供商 | 请求体不变,仅转换 URL 和认证头 |
---
## 4. 核心转换文件详解
### 4.1 转换文件总览表
| 入站 API 协议 | 目标提供商 | 转换文件 |
|---------------|-----------|----------|
| `/v1/chat/completions` (OpenAI) | Anthropic | `litellm/llms/anthropic/chat/transformation.py` |
| `/v1/chat/completions` (OpenAI) | Bedrock Converse | `litellm/llms/bedrock/chat/converse_transformation.py` |
| `/v1/chat/completions` (OpenAI) | Bedrock Invoke | `litellm/llms/bedrock/chat/invoke_transformations/*.py` |
| `/v1/chat/completions` (OpenAI) | Gemini | `litellm/llms/gemini/chat/transformation.py` (薄层) → `litellm/llms/vertex_ai/gemini/transformation.py` (核心) |
| `/v1/chat/completions` (OpenAI) | Vertex AI | `litellm/llms/vertex_ai/vertex_ai_partner_models/main.py` |
| `/v1/chat/completions` (OpenAI) | OpenAI | `litellm/llms/openai/chat/gpt_transformation.py` |
| `/v1/chat/completions` (OpenAI) | Ollama | `litellm/llms/ollama/chat/transformation.py` |
| `/v1/chat/completions` (OpenAI) | Cohere | `litellm/llms/cohere/chat/transformation.py` |
| `/v1/chat/completions` (OpenAI) | Mistral | `litellm/llms/mistral/chat/transformation.py` |
| `/v1/messages` (Anthropic) | Anthropic/Bedrock/Vertex | `litellm/llms/base_llm/anthropic_messages/transformation.py` |
| 直通端点 | 所有 | `litellm/proxy/pass_through_endpoints/` |
---
### 4.2 Anthropic 转换层
**文件**: `litellm/llms/anthropic/chat/transformation.py` (2,096 行)
**类**: `AnthropicConfig(AnthropicModelInfo, BaseConfig)`
#### 请求转换OpenAI → Anthropic
`transform_request()` 方法核心转换逻辑:
1. **系统消息提取**: 将 `role: "system"` 消息从 messages 列表中提取出来,放入 Anthropic 顶层 `system` 字段Anthropic API 不使用 system-role 消息)
2. **消息格式转换**: 调用 `anthropic_messages_pt()` 将 OpenAI 消息转为 Anthropic Messages 格式
3. **工具调用转换**: OpenAI 的 `function.parameters` → Anthropic 的 `input_schema`;工具选择 `"required"``"any"``parallel_tool_calls: false``disable_parallel_tool_use: true`
4. **Beta Header 管理**: 根据使用的功能web search、memory、structured output、computer use、code execution、MCP server 等)自动添加 `anthropic-beta` 请求头
5. **Thinking 参数处理**: 管理 Anthropic Extended Thinking 参数
6. **JSON 模式**: 旧模型通过创建合成工具实现 JSON 模式新模型Sonnet 4.5+)原生支持结构化输出
**关键参数映射**:
| OpenAI 参数 | Anthropic 参数 | 说明 |
|-------------|---------------|------|
| `max_tokens` / `max_completion_tokens` | `max_tokens` | 直接映射,默认值可通过环境变量配置 |
| `tools[].function.parameters` | `tools[].input_schema` | JSON Schema 格式转换 |
| `tool_choice: "required"` | `tool_choice: {"type": "any"}` | 语义映射 |
| `parallel_tool_calls: false` | `disable_parallel_tool_use: true` | 反转布尔值 |
| `stop` | `stop_sequences` | 字符串 → 列表 |
| `response_format` (JSON) | 合成工具方式 / `output_format` | 按模型能力选择 |
| `reasoning_effort` | `thinking` + `output_config` | 努力级别映射为思考预算 |
| `web_search_options` | `tools` (web_search hosted tool) | 转换为 Anthropic 原生搜索工具 |
| `user` | `metadata.user_id` | 嵌套到 metadata |
#### 响应转换Anthropic → OpenAI
1. **内容块解析**: text → 文本, tool_use → ToolCall, thinking/redacted_thinking → `reasoning_content` + `thinking_blocks`
2. **JSON 模式逆向转换**: 如果启用了 JSON 模式,将工具调用的参数提取为文本内容
3. **停止原因映射**: `end_turn`/`stop_sequence``stop`, `max_tokens``length`, `tool_use``tool_calls`
4. **Usage 计算**: 输入/输出 token、缓存 token、推理 token 等
---
### 4.3 OpenAI 转换层(基准实现)
**文件**: `litellm/llms/openai/chat/gpt_transformation.py` (820 行)
**类**: `OpenAIGPTConfig(BaseLLMModelInfo, BaseConfig)` — 标记 `_is_base_class = True`
OpenAI 是"原生"格式提供者,其转换层最简单:
- **`transform_request()`**: 仅做少量规范化image_url 格式化、移除 `cache_control`、PDF URL 转 base64
- **`transform_response()`**: JSON 解析后直接构建 `ModelResponse`
- **`map_openai_params()`**: 简单的参数过滤(只保留支持的参数列表)
**作为基类被复用**:
- `OpenAIOSeriesConfig` — O1/O3 推理模型(`system` 角色转 `user`,限制 temperature
- `OpenAIGPTAudioConfig` — GPT-4o 音频模型
- `OpenAIGPT5Config` — GPT-5 系列(推理努力级别标准化)
- 许多 OpenAI 兼容提供商DeepSeek、Groq、Together AI 等)也复用此类
- **Mistral** 也继承自 `OpenAIGPTConfig`API 大部分兼容,额外处理 schema 清理和 thinking
---
### 4.4 Google Gemini 转换层
**文件**:
- `litellm/llms/gemini/chat/transformation.py` (154 行) — 薄层,继承 Vertex Gemini
- `litellm/llms/vertex_ai/gemini/transformation.py` (941 行) — 核心转换逻辑
**类继承链**:
```
BaseConfig → VertexAIBaseConfig → VertexGeminiConfig → GoogleAIStudioGeminiConfig
```
#### 请求转换OpenAI → Gemini
这是最复杂的转换之一:
| 方面 | OpenAI 格式 | Gemini 格式 |
|------|-----------|------------|
| 消息容器 | `messages: [{role, content}]` | `contents: [{role, parts}]` |
| 角色类型 | `system`, `user`, `assistant`, `tool` | 仅 `user``model` |
| 内容格式 | 字符串或内容对象列表 | `PartType` 对象列表 |
| 工具结果 | 独立的 `role: "tool"` 消息 | 合并到 `role: "user"` 的 parts |
| 连续消息 | 允许任意顺序 | 必须严格交替 user/model |
转换算法核心步骤:
1. **系统消息提取**: 放入 `system_instruction` 字段
2. **角色映射**: `user`/`system``user`, `assistant``model`, `tool`/`function``user`
3. **角色交替强制**: 合并连续相同角色的消息
4. **媒体处理**: image_url → `inline_data` (base64) 或 `file_data` (GCS URI)
5. **工具调用转换**: OpenAI `tool_calls` → Gemini `functionCall` parts
6. **搜索工具冲突处理**: 搜索工具与函数声明互斥时优先保留函数声明
**关键参数映射**:
| OpenAI 参数 | Gemini 参数 | 说明 |
|-------------|-----------|------|
| `max_tokens` | `max_output_tokens` | 名称变更 |
| `stop` | `stop_sequences` | 字符串 → 列表 |
| `n` | `candidate_count` | 名称变更 |
| `response_format` | `response_mime_type` + `response_schema` | 复杂 Schema 转换 |
| `reasoning_effort` | `thinkingConfig` | 努力级别 → 思考预算 |
| `tool_choice: "none"` | `NONE` | 大写枚举 |
| `tool_choice: "required"` | `ANY` | 语义映射 |
| `modalities: ["image"]` | `responseModalities: ["IMAGE"]` | 大写枚举 |
| `web_search_options` | `tools` (googleSearch) | 转换为 Gemini 搜索工具 |
---
### 4.5 AWS Bedrock 转换层
Bedrock 是架构最复杂的转换层,支持 **四条独立转换路径**
#### 路径 1Converse API推荐
**文件**: `litellm/llms/bedrock/chat/converse_transformation.py` (2,129 行)
**类**: `AmazonConverseConfig(BaseConfig)`
使用 Bedrock 的统一 Converse API所有提供商共享同一套请求/响应结构:
```
请求格式:{
"messages": [...], # Bedrock MessageBlock 格式
"system": [...], # SystemContentBlock 格式
"inferenceConfig": {...}, # maxTokens, temperature, topP, topK 等
"additionalModelRequestFields": {...}, # 提供商特定字段
"toolConfig": {...}, # 工具配置
"guardrailConfig": {...}, # 护栏配置
}
```
支持最丰富的功能集工具调用、思考模式、结构化输出、护栏、Web 搜索、Computer Use 等。
#### 路径 2Invoke API传统/遗留)
**文件**: `litellm/llms/bedrock/chat/invoke_transformations/` — 15 个提供商特定文件
**基类**: `AmazonInvokeConfig(BaseConfig, BaseAWSLLM)`
采用 **分派模式 (Dispatcher Pattern)**`get_bedrock_chat_config()` 从模型名解析提供商,然后委托给对应的转换类:
```
AmazonInvokeConfig.transform_request()
→ get_bedrock_invoke_provider(model) # 从模型名解析提供商
→ 按提供商委托:
- "anthropic" → AmazonAnthropicClaudeConfig复用 AnthropicConfig
- "cohere" → 构建 Cohere 格式请求
- "meta" → {"prompt": ...} 文本格式
- "nova" → AmazonInvokeNovaConfig
- "deepseek_r1", "qwen3", "qwen2", "moonshot", "openai" 等 → 各自的转换器
```
**Anthropic Claude Invoke 特殊设计**: `AmazonAnthropicClaudeConfig` 通过多重继承 `AmazonInvokeConfig + AnthropicConfig`,复用 Anthropic 原生转换逻辑,但去除 Bedrock 不兼容的字段并设置 `anthropic_version = "bedrock-2023-05-31"`
#### 路径 3Converse-like — Converse 的轻量包装器
#### 路径 4Invoke Agent — Bedrock Agent 编排调用的专用路径
**路由决策** (`get_bedrock_chat_config()`): 先按 `bedrock_route`converse/openai/agent/agentcore选择路径再按 `bedrock_invoke_provider` 选择子提供商。
---
### 4.6 Vertex AI 转换层
**文件**: `litellm/llms/vertex_ai/` — 多提供商门面 (Multi-Provider Facade)
Vertex AI 在 Google Cloud 上托管了多种第三方模型:
| 模型类型 | Config 类 / 处理方式 | 转换方式 |
|----------|----------|----------|
| Gemini | `VertexGeminiConfig` | 深度转换(同 Gemini 格式) |
| Anthropic Claude | `VertexAIAnthropicConfig` | 复用 AnthropicConfig + Vertex 认证 + `anthropic_version: "vertex-2023-10-16"` |
| Meta Llama | `OpenAILikeChatHandler` / `base_llm_http_handler` | OpenAI 兼容端点 |
| AI21 Jamba | `VertexAIAi21Config` | 继承 OpenAIGPTConfig |
| DeepSeek / Qwen / Minimax / Moonshot | `base_llm_http_handler` | OpenAI 兼容端点 |
| Mistral/Codestral | `MistralConfig` / `CodestralTextCompletionConfig` | OpenAI 兼容 |
**调度器**: `VertexAIPartnerModels` 类通过 `PartnerModelPrefixes` 枚举META, DEEPSEEK, MISTRAL, CODERESTAL, JAMBA, CLAUDE, QWEN, GPT_OSS 等)路由到正确的处理器。
---
### 4.7 其他重要提供商转换
#### Ollama本地模型
**文件**: `litellm/llms/ollama/chat/transformation.py` (580 行)
| OpenAI 参数 | Ollama 参数 | 说明 |
|-------------|-----------|------|
| `max_tokens` | `num_predict` | 名称变更 |
| `frequency_penalty` | `repeat_penalty` | 语义差异 |
| `response_format(json_object)` | `format: "json"` | 简化映射 |
| `response_format(json_schema)` | `format: <schema>` | 直接传入 schema |
| `reasoning_effort` | `think` | 布尔/字符串 |
支持 Ollama 特有参数Mirostat 采样、num_ctx、num_gpu、num_thread 等),支持从 `<think/>` 标签中提取推理内容。
#### Cohere
- **v1 API**: `CohereChatConfig(BaseConfig)` — 完全独立的转换(`chat_history` + 最新消息分离)
- **v2 API**: `CohereV2ChatConfig` — v2 是 OpenAI 兼容的,复用 OpenAI 转换
- 通过 `_get_cohere_config(model)` 根据模型选择版本
#### Mistral
**类**: `MistralConfig(OpenAIGPTConfig)`**继承自 OpenAI Config**(非直接继承 BaseConfig
特殊处理:工具 Schema 清理(移除 `$id``$schema``additionalProperties` 等 Mistral 不支持的字段Magistral 模型的推理通过注入专用系统提示实现。
---
### 4.8 Anthropic Messages API 直通层
**文件**: `litellm/llms/base_llm/anthropic_messages/transformation.py` (165 行)
**基类**: `BaseAnthropicMessagesConfig(ABC)` — **独立的**抽象基类,与 `BaseConfig` 并行
这是一条与标准 OpenAI 格式 `BaseConfig` 并行的转换路径,用于直接接受 Anthropic Messages API 格式(`/v1/messages`)的请求并转发到多个云提供商:
| 提供商 | 实现类 | 认证方式 |
|--------|--------|----------|
| Anthropic 原生 | `AnthropicMessagesConfig` | API Key |
| AWS Bedrock | `BedrockModelInfo.get_bedrock_provider_config_for_messages_api()` | AWS SigV4 |
| Vertex AI Claude | `VertexAIPartnerModelsAnthropicMessagesConfig` | Google OAuth |
| Azure AI Claude | `AzureAnthropicMessagesConfig` | Azure Token |
**关键抽象方法** (5 个):
1. `validate_anthropic_messages_environment()` — 返回 (headers, api_base)
2. `get_complete_url()` — URL 构建
3. `get_supported_anthropic_messages_params()` — 支持的参数列表
4. `transform_anthropic_messages_request()` — 请求转换
5. `transform_anthropic_messages_response()` — 响应转换
**自修复能力**: 支持在 thinking signature 无效时自动重试(`should_retry_anthropic_messages_on_http_error()`,最多 2 次)。
### 4.9 Proxy 直通模式
**目录**: `litellm/proxy/pass_through_endpoints/`
直通模式允许用户直接调用提供商的原生 API**请求体不做任何转换**,仅转换:
- **URL**: 将代理路由映射到提供商的实际 API 端点
- **认证**: 将 LiteLLM 虚拟密钥替换为提供商的 API 密钥(`PassthroughEndpointRouter` 管理凭证)
- **元数据**: 注入日志和计费信息
主要文件: `llm_passthrough_endpoints.py` (2,371 行)、`pass_through_endpoints.py``passthrough_guardrails.py` 等。
---
## 5. 流式传输处理
### 5.1 统一流式包装器
**文件**: `litellm/litellm_core_utils/streaming_handler.py` (2,418 行)
**类**: `CustomStreamWrapper`
所有提供商共用的流式响应统一化层,实现 `__iter__`/`__next__`(同步)和 `__aiter__`/`__anext__`(异步)。
**核心设计**: 引入 `GenericStreamingChunk` 统一接口:
```python
GenericStreamingChunk = {
"text": str,
"is_finished": bool,
"finish_reason": str,
"usage": Usage,
"tool_use": [...],
}
```
所有实现了 `BaseConfig.get_model_response_iterator()` 的提供商都返回符合此格式的块,`CustomStreamWrapper` 可以统一处理。
**关键功能**:
- 特殊 token 处理(`<|im_start|>`, `<|im_end|>` 等)
- Stream options 支持(`include_usage`
- 最大流式持续时间强制
- `merge_reasoning_content_in_choices` 支持
- `aclose()` 通过 `anyio.CancelScope` 正确清理
### 5.2 提供商特定的流式解析器
每个提供商都有独立的 `ModelResponseIterator` 实现:
| 提供商 | 解析器位置 | 特殊处理 |
|--------|-----------|---------|
| Anthropic | `llms/anthropic/chat/handler.py` | SSE 事件类型分派message_start/content_block_delta/message_delta |
| OpenAI | `llms/openai/chat/gpt_transformation.py` | 几乎直通 |
| Gemini | Vertex AI 流式处理 | `contents[0].parts[0].text` 格式 |
| Bedrock Converse | `llms/bedrock/chat/converse_handler.py` | AWS 事件流格式 |
| Ollama | 内置于 transformation.py | JSON 行格式,`<think/>` 标签解析 |
### 5.3 Fake Stream 机制
部分提供商(如某些 Bedrock Invoke 模型不支持真正的流式传输。LiteLLM 通过 `should_fake_stream()` 检测这种情况,先发送非流式请求获取完整响应,然后通过 `MockResponseIterator` 逐块模拟流式输出。
---
## 6. 转换层设计模式总结
### 6.1 三层架构
```
┌─────────────────────────────────────────┐
│ Proxy Layer (proxy/) │ 认证、限流、路由、计费
├─────────────────────────────────────────┤
│ SDK Layer (litellm/) │ 统一入口、参数映射
│ ┌─────────────────────────────────┐ │
│ │ BaseLLMHTTPHandler │ │ HTTP 编排器(不变)
│ │ ┌───────────────────────────┐ │ │
│ │ │ ProviderConfig (BaseConfig)│ │ │ 转换策略(可变)
│ │ │ • transform_request() │ │ │
│ │ │ • transform_response() │ │ │
│ │ │ • map_openai_params() │ │ │
│ │ └───────────────────────────┘ │ │
│ └─────────────────────────────────┘ │
├─────────────────────────────────────────┤
│ HTTP Client (httpx) │ 底层 HTTP 通信
└─────────────────────────────────────────┘
```
### 6.2 关键设计模式
| 模式 | 应用位置 | 说明 |
|------|---------|------|
| **策略模式** | `BaseConfig` + 各提供商 Config | 每个提供商的转换逻辑独立封装 |
| **工厂模式** | `ProviderConfigManager` | 根据模型/提供商动态创建 Config 实例(含子工厂) |
| **分派模式** | `AmazonInvokeConfig`, `VertexAIPartnerModels` | 根据模型名检测子提供商并委托转换 |
| **模板方法** | `BaseConfig` 抽象方法 | 定义统一的转换接口,子类实现细节 |
| **门面模式** | `VertexAIPartnerModels` | 统一入口管理多种 Vertex AI 合作模型 |
| **适配器模式** | `AmazonAnthropicClaudeConfig` | 通过多重继承适配不同 APIBedrock + Anthropic |
| **装饰器模式** | `CustomStreamWrapper` | 统一包装各提供商的流式响应 |
### 6.3 转换类型分类
| 转换类型 | 复杂度 | 典型提供商 | 说明 |
|----------|--------|-----------|------|
| **直通型** | 低 | OpenAI, Azure, DeepSeek, Groq | 格式基本兼容,仅过滤/重命名少量参数 |
| **参数重映射型** | 中 | Ollama, Cohere v1 | 参数名称变更,消息格式基本兼容 |
| **深度转换型** | 高 | Anthropic, Gemini, Bedrock Converse | 消息结构完全不同,角色系统不同,工具调用格式不同 |
| **分派委托型** | 高 | Bedrock Invoke, Vertex AI | 根据子提供商委托到不同的转换器 |
| **直通代理型** | 极低 | Proxy 直通模式 | 仅转换 URL 和认证,请求体不变 |
### 6.4 添加新提供商的标准流程
1.`litellm/llms/{provider}/chat/transformation.py` 创建 Config 类
2. 实现 `BaseConfig` 的 6 个抽象方法
3.`ProviderConfigManager._build_provider_config_map()` 中注册
4.`get_llm_provider()` 中添加提供商解析规则
5.`tests/llm_translation/test_{provider}.py` 添加单元测试
---
## 7. 关键目录结构图
```
litellm/
├── main.py # 统一入口completion(), acompletion() (7,807行)
├── utils.py # ProviderConfigManager, 工厂方法 (9,568行)
├── types/
│ ├── llms/openai.py # OpenAI 格式类型定义
│ └── utils.py # LlmProviders 枚举 (133个), SearchProviders 枚举 (12个)
├── llms/
│ ├── base_llm/ # 抽象基类目录 (29子目录, 覆盖多种模态)
│ │ ├── chat/transformation.py # ★ BaseConfig 抽象基类 (466行)
│ │ ├── anthropic_messages/ # Anthropic Messages 直通基类
│ │ ├── embedding/ # Embedding 基类
│ │ ├── rerank/ # Rerank 基类
│ │ └── ... (其他模态)
│ ├── custom_httpx/
│ │ ├── llm_http_handler.py # ★ BaseLLMHTTPHandler 编排器 (12,161行)
│ │ └── http_handler.py # HTTP 客户端封装
│ ├── anthropic/chat/transformation.py # Anthropic 转换 (2,096行)
│ ├── openai/chat/gpt_transformation.py # OpenAI 转换 (820行)
│ ├── gemini/chat/transformation.py # Gemini 转换 (薄层, 154行)
│ ├── vertex_ai/gemini/transformation.py # Gemini 核心转换 (941行)
│ ├── vertex_ai/vertex_ai_partner_models/ # Vertex AI 合作模型门面
│ ├── bedrock/chat/
│ │ ├── converse_transformation.py # Bedrock Converse 转换 (2,129行)
│ │ └── invoke_transformations/ # Bedrock Invoke (15个子提供商)
│ ├── ollama/chat/transformation.py # Ollama 转换 (580行)
│ ├── cohere/chat/transformation.py # Cohere 转换 (373行)
│ ├── mistral/chat/transformation.py # Mistral 转换 (686行, 继承OpenAI)
│ └── ... (~118 个提供商目录)
├── litellm_core_utils/
│ ├── streaming_handler.py # CustomStreamWrapper 统一流式处理 (2,418行)
│ └── get_llm_provider_logic.py # 提供商解析逻辑 (958行)
├── proxy/
│ └── pass_through_endpoints/ # 直通代理模式 (含凭证管理)
└── router.py # 负载均衡、故障转移
```
---
## 8. 总结
LiteLLM 的转换层是一个设计精良的**统一 API 网关转换引擎**,其核心优势在于:
1. **高度模块化**: 每个提供商的转换逻辑完全独立,修改一个提供商不影响其他提供商
2. **统一抽象**: 所有提供商通过 `BaseConfig` 接口统一管理,编排器不需要感知提供商差异
3. **广泛覆盖**: 支持 133 个 LLM 提供商、12 个搜索提供商,涵盖所有主流云厂商、开源模型和本地部署
4. **多协议支持**: 同时支持 OpenAI Chat Completions、Anthropic Messages (直通)、Gemini GenerateContent 等多种协议
5. **多模态支持**: 29 个 base_llm 子目录覆盖 Chat、Embedding、Rerank、Audio、OCR、Image、Video、Responses API 等模态
6. **可扩展性**: 添加新提供商只需实现一个 Config 类并注册
7. **流式兼容**: 通过 `GenericStreamingChunk` + `CustomStreamWrapper` (2,418行) 统一处理所有提供商的流式响应
8. **自修复能力**: 支持 HTTP 错误时自动重试和修复thinking signature、请求体重构等

View File

@@ -0,0 +1,507 @@
# New-API 项目 API 代理转换层详细分析报告
## 一、项目概述
`new-api` 是一个用 Go 语言编写的 AI API 网关/代理项目,基于 Gin Web 框架和 GORM ORM 构建。其核心功能是将 30+ 种上游 AI 服务提供商OpenAI、Claude/Anthropic、Google Gemini、AWS Bedrock、阿里通义千问、百度文心等的 API 统一管理,并以标准化格式对外提供统一入口。
### 与同类项目的核心差异
New-API 是 One-API 的增强分支,在 One-API 的 OpenAI-only 输入基础上增加了**多协议输入**能力(同时接受 OpenAI、Claude、Gemini 三种格式的客户端请求)和 **Hub-and-Spoke 转换架构**(以 OpenAI 格式为内部枢纽)。同时引入了 **Chat Completions via Responses API** 内部路由、**Thinking-to-Content** 转换、异步任务适配器等高级特性。
---
## 二、技术架构
### 2.1 分层架构
项目采用经典的分层架构:**Router → Controller → Relay Handler → Adaptor → Upstream API**
```
┌──────────────────────────────────────────────────────────────┐
│ 客户端请求 │
│ (OpenAI / Claude / Gemini / Embedding / Audio / Image ...) │
└───────────────────────────┬──────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ router/relay-router.go │
│ 路由层URL路径 → RelayFormat 映射 │
│ /v1/chat/completions → RelayFormatOpenAI │
│ /v1/messages → RelayFormatClaude │
│ /v1beta/models/* → RelayFormatGemini │
│ /v1/responses → RelayFormatOpenAIResponses │
│ /v1/embeddings → RelayFormatEmbedding ... │
└───────────────────────────┬──────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ controller/relay.go │
│ 控制器层:请求验证、计费预扣、重试循环 │
│ │
│ Relay() 函数: │
│ 1. 解析验证请求 (GetAndValidateRequest) │
│ 2. 生成 RelayInfo (GenRelayInfo) │
│ 3. Token估算 + 敏感词检测 │
│ 4. 价格计算 + 预扣费 │
│ 5. 渠道选择 + 重试循环 │
│ 6. 按 RelayFormat 分发到 Handler │
└───────────────────────────┬──────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ relay/ 目录 - Handler + Adaptor 层 │
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │compatible_handler│ │claude_handler.go │ │
│ │ (OpenAI格式) │ │ (Claude格式) │ │
│ └────────┬─────────┘ └────────┬─────────┘ │
│ │ │ │
│ ┌────────┴─────────┐ ┌────────┴─────────┐ │
│ │gemini_handler.go │ │responses_handler │ │
│ │ (Gemini格式) │ │ (Responses API) │ │
│ └────────┬─────────┘ └────────┬─────────┘ │
│ └──────────┬───────────┘ │
│ ▼ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ relay/relay_adaptor.go │ │
│ │ GetAdaptor(apiType) → channel.Adaptor │ │
│ └──────────────────────┬───────────────────────────┘ │
│ ▼ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ relay/channel/adapter.go (Adaptor接口) │ │
│ │ ConvertOpenAIRequest() - OpenAI格式请求转换 │ │
│ │ ConvertClaudeRequest() - Claude格式请求转换 │ │
│ │ ConvertGeminiRequest() - Gemini格式请求转换 │ │
│ │ ConvertEmbeddingRequest() / Audio / Image / Rerank│ │
│ │ ConvertOpenAIResponsesRequest() - Responses转换 │ │
│ │ DoRequest() + DoResponse() │ │
│ └──────────────────────┬───────────────────────────┘ │
└─────────────────────────┼────────────────────────────────────┘
┌──────────────────────────────────────────────────────────────┐
│ 上游 AI 服务提供商 │
│ OpenAI / Claude / Gemini / AWS Bedrock / 阿里 / 百度 / ... │
└──────────────────────────────────────────────────────────────┘
```
### 2.2 核心设计模式
项目采用 **适配器模式 + Hub-and-Spoke 架构**
1. **统一接口 (`channel.Adaptor`)**:定义所有提供商必须实现的转换方法
2. **工厂函数 (`GetAdaptor`)**:根据 `apiType` 创建对应提供商的适配器实例
3. **Hub-and-Spoke 转换架构**:以 OpenAI 格式作为内部规范格式Canonical Format所有其他格式先转换为 OpenAI 格式,再由 OpenAI 格式转换为目标格式
### 2.3 关键接口定义
**同步请求适配器接口** (`relay/channel/adapter.go`, 83 行)
```go
type Adaptor interface {
Init(info *relaycommon.RelayInfo)
GetRequestURL(info *relaycommon.RelayInfo) (string, error)
SetupRequestHeader(c *gin.Context, req *http.Header, info *relaycommon.RelayInfo) error
ConvertOpenAIRequest(c, info, request *dto.GeneralOpenAIRequest) (any, error)
ConvertClaudeRequest(c, info, request *dto.ClaudeRequest) (any, error)
ConvertGeminiRequest(c, info, request *dto.GeminiChatRequest) (any, error)
ConvertRerankRequest(c, relayMode int, request dto.RerankRequest) (any, error)
ConvertEmbeddingRequest(c, info, request dto.EmbeddingRequest) (any, error)
ConvertAudioRequest(c, info, request dto.AudioRequest) (io.Reader, error)
ConvertImageRequest(c, info, request dto.ImageRequest) (any, error)
ConvertOpenAIResponsesRequest(c, info, request dto.OpenAIResponsesRequest) (any, error)
DoRequest(c, info, requestBody io.Reader) (any, error)
DoResponse(c, resp, info) (usage any, err *types.NewAPIError)
GetModelList() []string
GetChannelName() string
}
```
**异步任务适配器接口** (`TaskAdaptor`)16 个方法,覆盖任务提交、轮询、计费估算等全生命周期。
---
## 三、支持的协议/格式类型
### 3.1 输入格式RelayFormat
**文件**: `types/relay_format.go` (19 行)
| RelayFormat 常量 | 值 | 对应路由 |
|---|---|---|
| `RelayFormatOpenAI` | `"openai"` | `/v1/chat/completions`, `/v1/completions`, `/v1/moderations` |
| `RelayFormatClaude` | `"claude"` | `/v1/messages` |
| `RelayFormatGemini` | `"gemini"` | `/v1beta/models/*`, `/v1/engines/:model/embeddings` |
| `RelayFormatOpenAIResponses` | `"openai_responses"` | `/v1/responses` |
| `RelayFormatOpenAIResponsesCompaction` | `"openai_responses_compaction"` | `/v1/responses/compact` |
| `RelayFormatOpenAIAudio` | `"openai_audio"` | `/v1/audio/*` |
| `RelayFormatOpenAIImage` | `"openai_image"` | `/v1/images/*` |
| `RelayFormatOpenAIRealtime` | `"openai_realtime"` | `/v1/realtime` (WebSocket) |
| `RelayFormatRerank` | `"rerank"` | `/v1/rerank` |
| `RelayFormatEmbedding` | `"embedding"` | `/v1/embeddings` |
| `RelayFormatTask` | `"task"` | 异步任务 (Suno/Video等) |
| `RelayFormatMjProxy` | `"mj_proxy"` | `/mj/*` |
### 3.2 上游提供商 API 类型
**文件**: `constant/api_type.go` (40 行)
定义 35 种 API 类型iota 0-34, 加 APITypeDummy=35工厂函数 `GetAdaptor()` 映射了其中 29 种(其余复用 OpenAI 适配器或未使用):
| API 类型 | 提供商 | 适配器目录 |
|---|---|---|
| `APITypeOpenAI` (0) | OpenAI / Azure / OpenRouter / Xinference | `relay/channel/openai/` |
| `APITypeAnthropic` (1) | Anthropic Claude | `relay/channel/claude/` |
| `APITypeGemini` (9) | Google Gemini | `relay/channel/gemini/` |
| `APITypeAws` (13) | AWS Bedrock | `relay/channel/aws/` |
| `APITypeAli` (5) | 阿里云通义千问 | `relay/channel/ali/` |
| `APITypeBaidu` (3) / `APITypeBaiduV2` (24) | 百度文心 | `relay/channel/baidu/`, `baidu_v2/` |
| `APITypeZhipu` (4) / `APITypeZhipuV4` (10) | 智谱AI (GLM) | `relay/channel/zhipu/`, `zhipu_4v/` |
| `APITypeXunfei` (6) | 科大讯飞 | `relay/channel/xunfei/` |
| `APITypeTencent` (8) | 腾讯混元 | `relay/channel/tencent/` |
| `APITypeOllama` (11) | Ollama | `relay/channel/ollama/` |
| `APITypeDeepSeek` (21) | DeepSeek | `relay/channel/deepseek/` |
| `APITypeVolcEngine` (23) | 火山引擎 | `relay/channel/volcengine/` |
| `APITypeVertexAi` (19) | Google Vertex AI | `relay/channel/vertex/` |
| `APITypeMistral` (20) | Mistral AI | `relay/channel/mistral/` |
| `APITypeCohere` (14) | Cohere | `relay/channel/cohere/` |
| `APITypeOpenRouter` (25) | OpenRouter | 复用 `openai/` |
| `APITypeXai` (27) | xAI (Grok) | `relay/channel/xai/` |
| `APITypeCoze` (28) | 扣子 | `relay/channel/coze/` |
| `APITypeMoonshot` (30) | Moonshot (月之暗面) | `relay/channel/moonshot/` |
| `APITypeMiniMax` (32) | MiniMax | `relay/channel/minimax/` |
| `APITypeCodex` (34) | Codex | `relay/channel/codex/` |
| ... 其他 | SiliconFlow, Cloudflare, Perplexity, Dify, Jina, Jimeng, MokaAI, Replicate, Submodel | 各自目录 |
---
## 四、转换层核心业务逻辑
### 4.1 格式转换核心 — Hub-and-Spoke 模式
项目以 **OpenAI 格式作为内部规范格式Canonical Format**,采用 Hub-and-Spoke 转换架构:
```
客户端输入格式
┌──────┬──────┬───────┐
│OpenAI│Claude│Gemini │ ...
└──┬───┴──┬───┴──┬────┘
│ │ │
▼ ▼ ▼
┌─────────────────────────┐
│ OpenAI 规范格式 (Hub) │
│ dto.GeneralOpenAIRequest │
└────────────┬──────────────┘
┌──────┼──────┐
▼ ▼ ▼
┌──────┐┌──────┐┌──────┐
│OpenAI││Claude││Gemini│ ... (上游格式)
└──┬───┘└──┬───┘└──┬───┘
│ │ │
▼ ▼ ▼
上游API 上游API 上游API
```
**请求转换方向**
- `Claude → OpenAI → 上游原生格式`(如上游是 Gemini: `Claude → OpenAI → Gemini`,链式转换)
- `Gemini → OpenAI → 上游原生格式`
- `OpenAI → 上游原生格式`(直接转换)
**响应转换方向**
- `上游原生格式 → OpenAI → 客户端格式`
- 如客户端请求 Claude 格式且上游是 Gemini: `Gemini → OpenAI → Claude`
### 4.2 转换链跟踪 (RequestConversionChain)
`RelayInfo` 中维护 `RequestConversionChain []types.RelayFormat` 字段,记录请求格式变化轨迹。例如:
- 用户发送 OpenAI 格式,上游是 Claude`["openai", "claude"]`
- 用户发送 Claude 格式,上游是 OpenAI 兼容:`["claude", "openai"]`
- 用户发送 Claude 格式,上游是 Gemini`["claude", "openai", "gemini"]`(链式转换)
`FinalRequestRelayFormat` 记录最终发送到上游的格式。
---
## 五、核心转换实现
### 5.1 核心转换服务层 — `service/convert.go` (~1007 行)
这是整个项目的**中心格式转换库**,包含 OpenAI ↔ Claude、OpenAI ↔ Gemini 的双向转换函数:
| 函数 | 转换方向 | 位置 |
|---|---|---|
| `ClaudeToOpenAIRequest()` | Claude 请求 → OpenAI 请求 | line 17 |
| `GeminiToOpenAIRequest()` | Gemini 请求 → OpenAI 请求 | line 658 |
| `ResponseOpenAI2Claude()` | OpenAI 响应 → Claude 响应 | line 607 |
| `StreamResponseOpenAI2Claude()` | OpenAI 流式 → Claude 流式 | line 255 |
| `ResponseOpenAI2Gemini()` | OpenAI 响应 → Gemini 响应 | line 828 |
| `StreamResponseOpenAI2Gemini()` | OpenAI 流式 → Gemini 流式 | line 907 |
### 5.2 Claude 适配器 — `relay/channel/claude/`
| 文件 | 行数 | 功能 |
|---|---|---|
| `relay-claude.go` | ~1009 | Claude 原生请求/响应转换 + 流式处理 |
| `adaptor.go` | ~134 | 适配器实现 |
| `dto.go` | — | Claude DTO 定义 |
| `constants.go` | — | Claude 模型列表 |
**关键转换函数**:
- `RequestOpenAI2ClaudeMessage()` — OpenAI→Claude 请求转换 (line 47): 工具转换 (function→input_schema)、Web Search 工具、tool_choice 映射 (required→any, named→tool)、parallel_tool_calls→disable_parallel_tool_use、Thinking adapter (Opus 4.6/4.7 adaptive/enabled 模式、ReasoningEffort→BudgetTokens 映射)、OpenRouter reasoning 参数覆盖、消息角色映射、媒体内容 (image/PDF→base64 source)
- `StreamResponseClaude2OpenAI()` / `ResponseClaude2OpenAI()` — Claude→OpenAI 响应转换
- `mapToolChoice()` — OpenAI tool_choice → Claude tool_choice
### 5.3 Gemini 适配器 — `relay/channel/gemini/`
| 文件 | 行数 | 功能 |
|---|---|---|
| `relay-gemini.go` | ~1900 | Gemini 原生请求/响应转换 + Thinking adapter + 原生直通处理 |
| `adaptor.go` | ~287 | 适配器实现,支持多级链式转换 |
| `relay-gemini-native.go` | 97 | Gemini 原生格式直通处理 |
**关键转换函数**:
- `CovertOpenAI2Gemini()` — OpenAI→Gemini 请求转换 (line 201): GenerationConfig 映射、ThinkingAdaptor (thinking 后缀/budget clamp/effort level)、安全设置、工具转换 (tools→functionDeclarations + googleSearch/codeExecution/urlContext)、tool_choice→Gemini ToolConfig、ResponseFormat→responseMimeType+responseSchema、角色映射 (assistant→model, system→SystemInstructions)、Markdown 图片检测、ThoughtSignature bypass、Schema 清理
- `ThinkingAdaptor()` — Thinking 配置适配 (line 134): 支持 `-thinking``-thinking-<budget>``-nothinking` 后缀model-specific budget clamp
- `responseGeminiChat2OpenAI()` / `streamResponseGeminiChat2OpenAI()` — Gemini→OpenAI 响应转换
**Gemini 适配器的链式转换支持**:
- `ConvertClaudeRequest()`: 先通过 OpenAI 适配器 Claude→OpenAI再 ConvertOpenAIRequest() 转 Gemini
- `DoResponse()`: 先 Gemini→OpenAI再根据客户端格式转 Claude/Gemini 输出
### 5.4 OpenAI 适配器 — `relay/channel/openai/`
| 文件 | 行数 | 功能 |
|---|---|---|
| `adaptor.go` | ~683 | OpenAI 适配器实现,含 Claude/Gemini→OpenAI 入口 |
| `relay-openai.go` | ~718 | OpenAI 响应处理,含多格式输出分发 |
| `helper.go` | ~261 | 流式格式分发器 `HandleStreamFormat()` |
| `chat_via_responses.go` | ~550 | Chat Completions via Responses API 模式 |
| `relay_responses.go` | ~150 | Responses API 原生处理 |
| `relay_responses_compact.go` | ~44 | Responses Compact API 处理 |
**`HandleStreamFormat()`** — 流式响应核心分发器 (helper.go line 22):
```go
switch info.RelayFormat {
case types.RelayFormatClaude:
handleClaudeFormat(c, data, info) // → service.StreamResponseOpenAI2Claude
case types.RelayFormatGemini:
handleGeminiFormat(c, data, info) // → service.StreamResponseOpenAI2Gemini
default:
sendStreamData(c, data) // 直接透传 OpenAI 格式 (含 forceFormat/thinkToContent)
}
```
### 5.5 Chat Completions ↔ Responses API 转换 — `service/openaicompat/`
| 文件 | 行数 | 功能 |
|---|---|---|
| `chat_to_responses.go` | ~402 | Chat Completions → Responses API |
| `responses_to_chat.go` | ~133 | Responses API → Chat Completions |
| `policy.go` | 19 | 基于正则的策略路由:判断是否将 Chat 请求路由到 Responses API |
**Chat-via-Responses 优化**: 当配置开启时(通过正则策略匹配模型名),系统将 Chat Completions 请求内部转换为 Responses API 格式发送,再将响应转回 Chat 格式。这允许在不改变客户端 API 的前提下利用 Responses API 的独有特性reasoning summaries、built-in tools
### 5.6 其他提供商适配器
| 提供商 | 转换逻辑 |
|---|---|
| **阿里 (ali/)** | OpenAI→DashScope 格式Claude 先→OpenAI→阿里支持 qwen 模型 Claude 兼容;异步图片生成 (X-DashScope-Async) |
| **AWS Bedrock (aws/)** | OpenAI→Claude 格式(通过 claude 包转换);支持 Nova 模型特殊处理API Key + AKSK 双认证模式 |
| **Ollama (ollama/)** | OpenAI→Ollama `/api/chat``/api/generate`Claude 先→OpenAI→Ollama |
| **百度 (baidu/, baidu_v2/)** | OpenAI→百度文心格式 |
| **智谱 (zhipu/, zhipu_4v/)** | OpenAI→智谱 GLM 格式 |
| **讯飞 (xunfei/)** | OpenAI→讯飞星火格式 |
| **腾讯 (tencent/)** | OpenAI→腾讯混元格式 |
| **火山引擎 (volcengine/)** | OpenAI→火山引擎格式 |
| **Vertex AI (vertex/)** | 继承 Gemini 适配器,添加 Google Cloud 认证 |
| **DeepSeek / Mistral** | OpenAI 兼容(基础直通) |
---
## 六、详细转换流程分析
### 6.1 场景一:用户发送 OpenAI 格式,上游是 Claude
**请求路径**: `POST /v1/chat/completions` → Claude 上游
```
1. 路由层: RelayFormatOpenAI → controller.Relay()
2. 控制器: relayHandler() → relay.TextHelper()
3. TextHelper():
a. 解析请求 → dto.GeneralOpenAIRequest
b. GetAdaptor(APITypeAnthropic) → claude.Adaptor
c. adaptor.ConvertOpenAIRequest() → RequestOpenAI2ClaudeMessage()
- 角色映射、工具转换、thinking adapter、参数映射
d. 发送到 Claude /v1/messages 端点
4. DoResponse():
- 流式: ClaudeStreamHandler() → StreamResponseClaude2OpenAI()
- 非流式: ClaudeHandler() → ResponseClaude2OpenAI()
转换链: ["openai", "claude"]
```
### 6.2 场景二:用户发送 Claude 格式,上游是 Gemini
**请求路径**: `POST /v1/messages` → Gemini 上游
```
1. 路由层: RelayFormatClaude → controller.Relay()
2. 控制器: ClaudeHelper()
3. ClaudeHelper():
a. 解析请求 → dto.ClaudeRequest
b. Thinking adapter (Opus 模型)
c. 可选: Chat-via-Responses 桥接
d. GetAdaptor(APITypeGemini) → gemini.Adaptor
e. adaptor.ConvertClaudeRequest():
- 第一步: openai.Adaptor.ConvertClaudeRequest() → ClaudeToOpenAIRequest()
- 第二步: CovertOpenAI2Gemini() → Gemini 格式
- 转换链: ["claude", "openai", "gemini"]
f. 发送到 Gemini generateContent 端点
4. DoResponse():
- 流式: GeminiChatStreamHandler() → streamResponseGeminiChat2OpenAI()
再根据客户端格式: service.StreamResponseOpenAI2Claude()
- 非流式: GeminiChatHandler() → responseGeminiChat2OpenAI()
再根据客户端格式: service.ResponseOpenAI2Claude()
```
### 6.3 场景三:用户发送 Gemini 格式,上游是 OpenAI
**请求路径**: `POST /v1beta/models/gemini-pro:generateContent` → OpenAI 上游
```
1. 路由层: RelayFormatGemini → GeminiHelper()
2. GeminiHelper():
a. 解析请求 → dto.GeminiChatRequest
b. Thinking adapter
c. adaptor.ConvertGeminiRequest(): Gemini→OpenAI (GeminiToOpenAIRequest)
d. 发送到 OpenAI /v1/chat/completions
3. DoResponse():
- OpenAI 响应 → Gemini 格式 (ResponseOpenAI2Gemini / StreamResponseOpenAI2Gemini)
转换链: ["gemini", "openai"]
```
### 6.4 场景四Chat Completions 经由 Responses API
```
1. POST /v1/chat/completions
2. TextHelper() 检测到 ShouldChatCompletionsUseResponsesGlobal() 为 true
3. chatCompletionsViaResponses():
a. ChatCompletionsRequestToResponsesRequest() — Chat → Responses API
b. adaptor.ConvertOpenAIResponsesRequest() — 转换为上游格式
c. 发送请求到上游 /v1/responses
d. OaiResponsesToChatHandler() / OaiResponsesToChatStreamHandler() — Responses → Chat
e. 如果客户端格式是 Claude/Gemini继续转换为对应格式
转换链: ["openai", "openai_responses"]
```
---
## 七、特殊功能与高级特性
### 7.1 Pass-Through 模式
当全局 `PassThroughRequestEnabled` 或渠道 `PassThroughBodyEnabled` 启用时,请求体直接透传到上游,跳过所有格式转换。
### 7.2 字段过滤 (RemoveDisabledFields)
根据渠道安全设置过滤敏感字段: `service_tier``inference_geo``speed``store``safety_identifier``stream_options.include_obfuscation`
### 7.3 参数覆写 (ParamOverride)
通过 `relay/common/override.go` 支持请求参数覆写(如强制 temperature、max_tokens
### 7.4 模型名称映射
支持将用户请求的模型名称映射为上游实际模型名称(渠道级配置)。
### 7.5 Thinking/Reasoning 适配
- **Claude**: `-thinking` 后缀 → 启用 extended thinking + BudgetTokensOpus 4.6/4.7 adaptive 模式
- **Gemini**: `ThinkingAdaptor()` 支持 `-thinking``-thinking-<budget>``-nothinking` 后缀model-specific budget clamp
- **OpenAI**: o-series 模型 → `-high/-medium/-low` 推理力度后缀
### 7.6 Thinking-to-Content 转换
渠道设置 `ThinkingToContent` 启用时,将 reasoning content 转换为带有 `<think\>...</think\>` 标签的普通文本内容,供不支持 reasoning 字段的客户端使用。
### 7.7 StreamOptions 适配
对支持 `stream_options` 的渠道自动添加 `include_usage: true` 以获取流式用量信息。
### 7.8 SSE Ping Keep-Alive
内置 ping 机制保持长时间流式连接活跃。
### 7.9 Header 覆写/透传
复杂的 Header 管理系统:支持占位符替换 (`{api_key}`, `{client_header:name}`)、正则规则透传、渠道级覆写。
### 7.10 异步任务适配器 (TaskAdaptor)
对视频/音乐等异步生成任务使用独立的 `TaskAdaptor` 接口支持任务提交、轮询、计费估算。覆盖平台Suno、Kling、Jimeng、Vidu、Doubao、Sora、Gemini、Vertex AI、Hailuo 等。
---
## 八、转换矩阵总结
### 请求转换矩阵
| 客户端 \ 上游 | OpenAI | Claude | Gemini | Ali | AWS | Ollama |
|---|---|---|---|---|---|---|
| **OpenAI** | 直通 | OpenAI→Claude | OpenAI→Gemini | OpenAI→Ali | OpenAI→Claude(Bedrock) | OpenAI→Ollama |
| **Claude** | Claude→OpenAI | 直通 | Claude→OpenAI→Gemini | Claude→OpenAI→Ali | 直通 | Claude→OpenAI→Ollama |
| **Gemini** | Gemini→OpenAI | Gemini→OpenAI→Claude | 直通 | × | × | × |
### 响应转换矩阵
| 上游 \ 客户端 | OpenAI | Claude | Gemini |
|---|---|---|---|
| **OpenAI** | 直通 | OpenAI→Claude | OpenAI→Gemini |
| **Claude** | Claude→OpenAI | 直通 | Claude→OpenAI→Gemini |
| **Gemini** | Gemini→OpenAI | Gemini→OpenAI→Claude | 直通 |
| **Ali/Ollama/其他** | →OpenAI | →OpenAI→Claude | × |
---
## 九、关键文件索引
| 文件路径 | 行数 | 功能 |
|---|---|---|
| `relay/channel/adapter.go` | 83 | Adaptor/TaskAdaptor 接口定义 |
| `relay/relay_adaptor.go` | 165 | 适配器工厂函数 GetAdaptor/GetTaskAdaptor |
| `types/relay_format.go` | 19 | RelayFormat 类型定义 |
| `constant/api_type.go` | 40 | API 类型常量 (35种) |
| `service/convert.go` | ~1007 | **核心转换库** (OpenAI↔Claude↔Gemini) |
| `relay/channel/claude/relay-claude.go` | ~1009 | Claude 原生格式转换 |
| `relay/channel/gemini/relay-gemini.go` | ~1900 | Gemini 原生格式转换 + Thinking adapter |
| `relay/channel/gemini/relay-gemini-native.go` | 97 | Gemini 原生直通处理 |
| `relay/channel/openai/adaptor.go` | ~683 | OpenAI 适配器 (含 Claude/Gemini 入口) |
| `relay/channel/openai/helper.go` | ~261 | 流式格式分发器 HandleStreamFormat |
| `relay/channel/openai/relay-openai.go` | ~718 | OpenAI 响应处理 (含 WebSocket 代理) |
| `relay/channel/openai/chat_via_responses.go` | ~550 | Chat→Responses 内部路由 |
| `service/openaicompat/chat_to_responses.go` | ~402 | Chat→Responses 转换 |
| `service/openaicompat/responses_to_chat.go` | ~133 | Responses→Chat 转换 |
| `relay/common/relay_info.go` | ~890 | RelayInfo 上下文对象 |
| `relay/common/request_conversion.go` | 40 | 转换链跟踪 |
| `controller/relay.go` | ~647 | 主控制器 (含重试循环) |
| `router/relay-router.go` | 224 | 路由定义 |
| `relay/compatible_handler.go` | 217 | OpenAI 格式文本请求处理 (TextHelper) |
| `relay/claude_handler.go` | 214 | Claude 格式请求处理 (ClaudeHelper) |
| `relay/gemini_handler.go` | 293 | Gemini 格式请求处理 (GeminiHelper) |
| `relay/responses_handler.go` | 161 | Responses API 处理 |
| `relay/chat_completions_via_responses.go` | 161 | Chat-via-Responses 桥接处理 |
---
## 十、总结
`new-api` 的转换层是一个**多协议 Hub-and-Spoke API 网关转换系统**,核心特点:
1. **Hub-and-Spoke 架构**: 以 OpenAI 格式为枢纽,避免 O(n²) 的转换矩阵,通过链式转换支持任意输入/输出格式组合
2. **多格式输入**: 同一上游提供商可接受 OpenAI、Claude、Gemini 三种主流格式,通过 `RequestConversionChain` 跟踪转换路径
3. **统一适配器接口**: `channel.Adaptor` 接口抽象所有提供商差异,新提供商只需实现该接口
4. **Chat-via-Responses 优化**: 在不改变客户端 API 的前提下内部路由到 Responses API利用其独有特性
5. **Thinking 深度适配**: Claude/Gemini/OpenAI 三种 thinking/reasoning 模式均有完善的参数适配
6. **流式实时转换**: SSE 流式响应逐块格式转换,包括复杂的状态机管理
7. **异步任务支持**: 独立 `TaskAdaptor` 接口覆盖视频/音乐生成等异步场景
8. **计费深度集成**: 转换层与预扣费、结算、退款等完整计费生命周期集成

View File

@@ -0,0 +1,667 @@
# One-API 项目转换层深度分析报告
## 一、项目概览
One-API 是一个基于 Go 语言Gin 框架)开发的大模型 API 网关代理项目。其核心功能是:**将用户发来的统一 OpenAI 格式请求,转换为各家大模型厂商的原生 API 格式,转发请求后,再将厂商的响应统一转换回 OpenAI 格式返回给用户**。
对外暴露的接口完全兼容 OpenAI API 规范,用户只需更改 `base_url``api_key` 即可使用。
### 与同类项目的核心差异
One-API 是此类项目的**鼻祖和基线**,采用最简单的架构模式——仅支持 OpenAI 格式作为唯一输入/输出协议,所有转换都是 **OpenAI ↔ 厂商原生格式** 的双向转换。New-API 在其基础上增加了 Claude/Gemini 输入支持和 Hub-and-Spoke 架构。One-API 的设计追求简洁实用56 种渠道类型通过三层映射归并为 19 种 API 类型,其中 18 种 OpenAI 兼容渠道直接复用同一适配器,实现零开销透传。
---
## 二、项目目录结构
```
one-api/
├── main.go # 程序入口
├── router/
│ └── relay.go # 路由定义(/v1/chat/completions 等)
├── middleware/
│ ├── auth.go # Token 认证
│ ├── distributor.go # 渠道分发(负载均衡)
│ └── rate-limit.go # 限流
├── controller/
│ └── relay.go # 入口控制器(含重试逻辑)
├── relay/ # ★ 转换层核心目录
│ ├── adaptor.go # Adaptor 工厂函数 (69行)
│ ├── adaptor/ # 各厂商 Adaptor 实现
│ │ ├── interface.go # Adaptor 接口定义 (9方法)
│ │ ├── common.go # 公共辅助函数
│ │ ├── openai/ # OpenAI 适配器(基座)
│ │ ├── anthropic/ # Anthropic Claude 适配器
│ │ ├── gemini/ # Google Gemini 适配器
│ │ ├── geminiv2/ # Gemini V2 模型适配器
│ │ ├── ali/ # 阿里通义千问适配器
│ │ ├── baidu/ # 百度文心适配器
│ │ ├── zhipu/ # 智谱 ChatGLM 适配器
│ │ ├── tencent/ # 腾讯混元适配器
│ │ ├── xunfei/ # 讯飞星火适配器
│ │ ├── aws/ # AWS Bedrock Claude 适配器 (含子适配器)
│ │ │ ├── claude/ # Claude on Bedrock
│ │ │ └── llama3/ # Llama3 on Bedrock
│ │ ├── vertexai/ # Google Vertex AI (含子适配器)
│ │ │ ├── claude/ # Claude on Vertex AI
│ │ │ └── gemini/ # Gemini on Vertex AI
│ │ ├── ollama/ # Ollama 适配器
│ │ ├── coze/ # Coze 适配器
│ │ ├── cohere/ # Cohere 适配器
│ │ ├── cloudflare/ # Cloudflare Workers AI 适配器
│ │ ├── deepl/ # DeepL 翻译适配器
│ │ ├── replicate/ # Replicate 适配器
│ │ ├── palm/ # Google PaLM 适配器
│ │ ├── proxy/ # 通用代理适配器
│ │ ├── aiproxy/ # AIProxy 适配器
│ │ └── ... (38个子目录)
│ ├── controller/ # 中转控制器(按功能分类)
│ │ ├── text.go # 文本类请求处理 (115行)
│ │ ├── image.go # 图片生成请求处理 (238行)
│ │ ├── audio.go # 音频请求处理 (281行)
│ │ ├── proxy.go # 通用代理请求处理
│ │ ├── helper.go # 辅助函数(配额、计费等)
│ │ └── error.go # 统一错误处理
│ ├── meta/
│ │ └── relay_meta.go # 请求元数据结构 (66行)
│ ├── model/ # 内部数据模型
│ │ ├── general.go # 统一 OpenAI 请求结构 (88行)
│ │ ├── message.go # 消息结构(支持多模态) (91行)
│ │ ├── misc.go # Usage、Error 等通用结构 (27行)
│ │ ├── tool.go # Tool/Function Calling 结构
│ │ └── image.go # 图片请求结构
│ ├── apitype/
│ │ └── define.go # API 类型枚举 (19种)
│ ├── channeltype/
│ │ ├── define.go # 渠道类型枚举 (56种)
│ │ ├── helper.go # ChannelType → APIType 映射
│ │ └── url.go # 各渠道默认 BaseURL (52条)
│ ├── relaymode/
│ │ ├── define.go # 中转模式枚举 (11种)
│ │ └── helper.go # URL 路径 → 中转模式映射
│ └── billing/ # 计费相关
└── model/ # 数据库模型
├── channel.go # 渠道配置
└── ...
```
---
## 三、转换层技术架构
### 3.1 整体架构图
```
┌─────────────────────────────────────────┐
│ 用户请求 (OpenAI 格式) │
│ POST /v1/chat/completions │
└──────────────────┬──────────────────────┘
┌──────────────────▼──────────────────────┐
│ router/relay.go │
│ 路由: /v1/* → controller.Relay │
└──────────────────┬──────────────────────┘
┌──────────────────▼──────────────────────┐
│ middleware (中间件链) │
│ CORS → GzipDecode → PanicRecover │
│ → TokenAuth → Distribute │
│ 1. 解析 Token, 验证身份 │
│ 2. 根据模型名选择渠道(随机负载均衡) │
│ 3. 将渠道信息注入 gin.Context │
└──────────────────┬──────────────────────┘
┌──────────────────▼──────────────────────┐
│ controller/relay.go │
│ 1. 根据 URL 判断 relayMode │
│ 2. 调用 relayHelper 分发 │
│ 3. 失败时自动重试(切换渠道) │
└──────────────────┬──────────────────────┘
┌────────────────────────┼────────────────────────┐
│ │ │
┌─────────▼──────────┐ ┌─────────▼──────────┐ ┌─────────▼──────────┐
│ RelayTextHelper │ │ RelayImageHelper │ │ RelayAudioHelper │
│ text.go (115行) │ │ image.go (238行) │ │ audio.go (281行) │
└─────────┬──────────┘ └─────────┬──────────┘ └─────────┬──────────┘
│ │ │
└────────────────────────┼────────────────────────┘
┌──────────────────▼──────────────────────┐
│ relay/adaptor.go │
│ GetAdaptor(apiType) → 具体Adaptor实例 │
└──────────────────┬──────────────────────┘
┌────────────────────────┼────────────────────────┐
│ │ │
┌─────────▼──────────┐ ┌─────────▼──────────┐ ┌─────────▼──────────┐
│ openai.Adaptor │ │ anthropic.Adaptor │ │ gemini.Adaptor │
│ ConvertRequest() │ │ ConvertRequest() │ │ ConvertRequest() │
│ DoRequest() │ │ DoRequest() │ │ DoRequest() │
│ DoResponse() │ │ DoResponse() │ │ DoResponse() │
└─────────┬──────────┘ └─────────┬──────────┘ └─────────┬──────────┘
│ │ │
└────────────────────────┼────────────────────────┘
┌──────────────────▼──────────────────────┐
│ 上游大模型厂商 API │
└─────────────────────────────────────────┘
```
### 3.2 核心设计模式:适配器模式
**Adaptor 接口** (`relay/adaptor/interface.go`, 21 行):
```go
type Adaptor interface {
Init(meta *meta.Meta) // 初始化元数据
GetRequestURL(meta *meta.Meta) (string, error) // 构建请求 URL
SetupRequestHeader(c, req, meta) error // 设置认证请求头
ConvertRequest(c, mode, request) (any, error) // 请求体转换 (OpenAI → 厂商)
ConvertImageRequest(request) (any, error) // 图片请求转换
DoRequest(c, meta, requestBody) (*http.Response, error) // 发送 HTTP 请求
DoResponse(c, resp, meta) (usage, err) // 响应体转换 (厂商 → OpenAI)
GetModelList() []string // 返回支持的模型列表
GetChannelName() string // 返回渠道名称
}
```
**9 个方法**构成最精简的适配器接口,相比 New-API 的 15+ 方法接口更聚焦。
### 3.3 三层映射关系
项目通过三层映射将"渠道"与"转换逻辑"关联:
```
ChannelType (渠道类型) → APIType (API 类型) → Adaptor (适配器实例)
(56 种) (19 种) (18 个实现)
```
- **ChannelType**: 具体的厂商/服务商标识56 种)
- **APIType**: 共享同一套转换逻辑的分组19 种,定义在 `relay/apitype/define.go`
- **Adaptor**: 负责具体转换逻辑的实例
**映射代码**: `relay/channeltype/helper.go``ToAPIType()` 函数。
**关键设计**: 18 种 OpenAI 兼容渠道的 APIType 被映射为 `apitype.OpenAI`,直接复用 `openai.Adaptor`,无需编写独立转换逻辑。兼容渠道列表 (`relay/adaptor/openai/compatible.go`):
```go
var CompatibleChannels = []int{
Azure, AI360, Moonshot, Baichuan, Minimax, Doubao,
Mistral, Groq, LingYiWanWu, StepFun, DeepSeek, TogetherAI,
Novita, SiliconFlow, XAI, BaiduV2, XunfeiV2,
}
```
另有 OpenRouter、AliBailian、GeminiOpenAICompatible 也有特殊映射处理。
### 3.4 Adaptor 工厂
**文件**: `relay/adaptor.go` (69 行)
`GetAdaptor(apiType int)` 映射 18 种 API 类型到具体适配器实例Proxy 类型额外处理)。
---
## 四、请求处理完整流程
### 4.1 路由阶段
**文件**: `router/relay.go` (74 行)
所有 `/v1/` 下的请求都经过中间件链:`CORS() → GzipDecodeMiddleware() → RelayPanicRecover → TokenAuth → Distribute`
### 4.2 中间件阶段
#### Token 认证 (middleware/auth.go, 167 行)
-`Authorization` 头提取 `Bearer <token>`
- 管理员可通过 `sk-<token>-<channelId>` 指定特定渠道
- 验证 Token 有效性,提取 userId、tokenId
- 从请求体解析 `model` 字段,验证模型可用性
- 支持 IP 子网限制
#### 渠道分发 (middleware/distributor.go, 102 行)
- 获取用户分组 (group)
- 根据 group + model 随机选择一个可用渠道
- 将渠道信息注入 `gin.Context`渠道类型、API Key、BaseURL、ModelMapping 等
- 渠道类型转换为 APIType存入 meta
### 4.3 控制器阶段
**文件**: `controller/relay.go` (156 行)
```go
func Relay(c *gin.Context) {
relayMode := relaymode.GetByPath(c.Request.URL.Path)
bizErr := relayHelper(c, relayMode)
// 失败时自动重试:切换到其他渠道
for i := retryTimes; i > 0; i-- {
channel := CacheGetRandomSatisfiedChannel(group, model)
SetupContextForSelectedChannel(c, channel, model)
bizErr = relayHelper(c, relayMode)
}
}
```
`relayHelper` 根据 `relayMode` 分发:
- `ChatCompletions / Completions / Embeddings / Moderations``RelayTextHelper`
- `ImagesGenerations``RelayImageHelper`
- `AudioSpeech / AudioTranscription / AudioTranslation``RelayAudioHelper`
- `Proxy``RelayProxyHelper`
**重试策略**: 429 和 5xx 触发重试400 和 2xx 不重试。管理员指定渠道时不重试。
### 4.4 转换处理阶段 — RelayTextHelper
**文件**: `relay/controller/text.go` (115 行)
```go
func RelayTextHelper(c *gin.Context) *model.ErrorWithStatusCode {
// 1. 解析请求(统一按 OpenAI 格式解析)
meta := meta.GetByContext(c)
textRequest := getAndValidateTextRequest(c, meta.Mode)
// 2. 模型名称映射
textRequest.Model = getMappedModelName(textRequest.Model, meta.ModelMapping)
// 3. 强制系统提示词注入
setSystemPrompt(textRequest, meta)
// 4. 计算配额、预扣费
promptTokens := getPromptTokens(textRequest, meta.Mode)
preConsumedQuota := preConsumeQuota(...) // 用户配额 > 100x预扣时跳过
// 5. 获取对应 Adaptor
adaptor := relay.GetAdaptor(meta.APIType)
adaptor.Init(meta)
// 6. 请求转换OpenAI 格式 → 厂商格式)
requestBody := getRequestBody(c, meta, textRequest, adaptor)
// 7. 发送请求到上游
resp := adaptor.DoRequest(c, meta, requestBody)
// 8. 响应转换(厂商格式 → OpenAI 格式)
usage := adaptor.DoResponse(c, resp, meta)
// 9. 后扣费
postConsumeQuota(usage, ...)
}
```
#### 请求体透传优化
**文件**: `relay/controller/text.go``getRequestBody()` 函数
当满足以下所有条件时,**跳过请求转换**,直接透传原始请求体:
- API 类型为 OpenAI
- 模型名未被映射
- 渠道不是百川(百川有特殊处理)
- 无强制系统提示词
- 未启用 include_usage 强制选项
```go
if meta.APIType == apitype.OpenAI &&
meta.OriginModelName == meta.ActualModelName &&
meta.ChannelType != channeltype.Baichuan &&
meta.ForcedSystemPrompt == "" {
return c.Request.Body, nil // 直接透传,零开销
}
```
---
## 五、各厂商转换逻辑详解
### 5.1 OpenAI 适配器(基座)
**文件**: `relay/adaptor/openai/` (9 文件)
作为项目的"基座适配器",不仅处理 OpenAI 自身,还被 18 种 OpenAI 兼容渠道复用。
**请求转换**: 基本不需要格式转换,直接透传 `GeneralOpenAIRequest`。唯一处理:流式模式下自动注入 `stream_options.include_usage = true`
**URL 构建**: 根据渠道类型有不同逻辑:
- 标准 OpenAI 兼容: `BaseURL + RequestURLPath`
- Azure: `/openai/deployments/{model}/{task}?api-version=xxx`
- Minimax/Doubao/Novita/BaiduV2/AliBailian/GeminiOpenAICompatible: 各有独立 URL 逻辑
**认证方式**:
- 标准渠道: `Authorization: Bearer <api_key>`
- Azure: `api-key: <api_key>`
- OpenRouter: 额外 `HTTP-Referer``X-Title`
**流式处理**: 逐行解析 SSE (`data: {...}`),提取 usage 信息,支持 Cloudflare AI Gateway URL。
### 5.2 Anthropic Claude 适配器
**文件**: `relay/adaptor/anthropic/` (4 文件, main.go 379 行)
Claude 使用完全不同的 API 格式,需要大量转换工作。
**请求转换** (main.go `ConvertRequest()`):
| OpenAI 字段 | Claude 字段 | 转换说明 |
|---|---|---|
| `messages[].role = "system"` | `system` (顶层字段) | 系统提示词提取到顶层 |
| `messages[].content` (string) | `messages[].content` (Content 数组) | 包装为 `[{type:"text", text:"..."}]` |
| `messages[].role = "tool"` | `messages[].role = "user"` + `type = "tool_result"` | 工具结果转为用户消息 |
| `messages[].tool_calls` | `content.type = "tool_use"` | 工具调用转为 content 块 |
| `tools[].function.parameters` | `tools[].input_schema` | 字段名转换 |
| `tool_choice` | `tool_choice.type + name` | auto/tool/any 格式映射 |
| `max_tokens` | `max_tokens` | 直接映射,默认 4096 |
**认证**: `x-api-key: <api_key>` + `anthropic-version: 2023-06-01`。Claude 3.5 Sonnet 额外添加 `anthropic-beta: max-tokens-3-5-sonnet-2024-07-15`
**URL**: `{base_url}/v1/messages`
**响应转换**:
| Claude 字段 | OpenAI 字段 |
|---|---|
| `content[].text` | `choices[].message.content` |
| `content[].type = "tool_use"` | `choices[].message.tool_calls[]` |
| `stop_reason: "end_turn"/"stop_sequence"` | `finish_reason: "stop"` |
| `stop_reason: "max_tokens"` | `finish_reason: "length"` |
| `stop_reason: "tool_use"` | `finish_reason: "tool_calls"` |
| `usage.input_tokens/output_tokens` | `usage.prompt_tokens/completion_tokens` |
**流式处理**: Claude 的 SSE 事件类型(`message_start``content_block_start``content_block_delta``message_delta`)逐事件转换为 OpenAI 的 `chat.completion.chunk` 格式。
### 5.3 Google Gemini 适配器
**文件**: `relay/adaptor/gemini/` (4 文件, main.go 437 行)
**请求转换**:
| OpenAI 字段 | Gemini 字段 | 转换说明 |
|---|---|---|
| `messages[]` | `contents[]` | 数组结构转换 |
| `role = "assistant"` | `role = "model"` | 角色名映射 |
| `role = "system"` | `systemInstruction``role = "user"` | 新模型用 systemInstruction旧模型转 user |
| `tools[].function` | `tools[].functionDeclarations[]` | 字段名转换 |
| `temperature/top_p/max_tokens` | `generationConfig.{temperature, topP, maxOutputTokens}` | 包装到 generationConfig |
| `response_format.type = "json_object"` | `responseMimeType = "application/json"` | MIME 类型映射 |
**特殊处理**:
- system 消息后自动插入 `model: "Okay"` 虚拟消息Gemini API 要求角色交替)
- 安全设置设为最低 (`GeminiSafetySetting`5 个类别)
- 图片数量限制最多 16 张,转为 base64 inlineData
- 支持 system instruction 的模型列表硬编码: `gemini-2.0-flash`, `gemini-2.0-flash-exp`, `gemini-2.0-flash-thinking-exp-01-21`
**URL**: `{base_url}/{version}/models/{model}:{action}`
- 版本选择: `gemini-2.0``gemini-1.5``v1beta`;其他用 `config.GeminiVersion`
- 非流式: `generateContent`
- 流式: `streamGenerateContent?alt=sse`
- Embedding: `batchEmbedContents`
**认证**: `x-goog-api-key: <api_key>`
### 5.4 阿里通义千问适配器
**文件**: `relay/adaptor/ali/` (5 文件, main.go 267 行)
使用阿里 DashScope API 格式。
**请求转换**:
- Messages 包装到 `Input.Messages`,参数包装到 `Parameters`(含 `ResultFormat: "message"`, `IncrementalOutput`, `EnableSearch`
- 模型名后缀 `-internet` 启用联网搜索 (`EnableSearch: true`)
- `top_p` 上限为 0.9999
- 图片生成使用异步模式 (`X-DashScope-Async: enable`),轮询等待(最多 20 步2 秒间隔)
**URL**:
- 文本: `/api/v1/services/aigc/text-generation/generation`
- Embedding: `/api/v1/services/embeddings/text-embedding/text-embedding`
- 图片: `/api/v1/services/aigc/text2image/image-synthesis`
**特殊头**: `X-DashScope-SSE: enable` (流式), `X-DashScope-Async: enable` (异步)
### 5.5 百度文心一言适配器
**文件**: `relay/adaptor/baidu/` (4 文件, main.go 312 行)
**认证方式**: 百度不使用 API Key 直接认证,而是先通过 `client_id|client_secret` 获取 `access_token`,然后在 URL 中传递。
**access_token 缓存机制**:
- `baiduTokenStore` 使用 `sync.Map` 存储
- 缓存的 token 在过期前 1 小时触发异步刷新
- API Key 格式: `client_id|client_secret`,通过 OAuth2 端点获取 token
**请求转换**: 系统消息提取到 `System` 字段,`FrequencyPenalty``PenaltyScore`
**URL**: `{base_url}/rpc/2.0/ai_custom/v1/wenxinworkshop/{suffix}?access_token={token}`
- URL 中包含模型端点名称(如 `chat/completions_pro`),通过大量 switch-case 映射模型名到端点
### 5.6 智谱 ChatGLM 适配器
**文件**: `relay/adaptor/zhipu/` (4 文件)
**双版本支持**:
- **v4** (`glm-*` 模型): `/api/paas/v4/chat/completions`**OpenAI 兼容**,直接复用 OpenAI 的 StreamHandler/Handler
- **v3**: `/api/paas/v3/model-api/{model}/{method}` — 自有格式
**认证**: JWT tokenAPI Key 格式 `id.secret`HMAC-SHA256 签名)
### 5.7 腾讯混元适配器
**文件**: `relay/adaptor/tencent/` (4 文件, main.go 307 行)
- URL: `{baseURL}/` (单一端点)
- 认证: **TC3-HMAC-SHA256** 签名算法(完整实现在 main.go 中)
- API Key 格式: `appId|secretId|secretKey`
### 5.8 讯飞星火适配器
**文件**: `relay/adaptor/xunfei/` (5 文件, main.go 273 行)
- **协议**: WebSocket唯一非 HTTP 适配器)
- `GetRequestURL()` 返回空字符串,`DoRequest()` 返回 dummy 200
- `DoResponse()` 处理实际 WebSocket 通信
- 认证: HMAC-SHA256 签名的 WebSocket URL
- API Key 格式: `appId|apiSecret|apiKey` (pipe 分隔三部分)
- 流式: 使用 `c.Stream()` + SSE 格式
- 非流式: 累积所有 WebSocket chunk 后返回单次响应
### 5.9 AWS Bedrock Claude 适配器
**文件**: `relay/adaptor/aws/` (5 文件)
- 使用 **AWS SDK v2** `bedrockruntime.Client`,不走 HTTP
- **子适配器模式**: `registry.go` 根据模型名分派到 `aws/claude/``aws/llama3/`
- `aws/claude/`: 复用 `anthropic.ConvertRequest()` 转换请求,使用 Bedrock `InvokeModel`/`InvokeModelWithResponseStream` 通信
- `aws/llama3/`: Llama3 专用转换
### 5.10 Google Vertex AI 适配器
**文件**: `relay/adaptor/vertexai/` (5 文件)
- URL: `{baseURL}/v1/projects/{project}/locations/{region}/publishers/google/models/{model}:{action}`
- Claude: `rawPredict`/`streamRawPredict?alt=sse`
- Gemini: `generateContent`/`streamGenerateContent?alt=sse`
- **子适配器模式**: `registry.go` 分派到 `vertexai/claude/``vertexai/gemini/`
- `vertexai/claude/`: 复用 `anthropic.ConvertRequest()`,设置 `anthropic_version: "vertex-2023-10-16"`
- `vertexai/gemini/`: 复用 `gemini.ConvertRequest()`
- 认证: Google Cloud IAM `GenerateAccessToken` (ADC)Token 缓存 50 分钟 TTL
### 5.11 其他适配器简述
| 适配器 | 文件 | 关键特征 |
|-------|------|---------|
| **Ollama** | `adaptor/ollama/` | 类 OpenAI 格式,`/api/chat``/api/generate` |
| **Coze** | `adaptor/coze/` | 字节跳动 Coze 平台 |
| **Cohere** | `adaptor/cohere/` | Cohere 自有 API 格式 |
| **Cloudflare** | `adaptor/cloudflare/` | Workers AIURL 支持 Cloudflare AI Gateway |
| **DeepL** | `adaptor/deepl/` | 翻译 API非 LLM |
| **Replicate** | `adaptor/replicate/` | 异步任务模式 |
| **PaLM** | `adaptor/palm/` | Google PaLM (旧版) |
| **Proxy** | `adaptor/proxy/` | 通用透传代理,无转换 |
---
## 六、统一数据模型
### 6.1 统一请求模型 (`relay/model/general.go`, 88 行)
```go
type GeneralOpenAIRequest struct {
Messages []Message // 聊天消息
Model string // 模型名
MaxTokens int // 最大生成 token
MaxCompletionTokens int // 最大完成 token (o-series)
Temperature *float64 // 温度
TopP *float64 // Top-P
TopK int // Top-K
Stream bool // 流式
StreamOptions *StreamOptions // 流式选项
Tools []Tool // 工具调用
ToolChoice any // 工具选择
Stop any // 停止词
ResponseFormat *ResponseFormat // 响应格式
ReasoningEffort string // 推理强度
Store *bool // 存储标记
// ... 更多字段
}
```
这是整个项目的"通用语言"——所有厂商的请求都先解析为此格式,再由各 Adaptor 转换为厂商格式。
### 6.2 统一消息模型 (`relay/model/message.go`, 91 行)
```go
type Message struct {
Role string // system / user / assistant / tool / developer
Content any // 字符串 或 多模态内容数组
ReasoningContent any // 推理内容 (o1 等模型)
Name *string // 消息发送者名称
ToolCalls []Tool // 工具调用结果
ToolCallId string // 工具调用 ID
}
```
`Content` 支持字符串纯文本和数组多模态text + image_url通过 `ParseContent()` 解析。
### 6.3 统一 Usage 模型
```go
type Usage struct {
PromptTokens int
CompletionTokens int
TotalTokens int
}
```
### 6.4 统一错误模型
所有上游错误统一转换为 OpenAI 错误格式:
```json
{"error": {"message": "...", "type": "...", "param": "...", "code": "..."}}
```
`GeneralErrorResponse` 兼容多种厂商的错误格式OpenAI 的 `error.message`、通用的 `message`、百度的 `error_msg` 等)。
---
## 七、中转模式 (RelayMode)
**文件**: `relay/relaymode/` (2 文件)
11 种中转模式,根据 URL 路径自动判断:
| 模式 | URL 路径 | 说明 |
|------|---------|------|
| ChatCompletions | `/v1/chat/completions` | Chat 聊天补全 |
| Completions | `/v1/completions` | 文本补全 |
| Embeddings | `/v1/embeddings`, `/v1/engines/:model/embeddings` | 文本嵌入 |
| Moderations | `/v1/moderations` | 内容审核 |
| ImagesGenerations | `/v1/images/generations` | 图片生成 |
| Edits | `/v1/edits` | 文本编辑 |
| AudioSpeech | `/v1/audio/speech` | 语音合成 |
| AudioTranscription | `/v1/audio/transcriptions` | 语音识别 |
| AudioTranslation | `/v1/audio/translations` | 语音翻译 |
| Proxy | `/v1/oneapi/proxy/:channelid/*target` | 通用代理 |
判断逻辑通过 `strings.HasPrefix` 匹配。
---
## 八、关键业务逻辑
### 8.1 配额管理
"预扣费 + 后结算"机制:
1. **预扣费**: 根据 prompt tokens 和模型倍率预先扣除估算配额
2. **后结算**: 根据 usage 的实际 token 数计算费用并调整
3. 信任优化: 用户配额超过预扣费 100 倍时跳过预扣
### 8.2 自动重试
请求失败时 (429/5xx)
1. 选择另一个不同的渠道(随机负载均衡)
2. 重新设置上下文
3. 从缓存的原始 body 重放请求
4. 最多重试 `RetryTimes`
5. 429 返回特殊消息"当前分组上游负载已饱和,请稍后再试"
### 8.3 错误监控
`processChannelRelayError()` 异步记录错误,并通过 `monitor.ShouldDisableChannel()` 自动禁用频繁出错的渠道。
---
## 九、流式 (SSE) 处理架构
### 9.1 统一处理流程
```
上游 SSE 流 → Scanner 逐行扫描 → 解析厂商 JSON → 转换为 OpenAI 格式 → SSE 写回客户端
```
### 9.2 各厂商 SSE 格式对比
| 厂商 | SSE 前缀 | 数据格式 | 结束标记 |
|------|---------|---------|---------|
| OpenAI | `data: ` | `ChatCompletionsStreamResponse` | `data: [DONE]` |
| Anthropic | `data:` | `StreamResponse` (type 字段区分事件) | 无显式标记 |
| Gemini | `data: ` | `ChatResponse` | 无显式标记 |
| 阿里 | `data:` | `ChatResponse` (`\n` 分隔) | 无显式标记 |
| 百度 | `data: ` | `ChatStreamResponse` | `is_end = true` |
### 9.3 OpenAI 兼容渠道的 SSE 优化
OpenAI 兼容渠道的流式数据直接透传给客户端,仅需解析统计 usage 信息,不需要格式转换。
---
## 十、架构总结
### 10.1 核心设计原则
1. **统一入口,适配器分发**: 所有请求通过同一入口,由工厂模式创建对应适配器
2. **OpenAI 格式为"通用语言"**: 内部所有流转使用 OpenAI 格式,仅在"进出"时转换
3. **接口抽象**: 通过 `Adaptor` 接口 (9 方法) 隔离各厂商差异,新增厂商只需实现接口
4. **零拷贝优化**: OpenAI 兼容渠道无特殊处理时直接透传请求体
5. **关注点分离**: 认证、分发、转换、计费、重试各自独立
### 10.2 新增厂商适配步骤
1.`relay/channeltype/define.go` 添加 `ChannelType` 常量
2.`relay/apitype/define.go` 添加 `APIType`(或复用已有)
3.`relay/channeltype/helper.go` 添加 `ToAPIType()` 映射
4.`relay/channeltype/url.go` 添加默认 BaseURL
5. 创建 `relay/adaptor/<vendor>/` 目录实现 `Adaptor` 接口
6. 如果复用 OpenAI 格式,只需在 `compatible.go``CompatibleChannels` 列表中添加
### 10.3 架构特点
**优点**:
- 接口精简9 方法),扩展性强
- 18 种 OpenAI 兼容渠道零开销透传
- 自动重试 + 渠道切换 + 错误监控自动禁用
- 统一的计费和错误处理
- 子适配器模式支持 Bedrock/Vertex AI 的多云厂商模型
**局限**:
- 仅支持 OpenAI 格式输入/输出New-API 已在此基础上扩展了 Claude/Gemini 输入)
- 音频处理未完全走 Adaptor 接口
- 部分适配器 SSE 解析逻辑有重复模式,可抽取公共基类
- 缺少请求/响应中间件钩子的统一注入点

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,291 @@
## List
**get** `/v1/models`
List available models.
The Models API response can be used to determine which models are available for use in the API. More recently released models are listed first.
### Query Parameters
- `after_id: optional string`
ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately after this object.
- `before_id: optional string`
ID of the object to use as a cursor for pagination. When provided, returns the page of results immediately before this object.
- `limit: optional number`
Number of items to return per page.
Defaults to `20`. Ranges from `1` to `1000`.
### Header Parameters
- `"anthropic-beta": optional array of AnthropicBeta`
Optional header to specify the beta version(s) you want to use.
- `UnionMember0 = string`
- `UnionMember1 = "message-batches-2024-09-24" or "prompt-caching-2024-07-31" or "computer-use-2024-10-22" or 20 more`
- `"message-batches-2024-09-24"`
- `"prompt-caching-2024-07-31"`
- `"computer-use-2024-10-22"`
- `"computer-use-2025-01-24"`
- `"pdfs-2024-09-25"`
- `"token-counting-2024-11-01"`
- `"token-efficient-tools-2025-02-19"`
- `"output-128k-2025-02-19"`
- `"files-api-2025-04-14"`
- `"mcp-client-2025-04-04"`
- `"mcp-client-2025-11-20"`
- `"dev-full-thinking-2025-05-14"`
- `"interleaved-thinking-2025-05-14"`
- `"code-execution-2025-05-22"`
- `"extended-cache-ttl-2025-04-11"`
- `"context-1m-2025-08-07"`
- `"context-management-2025-06-27"`
- `"model-context-window-exceeded-2025-08-26"`
- `"skills-2025-10-02"`
- `"fast-mode-2026-02-01"`
- `"output-300k-2026-03-24"`
- `"advisor-tool-2026-03-01"`
- `"user-profiles-2026-03-24"`
### Returns
- `data: array of ModelInfo`
- `id: string`
Unique model identifier.
- `capabilities: ModelCapabilities`
Model capability information.
- `batch: CapabilitySupport`
Whether the model supports the Batch API.
- `supported: boolean`
Whether this capability is supported by the model.
- `citations: CapabilitySupport`
Whether the model supports citation generation.
- `supported: boolean`
Whether this capability is supported by the model.
- `code_execution: CapabilitySupport`
Whether the model supports code execution tools.
- `supported: boolean`
Whether this capability is supported by the model.
- `context_management: ContextManagementCapability`
Context management support and available strategies.
- `clear_thinking_20251015: CapabilitySupport`
Indicates whether a capability is supported.
- `supported: boolean`
Whether this capability is supported by the model.
- `clear_tool_uses_20250919: CapabilitySupport`
Indicates whether a capability is supported.
- `supported: boolean`
Whether this capability is supported by the model.
- `compact_20260112: CapabilitySupport`
Indicates whether a capability is supported.
- `supported: boolean`
Whether this capability is supported by the model.
- `supported: boolean`
Whether this capability is supported by the model.
- `effort: EffortCapability`
Effort (reasoning_effort) support and available levels.
- `high: CapabilitySupport`
Whether the model supports high effort level.
- `supported: boolean`
Whether this capability is supported by the model.
- `low: CapabilitySupport`
Whether the model supports low effort level.
- `supported: boolean`
Whether this capability is supported by the model.
- `max: CapabilitySupport`
Whether the model supports max effort level.
- `supported: boolean`
Whether this capability is supported by the model.
- `medium: CapabilitySupport`
Whether the model supports medium effort level.
- `supported: boolean`
Whether this capability is supported by the model.
- `supported: boolean`
Whether this capability is supported by the model.
- `xhigh: CapabilitySupport`
Indicates whether a capability is supported.
- `supported: boolean`
Whether this capability is supported by the model.
- `image_input: CapabilitySupport`
Whether the model accepts image content blocks.
- `supported: boolean`
Whether this capability is supported by the model.
- `pdf_input: CapabilitySupport`
Whether the model accepts PDF content blocks.
- `supported: boolean`
Whether this capability is supported by the model.
- `structured_outputs: CapabilitySupport`
Whether the model supports structured output / JSON mode / strict tool schemas.
- `supported: boolean`
Whether this capability is supported by the model.
- `thinking: ThinkingCapability`
Thinking capability and supported type configurations.
- `supported: boolean`
Whether this capability is supported by the model.
- `types: ThinkingTypes`
Supported thinking type configurations.
- `adaptive: CapabilitySupport`
Whether the model supports thinking with type 'adaptive' (auto).
- `supported: boolean`
Whether this capability is supported by the model.
- `enabled: CapabilitySupport`
Whether the model supports thinking with type 'enabled'.
- `supported: boolean`
Whether this capability is supported by the model.
- `created_at: string`
RFC 3339 datetime string representing the time at which the model was released. May be set to an epoch value if the release date is unknown.
- `display_name: string`
A human-readable name for the model.
- `max_input_tokens: number`
Maximum input context window size in tokens for this model.
- `max_tokens: number`
Maximum value for the `max_tokens` parameter when using this model.
- `type: "model"`
Object type.
For Models, this is always `"model"`.
- `"model"`
- `first_id: string`
First ID in the `data` list. Can be used as the `before_id` for the previous page.
- `has_more: boolean`
Indicates if there are more results in the requested page direction.
- `last_id: string`
Last ID in the `data` list. Can be used as the `after_id` for the next page.
### Example
```http
curl https://api.anthropic.com/v1/models \
-H 'anthropic-version: 2023-06-01' \
-H "X-Api-Key: $ANTHROPIC_API_KEY"
```

View File

@@ -0,0 +1,269 @@
## Retrieve
**get** `/v1/models/{model_id}`
Get a specific model.
The Models API response can be used to determine information about a specific model or resolve a model alias to a model ID.
### Path Parameters
- `model_id: string`
Model identifier or alias.
### Header Parameters
- `"anthropic-beta": optional array of AnthropicBeta`
Optional header to specify the beta version(s) you want to use.
- `UnionMember0 = string`
- `UnionMember1 = "message-batches-2024-09-24" or "prompt-caching-2024-07-31" or "computer-use-2024-10-22" or 20 more`
- `"message-batches-2024-09-24"`
- `"prompt-caching-2024-07-31"`
- `"computer-use-2024-10-22"`
- `"computer-use-2025-01-24"`
- `"pdfs-2024-09-25"`
- `"token-counting-2024-11-01"`
- `"token-efficient-tools-2025-02-19"`
- `"output-128k-2025-02-19"`
- `"files-api-2025-04-14"`
- `"mcp-client-2025-04-04"`
- `"mcp-client-2025-11-20"`
- `"dev-full-thinking-2025-05-14"`
- `"interleaved-thinking-2025-05-14"`
- `"code-execution-2025-05-22"`
- `"extended-cache-ttl-2025-04-11"`
- `"context-1m-2025-08-07"`
- `"context-management-2025-06-27"`
- `"model-context-window-exceeded-2025-08-26"`
- `"skills-2025-10-02"`
- `"fast-mode-2026-02-01"`
- `"output-300k-2026-03-24"`
- `"advisor-tool-2026-03-01"`
- `"user-profiles-2026-03-24"`
### Returns
- `ModelInfo = object { id, capabilities, created_at, 4 more }`
- `id: string`
Unique model identifier.
- `capabilities: ModelCapabilities`
Model capability information.
- `batch: CapabilitySupport`
Whether the model supports the Batch API.
- `supported: boolean`
Whether this capability is supported by the model.
- `citations: CapabilitySupport`
Whether the model supports citation generation.
- `supported: boolean`
Whether this capability is supported by the model.
- `code_execution: CapabilitySupport`
Whether the model supports code execution tools.
- `supported: boolean`
Whether this capability is supported by the model.
- `context_management: ContextManagementCapability`
Context management support and available strategies.
- `clear_thinking_20251015: CapabilitySupport`
Indicates whether a capability is supported.
- `supported: boolean`
Whether this capability is supported by the model.
- `clear_tool_uses_20250919: CapabilitySupport`
Indicates whether a capability is supported.
- `supported: boolean`
Whether this capability is supported by the model.
- `compact_20260112: CapabilitySupport`
Indicates whether a capability is supported.
- `supported: boolean`
Whether this capability is supported by the model.
- `supported: boolean`
Whether this capability is supported by the model.
- `effort: EffortCapability`
Effort (reasoning_effort) support and available levels.
- `high: CapabilitySupport`
Whether the model supports high effort level.
- `supported: boolean`
Whether this capability is supported by the model.
- `low: CapabilitySupport`
Whether the model supports low effort level.
- `supported: boolean`
Whether this capability is supported by the model.
- `max: CapabilitySupport`
Whether the model supports max effort level.
- `supported: boolean`
Whether this capability is supported by the model.
- `medium: CapabilitySupport`
Whether the model supports medium effort level.
- `supported: boolean`
Whether this capability is supported by the model.
- `supported: boolean`
Whether this capability is supported by the model.
- `xhigh: CapabilitySupport`
Indicates whether a capability is supported.
- `supported: boolean`
Whether this capability is supported by the model.
- `image_input: CapabilitySupport`
Whether the model accepts image content blocks.
- `supported: boolean`
Whether this capability is supported by the model.
- `pdf_input: CapabilitySupport`
Whether the model accepts PDF content blocks.
- `supported: boolean`
Whether this capability is supported by the model.
- `structured_outputs: CapabilitySupport`
Whether the model supports structured output / JSON mode / strict tool schemas.
- `supported: boolean`
Whether this capability is supported by the model.
- `thinking: ThinkingCapability`
Thinking capability and supported type configurations.
- `supported: boolean`
Whether this capability is supported by the model.
- `types: ThinkingTypes`
Supported thinking type configurations.
- `adaptive: CapabilitySupport`
Whether the model supports thinking with type 'adaptive' (auto).
- `supported: boolean`
Whether this capability is supported by the model.
- `enabled: CapabilitySupport`
Whether the model supports thinking with type 'enabled'.
- `supported: boolean`
Whether this capability is supported by the model.
- `created_at: string`
RFC 3339 datetime string representing the time at which the model was released. May be set to an epoch value if the release date is unknown.
- `display_name: string`
A human-readable name for the model.
- `max_input_tokens: number`
Maximum input context window size in tokens for this model.
- `max_tokens: number`
Maximum value for the `max_tokens` parameter when using this model.
- `type: "model"`
Object type.
For Models, this is always `"model"`.
- `"model"`
### Example
```http
curl https://api.anthropic.com/v1/models/$MODEL_ID \
-H 'anthropic-version: 2023-06-01' \
-H "X-Api-Key: $ANTHROPIC_API_KEY"
```

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,89 @@
## List models
**get** `/models`
Lists the currently available models, and provides basic information about each one such as the owner and availability.
### Returns
- `data: array of Model`
- `id: string`
The model identifier, which can be referenced in the API endpoints.
- `created: number`
The Unix timestamp (in seconds) when the model was created.
- `object: "model"`
The object type, which is always "model".
- `"model"`
- `owned_by: string`
The organization that owns the model.
- `object: "list"`
- `"list"`
### Example
```http
curl https://api.openai.com/v1/models \
-H "Authorization: Bearer $OPENAI_API_KEY"
```
#### Response
```json
{
"data": [
{
"id": "id",
"created": 0,
"object": "model",
"owned_by": "owned_by"
}
],
"object": "list"
}
```
### Example
```http
curl https://api.openai.com/v1/models \
-H "Authorization: Bearer $OPENAI_API_KEY"
```
#### Response
```json
{
"object": "list",
"data": [
{
"id": "model-id-0",
"object": "model",
"created": 1686935002,
"owned_by": "organization-owner"
},
{
"id": "model-id-1",
"object": "model",
"created": 1686935002,
"owned_by": "organization-owner",
},
{
"id": "model-id-2",
"object": "model",
"created": 1686935002,
"owned_by": "openai"
},
]
}
```

1707
docs/conversion_design.md Normal file

File diff suppressed because it is too large Load Diff