fix: conflict editor memory leak and SaveMergedLoRA is_clip detection

- LoRAConflictEditor: clear loaded_loras after analysis to prevent
  unbounded memory accumulation across runs (100s of MB per large LoRA).
- SaveMergedLoRA: iterate model/clip patches separately with explicit
  is_clip flag instead of checking membership in clip_patches dict.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Ethanfel
2026-03-04 16:58:57 +01:00
co-authored by Claude Opus 4.6
parent 65db42c217
commit fbf7a7b15a
+26 -22
View File
@@ -3009,31 +3009,31 @@ class SaveMergedLoRA:
state_dict = {}
for target_key, patch in list(model_patches.items()) + list(clip_patches.items()):
is_clip = target_key in clip_patches
tkey = target_key[0] if isinstance(target_key, tuple) else target_key
lora_prefix = key_map.get(tkey, tkey)
for is_clip, patches in [(False, model_patches), (True, clip_patches)]:
for target_key, patch in patches.items():
tkey = target_key[0] if isinstance(target_key, tuple) else target_key
lora_prefix = key_map.get(tkey, tkey)
if isinstance(patch, LoRAAdapter):
mat_up, mat_down, alpha, mid, _, _ = patch.weights
alpha = float(alpha) if alpha is not None else float(mat_down.shape[0])
elif isinstance(patch, tuple) and len(patch) == 2 and patch[0] == "diff":
diff_tensor = patch[1][0]
rank = fallback_rank if auto_rank else save_rank
compressed = LoRAOptimizer._compress_to_lowrank(diff_tensor, rank)
mat_up, mat_down, alpha, mid, _, _ = compressed.weights
alpha = float(alpha)
else:
logging.warning(f"[Save Merged LoRA] Skipping unknown patch type for {lora_prefix}: {type(patch)}")
continue
if isinstance(patch, LoRAAdapter):
mat_up, mat_down, alpha, mid, _, _ = patch.weights
alpha = float(alpha) if alpha is not None else float(mat_down.shape[0])
elif isinstance(patch, tuple) and len(patch) == 2 and patch[0] == "diff":
diff_tensor = patch[1][0]
rank = fallback_rank if auto_rank else save_rank
compressed = LoRAOptimizer._compress_to_lowrank(diff_tensor, rank)
mat_up, mat_down, alpha, mid, _, _ = compressed.weights
alpha = float(alpha)
else:
logging.warning(f"[Save Merged LoRA] Skipping unknown patch type for {lora_prefix}: {type(patch)}")
continue
if bake_strength:
strength = clip_strength if is_clip else output_strength
alpha *= strength
if bake_strength:
strength = clip_strength if is_clip else output_strength
alpha *= strength
state_dict[f"{lora_prefix}.lora_up.weight"] = mat_up.contiguous()
state_dict[f"{lora_prefix}.lora_down.weight"] = mat_down.contiguous()
state_dict[f"{lora_prefix}.alpha"] = torch.tensor(alpha)
state_dict[f"{lora_prefix}.lora_up.weight"] = mat_up.contiguous()
state_dict[f"{lora_prefix}.lora_down.weight"] = mat_down.contiguous()
state_dict[f"{lora_prefix}.alpha"] = torch.tensor(alpha)
save_file(state_dict, save_path)
logging.info(f"[Save Merged LoRA] Saved {len(state_dict) // 3} LoRA keys to {save_path}")
@@ -3383,6 +3383,10 @@ class LoRAConflictEditor(_LoRAMergeBase):
prefixes_analyzed,
)
# Free loaded LoRA state dicts — the editor only needs them during analysis,
# and keeping them would leak hundreds of MB per run for large models.
self.loaded_loras.clear()
return (enriched, report, resolved_strategy)
@staticmethod