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

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
プロバイダーテンプレート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&lt;id, Provider&gt;]
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

マイグレーション処理:

  1. 起動時に、SQLite 内の llm_config_migrated フラグを確認する
  2. まだマイグレーションされていない場合は、llms-config.json を読み取る
  3. 各プロバイダーレコードを 1 件ずつ SQLite に書き込む
  4. モデルと modelGroups を対応するテーブルに同期する
  5. マイグレーション完了フラグを設定する
  6. JSON ファイルはバックアップとして保持される(以後、主ストレージではない)

プリセット / テンプレートシステム

デフォルトプロバイダー一覧

システムには、以下の組み込みプロバイダーテンプレートが含まれています(getDefaultProviders() で定義された順序):

#ID名前API フォーマット
1deepseekDeepSeekopenai
2openrouterOpenRouteropenai
3siliconSiliconFlowopenai
4openaiOpenAIopenai
5anthropicAnthropicanthropic
6geminiGoogle Geminigoogle
7zhipuZhipu AIopenai
8moonshotMoonshotopenai
9dashscopeTongyi Qianwenopenai
10ollamaOllamaopenai
11groqGroqopenai
12claude-codeClaude Codeanthropic

テンプレートからプロバイダーを作成する

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:getProvidersR → MなしLLMProvider[]なし
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 → MなしPresetProviderTemplate[]なし
llmConfig:addFromPresetR → M{ presetId, apiKey? }LLMProviderResultstring + string?
llmConfig:exportConfigR → MなしLLMsConfigなし
llmConfig:importConfigR → M設定オブジェクト{ success, message? }Zod オブジェクト

拡張ポイント

新しい組み込みプロバイダーを追加する

  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.tsProviderManager を初期化し、デリゲートを提供するホストサービス
capabilities/llm/config-service/schemas.tsZod バリデーションスキーマ(LLMProviderSchema など)
routers/llm/ProviderRouter.tsIPC レイヤー: フロントエンドのリクエストを受け取り、LLMConfigService を呼び出す
routers/llm/schemas.tsIPC パラメーターバリデーションスキーマ
shared/llm-config.ts共有型定義と組み込みテンプレート
shared/provider-presets.tsプリセットテンプレート、検索設定、Coding Plan URL
workers/DbClient.tsSQLite データベース操作
workers/db/apiKeys.tsAPI キーテーブル CRUD
capabilities/llm/completion/CompletionService.tsAPI 呼び出しでプロバイダー設定を利用する