docs: 添加 API 参考文档和技术分析文档
This commit is contained in:
722
docs/analysis_reference/analysis_cc-switch.md
Normal file
722
docs/analysis_reference/analysis_cc-switch.md
Normal 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_result(Claude Code 偶尔产生) |
|
||||
| **tool_result 合并** | 将多个 tool_result 合并为一个,减少 Copilot 高级计费 |
|
||||
| **热身降级** | 检测到热身请求时降级为小模型,节省成本 |
|
||||
| **确定性请求 ID** | 基于 session 生成确定性 `x-request-id` 和 `x-interaction-id`,提高 Copilot 稳定性 |
|
||||
| **子代理头修改** | 子代理请求时修改 `x-initiator`、`x-interaction-type` 为 agent |
|
||||
|
||||
### 7.3 Thinking 自修复机制
|
||||
|
||||
**文件**: `src-tauri/src/proxy/thinking_rectifier.rs` (609 行), `thinking_budget_rectifier.rs` (307 行)
|
||||
|
||||
两个独立的自修复 rectifier,在 Anthropic API 返回特定错误时自动修复并重试:
|
||||
|
||||
| Rectifier | 触发条件 | 修复动作 | 触发次数限制 |
|
||||
|-----------|---------|---------|------------|
|
||||
| **Signature Rectifier** | Anthropic API 报告 thinking signature 无效 | 移除 `thinking`/`redacted_thinking` blocks 和 `signature` 字段 | 每请求 1 次 |
|
||||
| **Budget Rectifier** | Anthropic API 报告 thinking budget 错误 | 调整 `thinking.budget_tokens` | 每请求 1 次 |
|
||||
|
||||
修复后通过 `release_permit_neutral()` 释放熔断器的 HalfOpen 探测槽位,不影响健康统计。
|
||||
|
||||
### 7.4 其他辅助机制
|
||||
|
||||
| 模块 | 文件 | 行数 | 说明 |
|
||||
|------|------|------|------|
|
||||
| **私有参数过滤** | `body_filter.rs` | 261 | 过滤 `_` 前缀的参数,允许客户端传递控制参数而不发送到上游 |
|
||||
| **缓存注入** | `cache_injector.rs` | 329 | 为 Bedrock 等支持 prompt 缓存的供应商注入缓存控制标记 |
|
||||
| **Thinking 优化** | `thinking_optimizer.rs` | 230 | Bedrock 场景下的 thinking 参数优化 |
|
||||
| **模型名映射** | `model_mapper.rs` | 312 | 将客户端请求的模型名映射为上游实际模型名 |
|
||||
| **Gemini URL** | `gemini_url.rs` | 583 | 处理 Gemini API 的复杂 URL 构建逻辑 |
|
||||
| **SSE 基础** | `sse.rs` | 294 | SSE 事件解析和生成的基础工具 |
|
||||
|
||||
---
|
||||
|
||||
## 8. 熔断器与故障转移
|
||||
|
||||
### 8.1 熔断器
|
||||
|
||||
**文件**: `src-tauri/src/proxy/circuit_breaker.rs` (482 行)
|
||||
|
||||
三态熔断器保护上游服务:
|
||||
|
||||
| 参数 | 默认值 |
|
||||
|------|-------|
|
||||
| `failure_threshold` | 4 (连续失败次数) |
|
||||
| `success_threshold` | 2 (连续成功次数) |
|
||||
| `timeout_seconds` | 60 (Open 状态持续时间) |
|
||||
| `error_rate_threshold` | 0.6 (错误率阈值) |
|
||||
| `min_requests` | 10 (最小请求数) |
|
||||
|
||||
**状态转换**: Closed (正常) → Open (熔断) → HalfOpen (探测,最多 1 个并发探测请求) → Closed/Open
|
||||
|
||||
### 8.2 供应商路由
|
||||
|
||||
**文件**: `src-tauri/src/proxy/provider_router.rs` (512 行)
|
||||
|
||||
- 故障转移关闭时:仅使用当前供应商
|
||||
- 故障转移开启时:返回按优先级排列的故障转移队列,跳过熔断中的供应商
|
||||
|
||||
---
|
||||
|
||||
## 9. 支持的协议转换矩阵
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────┐
|
||||
│ CC-Switch 代理 │
|
||||
│ │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
Claude Code ──────▶│ │ Claude │ │ Codex │ │ Gemini │ │
|
||||
(Anthropic │ │ Handler │ │ Handler │ │ Handler │ │
|
||||
Messages API) │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
|
||||
│ │ │ │ │
|
||||
│ ┌────▼─────────────▼──────────────▼─────┐ │
|
||||
│ │ Forwarder (转发器) │ │
|
||||
│ │ - 供应商选择 / 故障转移 / Copilot优化 │ │
|
||||
│ │ - Thinking自修复 / 参数过滤 │ │
|
||||
│ └────────────────┬───────────────────────┘ │
|
||||
│ │ │
|
||||
│ ┌───────────┼───────────┐ │
|
||||
│ ▼ ▼ ▼ │
|
||||
│ ┌─────────┐ ┌─────────┐ ┌──────────┐ │
|
||||
│ │OpenAI │ │OpenAI │ │ Gemini │ │
|
||||
│ │Chat │ │Responses│ │ Native │ │
|
||||
│ │Complet. │ │API │ │ │ │
|
||||
│ └────┬────┘ └────┬────┘ └────┬─────┘ │
|
||||
│ │ │ │ │
|
||||
└───────┼───────────┼───────────┼─────────────┘
|
||||
▼ ▼ ▼
|
||||
┌───────────┐ ┌──────────┐ ┌──────────────┐
|
||||
│OpenAI/ │ │ChatGPT/ │ │ Google AI │
|
||||
│OpenRouter/│ │OpenAI/ │ │ Gemini API │
|
||||
│Copilot │ │Codex OAuth│ │ │
|
||||
└───────────┘ └──────────┘ └──────────────┘
|
||||
```
|
||||
|
||||
### 转换路径汇总
|
||||
|
||||
| 转换路径 | 请求转换 | 响应转换 | 流式转换 | 使用场景 |
|
||||
|---------|---------|---------|---------|---------|
|
||||
| Anthropic → OpenAI Chat | `anthropic_to_openai()` | `openai_to_anthropic()` | `streaming.rs` | GitHub Copilot, OpenRouter (旧版) |
|
||||
| Anthropic → OpenAI Responses | `anthropic_to_responses()` | `responses_to_anthropic()` | `streaming_responses.rs` | Codex OAuth (ChatGPT Plus/Pro) |
|
||||
| Anthropic → Gemini | `anthropic_to_gemini_with_shadow()` | `gemini_to_anthropic_with_shadow_and_hints()` | `streaming_gemini.rs` | Gemini 模型直连 |
|
||||
| Anthropic → Anthropic (透传) | 直接转发 | 直接转发 | 直接转发 | Anthropic 官方, OpenRouter (新版) |
|
||||
|
||||
---
|
||||
|
||||
## 10. 文件索引
|
||||
|
||||
### 核心转换文件
|
||||
|
||||
| 文件路径 | 功能 | 代码行数 |
|
||||
|---------|------|---------|
|
||||
| `src-tauri/src/proxy/providers/transform.rs` | Anthropic ↔ OpenAI Chat Completions 转换 | 1193 |
|
||||
| `src-tauri/src/proxy/providers/transform_responses.rs` | Anthropic ↔ OpenAI Responses API 转换 | 1329 |
|
||||
| `src-tauri/src/proxy/providers/transform_gemini.rs` | Anthropic ↔ Gemini Native 转换 | 2152 |
|
||||
| `src-tauri/src/proxy/providers/streaming.rs` | OpenAI Chat SSE → Anthropic SSE 流式转换 | 821 |
|
||||
| `src-tauri/src/proxy/providers/streaming_responses.rs` | Responses SSE → Anthropic SSE 流式转换 | 1070 |
|
||||
| `src-tauri/src/proxy/providers/streaming_gemini.rs` | Gemini SSE → Anthropic SSE 流式转换 | 1054 |
|
||||
|
||||
### 适配器与认证
|
||||
|
||||
| 文件路径 | 功能 | 代码行数 |
|
||||
|---------|------|---------|
|
||||
| `src-tauri/src/proxy/providers/adapter.rs` | ProviderAdapter trait 定义 | 50 |
|
||||
| `src-tauri/src/proxy/providers/auth.rs` | AuthInfo / AuthStrategy 认证类型定义 | 259 |
|
||||
| `src-tauri/src/proxy/providers/claude.rs` | Claude 适配器 (API 格式检测, URL 构建, 认证) | 1313 |
|
||||
| `src-tauri/src/proxy/providers/codex.rs` | Codex 适配器 | 305 |
|
||||
| `src-tauri/src/proxy/providers/gemini.rs` | Gemini 适配器 | 442 |
|
||||
| `src-tauri/src/proxy/providers/mod.rs` | ProviderType 枚举, get_adapter() 工厂函数 | 515 |
|
||||
| `src-tauri/src/proxy/providers/copilot_auth.rs` | Copilot 动态 Token 管理 | 1820 |
|
||||
| `src-tauri/src/proxy/providers/codex_oauth_auth.rs` | Codex OAuth 动态 Token 管理 | 1132 |
|
||||
| `src-tauri/src/proxy/providers/gemini_shadow.rs` | Gemini Shadow Store (状态回放) | 389 |
|
||||
| `src-tauri/src/proxy/providers/gemini_schema.rs` | Gemini Schema 构建 (双通道策略) | 338 |
|
||||
|
||||
### 请求处理链
|
||||
|
||||
| 文件路径 | 功能 | 代码行数 |
|
||||
|---------|------|---------|
|
||||
| `src-tauri/src/proxy/server.rs` | Axum HTTP 服务器, 路由注册 | 354 |
|
||||
| `src-tauri/src/proxy/handlers.rs` | 请求处理器 (分发 + Claude 格式转换入口) | 643 |
|
||||
| `src-tauri/src/proxy/forwarder.rs` | 请求转发器 (供应商选择, 故障转移, 请求预处理) | 2169 |
|
||||
| `src-tauri/src/proxy/copilot_optimizer.rs` | Copilot 请求优化 | 1251 |
|
||||
| `src-tauri/src/proxy/thinking_rectifier.rs` | Thinking Signature 自修复 | 609 |
|
||||
| `src-tauri/src/proxy/thinking_budget_rectifier.rs` | Thinking Budget 自修复 | 307 |
|
||||
| `src-tauri/src/proxy/thinking_optimizer.rs` | Bedrock Thinking 优化 | 230 |
|
||||
| `src-tauri/src/proxy/cache_injector.rs` | Prompt 缓存注入 | 329 |
|
||||
| `src-tauri/src/proxy/body_filter.rs` | 私有参数过滤 (`_` 前缀) | 261 |
|
||||
| `src-tauri/src/proxy/model_mapper.rs` | 模型名映射 | 312 |
|
||||
| `src-tauri/src/proxy/gemini_url.rs` | Gemini URL 构建 | 583 |
|
||||
| `src-tauri/src/proxy/sse.rs` | SSE 基础工具 | 294 |
|
||||
| `src-tauri/src/proxy/provider_router.rs` | 供应商路由 (故障转移队列) | 512 |
|
||||
| `src-tauri/src/proxy/circuit_breaker.rs` | 熔断器实现 | 482 |
|
||||
| `src-tauri/src/proxy/handler_context.rs` | RequestContext 构建 | 246 |
|
||||
| `src-tauri/src/proxy/response_processor.rs` | 响应处理 (透传/日志/usage) | 915 |
|
||||
| `src-tauri/src/proxy/session.rs` | 会话管理 | 565 |
|
||||
| `src-tauri/src/proxy/hyper_client.rs` | Hyper HTTP 客户端 | 626 |
|
||||
| `src-tauri/src/proxy/http_client.rs` | Reqwest HTTP 客户端 (SOCKS5) | 392 |
|
||||
| `src-tauri/src/proxy/error.rs` | 错误类型定义 | 177 |
|
||||
| `src-tauri/src/proxy/error_mapper.rs` | 错误映射 | 99 |
|
||||
|
||||
---
|
||||
|
||||
## 11. 关键设计特点
|
||||
|
||||
### 11.1 单方向锚定转换
|
||||
|
||||
所有转换以 Anthropic Messages API 为锚点,仅实现 Anthropic ↔ X 的双向转换。客户端协议只有一种(Anthropic Messages API),上游协议有三种(OpenAI Chat、OpenAI Responses、Gemini Native)加一种透传(Anthropic 直连)。
|
||||
|
||||
### 11.2 流式 + 非流式双轨支持
|
||||
|
||||
每种协议转换都同时实现了非流式 (JSON-to-JSON) 和流式 (SSE 事件逐事件翻译,基于 `async_stream` 异步流)。
|
||||
|
||||
### 11.3 有状态转换 (Gemini Shadow Store)
|
||||
|
||||
Gemini 的 `thoughtSignature` 是有状态的,系统通过 Shadow Store 在内存中记录每轮 assistant 的原始 Gemini 响应,在后续请求中回放。这是 CC-Switch 独有的复杂机制。
|
||||
|
||||
### 11.4 自修复能力 (Thinking Rectifiers)
|
||||
|
||||
两个独立 rectifier 在 API 错误时自动修复请求并重试,大幅提高了对 Anthropic API thinking 功能变更的容错能力。
|
||||
|
||||
### 11.5 Copilot 深度适配
|
||||
|
||||
Copilot Optimizer 针对性地解决了 Copilot 的多个实际问题(无限空白 Bug、请求不稳定、高级计费优化等),是与供应商深度绑定的典型案例。
|
||||
|
||||
### 11.6 请求头大小写保留
|
||||
|
||||
通过手动 hyper accept loop + TCP `peek()` 捕获原始请求头大小写,转发给上游时保持一致(部分上游 API 对 header 大小写敏感)。
|
||||
|
||||
### 11.7 代理协议支持
|
||||
|
||||
支持 HTTP/CONNECT 和 SOCKS5 两种代理协议,分别使用 hyper 和 reqwest 客户端。
|
||||
|
||||
### 11.8 UTF-8 安全
|
||||
|
||||
流式转换中处理 TCP 分包导致的 UTF-8 多字节字符截断,使用 `utf8_remainder` 缓冲区确保不会产生 U+FFFD 替换字符。
|
||||
575
docs/analysis_reference/analysis_litellm.md
Normal file
575
docs/analysis_reference/analysis_litellm.md
Normal 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 是架构最复杂的转换层,支持 **四条独立转换路径**:
|
||||
|
||||
#### 路径 1:Converse 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 等。
|
||||
|
||||
#### 路径 2:Invoke 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"`。
|
||||
|
||||
#### 路径 3:Converse-like — Converse 的轻量包装器
|
||||
|
||||
#### 路径 4:Invoke 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` | 通过多重继承适配不同 API(Bedrock + 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、请求体重构等)
|
||||
507
docs/analysis_reference/analysis_new-api.md
Normal file
507
docs/analysis_reference/analysis_new-api.md
Normal 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 + BudgetTokens;Opus 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. **计费深度集成**: 转换层与预扣费、结算、退款等完整计费生命周期集成
|
||||
667
docs/analysis_reference/analysis_one_api.md
Normal file
667
docs/analysis_reference/analysis_one_api.md
Normal 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 token(API 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 AI,URL 支持 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 解析逻辑有重复模式,可抽取公共基类
|
||||
- 缺少请求/响应中间件钩子的统一注入点
|
||||
Reference in New Issue
Block a user