diff --git a/CHANGELOG.md b/CHANGELOG.md index a8b34ae..a1d8f36 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,10 +22,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Configured `fetch-depth: 0` in GitHub Actions checkout step to allow `gitleaks` secret detection to inspect the full commit history correctly. ### Added +- **OpenAI Decisions API 互換エンドポイント (`POST /v1/decisions`, PR #127)**: + - Logit Gate モデル(`LOGIT_GATE_MODELS`)と `AsciiMatcher` を用いた意思決定評価エンドポイントを追加。 + - `predicate`(確率判定)、`choice`(選択肢の Softmax 確率分布と確信度)、`score`(レベル別確率分布と期待値スコア)、`refusal`(未対応・不備時の拒否応答)をサポート。 + - `BaseDecisionsService` / `DecisionsService` / `MockDecisionsService` による DI 構造、推論セマフォ制御、および DoS 防止バリデーションを実装。 - **GPU 実機 Locust 負荷テスト自動化スイート & 検証レポート (`scripts/run_gpu_locust_suite.py`, `docs/infrastructure/benchmark_gpu_locust_load_test.md`)**: - NVIDIA GeForce RTX 3060 12GB 上で主要4モデル(Ruri-310m, EmbeddingGemma-2, Ruri-Reranker-310m, BGE-Visualized-M3)を同時展開し、10〜60同時ユーザー負荷における全12種エンドポイントの実測データ(1,630リクエスト、エラー率 0.00%)を記録。 ### Changed +- **RerankService の責務分離と境界値防御 (`src/app/services/rerank.py`, PR #126)**: + - `create_rerank` の処理を `_process_tei_proxy`, `_process_logit_gate`, `_process_standard_rerank` へ分離し、TEI プロキシ応答時の不正インデックス検証とキャラクタリゼーションテスト(`src/tests/test_rerank_characterization.py`)を追加。 - **PyTorch Source Constraint Relaxation (`pyproject.toml`)**: - Relaxed CPU-exclusive wheel index pinning for `torch` and `torchvision` to allow out-of-the-box CUDA GPU hardware acceleration while preserving CPU fallback. - **Code Style Alignment**: diff --git a/README.md b/README.md index 7461a51..af36889 100644 --- a/README.md +++ b/README.md @@ -198,6 +198,19 @@ curl -X POST "http://localhost:8000/v1/rerank" \ --- +### 2.4.1. 意思決定・ロジットルーター (Decisions API) + +`POST /v1/decisions` + +Logit Gate(`Qwen/Qwen2.5-1.5B-Instruct` 等)と ASCII Matcher を OpenAI Decisions API 互換スキーマで利用できるエンドポイントです。入力コンテキストに対して以下の質問タイプ(`questions`)を一括評価できます。 + +* **`predicate`**: コンテキストが指示条件を満たす確率 `probability`($0.0 \sim 1.0$)を返却。 +* **`choice`**: 候補 `options` ごとの Softmax 確率分布 `probabilities`、最良の選択肢 `choice`、および確信度 `confidence` を返却。 +* **`score`**: スコア定義 `levels`(`index`, `description`)ごとの確率分布 `probabilities` と期待値 `score` を返却。 +* **`refusal`**: 未対応の質問タイプや `options` / `levels` 欠落時に拒否理由を返却。 + +--- + ### 2.5. モデル一覧取得 (Models) `GET /v1/models` diff --git a/docs/architecture/services.md b/docs/architecture/services.md index 33b79e3..a6d55f8 100644 --- a/docs/architecture/services.md +++ b/docs/architecture/services.md @@ -5,7 +5,7 @@ description: BaseEmbeddingService/BaseRerankServiceの抽象化、FastAPI Depend status: stable generated: by: agent/antigravity - at: 2026-08-29T13:42:00Z + at: 2026-10-18T06:30:00Z tags: - architecture - dependency-injection @@ -19,6 +19,8 @@ sources: title: Concrete Embedding Service - resource: /src/app/services/rerank.py title: Concrete Rerank Service + - resource: /src/app/services/decisions.py + title: Concrete Decisions Service - resource: /src/app/services/mock.py title: Mock Services for Fast Testing - resource: /src/app/main.py @@ -33,38 +35,46 @@ APIルーター(FastAPI エンドポイント)とモデル推論・外部プ ```mermaid graph TD - Router["FastAPI ルーター (src/app/main.py)"] + Router["FastAPI ルーター (src/app/routers/)"] subgraph "依存性注入 (FastAPI Depends)" get_emb["get_embedding_service()"] get_rr["get_rerank_service()"] + get_dec["get_decisions_service()"] end subgraph "抽象基底クラス (src/app/services/base.py)" BaseEmb["BaseEmbeddingService"] BaseRR["BaseRerankService"] + BaseDec["BaseDecisionsService"] end subgraph "本番具象サービス" EmbServ["EmbeddingService (src/app/services/embedding.py)"] RRServ["RerankService (src/app/services/rerank.py)"] + DecServ["DecisionsService (src/app/services/decisions.py)"] end subgraph "テスト用高速モック" MockEmb["MockEmbeddingService (src/app/services/mock.py)"] MockRR["MockRerankService (src/app/services/mock.py)"] + MockDec["MockDecisionsService (src/app/services/mock.py)"] end Router -->|"Depends"| get_emb Router -->|"Depends"| get_rr + Router -->|"Depends"| get_dec get_emb -->|"返却"| BaseEmb get_rr -->|"返却"| BaseRR + get_dec -->|"返却"| BaseDec BaseEmb <|-- EmbServ BaseEmb <|-- MockEmb BaseRR <|-- RRServ BaseRR <|-- MockRR + BaseDec <|-- DecServ + BaseDec <|-- MockDec ``` --- @@ -74,17 +84,20 @@ graph TD ### 2.1. 抽象基底クラス (`src/app/services/base.py`) - **`BaseEmbeddingService`**: `create_embeddings(request: EmbeddingRequest) -> EmbeddingResponse` を定義。 - **`BaseRerankService`**: `create_rerank(request: RerankRequest) -> RerankResponse` を定義。 +- **`BaseDecisionsService`**: `create_decisions(request: DecisionsRequest) -> DecisionsResponse` を定義。 -### 2.2. 本番実装 (`EmbeddingService`, `RerankService`) +### 2.2. 本番実装 (`EmbeddingService`, `RerankService`, `DecisionsService`) - **`EmbeddingService`**: - テキスト・画像(Base64 / URL)のパース、SSRF検証。 - TEI Proxy有効時の自動ルーティング。 - Ruri-v3プレフィックス付与とトークン切り詰め(Truncation)。 - `anyio.to_thread.run_sync` によるスレッドセーフなモデル推論実行。 - **`RerankService`**: - - TEI Proxy / ローカル CrossEncoder の切り替え。 - - クエリ・ドキュメントペアのスコアリングとトークン数算出。 - - `top_n` による上位絞り込みとソート処理。 + - `create_rerank` をオーケストレーターとし、経路別プライベートヘルパー(`_process_tei_proxy`, `_process_logit_gate`, `_process_standard_rerank`)へ責務を分離。 + - TEI Proxy 応答時のドキュメントインデックス境界検証、クエリ・ドキュメントペアのスコアリングとトークン数算出、`top_n` による上位絞り込みとソート処理。 +- **`DecisionsService`**: + - OpenAI Decisions API 互換の意思決定評価サービス。 + - `LOGIT_GATE_MODELS`(`Qwen/Qwen2.5-1.5B-Instruct` 等)と `LogitGateService` + `AsciiMatcher` を用い、`anyio.to_thread.run_sync` でスレッドセーフに `predicate`(確率判定)、`choice`(Softmax 選択肢確率分布)、`score`(レベル別確率分布と期待値スコア)、および `refusal` を算出。 --- diff --git a/docs/domain/schemas.md b/docs/domain/schemas.md index a9c3b07..65db6f2 100644 --- a/docs/domain/schemas.md +++ b/docs/domain/schemas.md @@ -5,7 +5,7 @@ description: Pydantic モデル、リクエスト・レスポンス定義、お status: stable generated: by: agent/antigravity - at: 2026-08-16T09:10:00Z + at: 2026-10-18T06:30:00Z tags: - schemas - pydantic @@ -125,7 +125,77 @@ sources: --- -## 3. モデル一覧エンドポイント (`GET /v1/models`) +## 3. 意思決定エンドポイント (`POST /v1/decisions`) + +Logit Gate(`LOGIT_GATE_MODELS`)と `AsciiMatcher` を用いた OpenAI Decisions API 互換エンドポイントです。 + +### リクエスト (`DecisionsRequest`) +```json +{ + "model": "Qwen/Qwen2.5-1.5B-Instruct", + "input": [ + { + "role": "user", + "content": [ + {"type": "input_text", "text": "エラーコード E-5001 が発生しました。"} + ] + } + ], + "questions": [ + { + "type": "predicate", + "name": "has_error", + "instructions": "エラーコード E-5001 に関する記述が含まれているか?" + }, + { + "type": "choice", + "name": "category", + "instructions": "障害カテゴリを選択してください。", + "options": ["Network", "Database"] + }, + { + "type": "score", + "name": "severity", + "instructions": "重要度を評価してください。", + "levels": [ + {"index": 0, "description": "Low"}, + {"index": 1, "description": "High"} + ] + } + ] +} +``` + +### レスポンス (`DecisionsResponse`) +```json +{ + "answers": [ + { + "name": "has_error", + "type": "predicate", + "probability": 0.9241 + }, + { + "name": "category", + "type": "choice", + "choice": "Network", + "probabilities": {"Network": 0.78, "Database": 0.22}, + "confidence": 0.78 + }, + { + "name": "severity", + "type": "score", + "score": 0.85, + "probabilities": [0.15, 0.85] + } + ] +} +``` +> ※ 未対応の `type` や、`choice` / `score` で `options` / `levels` が空または欠落している場合は、`type: "refusal"`(`RefusalAnswer`)が返却されます。 + +--- + +## 4. モデル一覧エンドポイント (`GET /v1/models`) ### レスポンス (`ModelList`) ```json @@ -145,7 +215,7 @@ sources: --- -## 4. モデルアンロードエンドポイント (`POST /v1/models/unload`) +## 5. モデルアンロードエンドポイント (`POST /v1/models/unload`) ### リクエスト (`UnloadRequest`) ```json @@ -164,7 +234,7 @@ sources: --- -## 5. ヘルス・レディネス・メトリクス +## 6. ヘルス・レディネス・メトリクス - `GET /health`, `GET /healthz`: `{"status": "ok"}` - `GET /ready`: `{"status": "ready", "gpu_available": true, "models_loaded": [...]}` @@ -172,7 +242,7 @@ sources: --- -## 6. 標準エラーレスポンス (`ErrorResponse`) +## 7. 標準エラーレスポンス (`ErrorResponse`) HTTP 400, 401, 413, 429, 500, 503 時の共通レスポンス構造: ```json diff --git a/docs/log.md b/docs/log.md index 29b9bbe..f03892f 100644 --- a/docs/log.md +++ b/docs/log.md @@ -1,5 +1,16 @@ # Knowledge Update Log +## 2026-10-18 +* **Update (PR Triage, Jules Orchestration, Adversarial TDD & Merge)**: GitHub 上のオープン PR 2件(#126, #127)の評価・改修指示・TDD拡充・実働負荷検証および Squash Merge を完了。 + * **#126 (`refactor: separate concerns in RerankService with characterization tests`)**: + * `RerankService.create_rerank` を `_process_tei_proxy`, `_process_logit_gate`, `_process_standard_rerank` に分離するリファクタリングを採用。 + * 批判的テスト(Adversarial Testing)として、TEI プロキシが範囲外・負数・真偽値のドキュメントインデックスを返した場合の境界値検証と異常系テストを `src/tests/test_rerank_characterization.py` に追加。 + * **#127 (`Add OpenAI Decisions API compatible /v1/decisions endpoint`)**: + * 当初の架空モデル(`gpt-6-luna`)および固定ダミー値実装に対し、`jules-runner` オーケストレーションを通じて Logit Gate モデル(`LOGIT_GATE_MODELS`)と DI アーキテクチャ(`BaseDecisionsService`, `DecisionsService`, `MockDecisionsService`)、推論セマフォ保護、DoS 防止制約への改修を実施。 + * さらに `AsciiMatcher` 3-gram ブースト統合、リクエスト毎の不要な `calibrate()` 排除、重複 `options` の正規化、空配列 `options=[]`/`levels=[]` の Refusal 応答、セマフォタイムアウト(503)等の Adversarial テストを `src/tests/test_decisions.py` に追加。 + * **実働・負荷テスト検証**: 全282件の単体テスト(100%パス)、`ruff check` / `ruff format --check` / `uv audit` クリアに加え、Uvicorn 実サーバーによる `/v1/decisions`, `/v1/rerank`, `/v1/embeddings` の E2E 検証および 8ワーカー並行負荷テスト(64リクエスト、エラー率 0.0%、約 151 req/sec)を確認。 +* **Update (Documentation Sync)**: `README.md`, `CHANGELOG.md`, `docs/architecture/services.md`, `docs/domain/schemas.md` を最新実装に同期。 + ## 2026-10-12 * **PR Review, Merge & Code Health**: GitHub 上の未処理 PR 19件(#107〜#125)のレビュー・検証・処理を実施。 * **採用・マージ (15件)**: