メインコンテンツまでスキップ

ProviderManager 内部机制

ProviderManager 负责 LLM 提供商的生命周期管理,包括创建、读取、更新、删除操作,以及预设模板系统和持久化存储。


文件位置

文件路径
ProviderManagerpackages/desktop/app/main/services/capabilities/llm/config-service/ProviderManager.ts
LLMConfigServicepackages/desktop/app/main/services/capabilities/llm/config-service/LLMConfigService.ts
Provider 模板packages/desktop/app/shared/llm-config.ts (PROVIDER_TEMPLATES)
Provider 预设packages/desktop/app/shared/provider-presets.ts
Schemaspackages/desktop/app/main/services/capabilities/llm/config-service/schemas.ts
IPC Routerpackages/desktop/app/main/services/routers/llm/ProviderRouter.ts

架构上下文

委托模式

LLMConfigService 采用委托模式(Delegate Pattern),将职责分散到四个专职管理器。ProviderManager 通过 ProviderManagerDelegate 接口与宿主通信,避免循环依赖。

graph TB
subgraph LLMConfigService
direction TB
PM[ProviderManager]
MDM[ModelDiscoveryManager]
CIOM[ConfigIOManager]
AMM[AgentModelsManager]
end

PM -->|Delegate| LLMConfigService
MDM -->|Delegate| LLMConfigService
CIOM -->|Delegate| LLMConfigService
AMM -->|Delegate| LLMConfigService

subgraph 存储
SQLite[(SQLite 主存储)]
JSON[(JSON 回退)]
end

PM --> SQLite
PM --> JSON

subgraph 缓存
PI[Provider Index<br/>Map 索引]
end

PM --> PI

Delegate 接口

interface ProviderManagerDelegate {
loadConfig(): Promise<LLMsConfig>; // 加载 JSON 配置
saveConfig(): Promise<void>; // 保存 JSON 配置
getDb(): DbClient | null; // 获取数据库实例
waitForMigration(): Promise<void>; // 等待数据迁移完成
useSQLite(): boolean; // 是否使用 SQLite
isMigrated(): Promise<boolean>; // 是否已迁移
invalidateProviderIndex(): void; // 使 Provider 索引失效
invalidateConfig(): void; // 使配置缓存失效
buildProviderIndex(providers: LLMProvider[]): void; // 构建索引
getProviderFromIndex(id: string): LLMProvider | undefined; // 索引查询
hasProviderIndex(): boolean; // 索引是否存在
}

数据结构

LLMProvider 完整定义

interface LLMProvider {
id: string; // UUID 或预设 ID(如 'openai', 'anthropic')
name: string; // 显示名称
apiFormat?: ApiFormat; // 首选 API 格式字段
chatApiFormat?: ApiFormat; // 遗留格式字段(向后兼容)
apiType?: string; // 遗留类型字段
api_base_url?: string; // API Base URL
api_key: string; // 单密钥值(遗留,新系统使用 ApiKeyPool)
models: string[]; // 支持的模型 ID 列表
modelConfigs?: ModelConfig[]; // 每模型详细配置
modelGroups?: ModelGroup[]; // 模型分组(带分类信息)
modelsEndpoint?: string; // 模型发现 API 端点
enabled: boolean; // 是否启用
transformer?: TransformerConfig; // 请求/响应转换链
icon?: string; // 图标路径或 emoji
website?: string; // 官网链接
docsUrl?: string; // API 文档链接
defaultSettings?: CompletionSettings; // 默认补全设置
codingPlan?: CodingPlanConfig; // Coding Plan 专用配置
isSystem?: boolean; // 系统内置标记(不可删除)
presetId?: string; // 来源预设模板 ID
headers?: Record<string, string>; // 自定义 HTTP 头
createdAt: string; // ISO 时间戳
updatedAt: string; // ISO 时间戳
}

interface ModelConfig {
id: string;
name: string;
enabled: boolean;
category?: 'chat' | 'reasoning' | 'image' | 'video' | 'embedding' | 'code';
group?: string;
contextLength?: number;
maxTokens?: number;
completionSettings?: CompletionSettings;
vision?: boolean; // 视觉能力
functionCall?: boolean; // 函数调用能力
reasoning?: boolean; // 推理能力
webSearch?: boolean; // 搜索能力
}

