Nodes/ComfyUI-RyuuNoodles/Extract and Save Lora 🐲
ComfyUI Node

Extract and Save Lora 🐲

The built-in Extract LoRA node, except you get to drop the junk text encoder

By DraconicDragonΒ·Created about a year agoΒ·Updated 2 months agoΒ· 12
Extract and Save Lora 🐲
  • model_diff
  • text_encoder_diff
    β—„filename_prefixloras/ComfyUI_extracted_loraβ–Ί
    β—„filename_suffix{counter:05}β–Ί
    β—„rank16β–Ί
    β—„lora_typeβ–Ύβ–Ί
    β—„bias_difftrueβ–Ί
    β—„skip_on_any_diff_zerofalseβ–Ί
    β—„skip_on_proj_diff_zerofalseβ–Ί
    β—„embed_workflowtrueβ–Ί

    ComfyUI already ships an "Extract and Save LoRA" node. So why would you install a personal node pack for another one? Because the built-in version always saves the text encoder weights, and the text encoder is usually where the useless half of an extracted LoRA lives. This one lets you look at the diff first, skip the TE when it's empty, and walk away with a file whose name tells you exactly what's in it.

    What it actually does

    You know the workflow: merge two models with ModelMergeSimple, subtract the base checkpoint from the merge with ModelSubtract to get a model_diff, and CLIPSubtract for the text_encoder_diff. Feed those diffs into this node and it decomposes them into LoRA weights - a low-rank approximation of the difference, which is how you turn "merge of two checkpoints" into a portable 20-200 MB file instead of a 7 GB checkpoint. That's the standard merge-to-LoRA trick, and it's the same thing the core node does. The deltas are the point: LoRA extraction (taking the diff between base and fine-tune and compressing it) is a middle ground for distribution, per the lora-training docs.

    The math under the hood is an SVD per weight tensor: lora_up and lora_down matrices at your chosen rank, clamped to the 99th quantile to stop outliers from wrecking the file. Nothing exotic, but it's done in half() precision, so the output is small.

    What's different from core:

    • skip_on_any_diff_zero - if any text encoder weight diff is exactly zero, the TE weights are left out entirely.
    • skip_on_proj_diff_zero - same, but only when the *.transformer.text_projection.weight key is zero. That's the projection that SDXL-style models use, and it's a common source of "why is my extract bloated/garbage."
    • filename_suffix - you get to control the tail of the name, with {counter:05} zero-padded counters.
    • Self-documenting names: myextract_r40_TE_biasdiff_00001.safetensors - rank, whether TE is in, whether bias diffs are in. That alone beats the core node for tracking what you made.

    The inputs that matter

    • model_diff and text_encoder_diff - the two ModelSubtract/CLIPSubtract outputs. Both optional; you can extract just one.
    • rank - the LoRA dimension, default 16. The author's guidance: 16-64 depending on how much of the checkpoint difference you want to keep. Rank 32 is roughly a 217 MB LoRA if the TE is included.
    • lora_type - standard (the normal low-rank LoRA) or full_diff (stores the full weight difference, huge - don't do this by accident).
    • bias_diff - include .bias diffs. Minimal size cost, closer match to the original model. Leave it on.
    • filename_prefix / filename_suffix - prefix defaults to loras/..., so files land in ComfyUI/output/loras/.

    There are no outputs; it's a save node. Everything appears in your output folder.

    Installing it

    This is part of the RyuuNoodles pack, so the install is the pack install:

    cd ComfyUI/custom_nodes
    git clone https://github.com/DraconicDragon/ComfyUI-RyuuNoodles
    

    Restart ComfyUI. Or use ComfyUI Manager and search for "RyuuNoodles". There are no extra model downloads; the only real Python dependency in the pack is color-matcher (used by a different node).

    Where people get burned

    The author's own note is the honest one: "It's possible this node doesn't quite work for the TE saving... I've had unexpectedly mixed results for the same extracts." So treat the skip-TE logic as a convenience, not a guarantee - if you need a definitively clean TE exclusion, test the output on a known merge before relying on it. Also: if both diff inputs are left empty, the node silently does nothing. And since embed_workflow defaults to true, your workflow gets baked into the LoRA's metadata - nice for provenance, but that's why some extracted files carry the whole graph. Not a bug.

    CategoryRyuuNoodles 🐲/Utils

    Inputs (10)

    NameTypeDefaultDescription
    filename_prefixSTRINGloras/ComfyUI_extracted_loraβ€”
    filename_suffixSTRING{counter:05}Suffix to append to the filename. Will be appended after '_noTE', '_biasdiff', etc. Use {counter} for a counter, it supports zero padding (E.g.: test_{counter:05}_ = test_00001_).
    rankINT161–4096LoRA network dimension size (LoRA Rank). Prefer 16-64 depending on how much data you want to have saved in relation to the checkpoint differences.
    lora_typeCOMBOType of LoRA to extract. Standard is the default (like normal trained LoRA; rank 32 ~= 217mb LoRA assuming TE diff is extracted too), full_diff saves the full weight diff (very large).
    bias_diffBOOLEANtrueIf enabled, includes .bias weight differences in the extracted LoRA. This can help match the original model behavior more closely, with minimal impact on file size.
    skip_on_any_diff_zeroBOOLEANfalseIf True, skips saving the text encoder weights if any weight diff is zero.
    skip_on_proj_diff_zeroBOOLEANfalseIf True, skips saving the text encoder weights if 'text_projection.weight' diff is zero.
    embed_workflowBOOLEANtrueEnable or disable saving of workflow inside the LoRA metadata. Will not save metadata if it's disabled in ComfyUI globally.
    model_diffoptMODELThe ModelSubtract output to be converted to a lora.
    text_encoder_diffoptCLIPThe CLIPSubtract output to be converted to a lora.

    Outputs (0)

    No outputs