ProviderManager の内部構造
ProviderManager は、LLM プロバイダーのライフサイクルを処理します。これには作成、読み取り、更新、削除の各操作に加えて、プリセットテンプレートシステムと永続ストレージが含まれます。
ファイルの場所
| ファイル | パス |
|---|---|
| ProviderManager | packages/desktop/app/main/services/capabilities/llm/config-service/ProviderManager.ts |
| LLMConfigService | packages/desktop/app/main/services/capabilities/llm/config-service/LLMConfigService.ts |
| プロバイダーテンプレート | packages/desktop/app/shared/llm-config.ts (PROVIDER_TEMPLATES) |
| プロバイダープリセット | packages/desktop/app/shared/provider-presets.ts |
| スキーマ | packages/desktop/app/main/services/capabilities/llm/config-service/schemas.ts |
| IPC ルーター | packages/desktop/app/main/services/routers/llm/ProviderRouter.ts |
アーキテクチャ上の位置づけ
デリゲートパターン
LLMConfigService はデリゲートパターンを使用し、責務を 4 つの専用マネージャーに分散します。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 Storage ["Storage"]
SQLite[(SQLite primary storage)]
JSON[(JSON fallback)]
end
PM --> SQLite
PM --> JSON
subgraph Cache ["Cache"]
PI[Provider Index<br/>Map index]
end
PM --> PI
デリゲートインターフェース
interface ProviderManagerDelegate {
loadConfig(): Promise<LLMsConfig>; // Load JSON config
saveConfig(): Promise<void>; // Save JSON config
getDb(): DbClient | null; // Get database instance
waitForMigration(): Promise<void>; // Wait for data migration to complete
useSQLite(): boolean; // Whether to use SQLite
isMigrated(): Promise<boolean>; // Whether migration is complete
invalidateProviderIndex(): void; // Invalidate the Provider index
invalidateConfig(): void; // Invalidate config cache
buildProviderIndex(providers: LLMProvider[]): void; // Build the index
getProviderFromIndex(id: string): LLMProvider | undefined; // Query the index
hasProviderIndex(): boolean; // Whether the index exists
}
データ構造
LLMProvider の完全な定義
interface LLMProvider {
id: string; // UUID or preset ID (e.g. 'openai', 'anthropic')
name: string; // Display name
apiFormat?: ApiFormat; // Preferred API format field
chatApiFormat?: ApiFormat; // Legacy format field (backward compatibility)
apiType?: string; // Legacy type field
api_base_url?: string; // API Base URL
api_key: string; // Single key value (legacy; new system uses ApiKeyPool)
models: string[]; // List of supported model IDs
modelConfigs?: ModelConfig[]; // Per-model detailed config
modelGroups?: ModelGroup[]; // Model groups (with category info)
modelsEndpoint?: string; // Model discovery API endpoint
enabled: boolean; // Whether enabled
transformer?: TransformerConfig; // Request/response transformer chain
icon?: string; // Icon path or emoji
website?: string; // Official website link
docsUrl?: string; // API documentation link
defaultSettings?: CompletionSettings; // Default completion settings
codingPlan?: CodingPlanConfig; // Coding Plan dedicated config
isSystem?: boolean; // System built-in flag (cannot be deleted)
presetId?: string; // Source preset template ID
headers?: Record<string, string>; // Custom HTTP headers
createdAt: string; // ISO timestamp
updatedAt: string; // ISO timestamp
}
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; // Vision capability
functionCall?: boolean; // Function calling capability
reasoning?: boolean; // Reasoning capability
webSearch?: boolean; // Web search capability
}
アルゴリズムとロジック
プロバイダー CRUD 操作フロー
プロバイダーの追加(SQLite パス)
1. Receive LLMProviderInput
2. Generate UUID as provider.id
3. Fill defaults (enabled=false, defaultSettings=DEFAULT_COMPLETION_SETTINGS)
4. Validate with Zod Schema (LLMProviderSchema.parse)
5. Uniqueness check: query SQLite to confirm ID does not exist
6. Write to SQLite: db.llmProvidersInsert(provider)
7. Sync model list to llm_models table
8. Sync model groups to llm_model_groups table
9. Invalidate Provider Index: invalidateProviderIndex()
10. Return { success: true, provider }
プロバイダーの更新
1. Receive { id, ...updates }
2. Read existing provider from SQLite
3. Merge updated fields (shallow merge)
4. Update updatedAt timestamp
5. Write to SQLite: db.llmProvidersUpdate(id, updates)
6. If models or modelConfigs changed → sync model tables
7. Invalidate Provider Index
8. Return { success: true, provider }
プロバイダーの削除
1. Check isSystem flag → system built-ins cannot be deleted
2. Delete from SQLite: db.llmProvidersDelete(id)
3. Cascade delete associated models and groups
4. Invalidate Provider Index
5. Return { success: true }
プロバイダーインデックス(O(1) ルックアップ)
graph LR
subgraph FirstQuery ["First Query"]
Load[loadConfig or SQLite query] --> Build[Build Map]
Build --> Cache[providerIndex: Map<id, Provider>]
end
subgraph SubsequentQuery ["Subsequent Queries"]
Cache --> Lookup[O(1) Map.get]
end
subgraph Mutations ["Mutations"]
CRUD[add/update/delete] --> Invalidate[providerIndex = null]
Invalidate --> Load
end
動作ルール:
| 操作 | インデックスへの影響 |
|---|---|
getProvider(id) | インデックスヒット → 返す。ミス → インデックスを再構築してから検索 |
getProviders() | 全リストを返し、インデックスを再構築 |
addProvider | インデックスを無効化 |
updateProvider | インデックスを無効化 |
deleteProvider | インデックスを無効化 |
toggleProvider | インデックスを無効化 |
デュアルストレージ機構(SQLite + JSON フォールバック)
flowchart TD
Start[Startup] --> CheckDB{DbClient available?}
CheckDB -->|Yes| Migrate[ensureMigrated]
CheckDB -->|No| JSONMode[JSON mode]
Migrate --> CheckMigrated{Already migrated?}
CheckMigrated -->|Yes| SQLiteMode[SQLite mode]
CheckMigrated -->|No| DoMigrate[JSON → SQLite migration]
DoMigrate --> SQLiteMode
subgraph SQLiteMode ["SQLite Mode"]
SQLiteMode --> SQLiteRead[db.llmProvidersGetAll]
SQLiteMode --> SQLiteWrite[db.llmProvidersInsert/Update/Delete]
end
subgraph JSONMode ["JSON Mode"]
JSONMode --> JSONRead[fs.readFile + JSON.parse]
JSONMode --> JSONWrite[JSON.stringify + fs.writeFile]
end
マイグレーション処理:
- 起動時に、SQLite 内の
llm_config_migratedフラグを確認する - まだマイグレーションされていない場合は、
llms-config.jsonを読み取る - 各プロバイダーレコードを 1 件ずつ SQLite に書き込む
- モデルと modelGroups を対応するテーブルに同期する
- マイグレーション完了フラグを設定する
- JSON ファイルはバックアップとして保持される(以後、主ストレージではない)
プリセット / テンプレートシステム
デフォルトプロバイダー一覧
システムには、以下の組み込みプロバイダーテンプレートが含まれています(getDefaultProviders() で定義された順序):
| # | ID | 名前 | API フォーマット |
|---|---|---|---|
| 1 | deepseek | DeepSeek | openai |
| 2 | openrouter | OpenRouter | openai |
| 3 | silicon | SiliconFlow | openai |
| 4 | openai | OpenAI | openai |
| 5 | anthropic | Anthropic | anthropic |
| 6 | gemini | Google Gemini | |
| 7 | zhipu | Zhipu AI | openai |
| 8 | moonshot | Moonshot | openai |
| 9 | dashscope | Tongyi Qianwen | openai |
| 10 | ollama | Ollama | openai |
| 11 | groq | Groq | openai |
| 12 | claude-code | Claude Code | anthropic |
テンプレートからプロバイダーを作成する
createProviderFromTemplate(template):
1. Copy all config fields from template
2. Set api_key = '' (user must configure this)
3. Set enabled = false
4. Fill defaultSettings (use template.defaultSettings or DEFAULT_COMPLETION_SETTINGS)
5. Generate createdAt / updatedAt timestamps
6. Return LLMProvider instance
プロバイダープリセット
プリセットテンプレートは provider-presets.ts から提供され、組み込みテンプレートよりも豊富な設定を提供します。通常は中国のクラウドベンダーを対象としています。プリセットテンプレートには以下が含まれます:
interface PresetProviderTemplate {
id: string; // Preset ID
name: string; // Display name
apiFormat: ApiFormat; // API format
baseUrl: string; // Base URL
models: string[]; // Model list
modelConfigs: ModelConfig[]; // Per-model detailed config
features: string[]; // Feature tags
// ... more fields
}
プリセットからプロバイダーを追加する:
addFromPreset(presetId, apiKey?):
1. Find preset via getPresetById(presetId)
2. Convert preset to ProviderTemplate
3. Call createProviderFromTemplate(template)
4. If apiKey provided → set the key
5. Call addProvider() to persist
6. Return new provider
IPC 連携テーブル
| IPC チャンネル | 方向 | パラメーター | 戻り値 | Zod バリデーション |
|---|---|---|---|---|
llmConfig:getProviders | R → M | なし | LLMProvider[] | なし |
llmConfig:getProvider | R → M | { id: string } | LLMProvider | null | llmProviderIdSchema |
llmConfig:addProvider | R → M | LLMProviderInput | LLMProviderResult | llmProviderCreateSchema |
llmConfig:updateProvider | R → M | { id, ...fields } | LLMProviderResult | llmProviderUpdateSchema |
llmConfig:deleteProvider | R → M | { id: string } | { success, message? } | llmProviderIdSchema |
llmConfig:toggleProvider | R → M | { id, enabled } | LLMProviderResult | id + boolean |
llmConfig:discoverModels | R → M | { id, options? } | ProviderModelDiscoveryResult | modelDiscoverOptionsSchema |
llmConfig:getProviderPresets | R → M | なし | PresetProviderTemplate[] | なし |
llmConfig:addFromPreset | R → M | { presetId, apiKey? } | LLMProviderResult | string + string? |
llmConfig:exportConfig | R → M | なし | LLMsConfig | なし |
llmConfig:importConfig | R → M | 設定オブジェクト | { success, message? } | Zod オブジェクト |
拡張ポイント
新しい組み込みプロバイダーを追加する
packages/desktop/app/shared/llm-config.tsのPROVIDER_TEMPLATES配列にProviderTemplateを追加するProviderManager.getDefaultProviders()のdefaultTemplateIds配列に新しい ID を追加する- 特別な URL 構築ロジックが必要な場合は、
url-builder.tsに処理を追加する - 検索サポートが必要な場合は、
provider-presets.tsのPROVIDER_SEARCH_CONFIGSにエントリを追加する - Coding Plan サポートが必要な場合は、
CODING_PLAN_URL_PRESETSにエントリを追加する - Follow-Provider サポートが必要な場合は、
PROVIDER_MODEL_MAPPINGSにエントリを追加する
新しいプリセットテンプレートを追加する
provider-presets.tsのPROVIDER_PRESETS配列にPresetProviderTemplateを追加する- 完全なモデル設定(modelConfigs、contextLength、maxTokens、capabilities)を入力する
- フロントエンドは、新しいテンプレートをプリセット一覧に自動表示する
関連ファイル
| ファイル | 関係 |
|---|---|
capabilities/llm/config-service/LLMConfigService.ts | ProviderManager を初期化し、デリゲートを提供するホストサービス |
capabilities/llm/config-service/schemas.ts | Zod バリデーションスキーマ(LLMProviderSchema など) |
routers/llm/ProviderRouter.ts | IPC レイヤー: フロントエンドのリクエストを受け取り、LLMConfigService を呼び出す |
routers/llm/schemas.ts | IPC パラメーターバリデーションスキーマ |
shared/llm-config.ts | 共有型定義と組み込みテンプレート |
shared/provider-presets.ts | プリセットテンプレート、検索設定、Coding Plan URL |
workers/DbClient.ts | SQLite データベース操作 |
workers/db/apiKeys.ts | API キーテーブル CRUD |
capabilities/llm/completion/CompletionService.ts | API 呼び出しでプロバイダー設定を利用する |