Simplify CLIP encoding - remove broken Flux detection

CRITICAL FIX: hasattr(clip, 'encode_from_tokens_scheduled') is useless
- This method exists in base CLIP class inherited by ALL models
- Always returns True (SD/SDXL/Flux/Chroma/SD3/etc.)
- Cannot be used for model detection

Simplified to universal encoding for ALL models:
- Removed guidance parameter from RegionalPrompterFlux
- Removed all is_flux detection branching
- Use encode_from_tokens(return_pooled=True) universally
- ComfyUI's CLIP object handles 1/2/3/4 encoder complexity internally

Benefits:
- Simpler code (removed 30+ lines of complex branching)
- More maintainable (no model-specific special cases)
- Actually works correctly (previous logic was broken)

Documented in CONTEXT.md with encoder architecture table.
This commit is contained in:
Claude
2025-11-22 03:06:29 +00:00
parent f0fe230164
commit 467ed9c6fa
2 changed files with 34 additions and 60 deletions
+27 -18
View File
@@ -38,6 +38,7 @@
- **Format:** Must wrap in list: `[[cond, {"pooled_output": pooled}]]`
- **Works:** Identical to external CLIPTextEncode nodes
- **Benefit:** Massive UX improvement - no external nodes needed!
- **Universal:** Same method works for ALL models (SD/SDXL/Flux/Chroma/SD3/etc.) - ComfyUI handles multi-encoder complexity internally
#### Text Encoder Architectures by Model
**CRITICAL:** Different models use different numbers and types of text encoders. ComfyUI's CLIP object handles the complexity internally, but understanding the architecture helps with debugging.
@@ -47,34 +48,42 @@
| **SD 1.5** | 1 | CLIP-L | Standard `encode_from_tokens` |
| **SD 2.x** | 1 | OpenCLIP-H | Standard `encode_from_tokens` |
| **SDXL** | 2 | CLIP-L + OpenCLIP-G | Standard `encode_from_tokens` (handled internally) |
| **Flux** ✅ | 2 | T5-XXL + CLIP-L | **`encode_from_tokens_scheduled`** with guidance |
| **Flux** ✅ | 2 | T5-XXL + CLIP-L | Standard `encode_from_tokens` (handled internally) |
| **Chroma** ✅ | 1 | T5-XXL only | Standard `encode_from_tokens` (Flux-based but single encoder!) |
| **SD3** | 3 | CLIP-L + CLIP-G + T5-XXL | Standard `encode_from_tokens` (multi-encoder internal) |
| **SD3.5** | 3 | CLIP-L + CLIP-G + T5-XXL | Standard `encode_from_tokens` (multi-encoder internal) |
| **HiDream** | 4 | CLIP-L + CLIP-G + 2× T5 variants | ComfyUI uses 4 encoder files |
| **WAN 2.1** | 1 | UMT5-XXL | Standard encoding (fp16/fp8/bf16 variants) |
| **WAN 2.2** | 1 | UMT5-XXL + MoE | Standard encoding (nf4 4-bit quantization) |
| **Qwen-Image** | 1 | Qwen 2.5 VL (qwen_2.5_vl_7b.safetensors) | Standard encoding (vision-language LLM encoder) |
| **HiDream** | 4 | CLIP-L + CLIP-G + 2× T5 variants | Standard `encode_from_tokens` (multi-encoder internal) |
| **WAN 2.1** | 1 | UMT5-XXL | Standard `encode_from_tokens` (fp16/fp8/bf16 variants) |
| **WAN 2.2** | 1 | UMT5-XXL + MoE | Standard `encode_from_tokens` (nf4 4-bit quantization) |
| **Qwen-Image** | 1 | Qwen 2.5 VL (qwen_2.5_vl_7b.safetensors) | Standard `encode_from_tokens` (vision-language LLM) |
**Detection Strategy:**
**Implementation (Simplified - November 2025):**
```python
# Current implementation (correct for most models)
is_flux = hasattr(clip, 'encode_from_tokens_scheduled')
if is_flux:
# Flux-specific: dual encoder with guidance scheduling
cond = clip.encode_from_tokens_scheduled(tokens, add_dict={"guidance": guidance})
else:
# Standard encoding: SD/SDXL/Chroma/SD3/SD3.5/HiDream/WAN/Qwen
# ComfyUI's CLIP object handles multi-encoder complexity internally
cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True)
# Universal encoding for ALL models (1, 2, 3, or 4 encoders)
# ComfyUI's CLIP object handles multi-encoder complexity internally
for prompt in prompts:
if prompt and prompt.strip():
tokens = clip.tokenize(prompt)
cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True)
encoded_conditionings.append([[cond, {"pooled_output": pooled}]])
```
**Critical Discovery (November 2025):**
- ❌ **WRONG:** Using `hasattr(clip, 'encode_from_tokens_scheduled')` to detect Flux
- This method exists in the **base CLIP class** that ALL models inherit from
- Returns `True` for SD1.5, SDXL, SD3, Flux, Chroma - everything!
- Completely useless for model detection
- ✅ **CORRECT:** Use standard `encode_from_tokens(return_pooled=True)` for ALL models
- Works universally: SD (1 encoder), SDXL (2), Flux (2), SD3 (3), HiDream (4)
- ComfyUI's CLIP object abstracts encoder complexity
- No model-specific branching needed
- 📝 **Note:** `encode_from_tokens_scheduled` is what CLIPTextEncode uses internally, but for regional prompting without temporal effects, `encode_from_tokens` works perfectly
**Key Insights:**
- ✅ **Chroma is Flux-based architecture but uses ONLY 1 encoder** (T5-XXL, not CLIP-L+T5-XXL)
- ✅ **SD3/SD3.5 use 3 encoders** but ComfyUI's CLIP handles it transparently via standard method
- ✅ **SD3/SD3.5 use 3 encoders** but ComfyUI's CLIP handles it transparently
- ✅ **HiDream uses 4 encoders** in ComfyUI (CLIP-L, CLIP-G, T5 variants)
- ✅ **Flux is the ONLY model** currently requiring `encode_from_tokens_scheduled` method
- ✅ **ALL models** use the same encoding method - no special cases needed
- ⚠️ **Don't assume encoder count from architecture** - Chroma proves Flux-based ≠ dual encoder!
#### JavaScript UI
+7 -42
View File
@@ -207,13 +207,6 @@ class RegionalPrompterFlux:
"default": True,
"tooltip": "✅ RECOMMENDED: Softer masks (0.8 strength + gentle edge blend) work better than harsh full-strength (1.0) masks. Confirmed better for Flux, works well with other mask-based models. Disable if you prefer sharper region edges."
}),
"guidance": ("FLOAT", {
"default": 3.5,
"min": 0.0,
"max": 100.0,
"step": 0.1,
"tooltip": "Guidance strength for Flux (default 3.5). Higher values = stronger prompt adherence. Try 5-7 for better regional control."
}),
},
"optional": {
"region2_prompt": ("STRING", {
@@ -267,15 +260,15 @@ How to use:
• Works well with other mask-based models - try it first!
Model-Specific Tips:
• FLUX: Use 3-4 regions max (quality degrades with more), increase CFG to 5-7
• FLUX: Use 3-4 regions max (quality degrades with more)
• FLUX-BASED (Chroma, etc.): Likely 3-4 region limit too (untested), try and see!
• SD3/SD3.5: No known region limit, standard CFG values
• SD3/SD3.5: No known region limit
• QWEN-IMAGE: Experimental - try mask-based conditioning and report results!
Compatible: Flux (all variants), Chroma, SD3, SD3.5, and other mask-based models"""
def encode_regions_flux(self, clip, width, height, background_prompt, region1_prompt,
soften_masks, guidance, extra_pnginfo, unique_id,
soften_masks, extra_pnginfo, unique_id,
region2_prompt="", region3_prompt="", region4_prompt=""):
"""Encode all prompts and apply mask-based regional conditioning for Flux/Chroma/SD3."""
@@ -314,43 +307,15 @@ Compatible: Flux (all variants), Chroma, SD3, SD3.5, and other mask-based models
print(f"⚠️ Warning: {num_regions} regions detected. Flux works best with 3-4 regions maximum.")
# Encode each prompt using CLIP
# Flux uses dual-encoder (CLIP-L + T5-XXL), Chroma/Qwen use single encoder
# ComfyUI's CLIP object handles multi-encoder complexity internally
# Works for all models: Flux (2 encoders), SD3 (3 encoders), Chroma/Qwen (1 encoder), etc.
encoded_conditionings = []
# Detect if this is Flux by checking for encode_from_tokens_scheduled method
is_flux = hasattr(clip, 'encode_from_tokens_scheduled')
for prompt in prompts:
if prompt and prompt.strip():
tokens = clip.tokenize(prompt)
if is_flux:
# Flux-specific: Dual encoder (CLIP-L + T5-XXL) with guidance
# Check if t5xxl tokens are available
if "t5xxl" in tokens:
# Already has both encoders in single tokenize call
pass
else:
# Need to merge t5xxl tokens manually
try:
t5_result = clip.tokenize(prompt)
if "t5xxl" in t5_result:
tokens["t5xxl"] = t5_result["t5xxl"]
except:
pass
# Use Flux's scheduled encoding with guidance
try:
cond = clip.encode_from_tokens_scheduled(tokens, add_dict={"guidance": guidance})
encoded_conditionings.append(cond)
except Exception as e:
print(f"⚠️ Flux encoding failed: {e}, using fallback")
cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True)
encoded_conditionings.append([[cond, {"pooled_output": pooled}]])
else:
# Standard encoding for Chroma, Qwen, SD3, etc. (single encoder)
cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True)
encoded_conditionings.append([[cond, {"pooled_output": pooled}]])
cond, pooled = clip.encode_from_tokens(tokens, return_pooled=True)
encoded_conditionings.append([[cond, {"pooled_output": pooled}]])
else:
encoded_conditionings.append(None)