From b683f8a2bdba173c5c1adf7945ff3bd33463f63c Mon Sep 17 00:00:00 2001 From: ussoewwin <136552381+ussoewwin@users.noreply.github.com> Date: Wed, 22 Jul 2026 15:56:43 +0900 Subject: [PATCH] fix(lora): restore first-LoRA state-dict cache and fix v2 IS_CHANGED Post-merge additional fixes for PR #52 (per md/PR52_MERGE_PLAN.md): - compose_loras_v2: restore _cached_first_lora_state_dict so the first LoRA state dict loaded during section-3 debug inspection is reused in the section-4 main loop. Without this the first LoRA is read from disk twice whenever the precompiled cache is disabled (default). - qwenimage_v2.py IS_CHANGED: drop the `model` arg (ComfyUI never passes MODEL inputs to IS_CHANGED; the TypeError -> NaN path forced re-compose every run) and remove str(model) from the hash (object repr changes every run, which also forced constant re-execution). --- nodes/lora/qwenimage_v2.py | 3 +-- nunchaku_code/lora_qwen.py | 10 +++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/nodes/lora/qwenimage_v2.py b/nodes/lora/qwenimage_v2.py index 4255c78..df2c820 100644 --- a/nodes/lora/qwenimage_v2.py +++ b/nodes/lora/qwenimage_v2.py @@ -30,14 +30,13 @@ class NunchakuQwenImageLoraStackV2: Node for loading and applying multiple LoRAs to a Nunchaku Qwen Image model with dynamic UI. """ @classmethod - def IS_CHANGED(cls, model, lora_count, cpu_offload="disable", save_precompiled_lora=False, **kwargs): + def IS_CHANGED(cls, lora_count, cpu_offload="disable", save_precompiled_lora=False, **kwargs): """ Detect changes to trigger node re-execution. Returns a hash of relevant parameters to detect changes. """ import hashlib m = hashlib.sha256() - m.update(str(model).encode()) m.update(str(lora_count).encode()) m.update(cpu_offload.encode()) m.update(str(kwargs.get("apply_awq_mod", False)).encode()) diff --git a/nunchaku_code/lora_qwen.py b/nunchaku_code/lora_qwen.py index f358374..0af5503 100644 --- a/nunchaku_code/lora_qwen.py +++ b/nunchaku_code/lora_qwen.py @@ -1315,6 +1315,8 @@ def compose_loras_v2( # --- 3. Debug Inspection (First LoRA) --- # Safe unpack: entries may be 2-tuples (path, strength) or 3-tuples # (path, strength, meta_dict) when the cache meta-dict is attached by the node. + # OPTIMIZATION: Cache first LoRA state dict for reuse in the processing loop + _cached_first_lora_state_dict = None if lora_configs: _first_entry = lora_configs[0] first_lora_path_or_dict = _first_entry[0] @@ -1336,6 +1338,7 @@ def compose_loras_v2( _first_detection = {"has_standard": True, "details": "Standard LoRA (Precompiled Cache)"} else: first_lora_state_dict = _load_lora_state_dict_robust(first_lora_path_or_dict) + _cached_first_lora_state_dict = first_lora_state_dict # Cache for reuse in main loop # Simple logging of format detection _first_detection = _detect_lora_format(first_lora_state_dict) _log_lora_format_detection(str(first_lora_path_or_dict)[:50], _first_detection) @@ -1417,7 +1420,12 @@ def compose_loras_v2( # ------------------------------------------------------------------ # # NORMAL (FULL) FUSE PATH # # ------------------------------------------------------------------ # - lora_state_dict = _load_lora_state_dict_robust(lora_path_or_dict) + # Reuse first LoRA state dict cached during debug inspection (avoids double-read) + # Skip reuse when precompiled cache is enabled (matches original flow) + if idx == 0 and _cached_first_lora_state_dict is not None and _cache_path is None: + lora_state_dict = _cached_first_lora_state_dict + else: + lora_state_dict = _load_lora_state_dict_robust(lora_path_or_dict) if not lora_state_dict: continue