darask0 commited on
Commit
1ec327b
·
verified ·
1 Parent(s): 8ad5e9b

Fix rapid-moss compatibility with native ComfyUI MOSS models

Browse files

Support fused QKV projections and preserve fp32 modulation/residuals in native blocks. Add native UI/API workflows and regression coverage. Validated 1024x1024, 4-step generation with native and HF loaders; 8 tests pass.

README.md CHANGED
@@ -71,6 +71,10 @@ LoRA の適用方法、CFG、解像度などの決まりは、下の「[注意
71
 
72
  カスタムノード `comfyui/rapid-moss/` を同梱しています。ComfyUI 標準の `LoraLoaderModelOnly` は LoRA を重みにマージするので、このアダプタには使えません。このノードを使ってください。
73
 
 
 
 
 
74
  1. MOSS-Image 公式ノード([OpenMOSS/MOSS-Image](https://github.com/OpenMOSS/MOSS-Image) の `comfyui/moss-image/`)を入れます。
75
  2. このリポジトリの `comfyui/rapid-moss/` を `ComfyUI/custom_nodes/rapid-moss/` にコピーします。
76
  3. 上の 3 ファイルを `ComfyUI/models/rapid_moss/rapid-moss-4step/` に置きます。
 
71
 
72
  カスタムノード `comfyui/rapid-moss/` を同梱しています。ComfyUI 標準の `LoraLoaderModelOnly` は LoRA を重みにマージするので、このアダプタには使えません。このノードを使ってください。
73
 
74
+ **ComfyUI 標準ローダーにも対応しています。** `comfyui/rapid-moss/example_workflows/rapid-moss-4step-native.json` を使い、`Load Diffusion Model` で `moss_image_sft_bf16.safetensors`、`Load CLIP` で Qwen3.5-9B (type: `moss_image`)、`Load VAE` で FLUX.2 VAE を選んでください。QKV が結合されたモデルで出ていた `ModuleDict ... has no attribute q` を修正しました。この構成では OpenMOSS の HF ローダーノードは不要です。
75
+
76
+ 従来の HF ローダーを使う場合:
77
+
78
  1. MOSS-Image 公式ノード([OpenMOSS/MOSS-Image](https://github.com/OpenMOSS/MOSS-Image) の `comfyui/moss-image/`)を入れます。
79
  2. このリポジトリの `comfyui/rapid-moss/` を `ComfyUI/custom_nodes/rapid-moss/` にコピーします。
80
  3. 上の 3 ファイルを `ComfyUI/models/rapid_moss/rapid-moss-4step/` に置きます。
code/tests/test_comfy_adapter.py ADDED
@@ -0,0 +1,132 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """COMFYUI_ROOT=/path/to/ComfyUI python -m pytest code/tests/test_comfy_adapter.py"""
2
+ import importlib.util
3
+ import os
4
+ from pathlib import Path
5
+ import sys
6
+
7
+ import pytest
8
+ import torch
9
+ from safetensors.torch import save_file
10
+
11
+ sys.path.insert(0, os.environ['COMFYUI_ROOT'])
12
+ from comfy.cli_args import args
13
+ args.cpu = True
14
+ import comfy.ops
15
+ from comfy.ldm.moss_image.model import MOSSImage
16
+ from comfy.model_patcher import ModelPatcher
17
+
18
+ spec = importlib.util.spec_from_file_location('rapid_nodes', Path(__file__).parents[2] / 'comfyui/rapid-moss/nodes.py')
19
+ rapid = importlib.util.module_from_spec(spec)
20
+ spec.loader.exec_module(rapid)
21
+
22
+
23
+ @pytest.fixture
24
+ def adapter(tmp_path, monkeypatch):
25
+ torch.manual_seed(123)
26
+ dim, ffn, rank = 128, 192, 3
27
+ lora = {}
28
+ for name in rapid.PROJ:
29
+ inp, out = (ffn, dim) if name == 'ffn_down' else (dim, 2 * ffn if name == 'ffn_up_gate' else dim)
30
+ prefix = 'diffusion_model.layers.0.proj.' + name
31
+ lora[prefix + '.lora_down.weight'] = torch.randn(rank, inp) * 0.03
32
+ lora[prefix + '.lora_up.weight'] = torch.randn(out, rank) * 0.03
33
+ lora[prefix + '.alpha'] = torch.tensor(float(rank))
34
+ time = {
35
+ 'time_embedding.0.weight': torch.randn(dim, 256) * .01,
36
+ 'time_embedding.0.bias': torch.randn(dim) * .01,
37
+ 'time_embedding.2.weight': torch.randn(dim, dim) * .01,
38
+ 'time_embedding.2.bias': torch.randn(dim) * .01,
39
+ 'shared_block_modulation.bias': torch.randn(6 * dim) * .01,
40
+ 'video_head_modulation.bias': torch.randn(2 * dim) * .01,
41
+ }
42
+ save_file(lora, tmp_path / 'lora.safetensors')
43
+ save_file(time, tmp_path / 'time_embedding.safetensors')
44
+ save_file({'latent_mix.Q': torch.eye(128), 'step_codes': torch.tensor([1000, 968, 926, 871])},
45
+ tmp_path / 'dataspace.safetensors')
46
+ monkeypatch.setattr(rapid, '_adapter_dirs', lambda: {'test': str(tmp_path)})
47
+ return lora
48
+
49
+
50
+ def make_model(fused):
51
+ holder = torch.nn.Module()
52
+ holder.diffusion_model = MOSSImage(dim=128, ffn_dim=192, num_heads=1, num_layers=1,
53
+ fused_qkv=fused, dtype=torch.float32, operations=comfy.ops.manual_cast)
54
+ for p in holder.parameters():
55
+ torch.nn.init.normal_(p, std=.02)
56
+ return ModelPatcher(holder, torch.device('cpu'), torch.device('cpu'))
57
+
58
+
59
+ @pytest.mark.parametrize('fused', [False, True])
60
+ def test_lora_apply_and_restore(adapter, fused):
61
+ model = make_model(fused)
62
+ layer = model.model.diffusion_model.layers[0]
63
+ base_weights = {k: m.weight.detach().clone() for k, m in layer.proj.items()}
64
+ patched, _, sigmas = rapid.RapidMOSSAdapter().apply(model, 'test', 4, .7)
65
+ assert sigmas.tolist() == pytest.approx([1, .968, .926, .871, 0])
66
+ assert not model.object_patches
67
+ patched.patch_model(load_weights=False)
68
+ for name, module in layer.proj.items():
69
+ x = torch.randn(1, 5, module.weight.shape[1])
70
+ expected = torch.nn.functional.linear(x, base_weights[name])
71
+ names = ('q', 'k', 'v') if name == 'qkv' else (name,)
72
+ deltas = []
73
+ for proj in names:
74
+ prefix = 'diffusion_model.layers.0.proj.' + proj
75
+ deltas.append(.7 * (x @ adapter[prefix + '.lora_down.weight'].T) @ adapter[prefix + '.lora_up.weight'].T)
76
+ expected += torch.cat(deltas, dim=-1)
77
+ torch.testing.assert_close(module(x), expected)
78
+ torch.testing.assert_close(module.weight, base_weights[name], rtol=0, atol=0)
79
+ patched.unpatch_model()
80
+ for name, module in layer.proj.items():
81
+ x = torch.randn(1, 2, module.weight.shape[1])
82
+ torch.testing.assert_close(module(x), torch.nn.functional.linear(x, base_weights[name]))
83
+
84
+
85
+ def test_sampler_matches_dataspace_recurrence():
86
+ torch.manual_seed(4)
87
+ z = torch.randn(1, 3, 2, 2)
88
+ q = torch.randn(3, 3) * .1
89
+ codes = torch.tensor([1000, 968, 926, 871])
90
+ sigmas = torch.cat((codes.float() / 1000, torch.zeros(1)))
91
+ calls = []
92
+ def denoise(x, sigma):
93
+ calls.append(sigma.clone())
94
+ return x - sigma[:, None, None, None] * (x * .3 + sigma[:, None, None, None])
95
+ expected = torch.zeros_like(z)
96
+ for code in codes:
97
+ x = z + torch.einsum('oc,bchw->bohw', q, expected)
98
+ expected = z - (x * .3 + code / 1000)
99
+ actual = rapid.sample_dataspace(denoise, z, sigmas, Q=q, codes=codes)
100
+ torch.testing.assert_close(actual, expected)
101
+ assert len(calls) == 4
102
+
103
+
104
+ def test_reject_too_many_steps(adapter):
105
+ with pytest.raises(ValueError, match='4 step codes'):
106
+ rapid.RapidMOSSAdapter().apply(make_model(True), 'test', 5, 1.)
107
+
108
+
109
+ @pytest.mark.parametrize('dtype', [torch.float32, torch.bfloat16])
110
+ @pytest.mark.parametrize('device', ['cpu', pytest.param('cuda', marks=pytest.mark.skipif(
111
+ os.environ.get('RAPID_MOSS_TEST_CUDA') != '1', reason='set RAPID_MOSS_TEST_CUDA=1 for GPU checks'))])
112
+ def test_native_forward_fused_matches_split(adapter, dtype, device):
113
+ model = make_model(False)
114
+ fused = make_model(True)
115
+ sd = model.model.state_dict()
116
+ prefix = 'diffusion_model.layers.0.proj.'
117
+ sd[prefix + 'qkv.weight'] = torch.cat([sd.pop(prefix + n + '.weight') for n in ('q', 'k', 'v')])
118
+ fused.model.load_state_dict(sd)
119
+ x = torch.randn(1, 128, 2, 3, device=device, dtype=dtype)
120
+ ctx = torch.randn(1, 4, 4096, device=device, dtype=dtype)
121
+ t = torch.tensor([.968], device=device)
122
+ outputs = []
123
+ for m in (model, fused):
124
+ m.model.to(device=device, dtype=dtype)
125
+ patched, _, _ = rapid.RapidMOSSAdapter().apply(m, 'test', 4, 1.)
126
+ patched.patch_model(load_weights=False)
127
+ with torch.no_grad():
128
+ outputs.append(m.model.diffusion_model(x.clone(), t, ctx))
129
+ patched.unpatch_model()
130
+ assert all(torch.isfinite(out).all() for out in outputs)
131
+ assert all(out.abs().max() < 10 for out in outputs)
132
+ torch.testing.assert_close(*outputs, rtol=.01, atol=.001)
comfyui/rapid-moss/README.md CHANGED
@@ -3,7 +3,23 @@
3
  MOSS-Image-SFT を **4 ステップ・CFG なし**で動かすカスタムノードです(rapid-moss の DMAD 4 ステップアダプタ。lodestones の 8 ステップ DMD も同じノードで使えます)。
4
  ComfyUI 標準の `SamplerCustomAdvanced` と組み合わせて使います。
5
 
6
- ## 必要なもの
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
  | もの | 入手先 |
9
  |---|---|
@@ -11,14 +27,14 @@ ComfyUI 標準の `SamplerCustomAdvanced` と組み合わせて使います。
11
  | MOSS-Image 公式ノード | [OpenMOSS/MOSS-Image](https://github.com/OpenMOSS/MOSS-Image) の `comfyui/moss-image/` を `ComfyUI/custom_nodes/moss-image/` へコピー(ComfyUI Manager で「MOSS-Image」を検索してもよい) |
12
  | MOSS-Image-SFT 本体 | [OpenMOSS-Team/MOSS-Image-SFT](https://huggingface.co/OpenMOSS-Team/MOSS-Image-SFT) のフォルダ一式(`transformer/`、`text_encoder/`、`tokenizer/`、`vae/`) |
13
  | このノード | このフォルダ(`comfyui/rapid-moss/`)を `ComfyUI/custom_nodes/rapid-moss/` へコピー |
14
- | アダプタ | [darask0/rapid-moss](https://huggingface.co/darask0/rapid-moss) の 3 ファイルを `ComfyUI/models/rapid_moss/rapid-moss-4step/` へ |
15
 
16
  ```bash
17
  cd ComfyUI
18
  hf download OpenMOSS-Team/MOSS-Image-SFT --local-dir /path/to/MOSS-Image-SFT --exclude "assets/*"
19
- hf download darask0/rapid-moss lora.safetensors time_embedding.safetensors dataspace.safetensors \
20
  --local-dir models/rapid_moss/rapid-moss-4step
21
- hf download darask0/rapid-moss --include "comfyui/rapid-moss/*" --local-dir /tmp/rapid-moss
22
  cp -r /tmp/rapid-moss/comfyui/rapid-moss custom_nodes/rapid-moss
23
  # 任意: lodestones の 8 ステップ版も使う場合
24
  hf download lodestones/MOSS-Image-SFT-8-steps-dmd lora.safetensors time_embedding.safetensors dataspace.safetensors \
@@ -57,7 +73,7 @@ API で投げる場合は `example_workflows/rapid-moss-4step-api.json` を使
57
 
58
  ## 注意
59
 
60
- モデル全体の注意点(分かっている弱点、検証していないことなど)は、[モデルカードの「注意点」](https://huggingface.co/darask0/rapid-moss#注意点)にまとめています。ComfyUI で特に気をつけることは次のとおりです。
61
 
62
  - **`model_path` は MOSS-Image-SFT のフォルダにしてください。** アダプタは SFT から学習しています。Base との組み合わせは確かめていません。
63
  - **このアダプタに `LoraLoaderModelOnly` は使えません。** LoRA を重みにマージすると、差分が bf16 の丸めで消えて効きません。必ずこのノードで適用してください。
@@ -72,17 +88,22 @@ API で投げる場合は `example_workflows/rapid-moss-4step-api.json` を使
72
 
73
  ## 動作確認(2026-10-17)
74
 
 
 
 
 
 
75
  - **環境:** ComfyUI master(fc04da9、2026-10-16)、MOSS-Image 公式ノード(c1355e6)、torch 2.11 + CUDA 12.8、RTX PRO 6000。
76
  - **結果:** 学習コードで作った正解画像(同じ seed 1234)とほぼ同じ絵になりました。テキストエンコーダの実装の違いによる細かい差だけです。
77
  - **lodestones 8 ステップ:** 同じノードで動きました。
78
  - **時間:** 1024² 1 枚あたり 4.5〜6.9 秒(テキストエンコード・VAE 込み、他のジョブと GPU を共用した状態)。
79
 
80
- ![ComfyUI check](https://huggingface.co/darask0/rapid-moss/resolve/main/images/comfyui-check.jpg)
81
 
82
  ## English
83
 
84
- Custom node for 4-step, CFG-free MOSS-Image-SFT with the rapid-moss adapter (also runs lodestones' 8-step DMD). Install the
85
- official MOSS-Image nodes, copy this folder to `ComfyUI/custom_nodes/rapid-moss/`, and put `lora.safetensors`,
86
  `time_embedding.safetensors`, `dataspace.safetensors` in `ComfyUI/models/rapid_moss/<name>/`. Load
87
  `example_workflows/rapid-moss-4step.json`, set the HF loader's `model_path` to MOSS-Image-SFT, and run. Use `BasicGuider`
88
  (no CFG / negative), no `ModelSamplingSD3` or SLG, `weight_dtype` default/bf16/fp32 (fp8 unsupported). The node applies the
 
3
  MOSS-Image-SFT を **4 ステップ・CFG なし**で動かすカスタムノードです(rapid-moss の DMAD 4 ステップアダプタ。lodestones の 8 ステップ DMD も同じノードで使えます)。
4
  ComfyUI 標準の `SamplerCustomAdvanced` と組み合わせて使います。
5
 
6
+ ## ComfyUI 標準ローダーを使う場合
7
+
8
+ MOSS-Image 対応の最新 ComfyUI と、この `rapid-moss` ノードだけで使えます。OpenMOSS の HF ローダーノードは不要です。
9
+
10
+ 1. [Comfy-Org/MOSS-Image](https://huggingface.co/Comfy-Org/MOSS-Image) から `moss_image_sft_bf16.safetensors` を `models/diffusion_models/`、Qwen3.5-9B のテキストエンコーダを `models/text_encoders/`、`flux2-vae.safetensors` を `models/vae/` に置きます。
11
+ 2. アダプタの3ファイルを `models/rapid_moss/rapid-moss-4step/` に置き、このフォルダを `custom_nodes/rapid-moss/` にコピーして再起動します。
12
+ 3. `example_workflows/rapid-moss-4step-native.json` を開きます。`Load CLIP` の type は `moss_image` にします。API 版は `rapid-moss-4step-native-api.json` です。
13
+
14
+ `AttributeError: 'ModuleDict' object has no attribute 'q'` が出る場合は、このノードの古い版が使われています。ComfyUI 用のモデルは Q/K/V が `qkv` にまとめられているため、この版では各 LoRA の差分を Q/K/V の対応する出力へ別々に適用します。元の HF 形式も引き続き使えます。
15
+
16
+ また、標準モデルのブロックではアダプタの FP32 modulation と残差を保持します。BF16 用の fused RMS/AdaLN に FP32 modulation を渡すことによる黒画像を防ぎ、FFN の LoRA も必ず適用します。
17
+
18
+ 画像生成モデルは **SFT の BF16** を使ってください。INT8/FP8/GGUF の生成モデルはこのアダプタの検証対象外です。テキストエンコーダの量子化は別で、ComfyUI が対応する Qwen3.5-9B を選べます。
19
+
20
+ **English:** Native ComfyUI loaders are supported. Update this node and use `rapid-moss-4step-native.json` with the SFT BF16 diffusion model, `CLIPLoader` type `moss_image`, and the FLUX.2 VAE. The OpenMOSS custom loader is optional. This fixes discussion #1: repacked models have a fused `proj.qkv`, while the adapter has separate Q/K/V LoRAs. Their unmerged fp32 updates are applied to the corresponding output slices. Native blocks also preserve fp32 modulation/residuals instead of feeding mixed dtypes into the fused RMS/AdaLN kernel, which can produce black images. The original HF-loader workflow remains supported.
21
+
22
+ ## HF ローダーを使う場合の必要なもの
23
 
24
  | もの | 入手先 |
25
  |---|---|
 
27
  | MOSS-Image 公式ノード | [OpenMOSS/MOSS-Image](https://github.com/OpenMOSS/MOSS-Image) の `comfyui/moss-image/` を `ComfyUI/custom_nodes/moss-image/` へコピー(ComfyUI Manager で「MOSS-Image」を検索してもよい) |
28
  | MOSS-Image-SFT 本体 | [OpenMOSS-Team/MOSS-Image-SFT](https://huggingface.co/OpenMOSS-Team/MOSS-Image-SFT) のフォルダ一式(`transformer/`、`text_encoder/`、`tokenizer/`、`vae/`) |
29
  | このノード | このフォルダ(`comfyui/rapid-moss/`)を `ComfyUI/custom_nodes/rapid-moss/` へコピー |
30
+ | アダプタ | [darask0/MOSS-Image_4-step_adapter_lora](https://huggingface.co/darask0/MOSS-Image_4-step_adapter_lora) の 3 ファイルを `ComfyUI/models/rapid_moss/rapid-moss-4step/` へ |
31
 
32
  ```bash
33
  cd ComfyUI
34
  hf download OpenMOSS-Team/MOSS-Image-SFT --local-dir /path/to/MOSS-Image-SFT --exclude "assets/*"
35
+ hf download darask0/MOSS-Image_4-step_adapter_lora lora.safetensors time_embedding.safetensors dataspace.safetensors \
36
  --local-dir models/rapid_moss/rapid-moss-4step
37
+ hf download darask0/MOSS-Image_4-step_adapter_lora --include "comfyui/rapid-moss/*" --local-dir /tmp/rapid-moss
38
  cp -r /tmp/rapid-moss/comfyui/rapid-moss custom_nodes/rapid-moss
39
  # 任意: lodestones の 8 ステップ版も使う場合
40
  hf download lodestones/MOSS-Image-SFT-8-steps-dmd lora.safetensors time_embedding.safetensors dataspace.safetensors \
 
73
 
74
  ## 注意
75
 
76
+ モデル全体の注意点(分かっている弱点、検証していないことなど)は、[モデルカードの「注意点」](https://huggingface.co/darask0/MOSS-Image_4-step_adapter_lora#注意点)にまとめています。ComfyUI で特に気をつけることは次のとおりです。
77
 
78
  - **`model_path` は MOSS-Image-SFT のフォルダにしてください。** アダプタは SFT から学習しています。Base との組み合わせは確かめていません。
79
  - **このアダプタに `LoraLoaderModelOnly` は使えません。** LoRA を重みにマージすると、差分が bf16 の丸めで消えて効きません。必ずこのノードで適用してください。
 
88
 
89
  ## 動作確認(2026-10-17)
90
 
91
+ - **標準ローダー対応の修正版:** ComfyUI `5cc4b870`、torch 2.13.0+cu130、RTX PRO 6000 で、標準ローダーと HF ローダーの両方から 1024×1024・4ステップ・seed 1234 の画像生成を確認しました。標準側は公式 SFT 重みの Q/K/V を連結した BF16 ファイルと、BF16 Qwen3.5-9B を使用しています。両実装の計算の違いにより画像の細部は異なります。
92
+ - **回帰テスト:** Q/K/V 分離・結合形式の LoRA 計算と解除、専用サンプラー、ステップ数上限、CPU/CUDA の FP32/BF16 forward の計8件が成功しました。`COMFYUI_ROOT=/path/to/ComfyUI RAPID_MOSS_TEST_CUDA=1 python -m pytest code/tests/test_comfy_adapter.py` で実行できます。
93
+
94
+ 以下は初版の HF ローダーでの確認記録です。
95
+
96
  - **環境:** ComfyUI master(fc04da9、2026-10-16)、MOSS-Image 公式ノード(c1355e6)、torch 2.11 + CUDA 12.8、RTX PRO 6000。
97
  - **結果:** 学習コードで作った正解画像(同じ seed 1234)とほぼ同じ絵になりました。テキストエンコーダの実装の違いによる細かい差だけです。
98
  - **lodestones 8 ステップ:** 同じノードで動きました。
99
  - **時間:** 1024² 1 枚あたり 4.5〜6.9 秒(テキストエンコード・VAE 込み、他のジョブと GPU を共用した状態)。
100
 
101
+ ![ComfyUI check](https://huggingface.co/darask0/MOSS-Image_4-step_adapter_lora/resolve/main/images/comfyui-check.jpg)
102
 
103
  ## English
104
 
105
+ Custom node for 4-step, CFG-free MOSS-Image-SFT with the rapid-moss adapter (also runs lodestones' 8-step DMD). For the
106
+ HF-loader workflow below, install the official MOSS-Image nodes. Copy this folder to `ComfyUI/custom_nodes/rapid-moss/`, and put `lora.safetensors`,
107
  `time_embedding.safetensors`, `dataspace.safetensors` in `ComfyUI/models/rapid_moss/<name>/`. Load
108
  `example_workflows/rapid-moss-4step.json`, set the HF loader's `model_path` to MOSS-Image-SFT, and run. Use `BasicGuider`
109
  (no CFG / negative), no `ModelSamplingSD3` or SLG, `weight_dtype` default/bf16/fp32 (fp8 unsupported). The node applies the
comfyui/rapid-moss/example_workflows/rapid-moss-4step-native-api.json ADDED
@@ -0,0 +1,144 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "1": {
3
+ "class_type": "UNETLoader",
4
+ "inputs": {
5
+ "unet_name": "moss_image_sft_bf16.safetensors",
6
+ "weight_dtype": "default"
7
+ }
8
+ },
9
+ "2": {
10
+ "class_type": "RapidMOSSAdapter",
11
+ "_meta": {
12
+ "title": "MOSS-Image Few-Step Adapter (rapid-moss)"
13
+ },
14
+ "inputs": {
15
+ "model": [
16
+ "1",
17
+ 0
18
+ ],
19
+ "adapter": "rapid-moss-4step",
20
+ "steps": 4,
21
+ "lora_strength": 1.0
22
+ }
23
+ },
24
+ "3": {
25
+ "class_type": "CLIPTextEncode",
26
+ "_meta": {
27
+ "title": "Prompt"
28
+ },
29
+ "inputs": {
30
+ "clip": [
31
+ "10",
32
+ 0
33
+ ],
34
+ "text": "A high-quality anime illustration of a girl with long silver hair and teal eyes, wearing a yellow raincoat, standing at a rainy bus stop at night, neon reflections on wet asphalt"
35
+ }
36
+ },
37
+ "4": {
38
+ "class_type": "BasicGuider",
39
+ "_meta": {
40
+ "title": "BasicGuider (no CFG)"
41
+ },
42
+ "inputs": {
43
+ "model": [
44
+ "2",
45
+ 0
46
+ ],
47
+ "conditioning": [
48
+ "3",
49
+ 0
50
+ ]
51
+ }
52
+ },
53
+ "5": {
54
+ "class_type": "RandomNoise",
55
+ "_meta": {
56
+ "title": "RandomNoise"
57
+ },
58
+ "inputs": {
59
+ "noise_seed": 42
60
+ }
61
+ },
62
+ "6": {
63
+ "class_type": "EmptyFlux2LatentImage",
64
+ "_meta": {
65
+ "title": "Empty Flux 2 Latent"
66
+ },
67
+ "inputs": {
68
+ "width": 1024,
69
+ "height": 1024,
70
+ "batch_size": 1
71
+ }
72
+ },
73
+ "7": {
74
+ "class_type": "SamplerCustomAdvanced",
75
+ "_meta": {
76
+ "title": "SamplerCustomAdvanced"
77
+ },
78
+ "inputs": {
79
+ "noise": [
80
+ "5",
81
+ 0
82
+ ],
83
+ "guider": [
84
+ "4",
85
+ 0
86
+ ],
87
+ "sampler": [
88
+ "2",
89
+ 1
90
+ ],
91
+ "sigmas": [
92
+ "2",
93
+ 2
94
+ ],
95
+ "latent_image": [
96
+ "6",
97
+ 0
98
+ ]
99
+ }
100
+ },
101
+ "8": {
102
+ "class_type": "VAEDecode",
103
+ "_meta": {
104
+ "title": "VAE Decode"
105
+ },
106
+ "inputs": {
107
+ "samples": [
108
+ "7",
109
+ 0
110
+ ],
111
+ "vae": [
112
+ "11",
113
+ 0
114
+ ]
115
+ }
116
+ },
117
+ "9": {
118
+ "class_type": "SaveImage",
119
+ "_meta": {
120
+ "title": "Save Image"
121
+ },
122
+ "inputs": {
123
+ "filename_prefix": "rapid-moss",
124
+ "images": [
125
+ "8",
126
+ 0
127
+ ]
128
+ }
129
+ },
130
+ "10": {
131
+ "class_type": "CLIPLoader",
132
+ "inputs": {
133
+ "clip_name": "qwen3.5_9b_int8_convrot.safetensors",
134
+ "type": "moss_image",
135
+ "device": "default"
136
+ }
137
+ },
138
+ "11": {
139
+ "class_type": "VAELoader",
140
+ "inputs": {
141
+ "vae_name": "flux2-vae.safetensors"
142
+ }
143
+ }
144
+ }
comfyui/rapid-moss/example_workflows/rapid-moss-4step-native.json ADDED
@@ -0,0 +1,546 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "last_node_id": 11,
3
+ "last_link_id": 12,
4
+ "nodes": [
5
+ {
6
+ "id": 1,
7
+ "type": "UNETLoader",
8
+ "pos": [
9
+ 40,
10
+ 120
11
+ ],
12
+ "size": [
13
+ 380,
14
+ 100
15
+ ],
16
+ "flags": {},
17
+ "order": 0,
18
+ "mode": 0,
19
+ "inputs": [],
20
+ "outputs": [
21
+ {
22
+ "name": "MODEL",
23
+ "type": "MODEL",
24
+ "links": [
25
+ 1
26
+ ],
27
+ "slot_index": 0
28
+ }
29
+ ],
30
+ "properties": {
31
+ "Node name for S&R": "UNETLoader"
32
+ },
33
+ "widgets_values": [
34
+ "moss_image_sft_bf16.safetensors",
35
+ "default"
36
+ ],
37
+ "title": "Load MOSS-Image SFT (BF16)"
38
+ },
39
+ {
40
+ "id": 2,
41
+ "type": "RapidMOSSAdapter",
42
+ "pos": [
43
+ 470,
44
+ 60
45
+ ],
46
+ "size": [
47
+ 380,
48
+ 130
49
+ ],
50
+ "flags": {},
51
+ "order": 1,
52
+ "mode": 0,
53
+ "inputs": [
54
+ {
55
+ "name": "model",
56
+ "type": "MODEL",
57
+ "link": 1
58
+ }
59
+ ],
60
+ "outputs": [
61
+ {
62
+ "name": "model",
63
+ "type": "MODEL",
64
+ "links": [
65
+ 3
66
+ ],
67
+ "slot_index": 0
68
+ },
69
+ {
70
+ "name": "sampler",
71
+ "type": "SAMPLER",
72
+ "links": [
73
+ 7
74
+ ],
75
+ "slot_index": 1
76
+ },
77
+ {
78
+ "name": "sigmas",
79
+ "type": "SIGMAS",
80
+ "links": [
81
+ 8
82
+ ],
83
+ "slot_index": 2
84
+ }
85
+ ],
86
+ "properties": {
87
+ "Node name for S&R": "RapidMOSSAdapter"
88
+ },
89
+ "widgets_values": [
90
+ "rapid-moss-4step",
91
+ 4,
92
+ 1.0
93
+ ]
94
+ },
95
+ {
96
+ "id": 3,
97
+ "type": "CLIPTextEncode",
98
+ "pos": [
99
+ 470,
100
+ 250
101
+ ],
102
+ "size": [
103
+ 420,
104
+ 200
105
+ ],
106
+ "flags": {},
107
+ "order": 2,
108
+ "mode": 0,
109
+ "inputs": [
110
+ {
111
+ "name": "clip",
112
+ "type": "CLIP",
113
+ "link": 2
114
+ }
115
+ ],
116
+ "outputs": [
117
+ {
118
+ "name": "CONDITIONING",
119
+ "type": "CONDITIONING",
120
+ "links": [
121
+ 4
122
+ ],
123
+ "slot_index": 0
124
+ }
125
+ ],
126
+ "properties": {
127
+ "Node name for S&R": "CLIPTextEncode"
128
+ },
129
+ "widgets_values": [
130
+ "A high-quality anime illustration of a girl with long silver hair and teal eyes, wearing a yellow raincoat, standing at a rainy bus stop at night, neon reflections on wet asphalt"
131
+ ],
132
+ "title": "Prompt"
133
+ },
134
+ {
135
+ "id": 4,
136
+ "type": "BasicGuider",
137
+ "pos": [
138
+ 940,
139
+ 60
140
+ ],
141
+ "size": [
142
+ 260,
143
+ 50
144
+ ],
145
+ "flags": {},
146
+ "order": 3,
147
+ "mode": 0,
148
+ "inputs": [
149
+ {
150
+ "name": "model",
151
+ "type": "MODEL",
152
+ "link": 3
153
+ },
154
+ {
155
+ "name": "conditioning",
156
+ "type": "CONDITIONING",
157
+ "link": 4
158
+ }
159
+ ],
160
+ "outputs": [
161
+ {
162
+ "name": "GUIDER",
163
+ "type": "GUIDER",
164
+ "links": [
165
+ 6
166
+ ],
167
+ "slot_index": 0
168
+ }
169
+ ],
170
+ "properties": {
171
+ "Node name for S&R": "BasicGuider"
172
+ },
173
+ "widgets_values": [],
174
+ "title": "BasicGuider (no CFG)"
175
+ },
176
+ {
177
+ "id": 5,
178
+ "type": "RandomNoise",
179
+ "pos": [
180
+ 940,
181
+ 160
182
+ ],
183
+ "size": [
184
+ 260,
185
+ 82
186
+ ],
187
+ "flags": {},
188
+ "order": 4,
189
+ "mode": 0,
190
+ "inputs": [],
191
+ "outputs": [
192
+ {
193
+ "name": "NOISE",
194
+ "type": "NOISE",
195
+ "links": [
196
+ 5
197
+ ],
198
+ "slot_index": 0
199
+ }
200
+ ],
201
+ "properties": {
202
+ "Node name for S&R": "RandomNoise"
203
+ },
204
+ "widgets_values": [
205
+ 42,
206
+ "randomize"
207
+ ]
208
+ },
209
+ {
210
+ "id": 6,
211
+ "type": "EmptyFlux2LatentImage",
212
+ "pos": [
213
+ 940,
214
+ 290
215
+ ],
216
+ "size": [
217
+ 260,
218
+ 106
219
+ ],
220
+ "flags": {},
221
+ "order": 5,
222
+ "mode": 0,
223
+ "inputs": [],
224
+ "outputs": [
225
+ {
226
+ "name": "LATENT",
227
+ "type": "LATENT",
228
+ "links": [
229
+ 9
230
+ ],
231
+ "slot_index": 0
232
+ }
233
+ ],
234
+ "properties": {
235
+ "Node name for S&R": "EmptyFlux2LatentImage"
236
+ },
237
+ "widgets_values": [
238
+ 1024,
239
+ 1024,
240
+ 1
241
+ ]
242
+ },
243
+ {
244
+ "id": 7,
245
+ "type": "SamplerCustomAdvanced",
246
+ "pos": [
247
+ 1250,
248
+ 60
249
+ ],
250
+ "size": [
251
+ 300,
252
+ 110
253
+ ],
254
+ "flags": {},
255
+ "order": 6,
256
+ "mode": 0,
257
+ "inputs": [
258
+ {
259
+ "name": "noise",
260
+ "type": "NOISE",
261
+ "link": 5
262
+ },
263
+ {
264
+ "name": "guider",
265
+ "type": "GUIDER",
266
+ "link": 6
267
+ },
268
+ {
269
+ "name": "sampler",
270
+ "type": "SAMPLER",
271
+ "link": 7
272
+ },
273
+ {
274
+ "name": "sigmas",
275
+ "type": "SIGMAS",
276
+ "link": 8
277
+ },
278
+ {
279
+ "name": "latent_image",
280
+ "type": "LATENT",
281
+ "link": 9
282
+ }
283
+ ],
284
+ "outputs": [
285
+ {
286
+ "name": "output",
287
+ "type": "LATENT",
288
+ "links": [
289
+ 10
290
+ ],
291
+ "slot_index": 0
292
+ },
293
+ {
294
+ "name": "denoised_output",
295
+ "type": "LATENT",
296
+ "links": [],
297
+ "slot_index": 1
298
+ }
299
+ ],
300
+ "properties": {
301
+ "Node name for S&R": "SamplerCustomAdvanced"
302
+ },
303
+ "widgets_values": []
304
+ },
305
+ {
306
+ "id": 8,
307
+ "type": "VAEDecode",
308
+ "pos": [
309
+ 1250,
310
+ 230
311
+ ],
312
+ "size": [
313
+ 210,
314
+ 46
315
+ ],
316
+ "flags": {},
317
+ "order": 7,
318
+ "mode": 0,
319
+ "inputs": [
320
+ {
321
+ "name": "samples",
322
+ "type": "LATENT",
323
+ "link": 10
324
+ },
325
+ {
326
+ "name": "vae",
327
+ "type": "VAE",
328
+ "link": 11
329
+ }
330
+ ],
331
+ "outputs": [
332
+ {
333
+ "name": "IMAGE",
334
+ "type": "IMAGE",
335
+ "links": [
336
+ 12
337
+ ],
338
+ "slot_index": 0
339
+ }
340
+ ],
341
+ "properties": {
342
+ "Node name for S&R": "VAEDecode"
343
+ },
344
+ "widgets_values": []
345
+ },
346
+ {
347
+ "id": 9,
348
+ "type": "SaveImage",
349
+ "pos": [
350
+ 1600,
351
+ 60
352
+ ],
353
+ "size": [
354
+ 520,
355
+ 560
356
+ ],
357
+ "flags": {},
358
+ "order": 8,
359
+ "mode": 0,
360
+ "inputs": [
361
+ {
362
+ "name": "images",
363
+ "type": "IMAGE",
364
+ "link": 12
365
+ }
366
+ ],
367
+ "outputs": [],
368
+ "properties": {
369
+ "Node name for S&R": "SaveImage"
370
+ },
371
+ "widgets_values": [
372
+ "rapid-moss"
373
+ ]
374
+ },
375
+ {
376
+ "id": 10,
377
+ "type": "CLIPLoader",
378
+ "title": "Load Qwen3.5 text encoder",
379
+ "pos": [
380
+ 40,
381
+ 310
382
+ ],
383
+ "size": [
384
+ 380,
385
+ 120
386
+ ],
387
+ "flags": {},
388
+ "order": 0,
389
+ "mode": 0,
390
+ "inputs": [],
391
+ "outputs": [
392
+ {
393
+ "name": "CLIP",
394
+ "type": "CLIP",
395
+ "links": [
396
+ 2
397
+ ],
398
+ "slot_index": 0
399
+ }
400
+ ],
401
+ "properties": {
402
+ "Node name for S&R": "CLIPLoader"
403
+ },
404
+ "widgets_values": [
405
+ "qwen3.5_9b_int8_convrot.safetensors",
406
+ "moss_image",
407
+ "default"
408
+ ]
409
+ },
410
+ {
411
+ "id": 11,
412
+ "type": "VAELoader",
413
+ "title": "Load FLUX.2 VAE",
414
+ "pos": [
415
+ 40,
416
+ 510
417
+ ],
418
+ "size": [
419
+ 380,
420
+ 120
421
+ ],
422
+ "flags": {},
423
+ "order": 0,
424
+ "mode": 0,
425
+ "inputs": [],
426
+ "outputs": [
427
+ {
428
+ "name": "VAE",
429
+ "type": "VAE",
430
+ "links": [
431
+ 11
432
+ ],
433
+ "slot_index": 0
434
+ }
435
+ ],
436
+ "properties": {
437
+ "Node name for S&R": "VAELoader"
438
+ },
439
+ "widgets_values": [
440
+ "flux2-vae.safetensors"
441
+ ]
442
+ }
443
+ ],
444
+ "links": [
445
+ [
446
+ 1,
447
+ 1,
448
+ 0,
449
+ 2,
450
+ 0,
451
+ "MODEL"
452
+ ],
453
+ [
454
+ 2,
455
+ 10,
456
+ 0,
457
+ 3,
458
+ 0,
459
+ "CLIP"
460
+ ],
461
+ [
462
+ 3,
463
+ 2,
464
+ 0,
465
+ 4,
466
+ 0,
467
+ "MODEL"
468
+ ],
469
+ [
470
+ 4,
471
+ 3,
472
+ 0,
473
+ 4,
474
+ 1,
475
+ "CONDITIONING"
476
+ ],
477
+ [
478
+ 5,
479
+ 5,
480
+ 0,
481
+ 7,
482
+ 0,
483
+ "NOISE"
484
+ ],
485
+ [
486
+ 6,
487
+ 4,
488
+ 0,
489
+ 7,
490
+ 1,
491
+ "GUIDER"
492
+ ],
493
+ [
494
+ 7,
495
+ 2,
496
+ 1,
497
+ 7,
498
+ 2,
499
+ "SAMPLER"
500
+ ],
501
+ [
502
+ 8,
503
+ 2,
504
+ 2,
505
+ 7,
506
+ 3,
507
+ "SIGMAS"
508
+ ],
509
+ [
510
+ 9,
511
+ 6,
512
+ 0,
513
+ 7,
514
+ 4,
515
+ "LATENT"
516
+ ],
517
+ [
518
+ 10,
519
+ 7,
520
+ 0,
521
+ 8,
522
+ 0,
523
+ "LATENT"
524
+ ],
525
+ [
526
+ 11,
527
+ 11,
528
+ 0,
529
+ 8,
530
+ 1,
531
+ "VAE"
532
+ ],
533
+ [
534
+ 12,
535
+ 8,
536
+ 0,
537
+ 9,
538
+ 0,
539
+ "IMAGE"
540
+ ]
541
+ ],
542
+ "groups": [],
543
+ "config": {},
544
+ "extra": {},
545
+ "version": 0.4
546
+ }
comfyui/rapid-moss/nodes.py CHANGED
@@ -1,6 +1,6 @@
1
  """Few-step MOSS-Image adapters (rapid-moss DMAD 4-step, lodestones 8-step DMD) for ComfyUI.
2
 
3
- Works on the MODEL from the MOSS-Image nodes (github.com/OpenMOSS/MOSS-Image, comfyui/moss-image).
4
  An adapter is a folder in models/rapid_moss/ holding lora.safetensors, time_embedding.safetensors and
5
  dataspace.safetensors. The node returns a patched MODEL plus the data-space SAMPLER and SIGMAS for
6
  SamplerCustomAdvanced (use BasicGuider: no CFG, no negative prompt).
@@ -72,6 +72,47 @@ def _lora_forward(module, down, up, scale):
72
  return forward
73
 
74
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
75
  @torch.no_grad()
76
  def sample_dataspace(model, x, sigmas, extra_args=None, callback=None, disable=None, Q=None, codes=None):
77
  """x0 = 0; for r: x = z + Q x0, v = net(x, code_r), x0 = z - v (one noise z, no schedule, no CFG)."""
@@ -132,13 +173,22 @@ class RapidMOSSAdapter:
132
  for name in ('shared_block_modulation', 'video_head_modulation'):
133
  weight = patched.get_model_object(DM + name).weight.detach().to('cpu', torch.float32)
134
  patched.add_object_patch(DM + name, _FP32Linear(weight, time_path[name + '.bias'].float()))
135
- for i in range(len(patched.get_model_object(DM + 'layers'))):
 
 
 
136
  for proj in PROJ:
137
  target = f'{DM}layers.{i}.proj.{proj}'
138
  down = lora[target + '.lora_down.weight'].float()
139
  up = lora[target + '.lora_up.weight'].float()
140
  scale = lora_strength * float(lora[target + '.alpha']) / down.shape[0]
141
- patched.add_object_patch(target + '.forward', _lora_forward(patched.get_model_object(target), down, up, scale))
 
 
 
 
 
 
142
 
143
  sampler = comfy.samplers.KSAMPLER(sample_dataspace, extra_options={'Q': data['latent_mix.Q'].float(), 'codes': codes})
144
  sigmas = torch.cat((codes[:steps] / 1000.0, torch.zeros(1)))
 
1
  """Few-step MOSS-Image adapters (rapid-moss DMAD 4-step, lodestones 8-step DMD) for ComfyUI.
2
 
3
+ Works on native ComfyUI MOSS-Image models and the OpenMOSS HF loader.
4
  An adapter is a folder in models/rapid_moss/ holding lora.safetensors, time_embedding.safetensors and
5
  dataspace.safetensors. The node returns a patched MODEL plus the data-space SAMPLER and SIGMAS for
6
  SamplerCustomAdvanced (use BasicGuider: no CFG, no negative prompt).
 
72
  return forward
73
 
74
 
75
+ def _qkv_lora_forward(module, adapters):
76
+ """Repacked ComfyUI weights fuse Q/K/V; each LoRA still has its own down projection."""
77
+ class_forward = type(module).forward
78
+ tensors = [_DeviceCopy(down, up) for down, up, _ in adapters]
79
+
80
+ def forward(x, *args, **kwargs):
81
+ out = class_forward(module, x, *args, **kwargs)
82
+ parts = out.chunk(3, dim=-1)
83
+ result = []
84
+ for part, cached, (_, _, scale) in zip(parts, tensors, adapters):
85
+ down, up = cached.get(x.device)
86
+ delta = ((x.float() @ down.t()) @ up.t()) * scale
87
+ result.append(part + delta.to(part.dtype))
88
+ return torch.cat(result, dim=-1)
89
+ return forward
90
+
91
+
92
+ def _native_block_forward(block):
93
+ # Import only for native models; the standalone OpenMOSS plugin also runs on older ComfyUI.
94
+ from comfy.ldm.moss_image.model import attention, situ
95
+
96
+ def forward(x, mod, pe, transformer_options=None):
97
+ transformer_options = {} if transformer_options is None else transformer_options
98
+ shift_a, scale_a, gate_a, shift_f, scale_f, gate_f = mod
99
+ dtype = block.proj['ffn_up_gate'].weight.dtype
100
+ # The native fused RMS/AdaLN kernel assumes matching dtypes. The adapter's
101
+ # modulation is fp32, so use the module norm and keep the residual in fp32.
102
+ y = (block.norm1(x) * (1 + scale_a) + shift_a).to(dtype)
103
+ if block.fused_qkv:
104
+ q, k, v = block.proj['qkv'](y).chunk(3, dim=-1)
105
+ else:
106
+ q, k, v = (block.proj[n](y) for n in ('q', 'k', 'v'))
107
+ out = attention(q, k, v, block.q_norm, block.k_norm, pe, block.num_heads,
108
+ block.comfy_attention, transformer_options)
109
+ x = x + block.proj['o'](out).float() * gate_a
110
+ y = (block.norm2(x) * (1 + scale_f) + shift_f).to(dtype)
111
+ hidden, gate = block.proj['ffn_up_gate'](y).chunk(2, dim=-1)
112
+ return x + block.proj['ffn_down'](situ(hidden, gate)).float() * gate_f
113
+ return forward
114
+
115
+
116
  @torch.no_grad()
117
  def sample_dataspace(model, x, sigmas, extra_args=None, callback=None, disable=None, Q=None, codes=None):
118
  """x0 = 0; for r: x = z + Q x0, v = net(x, code_r), x0 = z - v (one noise z, no schedule, no CFG)."""
 
173
  for name in ('shared_block_modulation', 'video_head_modulation'):
174
  weight = patched.get_model_object(DM + name).weight.detach().to('cpu', torch.float32)
175
  patched.add_object_patch(DM + name, _FP32Linear(weight, time_path[name + '.bias'].float()))
176
+ for i, layer in enumerate(patched.get_model_object(DM + 'layers')):
177
+ if hasattr(layer, 'fused_qkv'):
178
+ patched.add_object_patch(f'{DM}layers.{i}.forward', _native_block_forward(layer))
179
+ qkv = []
180
  for proj in PROJ:
181
  target = f'{DM}layers.{i}.proj.{proj}'
182
  down = lora[target + '.lora_down.weight'].float()
183
  up = lora[target + '.lora_up.weight'].float()
184
  scale = lora_strength * float(lora[target + '.alpha']) / down.shape[0]
185
+ if proj in ('q', 'k', 'v') and 'qkv' in layer.proj:
186
+ qkv.append((down, up, scale))
187
+ else:
188
+ patched.add_object_patch(target + '.forward', _lora_forward(patched.get_model_object(target), down, up, scale))
189
+ if qkv:
190
+ target = f'{DM}layers.{i}.proj.qkv'
191
+ patched.add_object_patch(target + '.forward', _qkv_lora_forward(patched.get_model_object(target), qkv))
192
 
193
  sampler = comfy.samplers.KSAMPLER(sample_dataspace, extra_options={'Q': data['latent_mix.Q'].float(), 'codes': codes})
194
  sigmas = torch.cat((codes[:steps] / 1000.0, torch.zeros(1)))