Extract Klein9 RefMod (FLUX.2)
Turn a Pile of Reference Photos Into One Reusable Latent File
- images
- vae
- klein9_refmods
- latent
- saved_path
This node does the one-time work: reference images plus a VAE in, a .safetensors file written to models/refmods-klein9/ out. From then on that character, outfit, or style is a dropdown entry instead of a folder of JPEGs you re-encode every run.
If you've trained a LoRA, the shape of the job will feel familiar, and that's not a coincidence - the author is a CivitAI LoRA-training person, and the dataset-prep tooling linked from the README is theirs too. A RefMod is the cheap cousin: no training run, no twenty minutes of GPU per concept.
What it does, mechanically
Klein conditions on reference images as latents injected into the sequence at the model's 2D RoPE positions - the mechanism the core ReferenceLatent node feeds, and the thing that makes multi-reference editing work at all. Normally that encode happens inside the graph on every sample, from a full-resolution image, paying tokens and attention for work that hasn't changed. This node pulls it out of the loop: images in, vae in, a compressed latent bundle out.
The compression is the interesting part. token_budget sets how many tokens the reference may occupy, with native meaning "don't compress, keep the full encode." The README frames it as native resolution versus spatial pooling to a target budget, hence the round numbers - 4096, 2048, 1024, 512, 256. That count is your quality/VRAM dial, and it's what shows up in attention at sampling time.
Inputs you actually set
images is a batch of references - from the folder loader, a couple of LoadImage nodes, whatever. vae must be the VAE you sample with. Reference latents are only meaningful in the latent space that matches your sampling stack, so a mismatched VAE gives you a file that loads fine and an output that ignores your reference entirely. That failure looks like "the node doesn't work" when it's really "wrong latent space."
mod_name is both the filename and the label in the loader dropdown. The default, fk9_character_v1_refmod, is a decent convention - name the concept, version it, and you'll tell your files apart in three months. concept_type is just a label (identity, style, character, clothing, background, generic); the CLI takes the same flag, and the README's example uses identity for a person.
Then the two that change results:
token_budget-1024is the default and a sane starting point for a person. A whole style or background set can afford512;256is going to smear identity.4096/nativeis for when you genuinely need the detail and have the headroom.mode-encodeversuspooled. Both sit inside the README's "native resolution or spatial pooling" framing; start onencode.
Two optional booleans: save_to_disk and overwrite, both on by default. Which means a re-run with the same mod_name silently replaces your earlier file - usually what you want while iterating, usually not once you've shipped a workflow.
Outputs: klein9_refmods (wire straight into Apply if you'd rather skip disk), latent, and saved_path, a string confirming where the file landed - more useful than it sounds, since it's how you confirm a save happened.
The CLI does the same thing in bulk
The README ships a script for pre-encoding a whole dataset without the graph:
python custom_nodes/ComfyUI-Flux2Klein9Mod/extract_klein9_mod.py \
--name fk9_billieeilish_v1_refmod \
--dataset-dir C:/Development/ai-toolkit/datasets/billieeilish \
--concept-type identity \
--token-budget 1024 \
--output-dir C:/Development/ComfyUI/models/refmods-klein9/
Which one to use: the node when you're iterating, the CLI when you're encoding forty subjects and want to walk away.
Install
Same pack as the other three:
cd ComfyUI/custom_nodes
git clone https://github.com/malcolmamal/ComfyUI-Flux2Klein9Mod.git
mkdir -p ComfyUI/models/refmods-klein9
Restart ComfyUI after cloning. The README lists no model downloads and no heavy dependencies of its own; you need working Klein or FLUX.2 weights with their Qwen3 encoder and VAE, because this node borrows your VAE.
What goes wrong
Your refs are bad, and the RefMod inherits that. Twelve mixed-resolution selfies in three lighting conditions encode exactly that messiness, and there's no training step to average it out. The dataset rules from character-LoRA training apply almost unchanged, because it's the same data through a shorter pipeline: 15–25 images for a person, close-ups mixed with full-body, varied angles - and backgrounds that repeat in every shot get learned as part of the subject. Crop, dedupe, keep them clean.
It saves, but nothing changes. Check overwrite and mod_name first: with overwrite off, a re-run leaves the old file in place and still reports success.
One loose thread in the README: it advertises compatibility with "Klein 4B, Klein 8B, and Klein 9B models." There's no Klein 8B - BFL shipped 4B and 9B, each in base and distilled flavours. See 8B in this ecosystem and it's shorthand for the Qwen3-8B text encoder that rides with the 9B.
Inputs (8)
| Name | Type | Default | Description |
|---|---|---|---|
| images | IMAGE | — | |
| vae | VAE | — | |
| mod_name | STRING | fk9_character_v1_refmod | — |
| concept_type | COMBO | identity | 6 options: identity, style, character, clothing, background, generic |
| token_budget | COMBO | 1024 | 6 options: native, 4096, 2048, 1024, 512, 256 |
| mode | COMBO | encode | 2 options: encode, pooled |
| save_to_diskopt | BOOLEAN | true | — |
| overwriteopt | BOOLEAN | true | — |
Outputs (3)
| Name | Type | Description |
|---|---|---|
| klein9_refmods | KLEIN9_REFMODS | — |
| latent | LATENT | — |
| saved_path | STRING | — |