diff options
| author | tanaka takashi <isazaka@gmail.com> | 2026-09-12 18:04:56 +0900 |
|---|---|---|
| committer | tanaka takashi <isazaka@gmail.com> | 2026-09-12 18:04:56 +0900 |
| commit | cba15a62829a0fe57a03c8031f981e8f790e2efc (patch) | |
| tree | 3bc0ef1a243f356f55c2fa97f63c3bfd5c77e8fc /doc | |
feat: Stable Diffusion 1.x MCPサーバを実装
仕様書・設計書に基づき、ローカルCUDA GPU上のStable Diffusion 1.x互換モデルをMCP経由で利用するサーバを新規実装。
実装内容:
- generate_image、generate_guided_image、inpaint_imageの3ツールをMCP SDK v2で公開
- TOMLからcheckpoint、VAE、複数LoRAと重み、ControlNet、生成設定を読み込み
- モデル種別ごとの固定ディレクトリ解決と起動時の設定・ファイル存在検証を追加
- Depth/MiDaS、OpenPose body/full、ControlNet Inpaintの前処理とパイプライン切替を実装
- FP16/CUDA、1枚固定生成、ControlNetオンデマンドロード、単一GPU直列実行を実装
- Base64 PNG/JPEG入力の形式・サイズ検証と任意のdata URI入力に対応
- seed、サイズ、モード、時刻、処理時間をPNG metadataへ記録
- 任意のローカルPNG保存、生成エラー分類、CUDA OOM後のキャッシュ回復を追加
- --configと--versionを備えたstdio CLIエントリポイントを追加
開発・運用:
- Python 3.11およびuvによる依存・lock管理を構成
- config.example.tomlとセットアップ・運用手順をREADMEへ追加
- Ruff、Pyright、pytestの設定を追加
- 設定、パス解決、schema、画像検証、サービスルーティング、排他制御、MCP画像返却をテスト
検証結果:
- pytest: 30 passed
- Ruff lint/format: passed
- Pyright: 0 errors, 0 warnings
- CLI versionおよびMCPクライアント経由のPNG返却を確認
- 実GPU smoke testはCUDA GPUとモデルファイルがないため対象外
Diffstat (limited to 'doc')
| -rw-r--r-- | doc/Design.md | 418 | ||||
| -rw-r--r-- | doc/Specifications.md | 256 |
2 files changed, 674 insertions, 0 deletions
diff --git a/doc/Design.md b/doc/Design.md new file mode 100644 index 0000000..f22c24d --- /dev/null +++ b/doc/Design.md @@ -0,0 +1,418 @@ +# Design.md + +## 1. 設計方針 + +| 方針 | 内容 | +|---|---| +| MCP公開面を小さくする | LLMには生成意図に必要な入力だけを公開する | +| 実行設定はTOMLへ集約 | モデル・VAE・LoRA・ControlNet・生成パラメータを一元管理 | +| SD1.x互換に限定 | SDXL / FLUX等は対象外 | +| 依存分離 | MCP層とDiffusion層を直接密結合させない | +| 単一GPU前提 | 生成処理は1本のキューで直列化 | +| ControlNetは固定3種 | Depth / Openpose / Inpaintのみ | +| 過剰な汎用化を避ける | 未使用ControlNetや動的Tool設定は持たない | + +--- + +## 2. 全体アーキテクチャ + +```text +MCP Client / LLM + │ + ▼ +┌──────────────────────┐ +│ MCP Server │ +│ - tool schema │ +│ - request validation │ +└──────────┬───────────┘ + │ + ▼ +┌──────────────────────┐ +│ ImageGenerationService│ +│ - request routing │ +│ - queue control │ +└──────────┬───────────┘ + │ + ▼ +┌──────────────────────┐ +│ ModelManager │ +│ - checkpoint │ +│ - VAE │ +│ - LoRA │ +│ - ControlNet │ +└──────────┬───────────┘ + │ + ▼ +┌──────────────────────┐ +│ Diffusers / PyTorch │ +│ CUDA │ +└──────────────────────┘ +``` + +--- + +## 3. モジュール構成 + +```text +mcp-sd15/ +├─ pyproject.toml +├─ uv.lock +├─ .python-version +├─ config.example.toml +├─ src/ +│ └─ mcp_sd15/ +│ ├─ __init__.py +│ ├─ cli.py +│ ├─ server.py +│ ├─ config.py +│ ├─ schema.py +│ ├─ service.py +│ ├─ model_manager.py +│ ├─ pipeline_factory.py +│ ├─ preprocessors/ +│ │ ├─ depth.py +│ │ └─ openpose.py +│ └─ tools/ +│ ├─ generate_image.py +│ ├─ generate_guided_image.py +│ └─ inpaint_image.py +└─ tests/ + ├─ test_config.py + ├─ test_schema.py + ├─ test_model_paths.py + └─ test_tools.py +``` + +--- + +## 4. 各モジュールの責務 + +| モジュール | 責務 | +|---|---| +| `cli.py` | `--config`, `--version`の処理 | +| `server.py` | MCP server起動、Tool登録 | +| `config.py` | TOML読込、型変換、validation | +| `schema.py` | MCP入力値・内部request型 | +| `service.py` | Tool要求を生成処理へ振り分け | +| `model_manager.py` | checkpoint / VAE / LoRA / ControlNetの管理 | +| `pipeline_factory.py` | Diffusers pipeline構築 | +| `preprocessors/depth.py` | Midas前処理 | +| `preprocessors/openpose.py` | Openpose / Openpose Full前処理 | +| `tools/*` | MCP Toolからserviceへの薄いadapter | + +--- + +## 5. 起動フロー + +| Step | 処理 | +|---|---| +| 1 | CLI引数を解析 | +| 2 | TOMLを読込 | +| 3 | 設定値をvalidation | +| 4 | モデルパスを解決 | +| 5 | checkpoint存在確認 | +| 6 | VAE / LoRA / ControlNet存在確認 | +| 7 | Base SD1.x pipelineを構築 | +| 8 | VAEを適用 | +| 9 | LoRAを順次ロード | +| 10 | GPUへ配置 | +| 11 | MCP Toolを公開 | +| 12 | リクエスト待受開始 | + +モデル構成の初期化に失敗した場合はToolを公開せず、プロセスを異常終了する。 + +--- + +## 6. 設定オブジェクト + +推奨内部型: + +```python +@dataclass(frozen=True) +class LoRAConfig: + file: str + weight: float = 1.0 + +@dataclass(frozen=True) +class ModelConfig: + root: Path + checkpoint: str + vae: str | None + loras: tuple[LoRAConfig, ...] + +@dataclass(frozen=True) +class GenerationConfig: + steps: int + cfg_scale: float + scheduler: str + default_size: str +``` + +Pydanticは必須とせず、`tomllib + dataclass + 明示的validation`を基本とする。 + +--- + +## 7. モデルパス解決 + +| 種類 | 解決先 | +|---|---| +| checkpoint | `<root>/checkpoints/<file>` | +| VAE | `<root>/vae/<file>` | +| LoRA | `<root>/lora/<file>` | +| ControlNet | `<root>/controlnet/<file>` | + +設計上、MCP入力から任意モデルパスを指定することは禁止する。 + +--- + +## 8. Diffusers構成 + +| 機能 | 実装方針 | +|---|---| +| Base model | SD1.x互換checkpointをロード | +| VAE | 設定された場合のみ置換 | +| LoRA | 複数adapterをロード | +| LoRA weight | `set_adapters`相当で適用 | +| LoRA fuse | 将来オプション。初期実装では必須としない | +| dtype | FP16 | +| device | CUDA | +| scheduler | TOMLで指定 | +| batch | 1 | +| images per prompt | 1 | +| CPU offload | 初期値OFF | + +--- + +## 9. ControlNet設計 + +### 9.1 モード + +| MCP mode | ControlNet | Preprocessor | +|---|---|---| +| `depth` | Depth 1.1 | Midas | +| `openpose` | Openpose 1.1 | body | +| `openpose_full` | Openpose 1.1 | body + hand + face | +| `inpaint` | Inpaint 1.1 | mask生成規約に従う | + +### 9.2 パイプライン + +```text +Depth/Openpose: +input image + │ + ▼ +preprocessor + │ + ▼ +control image + │ + ▼ +StableDiffusionControlNetPipeline + │ + ▼ +PNG +``` + +```text +Inpaint: +input image + mask + │ + ▼ +ControlNet Inpaint input + │ + ▼ +Inpaint-compatible pipeline + │ + ▼ +PNG +``` + +### 9.3 ロード方針 + +8GB級GPUを主対象とするため、ControlNetは必要時ロードを基本候補とする。 + +| 状態 | 動作 | +|---|---| +| 未ロード | 必要なControlNetをロード | +| 同一ControlNet | 再利用 | +| 別ControlNet要求 | 既存ControlNetを解放後ロード | +| generation中 | 切替禁止 | + +--- + +## 10. MCP Tool設計 + +### `generate_image` + +```text +Input: +- positive_prompt +- negative_prompt? +- size? + +Output: +- image/png +- generation metadata +``` + +### `generate_guided_image` + +```text +Input: +- positive_prompt +- negative_prompt? +- control_mode: depth | openpose | openpose_full +- input_image +- size? + +Output: +- image/png +- generation metadata +``` + +### `inpaint_image` + +```text +Input: +- positive_prompt +- negative_prompt? +- input_image +- mask_image +- size? + +Output: +- image/png +- generation metadata +``` + +Tool層はDiffusers APIを直接呼ばず、必ずService層を経由する。 + +--- + +## 11. 生成キュー + +```text +MCP Request + │ + ▼ +asyncio.Semaphore(1) + │ + ▼ +GenerationService + │ + ▼ +GPU +``` + +| 項目 | 方針 | +|---|---| +| 同時GPU処理 | 1 | +| キュー | FIFO相当 | +| OOM防止 | 並列生成禁止 | +| cancellation | MCP SDKで扱える範囲で対応 | +| timeout | 設定値として追加可能 | + +--- + +## 12. 出力 + +| 項目 | 仕様 | +|---|---| +| Format | PNG | +| MCP返却 | ImageContent相当 | +| ローカル保存 | 設定により有効化可能 | +| ファイル名 | UUIDまたはgeneration ID | +| metadata | seed, size, mode, timestamp等 | +| prompt保存 | ログ方針により設定 | + +--- + +## 13. エラー設計 + +| エラー分類 | 例 | 処理 | +|---|---|---| +| CONFIG_ERROR | TOML不正 | 起動失敗 | +| MODEL_NOT_FOUND | モデル未配置 | 起動失敗 | +| MODEL_INCOMPATIBLE | SD1.x非互換 | 起動失敗 | +| INVALID_REQUEST | 不正size / mode | Tool error | +| INVALID_IMAGE | 画像形式不正 | Tool error | +| CUDA_OOM | VRAM不足 | Tool error + GPU状態回復 | +| GENERATION_FAILED | 推論例外 | Tool error | +| INTERNAL_ERROR | 想定外例外 | ログ出力 + Tool error | + +--- + +## 14. pyproject / uv方針 + +### Runtime依存 + +| Package | 用途 | +|---|---| +| `mcp` | MCP server | +| `torch` | CUDA / Tensor | +| `diffusers` | SD / ControlNet | +| `transformers` | CLIP | +| `peft` | LoRA | +| `safetensors` | モデル形式 | +| `pillow` | 画像I/O | +| `numpy` | 数値処理 | +| `opencv-python` | 画像前処理 | + +### Development依存 + +| Package | 用途 | +|---|---| +| `pytest` | テスト | +| `ruff` | lint / format | +| `pyright` | 型チェック | + +### 例 + +```toml +[project] +name = "mcp-sd15" +version = "0.1.0" +requires-python = ">=3.11,<3.12" + +[project.scripts] +mcp-sd15 = "mcp_sd15.cli:main" +``` + +運用は`pyproject.toml + uv.lock`を基準とする。 + +--- + +## 15. テスト設計 + +| テスト種別 | GPU | 内容 | +|---|---:|---| +| Config parsing | 不要 | TOML読込・型検証 | +| Path resolution | 不要 | モデルパス解決 | +| MCP schema | 不要 | Tool schema検証 | +| Request validation | 不要 | mode / size / image必須条件 | +| Service routing | 不要 | 適切なbackend呼出 | +| Model load smoke | 必要 | checkpoint / VAE / LoRA | +| Txt2Img smoke | 必要 | 512x512生成 | +| Depth smoke | 必要 | ControlNet Depth | +| Openpose smoke | 必要 | Openpose / Full | +| Inpaint smoke | 必要 | mask付き生成 | + +--- + +## 16. 将来拡張 + +初期設計では以下を実装対象外とする。 + +| 項目 | 方針 | +|---|---| +| SDXL / FLUX | 対象外 | +| Multi-ControlNet | 対象外 | +| MCPからのモデル切替 | 対象外 | +| 任意width / height | 対象外 | +| batch > 1 | 対象外 | +| LoRA動的追加 | 対象外 | +| Web UI | 対象外 | +| 学習機能 | 対象外 | +| 複数GPU | 対象外 | + +必要になった場合のみ、既存のService / ModelManager境界を維持したまま拡張する。 diff --git a/doc/Specifications.md b/doc/Specifications.md new file mode 100644 index 0000000..3d2cd28 --- /dev/null +++ b/doc/Specifications.md @@ -0,0 +1,256 @@ +# Specifications.md + +## 1. 目的 + +本システムは、LLMがMCP経由でローカルGPU上のStable Diffusion 1.x互換モデルを利用して画像生成・画像誘導生成・インペイントを実行できるMCPサーバを提供する。 + +LLM側には生成意図に必要な最小限の入力のみを公開し、モデル構成・VAE・LoRA・ControlNet・生成パラメータなどの実行環境設定はMCPサーバ側で管理する。 + +--- + +## 2. スコープ + +| 区分 | 内容 | +|---|---| +| 対応モデル | Stable Diffusion 1.x互換モデル | +| モデル差し替え | 対応 | +| VAE差し替え | 対応 | +| LoRA | 複数指定・重み指定に対応 | +| ControlNet | ControlNet 1.1 Depth / Openpose / Inpaint | +| MCP経由のモデル変更 | 非対応 | +| モデル・生成設定 | TOML設定ファイルで管理 | +| CLI | `--config`, `--version` のみ | +| パッケージ管理 | uv | +| 実装言語 | Python 3.11 | +| GPU | NVIDIA CUDA GPUを主対象 | +| 主対象VRAM | 8GB級 | +| バッチ生成 | 1枚固定 | +| 同時生成 | 1ジョブのみ | +| 出力形式 | PNG | + +--- + +## 3. 機能要件 + +### 3.1 MCPツール + +| Tool | 用途 | 必須入力 | 任意入力 | +|---|---|---|---| +| `generate_image` | Text-to-Image | `positive_prompt` | `negative_prompt`, `size` | +| `generate_guided_image` | Depth / Openposeによる誘導生成 | `positive_prompt`, `control_mode`, `input_image` | `negative_prompt`, `size` | +| `inpaint_image` | 指定領域の再生成 | `positive_prompt`, `input_image`, `mask_image` | `negative_prompt`, `size` | + +### 3.2 `generate_image` + +| 項目 | 仕様 | +|---|---| +| positive_prompt | 必須文字列 | +| negative_prompt | 任意文字列。未指定時は空文字 | +| size | 許可済みサイズの列挙値 | +| 出力 | PNG画像1枚 | +| seed | 内部生成。結果メタデータへの記録は可 | + +### 3.3 `generate_guided_image` + +| 項目 | 仕様 | +|---|---| +| positive_prompt | 必須文字列 | +| negative_prompt | 任意文字列 | +| control_mode | `depth`, `openpose`, `openpose_full` | +| input_image | 必須 | +| size | 許可済みサイズの列挙値 | +| ControlNet同時利用 | 1種類のみ | + +### 3.4 `inpaint_image` + +| 項目 | 仕様 | +|---|---| +| positive_prompt | 必須文字列 | +| negative_prompt | 任意文字列 | +| input_image | 必須 | +| mask_image | 必須 | +| size | 許可済みサイズの列挙値 | +| 出力 | PNG画像1枚 | + +--- + +## 4. ControlNet要件 + +| モード | モデル | 入力 | 前処理 | +|---|---|---|---| +| Depth | ControlNet 1.1 Depth | 元画像 | Midas固定 | +| Openpose | ControlNet 1.1 Openpose | 元画像 | body | +| Openpose Full | ControlNet 1.1 Openpose | 元画像 | body + hand + face | +| Inpaint | ControlNet 1.1 Inpaint | 元画像 + mask | 専用Inpaint入力 | + +### 制約 + +| 項目 | 仕様 | +|---|---| +| Multi-ControlNet | 非対応 | +| Preprocessor選択 | MCPには公開しない | +| ControlNet strength | MCPには公開しない | +| ControlNetモデル切替 | 設定ファイルで管理 | +| GPU常駐 | 必要時ロード方式を許容 | + +--- + +## 5. 画像サイズ + +初期対応サイズはStable Diffusion 1.xの512px基準に限定する。 + +| ID | 解像度 | 用途 | +|---|---:|---| +| `square_512` | 512 x 512 | 標準 | +| `landscape_512` | 512 x 384 | 横長 | +| `portrait_512` | 384 x 512 | 縦長 | + +自由なwidth/height指定はMCPには公開しない。 + +--- + +## 6. モデル設定要件 + +### 6.1 設定方法 + +| 項目 | 仕様 | +|---|---| +| 設定形式 | TOML | +| 設定指定 | CLI `--config PATH` | +| デフォルト設定 | アプリケーション側で最小限保持可 | +| CLI上書き | 行わない | +| MCP経由変更 | 行わない | +| 起動時検証 | 必須 | + +### 6.2 モデル構成 + +| 項目 | 仕様 | +|---|---| +| Base checkpoint | 1ファイル | +| VAE | 0または1ファイル | +| LoRA | 0個以上 | +| LoRA weight | LoRAごとに指定 | +| ControlNet | Depth / Openpose / Inpaintを個別設定 | +| モデルルート | TOMLで1つのルートディレクトリを指定 | + +### 6.3 推奨ディレクトリ + +```text +models/ +├─ checkpoints/ +├─ vae/ +├─ lora/ +└─ controlnet/ +``` + +設定値には原則としてファイル名を記述し、内部で用途別ディレクトリへ解決する。 + +--- + +## 7. 設定ファイル仕様 + +例: + +```toml +[model] +root = "D:/AI/models" +checkpoint = "model.safetensors" +vae = "vae.safetensors" + +[[model.lora]] +file = "detail.safetensors" +weight = 0.8 + +[[model.lora]] +file = "style.safetensors" +weight = 0.5 + +[generation] +steps = 25 +cfg_scale = 7.0 +scheduler = "dpmpp_2m" +default_size = "square_512" + +[controlnet.depth] +enabled = true +model = "control_v11f1p_sd15_depth.safetensors" +preprocessor = "midas" + +[controlnet.openpose] +enabled = true +model = "control_v11p_sd15_openpose.safetensors" + +[controlnet.inpaint] +enabled = true +model = "control_v11p_sd15_inpaint.safetensors" + +[output] +directory = "./outputs" +``` + +--- + +## 8. CLI要件 + +| 引数 | 仕様 | +|---|---| +| `--config PATH` | 使用するTOML設定ファイルを指定 | +| `--version` | アプリケーションのバージョンを表示 | + +その他の実行設定はCLIでは提供しない。 + +--- + +## 9. 非機能要件 + +| 分類 | 要件 | +|---|---| +| VRAM | 8GB級GPUでの動作を主対象 | +| Precision | FP16を基本 | +| Batch | 1固定 | +| 同時実行 | 1生成のみ | +| キュー | 単一GPUキュー | +| ネットワーク | 推論時に外部通信を必須としない | +| モデルDL | 起動時の自動ダウンロードを前提としない | +| 異常系 | 設定不正・モデル不正は起動時エラー | +| ログ | 実行状態、モデル構成、エラーを記録 | +| 再現性 | seedを結果メタデータとして記録可能 | +| セキュリティ | 設定外パスへの任意アクセスを避ける | +| 保守性 | MCP層と画像生成層を分離 | + +--- + +## 10. 主要コンポーネント + +| コンポーネント | 役割 | +|---|---| +| Python 3.11 | 実装言語 | +| uv | Python・依存関係・仮想環境・lock管理 | +| MCP Python SDK | MCP server実装 | +| PyTorch | CUDA / Tensor実行 | +| Diffusers | SD1.x / VAE / LoRA / ControlNet統合 | +| Transformers | CLIP tokenizer / text encoder | +| PEFT | LoRA adapter管理 | +| Safetensors | モデルファイル読込 | +| Pillow | 画像入出力 | +| NumPy | 画像・前処理 | +| OpenCV | 必要な画像前処理 | +| tomllib | TOML設定読込 | +| pytest | テスト | +| Ruff | lint / format | +| Pyright | 静的型チェック | + +--- + +## 11. 完了条件 + +| ID | 完了条件 | +|---|---| +| G1 | TOMLからcheckpoint / VAE / 複数LoRAをロードできる | +| G2 | `generate_image`でPNG画像を1枚生成できる | +| G3 | Depth / Openpose / Openpose Full生成が動作する | +| G4 | Inpaintが元画像+maskから動作する | +| G5 | 3ツールすべてMCP経由で呼び出せる | +| G6 | 同時生成が1ジョブに制御される | +| G7 | 不正設定・不正モデル時に起動失敗する | +| G8 | GPU不要の設定・schemaテストが自動実行できる | |
