6.7 KiB
LLM モデル設定の再編
背景
決定済みの LLM プロバイダサポート方針(docs/plan/llm_providers.md)に従って llm-worker のプロバイダ層を再編する。Pod 側で「使う LLM モデル」を宣言する構造にし、共通の通信層 + scheme の組合せで任意のプロバイダを収容できるようにする。
現状の問題
crates/llm-worker/src/llm_client/providers/{anthropic,openai,gemini,ollama}.rsは各々 HTTP 骨格 + scheme 呼び出しの薄いアダプタで、構造が重複providers/ollama.rs(67行) は scheme 未分離で整合性が崩れている- OpenAI 互換ルーター系(xAI / Groq / OpenRouter / Together / BLACKBOX 等)を追加するたびに新ファイルを書く必要がある
要件
-
Pod マニフェストで LLM モデルを宣言できる
ModelConfig { scheme: Scheme, // Anthropic / OpenAIChat / OpenAIResponses / Gemini base_url: Url, model_id: String, auth: AuthRef, }継承(commit ebee0b9)と整合し、親 Pod の定義を子 Pod が override 可能。
-
providers/層の廃止: llm-worker はHttpTransport<S: Scheme>相当の汎用通信層 1 本を持ち、ModelConfigを食わせてインスタンス化する -
既存 scheme の再編:
scheme/openaiをscheme/openai_chatにリネームscheme/anthropic/scheme/geminiはそのままscheme/openai_responsesは別チケット(llm-scheme-openai-responses)で新設- Ollama は scheme/anthropic を base_url 差し替えで流用(独自 scheme は作らない)
-
認証の分離:
AuthRefはApiKey(EnvVar | ConfigRef)/CodexOAuth/Noneを表現でき、scheme とは直交する層で管理される。CodexOAuthの実装自体は別チケット(llm-auth-codex-oauth) -
決定済みプロバイダ方針との整合:
- 第一級: Ollama / Codex OAuth / Anthropic API
- 二次: OpenAI 互換共通枠(
{ scheme: OpenAIChat, base_url: 各社, auth: ApiKey }の宣言だけで収容)
-
ModelCapability を分離: モデルに紐づく機能差を別メタデータとして表現
ModelCapability { tool_calling: ToolCallingSupport, // parallel 可否含む structured_output: StructuredOutput, // JsonObject / JsonSchema reasoning: Option<ReasoningSupport>, // effort / budget_tokens vision: bool, prompt_caching: CacheStrategy, // Explicit { max_breakpoints } / Auto }ReasoningControl { effort, budget_tokens }は共通型、scheme アダプタで各社形式に投影。プロバイダ側高次ツール(web_search 等)は採用しないため軸から除外。 -
Streaming は現状維持: 既存
BlockStart / BlockDelta / BlockStop / BlockAbort+DeltaContent::{Text, Thinking, InputJson}を変更しない。Gemini や Ollama のように ToolCall 引数 delta を送らないプロバイダは scheme アダプタで「BlockStart → InputJson(全体 1 回) → BlockStop」の擬似ストリーム化で吸収 -
Ollama 運用の注意点: scheme/anthropic 流用前提で以下を守る
cache_controlは送らない(ModelCapability::prompt_caching = Auto)tool_choice/metadata/ URL 画像は送らない(Ollama 側非対応)/v1/messages/count_tokensは叩かない(issue #13949 でサーバ不安定化)/v1/chat/completionsは stream+tools バグ (#9092) のため使わない
-
完了時の動作: 既存の動作は変わらず、Pod マニフェストで
ModelConfigを宣言するだけでモデル切替できる。OpenAI 互換の新規プロバイダは新コードなしで追加可能
設計決定
1. Scheme trait の境界(方針A: 全面抽象化)
trait で URL 組立・認証要件・body 変換・SSE パースをすべて抽象化し、HttpTransport は 1 本にする。trait スケッチ:
trait Scheme {
fn path(&self, model: &str) -> String; // "/v1/messages" 等
fn required_auth(&self) -> AuthRequirement; // Bearer / XApiKey / QueryParam / None
fn additional_headers(&self) -> Vec<(&str, String)>; // anthropic-version 等
fn build_request_body(&self, model: &str, req: &Request, cap: &ModelCapability) -> Value;
fn parse_sse(&self, event_type: &str, data: &str) -> Result<Vec<Event>, ClientError>;
fn default_base_url(&self) -> &'static str;
}
parse_sse は Vec<Event> に統一(Anthropic は 1 要素 Vec で扱う)。
2. AuthRef と Scheme の組合せ検証(方針B: 構築時検証)
Scheme::required_auth() で要求する AuthRequirement を宣言し、build_client 時に AuthRef と照合。非対応組合せは構築エラーにする(実行時に落とさない)。
3. crates/provider/ の去就(方針A: 残す)
provider クレートは保持。build_client(ModelConfig) -> Box<dyn LlmClient> は (Scheme, AuthRef) 照合 + HttpTransport::new の薄ラッパーに縮退する。~/.codex/auth.json 読み取り等の認証ストア解決は provider クレートに肉付けしていく(llm-worker は低レベル基盤に留める方針と整合)。
4. ModelCapability の保持(方針: ハイブリッド)
- scheme 実装側に
model_id → ModelCapabilityの静的テーブルを持つ(既知モデル分) ModelConfigで明示宣言すれば override- 未知モデルは scheme ごとの安全側デフォルト(
prompt_caching: Auto等)にフォールバック
今チケットのスコープ:
- 型定義 + 既知モデルの固定値
CacheStrategy::Explicitでcache_controlマーカー挿入(既存実装を capability で分岐)ReasoningControl { effort, budget_tokens }を scheme 側で各社形式に投影CacheStrategy::Autoは何もしない(Ollama で重要)
5. マニフェスト継承との統合(方針: フィールド単位 override)
ProviderConfigPartial と同じ方針で、ModelConfig の全フィールド(scheme / base_url / model_id / auth)を Option<T> で継承。子 Pod は base_url だけ差し替える等が可能。
6. TOML 後方互換
旧 [provider] kind = "..." フォーマットは互換を切る。新 [model] セクションで scheme / base_url / model_id / auth を宣言する。
Scope 外
- OpenAI Responses scheme の新設(
tickets/llm-scheme-openai-responses.md) - Codex OAuth 認証アダプタの実装(
tickets/llm-auth-codex-oauth.md) - OpenAI 互換ルーター各社の動作確認
- プロバイダ選択 UI(将来の native GUI / TUI 拡張)