Umaru 全機能仕様書(実装準拠版)
Umaru 全機能仕様書(実装準拠版)
本書はリポジトリの実コード(
backend/の Python FastAPI、src/の React、src-tauri/の Rust)を直接読み解いて再構成した、Umaru v1.3.0 の全機能・全仕様リファレンスである。各仕様には対応する実装ファイル/関数/エンドポイントを併記している。
- 製品名: Umaru —「祈璃(いのり)と作るローカル画像生成・LoRA 学習アプリ」
- バージョン: 1.3.0(
package.json/tauri.conf.json) - 識別子:
app.umaru.inori - 対象 OS: Windows 11(配布ターゲットは NSIS / perMachine)。Rust 側は macOS / Linux 分岐も保持
- コンセプト: ローカル PC 完結で、SDXL 系モデルによる画像生成と LoRA 学習を「初心者でも迷わない言葉」で進める常駐型デスクトップアプリ
目次
- システムアーキテクチャ
- 起動シーケンスとプロセス監視
- ディレクトリ・データ配置
- データモデル(SQLite)
- 認証・セキュリティ
- 画面構成・ナビゲーション・用語体系
- 機能詳細
- 生成エンジン詳細
- ジョブ・進捗管理
- エラー分類
- REST / WebSocket API 一覧
- Tauri ネイティブコマンド一覧
- ビルド・配布・リリースゲート
- テスト構成
1. システムアーキテクチャ
Umaru は 3 層構成。Tauri(Rust)がアプリ本体(ウィンドウ+ライフサイクル)を担い、WebView 内で React UI を描画し、画像生成・学習などの重い処理は Python サイドカープロセス(FastAPI/uvicorn)にローカル HTTP/WebSocket で委譲する。
flowchart TB
subgraph Desktop["デスクトップ(Tauri アプリ)"]
direction TB
Rust["Rust シェル (src-tauri/src/lib.rs)\n・サイドカー起動/停止\n・Python 環境展開\n・ネイティブダイアログ/フォルダ操作\n・ハートビート送信"]
subgraph WebView["WebView2 (main window)"]
React["React 19 UI (src/)\n・@tanstack/react-query\n・zustand (pose store)\n・three.js / r3f (pose editor)"]
end
end
subgraph Sidecar["Python サイドカー (backend/)"]
direction TB
FastAPI["FastAPI / uvicorn (backend.main:app)\nrouters: settings/jobs/dictionaries/prompts\n/release/training_runtime/diagnostics/workflows"]
Services["services/\n生成・辞書・学習・ControlNet・設定・VRAMキュー"]
Diffusers["diffusers / torch / peft / bitsandbytes\n(SDXL 生成・NF4・LoRA)"]
Kohya["sd-scripts (kohya)\nLoRA 学習サブプロセス"]
end
DB[("SQLite\n%APPDATA%/Umaru/data/umaru.db")]
FS[("ファイル群\noutputs / characters / models / loras")]
React -- "invoke (IPC)" --> Rust
React -- "HTTP / WS\n127.0.0.1:port\nX-Umaru-Token" --> FastAPI
Rust -- "spawn + stdin token" --> FastAPI
FastAPI --> Services --> Diffusers
Services --> Kohya
Services --> DB
Services --> FS
技術スタック
| 層 | 主要技術 | 依拠ファイル |
|---|---|---|
| デスクトップシェル | Tauri 2.11, Rust(rfd / zip / portpicker / rand) | src-tauri/Cargo.toml, src/lib.rs |
| フロントエンド | React 19.2, TypeScript 5.9, Vite 7, TanStack Query 5, Zustand 5 | package.json, src/ |
| 3D ポーズ | three 0.184, @react-three/fiber, @react-three/drei | src/features/pose/ |
| バックエンド | Python FastAPI, uvicorn, aiosqlite, Pydantic | backend/main.py, backend/models.py |
| 生成エンジン | diffusers, torch, peft, bitsandbytes, compel(LPW), Pillow | backend/services/generation_service.py |
| 学習 | kohya-ss/sd-scripts, accelerate | backend/services/training_service.py, backend/training_runtime.py |
通信方式
- IPC(Tauri invoke): ウィンドウ操作、フォルダ/ファイル選択、外部リンク、サイドカー設定取得など OS に近い操作。
- HTTP/WS(localhost): 業務 API。フロントは
src/api.tsのapi<T>()ラッパ経由でX-Umaru-Tokenを必ず付与。ベース URL とトークンは Rust のget_backend_configコマンドで取得(src/api.ts:getRuntimeConfig)。 - CSP(
tauri.conf.json):connect-srcをipc:とhttp://127.0.0.1:*/ws://127.0.0.1:*に限定。img-srcは127.0.0.1とdata:/blob:のみ。
2. 起動シーケンスとプロセス監視
2.1 二段階スプラッシュと起動フロー
ウィンドウは 2 枚定義されている(tauri.conf.json)。splashscreen(480×280, alwaysOnTop, 可視)と main(1280×820, decorations:false, 初期非可視)。暗転・白転を避けるため 2 段階ハンドオフ(show_main_window → 実コンテンツ描画 → close_splashscreen)を行う。
sequenceDiagram
participant U as ユーザー
participant S as Splash Window
participant R as Rust (lib.rs)
participant P as Python Sidecar
participant M as Main Window (React)
U->>S: アプリ起動
S->>R: start_backend()
R->>R: setup_python_environment()<br/>(同梱zip展開→pip→基本依存)
R->>P: spawn uvicorn + stdin にトークン
Note over R,P: UMARU_PARENT_PID を環境変数で渡す
loop wait_for_sidecar (最大35秒)
R->>P: /healthz → /api/settings,characters,album,models
R->>P: /api/heartbeat(準備中の空白を埋める)
end
R-->>S: backend_startup イベント(phase/progress)
R->>M: emit "ready"
M->>R: get_backend_config()
M->>P: 基礎データ取得 (settings/characters/album)
M->>R: show_main_window()
M->>R: close_splashscreen()
Note over R,P: 以後 Rust スレッドが2.5秒周期で<br/>/api/heartbeat を送信
- Python 環境の自動準備(Windows,
setup_python_environment): 同梱resources/python-embed-amd64.zipを%APPDATA%/Umaru/pythonに展開 →python311._pthのimport siteを有効化 →get-pip.pyで pip 投入 →backend/requirements.txtを導入 → 完了マーカー.umaru-basic-deps-readyを書く。2 回目以降はマーカーで短絡。 - 起動準備完了の判定(
wait_for_sidecar):/healthzが 200 かつ/api/settings・/api/characters・/api/album・/api/modelsが 2 連続で 200 を返すまで待機(最大 35 秒)。進捗はbackend_startupイベントで Splash に反映。 - フロント側のリトライ(
AppContent.tsx):getRuntimeConfig()をコールド起動の取りこぼし対策として 400ms 間隔で粘り強く再試行。15 秒周期で/api/heartbeatを打って接続状態(ConnectionState)を更新。
2.2 親子プロセス監視(孤児検出と自爆)
サイドカーが孤児化したまま残らないよう、双方向で監視する。
flowchart LR
subgraph Rust
HB["heartbeat thread\n2.5秒ごとに POST /api/heartbeat"]
end
subgraph Python
Watch["watch_parent_process()\n2秒ごとに親PID生存確認"]
Decide{"parent_watch_decision\n親PIDが死んでいる?"}
Kill["os._exit(0)\nsidecar.log に理由記録"]
end
HB --> Watch
Watch --> Decide
Decide -- "はい(pid_alive=false)" --> Kill
Decide -- "いいえ" --> Watch
- 判定は 親 PID の死活が正(
security.py:parent_watch_decision)。ハートビート切れ単独では停止しない(起動準備中やイベントループ一時停止を「親の死」と誤判定して自爆していた不具合への対策)。HEARTBEAT_TIMEOUT_SECONDS=90は保険値。 - Rust 側は
mainウィンドウのCloseRequestedとBackendState::dropでサイドカーをkill+wait(lib.rs:stop_backend_state)。 - 全ての起動・監視・走査は 1 行診断ログ(
diagnostics.py:diag)として stderr →logs/sidecar.logに集約。diag_timerが閾値超過の走査に_slowを付ける。
3. ディレクトリ・データ配置
ルートは UMARU_DATA_DIR 環境変数 > %APPDATA%/Umaru の優先順(backend/paths.py, Rust umaru_data_root)。起動時に ensure_app_dirs() が全サブディレクトリを作成。
flowchart TD
Root["%APPDATA%/Umaru/"]
Root --> python["python/ (同梱 embeddable Python)"]
Root --> venv["venv/ (開発互換 venv)"]
Root --> config["config/"]
Root --> data["data/umaru.db (SQLite)"]
Root --> outputs["outputs/YYYY-MM-DD/ (生成PNG)"]
Root --> outputs2["outputs/controlnet/ (制御画像)"]
Root --> chars["characters/{id}/"]
chars --> mem["memories/ (学習素材)"]
chars --> ds["lora_training/{session}/dataset/"]
chars --> loraout["loras/ (学習出力 .safetensors)"]
chars --> exp["exports/ (yurari/suzuna JSON)"]
Root --> models["models/ (ベースモデル)"]
models --> cn["controlnet/ (DLした OpenPose CN)"]
Root --> loras["loras/ (ユーザー LoRA)"]
Root --> kohya["kohya/ (手動指定 sd-scripts)"]
Root --> training["training/ (自動セットアップ sd-scripts + venv)"]
Root --> dicts["dictionaries/ (CSV 辞書)"]
Root --> logs["logs/ (sidecar.log / kohya / inference-check)"]
Root --> cache["cache/"]
- 画像参照は app-relative パスで DB に保存(
paths.py:app_relative/from_app_relative)。読み出し時はresolve_under()がAPP_ROOT配下を保証し、\x00とバックスラッシュを含むパスを 400 で拒否(パストラバーサル対策)。 - 同梱日本語タグ辞書は
resources/tags/tags_ja.csv(自作 Unlicense CSV)。
4. データモデル(SQLite)
backend/database.py の migrate() が冪等にスキーマを構築(schema_migrations でバージョン管理、現在 v7 まで)。文字列日時は UTC ISO8601(now_iso())。
erDiagram
characters ||--o{ generations : "character_id"
characters ||--o{ character_memory_images : "character_id"
characters ||--o{ prayer_sessions : "character_id"
characters ||--o{ remembering_sessions : "character_id"
characters ||--o{ growth_album_sessions : "character_id"
characters ||--o{ exports : "character_id"
characters ||--o{ favorite_seeds : "character_id"
generations ||--o{ character_memory_images : "generation_id"
generations ||--o{ growth_album_steps : "selected_generation_id"
prayer_sessions ||--o{ prayer_groups : "session_id (CASCADE)"
prayer_sessions ||--o{ prayer_candidates : "session_id (CASCADE)"
prayer_groups ||--o{ prayer_candidates : "group_id (CASCADE)"
generations ||--o{ prayer_candidates : "generation_id"
growth_album_sessions ||--o{ growth_album_steps : "session_id (CASCADE)"
growth_album_sessions ||--o{ growth_album_sessions : "parent_session_id"
characters {
int id PK
string name
string reading
string concept
string personality
string world_note
string default_prompt_layers "JSON: positive/negative/apply_mode"
int cover_image_id "固定カード画像"
string linked_lora_path
string linked_lora_trigger_word
string builder_state "特徴パネル選択 JSON"
string status "active|deleted"
}
generations {
int id PK
int character_id FK
string source_type "txt2img|img2img|controlnet|prayer|pose_import"
string resolved_positive
string resolved_negative
string model_name
string loras "JSON 配列"
string settings_snapshot "JSON 全パラメータ"
int seed
string image_rel_path
int width
int height
string metadata_json "job_id/engine/seed"
}
character_memory_images {
int id PK
int character_id FK
int generation_id FK
int prayer_candidate_id
string image_rel_path
string role "identity|outfit|expression|pose|style|background|training_candidate|rejected"
int rank
int is_training_candidate
string caption
}
prayer_sessions {
int id PK
int character_id FK
string wish
string status "draft|selecting_final_winners|completed"
string model_path
string model_name
string compare_theme "none|cfg|sampler|prompt_tag"
string compare_config "JSON"
}
prayer_groups {
int id PK
int session_id FK
string group_key "A|B|C|D"
int requested_count
string variation_label
string generation_overrides "JSON"
string last_error
}
prayer_candidates {
int id PK
int group_id FK
int session_id FK
int generation_id FK
int rank
int is_finalist
int seed
string image_rel_path
}
remembering_sessions {
int id PK
int character_id FK
string status "prepared|training|completed|error|cancelled"
string name
string trigger_word
string memory_image_ids "JSON 配列"
string dataset_rel_path
string output_lora_rel_path
int steps_completed
int total_steps
}
growth_album_sessions {
int id PK
int character_id FK
string title
string base_prompt
int current_step
int parent_session_id "分岐元"
int parent_step_index
}
growth_album_steps {
int id PK
int session_id FK
int step_index
string step_kind "base|identity|outfit|scene|lighting|style"
string label
string prompt_append
int selected_generation_id FK
}
favorite_seeds {
int id PK
int seed
string model_name
string prompt_snapshot
string settings_snapshot
string memo
string name
}
prompt_dictionaries {
int id PK
string source_name
string license_name
string entry_json "tag/ja_label/aliases/post_count等"
string nsfw_level "safe|sensitive|explicit"
}
その他のテーブル: app_settings(key-value 設定), model_adapters(モデルアダプタ管理・予約), exports(エクスポート記録), audit_events(監査ログ)。
マイグレーション履歴
| ver | 追加内容 |
|---|---|
| 1 | 基本スキーマ一式 |
| 2 | お祈りに使用モデル情報(prayer_sessions.model_path/name)、prayer_groups.requested_count/last_error |
| 3 | favorite_seeds(雰囲気の種メモ) |
| 4 | お祈りラボ(compare_theme/config, variation_label, generation_overrides)、favorite_seeds.name |
| 5 | 成長アルバム(growth_album_sessions/steps) |
| 6 | prayer_candidates.seed(実 seed の可視化) |
| 7 | characters.builder_state(特徴パネルの選択状態) |
5. 認証・セキュリティ
flowchart LR
Rust["Rust: 起動時に48文字\nランダムトークン生成"] -- "stdin で1行送信" --> Py["Python: read_startup_token()"]
Rust -- "get_backend_config(IPC)" --> Front["React"]
Front -- "X-Umaru-Token ヘッダ" --> API["require_token / require_ws_token"]
API -- "secrets.compare_digest で定数時間比較" --> OK{"一致?"}
OK -- いいえ --> E401["401 unauthorized"]
OK -- はい --> Handler["各エンドポイント"]
- トークン: Rust が
Alphanumeric48 文字を生成し、サイドカーの stdin へ書き込む。Python はUMARU_DEV_TOKEN環境変数 > stdin > 生成の順で読む(security.py:read_startup_token)。比較はsecrets.compare_digest(タイミング攻撃耐性)。 - 画像系 GET(
/image・/thumbnail・/download)はヘッダではなく?token=クエリで検証(<img src>から直接参照するため)。同じく定数時間比較。 - CORS:
tauri://localhost/http://127.0.0.1:5173等に限定、allow_credentials=False、許可ヘッダはX-Umaru-TokenとContent-Typeのみ。 - パストラバーサル: 全ファイル参照は
resolve_under()でAPP_ROOT配下に拘束。 - Zip Slip 対策(学習ツール展開
training_runtime.py:_extract): 絶対パス・..・展開先逸脱・シンボリックリンク entry を事前検証で全拒否。 - 外部リンク(Rust
open_external_url):github.com/DraconicDragon/とgithub.com/DominikDoom/(辞書出典)のみ許可。 - フォルダを開く(
album_open_folder):shell=Trueを使わずexplorer /selectを引数渡し。ID だけ受け取りAPP_ROOT配下を保証。 - 未処理例外: グローバルハンドラが内部詳細を伏せた一般メッセージ(
internal_error)を返しつつ監査ログに記録(main.py)。
6. 画面構成・ナビゲーション・用語体系
6.1 タブ構成
src/app/navigation.ts の baseNavItems と、内部で切り替える追加タブ(growth/experiment/release)からなる 14 タブ。シェルは App.tsx(ドロワー開閉+カスタムタイトルバー)→ AppContent.tsx(タブ振り分け+共通状態)。
flowchart TD
Home["🏠 ホーム HomeView"] --> Setup["✨ 初回セットアップ/ステータス SetupView"]
Home --> Chars["👤 キャラクター CharactersView"]
Home --> Gen["🖌 つくる GenerateView"]
Home --> Pose["🧍 ポーズ PoseEditorTab"]
Home --> Album["🖼 アルバム AlbumView"]
Home --> Mem["❤️ 学習素材 MemoryView"]
Home --> Pray["✨ お祈り PrayerView"]
Home --> Remember["🧠 この子を学習する RememberView"]
Home --> Dict["📖 辞書 DictionariesView"]
Home --> Settings["⚙️ 設定 SettingsView"]
Gen -. "ControlNet/i2i 連携" .-> Pose
Album -. "復元/タグ追記→つくる" .-> Gen
Pray -. "成長アルバム" .-> Growth["🌱 成長アルバム GrowthAlbumView"]
Settings -. "配布準備" .-> Release["📦 配布準備 ReleaseView"]
Settings -. "実験室" .-> Experiment["🧪 実験室 ExperimentView"]
6.2 生成中のタブ制御
長時間ジョブ実行中(activeLongTask)は、重い処理がぶつかるタブをブロックする(navigation.ts)。
- 許可(
ALLOWED_TABS_DURING_GENERATION): home, setup, album, characters, memory, growth, dictionaries, pose(編集はローカル完結。i2i 投入のみ ExportPanel が抑止)。 - ブロック(理由文言つき): generate, prayer, remember, experiment, release, settings。
6.3 用語モード(creator / expert)
AppSettings.ui_mode で UI 文言を切替(生成ロジックは不変)。src/terminology.ts。
| キー | creator(初心者向け) | expert |
|---|---|---|
| prompt | 描いてほしいこと | Prompt(描いてほしいこと) |
| negative | 避けたい描写 | Negative Prompt(避けたい描写) |
| seed | 雰囲気の種(0で自動) | Seed(雰囲気の種、0で自動) |
| steps | 描き込み量 | Sampling Steps |
| cfg | 指示への忠実度 | CFG Scale |
| denoise | 元画像の残し具合 | Denoise Strength |
「お祈り」「お守り」「雰囲気の種」「この子を学習する」など、技術用語を擬人化・平易化した独自ボキャブラリが UI 全体を貫く。
7. 機能詳細
7.1 初回セットアップ / ステータス(SetupView)
実機が「生成できる状態か」を 1 画面で診断し、不足はワンクリックで補う。GET /api/setup/status が診断項目(items[])を返す。
flowchart TD
Status["GET /api/setup/status"] --> Items
subgraph Items["診断項目(section 別)"]
c1["connection: backend_alive / settings_loaded"]
c2["generation: python_env / basic_deps / sdxl_deps / gpu / model_dir / model_detect"]
c3["save: output_dir(書込テスト)"]
c4["lora: lora_dir"]
c5["training: training_cuda / kohya / accelerate"]
c6["dictionary: dictionary_dir(タグ件数)"]
end
Items --> Actions["needs_action 行に install_basic /\ninstall_sdxl_cuda / install_sdxl_cpu /\nsettings 等のアクション"]
- 各項目は
status(ready/needs_action/warning/failed_check),necessity(required_for_generation / required_for_training / optional / info),explanation(cause/next/beginner の 3 段説明),actionsを持つ(src/app/types.ts:DiagItem)。 - 重い同期チェック(
_check_venv_import/_detect_gpu/_check_venv_cuda)はasyncio.to_threadで並列化し、イベントループ固着→自爆を回避。 - SDXL 依存導入:
POST /api/setup/install-sdxl(profile=auto/cpu/cuda)。GPU 世代に応じ wheel index を選択(gpu.py:select_cuda_index_url、Blackwell=cu128 / それ以外=cu126 / CPU=cpu)。進捗はWS /ws/setup/progress。 - 基本依存:
POST /api/setup/install-dependencies(backend/requirements.txt)。 - 画像生成は CPU フォールバック可、LoRA 学習は CUDA 必須を文言で明確に分離(
training_cuda行)。
7.2 設定(SettingsView)
GET/PATCH /api/settings。app_settings テーブルに key-value で永続化(settings_service.py)。
| 設定 | 既定 | 用途 |
|---|---|---|
ui_mode | creator | 用語モード |
model_dir / lora_dir / kohya_dir / output_dir | %APPDATA%/Umaru/* | フォルダ |
controlnet_openpose_path | "" | OpenPose ControlNet モデル(同梱せず手動配置 or DL) |
nsfw_filter | true | つくる画面の生成前警告 |
danbooru_nsfw_level | safe | 辞書検索・タグ候補の表示範囲(safe/sensitive/explicit) |
remove_exif_by_default | true | PNG メタデータ保存とダウンロード時再保存に反映 |
フォルダ選択は Rust の select_folder / select_file(rfd ネイティブダイアログ)。GET /api/folder-status が各フォルダの存在・書込可否・kohya スクリプト有無を返す。
7.3 キャラクター(CharactersView)
キャラクターは育成の中心エンティティ。/api/characters 系で CRUD。ソフトデリート(status='deleted')で過去画像参照を壊さない。
- 雛形プロンプト(IA-13):
default_positive_prompt/default_negative_prompt/default_prompt_apply_mode(prepend/append/replace)をdefault_prompt_layers(JSON)に格納。API では typed フィールドへ展開(workflows.py:_decorate_character_prompts)。 - カード画像固定(HF-15):
PUT/DELETE /api/characters/{id}/cover。アルバム画像をcover_image_idに固定(他キャラ画像は拒否)。 - サマリ(HF-04):
GET /api/characters/summariesが 1 リクエストで生成数・学習素材数・学習候補数・完了 LoRA 数・学習状態・最新/固定カバー画像・頻出タグ Top5 を集計返却。 - 特徴パネル状態:
builder_state(JSON)につくる画面のビルダー選択を保存。
7.4 つくる(生成)GenerateView
最重要画面。1 フォームで txt2img / img2img / ControlNet を扱う。フォームは localStorage(umaru_generate_form_v1)に永続化(generateForm.ts)。
flowchart TB
subgraph Form["GenerateForm(src/features/generate/generateForm.ts)"]
model["モデル選択(名前+実パス両必須)"]
char["キャラクター(メイン選択と双方向連動)"]
res["解像度プリセット6種 + カスタム(64–2048)"]
pos["描いてほしいこと(positive)"]
neg["避けたい描写(negative)"]
loras["複数 LoRA(最大5、各 path+weight 0–2)"]
params["steps/cfg/seed/scheduler(7種)/NF4/LPW/TAESDXL"]
end
subgraph Tools["補助ツール"]
builder["キャラクターの特徴パネル\nbuildCharacterTags()"]
color["カラーピッカー → SDタグ\n/api/tags/color-to-tag"]
charm["お守り(ネガティブ プリセット5種)\nmergeNegativeWithCharms()"]
cleanup["言葉の整頓(重複/矛盾検出)\ncleanupPrompt()"]
volume["ボリューム目安\nestimateVolume()"]
tagsearch["タグ検索 /api/dictionaries/search"]
seedpick["雰囲気の種ピッカー /api/favorite-seeds"]
end
Form --> Submit["POST /api/generate | /api/img2img | /api/pose/controlnet"]
Submit --> Job["job_id を受領 → ポーリング → アルバムへ"]
主な仕様:
- モデル選択ガード(
isModelSelectionReady): 名前と実パス両方が必要。アルバム復元で元モデルが消えている場合は黙って差し替えず未選択にして明示選択を促す(resolveModelSelection, allowAutoSelect=false)。 - キャラクターの特徴パネル(機能②,
characterBuilder.ts): 頭髪(長さ/スタイル/質感/アホ毛/色)・目(色/形/特徴)・表情・身体(身長/体型/胸/追加)・服装(カテゴリ→スタイル)をプルダウンで選び、buildCharacterTags()で英語タグ列へ変換しプロンプトへ追記。色はcolorTags.tsのパレット/HEX を解決。 - カラーピッカー(
ColorPickerWidget): HEX を/api/tags/color-to-tagに送り、HSL 変換ベースで髪色/目色/服色タグへ(color_tags.py、純ロジックでテスト可能)。 - お守り(ネガティブプリセット): quality / hands / text / blur / body の 5 種。
prompt-helpers.ts:mergeNegativeWithCharmsがユーザー入力を優先しつつ重複除去マージ(サーバ側prompts.pyと同一規則)。 - 言葉の整頓 / ボリューム: クライアント(
cleanupPrompt/estimateVolume)とサーバ(POST /api/prompt/cleanup/estimate-volume)で同一ロジック。矛盾は自動削除せず通知のみ。トークン数は近似(CLIP tokenizer 非搭載のため「目安」と明記)。 - 複数 LoRA:
lora_list(最大 5)。後方互換で先頭を単一loras/lora_weightにも反映。 - 復元モード(
PendingGenerateRestore): アルバムから restore / fix_seed(seed 固定)/ free_seed(種だけ変更)/ img2img の 4 モード。自動生成せずユーザー確認後に発火。
7.5 ポーズエディタ(PoseEditorTab)
three.js / r3f による 3D スケルトン編集。出力した OpenPose 制御画像で構図を転写する。遅延ロード(メインバンドル軽量化)。状態は zustand(store/poseStore.ts)。
flowchart LR
Skeleton["3D スケルトン\nCOCO-18 + 体幹リグ(骨盤/脊椎/胸)"] --> Edit["関節ドラッグ + FK連鎖\n(kinematics.moveJoint)"]
Preset["プリセット11種\nT/A-Pose,Stand,Sit,Walk,Wave,\nCheer,ArmsCrossed,Run,Crouch,SitTurn"] --> Skeleton
Edit --> Project["カメラ投影 projection.ts"]
Project --> OP["OpenPose JSON(pose_keypoints_2d/3d)\nopenpose.ts"]
Project --> PNG["OpenPose 骨格 PNG\nopenposeRender.ts / pngExport.ts"]
PNG --> CN["onSendToControlNet\n→ つくる(ControlNet)"]
PNG --> I2I["onSendToI2i\n→ POST /api/pose/to-i2i → img2img"]
- 骨格定義(
skeleton.ts): OpenPose body_18(id 0–17)を据え置き、編集用に体幹リグ(骨盤 18 / 脊椎 19 / 胸 20)を追加。これにより「胸を動かすと上半身だけ動く」「骨盤主導で下半身を捻る」を実現。左=青 / 右=赤 / 中心=灰で色分け。 - 座標系: +X=キャラの左 / +Y=上 / +Z=正面(視点側)。身長 ≒1.7〜1.8 ユニット。
- OpenPose 出力(
openpose.ts): 体幹リグを除外し COCO-18 のみをpose_keypoints_2d(54 値)として書き出し。再編集用に独自拡張pose_keypoints_3d(72 値)も併記。version 1.3、ComfyUI OpenPose Studio 互換。 - 2 つの転写経路: ① ControlNet(
POST /api/pose/controlnet、SDXL 専用)で骨格を厳密転写。② img2img(POST /api/pose/to-i2iで骨格 PNG を generation 化 → 起点に)。
7.6 アルバム(AlbumView / AlbumDetailModal)
全生成画像の一覧・管理。GET /api/album({items,total,limit,offset,has_next} のページング、limit=0 で全件、character_id=0 で未紐付けのみ)。
- 自己整合: 実ファイルが消えた行は一覧取得時に自動削除(
_reconcile_album_items)。 - 詳細: seed・モデル・パラメータ・エンジン情報(
metadata_json)を表示。 - 復元(
GET /api/album/{id}/restore):settings_snapshotを「つくる」フォーム形へ正規化し、欠落モデル/LoRA/seed を警告つきで返す。 - ダウンロード(
/download?format=png|jpg): EXIF 削除設定を尊重。png は info を捨てて再構築、jpg は透過を白背景合成。元 PNG は不変更。 - キャラ一括再割当(v1.3,
POST /api/album/reassign-character): 複数generation_idsのcharacter_idを一括変更。付け替えで持ち主が変わった画像のカバー固定は整合解除。 - 雰囲気の種(
favorite_seeds): 良い seed をメモ名つきで保存(FavoriteSeedSection)。つくる画面の seed ピッカーから呼べる。 - フォルダを開く / 学習素材へ保存 の導線。
7.7 学習素材(MemoryView)
キャラクターごとの「思い出 = 学習素材画像」を管理。GET/POST /api/characters/{id}/memory-images。アルバム画像やお祈り候補を copy_to_memory() で characters/{id}/memories/ へ複製し、role(identity/outfit/expression/pose/style/background/training_candidate/rejected)と rank・caption を付与。mark-training-candidate で学習候補化。サムネは /api/memory-images/{id}/thumbnail。
7.8 お祈り(PrayerView)
「願いごと(wish)」から A〜D の 4 組 に分けて候補を量産し、ランク付けして勝者を学習素材へ昇格させる、ガチャ的探索ワークフロー。LLM 自動評価はせずユーザーが選ぶ。
stateDiagram-v2
[*] --> draft: POST /api/prayer-sessions<br/>(wish + model + compare_theme)
draft --> draft: 各組 generate<br/>POST /api/prayer-groups/{id}/generate<br/>(mode: generate/fill_missing/regenerate)
draft --> selecting_final_winners: finalize-groups<br/>(rank1-3 を決勝候補に)
selecting_final_winners --> completed: save-winners<br/>(rank1-3 を identity 学習素材へ)
completed --> [*]
- 組ジョブ(
_run_prayer_group_job): 1 ジョブで N 枚を逐次生成。seed はセッション内で重複回避(既出 seed を集合管理、最大 32 回再抽選)。部分失敗は止めずにlast_errorを残し、statusを completed / partial / partial_failed / failed で出し分け。 - お祈りラボ(
compare_theme): A〜D に比較条件を割り当て(_build_compare_overrides)。cfg: 各組で CFG 値を変える(既定 3.5/5.5/7/9)sampler: スケジューラを変える(ホワイトリスト外は default に丸め)prompt_tag: 追加タグを変える
- ランク(
PATCH /api/prayer-candidates/{id}/rank): 同一組内で同 rank の重複を NULL に戻してから設定(IA-11)。 - 設定継承(
prayerSettings.ts): つくる画面で確定済みの positive/negative/LoRA を引き継ぐ(キャラ雛形の二重適用を避け dedup のみ)。wish は backend が base_positive に注入。
7.9 成長アルバム(GrowthAlbumView)
キャラを「ベース → アイデンティティ → 衣装 → シーン → ライティング → スタイル」と段階的に育てる記録。各段階で良い generation を選び積み重ねる。
POST /api/growth-albums(作成時に base 段階を自動追加)。POST /api/growth-albums/{id}/steps(step_kind: base/identity/outfit/scene/lighting/style)。POST /api/growth-album-steps/{id}/select-generation(別キャラ画像は拒否、未紐付けは許可)。- 分岐(
POST /api/growth-albums/{id}/branch): 指定段階までを複製して別ルートを作成(parent_session_idでツリー化)。
7.10 この子を学習する(RememberView / LoRA 学習)
学習素材から SDXL 専用 LoRA を kohya-ss/sd-scripts で学習する。
sequenceDiagram
participant U as ユーザー
participant API as workflows.py
participant T as training_service.py
participant K as sdxl_train_network.py (subprocess)
U->>API: POST .../remember/prepare (画像15枚以上)
API->>T: prepare_remembering_dataset()
T->>T: dataset/ に 0001.png + 0001.txt 生成
U->>API: POST .../start
API->>T: 開始前診断(kohya/accelerate/CUDA/SDXL判定)
T->>T: config.toml + dataset.toml 生成
T->>K: accelerate launch sdxl_train_network.py
loop 学習ループ
K-->>T: stdout "step/total loss=..."
T-->>U: WS /ws/remembering/{id} 進捗+ETA
end
K-->>T: 終了コード0
T->>T: 開始前snapshotとの差分で新規 .safetensors 検出
T->>API: status=completed, output_lora_rel_path
- 前提: 学習画像 15 枚以上、SDXL チェックポイント(
_detect_sdxl_checkpointがヘッダで判定)、CUDA 必須(学習 venv のtorch.cuda.is_available())、accelerate とsdxl_train_network.pyの実在。 - 学習設定(
_generate_kohya_toml): network_dim=32 / alpha=16、U-Net only、bf16、AdamW8bit、10 epochs、lr=1e-4(cosine_with_restarts)。8GB VRAM 最適化(gradient_checkpointing、latent/text encoder 出力キャッシュ、SDPA)。データセットは--dataset_config形式(ベタ置きフォルダを num_repeats=10 で subset 化)。 - 進捗: stdout の
step/total loss=を正規表現抽出し、ETA を計算してWS /ws/remembering/{id}で配信。TrainingRuntimePanelがログ表示。 - 完了後、開始前 snapshot との差分で新規 LoRA を検出し
output_lora_rel_pathに確定。
7.11 学習ツール自動セットアップ(training_runtime.py)
sd-scripts は同梱せず、ユーザーが「学習機能を有効化」した時に明示同意のうえ DL→展開→venv 作成→依存導入する。
stateDiagram-v2
[*] --> not_installed
not_installed --> consent_required: POST consent (同意)
consent_required --> downloading: POST install
downloading --> extracting: sha256 照合
extracting --> creating_venv: Zip Slip 検証
creating_venv --> installing_dependencies: venv/virtualenv
installing_dependencies --> verifying: torch(profile別 index)/accelerate
verifying --> ready: train_network.py + sdxl_train_network.py 確認
downloading --> error: cancel/失敗
installing_dependencies --> error
error --> not_installed: reset_runtime (退避)
- manifest(
backend/training_runtime_manifest.json)で sd-scripts の ref/commit/sha256 を固定。配布前ゲートは未固定だと pending(開発時はaccept_unpinned同意で main 取得可)。 - profile(cpu/cuda)に応じ torch の wheel index を選択(CUDA は GPU 世代判定)。
- 中断対応: executor スレッドの DL は
threading.Eventをキャンセルトークンにしてチャンクループで監視。子プロセスは terminate→kill。 POST /api/training-runtime/{consent,install,cancel,update,reset}/GET status,logs。
7.12 辞書(DictionariesView)
プロンプト候補となる Danbooru 系タグの日本語辞書。prompt_dictionaries テーブル(entry_json に tag/raw_tag/ja_label/aliases/ja_aliases/post_count/category/nsfw_level)。
- 内蔵辞書(機能①): 起動時に同梱
tags_ja.csv(自作 Unlicense)を冪等取り込み(size:mtime 署名でスキップ判定、import_builtin_tag_dictionary)。 - 取り込み: フォルダ一括(
import-folder)/ 自作 CSV(import-custom、要同意)/ 汎用 import。CSV はヘッダ有無の両形式対応、_をスペースへ正規化、同一 source 内の重複タグは post_count・日本語有無で最良を残す。 - 検索(
/api/dictionaries/search): tag / raw_tag / ja_label / aliases / ja_aliases を横断、NSFW レベルでフィルタ(safe⊂sensitive⊂explicit)、post_count 降順。ユーザー辞書を内蔵辞書より優先。 - 一覧(
/api/dictionaries/entries): カテゴリ(general/artist/copyright/character/meta/other)別の件数とページング。 - 既存正規化: 起動時に
raw_tag欠落エントリをバッチ補完(normalize_existing_dictionary_entries)。 - 辞書フォルダは Rust
open_dictionary_folderで開ける。
7.13 実験室(ExperimentView)
POST /api/prompt/resolve で日本語プロンプトの簡易タグ解決を試す開発者向け画面。resolve_prompt() は固定マッピング(笑顔→smile 等)と NSFW 警告を返す軽量実装。
7.14 配布準備・エクスポート(ReleaseView)
- キャラクタープロフィール書き出し:
POST /api/characters/{id}/exports/yurari(ゆらり=アニメ連携想定)/exports/suzuna-profile(すずな=音声連携想定)。スキーマ 0.1.0 の JSON にキャラ情報・参照画像・LoRA リンクを格納しcharacters/{id}/exports/へ保存。GET /api/exports/{id}で取得。 - 法務通知(
GET /api/legal-notices): NOTICE / EULA / 第三者ライセンス一覧 / モデル・LoRA・辞書の注意 / 確認対象ライセンスの存在を返す。 - 配布前ゲート(
GET /api/release/status): ローカル成果物から各ゲートの状態を集計(後述 §13)。
8. 生成エンジン詳細
generation_service.py の create_generation() が中核。VRAM キュー(vram_queue)で生成・学習の同時実行を直列化し、二重常駐を防ぐ。
flowchart TD
Start["create_generation(payload, job_id)"] --> Queue["vram_queue.task() で直列化"]
Queue --> Route{"control_image_path あり?"}
Route -- "はい" --> CN["_render_with_controlnet\n(SDXL専用)"]
Route -- "いいえ" --> Diff["_render_with_diffusers"]
Diff --> Family["モデル系統判定\n_detect_model_family\n(名前→model_index.json→safetensorsヘッダ)"]
Family --> Kind{"sdxl系? (sdxl/illustrious/pony)"}
Kind -- "はい" --> XL["StableDiffusionXLPipeline / fp16"]
Kind -- "いいえ(sd1)" --> SD["StableDiffusionPipeline / fp32"]
XL --> Pre["_preflight_cuda_kernel\n(Blackwell+cu126 等を事前検出)"]
SD --> Pre
Pre --> Cache{"pipeline_cache ヒット?"}
Cache -- "ミス" --> Load["from_single_file/pretrained\n+ TAESDXL軽量VAE\n+ NF4量子化\n+ 複数LoRA(set_adapters)\n+ scheduler"]
Cache -- "ヒット" --> Infer
Load --> Optimize["_optimize_pipeline_for_cuda\nxFormers/channels_last/VAE slicing"]
Optimize --> Infer["pipe(...) + callback 進捗"]
Infer --> Save["_save_image + PNG metadata\n+ 設定出力先へコピー"]
Save --> DB["generations へ INSERT\n(engine 診断メタ込み)"]
主要機能
| 機能 | 仕様 |
|---|---|
| モデル系統判定 | 名前 → model_index.json の _class_name → safetensors メタ/キー(text_encoder_2 / conditioner.embedders.1 で SDXL)。単一 file は既定 sd1、フォルダは既定 sdxl |
| NF4 量子化 | UNet と Text Encoder の Linear 層を bitsandbytes の Linear4bit(nf4, compute_dtype=bfloat16)へ。VAE は fp16 維持。SD1.x では無効化。CUDA 必須 |
| TAESDXL 軽量 VAE | 既定 ON。madebyollin/taesdxl(SDXL)/taesd(SD1.x)で VAE 復号を高速化。DL 失敗時は同梱フル VAE に自動フォールバック |
| 複数 LoRA | adapter として個別ロードし set_adapters で個別強度適用(fuse しない)。peft 必須 |
| スケジューラ | default / ddim / pndm / euler / euler_ancestral / dpm / lms |
| LPW(長文プロンプト重み付け) | compel で 77 トークン超を埋め込み(未導入時は通常処理にフォールバック) |
| 進捗コールバック | callback_on_step_end で step/total と s/step を逐次配信。無音固着と区別 |
| パイプラインキャッシュ | モデル/LoRA の mtime+size、device、NF4、scheduler、TAESDXL をキーに常駐再利用。種別跨ぎロード時は相手側キャッシュを解放(単一 GPU の二重常駐=sysmem 落ち防止) |
ControlNet 経路(controlnet_service.py)
SDXL 専用(StableDiffusionXLControlNetPipeline + xinsir OpenPose CN)。VRAM 量に応じた配置戦略を自動選択:
flowchart TD
Place["_place_controlnet_pipeline"] --> V{"VRAM 量"}
V -- "6GB未満(4GB機)" --> Seq["enable_sequential_cpu_offload\n(遅いが確実に動く)"]
V -- "6GB以上 かつ NF4 ON" --> Full1["フルGPU配置\n(offload非併用=8GB高速化の本命)"]
V -- "6〜12GB" --> Model["enable_model_cpu_offload"]
V -- "12GB以上" --> Full2["フルGPU配置"]
control_guidance_start/endで ControlNet を効かせる denoise 区間を制御(end<1.0 で後半スキップ=高速化)。- ControlNet モデルは同梱せず
GET/POST /api/pose/controlnet/...で xinsir SDXL(Apache-2.0, 約2.5GB)を DL(controlnet_models.py、httpx ストリーミング)。
9. ジョブ・進捗管理
生成・お祈り・学習はすべて非同期ジョブとして job_store.py の jobs dict で一元管理し、フロントが GET /api/jobs/{id} または WebSocket でポーリングする。
stateDiagram-v2
[*] --> queued
queued --> running
running --> completed
running --> error
running --> cancelled: POST /api/jobs/{id}/cancel
running --> failed: お祈り0枚
running --> partial: お祈り不足
running --> partial_failed: お祈り一部失敗
completed --> [*]
error --> [*]
cancelled --> [*]
failed --> [*]
partial --> [*]
partial_failed --> [*]
- terminal 状態(
JOB_TERMINAL_STATUSES): completed / cancelled / error / failed / partial / partial_failed。update_jobは terminal への status 上書きを無視(cancel/error 後に遅れて completed が来ても terminal 維持)。フロントsrc/app/types.tsと手動同期。 - ジョブログは直近 200 件保持。終了 30 分後に
cleanup_old_jobsが掃除。 - WebSocket(
/ws/jobs/{id},/ws/remembering/{id},/ws/setup/progress)は terminal/消失で close。
10. エラー分類
生成失敗を 4 カテゴリへ分け、UI が「設定見直し系」か「要報告の不具合」かを出し分ける(error_classify.py)。
| カテゴリ | 意味 | 例(code) |
|---|---|---|
config | 設定・入力で直る | model_not_selected, controlnet_requires_sdxl, lora_not_found, controlnet_model_incompatible |
environment | 環境・セットアップ | deps_not_ready, nf4_dependency_missing, cuda_kernel_incompatible |
resource | リソース不足 | vram_insufficient, ram_insufficient |
bug | 想定外(要報告) | generation_error |
classify_exception() が型なし例外を解析(“meta tensor”→非互換 SDXL、“out of memory”→VRAM 不足、“no kernel image”→GPU 非対応 等)。要報告カテゴリのみ traceback を添えて開発者報告導線を出す。
11. REST / WebSocket API 一覧
すべて X-Umaru-Token 必須(画像 GET はクエリトークン)。/healthz のみ無認証。
セットアップ・設定・モデル
| メソッド | パス | 機能 |
|---|---|---|
| GET | /healthz | 死活+VRAMキュー状態 |
| POST | /api/heartbeat | ハートビート |
| GET | /api/setup/status | 起動診断項目 |
| GET | /api/folder-status | フォルダ状態 |
| POST | /api/setup/install-python | Python 環境確認 |
| POST | /api/setup/install-dependencies | 基本依存導入 |
| POST | /api/setup/install-sdxl | SDXL 依存導入(cpu/cuda) |
| POST | /api/setup/download-dictionary | 辞書 DL |
| WS | /ws/setup/progress | 導入進捗 |
| GET/PATCH | /api/settings | 設定 |
| GET | /api/models / /api/loras | モデル/LoRA 一覧(状態付) |
キャラクター・生成・アルバム
| メソッド | パス | 機能 |
|---|---|---|
| GET/POST | /api/characters | 一覧/作成 |
| GET | /api/characters/summaries | カード集計 |
| GET/PATCH/DELETE | /api/characters/{id} | 取得/更新/削除 |
| PUT/DELETE | /api/characters/{id}/cover | カバー固定/解除 |
| POST | /api/generate | txt2img 生成 |
| POST | /api/img2img | img2img 生成 |
| POST | /api/pose/to-i2i | ポーズ PNG→generation |
| POST | /api/pose/controlnet | ControlNet 生成 |
| GET/POST | /api/pose/controlnet/models,/download | CN モデル一覧/DL |
| GET | /api/album | 一覧(ページング) |
| GET/DELETE | /api/album/{id} | 詳細/削除 |
| GET | /api/album/{id}/image,/thumbnail,/download | 画像/サムネ/DL |
| GET | /api/album/{id}/restore | フォーム復元 |
| POST | /api/album/{id}/open-folder | 保存先を開く |
| POST | /api/album/reassign-character | キャラ一括再割当 |
| GET/POST/DELETE | /api/favorite-seeds | 雰囲気の種 |
学習素材・お祈り・成長・学習
| メソッド | パス | 機能 |
|---|---|---|
| GET/POST | /api/characters/{id}/memory-images | 学習素材 |
| PATCH/DELETE | /api/memory-images/{id} | 更新/削除 |
| POST | /api/memory-images/{id}/mark-training-candidate | 学習候補化 |
| POST/GET | /api/prayer-sessions | お祈り作成/一覧 |
| GET/DELETE | /api/prayer-sessions/{id} | 取得/削除 |
| PATCH | /api/prayer-sessions/{id}/compare-theme | ラボ条件変更 |
| POST | /api/prayer-groups/{id}/generate,/regenerate | 組生成 |
| PATCH | /api/prayer-candidates/{id}/rank | ランク付け |
| POST | /api/prayer-sessions/{id}/finalize-groups,/final,/save-winners | 決勝/勝者保存 |
| GET/POST | /api/growth-albums | 成長アルバム |
| POST | /api/growth-albums/{id}/steps,/branch | 段階追加/分岐 |
| POST | /api/growth-album-steps/{id}/select-generation | 段階の画像選択 |
| POST | /api/characters/{id}/remember/prepare | 学習準備 |
| POST | /api/remembering-sessions/{id}/start,/stop | 学習開始/停止 |
| GET | /api/remembering-sessions/{id}/progress,/config | 進捗/設定 |
| WS | /ws/remembering/{id} | 学習進捗 |
| GET/POST | /api/jobs/{id},/cancel | ジョブ取得/中止 |
| WS | /ws/jobs/{id} | ジョブ進捗 |
辞書・プロンプト・学習ツール・配布
| メソッド | パス | 機能 |
|---|---|---|
| POST | /api/dictionaries/import-folder,/import-custom,/import | 取り込み |
| GET | /api/dictionaries/search,/entries,/count,“ | 検索/一覧/件数 |
| POST | /api/tags/color-to-tag | 色→タグ |
| GET | /api/negative-charms | お守り一覧 |
| POST | /api/prompt/cleanup,/estimate-volume,/resolve | 整頓/ボリューム/解決 |
| GET/POST | /api/training-runtime/status,/consent,/install,/cancel,/update,/reset,/logs | 学習ツール |
| GET/POST | /api/diagnostics/inference-check | 実推論診断 |
| GET | /api/legal-notices,/api/release/status | 法務/配布ゲート |
| POST | /api/characters/{id}/exports/yurari,/suzuna-profile | エクスポート |
| GET | /api/exports/{id} | エクスポート取得 |
12. Tauri ネイティブコマンド一覧
src-tauri/src/lib.rs の invoke_handler。フロントは src/api.ts 経由で呼ぶ。
| コマンド | 機能 |
|---|---|
start_backend / stop_backend | サイドカー起動/停止 |
get_backend_config | base_url + token 取得 |
show_main_window / close_splashscreen | 2 段階ハンドオフ |
log_frontend_error | フロントエラーを umaru-tauri.log へ |
open_dictionary_folder / open_folder | フォルダを開く |
open_external_url | 許可ドメインのみ外部リンク |
select_folder / select_file | ネイティブ選択ダイアログ(rfd) |
save_bytes | 保存ダイアログ+バイト書き出し(WebView2 のDL先不定問題対策) |
get_default_folders | 既定フォルダ一覧 |
ウィンドウ操作(最小化/最大化/閉じる/ドラッグ)は @tauri-apps/api/window を src/api.ts がラップ(decorations:false のカスタムタイトルバー対応)。
13. ビルド・配布・リリースゲート
ビルド
- フロント:
npm run build=tsc && node build.mjs。Node 24 + Rollup の bundling クラッシュ回避のため、Vite ではなく esbuild 直接バンドル(build.mjs)。 - アプリ:
npm run tauri build(NSIS, Japanese, perMachine)。resourcesに backend / assets / resources / LICENSES / EULA / NOTICE を同梱。 - dev:
tauri.conf.jsonのbeforeDevCommandがscripts/dev-start.ps1、フロントはvite(:5173)、バックエンドはnpm run backend(uvicorn :17891,UMARU_DEV_TOKEN=dev-token)。
リリースゲート(GET /api/release/status, release.py)
ローカル成果物から各ゲートを集計し、全 ready まで配布前は閉じる。
flowchart TD
R["release/status"] --> G1["licenses(third-party-licenses.json の blocked/review/missing=0)"]
R --> G2["real_model(inference-check JSON)"]
R --> G3["training(manifest commit/sha256 固定)"]
R --> G4["eula / notice / checksums"]
R --> G5["installer(bundle 内 .msi/.exe)"]
R --> G6["signing / defender / smartscreen(RELEASE_CHECK_*.md の人手記録)"]
- 品質チェック:
npm run check(build + audit + cargo check + py compile +security_smoke.py+check_no_bundled_danbooru.py)。 - ライセンス監査:
npm run licenses(scripts/generate_licenses.pyが npm/Rust/Python 依存を集計、deny.tomlポリシー)。 - チェックサム:
npm run checksums(checksums.sha256)。 - 実推論診断(
/api/diagnostics/inference-check): 直近 200 件の生成からengine_mode(real_gpu/real_cpu)を集計し、placeholder でない実モデル推論実績をlogs/inference-check-*.jsonに記録。 - 同梱禁止チェック(
check_no_bundled_danbooru.py)で Danbooru 由来データの混入を防止。
14. テスト構成
flowchart LR
subgraph Backend["backend/tests (pytest)"]
U["unit: color_to_tag / controlnet_models /\nplacement / detect_offloading / error_classify /\ngpu / job_store / paths / security /\ntag_dict / training_runtime / vram_queue 他"]
I["integration: api_characters / api_tags /\napi_pose / api_album_download / api_album_reassign /\napi_auth_coverage / controlnet_family / controlnet_nf4 他"]
end
subgraph Frontend["src/**/__tests__ (vitest + RTL + fast-check)"]
F["album / generate(characterBuilder/colorPicker/\ngenerateForm/promptHelpers) / pose(kinematics/\nopenpose/skeleton/projection/presets) / prayer 他"]
end
subgraph Rust["src-tauri (cargo test)"]
RT["resource 解決 / sidecar_work_dir レイアウト"]
end
- CI:
.github/workflows/test.yml。 - フロントは
vitest(jsdom)+ Testing Library + プロパティテスト(fast-check、色変換やキネマティクスの不変条件)。 - バックエンドは
pytest(unit / integration)。生成・学習・ControlNet を含む API カバレッジ。
付録: 機能とコードの対応早見表
| 機能 | フロント | バックエンド |
|---|---|---|
| つくる(生成) | features/generate/* | routers/workflows.py, services/generation_service.py |
| ポーズエディタ | features/pose/* | services/controlnet_service.py |
| アルバム | features/album/* | routers/workflows.py(album 系) |
| お祈り | features/prayer/* | routers/workflows.py(prayer 系) |
| 成長アルバム | features/growth/* | routers/workflows.py(growth 系) |
| 学習素材 | features/memory/* | routers/workflows.py(memory-images) |
| この子を学習 | features/remember/* | services/training_service.py, training_runtime.py |
| 辞書 | features/dictionaries/* | routers/dictionaries.py, services/dictionary_service.py |
| セットアップ | features/setup/* | routers/workflows.py(setup 系) |
| 設定 | features/settings/* | routers/settings.py, services/settings_service.py |
| 配布準備 | features/release/* | routers/release.py, routers/diagnostics.py |
| 起動/IPC | src/api.ts, src/app/* | src-tauri/src/lib.rs, backend/main.py, security.py |
本書は実装(コミット時点の main)から機械的・網羅的に再構成したものです。挙動の最終的な正は各実装ファイルにあります。