Extensions/ComfyUI-SD-Slicer
ComfyUI Extension

ComfyUI-SD-Slicer

A ComfyUI extension with 6 custom nodes.

By kuschanow·Created 14 days ago·Updated 13 days ago· 0
kuschanow/ComfyUI-SD-Slicer
Nodes6
On cloudLocal install
CategorySD-Slicer
Stars0
Updated13 days ago
Readme

ComfyUI-SD-Slicer

This custom node pack crossbreeds models of the same architecture, block by block.

Blend several models with individual weights per architecture block, multiply a block by a factor k, apply a standard formula (interpolate, add-difference, weighted subtraction) or write your own, then save the result as a full checkpoint or a raw .safetensors. It works not only with MODEL (UNet/DiT), but also with VAE and with any object exposing a state_dict (LoRA, ControlNet, plugin types) through a wildcard node.

Merging only makes sense between models of the same architecture — tensor shapes must match. SD-Slicer checks this before merging and reports what diverged.

Layer Merge

Crossbreed N MODELs block by block. This is the main node. It keeps ComfyUI's lazy path (the merge is expressed as ModelPatcher.add_patches), so inputs are not mutated and the output is a proper MODEL usable downstream.

Input:

  • model_1, model_2, … – dynamic list; a new port appears once the last one is connected

Fields:

  • per-block weights sN – weight of model_N for a given block
  • k – multiplier applied to the whole block
  • formula – optional custom formula (empty = weighted sum)

Output:

  • model

Usage:
sample

The node is an OUTPUT_NODE, so a workflow can run with just the loaders and this node. The per-block formula is W_out = k · ( w₁·W₁ + w₂·W₂ + … + w_N·W_N ).

Dynamic ports: connect a model to the last free input and the next one appears. Weights are bound to the port number, not its position, so disconnecting a middle input does not shift the others.

sample

Per-block weights

Run the workflow once (Queue Prompt), then press 🔄 Refresh block list: the node reads the model keys and shows every architecture block with its sN weight fields and a k multiplier. There is a filter box.

sample

A block id is the key prefix up to and including the first numeric component, so one node slices any architecture the same way:

  • input_blocks.1.0.in_layers.0.weightinput_blocks.1
  • joint_blocks.5.x_block.attn.qkv.weightjoint_blocks.5 (SD3 MMDiT)
  • decoder.up.2.block.0.norm1.weightdecoder.up.2 (VAE)
  • norm_out.linear.weight (no digits) → norm_out

Example — take structure from A and style/detail from B: set input_blocks.* to s1=1, s2=0 and output_blocks.* to s1=0, s2=1.

sample

Values without an explicit override fall back to the default row.

Formulas and presets

A formula can be applied on top of the blocks. The UI has a preset dropdown (filtered by the number of connected inputs) and a custom formula field.

sample

Variables are positional (1 = first connected input):

  • a, b, c, … and m1, m2, m3, … – source tensors
  • s1, s2, s3, … – per-block weights (the same sN fields shown in the weight editor; w1, w2, … still work as back-compat aliases)
  • k – block multiplier, n – number of sources
  • functions: lerp, clamp, abs, min, max, sign, sqrt

An empty formula falls back to the built-in weighted sum (works for any N).

| Preset | Min. inputs | Formula | |--------|:-----------:|---------| | Weighted sum (default) | 1 | (empty) | | Interpolate | 2 | lerp(a, b, s2) | | Weighted subtraction | 2 | a - s2*b | | Add difference | 3 | a + s2*(b - c) | | Triple weighted | 3 | k*(s1*a + s2*b + s3*c) |

Add difference (A + (B − C)) is available with ≥3 inputs. For 4+ models there are no established named methods — the list collapses to weighted sum and you write your own combination (ah, m1mN) in the formula field.

sample

Formulas are evaluated by a small AST evaluator with a whitelist (numbers, names, + - * / **, unary minus, and the functions above), not by eval. Attribute access, subscripting, calls to anything else, lambdas and conditionals are rejected at compile time, so a formula carried inside a shared workflow cannot run arbitrary code.

