# 機能設計書 27-マルチプロバイダーサポート

## 概要

本ドキュメントは、OpenCodeにおけるマルチプロバイダーサポート機能の設計を定義する。本機能は30以上のAIプロバイダーを統一的なインターフェースで利用可能にし、プロバイダー固有の設定や認証を抽象化する。

### 本機能の処理概要

マルチプロバイダーサポート機能は、様々なAIプロバイダー（Anthropic、OpenAI、Google、Azure、AWS Bedrock等）を統一されたAPI経由で利用可能にする機能である。プロバイダーの検出、SDK読み込み、モデル取得を自動化する。

**業務上の目的・背景**：ユーザーが契約している様々なAIサービスを、同一のインターフェースで利用可能にする。プロバイダー切り替えのコストを削減し、最適なモデルを柔軟に選択できるようにする。

**機能の利用シーン**：ユーザーが複数のAIプロバイダーを切り替えて使用する際、設定ファイルや環境変数でプロバイダーを設定する際、新しいプロバイダーを追加する際に利用される。

**主要な処理内容**：
1. プロバイダー情報の読み込み（models.dev、設定ファイル）
2. 認証情報の検出（環境変数、設定ファイル、Auth API）
3. プロバイダーSDKの動的読み込み
4. モデル情報の取得とフィルタリング
5. LanguageModel V2インスタンスの生成

**関連システム・外部連携**：各AIプロバイダーAPI、models.dev（モデルカタログ）、AI SDK（Vercel）。

**権限による制御**：enabled_providers/disabled_providersで利用可能プロバイダーを制限可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 4 | レイアウト | 補助機能 | プロバイダー接続ダイアログの表示 |
| 14 | ワークスペースダッシュボード | 補助機能 | プロバイダーセクションの表示 |

## 機能種別

データ読み込み / 外部API連携 / ファクトリーパターン

## 入力仕様

### 入力パラメータ

#### プロバイダー取得

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| providerID | string | Yes | プロバイダー識別子 | 存在するプロバイダーID |

#### モデル取得

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| providerID | string | Yes | プロバイダー識別子 | 存在するプロバイダーID |
| modelID | string | Yes | モデル識別子 | 存在するモデルID |

### 入力データソース

- models.dev（APIまたはローカルキャッシュ）
- 設定ファイル（opencode.json/opencode.jsonc）
- 環境変数
- Auth API（認証情報）
- プラグイン

## 出力仕様

### 出力データ

#### Provider.Info

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | string | プロバイダーID |
| name | string | 表示名 |
| source | "env" \| "config" \| "custom" \| "api" | 認証情報のソース |
| env | string[] | 環境変数名リスト |
| key | string? | APIキー |
| options | Record<string, any> | プロバイダー固有オプション |
| models | Record<string, Model> | 利用可能モデル一覧 |

#### Provider.Model

| 項目名 | 型 | 説明 |
|--------|-----|------|
| id | string | モデルID |
| providerID | string | プロバイダーID |
| name | string | 表示名 |
| api | {id, url, npm} | API情報 |
| capabilities | object | モデル能力（reasoning, toolcall等） |
| cost | object | 料金情報 |
| limit | object | 制限情報（context, output） |

### 出力先

- Provider.list(): プロバイダー一覧
- Provider.getModel(): モデル情報
- Provider.getLanguage(): LanguageModelV2インスタンス

## 処理フロー

### 処理シーケンス

