Lora Epoch Sweeper
Press Queue once, get an image from every one of your LoRA's training checkpoints
- model
- clip
- model
- clip
- step_label
- lora_name
You just finished a LoRA training run and you have 32 checkpoints sitting in a folder. Which one is actually good? The answer is almost never "the last one" - the community has said for years that you should save intermediate epochs because the final checkpoint is frequently overtrained (that advice predates the Flux era and survives it untouched). The only honest way to find your best step is to look at all of them, side by side, same seed, same prompt.
The Lora Epoch Sweeper is the node that makes that look trivial. It points at your training output folder, you tell it which step range to sweep, and it emits a list of patched models. Because ComfyUI runs everything downstream of a list once per item, one press of Queue gives you one image per checkpoint. No hand-editing the LoRA loader twenty times, no giant manually-built workflow. For anyone who trains their own LoRAs (ai-toolkit or kohya output, e.g. my_lora_000002000.safetensors), this is one of those "how did I live without this" nodes. If you only download finished LoRAs, skip it - there's nothing here for you.
How it works
Under the hood it's honest plumbing: the node scans the folder, parses the trailing _<digits> step from each filename, groups files by LoRA name, filters by your step range, then patches the base model once per selected checkpoint using ComfyUI's own load_lora_for_models. The interesting part is the step_interval thinning: it targets the round multiples of your interval inside the range and snaps each to the nearest available checkpoint. With epochs saved every 250 steps, step_interval = 500 gives you 500, 1000, 1500… exactly the grid you actually want, instead of a wobbly sample.
The source has zero dependencies - no requirements, no model files, no API. It's a single small Python file, MIT licensed.
The inputs that matter
You'll set a handful, and the defaults cover the rest:
model(required) - your base model.clipis optional; connect it and it gets patched too.folder_path- the epoch folder.~expands; a bare subfolder name also works if it lives inside your loras dir.start_step/end_step- the inclusive range of training steps.end_step = 0means no upper limit.step_interval- thin the range to ~one checkpoint per this many steps.0keeps every epoch.lora_basename- when a folder holds several training runs, pick one by substring; leave empty to auto-pick the LoRA with the most epochs.include_base,max_count,strength_model/strength_clip- include the suffix-less final file, cap the sweep, and set the usual LoRA strengths.
Outputs are all lists: model, clip, step_label (the step number as a string, "final" for the base file), and lora_name. Wire model to your KSampler, clip to your text encoders.
Install
Clone into custom_nodes and restart ComfyUI:
cd ComfyUI/custom_nodes
git clone https://github.com/ethanfel/ComfyUI-Lora-Sweeper
ComfyUI Manager can find it as ComfyUI-Lora-Sweeper too. No extra steps, no dependencies to fight.
Where people get burned
The filename convention is the whole contract: the node only parses steps from a _<at least 4 digits>.safetensors suffix. If your trainer saves epochs as _step2000 or without the underscore, every file gets treated as the "final" checkpoint and your sweep collapses into one image. kohya and ai-toolkit both output the expected format, so you're fine unless you renamed things.
Two more gotchas worth knowing. First, each epoch is a separate patched model held in memory, so a 40-epoch sweep eats RAM - that's what step_interval and max_count are for. Second, the node prints what it's sweeping to the console ([LoraEpochSweeper] sweeping N epochs of 'my_lora'), which is genuinely useful when it picks the wrong LoRA from a mixed folder - the error message lists the ambiguous matches.
One last trick that makes it complete: right-click SaveImage → Convert filename_prefix to input, and wire step_label (or lora_name) into it. Now every saved file is named after the epoch that made it, and comparing a grid of 16 images is actually a pleasant experience instead of a guessing game.
Inputs (11)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | Base diffusion model to apply each epoch to. | |
| folder_path | STRING | Absolute path to a folder of LoRA epochs (or a sub-folder name inside the loras dir). | |
| lora_basename | STRING | Which LoRA in the folder to sweep (substring of its name). Leave empty to auto-pick the LoRA with the most epochs. | |
| start_step | INT | 00–18446744073709550000 | Lowest training step to include (inclusive). |
| end_step | INT | 00–18446744073709550000 | Highest training step to include. 0 = no upper limit. |
| step_interval | INT | 00–18446744073709550000 | Thin the range to ~one checkpoint per this many training steps (e.g. 500). 0 = keep every epoch. |
| strength_model | FLOAT | 1.00-100–100 | — |
| strength_clip | FLOAT | 1.00-100–100 | — |
| clipopt | CLIP | Optional CLIP/text-encoder to also patch. | |
| include_baseopt | BOOLEAN | true | Also include the final, suffix-less .safetensors. |
| max_countopt | INT | 00–1000 | Cap the number of epochs swept. 0 = unlimited. |
Outputs (4)
| Name | Type | Description |
|---|---|---|
| model | MODEL | List of models, one per selected epoch. |
| clip | CLIP | List of CLIPs, one per selected epoch (passthrough if no clip given). |
| step_label | STRING | Training step of each epoch as a string ('final' for the suffix-less file). |
| lora_name | STRING | Filename (without extension) of each epoch. |