Nodes/saya-comfy-couple-plus/Saya Comfy Couple - HiDream (native regional)
ComfyUI Node

Saya Comfy Couple - HiDream (native regional)

Regional prompting for HiDream, with no patch on the model at all

By alphaziod·Created 3 months ago·Updated 2 days ago· 3
Saya Comfy Couple - HiDream (native regional)
  • main_positive
  • person_1_positive
  • person_2_positive
  • negative
  • latent
  • couple_config
  • clip
  • positive
  • detailer_positive
  • negative
  • mask_person_1
  • mask_person_2
  • couple_config
use_couple_attentiontrue
context_modemain_plus_person_concat
orientationhorizontal
center0.50
transition0.03
mask_floor0.00
swap_person_positionsfalse
include_main_contactfalse
add_global_main_entryfalse
shared_contact_strength0.90
contact_width0.24
main_text
person_1_text
person_2_text

Why this isn't just the Forge node with a different label

HiDream-I1's whole reputation is prompt adherence: 17B parameters, MIT licence, April 2025, driven by T5-XXL and Llama-3.1-8B-Instruct. That's where the instruction-following came from, and also the reason it spent 2025 being quantised down to something a 16GB card could run before fading out of the conversation entirely. If you're still running it - usually as a GGUF - you probably want the one thing it's good at applied to two people at once.

Here's the problem. The Forge engine patches attn2. HiDream's joint attention doesn't work that way, so patching cross-attention is roughly a no-op on it. So this node does something structurally different, and the pack calls it Architecture E: there is no MODEL socket on this node at all. It never wraps, clones or patches the model. It takes your four native HiDream conditionings and returns more native HiDream conditionings, one per region, each carrying a spatial mask. ComfyUI's own sampler then runs one HiDream forward per region and composites the predictions by mask.

That's the trick worth appreciating. Regional routing done as data the stock sampler already understands means scheduling, memory batching, EasyCache and detailers all keep working - nothing is monkey-patched, and no big regional tensor stays alive after the node call.

The inputs you'll actually touch

Required: main_positive, person_1_positive, person_2_positive, negative, latent, use_couple_attention, context_mode, orientation, center, transition, mask_floor, swap_person_positions. Same geometry vocabulary as the Forge node: center is the split, transition the soft edge, orientation horizontal or vertical.

context_mode is the one that decides how much of the scene each region knows about:

  • main_plus_person_concat (default) - token-concatenates the separately-encoded MAIN and person sequences, taking MAIN's pooled embedding. Cheap, no extra encode, but the text encoders never saw the two prompts together.
  • person_only - each region carries only its person. The scene prompt is simply absent from the regions, so turn on add_global_main_entry (an unmasked MAIN entry that contributes everywhere) or include_main_contact if you still want the background to mean something.
  • main_plus_person_reencoded - genuinely re-encodes "<main>. <person>" through the quad CLIP. Needs a clip input plus main_text, person_1_text and person_2_text filled in; without them it's a hard error rather than a fudged embedding concat. Closest to the training distribution, costs two extra encodes.

Two optional flags handle the seam: include_main_contact restricts a slice of MAIN to the soft band where the two people physically meet, with shared_contact_strength (0.90) and contact_width (0.24) as the knobs. Unlike the Forge node, these are exposed here - which is the honest reason to prefer this pair if you like tuning.

Outputs

positive goes into KSampler.positive - this time it really is the regional conditioning, not a base list. negative goes to the sampler's negative. detailer_positive goes to detailers only and never to this KSampler. mask_person_1 and mask_person_2 are pixel-space masks (handy for detailers and inpaints), while the masks the sampler sees are at latent resolution. couple_config feeds the COPY node.

Install

cd /path/to/ComfyUI/custom_nodes
git clone https://github.com/alphaziod/saya-comfy-couple-plus.git

Restart and refresh. For a GGUF HiDream checkpoint you also want ComfyUI-GGUF; the pack itself only asks for NumPy and Pillow and refuses to install PyTorch, which is correct behaviour - leave ComfyUI's build alone. You also need the HiDream text encoders in ComfyUI's text_encoders search path:

clip_l_hidream.safetensors
clip_g_hidream.safetensors
t5xxl_fp8_e4m3fn.safetensors
llama_3.1_8b_instruct_fp8_scaled.safetensors

That quad CLIP is around 13.8 GB of the load, so budget for it. No model weights come with the pack. Keep one copy installed - an old comfy_saya_couple folder registers the same node IDs.

Where people get burned

You handed it SDXL conditioning. The node guards the model family explicitly and refuses with not a native HiDream conditioning (family guard, cf. ANIMA adapter) or, if the ollama3 tensor is missing, has no conditioning_llama3 tensor. That's the pack being loud on purpose: a conditioning that doesn't match the active model family degrades into an unmasked, bleeding image otherwise, and you'd blame the node.

You skipped ModelSamplingSD3. The diffusion model goes straight into the sampler through it; the conditioning path is entirely separate. These two lines stay separate, and that's by design.

You picked main_plus_person_reencoded and nothing happened. It's a hard error, not a fallback. Wire clip and fill the three *_text fields.

person_only gave you great characters in a contextless void. Expected. Add the global MAIN entry.

Categorysaya/hidream

Inputs (21)

NameTypeDefaultDescription
main_positiveCONDITIONING
person_1_positiveCONDITIONING
person_2_positiveCONDITIONING
negativeCONDITIONING
latentLATENT
use_couple_attentionBOOLEANtrue
context_modeCOMBOmain_plus_person_concat3 options: main_plus_person_concat, person_only, main_plus_person_reencoded
orientationCOMBOhorizontal2 options: horizontal, vertical
centerFLOAT0.500.15–0.85
transitionFLOAT0.030.01–0.2
mask_floorFLOAT0.000–0.2
swap_person_positionsBOOLEANfalse
couple_configoptSAYA_COUPLE_CONFIG
include_main_contactoptBOOLEANfalse
add_global_main_entryoptBOOLEANfalse
shared_contact_strengthoptFLOAT0.900–0.95
contact_widthoptFLOAT0.240–0.5
clipoptCLIP
main_textoptSTRING
person_1_textoptSTRING
person_2_textoptSTRING

Outputs (6)

NameTypeDescription
positiveCONDITIONING
detailer_positiveCONDITIONING
negativeCONDITIONING
mask_person_1MASK
mask_person_2MASK
couple_configSAYA_COUPLE_CONFIG