Vol. 01
2026.06.11
PREVIEWING CONTENT

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 学習を「初心者でも迷わない言葉」で進める常駐型デスクトップアプリ

目次

  1. システムアーキテクチャ
  2. 起動シーケンスとプロセス監視
  3. ディレクトリ・データ配置
  4. データモデル(SQLite)
  5. 認証・セキュリティ
  6. 画面構成・ナビゲーション・用語体系
  7. 機能詳細
  8. 生成エンジン詳細
  9. ジョブ・進捗管理
  10. エラー分類
  11. REST / WebSocket API 一覧
  12. Tauri ネイティブコマンド一覧
  13. ビルド・配布・リリースゲート
  14. テスト構成

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 5package.json, src/
3D ポーズthree 0.184, @react-three/fiber, @react-three/dreisrc/features/pose/
バックエンドPython FastAPI, uvicorn, aiosqlite, Pydanticbackend/main.py, backend/models.py
生成エンジンdiffusers, torch, peft, bitsandbytes, compel(LPW), Pillowbackend/services/generation_service.py
学習kohya-ss/sd-scripts, acceleratebackend/services/training_service.py, backend/training_runtime.py

通信方式

  • IPC(Tauri invoke): ウィンドウ操作、フォルダ/ファイル選択、外部リンク、サイドカー設定取得など OS に近い操作。
  • HTTP/WS(localhost): 業務 API。フロントは src/api.tsapi<T>() ラッパ経由で X-Umaru-Token を必ず付与。ベース URL とトークンは Rust の get_backend_config コマンドで取得(src/api.ts:getRuntimeConfig)。
  • CSPtauri.conf.json): connect-srcipc:http://127.0.0.1:* / ws://127.0.0.1:* に限定。img-src127.0.0.1data: / 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._pthimport 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 ウィンドウの CloseRequestedBackendState::drop でサイドカーを killwaitlib.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.pymigrate() が冪等にスキーマを構築(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
3favorite_seeds(雰囲気の種メモ)
4お祈りラボ(compare_theme/config, variation_label, generation_overrides)、favorite_seeds.name
5成長アルバム(growth_album_sessions/steps
6prayer_candidates.seed(実 seed の可視化)
7characters.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 が Alphanumeric 48 文字を生成し、サイドカーの 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-TokenContent-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.tsbaseNavItems と、内部で切り替える追加タブ(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-dependenciesbackend/requirements.txt)。
  • 画像生成は CPU フォールバック可、LoRA 学習は CUDA 必須を文言で明確に分離(training_cuda 行)。

7.2 設定(SettingsView)

GET/PATCH /api/settingsapp_settings テーブルに key-value で永続化(settings_service.py)。

設定既定用途
ui_modecreator用語モード
model_dir / lora_dir / kohya_dir / output_dir%APPDATA%/Umaru/*フォルダ
controlnet_openpose_path""OpenPose ControlNet モデル(同梱せず手動配置 or DL)
nsfw_filtertrueつくる画面の生成前警告
danbooru_nsfw_levelsafe辞書検索・タグ候補の表示範囲(safe/sensitive/explicit)
remove_exif_by_defaulttruePNG メタデータ保存とダウンロード時再保存に反映

フォルダ選択は 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 を扱う。フォームは localStorageumaru_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_idscharacter_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)と rankcaption を付与。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}/stepsstep_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 (退避)
  • manifestbackend/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.pycreate_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 層を bitsandbytesLinear4bit(nf4, compute_dtype=bfloat16)へ。VAE は fp16 維持。SD1.x では無効化。CUDA 必須
TAESDXL 軽量 VAE既定 ON。madebyollin/taesdxl(SDXL)/taesd(SD1.x)で VAE 復号を高速化。DL 失敗時は同梱フル VAE に自動フォールバック
複数 LoRAadapter として個別ロードし 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.pyjobs 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-pythonPython 環境確認
POST/api/setup/install-dependencies基本依存導入
POST/api/setup/install-sdxlSDXL 依存導入(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/generatetxt2img 生成
POST/api/img2imgimg2img 生成
POST/api/pose/to-i2iポーズ PNG→generation
POST/api/pose/controlnetControlNet 生成
GET/POST/api/pose/controlnet/models,/downloadCN モデル一覧/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.rsinvoke_handler。フロントは src/api.ts 経由で呼ぶ。

コマンド機能
start_backend / stop_backendサイドカー起動/停止
get_backend_configbase_url + token 取得
show_main_window / close_splashscreen2 段階ハンドオフ
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/windowsrc/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.jsonbeforeDevCommandscripts/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 licensesscripts/generate_licenses.py が npm/Rust/Python 依存を集計、deny.toml ポリシー)。
  • チェックサム: npm run checksumschecksums.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
起動/IPCsrc/api.ts, src/app/*src-tauri/src/lib.rs, backend/main.py, security.py

本書は実装(コミット時点の main)から機械的・網羅的に再構成したものです。挙動の最終的な正は各実装ファイルにあります。