docs: 添加 API 参考文档和技术分析文档
This commit is contained in:
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