Nodes/WAS Node Suite v3/Power LoRA Merger
ComfyUI Node Runs on cloud

Power LoRA Merger

Bake several LoRAs into one file instead of stacking loaders

By WASasquatch·Created 3 years ago·Updated 3 days ago· 1,832
Power LoRA Merger
  • model
  • clip
  • options
  • model
  • clip
  • lora_path
output_filenamemerged_lora.safetensors
output_model_strength1.00
output_clip_strength1.00
modesvd
block_mix_recipeconcept_a_style_b
lora_1_enabledtrue
lora_1None
lora_1_weight1.00
lora_2_enabledtrue
lora_2None
lora_2_weight1.00
lora_3_enabledtrue
lora_3None
lora_3_weight1.00
lora_4_enabledtrue
lora_4None
lora_4_weight1.00
lora_5_enabledtrue
lora_5None
lora_5_weight1.00
lora_6_enabledtrue
lora_6None
lora_6_weight1.00
lora_7_enabledtrue
lora_7None
lora_7_weight1.00
lora_8_enabledtrue
lora_8None
lora_8_weight1.00
lora_9_enabledtrue
lora_9None
lora_9_weight1.00
lora_10_enabledtrue
lora_10None
lora_10_weight1.00
lora_11_enabledtrue
lora_11None
lora_11_weight1.00
lora_12_enabledtrue
lora_12None
lora_12_weight1.00
lora_13_enabledtrue
lora_13None
lora_13_weight1.00
lora_14_enabledtrue
lora_14None
lora_14_weight1.00
lora_15_enabledtrue
lora_15None
lora_15_weight1.00
lora_16_enabledtrue
lora_16None
lora_16_weight1.00
lora_17_enabledtrue
lora_17None
lora_17_weight1.00
lora_18_enabledtrue
lora_18None
lora_18_weight1.00
lora_19_enabledtrue
lora_19None
lora_19_weight1.00
lora_20_enabledtrue
lora_20None
lora_20_weight1.00
lora_21_enabledtrue
lora_21None
lora_21_weight1.00
lora_22_enabledtrue
lora_22None
lora_22_weight1.00
lora_23_enabledtrue
lora_23None
lora_23_weight1.00
lora_24_enabledtrue
lora_24None
lora_24_weight1.00
lora_25_enabledtrue
lora_25None
lora_25_weight1.00
lora_26_enabledtrue
lora_26None
lora_26_weight1.00

If you've ever chained four LoraLoader nodes to combine a character LoRA with a style LoRA and a couple of detail-boost LoRAs, you already know the annoyance: every run reloads and reapplies all four, and sharing the workflow means sharing four separate files. WAS Power LoRA Merger merges them once into a single .safetensors you can drop into models/loras and load normally from then on - with a choice of real merge strategies, not just a naive average.

The "Power" in the name isn't an accident - it's the same convention rgthree uses for its Power Lora Loader, and it means the same thing here: the LoRA files you're actually merging aren't separate graph inputs. You add them through the node's own on-node UI. If you're staring at this node hunting for lora_1/lora_2 sockets to wire up, that's why you won't find them.

How it works

You pick a mode, optionally tune it further with a WAS Power LoRA Merger Options node (its own article - plug it into the optional options input), and the merger writes the result to models/loras. If you also wire in a model and clip, it applies the freshly-merged LoRA to them immediately, so the merge and the apply happen in one run instead of needing a separate LoraLoader afterward.

The modes, per the node's own tooltip:

  • svd (default) - recompress the merged delta via SVD. The general-purpose choice.
  • rebase - SVD-recompress a single LoRA on its own.
  • add - exact linear stacking, no compression.
  • add-diff - start from a base LoRA and add weighted diffs toward the others.
  • add-orth - same idea, but orthogonalizes the contributions so they don't cancel each other out.
  • diff-export - exports only the difference between the first two, useful for extracting what one LoRA added on top of another.
  • moe - routes each module through a mixture-of-experts gate across your source LoRAs.
  • obfuscate - a stack-equivalent factor rebasis that skips SVD.
  • block-mix - routes specific modules to LoRA A or B by a block_mix_recipe, then merges via stack or SVD.

SVD-based LoRA merging isn't new - Kohya's GUI has shipped a "Merge LoRA (SVD)" option for a while, valued because it copes better with mismatched ranks and architectures than a straight stack. This node brings that idea, plus several other strategies, natively into ComfyUI.

block_mix_recipe only matters in block-mix mode: the default concept_a_style_b keeps concept/attention blocks from LoRA A and style/feed-forward blocks from B, with mirror-image and image/text-stream variants for different architectures.

