CWB Embedding Multi-Merge
Merge 2 to 8 embeddings into one file without a training run
- cwb_config
- output_filename
- documentation
- cwb_report
Ask the old-timers how to combine two embeddings and they'll tell you not to. "You don't merge embeddings, just use both in the prompt." That's been the correct answer for years, and it's still the correct answer for 99% of cases. The CWB Embedding Multi-Merge is the experimental corner case that finally does the thing nobody built: it actually blends 2 to 8 embedding files into one new .pt, then cleans up the redundant rows that blending creates.
What it is
Embeddings (textual inversions, if you remember that term) are just a handful of learned token vectors plus a trigger word - 10 to 80 KB, no model weights involved. This node takes up to eight of them, aligns them row by row, and blends each aligned group into one vector using Consensus-Weighted Blending (CWB, the algorithm this whole pack is built around). Then, because a merge tends to leave near-duplicate rows behind, it runs a "coalesce" pass that folds similar vectors together. The result is one embedding file you can drop into ComfyUI/models/embeddings and trigger with a single token, instead of maintaining a prompt stack.
Why bother? Mostly portability and cleanliness: one file to share, one file to ship in a workflow, and one file that keeps the useful concept signal from several embeddings without you babysitting prompt weights. This is a deep-end tool, and it shows - the node is marked experimental and has essentially zero community footprint yet. Treat it as "nice, it exists" rather than a daily driver.
How the merge works
For every aligned group of token vectors across your inputs, CWB does the same thing:
- Compute an element-wise mean or median of the group (the "consensus").
- Measure each source vector's cosine similarity to that consensus.
- Weight each contributor by that similarity, so vectors that agree with the consensus dominate the blend.
That's the whole trick - it's a weighted average where the weights come from how well each source matches the group's shared direction, not from a manual strength slider. The default balanced_sim_mean preset uses similarity-based row alignment, mean consensus, and a touch of position weighting, which is a sane starting point.
The coalesce stage is separate and purely local: it repeatedly finds pairs of rows that are mutual nearest neighbors (each is the other's closest match), requires that pair to be both similar enough and close enough in row position, and merges it into one row. Rinse and repeat until you hit your target row count.
Inputs that matter
- embedding_count - how many of the eight input slots to use. If it's set to 4 but one slot is "None", it errors rather than silently merging garbage.
- embedding_1 … embedding_8 - the contributors. Embedding 1 is the anchor: it supplies the output metadata and the reference for shared shape and naming.
- cwb_preset - the blend recipe.
balanced_sim_mean(default) is the safe one; thefocused_*variants get stricter about which rows match. - target_vector_count - the row count you want after coalescing. 0 means "coalesce every eligible pair"; otherwise it stops at this count.
- coalesce_similarity_threshold (0.95) and coalesce_position_window (0.1) - how similar two rows must be, and how close in position, before they're merged. Lower the threshold to shrink harder.
- vision_boundary_embeddings - leave this off for ordinary textual inversions. Turn it on only if your embedding is a complete encoded visual block where the start/end rows are structural and must be preserved.
- output_filename - no extension; the file lands in the embeddings folder.
The node outputs output_filename (the file it wrote), documentation (the full CWB manual, handy), and cwb_report (a text summary of what got merged and coalesced).
Installing it
Grab the whole toolkit, since these nodes ship inside it:
cd ComfyUI/custom_nodes
git clone https://github.com/silveroxides/ComfyUI-ModelUtils
# restart ComfyUI
Or use ComfyUI Manager and search "Model Utility Toolkit" - that's the pack's display name. Installer-wise it pulls requests, Pillow, mutagen, unifiedefficientloader (the streaming library behind lazy_load), and av. No model files to download; everything it touches is already in your embeddings folder.
Where people get burned
- Embeddings are encoder-locked. SD 1.5, SDXL, and Pony each have their own CLIP variant, and blending across them produces a vector that means nothing in either space. Only merge embeddings trained for the same encoder - the KB's embedding research is blunt about this.
- Merging into a bigger mess. The coalesce pass is what makes a multi-merge usable; skip it (or set a huge target count) and you'll just get a longer, bloatier embedding.
- It writes files. Set
execution_modetoDOCUMENTATION ONLYfirst if you just want to read the docs without generating anything.
If the merge silently produces nonsense, your inputs are almost certainly from different encoders. Check that before you suspect the node.
Inputs (25)
| Name | Type | Default | Description |
|---|---|---|---|
| embedding_count | COMBO | 2 | Number of consecutive embedding inputs to merge. |
| embedding_1 | COMBO | Primary embedding and normal visual-boundary anchor. | |
| embedding_2 | COMBO | Second equal-prior embedding contributor. | |
| embedding_3 | COMBO | None | Optional embedding contributor. |
| embedding_4 | COMBO | None | Optional embedding contributor. |
| embedding_5 | COMBO | None | Optional embedding contributor. |
| embedding_6 | COMBO | None | Optional embedding contributor. |
| embedding_7 | COMBO | None | Optional embedding contributor. |
| embedding_8 | COMBO | None | Optional embedding contributor. |
| execution_mode | COMBO | MERGE | MERGE writes a new embedding. DOCUMENTATION ONLY opens no files. |
| cwb_preset | COMBO | balanced_sim_mean | Embedding CWB preset used for multi-input alignment and coalesced row pairs. |
| target_vector_count | INT | 00–100000 | Target body-row count. Zero coalesces every eligible pair; otherwise stops at this count or when no eligible pair remains. |
| coalesce_similarity_threshold | FLOAT | 0.950–1 | Minimum cosine similarity for a mutual-nearest body-row pair to coalesce. |
| coalesce_position_window | FLOAT | 0.100–1 | Maximum normalized row-position distance for an eligible pair. |
| vision_boundary_embeddings | BOOLEAN | false | Enable for complete encoded visual blocks. Start/end rows are validated and preserved; False treats the full tensor as open textual inversion. |
| legacy_boundary_search | BOOLEAN | false | Search and trim legacy template rows using Boundary Reference Embedding. Requires vision boundary mode and a selected reference. |
| boundary_reference_embedding | COMBO | None | Known-clean visual block used by legacy boundary search. Its first and last encoded rows define vision start/end. |
| boundary_similarity_threshold | FLOAT | 0.95-1–1 | Minimum encoded cosine similarity required independently for visual start and end validation. |
| output_filename | STRING | cwb_merged_coalesced_embedding | Filename without extension under ComfyUI's embeddings category. |
| save_dtype | COMBO | fp32 | Requested dtype for generated floating embedding tensors. |
| process_device | COMBO | cuda | Per-tensor FP32 CWB arithmetic device; CUDA OOM retries that tensor on CPU. |
| lazy_load | BOOLEAN | true | Use bounded UEL streaming and release each tensor after its work unit. |
| force_clear_cache | BOOLEAN | true | Clear Python and CUDA caches before each tensor at a possible speed cost. |
| override_dtype | BOOLEAN | false | Force generated floating tensors to Save Dtype. |
| cwb_configopt | CWB_CONFIG | Optional complete CWB configuration override. |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| output_filename | * | — |
| documentation | STRING | — |
| cwb_report | STRING | — |