New node for text-only Qwen models (no vision): - Qwen2.5 series: 1.5B, 3B, 7B, 14B - Qwen3 series: 1.7B, 4B, 8B - Abliterated (uncensored): 7B, 14B Features: - VRAM-efficient text-only inference - 4-bit, 8-bit, FP16 quantization - Model caching for fast repeated calls - OpenAI-compatible interface Updated SID_PromptModifier to support local_text provider. Ideal for prompt modification workflows without vision. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
595 lines
21 KiB
Python
595 lines
21 KiB
Python
"""
|
|
ComfyUI-AI-Photography-Toolkit
|
|
LLM Provider nodes for ComfyUI - Cloud and Local model support.
|
|
All nodes are prefixed with SID_ for easy identification.
|
|
|
|
Author: Siddhartha Lahiri
|
|
Version: 4.2.1
|
|
"""
|
|
|
|
__version__ = "4.2.1"
|
|
|
|
import sys
|
|
import subprocess
|
|
import importlib.util
|
|
|
|
# Track installation status for welcome message
|
|
_dependency_status = {}
|
|
_v3_api_available = False
|
|
|
|
|
|
def check_and_install_dependencies():
|
|
"""
|
|
Check and install required dependencies. Track status for logging.
|
|
"""
|
|
global _dependency_status
|
|
|
|
# Required dependencies (auto-installed)
|
|
dependencies = {
|
|
# LLM API dependencies
|
|
"anthropic": {
|
|
"import_name": "anthropic",
|
|
"pip_spec": "anthropic>=0.39.0",
|
|
"description": "Anthropic Claude API SDK",
|
|
},
|
|
"openai": {
|
|
"import_name": "openai",
|
|
"pip_spec": "openai>=1.0.0",
|
|
"description": "OpenAI API SDK (also used for Grok, LM Studio, etc.)",
|
|
},
|
|
"pyyaml": {
|
|
"import_name": "yaml",
|
|
"pip_spec": "pyyaml>=6.0",
|
|
"description": "YAML configuration parser",
|
|
},
|
|
"requests": {
|
|
"import_name": "requests",
|
|
"pip_spec": "requests>=2.28.0",
|
|
"description": "HTTP library (for Ollama API)",
|
|
},
|
|
"typing_extensions": {
|
|
"import_name": "typing_extensions",
|
|
"pip_spec": "typing_extensions>=4.0.0",
|
|
"description": "Extended typing support",
|
|
},
|
|
# Tagger dependencies - Core
|
|
"onnxruntime": {
|
|
"import_name": "onnxruntime",
|
|
"pip_spec": "onnxruntime>=1.16.0",
|
|
"description": "ONNX Runtime for WD14/PixAI taggers",
|
|
},
|
|
"huggingface_hub": {
|
|
"import_name": "huggingface_hub",
|
|
"pip_spec": "huggingface_hub>=0.20.0",
|
|
"description": "HuggingFace Hub for model downloads",
|
|
},
|
|
"opencv-python": {
|
|
"import_name": "cv2",
|
|
"pip_spec": "opencv-python>=4.8.0",
|
|
"description": "OpenCV for image processing and face detection",
|
|
},
|
|
# Tagger dependencies - ML Models
|
|
"transformers": {
|
|
"import_name": "transformers",
|
|
"pip_spec": "transformers>=4.36.0",
|
|
"description": "HuggingFace Transformers for CLIP, JoyTag, etc.",
|
|
},
|
|
"scikit-learn": {
|
|
"import_name": "sklearn",
|
|
"pip_spec": "scikit-learn>=1.3.0",
|
|
"description": "Scikit-learn for color clustering",
|
|
},
|
|
"safetensors": {
|
|
"import_name": "safetensors",
|
|
"pip_spec": "safetensors>=0.4.0",
|
|
"description": "Safe model weight format",
|
|
},
|
|
# NudeNet for body detection
|
|
"nudenet": {
|
|
"import_name": "nudenet",
|
|
"pip_spec": "nudenet>=3.0.0",
|
|
"description": "NudeNet for body part detection",
|
|
},
|
|
# MediaPipe for pose detection (lightweight alternative)
|
|
"mediapipe": {
|
|
"import_name": "mediapipe",
|
|
"pip_spec": "mediapipe>=0.10.0",
|
|
"description": "MediaPipe for pose detection",
|
|
},
|
|
}
|
|
|
|
python_exe = sys.executable
|
|
install_count = 0
|
|
|
|
# Check and install required dependencies
|
|
for pkg_name, pkg_info in dependencies.items():
|
|
import_name = pkg_info["import_name"]
|
|
pip_spec = pkg_info["pip_spec"]
|
|
description = pkg_info["description"]
|
|
|
|
if importlib.util.find_spec(import_name) is not None:
|
|
_dependency_status[pkg_name] = "installed"
|
|
else:
|
|
# Try to install
|
|
print(f"[SID Photography Toolkit] Installing {pkg_name}: {description}...")
|
|
try:
|
|
subprocess.check_call(
|
|
[python_exe, "-m", "pip", "install", pip_spec],
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.PIPE
|
|
)
|
|
_dependency_status[pkg_name] = "just_installed"
|
|
install_count += 1
|
|
print(f"[SID Photography Toolkit] {pkg_name} installed successfully")
|
|
except subprocess.CalledProcessError as e:
|
|
_dependency_status[pkg_name] = "failed"
|
|
print(f"[SID Photography Toolkit] Failed to install {pkg_name}: {e}")
|
|
|
|
if install_count > 0:
|
|
print(f"[SID Photography Toolkit] Installed {install_count} new dependencies")
|
|
|
|
|
|
# Run dependency check on module load
|
|
print("[SID Photography Toolkit] Checking dependencies...")
|
|
check_and_install_dependencies()
|
|
|
|
# Try to import V3 API (may not be available in older ComfyUI versions)
|
|
try:
|
|
from typing_extensions import override
|
|
from comfy_api.latest import ComfyExtension, io
|
|
_v3_api_available = True
|
|
except ImportError:
|
|
_v3_api_available = False
|
|
print("[SID Photography Toolkit] V3 API not available, using V1 fallback")
|
|
|
|
# Import new Prompt Generator (modular implementation) - V1 style
|
|
from .core.prompt_generator.generator import SID_PromptGenerator
|
|
|
|
# Import LLM model type for V1 wrappers
|
|
from .llm_providers.llm_model_type import LLMModelConfig
|
|
|
|
# V3 API imports (conditional)
|
|
if _v3_api_available:
|
|
# Import LLM provider nodes (unified cloud API + local models)
|
|
from .llm_providers.sid_llm_api import SID_LLM_API
|
|
from .llm_providers.sid_llm_local import SID_LLM_Local
|
|
|
|
# Import Z-Image Prompt Generator (unified node)
|
|
from .sid_zimage_prompt_generator import SID_ZImagePromptGenerator
|
|
|
|
class SIDPhotographyToolkitExtension(ComfyExtension):
|
|
"""
|
|
Extension class for SID Photography Toolkit.
|
|
Registers SID_ prefixed LLM nodes with ComfyUI.
|
|
"""
|
|
|
|
@override
|
|
async def get_node_list(self) -> list[type[io.ComfyNode]]:
|
|
"""
|
|
Return list of all nodes in this extension.
|
|
"""
|
|
return [
|
|
SID_LLM_API, # Cloud LLM (Anthropic, OpenAI, Gemini, Grok, Ollama, LM Studio)
|
|
SID_LLM_Local, # Local models (Florence-2, Moondream2, SmolVLM, Phi-3.5, QwenVL)
|
|
SID_ZImagePromptGenerator, # Z-Image prompt generator (auto Single-Shot/Agentic)
|
|
]
|
|
|
|
|
|
# =============================================================================
|
|
# V1-Style LLM Node Wrappers (fallback for older ComfyUI versions)
|
|
# =============================================================================
|
|
|
|
class SID_LLM_API_V1:
|
|
"""
|
|
V1-style wrapper for SID_LLM_API.
|
|
Cloud LLM provider supporting Anthropic, OpenAI, Gemini, Grok, Groq, etc.
|
|
"""
|
|
|
|
CATEGORY = "SID Photography Toolkit/LLM Providers"
|
|
RETURN_TYPES = ("LLM_MODEL",)
|
|
RETURN_NAMES = ("llm_model",)
|
|
FUNCTION = "execute"
|
|
|
|
# Provider configurations
|
|
PROVIDERS = {
|
|
"Anthropic": {
|
|
"api_url": "https://api.anthropic.com",
|
|
"models": ["claude-sonnet-4-5-20250929", "claude-haiku-4-5-20251001", "claude-opus-4-5-20251101"],
|
|
"default": "claude-sonnet-4-5-20250929",
|
|
},
|
|
"OpenAI": {
|
|
"api_url": "https://api.openai.com/v1",
|
|
"models": ["gpt-4.1", "gpt-4.1-mini", "gpt-4o", "gpt-4o-mini"],
|
|
"default": "gpt-4o",
|
|
},
|
|
"Google Gemini": {
|
|
"api_url": "https://generativelanguage.googleapis.com/v1beta",
|
|
"models": ["gemini-2.5-flash", "gemini-2.5-pro", "gemini-2.0-flash"],
|
|
"default": "gemini-2.5-flash",
|
|
},
|
|
"Groq (Free)": {
|
|
"api_url": "https://api.groq.com/openai/v1",
|
|
"models": ["meta-llama/llama-4-scout-17b-16e-instruct", "meta-llama/llama-4-maverick-17b-128e-instruct"],
|
|
"default": "meta-llama/llama-4-scout-17b-16e-instruct",
|
|
},
|
|
"Together AI": {
|
|
"api_url": "https://api.together.xyz/v1",
|
|
"models": ["meta-llama/Llama-Vision-Free", "meta-llama/Llama-3.2-11B-Vision-Instruct-Turbo"],
|
|
"default": "meta-llama/Llama-Vision-Free",
|
|
},
|
|
"OpenRouter": {
|
|
"api_url": "https://openrouter.ai/api/v1",
|
|
"models": ["google/gemini-2.0-flash-exp:free", "google/gemma-3-27b-it:free"],
|
|
"default": "google/gemini-2.0-flash-exp:free",
|
|
},
|
|
"Ollama (Local)": {
|
|
"api_url": "http://localhost:11434/v1",
|
|
"models": ["(use custom_model)"],
|
|
"default": "(use custom_model)",
|
|
},
|
|
"LM Studio (Local)": {
|
|
"api_url": "http://localhost:1234/v1",
|
|
"models": ["(use custom_model)"],
|
|
"default": "(use custom_model)",
|
|
},
|
|
}
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
providers = list(cls.PROVIDERS.keys())
|
|
all_models = []
|
|
for p, cfg in cls.PROVIDERS.items():
|
|
for m in cfg["models"]:
|
|
all_models.append(f"[{p}] {m}")
|
|
|
|
return {
|
|
"required": {
|
|
"provider": (providers, {"default": "Anthropic"}),
|
|
"model": (all_models, {"default": "[Anthropic] claude-sonnet-4-5-20250929"}),
|
|
"api_key": ("STRING", {"default": "", "multiline": False}),
|
|
},
|
|
"optional": {
|
|
"custom_model": ("STRING", {"default": "", "tooltip": "Override model name"}),
|
|
"api_url": ("STRING", {"default": "", "tooltip": "Custom API URL"}),
|
|
"temperature": ("FLOAT", {"default": 0.3, "min": 0.0, "max": 2.0, "step": 0.1}),
|
|
"max_tokens": ("INT", {"default": 2048, "min": 128, "max": 32000}),
|
|
},
|
|
}
|
|
|
|
def execute(self, provider, model, api_key, custom_model="", api_url="", temperature=0.3, max_tokens=2048):
|
|
# Parse model selection
|
|
if model.startswith("["):
|
|
end = model.find("]")
|
|
model_provider = model[1:end]
|
|
model_name = model[end + 2:]
|
|
else:
|
|
model_provider = provider
|
|
model_name = model
|
|
|
|
# Use custom model if provided
|
|
if custom_model.strip():
|
|
model_name = custom_model.strip()
|
|
|
|
# Determine API URL
|
|
if api_url.strip():
|
|
actual_url = api_url.strip()
|
|
else:
|
|
actual_url = self.PROVIDERS.get(provider, {}).get("api_url", "")
|
|
|
|
# Determine provider name for API calls
|
|
provider_map = {
|
|
"Anthropic": "anthropic",
|
|
"OpenAI": "openai",
|
|
"Google Gemini": "gemini",
|
|
"Groq (Free)": "groq",
|
|
"Together AI": "together",
|
|
"OpenRouter": "openrouter",
|
|
"Ollama (Local)": "ollama",
|
|
"LM Studio (Local)": "lmstudio",
|
|
}
|
|
provider_name = provider_map.get(provider, "openai")
|
|
|
|
# Check for reasoning support
|
|
reasoning_models = ["claude-sonnet-4", "claude-opus-4", "o1", "o3", "deepseek-r1"]
|
|
supports_reasoning = any(rm in model_name.lower() for rm in reasoning_models)
|
|
|
|
config = LLMModelConfig(
|
|
provider=provider_name,
|
|
model=model_name,
|
|
api_key=api_key.strip(),
|
|
api_url=actual_url,
|
|
max_tokens=max_tokens,
|
|
temperature=temperature,
|
|
supports_vision=True,
|
|
supports_system_prompt=True,
|
|
supports_reasoning=supports_reasoning,
|
|
)
|
|
|
|
print(f"[SID_LLM_API] Configured: {provider} - {model_name}")
|
|
return (config,)
|
|
|
|
|
|
class SID_LLM_Local_V1:
|
|
"""
|
|
V1-style wrapper for SID_LLM_Local.
|
|
Local vision models with VRAM management.
|
|
"""
|
|
|
|
CATEGORY = "SID Photography Toolkit/LLM Providers"
|
|
RETURN_TYPES = ("LLM_MODEL",)
|
|
RETURN_NAMES = ("llm_model",)
|
|
FUNCTION = "execute"
|
|
|
|
LOCAL_MODELS = [
|
|
"Qwen3-VL-2B-Abliterated",
|
|
"Qwen3-VL-2B-Instruct",
|
|
"Qwen3-VL-4B-Abliterated",
|
|
"Qwen3-VL-4B-Instruct",
|
|
"Qwen2.5-VL-3B-Instruct",
|
|
"Qwen2.5-VL-7B-Abliterated",
|
|
"Qwen2.5-VL-7B-Instruct",
|
|
"Qwen3-VL-8B-Abliterated",
|
|
"Qwen3-VL-8B-Instruct",
|
|
]
|
|
|
|
MODEL_REPOS = {
|
|
"Qwen3-VL-2B-Abliterated": "huihui-ai/Huihui-Qwen3-VL-2B-Instruct-abliterated",
|
|
"Qwen3-VL-2B-Instruct": "Qwen/Qwen3-VL-2B-Instruct",
|
|
"Qwen3-VL-4B-Abliterated": "huihui-ai/Huihui-Qwen3-VL-4B-Instruct-abliterated",
|
|
"Qwen3-VL-4B-Instruct": "Qwen/Qwen3-VL-4B-Instruct",
|
|
"Qwen2.5-VL-3B-Instruct": "Qwen/Qwen2.5-VL-3B-Instruct",
|
|
"Qwen2.5-VL-7B-Abliterated": "huihui-ai/Qwen2.5-VL-7B-Instruct-abliterated",
|
|
"Qwen2.5-VL-7B-Instruct": "Qwen/Qwen2.5-VL-7B-Instruct",
|
|
"Qwen3-VL-8B-Abliterated": "huihui-ai/Huihui-Qwen3-VL-8B-Instruct-abliterated",
|
|
"Qwen3-VL-8B-Instruct": "Qwen/Qwen3-VL-8B-Instruct",
|
|
}
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"model": (cls.LOCAL_MODELS, {"default": "Qwen3-VL-4B-Abliterated"}),
|
|
},
|
|
"optional": {
|
|
"quantization": (["Auto", "4-bit", "8-bit", "FP16"], {"default": "Auto"}),
|
|
"device": (["auto", "cuda", "cpu"], {"default": "auto"}),
|
|
"temperature": ("FLOAT", {"default": 0.3, "min": 0.0, "max": 1.0, "step": 0.1}),
|
|
"max_tokens": ("INT", {"default": 1024, "min": 128, "max": 4096}),
|
|
"keep_model_loaded": ("BOOLEAN", {"default": True}),
|
|
"hf_token": ("STRING", {"default": "", "tooltip": "HuggingFace token for gated models"}),
|
|
},
|
|
}
|
|
|
|
def execute(self, model, quantization="Auto", device="auto", temperature=0.3,
|
|
max_tokens=1024, keep_model_loaded=True, hf_token=""):
|
|
|
|
# Map quantization
|
|
quant_map = {"Auto": "4-bit", "4-bit": "4-bit", "8-bit": "8-bit", "FP16": "None (FP16)"}
|
|
quant = quant_map.get(quantization, "4-bit")
|
|
|
|
repo_id = self.MODEL_REPOS.get(model, "")
|
|
|
|
config = LLMModelConfig(
|
|
provider="local",
|
|
model=model,
|
|
api_key="",
|
|
api_url="",
|
|
max_tokens=max_tokens,
|
|
temperature=temperature,
|
|
supports_vision=True,
|
|
supports_system_prompt=True,
|
|
supports_reasoning=False,
|
|
extra_params={
|
|
"quantization": quant,
|
|
"device": device,
|
|
"attention_mode": "auto",
|
|
"keep_model_loaded": keep_model_loaded,
|
|
"repo_id": repo_id,
|
|
"family": "qwenvl",
|
|
"repetition_penalty": 1.2,
|
|
"top_p": 0.9,
|
|
"use_torch_compile": False,
|
|
"hf_token": hf_token if hf_token else None,
|
|
},
|
|
)
|
|
|
|
print(f"[SID_LLM_Local] Configured: {model} ({quant})")
|
|
return (config,)
|
|
|
|
|
|
class SID_LLM_Local_Text_V1:
|
|
"""
|
|
V1-style wrapper for SID_LLM_Local_Text.
|
|
Local text-only models for prompt modification (no vision).
|
|
Optimized for text generation tasks like prompt rewriting.
|
|
"""
|
|
|
|
CATEGORY = "SID Photography Toolkit/LLM Providers"
|
|
RETURN_TYPES = ("LLM_MODEL",)
|
|
RETURN_NAMES = ("llm_model",)
|
|
FUNCTION = "execute"
|
|
|
|
# Text-only Qwen models (smaller, faster for text tasks)
|
|
LOCAL_MODELS = [
|
|
# Qwen2.5 Text Models
|
|
"Qwen2.5-1.5B-Instruct",
|
|
"Qwen2.5-3B-Instruct",
|
|
"Qwen2.5-7B-Instruct",
|
|
"Qwen2.5-14B-Instruct",
|
|
# Qwen3 Text Models
|
|
"Qwen3-1.7B",
|
|
"Qwen3-4B",
|
|
"Qwen3-8B",
|
|
# Abliterated variants (uncensored)
|
|
"Qwen2.5-7B-Abliterated",
|
|
"Qwen2.5-14B-Abliterated",
|
|
]
|
|
|
|
MODEL_REPOS = {
|
|
# Qwen2.5 Text
|
|
"Qwen2.5-1.5B-Instruct": "Qwen/Qwen2.5-1.5B-Instruct",
|
|
"Qwen2.5-3B-Instruct": "Qwen/Qwen2.5-3B-Instruct",
|
|
"Qwen2.5-7B-Instruct": "Qwen/Qwen2.5-7B-Instruct",
|
|
"Qwen2.5-14B-Instruct": "Qwen/Qwen2.5-14B-Instruct",
|
|
# Qwen3 Text
|
|
"Qwen3-1.7B": "Qwen/Qwen3-1.7B",
|
|
"Qwen3-4B": "Qwen/Qwen3-4B",
|
|
"Qwen3-8B": "Qwen/Qwen3-8B",
|
|
# Abliterated
|
|
"Qwen2.5-7B-Abliterated": "huihui-ai/Qwen2.5-7B-Instruct-abliterated",
|
|
"Qwen2.5-14B-Abliterated": "huihui-ai/Qwen2.5-14B-Instruct-abliterated",
|
|
}
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"model": (cls.LOCAL_MODELS, {"default": "Qwen2.5-3B-Instruct"}),
|
|
},
|
|
"optional": {
|
|
"quantization": (["Auto", "4-bit", "8-bit", "FP16"], {"default": "Auto"}),
|
|
"device": (["auto", "cuda", "cpu"], {"default": "auto"}),
|
|
"temperature": ("FLOAT", {"default": 0.7, "min": 0.0, "max": 2.0, "step": 0.1}),
|
|
"max_tokens": ("INT", {"default": 2048, "min": 128, "max": 8192}),
|
|
"keep_model_loaded": ("BOOLEAN", {"default": True}),
|
|
"hf_token": ("STRING", {"default": "", "tooltip": "HuggingFace token for gated models"}),
|
|
},
|
|
}
|
|
|
|
def execute(self, model, quantization="Auto", device="auto", temperature=0.7,
|
|
max_tokens=2048, keep_model_loaded=True, hf_token=""):
|
|
|
|
# Map quantization
|
|
quant_map = {"Auto": "4-bit", "4-bit": "4-bit", "8-bit": "8-bit", "FP16": "None (FP16)"}
|
|
quant = quant_map.get(quantization, "4-bit")
|
|
|
|
repo_id = self.MODEL_REPOS.get(model, "")
|
|
|
|
config = LLMModelConfig(
|
|
provider="local_text",
|
|
model=model,
|
|
api_key="",
|
|
api_url="",
|
|
max_tokens=max_tokens,
|
|
temperature=temperature,
|
|
supports_vision=False, # Text-only model
|
|
supports_system_prompt=True,
|
|
supports_reasoning=False,
|
|
extra_params={
|
|
"quantization": quant,
|
|
"device": device,
|
|
"attention_mode": "auto",
|
|
"keep_model_loaded": keep_model_loaded,
|
|
"repo_id": repo_id,
|
|
"family": "qwen",
|
|
"repetition_penalty": 1.1,
|
|
"top_p": 0.9,
|
|
"use_torch_compile": False,
|
|
"hf_token": hf_token if hf_token else None,
|
|
},
|
|
)
|
|
|
|
print(f"[SID_LLM_Local_Text] Configured: {model} ({quant})")
|
|
return (config,)
|
|
|
|
|
|
def print_welcome_message():
|
|
"""
|
|
Print welcome message with dependency status and available nodes.
|
|
"""
|
|
global _dependency_status
|
|
|
|
# Header
|
|
print("")
|
|
print("=" * 65)
|
|
print(" SID Photography Toolkit for ComfyUI")
|
|
print(f" Version: {__version__}")
|
|
print(" Author: Siddhartha Lahiri")
|
|
print("=" * 65)
|
|
|
|
# Dependencies status
|
|
print("")
|
|
print(" Dependencies:")
|
|
|
|
# Required dependencies
|
|
required_pkgs = ["anthropic", "openai", "pyyaml", "requests", "typing_extensions"]
|
|
pkg_descriptions = {
|
|
"anthropic": "Anthropic Claude API",
|
|
"openai": "OpenAI/Grok/LM Studio API",
|
|
"pyyaml": "YAML config parser",
|
|
"requests": "HTTP library (Ollama)",
|
|
"typing_extensions": "Type hints",
|
|
}
|
|
|
|
for pkg in required_pkgs:
|
|
status = _dependency_status.get(pkg, "unknown")
|
|
desc = pkg_descriptions.get(pkg, pkg)
|
|
if status == "installed":
|
|
print(f" [OK] {desc}")
|
|
elif status == "just_installed":
|
|
print(f" [INSTALLED] {desc} (just installed)")
|
|
elif status == "failed":
|
|
print(f" [FAILED] {desc} - please install manually")
|
|
else:
|
|
print(f" [?] {desc}")
|
|
|
|
# Available nodes
|
|
print("")
|
|
print("-" * 65)
|
|
print(" Available Nodes:")
|
|
print("")
|
|
print(" LLM Providers:")
|
|
print(" - SID_LLM_API Cloud APIs (Anthropic, OpenAI, Gemini, Grok, Ollama, LM Studio)")
|
|
print(" - SID_LLM_Local Local models (Florence-2, Moondream, SmolVLM, Phi-3.5, QwenVL)")
|
|
print("")
|
|
print(" Prompt Generators:")
|
|
print(" - SID_ZImagePromptGenerator Z-Image prompts (auto Single-Shot/Agentic)")
|
|
print(" - SID_PromptGenerator New modular prompt generator (4.2.1)")
|
|
|
|
print("")
|
|
print("=" * 65)
|
|
print("")
|
|
|
|
|
|
# Import User Prompt node
|
|
from .core.prompt_generator.nodes.user_prompt import SID_UserPrompt
|
|
|
|
# Import Prompt Modifier node
|
|
from .core.prompt_generator.nodes.prompt_modifier import SID_PromptModifier
|
|
|
|
# V1-style node exports (always available, works with all ComfyUI versions)
|
|
NODE_CLASS_MAPPINGS = {
|
|
"SID_PromptGenerator": SID_PromptGenerator,
|
|
"SID_UserPrompt": SID_UserPrompt,
|
|
"SID_PromptModifier": SID_PromptModifier,
|
|
"SID_LLM_API": SID_LLM_API_V1,
|
|
"SID_LLM_Local": SID_LLM_Local_V1,
|
|
"SID_LLM_Local_Text": SID_LLM_Local_Text_V1,
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"SID_PromptGenerator": "SID Prompt Generator",
|
|
"SID_UserPrompt": "SID User Prompt",
|
|
"SID_PromptModifier": "SID Prompt Modifier",
|
|
"SID_LLM_API": "SID LLM API",
|
|
"SID_LLM_Local": "SID LLM Local",
|
|
"SID_LLM_Local_Text": "SID LLM Local Text",
|
|
}
|
|
|
|
# Web directory for JavaScript extensions (node colors, widget control)
|
|
WEB_DIRECTORY = "./web"
|
|
|
|
|
|
# V3 entrypoint (only if V3 API available)
|
|
if _v3_api_available:
|
|
async def comfy_entrypoint() -> SIDPhotographyToolkitExtension:
|
|
"""
|
|
ComfyUI calls this function to load the extension and its nodes.
|
|
This is the entry point for the custom node package.
|
|
"""
|
|
print_welcome_message()
|
|
return SIDPhotographyToolkitExtension()
|
|
else:
|
|
# Print welcome message for V1-only mode
|
|
print_welcome_message()
|