269 lines
11 KiB
Python
269 lines
11 KiB
Python
# Constants and shared data for all nodes
|
|
|
|
import os
|
|
import json
|
|
import codecs
|
|
|
|
# Category for all nodes
|
|
CUSTOM_CATEGORY = "comfyui_dagthomas"
|
|
|
|
def load_json_file(file_name):
|
|
"""Load data from a JSON file in the data directory"""
|
|
file_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "data", file_name)
|
|
with open(file_path, "r") as file:
|
|
return json.load(file)
|
|
|
|
def load_all_json_files(base_path):
|
|
"""Load all JSON files from a directory recursively"""
|
|
data = {}
|
|
for root, dirs, files in os.walk(base_path):
|
|
for file in files:
|
|
if file.endswith(".json"):
|
|
file_path = os.path.join(root, file)
|
|
relative_path = os.path.relpath(file_path, base_path)
|
|
key = os.path.splitext(relative_path)[0].replace(os.path.sep, "_")
|
|
try:
|
|
with codecs.open(file_path, "r", "utf-8") as f:
|
|
data[key] = json.load(f)
|
|
except UnicodeDecodeError:
|
|
print(
|
|
f"Warning: Unable to decode file {file_path} with UTF-8 encoding. Skipping this file."
|
|
)
|
|
except json.JSONDecodeError:
|
|
print(
|
|
f"Warning: Invalid JSON in file {file_path}. Skipping this file."
|
|
)
|
|
return data
|
|
|
|
# Load base directory paths
|
|
base_dir = os.path.dirname(os.path.dirname(__file__))
|
|
next_dir = os.path.join(base_dir, "data", "next")
|
|
prompt_dir = os.path.join(base_dir, "data", "custom_prompts")
|
|
|
|
# Load all JSON data
|
|
all_data = load_all_json_files(next_dir)
|
|
|
|
# Load individual JSON files
|
|
ARTFORM = load_json_file("artform.json")
|
|
PHOTO_FRAMING = load_json_file("photo_framing.json")
|
|
PHOTO_TYPE = load_json_file("photo_type.json")
|
|
DEFAULT_TAGS = load_json_file("default_tags.json")
|
|
ROLES = load_json_file("roles.json")
|
|
HAIRSTYLES = load_json_file("hairstyles.json")
|
|
ADDITIONAL_DETAILS = load_json_file("additional_details.json")
|
|
PHOTOGRAPHY_STYLES = load_json_file("photography_styles.json")
|
|
DEVICE = load_json_file("device.json")
|
|
PHOTOGRAPHER = load_json_file("photographer.json")
|
|
ARTIST = load_json_file("artist.json")
|
|
DIGITAL_ARTFORM = load_json_file("digital_artform.json")
|
|
PLACE = load_json_file("place.json")
|
|
LIGHTING = load_json_file("lighting.json")
|
|
CLOTHING = load_json_file("clothing.json")
|
|
COMPOSITION = load_json_file("composition.json")
|
|
POSE = load_json_file("pose.json")
|
|
BACKGROUND = load_json_file("background.json")
|
|
BODY_TYPES = load_json_file("body_types.json")
|
|
|
|
# Load model configurations
|
|
models_file = os.path.join(base_dir, "data", "gemini_models.json")
|
|
with open(models_file, 'r') as f:
|
|
models_data = json.load(f)
|
|
gemini_models = models_data.get('models', [])
|
|
|
|
gpt_models_file = os.path.join(base_dir, "data", "gpt_models.json")
|
|
with open(gpt_models_file, 'r') as f:
|
|
gpt_models_data = json.load(f)
|
|
gpt_models = gpt_models_data.get('models', [])
|
|
|
|
grok_models_file = os.path.join(base_dir, "data", "grok_models.json")
|
|
with open(grok_models_file, 'r') as f:
|
|
grok_models_data = json.load(f)
|
|
grok_models = grok_models_data.get('models', [])
|
|
|
|
claude_models_file = os.path.join(base_dir, "data", "claude_models.json")
|
|
with open(claude_models_file, 'r') as f:
|
|
claude_models_data = json.load(f)
|
|
claude_models = claude_models_data.get('models', [])
|
|
|
|
def load_qwenvl_models():
|
|
"""Load QwenVL models from JSON file, with optional private models addition."""
|
|
models = []
|
|
|
|
# Load main models file
|
|
qwenvl_models_file = os.path.join(base_dir, "data", "qwenvl_models.json")
|
|
try:
|
|
with open(qwenvl_models_file, 'r') as f:
|
|
data = json.load(f)
|
|
models.extend(data.get('models', []))
|
|
print(f"Loaded {len(data.get('models', []))} QwenVL models from qwenvl_models.json")
|
|
except FileNotFoundError:
|
|
print(f"Warning: qwenvl_models.json not found")
|
|
except Exception as e:
|
|
print(f"Warning: Could not load qwenvl_models.json: {e}")
|
|
|
|
# Look for private model files (private_*qwenvl*.json pattern in data folder)
|
|
data_dir = os.path.join(base_dir, "data")
|
|
try:
|
|
for filename in os.listdir(data_dir):
|
|
# Match files starting with "private_" and containing "qwenvl" (case-insensitive)
|
|
if filename.startswith("private_") and "qwenvl" in filename.lower() and filename.endswith(".json"):
|
|
private_file = os.path.join(data_dir, filename)
|
|
try:
|
|
with open(private_file, 'r') as f:
|
|
private_data = json.load(f)
|
|
private_models = private_data.get('models', [])
|
|
# Add models that aren't already in the list
|
|
for model in private_models:
|
|
if model not in models:
|
|
models.append(model)
|
|
print(f"Loaded {len(private_models)} private QwenVL models from {filename}")
|
|
except Exception as e:
|
|
print(f"Warning: Could not load {filename}: {e}")
|
|
except Exception as e:
|
|
print(f"Warning: Could not scan for private QwenVL model files: {e}")
|
|
|
|
# Fallback if no models loaded
|
|
if not models:
|
|
models = ["Qwen3-VL-4B-Instruct", "Qwen3-VL-2B-Instruct"]
|
|
print("Using fallback QwenVL models")
|
|
|
|
return models
|
|
|
|
qwenvl_models = load_qwenvl_models()
|
|
|
|
def load_groq_models_from_file():
|
|
"""Load Groq models from JSON file."""
|
|
try:
|
|
groq_models_file = os.path.join(base_dir, "data", "groq_models.json")
|
|
with open(groq_models_file, 'r') as f:
|
|
data = json.load(f)
|
|
|
|
# Support both old format (models array) and new format (text_models/vision_models)
|
|
if 'text_models' in data:
|
|
text_models = data.get('text_models', [])
|
|
vision_models = data.get('vision_models', [])
|
|
print(f"Loaded {len(text_models)} text models and {len(vision_models)} vision models from JSON file")
|
|
return text_models, vision_models
|
|
else:
|
|
# Old format compatibility
|
|
models = data.get('models', [])
|
|
print(f"Loaded {len(models)} Groq models from JSON file")
|
|
return models, []
|
|
except Exception as e:
|
|
print(f"Warning: Could not load Groq models from file: {e}")
|
|
# Return minimal defaults
|
|
default_text = ["llama-3.3-70b-versatile", "llama-3.1-8b-instant", "groq/compound"]
|
|
default_vision = ["llama-4-scout-17b-16e-instruct", "meta-llama/llama-4-scout-17b-16e-instruct"]
|
|
return default_text, default_vision
|
|
|
|
def get_groq_models_from_api():
|
|
"""
|
|
Fetch available Groq models dynamically from API.
|
|
Returns (text_models, vision_models) tuple.
|
|
"""
|
|
groq_api_key = os.environ.get("GROQ_API_KEY")
|
|
if not groq_api_key:
|
|
return None, None
|
|
|
|
try:
|
|
import requests
|
|
response = requests.get(
|
|
"https://api.groq.com/openai/v1/models",
|
|
headers={
|
|
"Authorization": f"Bearer {groq_api_key}",
|
|
"Content-Type": "application/json"
|
|
},
|
|
timeout=5
|
|
)
|
|
if response.status_code == 200:
|
|
models_data = response.json()
|
|
all_models = [model['id'] for model in models_data.get('data', [])]
|
|
|
|
# Filter out non-text models (audio, tts, guards, etc.)
|
|
text_models = [m for m in all_models if not any(x in m.lower() for x in ['whisper', 'tts', 'guard'])]
|
|
# Vision models have 'vision' in the name or certain model types
|
|
vision_models = [m for m in all_models if 'vision' in m.lower()]
|
|
|
|
if text_models:
|
|
print(f"Fetched {len(text_models)} text models and {len(vision_models)} vision models from Groq API")
|
|
return sorted(text_models), sorted(vision_models)
|
|
except Exception as e:
|
|
print(f"Warning: Could not fetch Groq models from API: {e}")
|
|
|
|
return None, None
|
|
|
|
# Load Groq models - try API first, then fall back to JSON file
|
|
groq_text_models, groq_vision_models = get_groq_models_from_api()
|
|
if groq_text_models is None:
|
|
groq_text_models, groq_vision_models = load_groq_models_from_file()
|
|
|
|
# For backward compatibility, provide 'groq_models' as the text models list
|
|
groq_models = groq_text_models
|
|
|
|
# Legacy constants that were in sdxl_utility.py (now moved to utils)
|
|
def tensor2pil(t_image):
|
|
"""Legacy tensor to PIL conversion function"""
|
|
import torch
|
|
import numpy as np
|
|
from PIL import Image
|
|
return Image.fromarray(np.clip(255.0 * t_image.cpu().numpy().squeeze(), 0, 255).astype(np.uint8))
|
|
|
|
# Cinematic terms for prompt enhancement
|
|
CINEMATIC_TERMS = {
|
|
"visual_style": [
|
|
"cinematic", "dramatic", "moody", "atmospheric", "ethereal", "surreal",
|
|
"photorealistic", "hyperrealistic", "stylized", "abstract", "minimalist",
|
|
"maximalist", "vintage", "retro", "futuristic", "cyberpunk", "steampunk"
|
|
],
|
|
"lighting_type": [
|
|
"soft lighting", "hard lighting", "dramatic lighting", "natural lighting",
|
|
"studio lighting", "golden hour", "blue hour", "twilight", "dawn",
|
|
"dusk", "overcast", "backlit", "rim lighting", "key lighting"
|
|
],
|
|
"light_source": [
|
|
"sunlight", "moonlight", "candlelight", "firelight", "neon lights",
|
|
"street lamps", "window light", "overhead lighting", "side lighting",
|
|
"bottom lighting", "practical lights", "ambient lighting"
|
|
],
|
|
"camera_angle": [
|
|
"low angle", "high angle", "eye level", "bird's eye view", "worm's eye view",
|
|
"dutch angle", "overhead shot", "ground level", "three-quarter view"
|
|
],
|
|
"shot_size": [
|
|
"extreme wide shot", "wide shot", "medium wide shot", "medium shot",
|
|
"medium close-up", "close-up", "extreme close-up", "establishing shot"
|
|
],
|
|
"lens_type": [
|
|
"wide-angle lens", "telephoto lens", "macro lens", "fisheye lens",
|
|
"portrait lens", "zoom lens", "prime lens", "tilt-shift lens"
|
|
],
|
|
"color_tone": [
|
|
"warm tones", "cool tones", "desaturated", "vibrant", "monochromatic",
|
|
"high contrast", "low contrast", "sepia", "black and white", "duotone"
|
|
],
|
|
"camera_movement": [
|
|
"static shot", "pan left", "pan right", "tilt up", "tilt down",
|
|
"dolly in", "dolly out", "tracking shot", "crane shot", "handheld"
|
|
],
|
|
"time_of_day": [
|
|
"sunrise", "morning", "midday", "afternoon", "sunset", "night",
|
|
"midnight", "pre-dawn", "twilight", "golden hour", "blue hour"
|
|
],
|
|
"visual_effects": [
|
|
"depth of field", "bokeh", "lens flare", "motion blur", "film grain",
|
|
"vignette", "chromatic aberration", "light rays", "fog", "mist"
|
|
],
|
|
"composition": [
|
|
"rule of thirds", "centered composition", "symmetrical", "asymmetrical",
|
|
"leading lines", "framing", "negative space", "foreground focus"
|
|
],
|
|
"motion": [
|
|
"static pose", "walking", "running", "jumping", "dancing", "flowing movement",
|
|
"dramatic gesture", "subtle movement", "frozen action", "dynamic pose"
|
|
],
|
|
"character_emotion": [
|
|
"confident", "mysterious", "contemplative", "joyful", "melancholic",
|
|
"determined", "serene", "intense", "playful", "stoic", "passionate"
|
|
]
|
|
}
|