算法/逻辑说明

Provider CRUD 操作流程

添加提供商(SQLite 路径)

1. 接收 LLMProviderInput
2. 生成 UUID 作为 provider.id
3. 填充默认值(enabled=false, defaultSettings=DEFAULT_COMPLETION_SETTINGS)
4. Zod Schema 验证(LLMProviderSchema.parse)
5. 唯一性检查:查询 SQLite 确认 ID 不存在
6. 写入 SQLite: db.llmProvidersInsert(provider)
7. 同步模型列表到 llm_models 表
8. 同步模型分组到 llm_model_groups 表
9. 使 Provider Index 失效: invalidateProviderIndex()
10. 返回 { success: true, provider }

更新提供商

1. 接收 { id, ...updates }
2. 从 SQLite 读取现有 provider
3. 合并更新字段(浅合并)
4. 更新 updatedAt 时间戳
5. 写入 SQLite: db.llmProvidersUpdate(id, updates)
6. 如果 models 或 modelConfigs 变更 → 同步模型表
7. 使 Provider Index 失效
8. 返回 { success: true, provider }

删除提供商

1. 检查 isSystem 标记 → 系统内置不可删除
2. 从 SQLite 删除: db.llmProvidersDelete(id)
3. 级联删除关联模型和分组
4. 使 Provider Index 失效
5. 返回 { success: true }

Provider Index(O(1) 查找)

graph LR
subgraph 首次查询
Load[loadConfig 或 SQLite 查询] --> Build[构建 Map]
Build --> Cache[providerIndex: Map&lt;id, Provider&gt;]
end

subgraph 后续查询
Cache --> Lookup[O(1) Map.get]
end

subgraph 变更操作
CRUD[add/update/delete] --> Invalidate[providerIndex = null]
Invalidate --> Load
end

行为规则

操作对索引的影响
getProvider(id)命中索引 → 返回;未命中 → 重建索引后查找
getProviders()返回完整列表并重建索引
addProvider使索引失效
updateProvider使索引失效
deleteProvider使索引失效
toggleProvider使索引失效

双存储机制(SQLite + JSON 回退)

flowchart TD
Start[启动] --> CheckDB{DbClient 可用?}
CheckDB -->|是| Migrate[ensureMigrated]
CheckDB -->|否| JSONMode[JSON 模式]

Migrate --> CheckMigrated{已迁移?}
CheckMigrated -->|是| SQLiteMode[SQLite 模式]
CheckMigrated -->|否| DoMigrate[JSON → SQLite 迁移]
DoMigrate --> SQLiteMode

subgraph SQLite模式
SQLiteMode --> SQLiteRead[db.llmProvidersGetAll]
SQLiteMode --> SQLiteWrite[db.llmProvidersInsert/Update/Delete]
end

subgraph JSON模式
JSONMode --> JSONRead[fs.readFile + JSON.parse]
JSONMode --> JSONWrite[JSON.stringify + fs.writeFile]
end

迁移过程

  1. 启动时检查 SQLite 中 llm_config_migrated 标记
  2. 如果未迁移,读取 llms-config.json
  3. 逐条将 Provider 数据写入 SQLite
  4. 同步 models、modelGroups 到对应表
  5. 设置迁移完成标记
  6. JSON 文件保留作为备份(不再作为主存储)

预设/模板系统

默认提供商列表

系统内置以下提供商模板(按 getDefaultProviders() 中的顺序):

序号ID名称API Format
1deepseekDeepSeekopenai
2openrouterOpenRouteropenai
3siliconSiliconFlowopenai
4openaiOpenAIopenai
5anthropicAnthropicanthropic
6geminiGoogle Geminigoogle
7zhipu智谱 AIopenai
8moonshotMoonshotopenai
9dashscope通义千问openai
10ollamaOllamaopenai
11groqGroqopenai
12claude-codeClaude Codeanthropic

