flow_stage_odd_even_hold
Two pipelines alternating on one wire
- stage_info
- odd_data_1
- even_data_2
- data_1
- data_2
The Apt_Preset flow_stage family turns a ComfyUI workflow into a loop. flow_stage_begin opens a run of N numbered stages, the graph executes once per stage, and flow_stage_end re-queues the whole prompt for the next one - carrying state and intermediate tensors on disk between passes, under output/.apt_stage_bridge/<run_id>/. Most stage nodes answer the question "does this pass run?" with yes or no.
flow_stage_odd_even_hold answers a different one: which of these two data paths is live this pass, and which one is just riding along? If your pipeline isn't one thing repeated but two things alternated - generate then refine, A-frame then B-frame, base pass then detail pass - this is the node that keeps both channels straight while the same workflow runs over and over.
How it works
The node branches on the stage index inside the stage_info it's handed. Stage numbering is 1-based in the UI, 0-based in the code, and the parity is what decides:
- Even internal index (stages 1, 3, 5 …):
odd_data_1is the fresh input. It comes out ofdata_1tagged as new, anddata_2echoes whatever the incomingstage_infowas already carrying for its second channel - or nothing at all, if there wasn't any. - Odd internal index (stages 2, 4, 6 …):
even_data_2is the fresh input, it goes out asdata_2, anddata_1is passed forward from the incomingstage_info- the hold.
The second half of the trick is laziness. Both optional inputs are declared lazy, and the node's check_lazy_status only requests the one the current stage needs. So on stage 1, the entire branch feeding even_data_2 is never evaluated. That's not a cosmetic optimization - it means your two alternating branches can be genuinely expensive (a different sampler, a different model, a ControlNet stack) and only the active one costs you anything.
Inputs and outputs that matter
stage_info (FLOW_STAGE_INFO, required) comes from flow_stage_begin or flow_stage_unpack and is the only thing you must set up right. odd_data_1 and even_data_2 are wildcard inputs - a LATENT, an IMAGE, whatever both branches produce. Keep them the same kind of thing.
Two wildcard outputs, data_1 and data_2, wire into flow_stage_end's matching inputs, or into one of the flow_stage_collect_* / flow_stage_unpack nodes if you want to fan them out mid-stage. flow_stage_unpack gives you the per-stage stage_data_1 / stage_data_2 back out, plus is_first / is_last flags that are handy for "only on the last pass" cleanup.
Install
ComfyUI Manager → search Apt_Preset → install, then restart. Manually:
cd ComfyUI/custom_nodes
git clone https://github.com/cardenluo/ComfyUI-Apt_Preset.git
On Windows the README's route is install.bat (the repo ships install-ComfyUI-Portable.bat and install-ComfyUI-aki.bat). On Linux or macOS those don't help you - run the requirements by hand:
cd ComfyUI/custom_nodes/ComfyUI-Apt_Preset
pip install -r requirements.txt # or python_embeded/python.exe -m pip install -r requirements.txt
The dependency list is not small: opencv-python-headless, scipy, matplotlib, openpyxl, soundfile, scenedetect, plus optional gguf, onnxruntime and transparent-background. The pack is a large toolbox, and the number one way people break it is a half-finished dependency install - the symptom is IMPORT FAILED next to custom_nodes/ComfyUI-Apt_Preset in the startup log and every node in the pack showing red. If that happens, re-run the requirements and read the pip error; it's almost never the stage nodes' fault.
Also worth knowing before you get attached to the pack's broader feature set: animated ControlNet scheduling wants ComfyUI-Advanced-ControlNet, GGUF loading wants ComfyUI-GGUF, and some mask nodes want the extra model folder described in the README (ComfyUI/models/Apt_File). None of those are needed for the stage loop itself.
Where people get burned
The node throws precise errors, which is rare and welcome:
odd stage requires odd_data_1/even stage requires even_data_2- the branch for the current parity isn't wired. Easy to hit when you test with one channel first and forget to connect the other.even stage is missing the previous data_1- you're on an even stage but the incomingstage_infohas nostage_data_1for it to hold. Usually that means the odd stage didn't hand one forward, or you jumped straight to stage 2 fromflow_stage_beginby setting the stage index manually. Even stages need their odd partner to have run.- Its
VALIDATE_INPUTSreturnsTrue, so ComfyUI does no type checking on either channel: feeddata_1an IMAGE on one branch and a LATENT on the other and nothing objects until something three nodes downstream explodes. Both channels are supposed to be the same shape of thing.
One last thing about the holds: a value forwarded by hold is the tensor that was already on disk, not a cached node output. The skipped branch genuinely didn't execute, so there's nothing in the cache to reuse - which is exactly why the next pass is fast.
Inputs (3)
| Name | Type | Default | Description |
|---|---|---|---|
| stage_info | FLOW_STAGE_INFO | — | |
| odd_data_1opt | * | — | |
| even_data_2opt | * | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| data_1 | * | — |
| data_2 | * | — |