SeFi Turbo (MLX, all-in-one)
The all-in-one MLX node
- clip
- vae
- image
- texture_latent
SeFi-Image got buried at launch. The quality was genuinely good - strong text rendering, real realism, a 5B model trained on a tenth of Z-Image's compute - but launch week was mostly people asking "will we even get ComfyUI support?" and grumbling about the CC-BY-NC license. Then SeFiTurboMLX shipped: the whole SeFi-Image turbo pipeline in one node, running end-to-end in Apple MLX. On Apple Silicon, this is the reason the "semantic-first diffusion" family is finally a real option.
What the node actually does
The name is doing a lot of work. One node downloads the gated SeFi-Image-*-turbo checkpoint, converts its DiT to MLX under models/mlx/sefi/, runs the dual-time turbo denoise loop, and VAE-decodes - no CLIPLoader, no KSampler, no VAELoader wiring required. The one thing it can't do is run on your Nvidia box: MLX is Apple Silicon only, and the code checks for it up front.
Under the hood it's a faithful MLX port of the Flux2-style dual-stream backbone SeFi uses, with the Qwen3-VL-4B language tower as text encoder and a 32-channel AutoencoderKL decoder. Everything converts once and caches - first run pays for download plus conversion, reruns are fast.
The semantic-first bit, briefly
SeFi's trick is "dual-time" diffusion: instead of one latent, you get a semantic stream and a texture stream, and the semantic one runs ahead - by default delta_t = 0.1 - so composition is decided first, texture fills in behind. That's what the "turbo" recipe exploits: at 4 steps with guidance_scale = 1.0 (no CFG, matching the DMD2-distilled reference) you get fast generations that still compose well. Steps are tuned for 4/8/10; anything else is allowed but you're off the beaten path.
The inputs that actually matter
Beginners can touch these and ignore the rest:
- model -
SeFi-Image-5B-turboby default, with 2B and 1B turbo variants if you want speed over quality. - prompt, width, height, seed - standard. 1024×1024 default, multiples of 16.
- steps - 4 default; try 8 for a bit more polish.
- quantize -
q8default,q4to slim the DiT down,bf16for full fidelity. Same ladder logic as GGUF: Q8 is basically indistinguishable from bf16 here, so if it fits, leave it. - hf_token - your HuggingFace token. The model repo is gated (CC-BY-NC), so you must accept the license on the HF model page while logged in before anything downloads. You can set
HF_TOKENin your environment instead.
The other two you'll actually meet in the wild: low_memory frees the MLX text encoder after encoding (~2.9GB resident instead of ~5.6GB, at the cost of re-loading it per new prompt), and bn_denorm stays ON - turn it off and output turns washed-out and grainy.
The node outputs image plus texture_latent - the raw texture stream, if you ever want to re-decode it yourself. And yes, you can override the MLX encoder and VAE with torch ones: wire a CLIP into the clip input or a VAE into vae, or flip mlx_encoder/mlx_vae off. Just don't point the VAE at a generic 16-channel Flux VAE - SeFi's texture VAE is 32-ch, and the node picks the right file by slug for a reason.
Installing it
Via ComfyUI Manager, search "SeFiMLXComfyNode" and install. Or the manual route:
cd ComfyUI/custom_nodes
git clone https://github.com/jwooldridge234/SeFiMLXComfyNode
cd SeFiMLXComfyNode
pip install -r requirements.txt
Then restart ComfyUI. Dependencies are light - mlx, huggingface_hub, safetensors, transformers - and no other custom nodes are required. The real heavy dependency is the model itself: the first generation triggers a multi-GB gated download plus a one-time torch→MLX conversion, so don't panic when the first run crawls. It's cached after that.
Where people get burned
- 403 / "not authorized" - the gated-repo classic. Accept the license at
huggingface.co/SeFi-Image/SeFi-Image-5B-turbowith the account that token belongs to, then pass the token. - Washed-out output -
bn_denormflipped off, orpad_to_maxoff (the DiT was trained on the padded sequence; turning it off is out-of-distribution → noise). - Outputs that look "off" at a given seed - this is a fresh MLX port. The key mappings are verified statically, but the numeric forward was never run on Apple hardware before release. The README ships a parity harness for exactly this:
python custom_nodes/ComfyUI-SeFi-MLX/dev/parity.pybuilds a tiny model and checks torch vs MLX on identical weights. Run it before you judge the model. Images also won't bit-match the native torch node at the same seed - the initial noise deliberately uses torch's RNG, since MLX's normal RNG gives worse compositions here.
Honest caveat: it's non-commercial by license, and the corner is weeks old. But as the one-stop shop for SeFi-Image on a Mac, it's the one you'd reach for.
Inputs (28)
| Name | Type | Default | Description |
|---|---|---|---|
| model | COMBO | SeFi-Image-5B-turbo | 3 options: SeFi-Image-5B-turbo, SeFi-Image-2B-turbo, SeFi-Image-1B-turbo |
| prompt | STRING | A blue ceramic mug on a white desk. | — |
| width | INT | 1024256–4096 | — |
| height | INT | 1024256–4096 | — |
| steps | INT | 41–200 | Denoise steps. Turbo is tuned for 4/8/10, but any value is allowed. |
| seed | INT | 00–18446744073709550000 | — |
| quantize | COMBO | q8 | 3 options: q8, q4, bf16 |
| clipopt | CLIP | OVERRIDE: a torch CLIP (CLIPLoader). If wired, used instead of the MLX encoder. | |
| vaeopt | VAE | OVERRIDE: a torch VAE (VAELoader). If wired, used instead of the MLX VAE. | |
| mlx_encoderopt | BOOLEAN | true | Run the Qwen3-VL-4B text encoder in MLX (torch-free). Ignored if a `clip` is wired. |
| mlx_vaeopt | BOOLEAN | true | Run the VAE decode in MLX (torch-free). Ignored if a `vae` is wired. |
| negative_promptopt | STRING | — | |
| guidance_scaleopt | FLOAT | 1.01–12 | Turbo = 1.0 (no CFG). >1 runs two passes/step. |
| delta_topt | FLOAT | 0.100–1 | Semantic lead (5B turbo = 0.1). |
| timestep_shift_alphaopt | FLOAT | 1.000.01–8 | SeFi runner u-warp (sefi_schedule.apply_timestep_shift). 1.0 = identity. Lower (e.g. 0.3) front-loads denoising; tune per resolution if adherence is off. |
| dynamic_shiftopt | BOOLEAN | false | Apply a resolution-aware flow shift exp(mu) to the sigma table (~1.88 at 512, ~3.16 at 1024) instead of scheduler_shift. Experimental. |
| scheduler_shiftopt | FLOAT | 1.000.1–12 | Flow-match table shift (sigma warp). 1.0 = linear, as the runner uses; the warping is normally done by timestep_shift_alpha. |
| rope_modeopt | COMBO | repo (5e6) | Encoder RoPE. 'repo' = official SeFi (theta 5e6, standard). 'comfy' = comfy's value (1e6). 'interleaved' = Qwen3-VL M-RoPE (5e6; == standard for text). |
| template_modeopt | COMBO | repo (bare) | Prompt template. 'repo' = official bare user/assistant. 'comfy' = adds an empty <think> block. 'none' = raw prompt, no chat template. |
| max_lengthopt | INT | 102416–8192 | Encoder token length. 1024 = the official SeFi value (sefi_config). |
| pad_to_maxopt | BOOLEAN | true | Pad conditioning to max_length. The SeFi DiT was trained on the padded sequence — leave ON; OFF is out-of-distribution -> noise. |
| encoder_quantizeopt | COMBO | q8 | Quantization for the Qwen3-VL-4B encoder. q4 is the fastest per-prompt encode (and smallest); bf16 is most accurate. |
| bn_denormopt | BOOLEAN | true | Rescale the latent from bn-normalized (std ~1.8) to the VAE's raw space before MLX decode. ON fixes washed-out/grainy output. |
| low_memoryopt | BOOLEAN | false | Free the MLX text encoder after encoding so only the DiT (+VAE) stay resident (~2.9GB vs ~5.6GB). Re-encoding a new prompt reloads it. |
| vae_quantizeopt | COMBO | bf16 | VAE storage + decode compute dtype. bf16 ~halves VAE weight and decode-activation memory vs fp32 (matches comfy's MPS VAE). |
| hf_tokenopt | STRING | HF token for the gated repo (or set HF_TOKEN env). License must be accepted. | |
| group_sizeopt | INT | 6432–128 | — |
| dual_timeopt | BOOLEAN | true | — |
Outputs (2)
| Name | Type | Description |
|---|---|---|
| image | IMAGE | — |
| texture_latent | LATENT | — |