Extract attention data
The reference pass that fills CharaConsist's attention cache
- model
- fg_condition_mask
- MODEL
- ATTN
ExtractAttn is the front half of the pack's core trick, and it's the node that makes the whole thing memory-hungry. The idea behind Comfyui_CharaConsist is "training-free character consistency": instead of training a LoRA or piping a reference through an IP-Adapter, you run your reference image through the model once and quietly record what the attention layers actually did. This node is the recording device. You chain it before a KSampler that draws the reference, and it hands you back an ATTN payload - the saved attention tensors - that GenConsistent will later replay into a fresh generation so the new image can literally attend to the same subject.
Think of it as a two-pass pipeline. Pass one: render the subject once, ExtractAttn snapshots the attention keys, values, and hidden states for the region you care about. Pass two: GenConsistent re-injects those snapshots into a new render of the same character in a new pose or scene. There's no fine-tuning anywhere. It's the same family of trick as attention caching / KV-reuse tricks you've seen in video and editing tools, ported to character identity.
How it works
ExtractAttn patches the model's attention path via a prepare_callback, then per denoising step, per layer, it stores the key/value tensors and the attention output - on CPU, which is why the README is blunt about RAM. You're caching the model's own internals for every step in range; a subject's attention cache runs to tens of gigabytes of system RAM. That's not a bug, it's the feature.
The step inputs decide what gets saved when:
kv_start_step/kv_end_step- the sampling step window where keys and values are recorded. Those are what GenConsistent re-injects.x_start_step/x_end_step- where hidden states (x) are saved, for cross-similarity matching and blending.mask_start_step/mask_end_step- where it accumulates foreground vs. background attention weights to derive a per-step foreground mask.
All three default to windows ending at step 9, so with a typical 10-step Chroma or Flux render, the last couple of steps get the mask, and steps 1–9 get KV and x. If you set ranges that never overlap an actual sampled step, you get an ATTN object that's mostly empty and every downstream node quietly does nothing - the pack won't tell you.
Inputs and outputs that matter
Required: model (the MODEL you're about to sample with), plus the three step-range pairs. Optional: fg_condition_mask - a MASK over the prompt tokens marking which tokens describe your subject. Give it one and the node stores a per-step fg_mask inside the ATTN dict, which is what PreviewSubjectMask displays and what GetCrossSim uses to restrict matching to the subject region. Skip it and you save RAM but lose the foreground mask entirely.
Outputs are MODEL (the patched model - feed it to your KSampler) and ATTN (the cache - wire it into GetCrossSim for matching, and ultimately GenConsistent). ATTN is a custom type that only exists inside this pack, so the shipped example workflows won't load without it installed.
Install
Same story as every node in this pack: ComfyUI Manager → search "Comfyui_CharaConsist", install, restart. Or by hand:
cd ComfyUI/custom_nodes
git clone https://github.com/thatname/Comfyui_CharaConsist.git
Restart, and the nodes appear under the chara_consist category. There's no requirements.txt - the pack uses only torch/einops, already ComfyUI's own dependencies. The catch is the model: it hooks Flux, Chroma and Qwen-Image attention paths, so it's a DiT-family tool, not something that runs on SD 1.5 or SDXL. The example chroma_chara_consist.json uses Chroma1-HD; the qwen_chara_consist.json uses Qwen-Image with a LoRA and CFG normalization.
Common issues
The dominant failure is the memory one: the README recommends 32GB of system RAM for a single subject and 64GB+ for multiples, because the cache lands in RAM, not VRAM. Don't be surprised when your 16GB laptop starts swapping. Second: no visible effect usually means the step ranges never hit, or the fg_condition_mask is missing so the downstream matching has nothing to anchor to. And because this pack is genuinely obscure - it's an academic-adjacent implementation of the CharaConsist paper, and there's basically zero community chatter about it - you'll be debugging from the console prints the node emits. Those prints are your best friend. It's a fiddly, RAM-hungry, rewarding tool. Expect to earn the first working run.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| model | MODEL | — | |
| kv_start_step | INT | 1 | — |
| kv_end_step | INT | 9 | — |
| x_start_step | INT | 1 | — |
| x_end_step | INT | 9 | — |
| mask_start_step | INT | 8 | — |
| mask_end_step | INT | 9 | — |
| fg_condition_maskopt | MASK | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| MODEL | MODEL | — |
| ATTN | ATTN | — |