Files

350 lines
15 KiB
Python

"""
ComfyUI-QwenImageWanBridge
Qwen2.5-VL implementation with custom vision support
"""
NODE_CLASS_MAPPINGS = {}
NODE_DISPLAY_NAME_MAPPINGS = {}
# Web directory for JavaScript extensions
WEB_DIRECTORY = "./web"
# ============================================================================
# CORE NODES - Qwen2.5-VL with proper vision support
# ============================================================================
try:
from .nodes.qwen_vl_encoder import QwenVLCLIPLoader, QwenVLTextEncoder, QwenLowresFixNode
NODE_CLASS_MAPPINGS["QwenVLCLIPLoader"] = QwenVLCLIPLoader
NODE_DISPLAY_NAME_MAPPINGS["QwenVLCLIPLoader"] = "Qwen2.5-VL CLIP Loader"
NODE_CLASS_MAPPINGS["QwenVLTextEncoder"] = QwenVLTextEncoder
NODE_DISPLAY_NAME_MAPPINGS["QwenVLTextEncoder"] = "Qwen2.5-VL Text Encoder"
NODE_CLASS_MAPPINGS["QwenLowresFixNode"] = QwenLowresFixNode
NODE_DISPLAY_NAME_MAPPINGS["QwenLowresFixNode"] = "Qwen Lowres Fix (Two-Stage)"
print("[QwenImageWanBridge] Loaded Qwen2.5-VL nodes using ComfyUI's CLIP")
except Exception as e:
print(f"[QwenImageWanBridge] Failed to load encoder nodes: {e}")
# Advanced encoder for power users
try:
from .nodes.qwen_vl_encoder_advanced import QwenVLTextEncoderAdvanced
NODE_CLASS_MAPPINGS["QwenVLTextEncoderAdvanced"] = QwenVLTextEncoderAdvanced
NODE_DISPLAY_NAME_MAPPINGS["QwenVLTextEncoderAdvanced"] = "Qwen2.5-VL Text Encoder (Advanced)"
print("[QwenImageWanBridge] Loaded Advanced Encoder with weighted resolution control")
except Exception as e:
print(f"[QwenImageWanBridge] Failed to load advanced encoder: {e}")
# Helper nodes
try:
from .nodes.qwen_vl_helpers import QwenVLEmptyLatent, QwenVLImageToLatent
NODE_CLASS_MAPPINGS["QwenVLEmptyLatent"] = QwenVLEmptyLatent
NODE_DISPLAY_NAME_MAPPINGS["QwenVLEmptyLatent"] = "Qwen VL Empty Latent"
NODE_CLASS_MAPPINGS["QwenVLImageToLatent"] = QwenVLImageToLatent
NODE_DISPLAY_NAME_MAPPINGS["QwenVLImageToLatent"] = "Qwen VL Image to Latent"
print("[QwenImageWanBridge] Loaded Qwen VL helper nodes (2 nodes)")
except Exception as e:
print(f"[QwenImageWanBridge] Failed to load helper nodes: {e}")
# Image batching node (aspect-ratio preserving with v2.6.1 scaling)
try:
from .nodes.qwen_image_batch import QwenImageBatch
NODE_CLASS_MAPPINGS["QwenImageBatch"] = QwenImageBatch
NODE_DISPLAY_NAME_MAPPINGS["QwenImageBatch"] = "Qwen Image Batch"
print("[QwenImageWanBridge] Loaded Qwen Image Batch node")
print("[QwenImageWanBridge] FEATURE: Aspect-ratio preserving batching with v2.6.1 scaling modes")
except Exception as e:
print(f"[QwenImageWanBridge] Failed to load image batch node: {e}")
# Qwen-to-Wan Video Bridge nodes
try:
from .nodes.qwen_wan_bridge import (
QwenToWanFirstFrameLatent,
QwenToWanLatentSaver,
QwenToWanImageSaver
)
NODE_CLASS_MAPPINGS["QwenToWanFirstFrameLatent"] = QwenToWanFirstFrameLatent
NODE_DISPLAY_NAME_MAPPINGS["QwenToWanFirstFrameLatent"] = "Qwen → Wan First Frame Latent"
NODE_CLASS_MAPPINGS["QwenToWanLatentSaver"] = QwenToWanLatentSaver
NODE_DISPLAY_NAME_MAPPINGS["QwenToWanLatentSaver"] = "Save First Frame Latent (Wan)"
NODE_CLASS_MAPPINGS["QwenToWanImageSaver"] = QwenToWanImageSaver
NODE_DISPLAY_NAME_MAPPINGS["QwenToWanImageSaver"] = "Save First Frame Image"
print("[QwenImageWanBridge] Loaded Qwen-to-Wan Video Bridge nodes (3 nodes)")
print("[QwenImageWanBridge] FEATURE: Image-to-video bridge with first frame conditioning")
except Exception as e:
print(f"[QwenImageWanBridge] Failed to load Wan bridge nodes: {e}")
# Qwen-to-ChronoEdit Bridge node
try:
from .nodes.qwen_chronoedit_bridge import QwenToChronoEditBridge
NODE_CLASS_MAPPINGS["QwenToChronoEditBridge"] = QwenToChronoEditBridge
NODE_DISPLAY_NAME_MAPPINGS["QwenToChronoEditBridge"] = "Qwen → ChronoEdit Bridge"
print("[QwenImageWanBridge] Loaded Qwen-to-ChronoEdit Bridge node")
print("[QwenImageWanBridge] FEATURE: Qwen image editing → ChronoEdit video animation")
except Exception as e:
print(f"[QwenImageWanBridge] Failed to load ChronoEdit bridge node: {e}")
# Resolution nodes removed - functionality integrated into encoder
# Mask-based inpainting nodes
try:
from .nodes.qwen_mask_processor import QwenMaskProcessor
from .nodes.qwen_inpaint_sampler import QwenInpaintSampler
NODE_CLASS_MAPPINGS["QwenMaskProcessor"] = QwenMaskProcessor
NODE_DISPLAY_NAME_MAPPINGS["QwenMaskProcessor"] = "Qwen Mask Processor"
NODE_CLASS_MAPPINGS["QwenInpaintSampler"] = QwenInpaintSampler
NODE_DISPLAY_NAME_MAPPINGS["QwenInpaintSampler"] = "Qwen Inpainting Sampler"
print("[QwenImageWanBridge] Loaded mask-based inpainting nodes (2 nodes)")
print("[QwenImageWanBridge] FEATURE: Mask-based spatial editing with diffusers patterns")
except Exception as e:
print(f"[QwenImageWanBridge] Failed to load mask-based inpainting nodes: {e}")
# Simplified template builder V2
try:
from .nodes.qwen_template_builder import (
QwenTemplateBuilderV2,
QwenTemplateConnector
)
NODE_CLASS_MAPPINGS["QwenTemplateBuilder"] = QwenTemplateBuilderV2
NODE_DISPLAY_NAME_MAPPINGS["QwenTemplateBuilder"] = "Qwen Template Builder"
NODE_CLASS_MAPPINGS["QwenTemplateConnector"] = QwenTemplateConnector
NODE_DISPLAY_NAME_MAPPINGS["QwenTemplateConnector"] = "Qwen Template Connector"
print("[QwenImageWanBridge] Loaded template builder nodes")
except Exception as e:
print(f"[QwenImageWanBridge] Failed to load template nodes: {e}")
# Qwen-WAN Bridge nodes removed - not related to Qwen-Image-Edit
# V2V nodes removed - not core functionality
# Multi-Reference handler deprecated - use Image Batch node instead
# EliGen Entity Control nodes (DiffSynth-Studio feature)
try:
from .nodes.qwen_eligen_entity_control import (
QwenEliGenEntityControl,
QwenEliGenMaskPainter
)
NODE_CLASS_MAPPINGS["QwenEliGenEntityControl"] = QwenEliGenEntityControl
NODE_DISPLAY_NAME_MAPPINGS["QwenEliGenEntityControl"] = "Qwen EliGen Entity Control"
NODE_CLASS_MAPPINGS["QwenEliGenMaskPainter"] = QwenEliGenMaskPainter
NODE_DISPLAY_NAME_MAPPINGS["QwenEliGenMaskPainter"] = "EliGen Mask Painter"
print("[QwenImageWanBridge] Loaded EliGen Entity Control nodes (2 nodes)")
print("[QwenImageWanBridge] FEATURE: Precise spatial control with masks and per-region prompts")
except Exception as e:
print(f"[QwenImageWanBridge] Failed to load EliGen nodes: {e}")
# Token Analysis nodes
try:
from .nodes.qwen_token_debugger import QwenTokenDebugger
from .nodes.qwen_token_analyzer_standalone import QwenTokenAnalyzerStandalone
from .nodes.qwen_spatial_token_generator import QwenSpatialTokenGenerator
NODE_CLASS_MAPPINGS["QwenTokenDebugger"] = QwenTokenDebugger
NODE_DISPLAY_NAME_MAPPINGS["QwenTokenDebugger"] = "Qwen Token Debugger"
NODE_CLASS_MAPPINGS["QwenTokenAnalyzer"] = QwenTokenAnalyzerStandalone
NODE_DISPLAY_NAME_MAPPINGS["QwenTokenAnalyzer"] = "Qwen Token Analyzer"
NODE_CLASS_MAPPINGS["QwenSpatialTokenGenerator"] = QwenSpatialTokenGenerator
NODE_DISPLAY_NAME_MAPPINGS["QwenSpatialTokenGenerator"] = "Qwen Spatial Token Generator"
print("[QwenImageWanBridge] Loaded Token Analysis nodes (3 nodes)")
except Exception as e:
print(f"[QwenImageWanBridge] Failed to load Token Analysis nodes: {e}")
# Debug Controller node
try:
from .nodes.qwen_debug_controller import QwenDebugController
NODE_CLASS_MAPPINGS["QwenDebugController"] = QwenDebugController
NODE_DISPLAY_NAME_MAPPINGS["QwenDebugController"] = "Qwen Debug Controller"
print("[QwenImageWanBridge] Loaded Debug Controller node")
print("[QwenImageWanBridge] FEATURE: Holistic debugging with performance profiling and log analysis")
except Exception as e:
print(f"[QwenImageWanBridge] Failed to load Debug Controller: {e}")
# Experimental Smart Crop node
try:
from .nodes.experimental_smart_crop import QwenSmartCrop
NODE_CLASS_MAPPINGS["QwenSmartCrop"] = QwenSmartCrop
NODE_DISPLAY_NAME_MAPPINGS["QwenSmartCrop"] = "Qwen Smart Crop (Experimental)"
print("[QwenImageWanBridge] Loaded Experimental Smart Crop node")
print("[QwenImageWanBridge] EXPERIMENTAL: Intelligent face cropping with VLM detection support")
except Exception as e:
print(f"[QwenImageWanBridge] Failed to load Smart Crop node: {e}")
# C2C Vision Bridge nodes archived - see nodes/archive/c2c/
# Inpainting nodes restored - mask-based approach aligns with DiffSynth patterns
# Note: Experimental multi-frame nodes have been archived
# Use Multi-Reference Handler with "index" mode for multi-frame support
# Patches removed - functionality integrated into main encoder
# Native nodes removed - incomplete implementation
# Wrapper nodes archived - see nodes/archive/wrapper/ (not production ready, had VRAM issues)
# ============================================================================
# EXPORTS
# ============================================================================
__all__ = ["NODE_CLASS_MAPPINGS", "NODE_DISPLAY_NAME_MAPPINGS"]
print(f"[QwenImageWanBridge] Total nodes loaded: {len(NODE_CLASS_MAPPINGS)}")
# Engine nodes removed - incomplete implementation
# ============================================================================
# HUNYUANVIDEO 1.5 NODES - Dual text encoder (Qwen2.5-VL + byT5)
# ============================================================================
# Loader nodes
try:
from .nodes.hunyuan_video_nodes import (
HunyuanVideoCLIPLoader,
HunyuanVideoVisionLoader,
HunyuanVideoEmptyLatent,
)
NODE_CLASS_MAPPINGS["HunyuanVideoCLIPLoader"] = HunyuanVideoCLIPLoader
NODE_DISPLAY_NAME_MAPPINGS["HunyuanVideoCLIPLoader"] = "HunyuanVideo CLIP Loader"
NODE_CLASS_MAPPINGS["HunyuanVideoVisionLoader"] = HunyuanVideoVisionLoader
NODE_DISPLAY_NAME_MAPPINGS["HunyuanVideoVisionLoader"] = "HunyuanVideo Vision Loader (SigLIP)"
NODE_CLASS_MAPPINGS["HunyuanVideoEmptyLatent"] = HunyuanVideoEmptyLatent
NODE_DISPLAY_NAME_MAPPINGS["HunyuanVideoEmptyLatent"] = "HunyuanVideo Empty Latent"
print("[QwenImageWanBridge] Loaded HunyuanVideo 1.5 loader nodes (3 nodes)")
print("[QwenImageWanBridge] FEATURE: Dual CLIP loader (Qwen2.5-VL + byT5)")
except Exception as e:
print(f"[QwenImageWanBridge] Failed to load HunyuanVideo loader nodes: {e}")
# Text encoder with template system
try:
from .nodes.hunyuan_video_encoder import HunyuanVideoTextEncoder, get_templates as get_hunyuan_video_templates
NODE_CLASS_MAPPINGS["HunyuanVideoTextEncoder"] = HunyuanVideoTextEncoder
NODE_DISPLAY_NAME_MAPPINGS["HunyuanVideoTextEncoder"] = "HunyuanVideo Text Encoder"
# Register API endpoint for templates (single source of truth)
try:
from aiohttp import web
from server import PromptServer
@PromptServer.instance.routes.get("/api/hunyuan_video_templates")
async def get_hunyuan_video_templates_api(request):
"""API endpoint for HunyuanVideo templates - JS fetches from here."""
templates = get_hunyuan_video_templates()
return web.json_response(templates)
print("[QwenImageWanBridge] Loaded HunyuanVideo text encoder")
print("[QwenImageWanBridge] API: /api/hunyuan_video_templates endpoint registered")
except Exception as api_err:
print(f"[QwenImageWanBridge] Loaded HunyuanVideo encoder (API endpoint failed: {api_err})")
except Exception as e:
print(f"[QwenImageWanBridge] Failed to load HunyuanVideo encoder: {e}")
# Prompt expander (lightweight alternative to Qwen3-235B rewriter)
try:
from .nodes.hunyuan_video_prompt_expander import HunyuanVideoPromptExpander
NODE_CLASS_MAPPINGS["HunyuanVideoPromptExpander"] = HunyuanVideoPromptExpander
NODE_DISPLAY_NAME_MAPPINGS["HunyuanVideoPromptExpander"] = "HunyuanVideo Prompt Expander"
print("[QwenImageWanBridge] Loaded HunyuanVideo prompt expander")
print("[QwenImageWanBridge] FEATURE: Lightweight prompt expansion using loaded Qwen2.5-VL")
except Exception as e:
print(f"[QwenImageWanBridge] Failed to load HunyuanVideo prompt expander: {e}")
# ============================================================================
# Z-IMAGE NODES - Proper thinking token support
# ============================================================================
try:
from .nodes.z_image_encoder import ZImageTextEncoder, ZImageMessageChain, get_templates
NODE_CLASS_MAPPINGS["ZImageTextEncoder"] = ZImageTextEncoder
NODE_DISPLAY_NAME_MAPPINGS["ZImageTextEncoder"] = "Z-Image Text Encoder"
NODE_CLASS_MAPPINGS["ZImageMessageChain"] = ZImageMessageChain
NODE_DISPLAY_NAME_MAPPINGS["ZImageMessageChain"] = "Z-Image Message Chain"
# Register API endpoint for templates (single source of truth)
try:
from aiohttp import web
from server import PromptServer
@PromptServer.instance.routes.get("/api/z_image_templates")
async def get_z_image_templates(request):
"""API endpoint for Z-Image templates - JS fetches from here."""
templates = get_templates()
return web.json_response(templates)
print("[QwenImageWanBridge] Loaded Z-Image encoder nodes (2 nodes)")
print("[QwenImageWanBridge] API: /api/z_image_templates endpoint registered")
except Exception as api_err:
print(f"[QwenImageWanBridge] Loaded Z-Image nodes (API endpoint failed: {api_err})")
except Exception as e:
print(f"[QwenImageWanBridge] Failed to load Z-Image encoder nodes: {e}")
# ============================================================================
# EXPERIMENTAL ANALYSIS NODES
# ============================================================================
# Template Influence Analyzer - tests if system prompts affect embeddings
try:
from .nodes.template_influence_analyzer import TemplateInfluenceAnalyzer
NODE_CLASS_MAPPINGS["TemplateInfluenceAnalyzer"] = TemplateInfluenceAnalyzer
NODE_DISPLAY_NAME_MAPPINGS["TemplateInfluenceAnalyzer"] = "Template Influence Analyzer"
print("[QwenImageWanBridge] Loaded Template Influence Analyzer (experimental)")
except Exception as e:
print(f"[QwenImageWanBridge] Failed to load Template Influence Analyzer: {e}")
# ============================================================================
# DEBUG TRACING - Opt-in via environment variable
# ============================================================================
import os
if os.environ.get('QWEN_ENABLE_DEBUG_PATCHES', '').lower() in ('true', '1', 'yes'):
try:
from .nodes import debug_patch
debug_patch.apply_debug_patches()
print("[QwenImageWanBridge] Debug patches applied (requested via QWEN_ENABLE_DEBUG_PATCHES)")
print("[QwenImageWanBridge] Set QWEN_DEBUG_VERBOSE=true for verbose tracing output")
except Exception as e:
print(f"[QwenImageWanBridge] Debug patches failed: {e}")