llmのモデル情報の設計チケット
This commit is contained in:
parent
a89701bc43
commit
e1d672e9c0
|
|
@ -1,4 +1,4 @@
|
||||||
設計指針を固め、全体の設計を進めるために、全体の俯瞰と細かいディテールを往復している。
|
全体設計が概ね固まり、随所の細かい仕様を詰めながら実装を進めている。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
設計指針を固め、全体の設計を進めるために、全体の俯瞰と細かいディテールを往復している。
|
全体設計が概ね固まり、随所の細かい仕様を詰めながら実装を進めている。
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
4
TODO.md
4
TODO.md
|
|
@ -2,6 +2,10 @@
|
||||||
- [ ] ツール設計
|
- [ ] ツール設計
|
||||||
- [ ] Bash ツール (Permission 層と統合) → [tickets/bash-tool.md](tickets/bash-tool.md)
|
- [ ] Bash ツール (Permission 層と統合) → [tickets/bash-tool.md](tickets/bash-tool.md)
|
||||||
- [ ] Protocol の設計 → [tickets/protocol-design.md](tickets/protocol-design.md)
|
- [ ] Protocol の設計 → [tickets/protocol-design.md](tickets/protocol-design.md)
|
||||||
|
- [ ] LLM プロバイダ統合
|
||||||
|
- [ ] モデル設定の構造再編(providers 層廃止 + Pod 宣言化) → [tickets/llm-model-config.md](tickets/llm-model-config.md)
|
||||||
|
- [ ] OpenAI Responses scheme の新設 → [tickets/llm-scheme-openai-responses.md](tickets/llm-scheme-openai-responses.md)
|
||||||
|
- [ ] Codex OAuth 認証の流用 → [tickets/llm-auth-codex-oauth.md](tickets/llm-auth-codex-oauth.md)
|
||||||
- [ ] パーミッション: パターンベースのツール実行制御 → [tickets/permission-extension-point.md](tickets/permission-extension-point.md)
|
- [ ] パーミッション: パターンベースのツール実行制御 → [tickets/permission-extension-point.md](tickets/permission-extension-point.md)
|
||||||
- [ ] Pod オーケストレーション
|
- [ ] Pod オーケストレーション
|
||||||
- [ ] 動的 Scope 変更 → [tickets/dynamic-scope.md](tickets/dynamic-scope.md)
|
- [ ] 動的 Scope 変更 → [tickets/dynamic-scope.md](tickets/dynamic-scope.md)
|
||||||
|
|
|
||||||
70
tickets/llm-auth-codex-oauth.md
Normal file
70
tickets/llm-auth-codex-oauth.md
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
# Codex OAuth 認証の流用
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
決定済み方針(`docs/plan/llm_providers.md`)で、ChatGPT サブスクリプションの OAuth トークンを流用して OpenAI Responses API を叩く経路を第一級サポートとする。OpenAI は Codex CLI を Apache-2.0 で公開し、ChatGPT OAuth の第三者ツール利用を service terms で名指し禁止していない(互換経路)。
|
||||||
|
|
||||||
|
Codex CLI の実装(github.com/openai/codex、`codex-rs/login/` 配下)から以下が確定:
|
||||||
|
|
||||||
|
- トークンは `~/.codex/auth.json` に `{ auth_mode, tokens: { id_token, access_token, refresh_token, account_id }, last_refresh }` 形式で保存
|
||||||
|
- リクエストは `https://chatgpt.com/backend-api/v1/responses` に投げる(Responses API wire)
|
||||||
|
- 認証ヘッダは `Authorization: Bearer <access_token>` + `ChatGPT-Account-ID: <account_id>`(+ FedRAMP 組織で `X-OpenAI-Fedramp: true`)
|
||||||
|
- リフレッシュは `https://auth.openai.com/oauth/token` に `grant_type=refresh_token`, `client_id=app_EMoamEEZ73f0CkXaXp7hrann`, `refresh_token` を POST
|
||||||
|
|
||||||
|
## 要件
|
||||||
|
|
||||||
|
1. **`AuthRef::CodexOAuth` の追加**: `llm-model-config` で定義する認証列挙型に ChatGPT OAuth バリアントを追加
|
||||||
|
|
||||||
|
2. **トークン読み取り**: `~/.codex/auth.json` を読み、以下を取り出す
|
||||||
|
- `tokens.access_token`
|
||||||
|
- `tokens.refresh_token`
|
||||||
|
- `tokens.account_id`
|
||||||
|
- `last_refresh`(期限判定用)
|
||||||
|
- `tokens.id_token` の JWT claims(`chatgpt_account_is_fedramp`、`chatgpt_plan_type` 参照)
|
||||||
|
|
||||||
|
3. **ヘッダ注入**: `HttpTransport` が `AuthRef::CodexOAuth` を解決するとき以下を組み立てる
|
||||||
|
- `Authorization: Bearer <access_token>`
|
||||||
|
- `ChatGPT-Account-ID: <account_id>`
|
||||||
|
- FedRAMP 組織なら `X-OpenAI-Fedramp: true`
|
||||||
|
|
||||||
|
4. **base_url の自動適用**: `ModelConfig.base_url` 未指定時は `https://chatgpt.com/backend-api` を既定とする。ユーザーが明示的に `https://api.openai.com` を指定した場合は尊重(API key 経路と共用したいケース用)
|
||||||
|
|
||||||
|
5. **トークンリフレッシュ**:
|
||||||
|
- `https://auth.openai.com/oauth/token` に `{ client_id: "app_EMoamEEZ73f0CkXaXp7hrann", grant_type: "refresh_token", refresh_token }` を POST
|
||||||
|
- 有効期限が近い(access_token が期限切れ or 残り時間が閾値以下)とき自動更新
|
||||||
|
- 更新後の token を `~/.codex/auth.json` に書き戻す
|
||||||
|
- 複数プロセス(Codex CLI との並行実行等)を想定した排他制御
|
||||||
|
|
||||||
|
6. **scheme/openai_responses との組合せで動作**: `ModelConfig { scheme: OpenAIResponses, base_url: (既定), model_id: "gpt-5-codex" 等, auth: CodexOAuth }` で ChatGPT 枠を使って Codex 相当の動作ができる
|
||||||
|
|
||||||
|
7. **完了時の動作**: ChatGPT アカウント保持者が `codex login` 済みの環境で insomnia を起動すると、追加設定なしで Codex と同じモデル(`gpt-5-codex` 等)が利用可能
|
||||||
|
|
||||||
|
## 設計課題
|
||||||
|
|
||||||
|
### 1. auth.json の書き戻しと競合制御
|
||||||
|
|
||||||
|
`~/.codex/auth.json` は Codex CLI と共用。両方が並行動作する場面で refresh token が古い値で上書きされると再認証が必要になる。ファイルロックを取るか、更新前に再読み込みして最新値で書くか。
|
||||||
|
|
||||||
|
### 2. refresh token の失効時の挙動
|
||||||
|
|
||||||
|
refresh token も失効するケース(Anthropic のサーバ側遮断のようにサブスク側が塞ぐケース)で、ユーザーにどう通知するか。insomnia CLI で `codex login` の再実行を促すメッセージを返す。
|
||||||
|
|
||||||
|
### 3. ChatGPT OAuth 専用モデル
|
||||||
|
|
||||||
|
`gpt-5-codex` / `codex-mini-latest` 等、ChatGPT backend でのみ利用可能なモデル ID がある場合、API key 経路と区別が必要。`ModelCapability` で「CodexOAuth でのみ有効」フラグを持つか、API 側の 401 を受けて動的にフォールバックするか。
|
||||||
|
|
||||||
|
### 4. セキュリティと権限
|
||||||
|
|
||||||
|
`~/.codex/auth.json` のパーミッション(600)を尊重。読み取り時にパーミッション確認 + 書き込み時にパーミッション再設定。
|
||||||
|
|
||||||
|
## Scope 外
|
||||||
|
|
||||||
|
- Claude Pro/Max OAuth 経路(方針上非採用)
|
||||||
|
- `claude -p` CLI fork 経路
|
||||||
|
- `codex login` 自体の実装(Codex CLI に任せ、insomnia は auth.json を読むのみ)
|
||||||
|
- ChatGPT backend の rate limit 観測(Retry-After 処理は HttpTransport 共通の責務)
|
||||||
|
|
||||||
|
## 依存
|
||||||
|
|
||||||
|
- `tickets/llm-model-config.md`(`AuthRef` / `HttpTransport` 構造)
|
||||||
|
- `tickets/llm-scheme-openai-responses.md`(`/v1/responses` wire format)
|
||||||
118
tickets/llm-model-config.md
Normal file
118
tickets/llm-model-config.md
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
# 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/openai` を `scheme/openai_chat` にリネーム
|
||||||
|
- `scheme/anthropic` / `scheme/gemini` はそのまま
|
||||||
|
- `scheme/openai_responses` は別チケット(llm-scheme-openai-responses)で新設
|
||||||
|
- Ollama は **scheme/anthropic を base_url 差し替えで流用**(独自 scheme は作らない)
|
||||||
|
|
||||||
|
4. **認証の分離**: `AuthRef` は `ApiKey(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 スケッチ:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
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 拡張)
|
||||||
68
tickets/llm-scheme-openai-responses.md
Normal file
68
tickets/llm-scheme-openai-responses.md
Normal file
|
|
@ -0,0 +1,68 @@
|
||||||
|
# OpenAI Responses scheme の新設
|
||||||
|
|
||||||
|
## 背景
|
||||||
|
|
||||||
|
現状の `crates/llm-worker/src/llm_client/scheme/openai` は OpenAI Chat Completions (`/v1/chat/completions`) wire format のみ実装。OpenAI の Responses API (`/v1/responses`) はリクエスト body・SSE イベント構造ともに Chat Completions と別物で、同じ scheme には乗らない。
|
||||||
|
|
||||||
|
Codex CLI (github.com/openai/codex) の実装を確認したところ、ChatGPT OAuth 経路でも OpenAI API Key 経路でもすべて `/v1/responses` を叩いており、Chat Completions は使っていない。Codex 流用(別チケット `llm-auth-codex-oauth`)を実現する前提として、この scheme が必要。
|
||||||
|
|
||||||
|
また OpenAI 本家の最新モデル(GPT-5 系・o シリーズの reasoning)は Responses API 経由が主要な経路であり、長期的にも Chat Completions の地位は低下していく。
|
||||||
|
|
||||||
|
## 要件
|
||||||
|
|
||||||
|
1. **`scheme/openai_responses` を新設**し、`HttpTransport<S: Scheme>` に差し込めるようにする
|
||||||
|
|
||||||
|
2. **リクエスト body** は `/v1/responses` の item-based 形式:
|
||||||
|
- `model`, `instructions` (system prompt 相当), `input: [ResponseItem]`, `tools`, `tool_choice`, `parallel_tool_calls`
|
||||||
|
- `reasoning: { effort?, summary? }`
|
||||||
|
- `store`, `stream: true`, `include: [String]`
|
||||||
|
- `service_tier?`, `prompt_cache_key?`, `text?: { verbosity?, format? }`
|
||||||
|
- `previous_response_id` は **使わない**(stateless で運用、履歴は insomnia 側管理)
|
||||||
|
|
||||||
|
3. **SSE event パース**:
|
||||||
|
- `response.created` / `response.completed` / `response.failed` / `response.incomplete`
|
||||||
|
- `response.output_item.added` / `response.output_item.done`
|
||||||
|
- `response.output_text.delta`
|
||||||
|
- `response.custom_tool_call_input.delta`(ToolCall 引数の partial JSON)
|
||||||
|
- `response.reasoning_text.delta` / `response.reasoning_summary_text.delta`
|
||||||
|
|
||||||
|
4. **BlockType / DeltaContent との対応**:
|
||||||
|
- `response.output_text.delta` → `DeltaContent::Text`
|
||||||
|
- `response.reasoning_text.delta` / `response.reasoning_summary_text.delta` → `DeltaContent::Thinking`
|
||||||
|
- `response.custom_tool_call_input.delta` → `DeltaContent::InputJson`
|
||||||
|
- `response.output_item.done` (tool_use) → `BlockMetadata::ToolUse { id, name }` の `BlockStart` 生成
|
||||||
|
|
||||||
|
5. **reasoning の item 構造対応**: `summary[]` / `encrypted_content` を持つ reasoning item の送受信をロスなく扱える
|
||||||
|
- 送信時は `BlockMetadata::Thinking` から `input[]` に再構築
|
||||||
|
- 受信時は `BlockType::Thinking` のブロックとしてストリームに流す
|
||||||
|
|
||||||
|
6. **認証は `AuthRef::ApiKey` のみ対応**: `Authorization: Bearer <api_key>` ヘッダ。`base_url` デフォルトは `https://api.openai.com`、パスは `/v1/responses`。ChatGPT OAuth 経路(`CodexOAuth`)は別チケット(`llm-auth-codex-oauth`)で追加
|
||||||
|
|
||||||
|
7. **Usage の正規化**: `response.completed` の `usage: { input_tokens, output_tokens, total_tokens }` を `UsageEvent` に変換。Chat Completions の `prompt_tokens` 等との表記揺れを scheme 側で吸収
|
||||||
|
|
||||||
|
8. **完了時の動作**: OpenAI API key (`OPENAI_API_KEY`) + モデル `gpt-5` 等で `ModelConfig { scheme: OpenAIResponses, base_url: https://api.openai.com, model_id: "gpt-5", auth: ApiKey }` を宣言すると、reasoning + tool call を含む会話が動作する
|
||||||
|
|
||||||
|
## 設計課題
|
||||||
|
|
||||||
|
### 1. reasoning item の encrypted_content
|
||||||
|
|
||||||
|
reasoning item の `encrypted_content` はサーバ側で暗号化された状態で返されることがあり、再送時にそのまま添える必要がある(ZDR 組織や `store=false` 運用時)。insomnia の `Item` enum に透過的に保持する仕組みが要る。
|
||||||
|
|
||||||
|
### 2. `include[]` と `store` のデフォルト
|
||||||
|
|
||||||
|
- `include: ["reasoning.encrypted_content"]` を常に付けるか、capability / config で制御するか
|
||||||
|
- `store=false` をデフォルトにするか `true` にするか(ZDR 既定なら false)
|
||||||
|
|
||||||
|
### 3. Responses 非対応パラメータ
|
||||||
|
|
||||||
|
`service_tier` / `prompt_cache_key` / `text.verbosity` は当面不要かもしれないが、将来対応時に scheme 拡張で入れられる構造にしておく。
|
||||||
|
|
||||||
|
## Scope 外
|
||||||
|
|
||||||
|
- ChatGPT OAuth 認証(`llm-auth-codex-oauth`)
|
||||||
|
- `previous_response_id` を使う stateful 運用
|
||||||
|
- 高次ツール(`web_search` / `code_interpreter` / `computer_use`)— insomnia では採用しない方針
|
||||||
|
|
||||||
|
## 依存
|
||||||
|
|
||||||
|
- `tickets/llm-model-config.md`(`HttpTransport<S>` 構造と `AuthRef` が前提)
|
||||||
Loading…
Reference in New Issue
Block a user