```
1. 初期化（state関数）
   ├─ Config.get()で設定読み込み
   ├─ ModelsDev.get()でモデルカタログ取得
   └─ fromModelsDevProviderでProvider.Info形式に変換

2. プロバイダー検出
   ├─ 環境変数からAPIキー検出
   ├─ Auth.all()から認証情報取得
   ├─ プラグインからの認証ローダー実行
   └─ CUSTOM_LOADERSによるプロバイダー固有初期化

3. 設定のマージ
   ├─ 設定ファイルからのオーバーライド
   ├─ モデルのwhitelist/blacklist適用
   └─ disabled_providers/enabled_providersによるフィルタ

4. モデル取得（getModel）
   ├─ プロバイダー存在確認
   ├─ モデル存在確認
   └─ 類似モデルサジェスト（fuzzysort）

5. SDK取得（getSDK）
   ├─ バンドルされたプロバイダーの確認
   ├─ 動的インポート（BunProc.install）
   └─ SDKインスタンスのキャッシュ

6. LanguageModel取得（getLanguage）
   └─ modelLoaders経由またはsdk.languageModel()
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B[設定読み込み]
    B --> C[models.dev取得]
    C --> D[環境変数から認証検出]
    D --> E[Auth APIから認証取得]
    E --> F[プラグイン認証ローダー実行]
    F --> G[CUSTOM_LOADERS実行]
    G --> H[設定マージ]
    H --> I[enabled/disabled適用]
    I --> J[プロバイダー一覧完成]

    K[getModel呼び出し] --> L{プロバイダー存在?}
    L -->|No| M[ModelNotFoundError + サジェスト]
    L -->|Yes| N{モデル存在?}
    N -->|No| M
    N -->|Yes| O[Model返却]

    P[getLanguage呼び出し] --> Q[getSDK]
    Q --> R{バンドル済み?}
    R -->|Yes| S[バンドルSDK使用]
    R -->|No| T[動的インポート]
    S --> U[LanguageModel生成]
    T --> U
    U --> V[キャッシュして返却]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-27-01 | 認証優先順位 | env > Auth API > プラグイン > 設定ファイルの順で認証情報を解決 | プロバイダー初期化時 |
| BR-27-02 | モデルフィルタリング | alpha/deprecated状態のモデルは除外（フラグで有効化可） | モデル一覧生成時 |
| BR-27-03 | プロバイダー制限 | enabled_providers指定時はそれ以外を除外 | プロバイダー一覧生成時 |
| BR-27-04 | ブラックリスト | whitelist/blacklistでモデル単位の制御 | モデル一覧生成時 |
| BR-27-05 | SDKキャッシュ | 同一設定のSDKはキャッシュして再利用 | SDK取得時 |

### 計算ロジック

SDKキャッシュキーの生成:
```typescript
const key = Bun.hash.xxHash32(JSON.stringify({
  providerID: model.providerID,
  npm: model.api.npm,
  options
}))
```

## データベース操作仕様

データベース操作は行わない（全てインメモリ処理）。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ProviderModelNotFoundError | NamedError | プロバイダー/モデルが存在しない | fuzzysortで類似候補を提示 |
| ProviderInitError | NamedError | SDK初期化失敗 | 原因をcauseに格納して伝播 |

### リトライ仕様

SDK読み込み失敗時のリトライは実装されていない。

## トランザクション仕様

トランザクションは不要（ステートレス処理）。

## パフォーマンス要件

- プロバイダー初期化は起動時に1回のみ実行
- SDKのキャッシュにより重複読み込みを回避
- models.devは1時間ごとにバックグラウンドで更新

## セキュリティ考慮事項

- APIキーは環境変数またはAuth APIから取得
- 設定ファイルのAPIキーは平文保存されるため注意が必要
- カスタムfetchでタイムアウトとリクエスト加工を制御

## 備考

- バンドルされたプロバイダー（BUNDLED_PROVIDERS）は動的インポート不要
- CUSTOM_LOADERSで各プロバイダー固有の初期化ロジックを実装
- OpenRouter、Vercel等はリファラーヘッダーを設定

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | provider.ts | `packages/opencode/src/provider/provider.ts` | Provider.Info zodスキーマ（664-677行目） |
| 1-2 | provider.ts | `packages/opencode/src/provider/provider.ts` | Provider.Model zodスキーマ（593-662行目） |
| 1-3 | models.ts | `packages/opencode/src/provider/models.ts` | ModelsDev.Provider/Model定義（18-82行目） |

**読解のコツ**: Provider.InfoとProvider.Modelの関係、models.devからの変換ロジックを理解する。

#### Step 2: 初期化処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | provider.ts | `packages/opencode/src/provider/provider.ts` | state関数（757-1035行目） |
| 2-2 | provider.ts | `packages/opencode/src/provider/provider.ts` | BUNDLED_PROVIDERS定義（87-110行目） |
| 2-3 | provider.ts | `packages/opencode/src/provider/provider.ts` | CUSTOM_LOADERS定義（119-591行目） |

**主要処理フロー**:
- **757-778行目**: 設定とモデルカタログの読み込み
- **780-795行目**: GitHub Copilot Enterpriseプロバイダーの生成
- **810-891行目**: 設定ファイルからのプロバイダー拡張
- **893-914行目**: 環境変数/Auth APIからの認証情報取得
- **916-961行目**: プラグイン認証ローダーの実行
- **963-977行目**: CUSTOM_LOADERSの実行

#### Step 3: モデル/SDK取得を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | provider.ts | `packages/opencode/src/provider/provider.ts` | Provider.getModel関数（1150-1168行目） |
| 3-2 | provider.ts | `packages/opencode/src/provider/provider.ts` | getSDK関数（1041-1144行目） |
| 3-3 | provider.ts | `packages/opencode/src/provider/provider.ts` | Provider.getLanguage関数（1170-1195行目） |

**主要処理フロー**:
- **1150-1168行目**: プロバイダー/モデル存在確認とfuzzysortサジェスト
- **1041-1144行目**: SDKキャッシュ、バンドル確認、動的インポート
- **1170-1195行目**: modelLoaders経由またはsdk.languageModel()でLanguageModel生成

### プログラム呼び出し階層図

```
Provider.list()
    │
    └─ state() [lazy初期化]
           │
           ├─ Config.get()
           ├─ ModelsDev.get()
           ├─ fromModelsDevProvider()
           │
           ├─ 環境変数チェック
           ├─ Auth.all()
           ├─ Plugin.list() + auth.loader()
           ├─ CUSTOM_LOADERS[providerID]()
           │
           └─ mergeProvider() [設定マージ]

