diff --git a/RELEASE_NOTES/v2.5.5.md b/RELEASE_NOTES/v2.5.5.md index 09327e4..43c2688 100644 --- a/RELEASE_NOTES/v2.5.5.md +++ b/RELEASE_NOTES/v2.5.5.md @@ -1,58 +1,60 @@ -# v2.5.5 技術解説書: Krea2 OpenPose ControlNet LoRA サポート +# v2.5.5 Technical Documentation: Krea2 OpenPose ControlNet LoRA Support -> 起点コミット: `75f99be1614b18e22c5d58a7c24115d5218e488e`(v2.5.4 時点) -> 対象コミット: `b7cbf11` / `042fe16` / `07ef7ec`(コード)+ `999bac6` / `533495f` / `67f84e6` / `5797d06`(ドキュメント) +> Baseline commit: `75f99be1614b18e22c5d58a7c24115d5218e488e` (v2.5.4) +> Code commits: `b7cbf11` / `042fe16` / `07ef7ec` — Docs commits: `999bac6` / `533495f` / `67f84e6` / `5797d06` --- -## ① 目的 +## ① Purpose -Krea2(`SingleStreamDiT`)向け ControlNet LoRA のうち、**openpose 版** -(`krea2_turbo_openpose_controlnet.safetensors`)のサポートを追加する。 +Add support for the **openpose** variant of the Krea2 (`SingleStreamDiT`) ControlNet LoRA +(`krea2_turbo_openpose_controlnet.safetensors`). -### 背景 +### Background -- v2.5.1 で Krea2 **depth** ControlNet LoRA(`krea2-depth-control-lora.safetensors`)を実装済み。 - これは「拡張 first 投影(`first.weight` を 2 倍入力チャンネルに拡張)+ コントロール潜像注入」方式。 -- openpose 版は構造が根本的に異なる: - - `first.weight` 拡張**なし**(228MB / bfloat16 / rank 32) - - `diffusion_model.blocks.{0-27}` の 28 DiT ブロック + `diffusion_model.txtfusion.layerwise_blocks.{0,1}` / - `refiner_blocks.{0,1}` の**純 LoRA**(256 ペア) - - 訓練時の条件経路が **Kontext 式 reference latent**(`index_timestep_zero`)であり、 - ostris の `comfyui-krea2-ostris-edit` や pysssss の `krea2_controlnet_pose.json` ワークフローが - kv_cache=true で参照画像を注入する方式で訓練されている +- v2.5.1 implemented Krea2 **depth** ControlNet LoRA (`krea2-depth-control-lora.safetensors`) + using the "expanded first projection" approach (`first.weight` expanded to 2x input channels + + control-latent injection). +- The openpose variant is structurally different: + - **No** `first.weight` expansion (228MB / bfloat16 / rank 32) + - Pure LoRA over `diffusion_model.blocks.{0-27}` (28 DiT blocks) + + `diffusion_model.txtfusion.layerwise_blocks.{0,1}` / `refiner_blocks.{0,1}` (256 pairs total) + - The conditioning pathway used at training time is the **Kontext-style reference latent** + (`index_timestep_zero`), i.e. the reference-image injection convention used by ostris' + `comfyui-krea2-ostris-edit` and pysssss' `krea2_controlnet_pose.json` workflow (kv_cache=true). -### 要求事項 +### Requirements -1. **排他的分岐**: openpose は depth / zimage / nunchaku_qwenimage / qwenimage_standard に - 一切影響させない。既存 4 ルートのコードは無変更(インデント調整のみ)であること。 -2. **自己完結**: ostris / pysssss のノードやワークフローに依存せず、このリポジトリ単体で - 同じ pose 制御効果を実現する。 -3. **ローダー非変更**: `Krea2ControlNetLoraLoader` と `__init__.py` は変更しない - (state_dict を MODEL_PATCH で運ぶだけ、分岐判定は `controlnet.py` 側で完結)。 +1. **Exclusive routing**: openpose must not affect depth / zimage / nunchaku_qwenimage / + qwenimage_standard in any way. The existing 4 routes must remain unchanged (indentation only). +2. **Self-contained**: reproduce the same pose-control effect within this repository alone, + without depending on ostris / pysssss nodes or workflows. +3. **Loader untouched**: `Krea2ControlNetLoraLoader` and `__init__.py` are not modified + (the loader just carries the state_dict via MODEL_PATCH; all routing decisions live in + `controlnet.py`). --- -## ② 新規作成・修正したファイル名 +## ② New / Modified Files -| ファイル | 種別 | 変更内容 | +| File | Type | Change | |---|---|---| -| `nodes/controlnet.py` | 修正(+382 行) | openpose ルート実装の中核。新規関数 13 個 + 分岐・INPUT_TYPES 変更 | -| `README.md` | 修正(+60) | Krea2 Control (Depth & OpenPose) セクション追加 | -| `zhmd/README.md` | 修正(+569/-530 実質) | 中文版 README 同内容 | -| `RELEASE_NOTES/changelog.md` | 修正(+10) | v2.5.5 エントリ追加 | -| `zhmd/changelogzh.md` | 修正(+9) | 中文版 changelog 同内容 | -| `RELEASE_NOTES/v2.5.5.md` | **新規** | 本技術解説書 | +| `nodes/controlnet.py` | modified (+382 lines) | Core of the openpose route. 13 new functions + routing / INPUT_TYPES changes | +| `README.md` | modified (+60) | New "Krea2 Control (Depth & OpenPose)" section | +| `zhmd/README.md` | modified | Chinese README, same content | +| `RELEASE_NOTES/changelog.md` | modified (+10) | v2.5.5 entry | +| `zhmd/changelogzh.md` | modified (+9) | Chinese changelog, same content | +| `RELEASE_NOTES/v2.5.5.md` | **new** | This technical documentation | -`__init__.py` は**無変更**(要件 3 を満たす)。 +`__init__.py` is **unchanged** (requirement 3). --- -## ③ 新規作成・修正したコード全文 +## ③ Full New / Modified Code -### 3.1 `nodes/controlnet.py` の変更 +### 3.1 `nodes/controlnet.py` changes -#### (a) import 追加 +#### (a) Added imports ```python import math @@ -77,7 +79,7 @@ import logging logger = logging.getLogger(__name__) ``` -#### (b) `_krea2_target_key` に txtfusion ルーティング追加 +#### (b) `_krea2_target_key`: txtfusion routing added ```python def _krea2_target_key(base): @@ -91,12 +93,12 @@ def _krea2_target_key(base): changed = True if base.startswith("blocks."): return f"diffusion_model.{base}.weight" - if base.startswith("txtfusion."): # ← 追加 + if base.startswith("txtfusion."): # ← added return f"diffusion_model.{base}.weight" return None ``` -#### (c) 新規関数群(`_classify_krea2_control_type` 〜 `_apply_krea2_openpose_control`) +#### (c) New function group (`_classify_krea2_control_type` .. `_apply_krea2_openpose_control`) ```python def _classify_krea2_control_type(state_dict): @@ -445,7 +447,7 @@ def _apply_krea2_openpose_control(model_patched, model_patch, vae, image, streng logger.info("[Krea2OpenposeControl] kv_cache isolated-ref forward armed") ``` -#### (d) INPUT_TYPES 変更(`use_kv_cache` 追加) +#### (d) INPUT_TYPES change (`use_kv_cache` added) ```python @classmethod @@ -460,7 +462,7 @@ def _apply_krea2_openpose_control(model_patched, model_patch, vae, image, streng "use_kv_cache": ("BOOLEAN", {"default": True, "tooltip": "Isolated reference K/V cache mode (ai-toolkit kv_cache training convention). Leave on for Krea2 control LoRAs trained with kv_cache."})}} ``` -#### (e) 実行分岐変更(depth / openpose 排他) +#### (e) Routing change (depth / openpose exclusive) ```python elif route == "krea2": @@ -485,163 +487,167 @@ def _apply_krea2_openpose_control(model_patched, model_patch, vae, image, streng ) ``` -### 3.2 ドキュメント変更(概要) +### 3.2 Documentation changes (summary) -- `README.md` / `zhmd/README.md`: ノード説明の更新 + `### Krea2 Control (Depth & OpenPose)` - セクション新設(`#### Krea2 Depth Control` / `#### Krea2 OpenPose Control`)。 -- `RELEASE_NOTES/changelog.md` / `zhmd/changelogzh.md`: v2.5.5 エントリ追加。 +- `README.md` / `zhmd/README.md`: node descriptions updated + new + `### Krea2 Control (Depth & OpenPose)` section (`#### Krea2 Depth Control` / + `#### Krea2 OpenPose Control`). +- `RELEASE_NOTES/changelog.md` / `zhmd/changelogzh.md`: v2.5.5 entry added. --- -## ④ コードの詳細な解説 +## ④ Detailed Code Explanation -### 4.0 全体アーキテクチャ +### 4.0 Overall Architecture ``` -Krea2ControlNetLoraLoader (変更なし) - │ MODEL_PATCH(LoRA safetensors の state_dict を内包) +Krea2ControlNetLoraLoader (unchanged) + │ MODEL_PATCH (carries the LoRA safetensors state_dict) ▼ NunchakuQI&ZITDiffsynthControlnet.diffsynth_controlnet_nunchaku │ _classify_controlnet_target(model, model_patch) │ → "zimage" | "nunchaku_qwenimage" | "qwenimage_standard" | "krea2" | "unknown" ▼ route == "krea2" - _classify_krea2_control_type(state_dict) ← first.weight の有無 - ├─ "depth" → _apply_krea2_control() (既存、無変更) - └─ "openpose" → _apply_krea2_openpose_control()(新規) - ├─ add_patches(ブロック LoRA 256 キー) - ├─ extra_conds パッチ(reference_latents 注入) - └─ diffusion_model.forward パッチ(kv_cache モード) + _classify_krea2_control_type(state_dict) ← presence of first.weight + ├─ "depth" → _apply_krea2_control() (existing, unchanged) + └─ "openpose" → _apply_krea2_openpose_control() (new) + ├─ add_patches(256 block LoRA keys) + ├─ extra_conds patch (reference_latents injection) + └─ diffusion_model.forward patch (kv_cache mode) ``` -### 4.1 `_classify_krea2_control_type` — depth / openpose の排他判定 +### 4.1 `_classify_krea2_control_type` — depth / openpose exclusive classification -LoRA ファイルの state_dict に `first.weight` 系キーが存在するかで判定する。 +Decides by the presence of a `first.weight`-style key in the LoRA state_dict. -- **depth**: `first.weight` 拡張((6144, 128))を持つ → 従来の depth ルート -- **openpose**: `first` キーなし(純ブロック LoRA)→ openpose ルート +- **depth**: has the expanded `first.weight` ((6144, 128)) → existing depth route +- **openpose**: no `first` key (pure block LoRA) → openpose route -depth ファイルと openpose ファイルは実物の構造が明確に異なる(depth: 861MB float32 rank64 -+ first 拡張 / openpose: 228MB bf16 rank32 純 LoRA)ため、誤判定の余地がない。 -execute 側も `if/elif` なので**両ルートが同時に適用されることは構造的に不可能**。 +The two files are structurally distinct (depth: 861MB float32 rank64 + first expansion / +openpose: 228MB bf16 rank32 pure LoRA), so there is no room for misclassification. +The execute side uses `if/elif`, so **both routes can never be applied simultaneously**. -### 4.2 `_krea2_fit_ref_image` — 参照画像の前処理 +### 4.2 `_krea2_fit_ref_image` — reference image preprocessing -ai-toolkit の Krea2 reference-latent 規約に合わせる: +Follows the ai-toolkit Krea2 reference-latent convention: -- 最大 **1MP**(1024×1024)に**縮小のみ**(拡大はしない) -- 縦横を **/16 にスナップ**(DiT の patch=2 のトークン化に安全) -- `common_upscale(..., "area", "disabled")` でリサイズ(area = ダウンスケールに適した補間) +- Downscale **only** (never upscale) to at most **1MP** (1024×1024) +- Snap width/height to **/16** (safe for the DiT patch=2 tokenization) +- Resize via `common_upscale(..., "area", "disabled")` (area = suitable for downscaling) -### 4.3 `_krea2_make_ref_cond_patch` — ネイティブ ref 注入(extra_conds パッチ) +### 4.3 `_krea2_make_ref_cond_patch` — native ref injection (extra_conds patch) -Krea2 コアモデル(`comfy/model_base.py` の `Krea2` クラス)は -`extra_conds` が `reference_latents` と `reference_latents_method` を処理する。 +The Krea2 core model (`Krea2` class in `comfy/model_base.py`) consumes +`reference_latents` / `reference_latents_method` produced by `extra_conds`. -- `extra_conds()`: 出力 dict に `ref_latents`(`CONDList`)と - `ref_latents_method = "index_timestep_zero"` を追加 -- `extra_conds_shapes()`: メモリプランニング用に平坦化形状 `[1, 16, prod//16]` を追加 -- **二重注入防止**: `kwargs["reference_latents"]` が既にある(= `TextEncodeKrea2OstrisEdit` - 併用時)か `out` に `ref_latents` が既にある場合は**スキップ**。 - → モデルノード単体でも動き、VLM テキストエンコーダ併用時も安全。 +- `extra_conds()`: adds `ref_latents` (`CONDList`) and + `ref_latents_method = "index_timestep_zero"` to the output dict +- `extra_conds_shapes()`: adds the flattened shape `[1, 16, prod//16]` for memory planning +- **Double-injection guard**: skipped when `kwargs["reference_latents"]` already exists + (= when `TextEncodeKrea2OstrisEdit` is used in parallel) or `ref_latents` is already in `out`. + → Works standalone, and is safe to combine with a VLM text encoder. -### 4.4 `_krea2_ref_pack` / `_krea2_patchify` — ref トークン化と RoPE 座標 +### 4.4 `_krea2_ref_pack` / `_krea2_patchify` — ref tokenization and RoPE positions -`_krea2_patchify` は通常の画像トークン化 `(B,C,H,W) → (B, L, C·p²)`。 +`_krea2_patchify` is the regular image tokenization `(B,C,H,W) → (B, L, C·p²)`. -`_krea2_ref_pack` は参照潜像をトークン化し、**軸 0 のインデックスを i+1** にする -(text=0、ref1=1、ref2=2、… の ai-toolkit 規約): +`_krea2_ref_pack` tokenizes reference latents and sets the **axis-0 index to i+1** +(the ai-toolkit convention: text=0, ref1=1, ref2=2, ...): ``` -rid[..., 0] = i ← 参照インデックス(axis-0) -rid[..., 1] = y グリッド -rid[..., 2] = x グリッド +rid[..., 0] = i ← reference index (axis-0) +rid[..., 1] = y grid +rid[..., 2] = x grid ``` -- 5D 潜像 `(B, C, T, H, W)`(Wan21 VAE、T=1)は 4D に reshape -- `pad_to_patch_size` + `repeat_to_batch_size` でバッチ整合 +- 5D latents `(B, C, T, H, W)` (Wan21 VAE, T=1) are reshaped to 4D +- `pad_to_patch_size` + `repeat_to_batch_size` handle padding and batch alignment -### 4.5 `_krea2_ref_attn_kv` — Attention の K/V capture / 注入 +### 4.5 `_krea2_ref_attn_kv` — Attention with K/V capture / injection -Krea2 の `Attention` を再実装し、2 つの拡張ポイントを持つ: +Re-implements Krea2 `Attention` with two extension points: -- **capture モード**(`kv_capture`): RoPE 適用後・GQA 展開前の `(k, v)` を記録 -- **cache モード**(`kv_cache`): 計算済み ref K/V を `torch.cat` で**キー側に追加** +- **capture mode** (`kv_capture`): records `(k, v)` after RoPE, before GQA expansion +- **cache mode** (`kv_cache`): appends the precomputed ref K/V to the key side via `torch.cat` -処理順: `wq/wk/wv/gate` → reshape → `qknorm` → `apply_rope` → capture/cache → -GQA 展開(`repeat_interleave`)→ `optimized_attention_masked` → `wo(out·sigmoid(gate))`。 +Order: `wq/wk/wv/gate` → reshape → `qknorm` → `apply_rope` → capture/cache → +GQA expansion (`repeat_interleave`) → `optimized_attention_masked` → `wo(out·sigmoid(gate))`. -### 4.6 `_krea2_ref_precompute_kv` — t=0 参照専用パス +### 4.6 `_krea2_ref_precompute_kv` — t=0 reference-only pass -**kv_cache モードの核**。参照トークンだけを全ブロックに通し、各ブロックの -post-RoPE K/V を記録する: +The **core of kv_cache mode**. Runs only the reference tokens through all blocks and +records each block's post-RoPE K/V: -1. `_krea2_ref_pack` で `(reftok, refpos)` を生成 -2. `dit.first(reftok)` で埋め込み -3. **t=0**(`timestep_embedding(zeros)`)のタイムベクトルで条件付け -4. 28 ブロックを `kv_capture` 付きで前進し、`ref_kv` リスト(28 個の (k, v))を得る +1. `_krea2_ref_pack` produces `(reftok, refpos)` +2. `dit.first(reftok)` embeds the tokens +3. Conditioned on the **t=0** time vector (`timestep_embedding(zeros)`) +4. Runs the 28 blocks with `kv_capture`, collecting `ref_kv` (28 (k, v) pairs) -参照トークンはテキストやノイズ画像を見ないため、**1 回のパスが全デノイズステップに再利用**できる。 +Reference tokens never see text or noisy images, so **one pass serves the whole denoise**. -### 4.7 `_krea2_forward_with_cached_refs` — デノイズ本体の forward +### 4.7 `_krea2_forward_with_cached_refs` — the denoising forward -通常の Krea2 forward の再実装。ライブシーケンスは「テキスト + ノイズ画像」のみで、 -各ブロックの attention にキャッシュ済み ref K/V を extra key として追加する: +A re-implementation of the regular Krea2 forward. The live sequence is only +"text + noisy image tokens"; each block's attention appends the cached ref K/V +as extra keys: -- 5D 入力(動画・temporal)は 4D に flatten、出力で復元 -- `_unpack_context` → `txtfusion` → `txtmlp` でテキストを処理 -- text 位置は axis-0=0、画像位置は y/x グリッドで RoPE 座標を構成 -- 出力は `dit.last` → 画像トークンのみ切り出し → patch 逆変換 → 元サイズ復元 +- 5D inputs (temporal) are flattened to 4D and restored on output +- Text processed via `_unpack_context` → `txtfusion` → `txtmlp` +- RoPE positions: text at axis-0=0, image tokens on a y/x grid +- Output: `dit.last` → slice image tokens → patch unpatch → restore original size -### 4.8 `_krea2_ref_fingerprint` — キャッシュの内容キー +### 4.8 `_krea2_ref_fingerprint` — content key for the cache -各ステップで `ref_latents` テンソルは再構築されるためオブジェクト ID は使えない。 -形状 + 和 + 二乗和をキーにして内容ベースでキャッシュを識別する。 +`ref_latents` tensors are rebuilt every step, so object identity cannot be used. +Shape + sum + sum-of-squares serve as a content-based key. -### 4.9 `_krea2_make_ref_kv_forward` — forward 差し替えの状態管理 +### 4.9 `_krea2_make_ref_kv_forward` — forward replacement state management -`diffusion_model.forward` を差し替え、以下のロジックを実装: +Replaces `diffusion_model.forward`: -1. `ref_latents` が無ければ**元の forward にそのまま委譲**(ノード非 openpose 時と同一挙動) -2. **新規実行検出**: sigma は実行内で単調減少する性質を使い、`sig > last_sigma` なら - 新しい実行とみなしてキャッシュをクリア(`sample_sigmas[0]` との一致でも検出) -3. キャッシュに無ければ `_krea2_ref_precompute_kv` で K/V を事前計算して保存 -4. `_krea2_forward_with_cached_refs` でデノイズ +1. No `ref_latents` → **delegate to the original forward** (identical to non-openpose behavior) +2. **New-run detection**: sigmas decrease monotonically within a run, so + `sig > last_sigma` (or matching `sample_sigmas[0]`) clears the cache +3. On cache miss, `_krea2_ref_precompute_kv` computes and stores the K/V +4. `_krea2_forward_with_cached_refs` runs the denoise -### 4.10 `_apply_krea2_openpose_control` — ノードからのエントリポイント +### 4.10 `_apply_krea2_openpose_control` — entry point from the node -1. **ブロック LoRA**: `_krea2_build_block_patches`(既存関数)で - `diffusion_model.blocks.*` + `diffusion_model.txtfusion.*` の 256 キーを - `LoRAAdapter` として構築し、`add_patches(strength_patch=strength)` で適用 -2. **ref 注入**: `_krea2_fit_ref_image` → `vae.encode` → `_krea2_make_ref_cond_patch` - を `extra_conds` / `extra_conds_shapes` のオブジェクトパッチとして登録 -3. **kv_cache**: `use_kv_cache=True`(デフォルト)なら `diffusion_model.forward` を - `_krea2_make_ref_kv_forward` で差し替え -4. vae / image 欠落時は LoRA のみ適用して警告ログ(ref 注入なしでもクラッシュしない) +1. **Block LoRA**: `_krea2_build_block_patches` (existing) builds the 256 keys of + `diffusion_model.blocks.*` + `diffusion_model.txtfusion.*` as `LoRAAdapter` and + applies them via `add_patches(strength_patch=strength)` +2. **Ref injection**: `_krea2_fit_ref_image` → `vae.encode` → `_krea2_make_ref_cond_patch` + registered as `extra_conds` / `extra_conds_shapes` object patches +3. **kv_cache**: with `use_kv_cache=True` (default), `diffusion_model.forward` is replaced + by `_krea2_make_ref_kv_forward` +4. Missing vae/image → LoRA-only with a warning log (no crash even without ref injection) -### 4.11 排他性の保証(重要) +### 4.11 Exclusivity Guarantee (important) -| ルート | 変更 | 根拠 | +| Route | Change | Basis | |---|---|---| -| zimage | 無変更 | `_classify_controlnet_target` の分岐コードは 75f99be から同一 | -| nunchaku_qwenimage | 無変更 | 同上 | -| qwenimage_standard | 無変更 | 同上 | -| krea2 depth | **インデントのみ** | `if/elif` 化に伴う字下げ(`git diff` で確認済み) | -| krea2 openpose | 新規 | 新規関数群 + 分岐追加 | +| zimage | unchanged | `_classify_controlnet_target` branch code identical since 75f99be | +| nunchaku_qwenimage | unchanged | same | +| qwenimage_standard | unchanged | same | +| krea2 depth | **indentation only** | re-indented for the `if/elif` structure (verified via `git diff`) | +| krea2 openpose | new | new functions + routing | -- `use_kv_cache` は **optional** 入力(デフォルト True)なので既存ワークフローは互換。 - openpose ルートでのみ参照され、他の 4 ルートには渡らない。 -- `_classify_controlnet_target` の判定順は zimage → nunchaku → qwenimage_standard → krea2。 - `SingleStreamDiT` は ComfyUI core の `comfy/ldm/krea2/model.py` にのみ存在するため、 - 他モデルが Krea2 に誤分類されることはない。 +- `use_kv_cache` is an **optional** input (default True), so existing workflows stay + compatible. It is only referenced by the openpose route and never reaches the other 4. +- `_classify_controlnet_target` checks zimage → nunchaku → qwenimage_standard → krea2 in order. + `SingleStreamDiT` exists only in `comfy/ldm/krea2/model.py`, so no other model family + can be misclassified as Krea2. -### 4.12 検証結果 +### 4.12 Verification Results -- 構文チェック / `python_embeded` での import 成功(INPUT_TYPES・シグネチャ確認) -- ユニットテスト: patchify 形状、ref_pack の axis-0=1・グリッド・5D 処理、fingerprint、 - 1MP スナップ、排他ルート(zimage / nunchaku / qwenimage_standard / depth)無変更 -- **実機 INT8 検証**: HSWQ `moodyKrea2Mix_v50_sci_1off_convrot_int8.safetensors` 上で - `patch_weight_to_device` による bake を確認 → LoRA 差分が weight に反映 - (相対変化 7.76%)。HSWQ v7 bake フック経由でも `int8_baked=256, patches_left=0` を確認。 -- 実機ログ: `route=krea2` → `sub-route: openpose` → `Applied 256 block LoRA patches` → +- Syntax check / import via `python_embeded` succeeded (INPUT_TYPES and signature verified) +- Unit tests: patchify shapes, ref_pack axis-0=1 grid / 5D handling, fingerprint, + 1MP snap, and that the exclusive routes (zimage / nunchaku / qwenimage_standard / depth) + are unchanged +- **Live INT8 verification**: on HSWQ `moodyKrea2Mix_v50_sci_1off_convrot_int8.safetensors`, + `patch_weight_to_device` bake confirmed the LoRA delta reaches the weights + (relative change 7.76%). The HSWQ v7 bake hook also reported + `int8_baked=256, patches_left=0`. +- Live run log: `route=krea2` → `sub-route: openpose` → `Applied 256 block LoRA patches` → `control ref latent shape=(1, 16, 1, 128, 128)` → `kv_cache isolated-ref forward armed` - → 12/12 ステップ完走(エラーなし)。 + → 12/12 steps completed (no errors).