Files
proxycast/docs/prd/credential-provider-plugin-architecture.md
T
cosoandClaude Opus 4.5 373ff8d2b5 feat: 支持 API Key Provider 在 /v1/chat/completions 和 /v1/messages 端点
- 添加 get_enabled_api_keys_by_type 方法按 Provider 类型获取 API Keys
- 添加 get_next_api_key_by_type 方法支持按类型轮询负载均衡
- 在 chat_completions 端点添加 API Key Provider 回退支持
- 在 anthropic_messages 端点添加 API Key Provider 回退支持
- 支持自定义 base_url 的 AnthropicKey 使用 OpenAI 兼容格式调用
- 添加 convert_openai_response_to_anthropic 响应转换函数
- 更新版本号到 v0.28.0

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 19:14:29 +08:00

2010 lines
86 KiB
Markdown
Raw 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.
# ProxyCast 凭证提供商插件化架构 PRD
> 版本: 1.0.0
> 日期: 2026-01-03
> 状态: Draft
---
## 一、背景与目标
### 1.1 背景
当前 ProxyCast 支持 11 种 Provider 类型,但存在以下问题:
1. **硬编码耦合**:Provider 类型通过 `ProviderType` 枚举硬编码,新增 Provider 需要修改核心代码
2. **协议转换分散**:`converter/` 和 `translator/` 两套转换逻辑并存
3. **风控逻辑耦合**:Kiro、Antigravity 等的特殊风控逻辑散落在各处
4. **难以独立更新**:某个 Provider(如 Kiro)因风控变化需要更新时,影响整体
### 1.2 目标
将 **OAuth 凭证系统**重构为插件化架构,**API Key 系统保持现有设计不变**:
1. **OAuth 凭证插件化**:Kiro、Codex、Gemini OAuth、Qwen、Antigravity、iFlow 等各自独立
2. **API Key 系统不变**:现有 `api_key_providers` + `api_keys` 表结构保持不变,60+ 系统预设继续使用
3. **独立更新**:某个 OAuth Provider 风控变化时,只需更新对应插件
4. **复用现有插件系统**:基于现有的 `Plugin` trait 扩展
### 1.3 插件化范围
| 系统 | 是否插件化 | 原因 |
|------|-----------|------|
| **OAuth 凭证** | ✅ 是 | Kiro 风控复杂、Token 刷新、不同凭证格式 |
| **API Key** | ❌ 否 | 统一结构、配置简单、用户可自定义、已有 60+ 预设 |
---
## 二、现有架构分析
### 2.1 凭证管理模块
```
src-tauri/src/credential/
├── types.rs # Credential, CredentialData, CredentialStatus
├── pool.rs # CredentialPool (DashMap 实现)
├── balancer.rs # LoadBalancer (轮询/最少使用/随机)
├── health.rs # HealthChecker (3次失败标记不健康)
├── quota.rs # QuotaManager (配额超限检测)
└── sync.rs # 数据库同步
```
**现有凭证数据结构**:
```rust
pub enum CredentialData {
OAuth { access_token, refresh_token, expires_at },
ApiKey { key, base_url },
}
```
### 2.2 Provider 类型(现有问题:硬编码枚举)
**问题**:现有设计使用硬编码枚举,新增 Provider 必须修改核心代码
```rust
// ❌ 现有设计:硬编码枚举
pub enum ProviderType {
Kiro, Gemini, Qwen, OpenAI, Claude, Antigravity,
Vertex, GeminiApiKey, Codex, ClaudeOAuth, IFlow,
// 新增 Provider?必须修改这个枚举!
}
pub enum CredentialData {
KiroOAuth { ... }, GeminiOAuth { ... }, ...
// 新增凭证类型?必须修改这个枚举!
}
```
**目标**:删除硬编码枚举,改为动态注册
```rust
// ✅ 目标设计:动态注册
// 不再有 ProviderType 枚举
// 不再有 CredentialData 枚举
// 新增 Provider 只需实现 trait 并注册
```
### 2.3 现有凭证类型(需要迁移到插件)
| Provider | 凭证类型 | 配置字段 |
|----------|---------|---------|
| Kiro | OAuth | creds_file_path |
| Gemini | OAuth | creds_file_path, project_id |
| Qwen | OAuth | creds_file_path |
| Antigravity | OAuth | creds_file_path, project_id |
| OpenAI | API Key | api_key, base_url |
| Claude | API Key | api_key, base_url |
| Vertex | API Key | api_key, base_url, model_aliases |
| GeminiApiKey | API Key | api_key, base_url, excluded_models |
| Codex | OAuth | creds_file_path, api_base_url |
| ClaudeOAuth | OAuth | creds_file_path |
| IFlow | OAuth/Cookie | creds_file_path |
**迁移后**:每个 Provider 插件自己定义凭证配置 Schema
### 2.4 协议转换
**现有协议类型**:
| 协议 | 用途 |
|------|------|
| OpenAI | 标准 Chat Completions API |
| Anthropic | Claude Messages API(Claude Code 使用此协议)|
| CodeWhisperer | AWS Kiro IDE(底层是 Claude)|
| Gemini | Google Gemini API |
| Antigravity | Google 内部(支持 Claude 和 Gemini 模型)|
#### 2.4.1 双向转换架构
ProxyCast 作为 **API 代理**,核心职责是协议转换:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 协议转换流程 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 客户端请求 (Anthropic 协议 /v1/messages) │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 输入转换层 (Request Translator) │ │
│ │ │ │
│ │ Anthropic → CodeWhisperer (translator/kiro/anthropic/request) │ │
│ │ Anthropic → Antigravity (converter/anthropic_to_antigravity)│ │
│ │ Anthropic → Anthropic (Claude OAuth, 直通) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ Provider 后端调用 │ │
│ │ │ │
│ │ Kiro: AWS CodeWhisperer API (返回 AWS Event Stream) │ │
│ │ Antigravity: Gemini CLI API (返回 Antigravity 响应) │ │
│ │ Claude OAuth: Anthropic API (返回 Anthropic SSE, 直通) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 输出转换层 (Response Translator) │ │
│ │ │ │
│ │ AWS Event Stream → Anthropic SSE │ │
│ │ (translator/kiro/anthropic/response.rs) │ │
│ │ │ │
│ │ Antigravity → Anthropic SSE (claude-* 模型) │ │
│ │ Antigravity → Gemini 协议 (gemini-* 模型) │ │
│ │ │ │
│ │ Anthropic SSE → Anthropic SSE (Claude OAuth, 直通) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Anthropic 协议响应 (给 Claude Code 等客户端) │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
#### 2.4.2 转换规则(按底层模型决定)
**核心原则**:输出协议由 **Provider 后端的底层模型** 决定,而非客户端输入格式
| Provider | 底层模型 | 后端协议 | 输出协议 |
|----------|---------|---------|---------|
| Kiro | Claude | CodeWhisperer (AWS Stream) | **Anthropic** (Claude API) |
| Antigravity (claude-*) | Claude | Antigravity | **Anthropic** (Claude API) |
| Antigravity (gemini-*) | Gemini | Antigravity | **Gemini** |
| Claude OAuth | Claude | Anthropic | **Anthropic** (直通) |
| Codex | GPT | OpenAI | **OpenAI** (直通) |
| Qwen | 通义千问 | OpenAI 兼容 | **OpenAI** |
| iFlow | - | OpenAI 兼容 | **OpenAI** |
| Gemini OAuth | Gemini | Gemini | **Gemini** |
**转换链路示例**:
```
┌─────────────────────────────────────────────────────────────────────┐
│ Kiro Provider(底层 Claude) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Anthropic 协议请求 (/v1/messages) │
│ │ │
│ ▼ │
│ 输入转换: Anthropic → CodeWhisperer 请求 │
│ │ (translator/kiro/anthropic/request.rs) │
│ ▼ │
│ Kiro 后端调用 (返回 AWS Event Stream) │
│ │ │
│ ▼ │
│ 输出转换: AWS Stream → Anthropic SSE │
│ │ (translator/kiro/anthropic/response.rs) │
│ ▼ │
│ Anthropic 协议响应 (给 Claude Code 等客户端) │
│ │
└─────────────────────────────────────────────────────────────────────┘
```
```
┌─────────────────────────────────────────────────────────────────────┐
│ Antigravity Provider(动态协议) │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ Anthropic 协议请求 (model: claude-opus-4.5) │
│ │ │
│ ▼ │
│ 输入转换: Anthropic → Antigravity 请求 │
│ │ │
│ ▼ │
│ 匹配规则: claude-* → 底层 Claude │
│ │ │
│ ▼ │
│ 输出转换: Antigravity → Anthropic SSE │
│ │
│ ───────────────────────────────────────────────────────────────── │
│ │
│ Anthropic 协议请求 (model: gemini-2.0-flash) │
│ │ │
│ ▼ │
│ 输入转换: Anthropic → Antigravity 请求 │
│ │ │
│ ▼ │
│ 匹配规则: gemini-* → 底层 Gemini │
│ │ │
│ ▼ │
│ 输出转换: Antigravity → Gemini 协议响应 │
│ │
└─────────────────────────────────────────────────────────────────────┘
```
**常见使用场景**:
| 客户端 | Provider | 底层模型 | 输出协议 |
|--------|----------|---------|---------|
| **ProxyCast 内置 Agent** | Kiro | Claude | Anthropic (Claude API) |
| **ProxyCast 内置 Agent** | Antigravity (claude-*) | Claude | Anthropic (Claude API) |
| **ProxyCast 内置 Agent** | Claude OAuth | Claude | Anthropic (Claude API) |
| Claude Code | Kiro | Claude | Anthropic (Claude API) |
| Claude Code | Antigravity (claude-*) | Claude | Anthropic (Claude API) |
| Claude Code | Claude OAuth | Claude | Anthropic (Claude API) |
| Gemini 客户端 | Antigravity (gemini-*) | Gemini | Gemini |
| OpenAI 兼容客户端 | Codex | GPT | OpenAI |
| OpenAI 兼容客户端 | Qwen/iFlow | 通义千问等 | OpenAI |
#### 2.4.3 ProxyCast API Server 架构
ProxyCast 提供 **API Server**,统一服务内部和外部客户端:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ ProxyCast API Server │
│ │
│ 端口: 8999 | 支持多种协议格式 | API Key 认证 │
│ 地址: 127.0.0.1:8999 (本地) | 198.18.0.1:8999 (局域网) │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 客户端接入 │ │
│ │ │ │
│ │ 内部客户端: │ │
│ │ ├── ProxyCast 内置 AI Agent(技能、话题、工具调用) │ │
│ │ │ │
│ │ 外部客户端: │ │
│ │ ├── Claude Code (Anthropic 协议) │ │
│ │ ├── Cursor (OpenAI 协议) │ │
│ │ ├── Continue (OpenAI 协议) │ │
│ │ └── 任意支持 OpenAI/Anthropic API 的应用 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ │ OpenAI / Anthropic / Gemini 等协议请求 │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 服务器控制 │ │
│ │ │ │
│ │ 默认 Provider: [Kiro] [Gemini] [Qwen] [Antigravity] [OpenAI] [Claude]│
│ │ 当前可用凭证: P2 (绿色表示健康) │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────────────┐ │
│ │ 代理层处理 │ │
│ │ │ │
│ │ 1. API Key 验证 │ │
│ │ 2. Provider 路由(根据默认设置或请求指定) │ │
│ │ 3. OAuth 凭证获取 │ │
│ │ 4. 协议转换(Anthropic → CodeWhisperer 等) │ │
│ │ 5. 后端调用 │ │
│ │ 6. 响应转换 │ │
│ └─────────────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ Kiro / Antigravity / Claude OAuth / Codex 等后端 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**API Server 配置**:
| 配置项 | 说明 |
|--------|------|
| 端口 | 默认 8999,可配置 |
| API Key | 访问密钥(如 Proxycast-key11)|
| 默认 Provider | 用户可选择默认路由的 Provider |
**支持的输入/输出协议**:
| 协议 | 端点 | 说明 |
|------|------|------|
| OpenAI | `/v1/chat/completions` | 标准 OpenAI Chat Completions API |
| Anthropic | `/v1/messages` | Claude Messages API |
| Gemini | `/v1/gemini/*` | Google Gemini API |
| OpenAI Responses | `/v1/responses` | OpenAI Responses API (流式) |
| 其他兼容协议 | - | 支持扩展更多协议 |
**内置 AI Agent 特点**:
| 功能 | 说明 |
|------|------|
| 模型选择 | 用户可选择任意已配置的模型(如 claude-opus-4-5-20251101)|
| 技能系统 | 支持自定义 Skills,扩展 Agent 能力 |
| 工具调用 | 支持 MCP、联网搜索、文件附件等 |
| 对话管理 | 话题列表、历史记录 |
**统一调用流程**(内部/外部客户端一致):
```
客户端发送请求 (内置 Agent 或外部 Claude Code/Cursor)
│
▼
API Server 接收 (端口 8999)
│
├── 验证 API Key
├── 识别协议格式 (OpenAI / Anthropic / Gemini / OpenAI Responses 等)
│
▼
根据默认 Provider 或请求参数路由
│
▼
OAuth Provider 处理
├── 获取凭证
├── 协议转换 (如 Anthropic → CodeWhisperer)
└── 调用后端
│
▼
响应转换 (如 AWS Stream → Anthropic SSE)
│
▼
返回给客户端
```
#### 2.4.4 StreamEvent 统一事件
中间表示层,解耦输入输出:
```rust
pub enum StreamEvent {
MessageStart { id: String, model: String },
ContentBlockStart { index: u32, block_type: ContentBlockType },
TextDelta { text: String },
ToolUseStart { id: String, name: String },
ToolUseInputDelta { id: String, partial_json: String },
ToolUseStop { id: String },
ContentBlockStop { index: u32 },
MessageStop { stop_reason: StopReason },
Usage { input_tokens: u32, output_tokens: u32 },
Error { error_type: String, message: String },
Ping,
}
```
#### 2.4.5 现有代码位置
| 功能 | 文件路径 |
|------|---------|
| Anthropic → Kiro 请求 | `translator/kiro/anthropic/request.rs` |
| Kiro → Anthropic 响应 | `translator/kiro/anthropic/response.rs` |
| OpenAI → Kiro 请求 | `translator/kiro/openai/request.rs` |
| Kiro → OpenAI 响应 | `translator/kiro/openai/response.rs` |
| OpenAI → Antigravity | `converter/openai_to_antigravity.rs` |
| AWS 流解析 | `stream/parsers/aws_event_stream.rs` |
| Anthropic SSE 生成 | `stream/generators/anthropic_sse.rs` |
| OpenAI SSE 生成 | `stream/generators/openai_sse.rs` |
#### 2.4.6 插件化后的协议转换
**目标**:每个 Provider 插件负责自己的双向转换
```rust
#[async_trait]
pub trait OAuthProviderPlugin: Send + Sync {
// ... 其他方法 ...
/// 输入转换:将客户端请求转换为 Provider 特有格式
async fn transform_request(&self, req: &mut ChatRequest) -> Result<ProviderRequest>;
/// 输出转换:将 Provider 响应转换为 StreamEvent
fn parse_response_chunk(&self, chunk: &[u8]) -> Result<Vec<StreamEvent>>;
/// 目标输出协议(决定使用哪个 SSE Generator)
fn output_protocol(&self) -> OutputProtocol; // Anthropic | OpenAI
}
pub enum OutputProtocol {
Anthropic, // Claude Code 使用
OpenAI, // OpenAI 兼容客户端使用
}
```
### 2.5 现有插件系统
```rust
#[async_trait]
pub trait Plugin: Send + Sync {
fn name(&self) -> &str;
fn version(&self) -> &str;
fn manifest(&self) -> &PluginManifest;
async fn init(&mut self, config: &PluginConfig) -> Result<(), PluginError>;
async fn on_request(&self, ctx: &mut PluginContext, request: &mut Value) -> Result<HookResult, PluginError>;
async fn on_response(&self, ctx: &mut PluginContext, response: &mut Value) -> Result<HookResult, PluginError>;
async fn on_error(&self, ctx: &mut PluginContext, error: &str) -> Result<HookResult, PluginError>;
async fn shutdown(&mut self) -> Result<(), PluginError>;
}
```
### 2.6 处理管道
```
Auth → Injection → Routing → PluginPre → Provider → PluginPost → Telemetry
```
---
## 三、目标架构设计
### 3.1 核心设计原则
1. **插件职责明确**:每个 Provider 插件负责 凭证管理 + 协议转换 + 风控适配
2. **转换成标准协议**:所有插件输出标准协议(Anthropic/OpenAI/Gemini)
3. **认证类型固定**:AuthType 作为枚举,不插件化
4. **复用现有基础设施**:复用 credential/pool.rs, balancer.rs, health.rs
### 3.2 完全动态化架构
**核心原则**:没有任何硬编码枚举,所有类型都通过注册表动态管理
```
┌─────────────────────────────────────────────────────────────────────┐
│ CredentialProviderRegistry(动态) │
│ │
│ 新增 Provider?只需实现 trait 并注册,无需修改核心代码 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ kiro │ │anthropic │ │ openai │ │ qwen │ │ iflow │ │
│ │ Provider │ │ Provider │ │ Provider │ │ Provider │ │ Provider │ │
│ │ │ │ │ │ │ │ │ │ │ │
│ │ OAuth │ │ ApiKey │ │ ApiKey │ │ OAuth │ │ OAuth │ │
│ │ 风控适配 │ │ 原生 │ │ 原生 │ │ 兼容 │ │ Cookie │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │ │ │
│ 目标协议: 目标协议: 目标协议: 目标协议: 目标协议: │
│ "anthropic" "anthropic" "openai" "qwen" "openai" │
│ │ │ │ │ │ │
│ └────────────┴────────────┴────────────┴────────────┘ │
│ │ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ gemini │ │ vertex │ │ codex │ │antigravity│ ...更多 │
│ │ Provider │ │ Provider │ │ Provider │ │ Provider │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ StandardProtocolRegistry(动态) │
│ │
│ 新增协议?只需实现 ProtocolHandler trait 并注册 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │anthropic │ │ openai │ │ gemini │ │ qwen │ │ doubao │ │
│ │ Handler │ │ Handler │ │ Handler │ │ Handler │ │ Handler │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ │
│ ┌──────────┐ ┌──────────┐ │
│ │ wenxin │ │ ... │ ← 可继续扩展中国厂商协议 │
│ │ Handler │ │ │ │
│ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 输出适配层 │
│ ┌────────────────┐ ┌────────────────┐ ┌────────────────┐ │
│ │ Anthropic API │ │ OpenAI API │ │ Claude Code │ │
│ │ Output │ │ Output │ │ Protocol │ │
│ └────────────────┘ └────────────────┘ └────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘
```
### 转换流程示例
```
用户请求 (model: claude-opus-4.5)
│
▼
CredentialProviderRegistry.find_by_model("claude-opus-4.5")
│
▼
KiroProvider (匹配 claude-* 模型)
├─ acquire_credential() → 获取 OAuth 凭证
├─ transform_request() → CodeWhisperer 格式
├─ apply_risk_control() → Machine ID、特殊头部
└─ target_protocol() → "anthropic"
│
▼
StandardProtocolRegistry.get("anthropic")
│
▼
AnthropicProtocolHandler
├─ format_request() → Anthropic Messages API 格式
└─ parse_response() → 统一响应格式
│
▼
输出适配层 (根据客户端选择)
├─ Anthropic API Output
├─ OpenAI API Output
└─ Claude Code Protocol Output
```
### 3.3 CredentialProviderPlugin Trait
```rust
/// 凭证提供商插件 - 核心 Trait
///
/// 设计原则:
/// - 不依赖任何硬编码枚举
/// - 新增 Provider 只需实现此 trait 并注册
/// - 凭证配置由插件自己定义 Schema
/// - 一个插件可支持多种认证方式(OAuth、API Key、第三方中转)
#[async_trait]
pub trait CredentialProviderPlugin: Send + Sync {
// ========== 基础信息 ==========
/// 插件唯一标识(代替 ProviderType 枚举)
fn id(&self) -> &str;
/// 显示名称
fn display_name(&self) -> &str;
/// 插件版本
fn version(&self) -> &str;
/// 插件描述
fn description(&self) -> &str { "" }
/// 默认目标标准协议
fn target_protocol(&self) -> &str;
/// 根据模型动态返回目标协议(用于 Antigravity 等多协议 Provider)
fn target_protocol_for_model(&self, model: &str) -> &str {
self.target_protocol() // 默认返回固定协议
}
// ========== 多认证方式支持 ==========
/// 支持的认证方式(一个插件可支持多种)
/// 例如 Anthropic 同时支持 OAuth、API Key、第三方中转
fn supported_auth_types(&self) -> Vec<AuthTypeInfo>;
/// 根据认证方式返回对应的凭证配置 Schema
fn credential_schema_for_auth(&self, auth_type: &str) -> serde_json::Value;
/// 解析凭证配置(从 JSON 解析成插件内部结构)
fn parse_credential_config(&self, auth_type: &str, config: serde_json::Value) -> Result<Box<dyn CredentialConfig>>;
/// 创建凭证(从用户输入创建)
async fn create_credential(&self, auth_type: &str, config: serde_json::Value) -> Result<String>;
// ========== 模型能力 ==========
/// 模型家族定义(用于 Mini/Pro/Max 分层)
fn model_families(&self) -> Vec<ModelFamily>;
/// 获取支持的模型列表
async fn list_models(&self) -> Result<Vec<ModelInfo>>;
/// 检查是否支持某个模型
fn supports_model(&self, model: &str) -> bool;
// ========== 凭证管理 ==========
/// 获取可用凭证
async fn acquire_credential(&self, model: &str) -> Result<AcquiredCredential>;
/// 释放凭证
async fn release_credential(&self, credential_id: &str, result: UsageResult);
/// 验证凭证有效性
async fn validate_credential(&self, credential_id: &str) -> Result<ValidationResult>;
/// 刷新 Token(OAuth 类型)
async fn refresh_token(&self, credential_id: &str) -> Result<TokenRefreshResult>;
// ========== 协议转换 ==========
/// 将输入请求转换成标准协议
async fn transform_request(&self, req: &mut ChatRequest) -> Result<()>;
/// 将响应转换回来(如果需要)
async fn transform_response(&self, resp: &mut ChatResponse) -> Result<()>;
// ========== 风控适配 ==========
/// 应用特有的风控逻辑
async fn apply_risk_control(&self, req: &mut ChatRequest, credential_id: &str) -> Result<()>;
/// 解析特有的错误码
fn parse_error(&self, status: u16, body: &str) -> Option<ProviderError>;
// ========== 插件配置(非凭证配置)==========
/// 插件配置 Schema(用于 UI 动态生成表单)
fn plugin_config_schema(&self) -> serde_json::Value { serde_json::json!({}) }
/// 更新插件配置
async fn update_plugin_config(&mut self, config: serde_json::Value) -> Result<()> { Ok(()) }
// ========== 生命周期 ==========
/// 初始化插件
async fn init(&mut self) -> Result<()>;
/// 关闭插件
async fn shutdown(&mut self) -> Result<()>;
}
/// 认证方式信息
pub struct AuthTypeInfo {
/// 认证方式 ID
pub id: String, // "oauth", "api_key", "third_party"
/// 显示名称
pub display_name: String, // "OAuth 登录", "官方 API Key", "第三方中转"
/// 描述
pub description: String, // "使用官方 OAuth 授权"
/// UI 分组(显示在哪个 Tab)
pub category: CredentialCategory,
}
/// 凭证配置 trait(代替 CredentialData 枚举)
/// 每个插件自己定义凭证配置结构
pub trait CredentialConfig: Send + Sync + Any {
fn as_any(&self) -> &dyn Any;
fn credential_type(&self) -> &str; // "oauth", "api_key", "third_party"
}
```
### 3.4 标准协议(动态注册)
```rust
// ❌ 不再硬编码枚举
// pub enum StandardProtocol { Anthropic, OpenAI, Gemini }
// ✅ 改为字符串标识 + 注册表
pub struct StandardProtocol(pub String);
impl StandardProtocol {
// 内置常量(方便使用,但不限制扩展)
pub const ANTHROPIC: &'static str = "anthropic";
pub const OPENAI: &'static str = "openai";
pub const GEMINI: &'static str = "gemini";
pub const QWEN: &'static str = "qwen"; // 通义千问
pub const DOUBAO: &'static str = "doubao"; // 字节豆包
pub const WENXIN: &'static str = "wenxin"; // 百度文心
// ... 可继续扩展
}
/// 标准协议注册表
pub struct StandardProtocolRegistry {
protocols: HashMap<String, Arc<dyn ProtocolHandler>>,
}
impl StandardProtocolRegistry {
pub fn new() -> Self {
let mut registry = Self::default();
// 内置协议处理器
registry.register("anthropic", Arc::new(AnthropicProtocolHandler::new()));
registry.register("openai", Arc::new(OpenAIProtocolHandler::new()));
registry.register("gemini", Arc::new(GeminiProtocolHandler::new()));
// 中国厂商(目前大多兼容 OpenAI)
registry.register("qwen", Arc::new(OpenAICompatHandler::new("qwen")));
registry.register("doubao", Arc::new(OpenAICompatHandler::new("doubao")));
registry
}
/// 注册新协议(支持运行时扩展)
pub fn register(&mut self, id: &str, handler: Arc<dyn ProtocolHandler>);
/// 获取协议处理器
pub fn get(&self, id: &str) -> Option<Arc<dyn ProtocolHandler>>;
/// 列出所有已注册协议
pub fn list(&self) -> Vec<String>;
}
/// 协议处理器 trait
#[async_trait]
pub trait ProtocolHandler: Send + Sync {
/// 协议 ID
fn id(&self) -> &str;
/// 显示名称
fn display_name(&self) -> &str;
/// 请求格式化(转换成该协议的请求格式)
fn format_request(&self, req: &ChatRequest) -> Result<serde_json::Value>;
/// 响应解析
fn parse_response(&self, resp: &serde_json::Value) -> Result<ChatResponse>;
/// 流式响应解析
fn parse_stream_chunk(&self, chunk: &[u8]) -> Result<Option<ChatChunk>>;
/// 错误解析
fn parse_error(&self, status: u16, body: &str) -> ProviderError;
}
```
### 3.5 现有系统代码分析
#### 3.5.1 现有硬编码枚举(需要消除)
**ProviderType 枚举(11种)** - `src-tauri/src/lib.rs:71-91`:
```rust
pub enum ProviderType {
Kiro, // AWS 凭证同步
Gemini, // Google Gemini OAuth
Qwen, // 阿里通义千问 OAuth
OpenAI, // OpenAI API Key
Claude, // Anthropic API Key
Antigravity, // Gemini 3 Pro
Vertex, // Google Vertex AI
GeminiApiKey,// Gemini API Key
Codex, // OpenAI OAuth
ClaudeOAuth, // Anthropic OAuth
IFlow, // IFlow
}
```
**ApiProviderType 枚举(10种)** - `src-tauri/src/database/dao/api_key_provider.rs`:
```rust
pub enum ApiProviderType {
Openai, OpenaiResponse, Anthropic, Gemini,
AzureOpenai, Vertexai, AwsBedrock, Ollama,
NewApi, Gateway,
}
```
**问题**:两套枚举并存,新增 Provider 需要修改多处代码。
#### 3.5.2 现有凭证数据结构(过于简单)
**CredentialData** - `src-tauri/src/credential/types.rs:73-88`:
```rust
pub enum CredentialData {
OAuth {
access_token: String,
refresh_token: Option<String>,
expires_at: Option<DateTime<Utc>>,
},
ApiKey {
key: String,
base_url: Option<String>,
},
}
```
**问题**:只有 2 种类型,无法支持:
- AWS 凭证(Access Key + Secret Key + Region)
- Azure 凭证(Endpoint + API Version + Deployment)
- 复杂配置(订阅类型、限流、额度等)
#### 3.5.3 现有数据库表结构
**表 1: provider_pool_credentials**(新凭证池):
```sql
uuid, provider_type, credential_data (JSON),
name, is_healthy, is_disabled,
check_health, check_model_name, not_supported_models,
usage_count, error_count, last_used, last_error_time,
cached_access_token, cached_refresh_token, token_expiry_time,
source, proxy_url, created_at, updated_at
```
**表 2: api_key_providers**(旧 API Key 系统):
```sql
id, name, type, api_host, is_system, group_name,
enabled, sort_order, api_version, project, location, region
```
#### 3.5.4 现有 UI 结构
```
ProviderPoolPage
├── Tab: OAuth(卡片布局)
│ └── CredentialCard × N
├── Tab: API Key(左右分栏)
│ ├── 左栏: ProviderList(60+ 系统预设)
│ └── 右栏: ProviderSetting + ApiKeyList
└── Tab: Config
└── VertexAI / Amp 配置
```
#### 3.5.5 OAuth Provider 插件架构(参考 MachineIdTool)
**核心设计理念**:
- **每个插件实现自己的 UI** - 包括凭证管理、配置界面、状态展示等
- **ProxyCast 作为入口和注册管理中心** - 提供插件容器、协议路由、注册表管理
- **插件完全自治** - UI、业务逻辑、风控策略都在插件内部实现
**插件架构设计**:OAuth Provider 采用与 MachineIdTool 一致的独立项目形式
```
┌─────────────────────────────────────────────────────────────────────────┐
│ OAuth Provider 插件架构 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 每个 OAuth Provider 是独立的项目/仓库: │
│ │
│ github.com/aiclientproxy/kiro-provider/ │
│ ├── plugin/ │
│ │ ├── plugin.json # 插件元数据 │
│ │ └── config.json # 插件配置 │
│ ├── src-tauri/src/ │
│ │ ├── lib.rs # 插件入口 │
│ │ ├── commands.rs # Tauri 命令 │
│ │ ├── service.rs # 核心服务逻辑 │
│ │ └── models.rs # 数据模型 │
│ └── src/ # 【必须】插件前端 UI │
│ ├── components/ # UI 组件 │
│ │ ├── CredentialList.tsx # 凭证列表 │
│ │ ├── CredentialForm.tsx # 凭证添加/编辑表单 │
│ │ ├── SettingsPanel.tsx # 插件设置面板 │
│ │ └── StatusCard.tsx # 状态卡片 │
│ ├── hooks/ # 插件专用 hooks │
│ └── index.tsx # 插件 UI 入口 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**ProxyCast 与插件的职责划分**:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 职责划分 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ ProxyCast 主应用(入口 + 注册中心): │
│ ├── 插件容器 (Plugin Host) │
│ │ ├── 加载插件 UI 组件 │
│ │ ├── 提供插件挂载点 (mounting points) │
│ │ └── 插件生命周期管理 │
│ ├── 注册管理中心 │
│ │ ├── OAuthProviderRegistry (插件注册表) │
│ │ ├── 插件发现与安装 │
│ │ └── 插件启用/禁用控制 │
│ ├── 协议路由层 │
│ │ ├── 请求路由到对应插件 │
│ │ └── 响应协议转换 │
│ └── 公共基础设施 │
│ ├── 数据库连接 │
│ ├── HTTP 客户端 │
│ └── 加密存储 │
│ │
│ ───────────────────────────────────────────────────────────────────── │
│ │
│ 插件(自治单元): │
│ ├── 【必须】自己的 UI │
│ │ ├── 凭证管理界面 │
│ │ ├── 配置界面 │
│ │ └── 状态展示 │
│ ├── 业务逻辑 │
│ │ ├── Token 刷新 │
│ │ ├── 凭证验证 │
│ │ └── 协议转换 │
│ └── 风控策略 │
│ ├── Machine ID 管理 │
│ ├── 限流检测 │
│ └── 冷却期控制 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**plugin.json 结构(参考 MachineIdTool)**:
```json
{
"name": "kiro-provider",
"version": "1.0.0",
"description": "Kiro (AWS CodeWhisperer) OAuth Provider - 支持 Claude 模型",
"author": "ProxyCast Team",
"homepage": "https://github.com/aiclientproxy/kiro-provider",
"license": "MIT",
"plugin_type": "oauth_provider",
"entry": "kiro-provider-cli",
"min_proxycast_version": "1.0.0",
"provider": {
"id": "kiro",
"display_name": "Kiro (CodeWhisperer)",
"target_protocol": "anthropic",
"supported_models": ["claude-*"],
"auth_type": "oauth",
"credential_schema": {
"type": "object",
"required": ["creds_file_path"],
"properties": {
"creds_file_path": {
"type": "string",
"title": "凭证文件路径",
"description": "AWS SSO 凭证文件"
}
}
}
},
"binary": {
"binary_name": "kiro-provider-cli",
"github_owner": "aiclientproxy",
"github_repo": "kiro-provider",
"platform_binaries": {
"macos-arm64": "kiro-provider-aarch64-apple-darwin",
"macos-x64": "kiro-provider-x86_64-apple-darwin",
"linux-x64": "kiro-provider-x86_64-unknown-linux-gnu",
"windows-x64": "kiro-provider-x86_64-pc-windows-msvc.exe"
},
"checksum_file": "checksums.txt"
},
"ui": {
"surfaces": ["oauth_providers"],
"icon": "Cloud",
"title": "Kiro Provider"
}
}
```
**config.json 结构**:
```json
{
"enabled": true,
"timeout_ms": 30000,
"settings": {
"risk_control": {
"machine_id_rotation": true,
"version_spoofing": true
}
}
}
```
**OAuth Provider 插件列表(每个独立仓库)**:
| 插件名 | 仓库 | plugin_type | 复杂度 |
|--------|------|-------------|--------|
| kiro-provider | aiclientproxy/kiro-provider | oauth_provider | 🔴 高 |
| antigravity-provider | aiclientproxy/antigravity-provider | oauth_provider | 🔴 高 |
| claude-oauth-provider | aiclientproxy/claude-oauth-provider | oauth_provider | 🟡 中 |
| codex-provider | aiclientproxy/codex-provider | oauth_provider | 🟡 中 |
| gemini-oauth-provider | aiclientproxy/gemini-oauth-provider | oauth_provider | 🟡 中 |
| qwen-provider | aiclientproxy/qwen-provider | oauth_provider | 🟢 低 |
| iflow-provider | aiclientproxy/iflow-provider | oauth_provider | 🟢 低 |
#### 3.5.6 ProxyCast 插件宿主架构
**核心理念**:ProxyCast 只提供入口和容器,具体 UI 由各插件自己实现
**ProxyCast 主应用 UI 结构**:
```
ProviderPoolPage
├── Tab: OAuth 插件
│ ├── 插件导航栏(顶部)
│ │ ├── [+ 安装插件] 按钮
│ │ └── 已安装插件列表
│ │ ├── 🔌 Kiro Provider ● 已启用 [打开]
│ │ ├── 🔌 Antigravity ● 已启用 [打开]
│ │ ├── 🔌 Claude OAuth ○ 已禁用 [打开]
│ │ └── ...
│ │
│ └── 插件 UI 挂载区域(主体)
│ └── <PluginContainer pluginId="kiro-provider">
│ │
│ └── 【由插件自己渲染的 UI】
│ ├── 凭证管理界面
│ ├── 配置面板
│ └── 状态展示
│
├── Tab: API Key(保持不变)
│
└── Tab: Config(保持不变)
```
**插件 UI 加载机制**:
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 插件 UI 加载流程 │
├─────────────────────────────────────────────────────────────────────────┤
│ │
│ 1. ProxyCast 启动 │
│ │ │
│ ▼ │
│ 2. 扫描 ~/.proxycast/plugins/ 目录 │
│ │ │
│ ▼ │
│ 3. 读取每个插件的 plugin.json │
│ ├── 获取 ui.entry 字段(如 "dist/index.js") │
│ └── 获取 ui.surfaces 字段(如 ["oauth_providers"]) │
│ │ │
│ ▼ │
│ 4. 注册到 PluginUIRegistry │
│ │ │
│ ▼ │
│ 5. 用户点击某个插件时 │
│ │ │
│ ▼ │
│ 6. PluginContainer 动态加载插件 UI │
│ ├── 加载插件的 JavaScript/CSS │
│ ├── 创建 iframe 或 Web Component 容器 │
│ └── 传入 ProxyCast SDK (数据库访问、HTTP 客户端等) │
│ │ │
│ ▼ │
│ 7. 插件 UI 渲染在容器中 │
│ │
└─────────────────────────────────────────────────────────────────────────┘
```
**plugin.json UI 配置扩展**:
```json
{
"ui": {
"surfaces": ["oauth_providers"],
"icon": "Cloud",
"title": "Kiro Provider",
"entry": "dist/index.js", // 插件 UI 入口文件
"styles": "dist/styles.css", // 插件样式文件
"default_width": 800,
"default_height": 600,
"permissions": [ // 插件需要的权限
"database:read",
"database:write",
"http:request",
"crypto:encrypt"
]
}
}
```
**ProxyCast SDK(供插件使用)**:
```typescript
// ProxyCast 提供给插件的 SDK
interface ProxyCastPluginSDK {
// 数据库操作
database: {
query<T>(sql: string, params?: any[]): Promise<T[]>;
execute(sql: string, params?: any[]): Promise<void>;
};
// HTTP 客户端
http: {
request(url: string, options?: RequestOptions): Promise<Response>;
};
// 加密存储
crypto: {
encrypt(data: string): Promise<string>;
decrypt(data: string): Promise<string>;
};
// 通知
notification: {
success(message: string): void;
error(message: string): void;
info(message: string): void;
};
// 插件间通信
events: {
emit(event: string, data: any): void;
on(event: string, callback: (data: any) => void): void;
};
}
```
**插件 UI 示例(Kiro Provider)**:
```tsx
// kiro-provider/src/index.tsx
import { ProxyCastPluginSDK } from '@proxycast/plugin-sdk';
interface PluginProps {
sdk: ProxyCastPluginSDK;
pluginId: string;
}
export default function KiroProviderUI({ sdk, pluginId }: PluginProps) {
const [credentials, setCredentials] = useState<Credential[]>([]);
useEffect(() => {
// 使用 ProxyCast SDK 查询数据
sdk.database.query<Credential>(
'SELECT * FROM plugin_credentials WHERE plugin_id = ?',
[pluginId]
).then(setCredentials);
}, []);
return (
<div className="kiro-provider-ui">
<CredentialList credentials={credentials} />
<CredentialForm onSubmit={handleAddCredential} />
<SettingsPanel />
</div>
);
}
```
**ProxyCast 提供的公共功能**(非 UI):
| 功能 | ProxyCast 提供 | 插件实现 |
|------|--------------|---------|
| 插件安装/卸载 | ✅ | - |
| 插件启用/禁用 | ✅ | - |
| 插件更新检查 | ✅ | - |
| UI 容器/挂载点 | ✅ | - |
| 凭证管理 UI | - | ✅ 各插件自己实现 |
| 配置界面 | - | ✅ 各插件自己实现 |
| 状态展示 | - | ✅ 各插件自己实现 |
| Token 刷新逻辑 | - | ✅ 各插件自己实现 |
| 风控策略 | - | ✅ 各插件自己实现 |
**插件管理功能(ProxyCast 实现)**:
| 功能 | 说明 |
|------|------|
| 安装插件 | 从 GitHub Release 下载或本地文件安装 |
| 检查更新 | 比较 GitHub Release 版本,提示更新 |
| 启用/禁用 | 修改 config.json 的 enabled 字段 |
| 卸载插件 | 删除插件文件和凭证数据(需确认)|
| 权限管理 | 控制插件可访问的 SDK 能力 |
**插件安装流程**:
```
1. 用户点击 [+ 安装插件]
│
▼
2. 选择安装方式
├── 从 GitHub: 输入仓库地址 (aiclientproxy/kiro-provider)
├── 从文件: 选择 plugin.json
└── 内置列表: 选择预置插件
│
▼
3. 下载插件
├── 解析 plugin.json
├── 根据平台下载对应二进制
├── 下载 UI 资源 (dist/index.js, dist/styles.css)
├── 校验 checksum
└── 安装到 ~/.proxycast/plugins/{plugin-name}/
│
▼
4. 注册插件
├── 加载到 OAuthProviderRegistry (后端)
├── 加载到 PluginUIRegistry (前端)
├── 创建 config.json
└── 显示在插件列表
│
▼
5. 用户点击插件
└── 加载插件自己的 UI
```
#### 3.5.7 插件化改造目标
```
┌─────────────────────────────────────────────────────────────────────────┐
│ 现有硬编码架构 → 插件化架构 │
├─────────────────────────────────────────────────────────────────────────┤
│ ProviderType 枚举 (11种) → CredentialProviderRegistry (动态注册) │
│ ApiProviderType 枚举 (10种) → 合并到 CredentialProviderPlugin │
│ CredentialData 枚举 (2种) → 插件自定义 CredentialConfig trait │
│ 硬编码配置字段 → 插件返回 JSON Schema │
├─────────────────────────────────────────────────────────────────────────┤
│ 新增 Provider: │
│ - 现有: 修改 2 个枚举 + 多处 match │
│ - 插件化: 实现 trait + 注册,核心代码不变 │
└─────────────────────────────────────────────────────────────────────────┘
```
#### 3.5.8 插件化凭证配置设计
**核心理念**:凭证配置由插件自己定义,通过 JSON Schema 动态生成前端表单
```rust
/// 插件返回 JSON Schema,前端动态渲染表单
pub trait CredentialProviderPlugin {
/// 返回凭证配置的 JSON Schema
fn credential_schema(&self) -> serde_json::Value;
/// 验证并解析凭证配置
fn parse_credential(&self, config: serde_json::Value) -> Result<Box<dyn CredentialConfig>>;
}
```
**示例:现有 ProviderType 迁移为插件**
| 现有类型 | 插件 ID | 凭证类型 | JSON Schema 定义的字段 |
|---------|--------|---------|----------------------|
| Kiro | `kiro` | OAuth | `creds_file_path` |
| ClaudeOAuth | `claude_oauth` | OAuth | `creds_file_path` |
| Claude | `claude_api` | ApiKey | `key`, `base_url` |
| OpenAI | `openai_api` | ApiKey | `key`, `base_url` |
| Codex | `codex` | OAuth | `creds_file_path`, `api_base_url` |
| Gemini | `gemini_oauth` | OAuth | `creds_file_path`, `project_id` |
| GeminiApiKey | `gemini_api` | ApiKey | `key`, `base_url`, `excluded_models` |
| Vertex | `vertex` | ApiKey | `key`, `base_url`, `project`, `location` |
| Antigravity | `antigravity` | OAuth | `creds_file_path`, `project_id` |
| Qwen | `qwen` | OAuth | `creds_file_path` |
| IFlow | `iflow` | OAuth/Cookie | `creds_file_path` |
**示例:现有 ApiProviderType 迁移为插件**
| 现有类型 | 插件 ID | JSON Schema 定义的字段 |
|---------|--------|----------------------|
| AzureOpenai | `azure_openai` | `api_host`, `api_key`, `api_version` |
| AwsBedrock | `aws_bedrock` | `api_key`, `region` |
| Vertexai | `vertexai` | `api_key`, `project`, `location` |
#### 3.5.9 插件实现示例
**Kiro Provider(OAuth + 风控)**:
```rust
pub struct KiroProvider {
credential_pool: CredentialPool,
machine_id_cache: HashMap<String, String>,
}
impl CredentialProviderPlugin for KiroProvider {
fn id(&self) -> &str { "kiro" }
fn display_name(&self) -> &str { "Kiro (AWS CodeWhisperer)" }
fn target_protocol(&self) -> &str { "anthropic" }
fn ui_category(&self) -> CredentialCategory { CredentialCategory::OAuth }
fn credential_schema(&self) -> serde_json::Value {
serde_json::json!({
"type": "object",
"required": ["creds_file_path"],
"properties": {
"creds_file_path": {
"type": "string",
"title": "凭证文件路径",
"description": "AWS SSO 凭证文件"
}
}
})
}
fn model_families(&self) -> Vec<ModelFamily> {
vec![
ModelFamily { name: "opus", pattern: "claude-opus-*" },
ModelFamily { name: "sonnet", pattern: "claude-sonnet-*" },
ModelFamily { name: "haiku", pattern: "claude-*-haiku" },
]
}
async fn apply_risk_control(&self, req: &mut ChatRequest, cred_id: &str) -> Result<()> {
// Kiro 特有风控:Machine ID、系统信息、版本号
let machine_id = self.get_or_generate_machine_id(cred_id);
req.headers.insert("X-Amz-Machine-Id", machine_id);
req.headers.insert("X-Amz-Os", get_os_info());
req.headers.insert("X-Kiro-Version", KIRO_VERSION);
Ok(())
}
}
```
**Azure OpenAI Provider(API Key + 特殊配置)**:
```rust
impl CredentialProviderPlugin for AzureOpenAIProvider {
fn id(&self) -> &str { "azure_openai" }
fn display_name(&self) -> &str { "Azure OpenAI" }
fn target_protocol(&self) -> &str { "openai" }
fn ui_category(&self) -> CredentialCategory { CredentialCategory::ApiKey }
fn credential_schema(&self) -> serde_json::Value {
serde_json::json!({
"type": "object",
"required": ["api_host", "api_key", "api_version"],
"properties": {
"api_host": {
"type": "string",
"title": "Azure Endpoint",
"description": "https://your-resource.openai.azure.com"
},
"api_key": { "type": "string", "title": "API Key" },
"api_version": {
"type": "string",
"title": "API 版本",
"default": "2024-02-01"
}
}
})
}
async fn acquire_credential(&self, model: &str) -> Result<AcquiredCredential> {
let cred = self.credential_pool.acquire().await?;
let config: AzureConfig = serde_json::from_value(cred.config)?;
// Azure 特殊的 URL 构建
let url = format!(
"{}/openai/deployments/{}/chat/completions?api-version={}",
config.api_host, model, config.api_version
);
Ok(AcquiredCredential {
id: cred.id,
base_url: url,
headers: vec![("api-key", config.api_key)],
})
}
}
```
### 3.6 凭证分组(用于 UI)
```rust
/// 凭证分组(用于 UI Tab 展示)
/// 保留枚举,因为 UI Tab 结构相对稳定
#[derive(Clone, Copy, Debug)]
pub enum CredentialCategory {
OAuth, // OAuth 凭证 Tab
ApiKey, // API Key Tab
Other, // 其他配置 Tab(第三方中转、Cookie 等)
}
```
### 3.7 认证类型
```rust
/// 认证类型(内置常见类型,但不限制扩展)
#[derive(Clone, Debug)]
pub enum AuthType {
/// API Key 认证
ApiKey {
header_name: String, // "Authorization", "x-api-key"
prefix: Option<String>, // "Bearer ", "sk-"
},
/// OAuth 2.0
OAuth {
token_url: String,
client_id: Option<String>,
client_secret: Option<String>,
},
/// Cookie 认证
Cookie {
cookie_name: String,
},
/// 自定义头部
CustomHeaders(HashMap<String, String>),
/// 无认证
None,
}
```
### 3.8 插件注册表
```rust
pub struct CredentialProviderRegistry {
providers: HashMap<String, Arc<dyn CredentialProviderPlugin>>,
model_index: HashMap<String, String>, // model -> provider_id
plugins_dir: PathBuf, // ~/.proxycast/plugins/
}
impl CredentialProviderRegistry {
pub fn new(plugins_dir: PathBuf) -> Self {
Self {
providers: HashMap::new(),
model_index: HashMap::new(),
plugins_dir,
}
}
/// 从外部目录加载所有插件
pub async fn load_plugins(&mut self) -> Result<()> {
// 扫描 ~/.proxycast/plugins/ 目录
for entry in fs::read_dir(&self.plugins_dir)? {
let plugin_dir = entry?.path();
if plugin_dir.is_dir() {
self.load_plugin(&plugin_dir).await?;
}
}
Ok(())
}
/// 加载单个插件
async fn load_plugin(&mut self, plugin_dir: &Path) -> Result<()> {
// 1. 读取 plugin.json
let manifest_path = plugin_dir.join("plugin.json");
let manifest: PluginManifest = serde_json::from_str(
&fs::read_to_string(&manifest_path)?
)?;
// 2. 检查 plugin_type 是否为 oauth_provider
if manifest.plugin_type != "oauth_provider" {
return Ok(()); // 跳过非 OAuth Provider 插件
}
// 3. 加载插件二进制
let binary_path = plugin_dir.join("bin").join(&manifest.binary.binary_name);
let plugin = ExternalOAuthPlugin::new(binary_path, manifest)?;
// 4. 注册到注册表
self.register(Arc::new(plugin));
Ok(())
}
/// 注册插件
pub fn register(&mut self, provider: Arc<dyn CredentialProviderPlugin>);
/// 根据模型查找 Provider
pub fn find_by_model(&self, model: &str) -> Option<Arc<dyn CredentialProviderPlugin>>;
/// 获取所有已启用的 Provider
pub fn get_enabled(&self) -> Vec<Arc<dyn CredentialProviderPlugin>>;
/// 获取某个 Provider
pub fn get(&self, id: &str) -> Option<Arc<dyn CredentialProviderPlugin>>;
/// 安装新插件(从 GitHub 或本地文件)
pub async fn install_plugin(&mut self, source: PluginSource) -> Result<()>;
/// 卸载插件
pub async fn uninstall_plugin(&mut self, plugin_id: &str) -> Result<()>;
/// 检查插件更新
pub async fn check_updates(&self) -> Result<Vec<PluginUpdate>>;
}
/// 外部 OAuth 插件(通过二进制调用)
pub struct ExternalOAuthPlugin {
manifest: PluginManifest,
binary_path: PathBuf,
config: PluginConfig,
}
impl CredentialProviderPlugin for ExternalOAuthPlugin {
// 通过调用外部二进制实现 trait 方法
// 使用 JSON-RPC 或 stdin/stdout 通信
}
```
---
## 四、OAuth 凭证插件迁移清单
### 4.1 需要插件化的 ProviderType(仅 OAuth)
根据现有代码 `src-tauri/src/lib.rs`,需要迁移 **8 个 OAuth 类型**:
| 现有枚举值 | 插件 ID | 认证方式 | 目标协议 | 复杂度 |
|-----------|--------|---------|---------|--------|
| `Kiro` | `kiro` | OAuth (creds_file) | anthropic | 🔴 高 |
| `Antigravity` | `antigravity` | OAuth (creds_file, project_id) | 动态* | 🔴 高 |
| `ClaudeOAuth` | `claude_oauth` | OAuth (creds_file) | anthropic | 🟡 中 |
| `Codex` | `codex` | OAuth (creds_file) | openai | 🟡 中 |
| `Gemini` | `gemini_oauth` | OAuth (creds_file, project_id) | gemini | 🟡 中 |
| `Qwen` | `qwen` | OAuth (creds_file) | openai | 🟢 低 |
| `IFlow` | `iflow` | OAuth/Cookie | openai | 🟢 低 |
> *Antigravity 根据模型动态选择协议:`claude-*` → anthropic, 其他 → gemini
### 4.2 不需要插件化的类型(保持现有)
**API Key 类型 - 继续使用现有 `api_key_providers` 系统**:
| 现有枚举值 | 处理方式 | 原因 |
|-----------|---------|------|
| `Claude` | 保持现有 | 简单 API Key,已有系统支持 |
| `OpenAI` | 保持现有 | 简单 API Key,已有系统支持 |
| `GeminiApiKey` | 保持现有 | 简单 API Key,已有系统支持 |
| `Vertex` | 保持现有 | 已有配置字段 (project, location) |
**ApiProviderType - 完全不变**:
- 60+ 系统预设 Provider 继续使用
- 用户自定义 Provider 功能继续使用
- `api_key_providers` + `api_keys` 表结构不变
### 4.3 复杂度分析
**🔴 高复杂度**:
- `kiro`: Machine ID 生成、系统指纹、版本号伪装、特殊头部
- `antigravity`: 动态协议选择、模型别名映射、Safety Settings
**🟡 中复杂度**:
- `claude_oauth`, `codex`, `gemini_oauth`: Token 刷新、凭证文件解析
**🟢 低复杂度**:
- `qwen`, `iflow`: 标准 OAuth 流程
---
## 五、迁移方案
### 5.1 OAuth 凭证代码映射
**迁移策略**:将现有代码迁移到独立的 GitHub 仓库
| 现有模块 | 迁移目标(独立仓库) | 说明 |
|---------|---------------------|------|
| `providers/kiro.rs` | `aiclientproxy/kiro-provider` | 提取风控逻辑 + UI |
| `providers/claude_oauth.rs` | `aiclientproxy/claude-oauth-provider` | Token 刷新 + UI |
| `providers/codex.rs` | `aiclientproxy/codex-provider` | OpenAI OAuth + UI |
| `providers/gemini.rs` | `aiclientproxy/gemini-oauth-provider` | Google OAuth + UI |
| `providers/antigravity.rs` | `aiclientproxy/antigravity-provider` | 动态协议 + UI |
| `providers/qwen.rs` | `aiclientproxy/qwen-provider` | + UI |
| `providers/iflow.rs` | `aiclientproxy/iflow-provider` | + UI |
| `converter/openai_to_cw.rs` | `kiro-provider` 内部 | 协议转换逻辑 |
| `translator/kiro/*` | `kiro-provider` 内部 | 请求/响应转换 |
**每个独立仓库包含**:
- 后端 Rust 代码(凭证管理、协议转换、风控)
- 前端 React UI(凭证列表、配置表单、状态展示)
- plugin.json 元数据
- GitHub Actions 自动构建发布
### 5.2 保持不变的模块
| 现有模块 | 处理方式 | 原因 |
|---------|---------|------|
| `database/dao/api_key_provider.rs` | **不变** | API Key 系统继续使用 |
| `database/system_providers.rs` | **不变** | 60+ 系统预设 |
| `providers/openai_custom.rs` | **不变** | API Key 方式 |
| `credential/pool.rs` | **复用** | 作为插件内部实现 |
| `credential/balancer.rs` | **复用** | |
| `credential/health.rs` | **复用** | |
### 5.3 目录结构
**ProxyCast 主项目结构**(入口 + 注册中心):
```
src-tauri/src/
├── credential/
│ ├── mod.rs
│ ├── types.rs # 保留,通用类型
│ ├── pool.rs # 保留,复用(供插件使用)
│ ├── balancer.rs # 保留,复用
│ ├── health.rs # 保留,复用
│ ├── quota.rs # 保留,复用
│ │
│ ├── plugin.rs # OAuthProviderPlugin trait (新增)
│ └── registry.rs # OAuthProviderRegistry (新增,管理外部插件)
│
├── plugin/
│ ├── mod.rs
│ ├── loader.rs # 插件加载器(从外部目录加载)
│ ├── host.rs # 插件宿主(生命周期管理)
│ └── sdk.rs # ProxyCast SDK(供插件调用)
│
├── database/
│ └── dao/
│ └── api_key_provider.rs # 保持不变!60+ 系统预设
│
├── converter/ # 保留
│ └── ...
│
└── providers/ # 渐进式清理
└── ... # OAuth 相关代码迁移到独立插件项目
```
**前端目录结构**(插件容器):
```
src/
├── components/
│ └── plugin/
│ ├── PluginContainer.tsx # 插件 UI 容器
│ ├── PluginLoader.tsx # 插件 JS/CSS 加载
│ └── PluginRegistry.tsx # 前端插件注册表
│
└── pages/
└── ProviderPool/
└── OAuthPluginTab.tsx # OAuth 插件列表 + 挂载点
```
**外部插件安装目录**:
```
~/.proxycast/plugins/
├── kiro-provider/ # 独立插件项目
│ ├── plugin.json # 插件元数据
│ ├── config.json # 插件配置
│ ├── bin/
│ │ └── kiro-provider-cli # 后端二进制
│ └── dist/
│ ├── index.js # 插件 UI 入口
│ └── styles.css # 插件样式
│
├── antigravity-provider/
│ └── ...
│
├── claude-oauth-provider/
│ └── ...
│
└── ... (其他 OAuth Provider 插件)
```
**插件项目结构**(独立仓库,参考 MachineIdTool):
```
github.com/aiclientproxy/kiro-provider/
├── plugin/
│ ├── plugin.json # 插件元数据
│ └── config.json # 默认配置
├── src-tauri/src/ # 后端 Rust 代码
│ ├── lib.rs
│ ├── commands.rs
│ ├── service.rs
│ └── models.rs
├── src/ # 前端 UI (React)
│ ├── components/
│ │ ├── CredentialList.tsx
│ │ ├── CredentialForm.tsx
│ │ └── SettingsPanel.tsx
│ └── index.tsx
└── .github/
└── workflows/
└── release.yml # 自动构建发布
```
### 5.4 数据库迁移
**新增表**:
```sql
-- 插件配置表
CREATE TABLE credential_provider_plugins (
id TEXT PRIMARY KEY, -- "kiro", "anthropic", etc.
display_name TEXT NOT NULL,
version TEXT NOT NULL,
enabled INTEGER DEFAULT 1,
config TEXT, -- JSON 配置
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL
);
-- 插件凭证关联表(替代现有的 provider_pool_credentials)
CREATE TABLE plugin_credentials (
id TEXT PRIMARY KEY,
plugin_id TEXT NOT NULL,
credential_data TEXT NOT NULL, -- 加密的凭证数据
status TEXT DEFAULT 'active', -- active/cooldown/unhealthy/disabled
stats TEXT, -- JSON 统计数据
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
FOREIGN KEY (plugin_id) REFERENCES credential_provider_plugins(id)
);
```
---
## 六、请求处理流程
### 6.1 新流程
```
客户端请求
│
▼
┌─────────────────────────────────────┐
│ 1. 路由解析 │
│ - 识别目标 Provider │
│ - 识别输入协议 │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 2. 插件注册表查找 │
│ CredentialProviderRegistry │
│ .find_by_model(model) │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 3. Provider 插件处理 │
│ ┌─────────────────────────────┐ │
│ │ acquire_credential() │ │
│ │ transform_request() │ │
│ │ apply_risk_control() │ │
│ └─────────────────────────────┘ │
└─────────────────────────────────────┘
│
▼ (标准协议: Anthropic/OpenAI/Gemini)
│
┌─────────────────────────────────────┐
│ 4. 输出适配(如需要) │
│ - Anthropic API Output │
│ - Claude Code Protocol Output │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 5. HTTP 请求发送 │
└─────────────────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ 6. 响应处理 │
│ - transform_response() │
│ - release_credential() │
│ - 错误处理和重试 │
└─────────────────────────────────────┘
```
### 6.2 处理管道集成
```rust
// 修改 ProviderStep,使用插件注册表
impl ProviderStep {
async fn execute(&self, ctx: &mut RequestContext, payload: &mut Value) -> Result<()> {
// 1. 获取目标 Provider 插件
let provider = self.registry
.find_by_model(&ctx.model)
.ok_or(Error::NoProviderFound)?;
// 2. 获取凭证
let credential = provider.acquire_credential(&ctx.model).await?;
// 3. 转换请求
provider.transform_request(payload).await?;
// 4. 应用风控
provider.apply_risk_control(payload, &credential).await?;
// 5. 发送请求
let result = self.http_client.send(payload, &credential).await;
// 6. 处理结果
match &result {
Ok(resp) => {
provider.transform_response(resp).await?;
provider.release_credential(&credential.id, UsageResult::Success).await;
}
Err(e) => {
let error = provider.parse_error(e.status, &e.body);
provider.release_credential(&credential.id, UsageResult::Error(error)).await;
}
}
result
}
}
```
---
## 七、与现有插件系统集成
### 7.1 关系设计
```
┌─────────────────────────────────────────────────────────────┐
│ PluginManager │
│ (管理通用插件: 请求修改、响应修改、监控等) │
└─────────────────────────────────────────────────────────────┘
│
│ 并行
│
┌─────────────────────────────────────────────────────────────┐
│ CredentialProviderRegistry │
│ (管理凭证 Provider 插件: Kiro, Anthropic, OpenAI 等) │
└─────────────────────────────────────────────────────────────┘
```
### 7.2 共享能力
- **PluginContext**:共享请求上下文
- **超时隔离**:复用 PluginManager 的超时机制
- **UI 系统**:CredentialProviderPlugin 可选实现 PluginUI trait
```rust
// 可选:实现 PluginUI 以提供配置界面
impl PluginUI for KiroProvider {
fn get_surfaces(&self) -> Vec<SurfaceDefinition> {
vec![
SurfaceDefinition {
id: "kiro_credentials",
title: "Kiro 凭证管理",
components: self.build_credential_list_ui(),
}
]
}
}
```
---
## 八、实施计划
### Phase 1: ProxyCast 插件宿主架构
**目标**:在 ProxyCast 中实现插件宿主和 SDK
**后端任务**:
1. 定义 `CredentialProviderPlugin` trait
2. 实现 `CredentialProviderRegistry`(从外部目录加载)
3. 实现 `PluginLoader`(加载插件二进制)
4. 实现 `ProxyCast SDK`(供插件调用的接口)
5. 创建数据库迁移脚本
**前端任务**:
1. 实现 `PluginContainer` 组件(加载插件 UI)
2. 实现 `PluginLoader`(动态加载 JS/CSS)
3. 实现 `OAuthPluginTab`(插件列表 + 挂载点)
4. 定义 `@proxycast/plugin-sdk` TypeScript 接口
**产出**:
- `src-tauri/src/credential/plugin.rs`
- `src-tauri/src/credential/registry.rs`
- `src-tauri/src/plugin/loader.rs`
- `src-tauri/src/plugin/sdk.rs`
- `src/components/plugin/PluginContainer.tsx`
- `src/components/plugin/PluginLoader.tsx`
### Phase 2: 核心 OAuth Provider 独立仓库
**目标**:创建最复杂的 OAuth Provider 独立项目
**优先级**:
1. **kiro-provider** - 最复杂,有风控逻辑、Machine ID、版本伪装
2. **antigravity-provider** - 动态协议选择(claude-* → Anthropic,gemini-* → Gemini)
**每个仓库任务**:
1. 创建 GitHub 仓库 `aiclientproxy/kiro-provider`
2. 搭建项目结构(plugin.json、src-tauri/、src/)
3. 迁移后端代码(凭证管理、协议转换、风控)
4. 实现前端 UI(凭证列表、配置表单、状态展示)
5. 配置 GitHub Actions 自动构建发布
6. 单元测试 + 集成测试
### Phase 3: 其他 OAuth Provider 独立仓库
**目标**:创建剩余 5 个 OAuth Provider 独立项目
**仓库列表**:
1. `aiclientproxy/claude-oauth-provider` - Anthropic OAuth + UI
2. `aiclientproxy/codex-provider` - OpenAI OAuth + UI
3. `aiclientproxy/gemini-oauth-provider` - Google OAuth + UI
4. `aiclientproxy/qwen-provider` - 阿里云 OAuth + UI
5. `aiclientproxy/iflow-provider` - iFlow OAuth/Cookie + UI
### Phase 4: 协议转换层完善
**目标**:确保所有输入/输出协议组合正常工作
**任务**:
1. 完善 `StreamEvent` 统一事件层
2. 实现 `OpenAI SSE Generator` - 输出 OpenAI 格式
3. 实现 `Anthropic SSE Generator` - 输出 Anthropic 格式
4. 支持按端点自动选择输出协议
5. 集成到处理管道
### Phase 5: 插件管理功能
**目标**:实现插件安装、更新、卸载功能
**任务**:
1. 实现从 GitHub Release 安装插件
2. 实现从本地文件安装插件
3. 实现插件更新检查
4. 实现插件卸载(清理文件和数据)
5. 实现插件启用/禁用
6. 实现插件权限管理
### Phase 6: 清理和测试
**任务**:
1. 删除 ProxyCast 中旧的 `providers/` OAuth 代码
2. 简化 `converter/` 模块
3. 端到端测试(安装插件 → 配置凭证 → 调用 API)
4. 文档更新
---
## 九、风险和缓解
| 风险 | 影响 | 缓解措施 |
|------|------|---------|
| 迁移过程中服务中断 | 高 | 保留旧代码,通过 feature flag 切换 |
| 某个 Provider 转换出错 | 中 | 每个 Provider 独立测试,灰度发布 |
| 性能下降 | 中 | 基准测试,优化热路径 |
| 数据库迁移失败 | 高 | 备份现有数据,提供回滚脚本 |
---
## 十、成功标准
### 10.1 OAuth 插件化
1. [ ] 所有 7 个 OAuth Provider 迁移为独立 GitHub 仓库
2. [ ] 每个插件包含完整的后端代码和前端 UI
3. [ ] 新增 OAuth Provider 只需创建新仓库,无需修改 ProxyCast 核心代码
4. [ ] 单个 OAuth Provider 可独立更新(通过 GitHub Release)
5. [ ] 插件 UI 在 ProxyCast 中正确加载和渲染
### 10.2 API Key 系统
1. [ ] API Key 系统保持现有设计不变
2. [ ] 60+ 系统预设 Provider 继续正常工作
3. [ ] 用户自定义 Provider 功能正常
### 10.3 协议转换
1. [ ] Kiro: Anthropic → CodeWhisperer → Anthropic SSE 转换正常
2. [ ] Antigravity (claude-*): Anthropic → Antigravity → Anthropic SSE 转换正常
3. [ ] Antigravity (gemini-*): Anthropic → Antigravity → Gemini 协议转换正常
4. [ ] Claude OAuth: Anthropic → Anthropic 直通正常
5. [ ] 所有 OAuth Provider 的协议转换测试通过
### 10.4 质量保证
1. [ ] 现有功能 100% 兼容
2. [ ] 处理性能无明显下降(<10%)
3. [ ] 完整的单元测试和集成测试
---
## 附录
### A. ProxyCast 关键文件路径
**后端(插件宿主)**:
| 文件 | 用途 |
|------|------|
| `/src-tauri/src/credential/plugin.rs` | CredentialProviderPlugin trait |
| `/src-tauri/src/credential/registry.rs` | CredentialProviderRegistry(加载外部插件)|
| `/src-tauri/src/plugin/loader.rs` | 插件加载器 |
| `/src-tauri/src/plugin/host.rs` | 插件宿主(生命周期管理)|
| `/src-tauri/src/plugin/sdk.rs` | ProxyCast SDK(供插件调用)|
**前端(插件容器)**:
| 文件 | 用途 |
|------|------|
| `/src/components/plugin/PluginContainer.tsx` | 插件 UI 容器 |
| `/src/components/plugin/PluginLoader.tsx` | 动态加载插件 JS/CSS |
| `/src/components/plugin/PluginRegistry.tsx` | 前端插件注册表 |
| `/src/pages/ProviderPool/OAuthPluginTab.tsx` | OAuth 插件列表 + 挂载点 |
### B. 外部插件仓库
| 仓库 | 用途 |
|------|------|
| `aiclientproxy/kiro-provider` | Kiro OAuth Provider(后端 + UI)|
| `aiclientproxy/antigravity-provider` | Antigravity OAuth Provider(后端 + UI)|
| `aiclientproxy/claude-oauth-provider` | Claude OAuth Provider(后端 + UI)|
| `aiclientproxy/codex-provider` | Codex OAuth Provider(后端 + UI)|
| `aiclientproxy/gemini-oauth-provider` | Gemini OAuth Provider(后端 + UI)|
| `aiclientproxy/qwen-provider` | Qwen OAuth Provider(后端 + UI)|
| `aiclientproxy/iflow-provider` | iFlow OAuth Provider(后端 + UI)|
### C. 参考现有代码
| 功能 | 现有文件(迁移到独立仓库)|
|------|---------|
| Kiro 风控逻辑 | `/src-tauri/src/providers/kiro.rs` → `kiro-provider` |
| 协议转换 | `/src-tauri/src/converter/openai_to_cw.rs` → `kiro-provider` |
| 凭证池 | `/src-tauri/src/credential/pool.rs`(保留,供插件复用)|
| 负载均衡 | `/src-tauri/src/credential/balancer.rs`(保留,供插件复用)|
| 插件 trait | `/src-tauri/src/plugin/types.rs`(参考)|
### D. 插件安装目录
```
~/.proxycast/plugins/
├── kiro-provider/
│ ├── plugin.json
│ ├── config.json
│ ├── bin/kiro-provider-cli
│ └── dist/
│ ├── index.js
│ └── styles.css
├── antigravity-provider/
│ └── ...
└── ...
```