Provider.getModel(providerID, modelID)
    │
    ├─ state()
    ├─ provider存在確認
    ├─ model存在確認
    └─ fuzzysort [候補サジェスト]

Provider.getLanguage(model)
    │
    ├─ state()
    ├─ getSDK(model)
    │      │
    │      ├─ BUNDLED_PROVIDERS[npm]
    │      │      └─ createXxx(options)
    │      │
    │      └─ BunProc.install()
    │             └─ 動的import
    │
    └─ modelLoaders[providerID](sdk, modelID)
           └─ sdk.languageModel(modelID)
```

### データフロー図

```
[入力]                    [処理]                         [出力]

models.dev ─────┐                                    ┌─> Provider.Info[]
                │                                    │
環境変数 ───────┼──▶ state() ─────────────────────────┼─> Provider.Model[]
                │       │                            │
Config ─────────┤       ├─ fromModelsDevProvider     └─> LanguageModelV2
                │       ├─ 認証検出
Auth API ───────┤       ├─ プラグインローダー
                │       └─ CUSTOM_LOADERS
プラグイン ─────┘

providerID ────┐
               ├──▶ Provider.getModel() ────────────▶ Provider.Model
modelID ───────┘

Provider.Model ─▶ Provider.getLanguage() ───────────▶ LanguageModelV2
                         │
                         ├─ getSDK()
                         │      ├─ BUNDLED_PROVIDERS
                         │      └─ 動的import
                         │
                         └─ modelLoaders
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| provider.ts | `packages/opencode/src/provider/provider.ts` | ソース | マルチプロバイダーサポートのメイン実装 |
| models.ts | `packages/opencode/src/provider/models.ts` | ソース | models.devからのモデル情報取得 |
| index.ts | `packages/opencode/src/auth/index.ts` | ソース | 認証情報の管理 |
| index.ts | `packages/opencode/src/plugin/index.ts` | ソース | プラグインシステム |
| config.ts | `packages/opencode/src/config/config.ts` | ソース | 設定ファイルの読み込み |
