Bundle (obvpm)
Eight wires become one, and the class-id clash that forced the rename
- in_1
- in_2
- in_3
- in_4
- in_5
- in_6
- in_7
- in_8
- in_9
- in_10
- in_11
- in_12
- in_13
- in_14
- in_15
- in_16
- out
The single best argument for bundling wires is the switch case, so start there. A Lazy Switch carries one value per branch. If the thing you want to route is "a model, its clip, a vae and a latent" - four wires - a single-socket switch can't help you, and you end up duplicating the whole switch once per wire and keeping four dropdowns in sync forever.
Bundle (obvpm) is the other half of that: it packs several values onto one wire (type OBVPM_BUNDLE) so one switch, one bundle, done. Unbundle on the other end unpacks it. This is the context-bus idea - the same one rgthree's Context, Efficiency's pipes and Easy-Use's pipe are built on - but deliberately dumber and more transparent: the bundle is a plain name/value mapping, there's no merge semantics, and the names come from your wiring rather than a schema someone else defined.
How the packing works
The node starts with one empty input and grows a fresh input as each one fills, so there's always exactly one spare socket. Unplug something in the middle and the gap closes. Up to 16 fields.
The field names are the interesting part: each name is derived from the far end of the wire - what the producing node calls that output. Plug a CheckpointLoader's MODEL in and the field is called model. You don't maintain a list, and the packing side and the unpacking side can't drift apart, because Unbundle traces the wire back to work out what's in there.
If you want control over the names, open the config dialog (the ⚙ button on the node, right-click → Configure…, or double-click). A rename sticks to the wire it was made on - it's keyed by the derived name, so it survives re-syncs and falls back to the wire's own name if the wire changes. The dialog also reorders fields, and reordering moves the wires with their fields: a wire that carried mask still carries mask wherever its pin lands.
The single output is out, the packed bundle. That's the whole node.
Why it matters for laziness
A bundle isn't just tidier wiring - it's what makes conditional branches affordable. Wire a Bundle onto each side of a Lazy Case Switch and the branches carry several values each. Because the Bundle sits on the branch, when that branch isn't selected the entire thing is skipped: bundle, and everything feeding it, exactly as if the values were wired directly. Laziness is preserved end to end, which is the property a naive "pack everything into a dict then always unpack it" node destroys.
Unbundling works through the same wire, including through a Lazy Case Switch, through subgraph boundaries, and through KJNodes Set/Get pairs. Nesting works too: a Bundle's output can be a field of another Bundle, at any depth.
The install, and the id clash you should know about
cd ComfyUI/custom_nodes
git clone https://github.com/chanon/comfyui-obvpm
…then restart. In ComfyUI Manager, search the pack title comfyui-obvpm. No extra Python dependencies are required; the pyproject.toml intentionally declares none.
The collision worth knowing: this node's class id used to be bare Bundle, which clashed with another pack's node of the same id. In 0.2.0 the author suffixed every id in the pack with (obvpm), so Bundle is now Bundle (obvpm). Old workflows are migrated on open by the pack's frontend script plus server-side node replacement - but if you have a workflow where the Bundle node still misbehaves, delete it and drop a fresh one in. That's the author's own instruction, and it's faster than debugging the migration.
Traps
- Rename before you ship. The tracing follows names, so naming is the contract: rename a field on the packing side and the Unbundle side follows the wire, but a field you rename for your own convenience is a field whose meaning changed for anyone downstream.
- Hiding fields on the Unbundle side is how you take a subset - and a field with connections can't be hidden, so unplug first.
- The README's own caution is real: "a Bundle sits on a branch" is the whole reason it's cheap. Bundle something off the branch, upstream of the switch, and you've just made the expensive half unconditional.
Inputs (17)
| Name | Type | Default | Description |
|---|---|---|---|
| names | STRING | Filled in from the connected wires. | |
| in_1opt | * | Value for name line 1. May be left unconnected; it then unpacks as None. | |
| in_2opt | * | Value for name line 2. May be left unconnected; it then unpacks as None. | |
| in_3opt | * | Value for name line 3. May be left unconnected; it then unpacks as None. | |
| in_4opt | * | Value for name line 4. May be left unconnected; it then unpacks as None. | |
| in_5opt | * | Value for name line 5. May be left unconnected; it then unpacks as None. | |
| in_6opt | * | Value for name line 6. May be left unconnected; it then unpacks as None. | |
| in_7opt | * | Value for name line 7. May be left unconnected; it then unpacks as None. | |
| in_8opt | * | Value for name line 8. May be left unconnected; it then unpacks as None. | |
| in_9opt | * | Value for name line 9. May be left unconnected; it then unpacks as None. | |
| in_10opt | * | Value for name line 10. May be left unconnected; it then unpacks as None. | |
| in_11opt | * | Value for name line 11. May be left unconnected; it then unpacks as None. | |
| in_12opt | * | Value for name line 12. May be left unconnected; it then unpacks as None. | |
| in_13opt | * | Value for name line 13. May be left unconnected; it then unpacks as None. | |
| in_14opt | * | Value for name line 14. May be left unconnected; it then unpacks as None. | |
| in_15opt | * | Value for name line 15. May be left unconnected; it then unpacks as None. | |
| in_16opt | * | Value for name line 16. May be left unconnected; it then unpacks as None. |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| out | OBVPM_BUNDLE | The packed fields as one value. |