Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**:
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
25 changes: 19 additions & 6 deletions docs/architecture/services.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
```

---
Expand All @@ -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` を算出。

---

Expand Down
80 changes: 75 additions & 5 deletions docs/domain/schemas.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -145,7 +215,7 @@ sources:

---

## 4. モデルアンロードエンドポイント (`POST /v1/models/unload`)
## 5. モデルアンロードエンドポイント (`POST /v1/models/unload`)

### リクエスト (`UnloadRequest`)
```json
Expand All @@ -164,15 +234,15 @@ sources:

---

## 5. ヘルス・レディネス・メトリクス
## 6. ヘルス・レディネス・メトリクス

- `GET /health`, `GET /healthz`: `{"status": "ok"}`
- `GET /ready`: `{"status": "ready", "gpu_available": true, "models_loaded": [...]}`
- `GET /metrics`: Prometheus テキスト形式メトリクス

---

## 6. 標準エラーレスポンス (`ErrorResponse`)
## 7. 標準エラーレスポンス (`ErrorResponse`)

HTTP 400, 401, 413, 429, 500, 503 時の共通レスポンス構造:
```json
Expand Down
11 changes: 11 additions & 0 deletions docs/log.md
Original file line number Diff line number Diff line change
@@ -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件)**:
Expand Down
Loading