Remove the autocomplete JS class (was ~155 lines). Instead, resolve
short LoRA names server-side: typing "Milena_20260216180133" matches
against installed LoRAs by filename stem, so it resolves to e.g.
"ZImageTurbo/character/Milena_20260216180133.safetensors". No Lora
Manager dependency needed — just plain text input with smart matching.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a multiline text widget where users can type LoRA paths directly
(one per line) with optional strength values. Supports three formats:
path.safetensors (strength 1.0), path:0.8 (both), path:0.8:0.5
(model:clip). When ComfyUI-Lora-Manager is installed, a lightweight
autocomplete dropdown suggests matches via its API. Works without
Lora Manager too (no autocomplete, user types exact paths).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix absolute→relative path matching (endsWith against fullLoraList)
- Fix page_size cap (paginate through all pages, build local map)
- Add explicit base_model_filter param to build_stack (no kwargs leak)
- Add retry when widget options not yet populated
- Preserve selected LoRA value when filter changes (no silent reset)
- Handle workflow restore (apply filter after interceptor setup)
- Extract interceptWidgetValue helper, add configurable:true
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a dropdown that filters LoRA dropdowns by base model type (SDXL,
Flux, Wan, etc.) using ComfyUI-Lora-Manager's API. If Lora Manager is
not installed, the filter widget is automatically hidden and dropdowns
show the full unfiltered list.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Thin subclass of LoRAOptimizer that accepts WANVIDEOMODEL input/output,
enabling LoRA merging for kijai's WanVideoWrapper pipeline. All merging
algorithms (TIES, DARE/DELLA, SVD, auto-strength) are inherited. Wan key
normalization (LyCORIS, diffusers, Fun LoRA, finetrainer, RS-LoRA) was
already supported by the parent class.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a new node that wraps merged LoRA patches as conditioning hooks (HOOKS)
for per-conditioning application instead of global model patching. Also
reorganize README with collapsible detail sections, reduce SVG margins,
and document the new node.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
The mask comparison now uses effective_diff (sign-corrected for negative
weight) instead of raw diff, consistent with the sign voting fix. Without
this, low_conflict and high_conflict were inverted for negative-strength
LoRAs.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- LoRAConflictEditor single-LoRA path now finds the original stack
position instead of hardcoding conflict_mode_1, consistent with
the multi-LoRA path fix from the previous commit.
- LoRAOptimizer.__init__ now calls super().__init__() for forward
compatibility with _LoRAMergeBase changes.
- Fix stale _merge_cache comment (4-tuple → 5-tuple).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Move _normalize_stack from LoRAOptimizer to _LoRAMergeBase so both
LoRAOptimizer and LoRAConflictEditor can use it. Previously the editor
crashed with AttributeError on every execution.
- Fix conflict-mode sign voting to account for negative eff_strength
(negate sign vote when weight < 0), consistent with _compute_conflict_mask.
- Fix widget-to-LoRA mapping to use original stack position instead of
active-only index, preventing misalignment when zero-strength LoRAs
exist in the stack.
- Add torch.cuda.empty_cache() after conflict editor GPU analysis loop.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- _compute_cache_key and LoRAConflictEditor.IS_CHANGED: float(None)
crash when tuples carry clip_strength=None (meaning "use global
multiplier"). Use sentinel -1.0 for hashing.
- SaveMergedLoRA.save_lora: early return when lora_data is None,
which can happen from optimizer early-exit paths.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- All optimize_merge early returns now include lora_data=None as 4th
element (prevents IndexError when SaveMergedLoRA is connected)
- merge_strategy_override now skips when optimization_mode is
weighted_sum_only (user's explicit choice to protect DPO/edit LoRAs
should not be overridden by upstream Conflict Editor)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Single-LoRA path now preserves clip_strength=None instead of
substituting model strength (preserves global multiplier semantics)
- Remove unnecessary GPU->CPU->GPU round-trip in conflict analysis
(keep diffs on GPU during pairwise comparisons)
- Include conflict_mode from stack entries in IS_CHANGED hash
(prevents stale cache when editor-chaining)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allow the LoRAConflictEditor's merge_strategy output to connect to the
optimizer and override its auto-detected merge strategy. Applies to both
global and per-prefix decision paths. Includes cache key and IS_CHANGED
updates so ComfyUI properly detects when the override changes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix prefix pre-filter to match all LoRA key formats (including
diffusers3 lora.up/lora.down patterns)
- Add explicit del mat_up/mat_down after diff computation for GPU memory
- Add early return for single-LoRA case (no pairwise analysis needed)
- Remove redundant import os (already at file level)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Allow users to control how each LoRA behaves in contested weight regions.
'low_conflict' applies only where the LoRA agrees with the majority sign,
'high_conflict' applies only where it disagrees. Defaults to 'all' (no
filtering) with zero overhead when unused.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Auto-strength now uses pairwise cosine similarity for exact vector-sum
energy normalization instead of assuming orthogonal LoRAs
- Save Merged LoRA defaults to ComfyUI loras folder; supports absolute paths
- Add JS extension for LoRA Stack (Dynamic) widget show/hide
- Fix LTX architecture detection for middle_block/mid_block keys
- Update banner SVG with new feature tags
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Only sparsify positions where LoRAs push in opposite directions (sign
conflict), leaving same-sign reinforcing positions untouched. Preserves
unique contributions while still reducing interference.
Adds dare_conflict and della_conflict to the sparsification dropdown.
Also includes tooltip improvements and SaveMergedLoRA auto-rank.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move torch.Generator to compute_device so bernoulli mask generation
stays on GPU, eliminating a full-tensor CPU round-trip per prefix
(~64MB for 4096x4096 layers).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add SaveMergedLoRA node that serializes merged patches to standard
.safetensors LoRA format (lora_up/lora_down/alpha). Full-rank diffs
are SVD-compressed automatically. Includes bake_strength option to
embed output/clip strength into saved weights.
Also adds LoRAStackDynamic node, DARE/DELLA sparsification support,
and related infrastructure (cache key updates, report formatting).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ComfyUI's LoRAAdapter stores data in .weights, not .lora_data.
The wrong attribute name would cause AttributeError at runtime
when re-fusing Z-Image QKV patches in the LoRAAdapter path.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When patches are SVD-compressed, each to_q/to_k/to_v gets an
independent decomposition with its own down matrix. The re-fusion
code assumed shared down matrices (correct for uncompressed LoRA,
wrong after SVD). K and V would use Q's down matrix, producing
incorrect attention weights.
Now checks if down matrices are actually shared (identity check).
If not, expands to full-rank diffs before fusing into a single
diff patch.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- LTX detection: exclude keys containing input_blocks/output_blocks/
down_blocks/up_blocks (SDXL patterns) to prevent Kohya SDXL LoRAs
from being misclassified as LTX and having keys mangled
- Wan RS-LoRA: replace unreliable marker key detection with heuristic
that checks for zero alpha keys in the file (RS-LoRA convention).
The old marker key existed in ALL Wan LoRAs, causing standard LoRAs
without explicit alphas to get sqrt(rank) amplification (8-11x)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Wan: move base_model.model. strip before LyCORIS check so
PEFT-wrapped LyCORIS keys are correctly handled
- FLUX: add missing PEFT prefix strip (was the only normalizer
without one, causing PEFT FLUX LoRAs to pass through unchanged)
- Z-Image: add missing PEFT prefix strip for consistency
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Wan LyCORIS: use regex [._] to match both underscore and dot as
separator for cross_attn/self_attn/ffn (block number fix changed
leading underscore to dot, breaking the subsequent replacements)
- SDXL/LTX/Wan/Qwen-Image: move base_model.model. PEFT prefix strip
before all other prefix checks so PEFT-format keys are correctly
handled by the transformer/unet/text_encoder handlers
- Wan Fun LoRA: consume remaining components after first + optional
_img suffix to avoid silently dropping sub-component segments
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Z-Image detection: exclude Kohya FLUX underscore format
(transformer_single_transformer_blocks_) from zimage match
- Architecture detection: iterate all LoRAs instead of only first,
so a minimal/unknown first LoRA doesn't skip normalization
- Wan LyCORIS: add block number dot separator after prefix strip
to handle non-standard component names after block index
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- QKV split: only split lora_B (up/fused), copy lora_A (down/shared)
to all three Q/K/V components instead of splitting both matrices
- QKV split: fix break→continue so other LoRA formats are tried when
dimension check fails
- QKV split: fix swapped down/up suffix pairs in format list
- Re-fusion: use shared down matrix (one copy) instead of concatenating
three identical copies in LoRAAdapter case
- Single-LoRA fast path: skip for Z-Image since normalized keys
(to_q/to_k/to_v) won't match model's fused qkv keys
- FLUX Kohya: smart underscore-to-dot preserving compound names
(img_attn, txt_mlp, key_norm, lora_up, etc.)
- Init _detected_arch=None in LoRAOptimizer.__init__
- LTX detection: hoist img_mlp check to avoid O(n²) iteration
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ComfyUI no longer recognizes ("lora", ...) tuple patches. LoRA
patches must now be LoRAAdapter instances from comfy.weight_adapter.
Fixes "patch type not recognized lora" error on Lumina2 and other models.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Priority-based dominant (TIES > slerp > avg > sum) ensures TIES
blocks are visible. Show per-strategy breakdown when mixed.
Conflict average excludes single-LoRA sum prefixes.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
GPU SVD is ~10-50x faster than CPU for the randomized SVD algorithm.
Defaults to GPU; CPU option available when GPU memory is tight.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Negative strength negates the LoRA's effect. TIES and weighted_average
handle this correctly but SLERP was using abs(weight) for interpolation
without flipping the diff vector. Now negates v1/v2 when weight < 0.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Spherical linear interpolation preserves magnitude better than
weighted_average when two LoRAs partially oppose each other. Vectors
don't cancel out — SLERP rotates smoothly on the hypersphere.
Automatically used in per_prefix mode when exactly 2 LoRAs contribute
to a prefix and conflict is low. No new settings — internal upgrade
from weighted_average. Falls back to weighted_average for 3+ LoRAs.
SLERP is a linear combination (scalar coefficients per tensor), so
results are fully SVD-compressible at sum-of-ranks — same RAM as
weighted_average.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
non_ties is strictly better than disabled — same quality with lower
RAM usage. Only TIES prefixes stay full-rank (compression is lossy
for them). Reorder options to put non_ties first.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The Auto-Selected Parameters section now correctly shows that
weighted_sum is forced and what the auto-detected mode would have been.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Forces simple weighted sum on all prefixes — no TIES or averaging.
Combined with compress_patches, all patches are fully compressible
with zero quality loss from compression.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TIES merging produces full-rank results from nonlinear ops (trim, sign
election), so SVD compression is inherently lossy on TIES prefixes.
New options:
- non_ties: compress only weighted_sum/weighted_average prefixes (lossless)
- all: compress everything (lossy on TIES, saves most RAM)
- disabled: no compression (default)
Rank is always auto-computed as sum of input LoRA ranks.
Removed manual rank options (64/128/256/512/1024).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Merging N rank-R LoRAs produces effective rank up to N*R. Using max
rank (R) lost ~(N-1)/N of the information. Now auto uses sum of all
input LoRA ranks to preserve full merge quality. Also adds 512 and
1024 rank options for manual control.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Full-rank TIES/weighted_average merged patches consume ~128x more RAM
than standard LoRA patches. This adds a compress_patches option that
re-compresses full-rank diffs back to low-rank via truncated SVD,
reducing RAM by ~32-64x per patch with slight quality loss.
Options: disabled (default), auto (uses max input LoRA rank), 64, 128, 256.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>