从模板创建提供商

createProviderFromTemplate(template):
1. 复制 template 的所有配置字段
2. 设置 api_key = ''(用户需要自行配置)
3. 设置 enabled = false
4. 填充 defaultSettings(使用 template.defaultSettings 或 DEFAULT_COMPLETION_SETTINGS)
5. 生成 createdAt / updatedAt 时间戳
6. 返回 LLMProvider 实例

预设模板(Provider Presets)

预设模板来自 provider-presets.ts,是比内置模板更丰富的配置,通常针对中国云厂商。预设模板包含:

interface PresetProviderTemplate {
id: string; // 预设 ID
name: string; // 显示名称
apiFormat: ApiFormat; // API 格式
baseUrl: string; // Base URL
models: string[]; // 模型列表
modelConfigs: ModelConfig[]; // 模型详细配置
features: string[]; // 特性标签
// ... 更多字段
}

从预设添加提供商

addFromPreset(presetId, apiKey?):
1. 通过 getPresetById(presetId) 查找预设
2. 将预设转为 ProviderTemplate
3. 调用 createProviderFromTemplate(template)
4. 如果提供 apiKey → 设置密钥
5. 调用 addProvider() 持久化
6. 返回新提供商

IPC 集成表

IPC 通道方向参数返回值Zod 验证
llmConfig:getProvidersR → MLLMProvider[]
llmConfig:getProviderR → M{ id: string }LLMProvider | nullllmProviderIdSchema
llmConfig:addProviderR → MLLMProviderInputLLMProviderResultllmProviderCreateSchema
llmConfig:updateProviderR → M{ id, ...fields }LLMProviderResultllmProviderUpdateSchema
llmConfig:deleteProviderR → M{ id: string }{ success, message? }llmProviderIdSchema
llmConfig:toggleProviderR → M{ id, enabled }LLMProviderResultid + boolean
llmConfig:discoverModelsR → M{ id, options? }ProviderModelDiscoveryResultmodelDiscoverOptionsSchema
llmConfig:getProviderPresetsR → MPresetProviderTemplate[]
llmConfig:addFromPresetR → M{ presetId, apiKey? }LLMProviderResultstring + string?
llmConfig:exportConfigR → MLLMsConfig
llmConfig:importConfigR → M配置对象{ success, message? }Zod object

扩展点

如何添加新的内置提供商

  1. packages/desktop/app/shared/llm-config.tsPROVIDER_TEMPLATES 数组中添加 ProviderTemplate
  2. ProviderManager.getDefaultProviders()defaultTemplateIds 数组中添加新 ID
  3. 如果需要特殊 URL 构建逻辑,在 url-builder.ts 中添加处理
  4. 如果需要搜索支持,在 provider-presets.tsPROVIDER_SEARCH_CONFIGS 中添加条目
  5. 如果需要 Coding Plan 支持,在 CODING_PLAN_URL_PRESETS 中添加条目
  6. 如果需要 Follow-Provider 支持,在 PROVIDER_MODEL_MAPPINGS 中添加条目

如何添加新的预设模板

  1. provider-presets.tsPROVIDER_PRESETS 数组中添加 PresetProviderTemplate
  2. 填充完整的模型配置(modelConfigs、contextLength、maxTokens、capabilities)
  3. 前端会自动在预设列表中显示新模板

关联文件表

文件关联方式
capabilities/llm/config-service/LLMConfigService.ts宿主服务,初始化 ProviderManager 并提供 Delegate
capabilities/llm/config-service/schemas.tsZod 验证 Schema(LLMProviderSchema 等)
routers/llm/ProviderRouter.tsIPC 层,接收前端请求并调用 LLMConfigService
routers/llm/schemas.tsIPC 参数验证 Schema
shared/llm-config.ts共享类型定义和内置模板
shared/provider-presets.ts预设模板、搜索配置、Coding Plan URL
workers/DbClient.tsSQLite 数据库操作
workers/db/apiKeys.tsAPI 密钥表 CRUD
capabilities/llm/completion/CompletionService.ts消费 Provider 配置进行 API 调用