yoi/tickets/llm-model-config.md

6.7 KiB
Raw Blame History

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 等)を追加するたびに新ファイルを書く必要がある

要件

  1. Pod マニフェストで LLM モデルを宣言できる

    ModelConfig {
        scheme: Scheme,      // Anthropic / OpenAIChat / OpenAIResponses / Gemini
        base_url: Url,
        model_id: String,
        auth: AuthRef,
    }
    

    継承commit ebee0b9と整合し、親 Pod の定義を子 Pod が override 可能。

  2. providers/ 層の廃止: llm-worker は HttpTransport<S: Scheme> 相当の汎用通信層 1 本を持ち、ModelConfig を食わせてインスタンス化する

  3. 既存 scheme の再編:

    • scheme/openaischeme/openai_chat にリネーム
    • scheme/anthropic / scheme/gemini はそのまま
    • scheme/openai_responses は別チケットllm-scheme-openai-responsesで新設
    • Ollama は scheme/anthropic を base_url 差し替えで流用(独自 scheme は作らない)
  4. 認証の分離: AuthRefApiKey(EnvVar | ConfigRef) / CodexOAuth / None を表現でき、scheme とは直交する層で管理される。CodexOAuth の実装自体は別チケットllm-auth-codex-oauth

  5. 決定済みプロバイダ方針との整合:

    • 第一級: Ollama / Codex OAuth / Anthropic API
    • 二次: OpenAI 互換共通枠({ scheme: OpenAIChat, base_url: 各社, auth: ApiKey } の宣言だけで収容)
  6. 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 等)は採用しないため軸から除外。

  7. Streaming は現状維持: 既存 BlockStart / BlockDelta / BlockStop / BlockAbort + DeltaContent::{Text, Thinking, InputJson} を変更しない。Gemini や Ollama のように ToolCall 引数 delta を送らないプロバイダは scheme アダプタで「BlockStart → InputJson(全体 1 回) → BlockStop」の擬似ストリーム化で吸収

  8. 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) のため使わない
  9. 完了時の動作: 既存の動作は変わらず、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_sseVec<Event> に統一Anthropic は 1 要素 Vec で扱う)。

2. AuthRefScheme の組合せ検証方針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::Explicitcache_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 拡張)