多 Provider 认证与协议转换架构
本文档描述 Codev (cc-haha) 的多 Provider 认证架构、OAuth 2.0 流程、API 协议转换机制以及 Provider 配置管理体系。 代码库:
/home/yuki/Code/Agent/Codev
1. 支持的认证提供者
Codev 支持以下 Provider,按认证方式与协议类型分类:
| Provider | 认证方式 | 协议格式 | 实现位置 |
|---|---|---|---|
| Anthropic (first-party) | OAuth 2.0 + PKCE 或 API Key | Anthropic Messages | src/services/oauth/, src/services/api/client.ts |
| Anthropic Bedrock | AWS STS / IAM 凭证 | Anthropic Messages (SDK) | src/services/api/client.ts (条件导入) |
| Anthropic Vertex AI | GCP google-auth-library | Anthropic Messages (SDK) | src/services/api/client.ts (条件导入) |
| Anthropic Foundry (Azure) | API Key 或 Azure AD | Anthropic Messages (SDK) | src/services/api/client.ts (条件导入) |
| OpenAI / Codex | API Key 或 Codex OAuth | openai_chat 或 openai_responses |
src/server/services/openaiOfficialProvider.ts |
| OpenRouter | API Key | openai_responses (Proxy) |
src/utils/model/providers.ts |
| OpenCode Zen | API Key 或免费 (public) | openai_chat (Fetch Override) |
src/services/api/opencodeClient.ts |
| NVIDIA NIM | API Key (build.nvidia.com) | openai_chat (Fetch Override) |
src/services/api/nvidiaClient.ts |
| Local (Ollama/LM Studio/vLLM) | 无 (Dummy Key) | anthropic 或 openai_chat |
src/server/config/providerPresets.json |
| DeepSeek, Zhipu GLM, Kimi, MiniMax, 接口AI, 胜算云 | API Key (auth_token) | anthropic (Native) |
src/server/config/providerPresets.json |
2. 认证架构模式
2.1 两级架构总览
Codev 拥有两套独立的 Provider 系统,设计目标不同:
Tier 1: TUI 内置 Provider (原始 Claude Code)
用途: 终端 /login 快速切换
存储: ~/.claude.json (单字段 authProvider)
实现: src/services/api/client.ts + src/utils/model/providers.ts
Tier 2: cc-haha Provider 预设系统 (Codev 扩展)
用途: 桌面端多 Provider 管理、预设配置、Proxy 转换
存储: ~/.claude/cc-haha/providers.json (结构化索引)
实现: src/server/services/providerService.ts
src/server/config/providerPresets.ts + .json
两个层级通过 ProviderService.autoImportTuiProvider() 自动同步:当桌面端检测到 TUI 已配置 Provider 但自身尚无活跃 Provider 时,自动导入 TUI 的 Provider 配置。
2.2 API Provider 类型定义
Provider 类型定义在 src/utils/model/providers.ts:
export type APIProvider =
| 'firstParty'
| 'openrouter'
| 'openai'
| 'local'
| 'opencode'
| 'nvidia'
| 'bedrock'
| 'vertex'
| 'foundry'
Provider 检测优先级:
- 显式环境变量覆盖 (
CLAUDE_CODE_API_PROVIDER或BETTER_CLAWD_API_PROVIDER) - SDK 标志变量 (
CLAUDE_CODE_USE_BEDROCK,CLAUDE_CODE_USE_VERTEX,CLAUDE_CODE_USE_FOUNDRY) - 配置检测 (
isOpencodeConfigured(),isNvidiaConfigured(),isOpenAIConfigured(),isOpenRouterConfigured()) - 文件缓存 (
~/.claude.json中的authProvider字段)
flowchart TD
A[getAPIProvider] --> B{显式 env 覆盖?}
B -->|是| C[返回覆盖值]
B -->|否| D{SDK 标志变量?}
D -->|Bedrock/Vertex/Foundry| E[返回 SDK 值]
D -->|否| F{配置检测}
F --> Opencode --> G["opencode"]
F --> NVIDIA --> H["nvidia"]
F --> OpenAI --> I["openai"]
F --> OpenRouter --> J["openrouter"]
F --> 缓存文件 --> K["firstParty / 缓存值"]
2.3 Anthropic OAuth 2.0 with PKCE
Anthropic first-party 认证使用标准的 OAuth 2.0 Authorization Code Flow + PKCE,完整流程:
┌─────────┐ ┌──────────────┐ ┌───────────────┐ ┌────────────┐
│ CLI │ │ localhost │ │ Anthropic │ │ Browser │
│ │ │ OAuth Server │ │ OAuth Provider│ │ (User) │
└────┬────┘ └──────┬───────┘ └───────┬───────┘ └─────┬──────┘
│ 1. start() │ │ │
│────────────────>│ │ │
│ 2. port │ │ │
│<────────────────│ │ │
│ │ │ │
│ 3. generateCodeVerifier(), challenge() │ │
│ 4. buildAuthUrl() │ │
│ │ │ │
│ 5. openBrowser(automaticUrl) │ │
│───────────────────────────────────────────────────────────>
│ │ │ │
│ │ 6. HTTP GET /callback?code=...&state= │
│ │<─────────────────────────────────────── │
│ │ │ │
│ 7. validate state, extract code │ │
│ 8. exchangeCodeForTokens(code) │ │
│───────────────────────────────────────>│ │
│ │ 9. access_token + refresh_token │
│<───────────────────────────────────────│ │
│ │ │ │
│10. store tokens, set API key to null │ │
│11. fetchProfileInfo() - 获取订阅类型 │ │
核心文件:
| 职责 | 文件 |
|---|---|
| OAuth 服务入口 | src/services/oauth/index.ts - OAuthService 类 |
| PKCE 加密工具 | src/services/oauth/crypto.ts - SHA-256 code challenge |
| 授权码监听 | src/services/oauth/auth-code-listener.ts - 本地 HTTP 服务器 |
| Token 交换与刷新 | src/services/oauth/client.ts - axios POST 到 token endpoint |
| Profile 获取 | src/services/oauth/getOauthProfile.ts |
| OAuth 类型定义 | src/services/oauth/types.ts |
| OAuth 端点配置 | src/constants/oauth.ts - prod/staging/local 三级 |
OAuth 端点配置 (src/constants/oauth.ts) 支持三层环境:
- Production:
api.anthropic.com,platform.claude.com - Staging:
api-staging.anthropic.com,platform.staging.ant.dev(仅 ant 内部) - Local: 可配置的 localhost 端口 (用于本地开发)
- Custom (FedStart): 受限的白名单 Base URL 覆盖
3. Protocol Translation (Fetch Override 模式)
3.1 核心问题
Claude Code 使用的 @anthropic-ai/sdk 只认识 Anthropic Messages API 格式。对于使用 OpenAI 协议的非 Anthropic Provider,必须进行协议转换。
Anthropic Messages API ←→ OpenAI Chat Completions / Responses API
────────────────────── ─────────────────────────────────────
POST /v1/messages POST /v1/chat/completions
POST /v1/messages?stream POST /v1/chat/completions?stream=true
GET /v1/models GET /v1/models
POST /v1/count_tokens (无对应端点,需要 Stub)
3.2 Fetch Override 机制
每个非 Anthropic Provider 实现一个 createXxxFetchOverride() 函数,通过 Anthropic SDK 的 ClientOptions['fetch'] 钩子注入:
export function createNvidiaFetchOverride():
(input: RequestInfo | URL, init?: RequestInit) => Promise<Response>
该重写在 getAnthropicClient() 中按 Provider 选择性注入:
// src/services/api/client.ts 第 150-163 行
const provider = getAPIProvider()
if (provider === 'opencode') {
opencodeFetchOverride = createOpenCodeFetchOverride(resolvedModel)
}
if (provider === 'nvidia') {
nvidiaFetchOverride = createNvidiaFetchOverride()
}
const resolvedFetch = buildFetch(fetchOverride || opencodeFetchOverride || nvidiaFetchOverride, source)
3.3 Fetch Override 通用模式
所有 Fetch Override 实现共享相同的拦截模式:
flowchart LR
A[SDK 发出请求] --> B{URL 匹配?}
B -->|/messages 或 /v1/| C{端点类型}
B -->|其他| D[透传 fetch]
C -->|/count_tokens| E[Stub: 返回 0]
C -->|/models| F[Stub: 返回空列表]
C -->|/messages| G[解析 Anthropic Body]
G --> H[转换消息格式]
H --> I[附加 Provider Auth Header]
I --> J[调用上游 API]
J --> K{流式?}
K -->|是| L[转换 SSE 流]
K -->|否| M[转换响应体]
L --> N[返回 Anthropic 格式 Response]
M --> N
3.4 消息格式转换
核心转换函数族位于 src/services/api/copilotClient.ts:
Anthropic → OpenAI:
convertAnthropicMessagesToOpenAI(messages, systemPrompt)
- system → role: 'system' 消息
- image block → image_url (data: URI)
- tool_result → role: 'tool' 消息
- tool_use → tool_calls 数组
- thinking → reasoning_content (提供商扩展)
convertAnthropicToolsToOpenAI(tools)
- { name, description, input_schema } → { type: 'function', function: { ... } }
OpenAI → Anthropic (流式):
convertOpenAIStreamToAnthropic(openaiStream, model)
- SSE data: {"choices":[{ "delta":{ "content":"..." } }]}
→ event: content_block_delta\ndata: {"delta":{"type":"text_delta","text":"..."}}
OpenAI → Anthropic (非流式):
- choices[0].message.content → content: [{ type: 'text', text: ... }]
- tool_calls → tool_use content blocks
- finish_reason 'stop' → 'end_turn'
- finish_reason 'tool_calls' → 'tool_use'
Streaming SSE 事件对照表:
| Anthropic (输出) | OpenAI (输入) |
|---|---|
message_start |
合成,包含 message ID |
content_block_start |
首次 delta.content 或 delta.tool_calls.id |
content_block_delta text_delta |
delta.content |
content_block_delta thinking_delta |
delta.reasoning_content |
content_block_start tool_use |
delta.tool_calls[].id |
content_block_delta tool_use_delta |
delta.tool_calls[].function.arguments |
content_block_stop |
delta 结束 / tool_call 完成 |
message_delta |
choices[0].finish_reason |
message_stop |
[DONE] |
3.5 服务端 Proxy 模式
除了客户端的 Fetch Override 模式,Codev 还实现了一套服务端 Proxy,位于 src/server/proxy/handler.ts。
设计目标:将协议转换逻辑从客户端分离到独立 HTTP 服务,支持更灵活的 Provider 管理。
CLI/SDK Proxy Server Upstream
│ │ │
│ POST /proxy/v1/messages │ │
│ (Anthropic 格式请求) │ │
│ ───────────────────────────────────────> │ │
│ │ 获取 Provider 配置 │
│ │ 读取 baseUrl, apiKey, apiFormat │
│ │ │
│ │ anthropicToOpenaiChat(body) │
│ │ ─ 或 ─ │
│ │ anthropicToOpenaiResponses(body) │
│ │ │
│ │ POST /v1/chat/completions │
│ │ 或 POST /v1/responses │
│ │ ──────────────────────────────────> │
│ │ │
│ │ openaiChatToAnthropic(res) │
│ │ 或 openaiResponsesToAnthropic(res) │
│ │ (流式: XxxStreamToAnthropic) │
│ │ │
│ ← Anthropic 格式响应 │ │
│ <─────────────────────────────────────── │ │
服务端 Proxy 转换文件结构:
src/server/proxy/
├── handler.ts # 主入口: POST 分发 + 错误处理
├── transform/
│ ├── types.ts # Anthropic & OpenAI 类型定义
│ ├── anthropicToOpenaiChat.ts # 请求转换: Messages → Chat Completions
│ ├── anthropicToOpenaiResponses.ts # 请求转换: Messages → Responses API
│ ├── openaiChatToAnthropic.ts # 响应转换: Chat Completions → Messages
│ ├── openaiResponsesToAnthropic.ts # 响应转换: Responses API → Messages
│ └── toolArguments.ts # Tool 参数格式修正工具
└── streaming/
├── openaiChatStreamToAnthropic.ts # 流式转换: Chat Completions SSE
├── openaiResponsesStreamToAnthropic.ts # 流式转换: Responses API SSE
└── openaiResponsesStreamToAnthropicResponse.ts
3.6 DeepSeek 推理兼容性
Proxy 还处理 DeepSeek 的特殊格式:
function shouldUseDeepSeekReasoningCompat(baseUrl: string): boolean {
return /(^|[./-])deepseek([./-]|$)/i.test(baseUrl) ||
/(^|[./-])opencode\.ai([:/]|$)/i.test(baseUrl)
}
启用后,thinking blocks 会通过 reasoning_content 字段回传,并且 Anthropic 的 thinking.type 会被转换为 DeepSeek 兼容格式。
4. 各 Provider 集成详解
4.1 NVIDIA NIM
文件: src/services/api/nvidiaClient.ts
- 认证:
getNvidiaApiKey()→ 写入Authorization: Bearer <key>请求头 - 特殊头:
HTTP-Referer: https://claude.ai/,X-BILLING-INVOKE-ORIGIN: Better-Clawd - 端点:
{baseUrl}/v1/chat/completions(默认https://integrate.api.nvidia.com/v1) - Model 列表: 从
/v1/models动态拉取,缓存于cachedNvidiaModels模块变量 - 默认 Model:
nvidia/llama-3.1-nemotron-70b-instruct(可通过NVIDIA_MODEL环境变量覆盖) - 网络架构: 通过 Sidecar 代理转发(
/api/proxy/nvidia),适用于需要特殊头或 CORS 处理的 Provider。与之对比,OpenCode/OpenRouter 使用直接 Fetch Override 模式。
4.2 OpenCode Zen
文件: src/services/api/opencodeClient.ts
- 认证: 支持 API Key 和匿名免费使用
- 免费模式: 当
apiKey为undefined或'public'时,注入 billing 特征码 (x-anthropic-billing-header: cc_version=2.1.0-dev...),标记请求来源用于服务端路由 - 端点:
https://opencode.ai/zen/v1/chat/completions - Model 发现:
- 从
https://models.dev/api.json动态获取模型元数据(云端成本策略) - 从
https://api.github.com/repos/anomalyco/opencode/releases/latest获取版本信息 - 缓存于
cachedModels模块变量 - 支持免费模型列表过滤(life-free models)
- 从
- 动态 UA: 根据版本和运行时自动构建
User-Agent - 推理内容: 支持
reasoning_content到thinkingblock 的转换 - 网络架构: 直连模式(Direct Fetch Override),无需 Sidecar 代理
4.3 OpenAI / Codex Official
文件: src/server/services/openaiOfficialProvider.ts
- 认证: Codex OAuth (通过
hahaOpenAIOAuthService) - 协议:
openai_responses(Responses API) - 运行时种类:
openai_oauth(需要 Token 刷新) - Base URL:
/backend-api/codex(从OPENAI_CODEX_API_ENDPOINT派生) - Model 列表: 来自
src/services/openaiAuth/models.ts的OPENAI_CODEX_MODEL_CATALOG
4.4 自定义 OpenAI 兼容 Provider
文件: src/services/api/customOpenAIClient.ts
- 认证:
ConnectedProviderInfo.apiKey→Bearer <key> - 协议:
openai_chat(Chat Completions) - Model: 使用
custom-openai:<modelId>前缀选择 - Model 拉取:
fetchOpenAICompatibleModelIds()从/v1/models获取 - 适用场景: vLLM, Together AI, Groq 等任何 OpenAI 兼容端点
4.5 GitHub Copilot
文件: src/services/api/copilotClient.ts (原 copilotClient,现已演化为通用转换库)
- 认证: OAuth Token (通过
connectedProviders['github-copilot']) - 协议:
openai_chat - 端点:
https://api.githubcopilot.com/chat/completions - 特殊头:
Openai-Intent: conversation-edits,x-initiator: user - Model 发现: 双源策略 — 优先从 Copilot API 获取,fallback 到
models.dev/api.json - 兼容性缓存:
copilotCompatibilityCache持久化模型兼容性信息到~/.claude.json - Token 参数自动修复: 当 API 返回
"Use 'max_completion_tokens' instead"时自动切换参数
5. Provider 预设系统 (cc-haha)
5.1 预设配置
Provider 预设定义在 src/server/config/providerPresets.json,每个预设包含:
{
"id": "deepseek",
"name": "DeepSeek",
"baseUrl": "https://api.deepseek.com/anthropic",
"apiFormat": "anthropic",
"defaultModels": {
"main": "deepseek-v4-pro",
"haiku": "deepseek-v4-flash",
"sonnet": "deepseek-v4-pro",
"opus": "deepseek-v4-pro"
},
"needsApiKey": true,
"authStrategy": "auth_token",
"defaultEnv": {
"ANTHROPIC_DEFAULT_HAIKU_MODEL_SUPPORTED_CAPABILITIES": "thinking,effort,..."
},
"modelContextWindows": {
"deepseek-v4-pro": 1000000
}
}
预设支持的 Provider (截至当前):
| Preset | API Format | Auth Strategy |
|---|---|---|
| official | anthropic | — |
| deepseek | anthropic | auth_token |
| zhipuglm | anthropic | auth_token |
| kimi | anthropic | auth_token |
| minimax | anthropic | auth_token |
| jiekouai | anthropic | auth_token |
| shengsuanyun | anthropic | auth_token |
| lmstudio | anthropic | auth_token_empty_api_key |
| ollama | anthropic | auth_token_empty_api_key |
| nvidia | openai_chat | api_key |
| custom | anthropic | auth_token |
5.2 认证策略 (ProviderAuthStrategy)
type ProviderAuthStrategy =
| 'api_key' // x-api-key: <key> (NVIDIA)
| 'auth_token' // Authorization: Bearer <key> (DeepSeek, Zhipu, Kimi...)
| 'auth_token_empty_api_key' // Bearer + dummy x-api-key (LM Studio, Ollama)
| 'dual_same_token' // x-api-key + Bearer 同一值
| 'dual_dummy' // x-api-key: dummy + Bearer: dummy (OpenAI OAuth)
5.3 API Format
type ApiFormat =
| 'anthropic' // 原生 Anthropic Messages API (直连,无需 Proxy)
| 'openai_chat' // OpenAI Chat Completions /v1/chat/completions
| 'openai_responses' // OpenAI Responses API /v1/responses
5.4 存储结构
cc-haha 的 Provider 数据存储在:
~/.claude/cc-haha/
├── providers.json # Provider 索引 (活跃 ID + Provider 列表)
└── settings.json # 同步到 SDK 的环境变量
迁移历史通过 persistentStorageMigrations.ts 管理,使用 CURRENT_PROVIDER_INDEX_SCHEMA_VERSION 追踪 schema 版本。
6. Model 管理与切换
6.1 Model 解析链
src/utils/model/
├── providers.ts → getAPIProvider() - 确定当前 Provider
├── modelStrings.ts → getModelStrings() - 解析 Provider 对应的 Model ID
├── model.ts → getMainLoopModel() - 最终选择的 Model
└── configs.ts → ALL_MODEL_CONFIGS - 每个 Model 在各 Provider 的映射
Model 字符串解析流程:
flowchart TD
A[getModelStrings] --> B{STATE.modelStrings 已缓存?}
B -->|是| C[返回缓存值]
B -->|否| D{Provider 类型}
D -->|firstParty| E[ALL_MODEL_CONFIGS 默认值]
D -->|openai| F[firstParty 默认 + openai 覆盖]
D -->|opencode| G[firstParty 默认 + opencode 覆盖]
D -->|openrouter| H[firstParty 默认 + openrouter 覆盖]
D -->|nvidia| I[所有模型 = NVIDIA_MODEL env / 默认]
D -->|bedrock| J[拉取 Bedrock 推理配置 → 匹配]
E --> K[applyModelOverrides]
F --> K
G --> K
H --> K
I --> K
J --> K
K --> L[返回 ModelStrings]
6.2 Provider 切换时的缓存清除
重要:切换 Provider 后必须调用 clearModelStrings(),否则旧的 Model ID 会持续生效:
// src/utils/model/modelStrings.ts
export function clearModelStrings(): void {
setModelStringsState(null as unknown as ModelStrings)
}
6.3 Model Context Windows
两种方式配置模型上下文窗口:
- Preset 预设 (
providerPresets.json中的modelContextWindows字段) - 用户覆盖 (
~/.claude/cc-haha/providers.json中的modelContextWindows字段)
7. Provider 运行时环境
7.1 运行环境构建
ProviderService.syncToSettings() 将活跃 Provider 的配置写入 ~/.claude/cc-haha/settings.json:
ANTHROPIC_BASE_URL=http://localhost:port/proxy/providers/<id>
ANTHROPIC_AUTH_TOKEN=dummy # 用于 Anthropic SDK 认证
ANTHROPIC_API_KEY=dummy # 同上
API_TIMEOUT_MS=300000
ANTHROPIC_MODEL=<model-id>
ANTHROPIC_DEFAULT_HAIKU_MODEL=...
ANTHROPIC_DEFAULT_SONNET_MODEL=...
ANTHROPIC_DEFAULT_OPUS_MODEL=...
MODEL_CONTEXT_WINDOWS={"model-id": 1000000}
7.2 OpenAI OAuth 运行时
对于 OpenAI Official Provider,特殊的环境变量:
CC_HAHA_OPENAI_OAUTH_PROVIDER=1
OPENAI_CODEX_OAUTH_FILE=<path-to-oauth-file>
8. 架构图汇总
graph TB
subgraph "Tier 1: TUI 内置 Provider"
A1[~/.claude.json]
A2[src/utils/model/providers.ts]
A3[src/services/api/client.ts]
A4[src/services/api/nvidiaClient.ts]
A5[src/services/api/opencodeClient.ts]
A2 -->|getAPIProvider| A3
A3 -->|createNvidiaFetchOverride| A4
A3 -->|createOpenCodeFetchOverride| A5
end
subgraph "Tier 2: cc-haha Provider 系统"
B1[~/.claude/cc-haha/providers.json]
B2[~/.claude/cc-haha/settings.json]
B3[src/server/services/providerService.ts]
B4[src/server/config/providerPresets.json]
B5[src/server/proxy/handler.ts]
B3 -->|读写| B1
B3 -->|syncToSettings| B2
B3 -->|activateProvider| B4
B3 -->|getProviderForProxy| B5
end
subgraph "Anthropic SDK"
C1[@anthropic-ai/sdk]
C2[AnthropicBedrock]
C3[AnthropicVertex]
C4[AnthropicFoundry]
end
subgraph "协议转换层"
D1[anthropicToOpenaiChat]
D2[anthropicToOpenaiResponses]
D3[openaiChatToAnthropic]
D4[openaiResponsesToAnthropic]
D5[openaiChatStreamToAnthropic]
D6[openaiResponsesStreamToAnthropic]
D7[copilotClient.ts - 通用转换]
end
subgraph "OAuth 2.0 流程"
E1[src/services/oauth/index.ts]
E2[src/services/oauth/client.ts]
E3[src/services/oauth/crypto.ts]
E4[src/services/oauth/auth-code-listener.ts]
E5[src/constants/oauth.ts]
end
A3 -->|第一方| C1
A3 -->|Bedrock| C2
A3 -->|Vertex| C3
A3 -->|Foundry| C4
A3 -->|Fetch Override| D7
B5 --> D1
B5 --> D2
B5 --> D3
B5 --> D4
B5 --> D5
B5 --> D6
C1 --> E1
9. 添加新 Provider 的标准流程
当需要支持一个新的 OpenAI 兼容 Provider 时,按以下步骤操作:
- Provider 类型: 在
src/utils/model/providers.ts的APIProvider联合类型中添加 - 检测函数: 实现
isXxxConfigured()并在getAPIProvider()调用链中加入 - Model 字符串: 在
src/utils/model/modelStrings.ts的getBuiltinModelStrings()中添加映射 - Fetch Override: 如果协议需要转换,实现
createXxxFetchOverride()(参考nvidiaClient.ts) - 客户端集成: 在
src/services/api/client.ts的getAnthropicClient()中注入 Override - 预设配置: 在
src/server/config/providerPresets.json中添加预设项 - 环境变量: 在
providerRuntimeEnv.ts的getManagedEnvKeys()中添加变量清理 - 认证策略: 如果使用非标准认证,在
buildAnthropicAuthHeaders()中添加策略
10. 关键陷阱与注意事项
10.1 Model Strings 缓存
问题: getModelStrings() 缓存 Provider 特定的 Model ID 到 STATE.modelStrings 中。切换 Provider 后,如果不调用 clearModelStrings(),旧的 Model ID 会持续生效,导致模型解析错误。
解决: 所有 Provider 切换路径 (/login, Provider 激活) 都必须调用 clearModelStrings()。
10.2 DeepSeek 的 max_tokens 限制
Claude Code 默认发送非常大的 max_tokens (如 128K),但 DeepSeek 限制为 8192。服务端 Proxy 的 anthropicToOpenaiChat() 已省略 max_tokens 透传,让上游使用自己的默认值。
10.3 OAuth Token 刷新竞态
refreshOAuthToken() 中有一个关键优化:当全局配置和 Secure Storage 中都有 profile 数据时,跳过 /api/oauth/profile 的额外网络请求。但在 installOAuthTokens → performLogout 的 re-login 路径中,需要穿透缓存以确保订阅类型正确。
10.4 Streaming SSE 转义
convertOpenAIStreamToAnthropic() 中使用 JSON.stringify(reasoning_content).slice(1, -1) 来转义内容中的特殊字符。如果 reasoning_content 包含换行符或 Unicode 字符,直接拼接字符串会导致 SSE 格式损坏。
10.5 Copilot 的 max_tokens → max_completion_tokens 自动修复
Copilot API 对部分模型使用 max_completion_tokens 而非 max_tokens。sendCopilotChatCompletion() 在收到特定错误信息时会自动切换参数并重试,并将兼容性信息缓存 24 小时。
11. 参考资料
- cc-switch (原始 Proxy 参考实现): https://github.com/farion1231/cc-switch
- Anthropic Messages API: https://docs.anthropic.com/en/api/messages
- OpenAI Chat Completions API: https://platform.openai.com/docs/api-reference/chat
- OpenAI Responses API: https://platform.openai.com/docs/api-reference/responses
- OAuth 2.0 with PKCE: https://oauth.net/2/pkce/
- NVIDIA NIM: https://build.nvidia.com/docs
- OpenCode Zen: https://opencode.ai