yoi/tickets/llm-provider-catalog.review.md
2026-04-23 15:37:51 +09:00

61 lines
6.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Review: LLM プロバイダ/モデルカタログ
## 前提・要件の確認
### 要件 1: TOML 形式の定義(`[[provider]]` 配列、`auth_hint = { kind, env? }`
満たされている。
- `crates/provider/assets/providers.toml` の 4 エントリがチケット例(`tickets/llm-provider-catalog.md:15-46`と一字一句合致しているid / display_name / scheme / base_url / auth_hint.kind / env 名 / default_models の順序と値)。
- `AuthHint` 側は `#[serde(tag = "kind", rename_all = "snake_case")]` でインラインテーブル `{ kind = "api_key", env = "..." }` を受け付ける(`crates/provider/src/catalog.rs:44-56`)。`kind = "codex_oauth"` はデフォルトの `snake_case` 変換が `codex_o_auth` になってしまう問題を `#[serde(rename = "codex_oauth")]` で明示的に回避しており、既存 `AuthRef::CodexOAuth``crates/manifest/src/model.rs:70`)と同じ書き方になっている。整合している。
### 要件 2: `Vec<ProviderEntry>` を返す読取 API を `crates/provider` に 1 本公開
満たされている。
- `pub fn load() -> Result<Vec<ProviderEntry>, CatalogError>` が主 API`catalog.rs:108`)。補助として `load_builtin` / `load_from_path` も公開されており、後者はテストと将来の明示パス指定に合理的。
- 配置は `crates/provider` 直下(`lib.rs:14` で `pub mod catalog;`)。`llm-worker` に下りていない。Memory の「llm-worker は低レベル基盤に留める」方針に整合。
### 要件 3: 配置パスbuiltin の `include_str!` / user override 置換 / 両方読めなければ builtin
満たされている。
- `BUILTIN_CATALOG: &str = include_str!("../assets/providers.toml")``catalog.rs:18`)。
- user override は `$XDG_CONFIG_HOME/insomnia/providers.toml`、未設定時は `$HOME/.config/insomnia/providers.toml``catalog.rs:137-154`)。チケットでは `$HOME` フォールバックは明示されていないが、XDG Base Directory spec の既定挙動として妥当で、過剰とも言えない。
- マージではなく置換(`catalog.rs:108-115`)。`load_prefers_override_over_builtin` / `load_falls_back_to_builtin_when_override_absent` テストで両経路をカバー。
### 要件 4: `ProviderEntry` + `model_id` → `ModelConfig` の変換
満たされている。
- `ProviderEntry::to_model_config(&self, model_id: impl Into<String>) -> ModelConfig``catalog.rs:82-99`)が `AuthHint` の 3 バリアントを `AuthRef` に 1:1 で写す。`capability` は常に `None` で返し、`build_client` 側の 3 段階 fallback`ModelConfig.capability` → `capability::lookup``scheme default`、`lib.rs:120-124`)に委ねるのはチケット設計判断「`capability` 宣言をカタログに入れない」(`llm-provider-catalog.md:77-79`)と整合。
- `to_model_config_maps_auth_hint` テスト(`catalog.rs:208-229`)で 3 バリアント全部を確認。
### 要件 5: 完了時の動作builtin 取得 / override 置換 / UI 未実装段階で unit test e2e
満たされている。
- `ollama_entry_builds_client``catalog.rs:231-239`)が「カタログ読取 → `ProviderEntry` 選択 → `ModelConfig` 生成 → `build_client` が成功」の経路を端から端まで通している。Ollama は `AuthRef::None` なので環境変数依存がなく、常に成功する選択として適切。
- `cargo test -p provider` が 43 件全部 pass、ワークスペース `cargo build` もクリーン。
## アーキテクチャ・スコープ
- **レイヤ分離**: `crates/provider` 内に閉じ、`llm-worker` には漏れていない。`build_client` / `resolve_auth` の既存シグネチャは一切変更していない(`lib.rs:53-146` は今回の差分外)。非破壊性 OK。
- **責務分離**: `AuthHint`UI メタ)と `AuthRef`(ランタイム解決)を別型として定義し、`to_model_config` で 1:1 変換する設計は、チケット「設計判断: `auth_hint``AuthRef` の二重定義」(`llm-provider-catalog.md:73-75`の意図を正確に反映している。型を流用する代替案もあり得たが、UI ヒントに `file: Option<PathBuf>` が紛れ込むとカタログのスコープが広がるため、別型にしたのは妥当。
- **過剰抽象の有無**: `discover: Option<DiscoverMode>` のような先取り実装はされていない(コメントで言及のみ、`catalog.rs:60-61`。チケットの「auto_discover は別チケット」方針に従っている。
- **依存追加**: `toml` を dev-dependencies から regular dependencies に「移動」する形で `cargo add` 相当の編集がされている。手動編集ではあるが、既存行を move しただけなので Memory の `Use cargo add` 方針からの逸脱は軽微。
- **モジュール分割**: `pod.rs` 等の既存 primary ファイルに詰め込まず、`catalog.rs` として独立モジュール化している。Memory の「feature モジュール分割」方針に合致。
- **Scope 外の侵食**: UI / auto_discover / 編集 UI / プロファイルのどれにも触れていない。境界遵守。
## 指摘事項
### Non-blocking / Follow-up
- `$HOME` フォールバック(`catalog.rs:143-152`がチケット本文に明示されていない。妥当な挙動ではあるが、チケットの「配置パス」記述と実装の解像度に乖離があり、ユーザーが後から読んだとき驚きがある。ticket 側に一行追記するか、コード側のドキュメント(モジュール冒頭 docに「XDG 既定挙動として `$HOME/.config/...` にフォールバック」を明記しておくと親切。
### Nits
- `catalog.rs:106` の doc コメントに "sigh" というタイポがある("silent" の意図と思われる)。該当箇所:
```
/// 存在すれば builtin を置き換える。存在しなければ builtin のみ。
/// user override が存在するが壊れている場合はエラーを返すsilent
/// fallback はしない — ユーザーが書いた設定が sigh なく無視されて
/// builtin に戻る挙動は気付きにくいため)。
```
`sigh``silent` に直すか、日本語化で「気付かれず」等に差し替え。
- `builtin_ollama_shape``catalog.rs:173-183`)の assert が `scheme == Anthropic` を確認しているが、コメントの「Ollama = Anthropic scheme」の意図は `lib.rs:278-290``ollama_succeeds_without_key` と重複する情報。コメントで相互参照しておくと将来の読み手に親切。
## 判断
**Approve完了可** — チケット要件 1〜5 は全て明確に満たされており、`AuthHint`/`AuthRef` の責務分離・レイヤ境界・既存 API 非破壊といったアーキテクチャ観点も適切。指摘は `sigh` タイポと `$HOME` フォールバックのドキュメント明示の 2 点のみで、どちらも blocking ではない。