From 3bb407efb0aba9b3fc7a58233f748e5115559fda Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Sat, 27 Jun 2026 19:11:39 +0200 Subject: [PATCH] fix(detect): stop krea2 from hijacking LTX-2 (to_gate_logits false-match) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit LTX-2 (LTXV 2.3) LoRAs have dual video/audio + cross-modal attention with '*_attn.to_gate_logits'. The krea2 gate detector's '(?=[._])' lookahead let the '..._attn.to_gate' substring match inside 'to_gate_logits', so LTX-2 LoRAs were detected as krea2, ran through krea2 normalization (which renames transformer_blocks->blocks), and then every key failed to map to the model (1632 skipped, nothing merged). Require a '.' after the gate/proj leaf — always the LoRA suffix in real krea2 keys — so LTX-2 falls through to the existing ltx detection (transformer_blocks + attn1/attn2) and its pass-through normalizer. No version bump (detection-only fix; correctly-detected arches unaffected). Co-Authored-By: Claude Opus 4.8 --- lora_optimizer.py | 6 +++++- tests/test_lora_optimizer.py | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/lora_optimizer.py b/lora_optimizer.py index a176355..e8e9df0 100644 --- a/lora_optimizer.py +++ b/lora_optimizer.py @@ -1069,7 +1069,11 @@ class _LoRAMergeBase: # diffusion_model.blocks.N.attn.wq (Comfy-Org native) # diffusion_model.transformer_blocks.N.attn.to_gate (krea_2 trainer) # transformer.transformer_blocks.N.attn.to_gate + text_fusion (diffusers) - if any(re.search(r'attn[._](?:to_gate|gate|w[qkvo])(?=[._])', k) for k in keys): + # The gate/proj is always a leaf module followed by the LoRA suffix '.' + # (.lora_down/.lora_A/.lokr_w1/.alpha). Require the '.' so this does NOT + # false-match LTX-2's '*_attn.to_gate_logits' (where '_logits' follows) — + # that hijacked LTX-2 LoRAs as krea2 and broke their merge. + if any(re.search(r'attn[._](?:to_gate|gate|w[qkvo])(?=\.)', k) for k in keys): return 'krea2' # Z-Image Turbo (Lumina2): layers.N with attention patterns diff --git a/tests/test_lora_optimizer.py b/tests/test_lora_optimizer.py index 0b4bd30..bd74339 100644 --- a/tests/test_lora_optimizer.py +++ b/tests/test_lora_optimizer.py @@ -4844,6 +4844,22 @@ class Krea2DetectionTests(unittest.TestCase): self.assertEqual(self.det(flux), "flux") self.assertEqual(self.det(qwen), "qwen_image") + def test_ltx2_gate_logits_not_stolen_by_krea2(self): + # Regression: LTX-2 (LTXV 2.3) has dual video/audio + cross-modal attention + # with '*_attn.to_gate_logits'. The substring '..._attn.to_gate' must NOT + # trip krea2's gate detection (krea2's gate is a leaf followed by '.', LTX-2's + # is 'to_gate_logits' with '_' after). These LTX LoRAs were mis-detected as + # krea2 -> wrong normalization (transformer_blocks->blocks) -> nothing merged. + s = _sd([ + "diffusion_model.transformer_blocks.0.attn1.to_q.lora_A.weight", + "diffusion_model.transformer_blocks.0.attn1.to_gate_logits.lora_A.weight", + "diffusion_model.transformer_blocks.0.attn2.to_k.lora_A.weight", + "diffusion_model.transformer_blocks.0.audio_to_video_attn.to_gate_logits.lora_A.weight", + "diffusion_model.transformer_blocks.0.video_to_audio_attn.to_v.lora_A.weight", + ]) + self.assertNotEqual(self.det(s), "krea2") + self.assertEqual(self.det(s), "ltx") + class Krea2NormalizationTests(unittest.TestCase): """Both Krea 2 trainer forms normalize to the model-native diffusion_model.* keys.