VAE Merge

Crossbreed N VAEs block by block; returns a usable VAE. Same per-block UI and formulas as Layer Merge.

Input:

  • vae_1, vae_2, … – dynamic list

Output:

  • vae

Usage:
sample

All inputs must share the VAE architecture (SD1.5/SDXL are 4-channel; the SD3 VAE is 16-channel and will not mix with them).

Merge (any)

Crossbreed the state_dict of N arbitrary objects (LoRA, ControlNet, upscale models, plugin types). Same per-block UI and formulas.

Input:

  • src_1, src_2, … – wildcard *

Output:

  • state_dict (wildcard *), meant to be written with Save (any)

Loadability of the result is the user's responsibility — merging makes sense between objects of the same base with matching shapes.

sample

Load LoRA (raw)

ComfyUI's stock LoRA loaders take a MODEL and return a MODEL — they apply the LoRA, so they can't feed one into Merge (any). SD Slicer — Load LoRA (raw) fills that gap: pick a file from your loras folder and it outputs the LoRA's raw state_dict on a wildcard output, without applying it to anything. Chain it as:

Load LoRA (raw) ┐
                ├─► Merge (any) ─► Save (any)  (destination: models/loras)
Load LoRA (raw) ┘

The block editor then lists the LoRA's own keys (lora_unet_…) instead of UNet blocks; the merged result loads back with the stock LoraLoader.

Save Model

Write a MODEL (optionally with CLIP/VAE/CLIP_VISION) as a full checkpoint using ComfyUI's native writer, so the output matches the built-in "Save Checkpoint".

Input:

  • model
  • clip, vae, clip_vision – optional; omit them to save the diffusion model alone

Fields:

  • filename_prefix
  • destinationoutput (safe, never touches the models list) or models/<category> (immediately visible to the matching loader)

Usage:
sample

Save (any)

Write the state_dict of any input (VAE, CLIP, LoRA/raw state_dict, plugin types, or the output of Merge (any)) to a .safetensors.

Input:

  • any – wildcard *

Fields:

  • filename_prefix
  • destination

Usage:
sample

Architecture primer

SD1.5, SDXL and SD3 are all still relevant. A quick note on what you will see in the block list of each.

  • SD 1.5 – UNet, 4-channel latent (512px), one CLIP (ViT-L/14). Blocks: input_blocks.0…11, middle_block.*, output_blocks.0…11, time_embed.*, out.*.
  • SDXL – larger UNet with the same naming plus label_emb.* (size/crop micro-conditioning), 4-channel latent (1024px), two text encoders (CLIP-L + OpenCLIP bigG/14). Incompatible with SD1.5 for merging (different UNet depth).
  • SD3 / SD3.5 – MMDiT instead of a UNet: joint_blocks.* plus x_embedder.*, t_embedder.*, y_embedder.*, context_embedder.*, final_layer.*, pos_embed. 16-channel latent, flow-matching, three text encoders (CLIP-L, CLIP-G, T5-XXL). Its VAE is 16-channel and does not mix with SD1.5/SDXL.

Flux and video models are sliced the same way (grouping by keys), as long as tensor shapes match.

Compatibility and memory

Before merging, the key sets and tensor shapes of all inputs are compared; on a mismatch you get a clear error listing missing/extra keys and an example shape difference.

sample

For MODEL with an empty formula, weights stay lazy (add_patches, like the built-in ModelMergeSimple). With a non-empty formula the diffusion weights are materialized (float32, then cast back) and applied as a replacement patch on a clone — the base model is never mutated, but peak memory is higher. Keep this in mind on 8 GB VRAM with SDXL/SD3.

Development

The ComfyUI-independent parts (merge math, block grouping, compatibility check, formula safety, and both code paths of the MODEL node via a fake ModelPatcher) are covered by offline tests — only torch is required:

python tests/test_offline.py     # or:  pytest tests/

Save Model / VAE Merge and the MODEL formula path touch version-sensitive ComfyUI glue (save_checkpoint, comfy.sd.VAE(sd=...), patch-tuple format) — verify those in a live ComfyUI.