Donut Extract LoRA (Raw → Patched)
Turn a patched model back into a portable LoRA
- raw_model
- patched_model
- saved_path
- report
LoRA extraction is the reverse-engineering trick that's been around since the SD 1.5 days: if you've got a base model and a patched version of it - a LoRA stack applied, a merge blended in, a fine-tune diff - you can capture exactly what changed as a small LoRA file instead of shipping a multi-gigabyte checkpoint. It's how you distribute a look, port a merge's style onto another base, or share a stack with someone who doesn't have your workflow. This node is that trick done right, with one important upgrade: it understands the Donut pack's own experimental bypass modes, which ordinary extractors can't even see.
Why it beats the built-in extractor
ComfyUI's stock approach subtracts state dicts, but Donut's "Experimental bypass" LoRAs live as forward hooks and the Krea2 merge bypass keeps model2 swaps as runtime source-model forwards - neither ever shows up in a state dict. Feed those into a normal subtraction and you extract nothing. DonutExtractLoRA recovers those adapters and pulls the model2 hard-swapped weights in before it factors anything, so the LoRA represents the model that actually ran. It's also polite about memory: it works one parameter at a time on CPU in float32, so you don't need the full model resident in VRAM, and it skips quantized models' bookkeeping keys like weight_scale instead of choking on them. Large matrices get a deterministic randomized low-rank SVD; small ones get an exact SVD.
The inputs
All five are required, and only the first two really need your attention:
- raw_model - the unmodified base, loaded before any LoRA or merge. This must be the same underlying checkpoint your patched side started from, not a lookalike.
- patched_model - the effective target after LoRAs, patches, or a Krea2 merge bypass. The classic setup is one CheckpointLoader feeding a DonutApplyLoRAStack (or merge) chain.
- rank - the SVD rank cap per matrix, default 32. Higher ranks preserve more merge detail at the cost of a bigger file; you rarely need to push far past 64 for a style.
- filename_prefix - defaults to
loras/Donut_extracted_lora, saving intoComfyUI/output/loras/. - dtype - just the storage precision for the extracted factors (fp16 default, bf16, or fp32); the math is always float32.
It outputs two strings, saved_path and report. You don't have to wire them anywhere - the report in the console tells you how many low-rank layers, exact diff layers, and skipped-zero layers you got, plus warnings if something didn't line up. That report is worth reading once, because it'll tell you if your raw and patched models don't actually share a base.
The important caveats
First, the file it writes uses alpha = rank, not the trainer convention of alpha at half rank, so applying it at strength 1.0 reconstructs the truncated delta exactly. Strength 1 is your natural starting point, not strength 0.7.
Second, be honest about what you hand it. If raw and patched don't share the same clone lineage (say you loaded the base twice from separate loaders), the node warns you it's capturing all model differences, not just attached LoRAs. That's a feature when you genuinely want base-vs-fine-tune; it's a bug when you accidentally diffed two different checkpoints and get a Frankenstein LoRA. Shape mismatches on individual layers are skipped with a console note, and if nothing extracts you get a clear error rather than a silent empty file.
And the standard LoRA rule applies: the result is architecture-bound. Extract it on SDXL and it patches SDXL checkpoints - it won't port to Flux or Z-Image.
Install
Same as the rest of the pack:
cd ComfyUI/custom_nodes
git clone https://github.com/DonutsDelivery/ComfyUI-DonutNodes.git donutnodes
cd donutnodes
python -m pip install -r requirements.txt
Or search "DonutNodes" in ComfyUI Manager, which handles the pip step automatically. Restart, and you'll find it under the model-merging category. Given extraction grinds through every weight on CPU, don't be alarmed when a big model takes a while - it's the price of not needing a second GPU.
Inputs (5)
| Name | Type | Default | Description |
|---|---|---|---|
| raw_model | MODEL | The unmodified/base diffusion model before LoRAs or merges are applied. | |
| patched_model | MODEL | The effective target model after LoRAs/patches/Krea2 merge bypasses. | |
| rank | INT | 321–4096 | Maximum SVD rank per matrix weight. Higher ranks preserve more model-merge detail and create larger LoRAs. |
| filename_prefix | STRING | loras/Donut_extracted_lora | — |
| dtype | COMBO | fp16 | Storage dtype for extracted factors/direct diffs. Computation is float32. |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| saved_path | STRING | — |
| report | STRING | — |