1
0
Files
nex/docs/analysis_reference/analysis_one_api.md

668 lines
31 KiB
Markdown
Raw Permalink Blame History

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