The inputs and outputs that matter

  • mode / block_mix_recipe - the strategy, as above.
  • output_filename (default merged_lora.safetensors) - relative to models/loras; .safetensors gets appended if you leave it off.
  • output_model_strength / output_clip_strength (default 1) - only relevant if you wired in model/clip; how hard the new LoRA gets applied to those pass-throughs.
  • Optional model / clip - apply the merge result to these immediately.
  • Optional options - a WAS_LORA_MERGE_OPTIONS from the companion Options node.

Outputs: model, clip (pass-through, patched if you wired inputs in), and lora_path (STRING) - where it landed, worth routing into a text/preview node to confirm the merge succeeded before you go hunting through models/loras.

How to install it

Via ComfyUI Manager: search WAS_Extras, install, restart. Manually:

cd ComfyUI/custom_nodes
git clone https://github.com/WASasquatch/WAS_Extras

Restart ComfyUI. No extra dependencies for this node specifically.

Common issues & troubleshooting

Merged result barely resembles either source LoRA. SVD-family modes are lossy compression by nature - a low rank or an aggressive auto_rank_threshold on the Options node throws away real signal. Raise the rank before assuming the algorithm is broken.

Picked moe or block-mix and it made things worse. Those modes are aimed at combining LoRAs that specialize in genuinely different things - identity from one, style from another. For merging near-duplicate training runs of the same subject, add or the default svd is the saner starting point.

Strength sliders don't seem to do anything. output_model_strength/output_clip_strength only matter if model/clip are actually wired in. If you're just here to bake a .safetensors and load it later with a normal LoraLoader, leave those inputs disconnected and ignore the sliders.

CategoryWAS Suite/LoRA

Inputs (86)

NameTypeDefaultDescription
output_filenameSTRINGmerged_lora.safetensorsName to save the merged LoRA under, inside your LoRA folder. Sub-folders are allowed, for example 'styles/mixed.safetensors'. '.safetensors' is added when it is missing, and the name cannot step outside the LoRA folder.
output_model_strengthFLOAT1.000–100How strongly the merged LoRA is applied to the model output, if a model is connected. 1.0 is full strength, 0.5 is half. This does not change the saved file, only what comes out of the model socket.
output_clip_strengthFLOAT1.000–100The same for the clip output: how strongly the merged LoRA is applied to the connected clip. Lower it when a LoRA's trigger words are overwhelming the rest of the prompt.
modeCOMBOsvdHow the LoRAs are combined. `svd` recompresses the combined result back to one rank, which keeps the file small and is the usual choice.
block_mix_recipeCOMBOconcept_a_style_bOnly read in `block-mix` mode: which of the two LoRAs each part of the model comes from. `all_a` and `all_b` route everything one way.
modeloptMODELOptional. A model to apply the merged LoRA to once it is saved, so the merge can be tested in the same run. Leave it unconnected to only write the file.
clipoptCLIPOptional, and only used when a model is connected too: the clip the merged LoRA's text-encoder half is applied to.
optionsoptWAS_LORA_MERGE_OPTIONSOptional settings from a Power LoRA Merger Options node: rank, precision, module filters and the per-mode controls. Leave it unconnected to merge at rank 32 in bf16.
lora_1_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_1optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_1_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_2_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_2optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_2_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_3_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_3optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_3_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_4_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_4optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_4_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_5_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_5optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_5_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_6_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_6optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_6_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_7_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_7optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_7_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_8_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_8optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_8_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_9_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_9optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_9_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_10_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_10optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_10_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_11_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_11optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_11_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_12_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_12optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_12_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_13_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_13optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_13_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_14_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_14optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_14_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_15_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_15optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_15_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_16_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_16optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_16_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_17_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_17optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_17_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_18_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_18optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_18_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_19_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_19optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_19_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_20_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_20optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_20_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_21_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_21optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_21_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_22_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_22optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_22_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_23_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_23optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_23_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_24_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_24optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_24_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_25_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_25optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_25_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.
lora_26_enabledoptBOOLEANtrueWhether this row takes part. `true` applies it; `false` mutes it and keeps the file and the strength it was set to, so neither is typed again to bring it back.
lora_26optCOMBONoneLoRA file this row uses. `None` leaves the row empty, which counts the same as switching it off.
lora_26_weightoptFLOAT1.00-10–10How strongly this row counts. 1.0 is the LoRA as it was trained, below 1 weakens it, above 1 pushes it past what it was trained for, and a negative value applies it in reverse. 0 leaves the row out.

Outputs (3)

NameTypeDescription
modelMODELThe connected model with the merged LoRA applied, or nothing when no model was connected.
clipCLIPThe connected clip with the merged LoRA applied, or nothing when no model and clip were connected.
lora_pathSTRINGThe saved file's name relative to your LoRA folder, such as 'styles/mixed.safetensors'. Feed it to a loader, or to a text node to record what a run produced.