Nodes/Helm/Helm Lab · Probe every block
ComfyUI Node

Helm Lab · Probe every block

Read all fifty blocks before you pick one

By U-N-B-R-A-N-D-E-D·Created 25 days ago·Updated 10 days ago· 7
Helm Lab · Probe every block
  • model
  • model
◄tagprobe_pos►
◄cond_onlyfalse►

Helm Collect reads one block. Which one? On a model nobody has calibrated, you have no honest way to answer that, so you guess, and twenty minutes later you discover block 8 was a dead end.

Helm Probe reads every transformer block at once, in a single pass, and writes one file containing all of them. Fifty rows on MiniMax H3, thirty on Z-Image. One queue each for your two contrast prompts and you have a profile of where in the network they differ. The pack calls it model onboarding, which is the right frame: this is the node you reach for before you've decided anything.

What it does, mechanically

It's a MODEL → MODEL node like the others, so it clips in between loader and sampler. Where Helm Collect hooks one block index, Helm Probe fires on every block in the list - discovered from the model itself (blocks, layers, transformer_blocks, and friends; first list with more than four entries wins), and that count becomes the file's row count.

Each block's output is reduced to a mean vector over sequence positions, accumulated across sampling steps, then divided by the step count. The result is a [n_blocks, dim] float32 tensor - roughly [50, 5376] for H3 - with kind: probe, n_blocks, dim, model and module in the header, in ComfyUI/user/steer/<tag>.safetensors.

The hook returns None. It's strictly read-only, so a probe run produces the same image it would have without the probe - safe to leave inline while you experiment, though it writes a file every run.

The inputs

Three, and only one is a judgement call.

  • model - the patcher.
  • tag - the output filename. Run the node twice: once with a pos-ish tag, once with a neg-ish tag, same model, only the prompt changing. Its default (probe_pos) lines up with the report node's, so leaving it alone is the path of least resistance.
  • cond_only - false preserves the published measurement; true averages only conditional batch items, which the tooltip recommends for CFG models. Same trap as the rest of the pack: the shipped axes weren't measured with it on. Switch it on for both passes or neither.

Output: a model socket. Wire it into the sampler. Yes, you have to actually sample - the file is written when the last block fires on the final step, so a probe graph with nothing downstream writes nothing.

Where it sits in the workflow

UNETLoader → HelmProbe(tag=probe_pos) → KSampler    # prompt A
UNETLoader → HelmProbe(tag=probe_neg) → KSampler    # prompt B, everything else identical
                              ↓
              HelmProbeReport(pos_tag=probe_pos, neg_tag=probe_neg) → peak_block

Two queue runs, then the report. The contrast pair is the only thing worth thinking about, and it's the collecting rule again: hold everything but one property fixed. "Corridor packed with glass server racks" versus "empty corridor, same camera, same light" gives the report something to rank; "a cat" versus "a spaceship" gives it two unrelated scenes, which is technically a separation and practically useless.

Don't feed probe files into Helm Axis. A probe file is [n_blocks, dim], an axis is [dim] - different objects in the same directory, which is exactly why the pack defaults to a probe_ prefix. Keep it and you'll never mix them up.

Install

ComfyUI Manager → search Helm → install → restart. Or:

cd ComfyUI/custom_nodes
git clone https://github.com/U-N-B-R-A-N-D-E-D/comfyui-helm.git comfyui-helm

Dependencies are safetensors and numpy on Python 3.10+ - nothing to compile, nothing to fight your transformers version. Z-Image/Lumina2 with core nodes only is the affordable way to see this work; the H3 path wants a ~21 GB int8 UNet and a Qwen3-VL 32B encoder, which isn't a 24 GB conversation. Custom-node pack, so local or self-hosted only - not Comfy Cloud.

Where people get burned

It probed a different number of blocks than expected. The console prints [helm] probing all <n> blocks under tag '<tag>' - that's your ground truth. If n looks wrong, calibration matched the wrong module, or the model exposes several block lists and Helm took the first with more than four entries.

The two files don't match. If the model, resolution or block count changed between the two passes, the report node will refuse them with a shape error. That's the pack being honest rather than averaging two incompatible profiles.

The top block found isn't the block to steer at. This is the important one, and the pack's own measurements say so: on Z-Image the probe's separation peak sat at block 0 (global) and block 7 within the middle band, while the band that actually verified by rendering was 9–21. High separation tells you the two prompts differ there. It does not tell you that steering there does anything good. Read HelmProbeReport as a map of where information changes, then sweep by hand.

You wanted one run and one answer. Not happening: the probe needs both halves of the contrast, so two prompts, two queues. What you save is the block dimension.

CategoryHelm/Lab

Inputs (3)

NameTypeDefaultDescription
modelMODELThe model to probe. The model passes through unchanged.
tagSTRINGprobe_posfile to write. Run this node TWICE against the same model — once with a `pos` tag, once with a `neg` tag — then feed both to HelmProbeReport.
cond_onlyBOOLEANfalseDefault False preserves the published result. True averages only cond batch items (recommended for CFG models).

Outputs (1)

NameTypeDescription
modelMODEL—