MAI Concept Capture Arm (alpha)
Tap a model's insides and see what a reference actually does
- model
- model
- capture
Before you read another word: this is a researcher's node, not a workflow node. It won't make your renders better. It exists to answer one question about MiniMax-H3 - what a piece of conditioning (a reference image, say) actually does to the model's internal block states - so that the answer can be measured, stored, and maybe compiled back into conditioning later.
That's the bet Concept Lab is making: a reusable concept doesn't have to live in trained weights. It can live in a measured functional delta. This node is the measuring instrument.
How it works
MAI Concept Capture Arm installs a tap on a clone of your model and hands the clone on. Your sampler downstream renders exactly as it would have - bit-identical, that's the whole discipline - while selected DiT block states get written to the concept space. Tensors go to disk as it captures rather than accumulating in VRAM.
One node is one arm of one comparison. The arm enum is the experiment design: R is the actual reference, N is a shape-matched control (same layout, same presentation tokens, different content), 0 is no reference, free is uncontrolled. That R-versus-N distinction is the clever part: adding a reference changes the packed layout, the presentation tokens, and the time origin of the target stream all at once, so "reference vs no reference" is a muddle. "Reference vs a shape-matched control" holds all three fixed and leaves only the content. That's the delta worth measuring.
The inputs that matter
model- in, a patched clone out; the input is untouchedseed- must equal the sampler's noise seed. The node can't read it (the seed lives in the sampler, not the model), so you copy it by hand, and a mismatch silently mislabels the capture - the tap now refuses on mismatchblocks- which DiT blocks to tap, default0,5,10,20,30,40,49. An experiment config, not a format factstep_quantiles- schedule-relative positions, default0.15,0.5,0.85store- what to keep:stats,frame_mean,sketchby default, orfullfor the whole state, which costs accordinglystack_label- the checkpoint name, typed by you, because a ModelPatcher doesn't carry the UNET filename and the node won't guess oneenabled=False- the pass-through control: tap installed, records nothing. That's how you prove the instrumentation is free
Outputs are model (the patched clone) and capture, a CONCEPT_CAPTURE you feed to MAI Concept Capture Flush.
The catch that will bite you
All arms of a comparison have to run in one ComfyUI process. This pipeline is bit-identical within a launch and only close across launches, so restarting between arms makes a new replicate rather than the same condition (the pack cites DEC-CL-0015). If you're building a real comparison, queue all arms before touching anything else. It's alpha from 2026-08-15, loads behind the pack's guarded loader, and ships in ComfyUI-MAINodes with no Python deps of its own:
cd ComfyUI/custom_nodes
git clone https://github.com/matlowai/ComfyUI-MAINodes
Restart, and remember it targets H3 - whose weights are licence-locked out of the US, EU, UK and Korea, so the whole exercise assumes you're somewhere the model is legal to run.
Inputs (20)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | the model this arm renders with; a patched CLONE comes out, the input is untouched | |
| arm | COMBO | R | R actual reference / N shape-matched control / 0 no reference / free (uncontrolled) |
| variant_name | STRING | a label; identity comes from what the model actually saw (text context, reference latents), measured by the tap | |
| seed | INT | 00–18446744073709550000 | MUST equal the sampler's noise seed. This node cannot read it (the seed lives in the sampler, not in the model), so it is copied by hand and a mismatch silently mislabels the capture; the tap now refuses on mismatch |
| replicate | INT | 00–999 | which repeat of this condition, inside this launch |
| blocks | STRING | 0,5,10,20,30,40,49 | DiT block indices to tap. An experiment config, not a format fact |
| step_quantiles | STRING | 0.15,0.5,0.85 | schedule-relative positions; 0 is the first step, 1 the last |
| steps | STRING | explicit step indices instead; giving both this and step_quantiles is refused | |
| segments | STRING | video,ref_img | packed-layout segment names: text, cond, cond_audio, ref_img, ref_audio, audio, video |
| store | STRING | stats,frame_mean,sketch | comma-joined subset of stats, frame_mean, sketch, full. 'full' is the whole state and costs accordingly |
| sketch_dim | INT | 2568–4096 | width of the seeded random projection |
| sketch_seed | INT | 00–4294967295 | recorded in the manifest; an unrecorded projection is an unreproducible capture |
| stack_label | STRING | the checkpoint name, from you. A ModelPatcher does not carry the UNET filename and this node will not guess one | |
| sampler_labelopt | STRING | sampler/scheduler/shift as you ran them; it joins the capture's identity, so two arms must agree | |
| corpus_row_idopt | STRING | the corpus row this arm renders, if there is one | |
| enabledopt | BOOLEAN | true | False installs the tap and records NOTHING: the pass-through control |
| cond_onlyopt | BOOLEAN | true | skip the unconditional forward; CFG makes two forwards per step and they are two different conditions |
| index_rootopt | STRING | manifest root; empty = the space default (H3_CONCEPT_INDEX_DIR) | |
| tensor_rootopt | STRING | tensor root; empty = the space default (H3_CONCEPT_TENSOR_DIR) | |
| notesopt | STRING | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| model | MODEL | — |
| capture | CONCEPT_CAPTURE | — |