LLM プロバイダーの拡張方法
このドキュメントでは、新しいプリセットテンプレートの追加、新しい API 形式の追加、プロバイダーへの検索機能設定という 3 つの一般的な拡張シナリオについて、手順を追って説明します。
ファイルの場所
| ファイル | パス |
|---|---|
| Provider テンプレート | packages/desktop/app/shared/llm-config.ts |
| Provider プリセット | packages/desktop/app/shared/provider-presets.ts |
| ProviderManager | packages/desktop/app/main/services/capabilities/llm/config-service/ProviderManager.ts |
| URL Builder | packages/desktop/app/main/services/capabilities/llm/completion/url-builder.ts |
| Header Builder | packages/desktop/app/main/services/capabilities/llm/completion/header-builder.ts |
| Message Converter | packages/desktop/app/main/services/capabilities/llm/completion/message-converter.ts |
| DirectApiHandler | packages/desktop/app/main/services/capabilities/llm/completion/DirectApiHandler.ts |
| StreamHandler | packages/desktop/app/main/services/capabilities/llm/completion/StreamHandler.ts |
| CompletionService | packages/desktop/app/main/services/capabilities/llm/completion/CompletionService.ts |
| Types | packages/desktop/app/main/services/capabilities/llm/completion/types.ts |
| ProviderSearchInjector | packages/desktop/app/main/services/capabilities/llm/completion/ProviderSearchInjector.ts |
| NativeSearchInjector | packages/desktop/app/main/services/capabilities/llm/completion/NativeSearchInjector.ts |
| IPC Router Schemas | packages/desktop/app/main/services/routers/llm/schemas.ts |
| フロントエンド i18n | packages/renderer/src/locales/{en,zh,ja}/providers/llm.json |
アーキテクチャ上の位置づけ
graph TB
subgraph ExtensionPoints ["Extension Points"]
direction TB
A["(1) New preset template<br/>provider-presets.ts"]
B["(2) New API format<br/>types + handlers"]
C["(3) Search config<br/>PROVIDER_SEARCH_CONFIGS"]
end
subgraph AffectedLayers ["Affected Layers"]
direction TB
Shared[shared layer<br/>types + presets]
Completion[completion layer<br/>Handlers + URL]
Config[config-service layer<br/>ProviderManager]
Router[router layer<br/>IPC Schemas]
Frontend[renderer layer<br/>UI + i18n]
end
A --> Shared
A --> Config
A --> Frontend
B --> Shared
B --> Completion
B --> Router
C --> Shared
C --> Completion
シナリオ 1: 新しいプリセットテンプレートを追加する
新しい LLM プロバイダー(新しいクラウドベンダーや海外プロバイダーなど)をサポートする必要がある場合、ユーザーが UI からワンクリックで追加できるように、プリセットテンプレートを作成します。
データ構造
// Full interface for a preset template
interface PresetProviderTemplate {
id: string; // Unique ID (lowercase, e.g. 'minimax')
name: string; // Display name
apiFormat: ApiFormat; // API format
baseUrl: string; // Base URL
modelsEndpoint?: string; // Model discovery endpoint
models: string[]; // List of supported model IDs
modelConfigs: ModelConfig[]; // Per-model detailed config
features: string[]; // Feature tags
icon?: string; // Icon
website?: string; // Official website
docsUrl?: string; // API documentation
defaultSettings?: CompletionSettings; // Default completion parameters
}
手順
ステップ 1: プリセットテンプレートを定義する
packages/desktop/app/shared/provider-presets.ts の PROVIDER_PRESETS 配列にエントリを追加します。
// Pseudocode — adding a new preset
{
id: 'newprovider',
name: 'NewProvider AI',
apiFormat: 'openai', // Most Chinese vendors are OpenAI-compatible
baseUrl: 'https://api.newprovider.com/v1',
modelsEndpoint: '/models',
models: ['np-large', 'np-lite', 'np-vision'],
modelConfigs: [
{
id: 'np-large',
name: 'NP Large',
enabled: true,
category: 'chat',
contextLength: 128000,
maxTokens: 8192,
vision: false,
functionCall: true,
reasoning: false,
},
{
id: 'np-lite',
name: 'NP Lite',
enabled: true,
category: 'chat',
contextLength: 32000,
maxTokens: 4096,
},
{
id: 'np-vision',
name: 'NP Vision',
enabled: true,
category: 'chat',
contextLength: 64000,
maxTokens: 4096,
vision: true,
},
],
features: ['chat', 'function_call'],
website: 'https://newprovider.com',
docsUrl: 'https://docs.newprovider.com/api',
}
ステップ 2(任意): デフォルトプロバイダーにする
新しいプロバイダーをすべてのユーザーの初期リストに表示したい場合:
ProviderManager.getDefaultProviders() の defaultTemplateIds 配列に ID を追加します。
あわせて packages/desktop/app/shared/llm-config.ts の PROVIDER_TEMPLATES に対応する ProviderTemplate を追加します。
ステップ 3(任意): Coding Plan サポートを追加する
そのプロバイダーに専用の Coding Plan API がある場合:
// Add to CODING_PLAN_URL_PRESETS
CODING_PLAN_URL_PRESETS['newprovider'] = {
baseUrl: 'https://api.newprovider.com/coding/v1',
separateApiKey: false, // Whether a separate API Key is needed
};
ステップ 4(任意): Follow-Provider のモデルマッピングを追加する
同じプロバイダーのバックグラウンド/vision モデルを自動選択したい場合:
// Add to PROVIDER_MODEL_MAPPINGS
PROVIDER_MODEL_MAPPINGS['newprovider'] = {
primary: 'np-large',
background: 'np-lite',
vision: 'np-vision', // Set to null if no vision model exists
};
ステップ 5: フロントエンド i18n を追加する
packages/renderer/src/locales/ 配下の en/zh/ja の providers/llm.json に、プロバイダー名の翻訳を追加します。
シナリオ 2: 新しい API 形式を追加する
既存形式(openai/anthropic/google/azure-openai/openai-response)と互換性のないプロバイダー API に遭遇した場合、新しい API 形式を追加する必要があります。
手順の概要
flowchart TD
S1["(1) Define format identifier<br/>types.ts"] --> S2["(2) URL builder<br/>url-builder.ts"]
S2 --> S3["(3) Header builder<br/>header-builder.ts"]
S3 --> S4["(4) Message conversion<br/>message-converter.ts"]
S4 --> S5["(5) Non-streaming handler<br/>DirectApiHandler.ts"]
S5 --> S6["(6) Streaming handler<br/>StreamHandler.ts"]
S6 --> S7["(7) Register dispatch<br/>CompletionService.ts"]
S7 --> S8["(8) Schema update<br/>schemas.ts"]
ステップ 1: 形式識別子を定義する
packages/desktop/app/main/services/capabilities/llm/completion/types.ts の ApiFormat に新しい値を追加します。
// Before
type ApiFormat = 'openai' | 'anthropic' | 'google' | 'azure-openai' | 'openai-response';
// After
type ApiFormat = 'openai' | 'anthropic' | 'google' | 'azure-openai' | 'openai-response' | 'newformat';
packages/desktop/app/main/services/capabilities/llm/config-service/schemas.ts の ApiFormatSchema も更新します。
const ApiFormatSchema = z.enum([
'openai', 'anthropic', 'google', 'azure-openai', 'openai-response', 'newformat'
]);
さらに packages/desktop/app/main/services/routers/llm/schemas.ts の llmProviderCreateSchema も更新します。
ステップ 2: URL Builder
url-builder.ts にビルダー関数を追加します。
// Pseudocode
function buildNewFormatApiUrl(baseUrl: string): string {
// Build the correct endpoint URL according to the provider's API docs
// Handle various baseUrl input formats
}
(apiType から推論する必要がある場合は)resolveApiFormat() に新形式の認識ロジックを追加します。
buildProviderApiUrl() に新形式の分岐を追加します。
ステップ 3: Header Builder
header-builder.ts の getProviderHeaders() で新形式の認証ヘッダーを処理します。
// Pseudocode — different providers use different auth schemes
// OpenAI: Authorization: Bearer <key>
// Anthropic: x-api-key: <key>
// New format may use different headers
ステップ 4: メッセージ形式変換
message-converter.ts に変換関数を追加します。
// Pseudocode
function convertMessageToNewFormat(message: SimpleChatMessage): NewFormatMessage {
// Convert the generic message format to the provider-specific format
// Handle role mapping, content structure, images, tool calls, etc.
}
ステップ 5: 非ストリーミング Handler
DirectApiHandler.ts に追加します。
// Pseudocode
async function callNewFormatCompletion(
provider: LLMProvider,
apiKey: string,
options: CompletionOptions,
logger: LoggerService
): Promise<CompletionResult> {
// Build request body
// Send request
// Parse response into CompletionResult
}
ステップ 6: ストリーミング Handler
StreamHandler.ts に追加します。
// Pseudocode
async function streamNewFormatCompletion(
provider: LLMProvider,
apiKey: string,
options: CompletionOptions,
messageId: string,
callbacks: StreamCallbacks,
logger: LoggerService
): Promise<void> {
// Build streaming request body
// Use streamSSEResponse() or custom stream parsing
// Call callbacks to deliver incremental content
}
ステップ 7: Dispatch に登録する
CompletionService に新形式を登録します。
// In the switch inside callDirectHandler
case 'newformat':
return callNewFormatCompletion(provider, apiKey, options, this.logger);
// In the switch inside callStreamHandler
case 'newformat':
await streamNewFormatCompletion(provider, apiKey, options, messageId, callbacks, this.logger);
return;
ステップ 8: Schema を更新する
ApiFormat enum を参照するすべての Zod schema が更新されていることを確認します。
capabilities/llm/config-service/schemas.ts→ApiFormatSchemarouters/llm/schemas.ts→llmProviderCreateSchema.apiFormat
シナリオ 3: プロバイダーに検索設定を追加する
プロバイダーが Web 検索をサポートしている場合は、検索の注入方法を設定する必要があります。
検索タイプの判断
flowchart TD
Start[Provider supports search?] --> Type{Search implementation}
Type -->|Request parameter| ModelParam["model-param<br/>modify request body"]
Type -->|Built-in tool definition| BuiltinTool["builtin-tool<br/>inject into tools array"]
Type -->|MCP server| MCP["mcp<br/>external handling"]
Type -->|SDK native| SDKNative["sdk-native<br/>NativeSearchInjector"]
Type -->|Not supported| None["none<br/>no config needed"]
手順
ステップ 1: 検索タイプを決定する
| 検索実装 | 判断基準 | 例 |
|---|---|---|
model-param | API がリクエストパラメーターで検索を有効化する(例: enable_search: true) | DashScope, Baidu |
builtin-tool | API が tools 配列への特定ツール定義の注入を要求する | Kimi, Volcengine |
mcp | 外部 MCP サーバーが検索を提供する | カスタムデプロイ |
sdk-native | SDK が検索をネイティブに処理する(例: Anthropic server-side tools) | Anthropic |
none | 検索をサポートしない | Ollama |
ステップ 2: 検索設定を追加する
packages/desktop/app/shared/provider-presets.ts の PROVIDER_SEARCH_CONFIGS にエントリを追加します。
model-param 型(リクエストパラメーター):
// Pseudocode
PROVIDER_SEARCH_CONFIGS['newprovider'] = {
type: 'model-param',
paramName: 'enable_search', // parameter name
paramValue: true, // parameter value
extraParams: { // extra parameters (optional)
search_mode: 'auto',
},
applicableModels: null, // null means all models
};
builtin-tool 型(組み込みツール):
// Pseudocode
PROVIDER_SEARCH_CONFIGS['newprovider'] = {
type: 'builtin-tool',
toolDefinition: {
type: 'function',
function: {
name: 'web_search',
description: 'Search the web for information',
parameters: {
type: 'object',
properties: {
query: { type: 'string', description: 'Search query' },
},
required: ['query'],
},
},
},
conflictsWithFC: false, // Whether it conflicts with function calling
applicableModels: ['np-large'], // Only certain models support it (null = all)
};
ステップ 3: 注入を確認する
ProviderSearchInjector は getSearchConfig() でプロバイダーの検索設定を自動検出し、buildSearchAugmentation() で注入内容を構築します。追加のコード変更は不要です。
ファイル変更チェックリスト
シナリオ 1: 新しいプリセットテンプレート
| ファイル | 変更内容 | 必須 |
|---|---|---|
shared/provider-presets.ts | PROVIDER_PRESETS エントリを追加 | はい |
shared/llm-config.ts | PROVIDER_TEMPLATES エントリを追加(デフォルトにする場合) | 任意 |
capabilities/llm/config-service/ProviderManager.ts | defaultTemplateIds に追加(デフォルトにする場合) | 任意 |
shared/provider-presets.ts | CODING_PLAN_URL_PRESETS(必要な場合) | 任意 |
shared/provider-presets.ts | PROVIDER_MODEL_MAPPINGS(必要な場合) | 任意 |
shared/provider-presets.ts | PROVIDER_SEARCH_CONFIGS(検索が必要な場合) | 任意 |
| i18n JSON ファイル(en/zh/ja) | プロバイダー名の翻訳 | 推奨 |
シナリオ 2: 新しい API 形式
| ファイル | 変更内容 | 必須 |
|---|---|---|
capabilities/llm/completion/types.ts | ApiFormat 型 | はい |
capabilities/llm/completion/url-builder.ts | URL builder + resolveApiFormat + buildProviderApiUrl | はい |
capabilities/llm/completion/header-builder.ts | 認証ヘッダー | はい |
capabilities/llm/completion/message-converter.ts | メッセージ形式変換 | はい |
capabilities/llm/completion/DirectApiHandler.ts | 非ストリーミング handler | はい |
capabilities/llm/completion/StreamHandler.ts | ストリーミング handler | はい |
capabilities/llm/completion/CompletionService.ts | switch 分岐登録 | はい |
capabilities/llm/config-service/schemas.ts | ApiFormatSchema | はい |
routers/llm/schemas.ts | llmProviderCreateSchema | はい |
シナリオ 3: 検索設定を追加する
| ファイル | 変更内容 | 必須 |
|---|---|---|
shared/provider-presets.ts | PROVIDER_SEARCH_CONFIGS エントリ | はい |
テストガイド
プリセットテンプレートのテスト
-
Unit tests: テンプレートデータの整合性を確認します
- 必須フィールドがすべて存在し、有効であること
- modelConfigs 内の各モデル ID が models 配列に存在すること
- apiFormat の値が ApiFormat enum の範囲内であること
-
Integration tests:
- プリセットからプロバイダーを作成 → プロバイダーデータが正しいことを確認
- プロバイダーを有効化 → 有効な API Key を設定 → テストメッセージを送信
- モデル検出 → 返されたモデルリストを確認
-
UI tests:
- 新しいプリセットが設定ページのプリセット一覧に表示される
- 「Add」をクリックするとプロバイダーが正しく作成される
- プロバイダー設定ページに正しいフィールドが表示される
API 形式のテスト
-
URL builder tests:
- さまざまな baseUrl 入力形式 → 正しい API endpoint
- パスプレフィックス付き URL → プレフィックスが失われない
-
Message conversion tests:
- プレーンテキストメッセージ → 正しい形式
- 画像付きメッセージ → 正しく処理される
- tool calls 付きメッセージ → 正しい形式
-
Handler tests:
- 非ストリーミング: リクエスト送信 → レスポンス解析 → CompletionResult
- ストリーミング: SSE events → callbacks が正しく呼ばれる
- エラー処理: ネットワークエラー、API エラー、形式エラー
-
End-to-end tests:
testModel()を使用してパイプライン全体を確認- ストリーミング会話 → onDelta/onDone callbacks を確認
検索設定のテスト
-
Injection tests:
getSearchConfig()が新しいプロバイダーを正しく認識するbuildSearchAugmentation()が正しい注入内容を生成する- model-param 型: リクエスト body に正しいパラメーターが含まれる
- builtin-tool 型: tools 配列に正しいツール定義が含まれる
-
Conflict tests:
conflictsWithFCが true の場合: 検索ツールと function calling が共存しないapplicableModels制限: 非対象モデルでは検索が注入されない
関連ファイル
| ファイル | 関係 |
|---|---|
shared/llm-config.ts | PROVIDER_TEMPLATES、ApiFormat、コア型 |
shared/provider-presets.ts | プリセットテンプレート、検索設定、Coding Plan URL、モデルマッピング |
capabilities/llm/config-service/ProviderManager.ts | デフォルトプロバイダーリスト、テンプレート作成ロジック |
capabilities/llm/config-service/schemas.ts | ApiFormatSchema、バリデーション schema |
capabilities/llm/completion/types.ts | ApiFormat 型定義 |
capabilities/llm/completion/url-builder.ts | resolveApiFormat、URL builders |
capabilities/llm/completion/header-builder.ts | 認証ヘッダー |
capabilities/llm/completion/message-converter.ts | メッセージ形式変換 |
capabilities/llm/completion/DirectApiHandler.ts | 非ストリーミング API handler |
capabilities/llm/completion/StreamHandler.ts | ストリーミング API handler |
capabilities/llm/completion/CompletionService.ts | Handler dispatch 登録 |
capabilities/llm/completion/ProviderSearchInjector.ts | 検索注入ロジック |
routers/llm/schemas.ts | IPC パラメーターバリデーション schema |
| i18n JSON ファイル | フロントエンド翻訳 |