# ComfyUI version of CreaPrompt by Jicé Deb import random import json import os import base64 from aiohttp import web from server import PromptServer script_directory = os.path.dirname(__file__) folder_path = os.path.join(script_directory, "csv" ) folder_path_1 = os.path.join(script_directory, "csv1" ) folder_path_2 = os.path.join(script_directory, "csv2" ) folder_path_3 = os.path.join(script_directory, "csv3" ) folder_path_4 = os.path.join(script_directory, "csv+weight" ) CSV_FOLDER = os.path.join(os.path.dirname(__file__), "csv") PRESET_FOLDER = os.path.join(os.path.dirname(__file__), "presets") app = PromptServer.instance.app async def preset_file(request): filename = request.match_info["filename"] path = os.path.join(PRESET_FOLDER, filename) if not os.path.isfile(path): return web.Response(status=404, text="Preset file not found.") try: with open(path, "r", encoding="utf-8") as f: content = f.read() return web.Response(text=content) except Exception as e: return web.Response(status=500, text=f"Error reading preset: {e}") async def save_preset(request): try: data = await request.json() name = data.get("name", "").strip() content = data.get("content", "").strip() if not name or len(name) < 2: return web.Response(status=400, text="Nom de preset invalide.") filename = os.path.join(PRESET_FOLDER, f"{name}.txt") with open(filename, "w", encoding="utf-8") as f: f.write(content) #print(f"✅ Preset sauvegardé : {filename}") return web.Response(status=200, text="Preset saved.") except Exception as e: return web.Response(status=500, text=f"Erreur lors de la sauvegarde : {e}") async def csv_list(request): #print("📥 csv_list endpoint hit") try: files = [f for f in os.listdir(CSV_FOLDER) if f.endswith(".csv")] return web.json_response(sorted(files)) except Exception as e: return web.json_response({"error": str(e)}, status=500) async def csv_file(request): filename = request.match_info["filename"] path = os.path.join(CSV_FOLDER, filename) if not os.path.isfile(path): return web.Response(status=404, text="File not found.") try: with open(path, "r", encoding="utf-8") as f: content = f.read() return web.Response(text=content) except Exception as e: return web.Response(status=500, text=f"Error reading file: {e}") async def list_presets(request): try: files = [f for f in os.listdir(PRESET_FOLDER) if f.endswith(".txt")] return web.json_response(files) except Exception as e: return web.Response(status=500, text=f"Erreur lecture presets : {e}") async def delete_preset(request): filename = request.match_info["filename"] path = os.path.join(PRESET_FOLDER, filename) if not os.path.isfile(path): return web.Response(status=404, text="Preset file not found.") try: os.remove(path) #print(f"🗑️ Preset supprimé : {path}") return web.Response(text="Preset deleted.") except Exception as e: return web.Response(status=500, text=f"Error deleting preset: {e}") app.router.add_get("/custom_nodes/creaprompt/csv_list", csv_list) app.router.add_get("/custom_nodes/creaprompt/csv/{filename}", csv_file) app.router.add_get("/custom_nodes/creaprompt/presets/{filename}", preset_file) app.router.add_post("/custom_nodes/creaprompt/save_preset", save_preset) app.router.add_get("/custom_nodes/creaprompt/presets_list", list_presets) app.router.add_delete("/custom_nodes/creaprompt/delete_preset/{filename}", delete_preset) print("✅ creaprompt_api registering endpoints") def getfilename(folder): name = [] for filename in os.listdir(folder): if filename.endswith(".csv"): name.append(filename[3:-4]) return name def select_random_line_from_collection(collection_file=None): if collection_file and collection_file != COLLECTION_LEGACY_LABEL: file_path = os.path.join(collections_folder_path, collection_file + ".txt") if not os.path.isfile(file_path): file_path = os.path.join(folder_path, "collection.txt") else: file_path = os.path.join(folder_path, "collection.txt") with open(file_path, "r", encoding="utf-8") as file: lines = file.readlines() readline = random.choice(lines).strip() return readline collections_folder_path = os.path.join(script_directory, "collections") os.makedirs(collections_folder_path, exist_ok=True) COLLECTION_LEGACY_LABEL = "collection.txt (legacy)" def get_collection_files(): try: files = sorted(os.path.splitext(f)[0] for f in os.listdir(collections_folder_path) if f.lower().endswith(".txt")) except Exception as e: print(f"⚠️ CreaPrompt: cannot list collections folder: {e}") files = [] return files def select_random_line_from_csv_file(file, folder): chosen_lines = [] for filename in os.listdir(folder): if filename.endswith(".csv") and filename[3:-4] == file: file_path = os.path.join(folder, filename) with open(file_path, 'r', encoding='utf-8') as f: lines = f.readlines() if lines: chosen_lines.append(random.choice(lines).strip()) lines_chosed = "".join(chosen_lines) return lines_chosed # ============================================================================ # CreaPrompt Enhancer (imports lourds paresseux, rien n'est chargé au boot) # ============================================================================ ENHANCER_PRESETS_PATH = os.path.join(script_directory, "enhancer_presets.json") def load_enhancer_presets(): try: with open(ENHANCER_PRESETS_PATH, "r", encoding="utf-8") as f: return json.load(f) except Exception as e: print(f"⚠️ CreaPrompt Enhancer: presets load error: {e}") return {} ENHANCER_PRESETS = load_enhancer_presets() ENHANCER_PRESET_KEYS = list(ENHANCER_PRESETS.keys()) def enhancer_precision_options(): import importlib.util opts = ["fp16", "bf16"] if importlib.util.find_spec("bitsandbytes") is not None: opts = ["int4", "int8"] + opts return opts class CreaPromptEnhancerManager: _instance = None def __new__(cls): if cls._instance is None: cls._instance = super().__new__(cls) cls._instance.cache = {} return cls._instance def get(self, key): return self.cache.get(key) def set(self, key, model, processor): self.cache[key] = (model, processor) def unload(self, key): if key in self.cache: import gc import torch model, processor = self.cache.pop(key) del model del processor gc.collect() if torch.cuda.is_available(): torch.cuda.empty_cache() print(f"🧹 CreaPrompt Enhancer: model '{key}' unloaded") ENHANCER_MANAGER = CreaPromptEnhancerManager() def enhancer_load_model(model_name, precision): key = f"{model_name}_{precision}" cached = ENHANCER_MANAGER.get(key) if cached: return cached import torch import folder_paths from huggingface_hub import snapshot_download from transformers import AutoProcessor try: from transformers import Qwen3VLForConditionalGeneration as MODEL_CLASS except ImportError: from transformers import Qwen2VLForConditionalGeneration as MODEL_CLASS models_dir = os.path.join(folder_paths.models_dir, "LLM") local_path = os.path.join(models_dir, model_name.split("/")[-1]) if not os.path.isfile(os.path.join(local_path, "config.json")): print(f"⬇️ CreaPrompt Enhancer: downloading {model_name} to {local_path}...") snapshot_download(repo_id=model_name, local_dir=local_path) load_kwargs = {} if precision in ("int4", "int8"): from transformers import BitsAndBytesConfig if precision == "int4": load_kwargs["quantization_config"] = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_compute_dtype=torch.float16, bnb_4bit_use_double_quant=True, ) else: load_kwargs["quantization_config"] = BitsAndBytesConfig(load_in_8bit=True) load_kwargs["device_map"] = "auto" else: load_kwargs["torch_dtype"] = torch.bfloat16 if precision == "bf16" else torch.float16 print(f"⏳ CreaPrompt Enhancer: loading {model_name} ({precision})...") processor = AutoProcessor.from_pretrained(local_path, trust_remote_code=True) model = MODEL_CLASS.from_pretrained( local_path, trust_remote_code=True, low_cpu_mem_usage=True, **load_kwargs ) if precision not in ("int4", "int8"): import comfy.model_management model.to(comfy.model_management.get_torch_device()) ENHANCER_MANAGER.set(key, model, processor) print(f"✅ CreaPrompt Enhancer: model '{key}' loaded and cached") return model, processor def enhancer_tensor_to_pil(tensor, max_size=1024, max_frames=None): from PIL import Image import numpy as np if tensor is None: return [] total = tensor.shape[0] indices = range(total) if max_frames is not None and total > max_frames: step = total / max_frames indices = [int(i * step) for i in range(max_frames)] images = [] for i in indices: img = Image.fromarray((tensor[i].cpu().numpy() * 255.0).astype(np.uint8)) w, h = img.size if max(w, h) > max_size: ratio = max_size / max(w, h) img = img.resize((int(w * ratio), int(h * ratio)), Image.LANCZOS) images.append(img) return images def enhancer_describe_images(images, model_name, precision, seed): """Passe 1 : décrit chaque image individuellement (fiable pour les petits VL).""" descriptions = [] for n, tensor in enumerate(images, 1): desc = enhancer_run( "Describe this image in detail, around 150 to 220 words: the main subject and its " "precise appearance (clothing, materials, textures) first, naming the SPECIFIC color " "of each major element (exact color of the clothing, hair, skin tone, key objects, " "background), using precise color qualifiers (vivid sky-blue, pale faded denim, deep " "forest green — never just 'blue' or 'green'), then its EXACT pose " "and body orientation relative to the camera — state explicitly whether the subject is " "seen frontally, in three-quarter view, in full profile, or from behind, which way the " "head is turned, where the gaze is directed, and the position of the arms, hands and " "legs — then the environment and background, the composition and framing, then the " "lighting stated PRECISELY: its direction (from the left, right, above, below, behind " "the subject, or frontal), its character (hard or soft, warm or cool, natural or " "artificial), and any backlight, rim light or colored light present, then the overall " "color character stated explicitly: whether the colors are vivid and saturated or muted " "and desaturated, whether the image is bright or dark, and whether the contrast is high " "or low, then the mood, and the medium or photographic " "style. Output only the description.", model_name, precision, "You are a precise visual analyst.", {"do_sample": False}, 448, seed + n, images=[tensor], ) desc = desc.replace("\n", " ").strip() descriptions.append(desc) print(f"🖼️CreaPrompt image {n}: {desc}") return descriptions def enhancer_merge_checklist(n_images, with_keywords, with_text=False, with_collection=False): """Checklist en fin de message : contre le 'lost in the middle' des petits LLM.""" items = "".join( f"- the main subject of Image {i}, in its EXACT pose and body orientation as described " f"(frontal, profile, from behind, head and gaze direction, position of the limbs), " f"plus elements of its style, lighting and mood\n" for i in range(1, n_images + 1) ) if with_collection: items += "- the full content and intent of the collection prompt above\n" if with_text: items += "- the full content and intent of the existing prompt above\n" if with_keywords: items += "- every keyword listed above\n" return ( "\nMANDATORY CHECKLIST — your single output prompt MUST contain ALL of the " "following, blended into ONE coherent scene:\n" + items + "Before writing your final answer, silently verify that every item above is " "present in your prompt. Output only the prompt." ) def enhancer_run(prompt_text, model_name, precision, system_prompt, gen_params, max_new_tokens, seed, images=None, video=None): import torch model, processor = enhancer_load_model(model_name, precision) torch.manual_seed(seed) if torch.cuda.is_available(): torch.cuda.manual_seed_all(seed) user_content = [] if video is not None: frames = enhancer_tensor_to_pil(video, max_frames=32) user_content.append({"type": "video", "video": frames, "fps": 1.0}) elif images: multi = len(images) > 1 for n, tensor in enumerate(images, 1): if multi: user_content.append({"type": "text", "text": f"Image {n}:"}) for img in enhancer_tensor_to_pil(tensor): user_content.append({"type": "image", "image": img}) if prompt_text: user_content.append({"type": "text", "text": prompt_text}) messages = [ {"role": "system", "content": [{"type": "text", "text": system_prompt.strip()}]}, {"role": "user", "content": user_content}, ] inputs = processor.apply_chat_template( messages, tokenize=True, add_generation_prompt=True, return_dict=True, return_tensors="pt" ) inputs = {k: v.to(model.device) for k, v in inputs.items()} params = dict(gen_params or {}) params.update({ "max_new_tokens": max_new_tokens, "pad_token_id": processor.tokenizer.eos_token_id, }) if params.get("do_sample") is False: # neutralise les defaults de generation_config.json (warning transformers en greedy) params.update({"temperature": None, "top_p": None, "top_k": None}) with torch.inference_mode(): generated_ids = model.generate(**inputs, **params) trimmed = [out[len(inp):] for inp, out in zip(inputs["input_ids"], generated_ids)] return processor.batch_decode( trimmed, skip_special_tokens=True, clean_up_tokenization_spaces=True )[0].strip() class CreaPrompt_0: RETURN_TYPES = ( "STRING", "INT", ) RETURN_NAMES = ( "prompt", "seed", ) FUNCTION = "create_prompt" CATEGORY = "CreaPrompt" def __init__(self, seed=None): self.rng = random.Random(seed) @classmethod def IS_CHANGED(self, **kwargs): return float("NaN") @classmethod def INPUT_TYPES(cls): preset_options = ENHANCER_PRESET_KEYS + ["Your instruction"] precision_options = enhancer_precision_options() collection_options = get_collection_files() or [COLLECTION_LEGACY_LABEL] return { "required": { "__csv_json": ("STRING", {"multiline": True, "default": "{}", "input": False}) }, "optional": { "Prompt_count": ("INT", {"default": 1, "min": 1, "max": 1000}), "CreaPrompt_Collection": (["disabled"] + ["enabled"], {"default": "disabled"}), "Choose_collection": (collection_options, {"default": collection_options[0]}), "seed": ("INT", {"default": 0, "min": 0, "max": 1125899906842624}), "Enhancer": (["disabled", "enabled"], {"default": "disabled"}), "Enhancer_model": ("STRING", {"default": "hfmaster/Qwen3-VL-4B"}), "Enhancer_precision": (precision_options, {"default": "fp16"}), "Enhancer_preset": (preset_options, {"default": preset_options[0]}), "Enhancer_instruction": ("STRING", {"multiline": True, "default": ""}), "Enhancer_max_tokens": ("INT", {"default": 512, "min": 64, "max": 4096, "step": 64}), "Use_image": ("BOOLEAN", {"default": True}), "Use_text": ("BOOLEAN", {"default": False}), "Use_categories": ("BOOLEAN", {"default": True}), "Unload_after_generation": ("BOOLEAN", {"default": True}), "text": ("STRING", {"forceInput": True}), "image": ("IMAGE",), "image_2": ("IMAGE",), "image_3": ("IMAGE",), "video": ("IMAGE",), } } def create_prompt(self, **kwargs): name_of_files = getfilename(folder_path) seed = kwargs.get("seed", 0) prompts_count = kwargs.get("Prompt_count", 0) concatenated_values = "" prompt_value = "" final_values = "" values = [] values = [""] * len(name_of_files) # 🔎 Debug #print("📦 kwargs:", json.dumps(kwargs, indent=2)) dynamic_values = json.loads(kwargs.get("__csv_json", "{}")) #print("🧩 dynamic_values:", json.dumps(dynamic_values, indent=2)) collection_mode = kwargs.get("CreaPrompt_Collection", 0) == "enabled" cat_keywords = [] def build_category_line(): vals = [] for i, filename in enumerate(name_of_files): val = dynamic_values.get(filename, "disabled") if val == "🎲random": v = select_random_line_from_csv_file(filename, folder_path) else: v = val.strip() if v != "disabled": vals.append(v) return ",".join(vals) if collection_mode: collection_file = kwargs.get("Choose_collection", None) for c in range(prompts_count): prompt_value = select_random_line_from_collection(collection_file) print(f"➡️CreaPrompt prompt: {prompt_value}") final_values += prompt_value + "\n" prompt_value = "" # keywords catégories par ligne, pour le mélange collection + catégories cat_keywords.append(build_category_line()) final_values = final_values.strip() else: for c in range(prompts_count): for i, filename in enumerate(name_of_files): val = dynamic_values.get(filename, "disabled") if val == "🎲random": values[i] = select_random_line_from_csv_file(filename, folder_path) else: values[i] = val.strip() for value in values: if value != "disabled": concatenated_values += value + "," print(f"➡️CreaPrompt prompt: {concatenated_values [:-1]}") final_values += concatenated_values [:-1] + "\n" concatenated_values = "" final_values = final_values.strip() # ===================== Enhancer ===================== if kwargs.get("Enhancer", "disabled") == "enabled": if kwargs.get("Use_image", True): images = [t for t in (kwargs.get("image"), kwargs.get("image_2"), kwargs.get("image_3")) if t is not None] video = kwargs.get("video", None) else: images = [] video = None model_name = kwargs.get("Enhancer_model", "hfmaster/Qwen3-VL-4B") precision = kwargs.get("Enhancer_precision", "int4") max_tokens = kwargs.get("Enhancer_max_tokens", 512) preset_name = kwargs.get("Enhancer_preset", "") preset = ENHANCER_PRESETS.get(preset_name, {}) if preset_name == "Your instruction": system_prompt = kwargs.get("Enhancer_instruction", "").strip() gen_params = {"do_sample": True, "temperature": 0.7, "top_p": 0.8} else: system_prompt = preset.get("system_prompt", "") gen_params = preset.get("gen_params", {}) user_instruction = kwargs.get("Enhancer_instruction", "").strip() if user_instruction: system_prompt = f"{system_prompt}\n\nAdditional user instruction (must be followed): {user_instruction}" use_text = kwargs.get("Use_text", False) use_categories = kwargs.get("Use_categories", True) input_text = (kwargs.get("text") or "").strip() if not input_text: use_text = False cat_lines = [l for l in final_values.split("\n") if l.strip()] if use_categories else [] try: if collection_mode: # ---- Mode collection : chaque ligne de collection passe TOUJOURS par # l'enhancer ; les toggles ajoutent leurs sources par-dessus # (Use_categories = keywords CSV, Use_text = prompt connecté, Use_image) ---- img_context = None if images: descs = enhancer_describe_images(images, model_name, precision, seed) img_context = "Below are detailed descriptions of the labeled images.\n" + \ "\n".join(f"Image {n}: {d}" for n, d in enumerate(descs, 1)) static_parts = [] if img_context: static_parts.append(img_context) if use_text: static_parts.append("Existing prompt to rework and incorporate:\n" + input_text) n_img = len(images) enhanced_lines = [] for idx, line in enumerate([l for l in final_values.split("\n") if l.strip()]): kw = None if use_categories and idx < len(cat_keywords) and cat_keywords[idx].strip(): kw = cat_keywords[idx].strip() if not static_parts and not kw: # collection seule : réécriture directe de la ligne text_input = line else: parts = list(static_parts) parts.append("Collection prompt to rework and incorporate:\n" + line) base_instruction = ( "Build ONE single coherent image generation prompt, following your " "style rules, that combines ALL of the materials below into one scene. " "Output only the prompt." ) checklist = enhancer_merge_checklist( n_img, with_keywords=bool(kw), with_text=use_text, with_collection=True ) text_input = f"{base_instruction}\n\n" + "\n\n".join(parts) if kw: text_input += f"\n\nKeywords: {kw}" text_input += f"\n{checklist}" enhanced = enhancer_run( text_input, model_name, precision, system_prompt, gen_params, max_tokens, seed + idx ) enhanced = enhanced.replace("\n", " ").strip() if enhanced else line enhanced_lines.append(enhanced) print(f"✨CreaPrompt enhanced: {enhanced}") if enhanced_lines: final_values = "\n".join(enhanced_lines) elif (images or video is not None) and not use_text and not cat_lines: # ---- Mode purement visuel : description simple ou fusion multi-image ---- if len(images) > 1: # Deux passes : descriptions individuelles (une seule fois) puis # un assemblage textuel par prompt demandé fusion_instruction = preset.get( "fusion_instruction", "Assemble ONE single image generation prompt made of one full " "passage per image, each preserving every detail of its " "description including its own background and lighting. " "Output only the prompt." ) descs = enhancer_describe_images(images, model_name, precision, seed) img_context = "Below are detailed descriptions of the labeled images.\n" + \ "\n".join(f"Image {n}: {d}" for n, d in enumerate(descs, 1)) checklist = enhancer_merge_checklist(len(images), with_keywords=False) text_input = f"{fusion_instruction}\n\n{img_context}\n{checklist}" run_kwargs = {} else: text_input = preset.get( "image_instruction", "Describe this image as a single detailed image generation prompt." ) run_kwargs = {"images": images or None, "video": video} enhanced_lines = [] for idx in range(prompts_count): enhanced = enhancer_run( text_input, model_name, precision, system_prompt, gen_params, max_tokens, seed + idx, **run_kwargs ) if enhanced: enhanced = enhanced.replace("\n", " ").strip() enhanced_lines.append(enhanced) print(f"✨CreaPrompt enhanced: {enhanced}") if enhanced_lines: final_values = "\n".join(enhanced_lines) elif images or use_text or cat_lines: # ---- Mode mélange : combine les sources activées (image/texte/catégories) ---- img_context = None if images: # Deux passes systématiques : descriptions individuelles fiables, # puis fusion purement textuelle — en un seul appel, l'attention # du petit VL ignore l'image dès que le texte est dense descs = enhancer_describe_images(images, model_name, precision, seed) img_context = "Below are detailed descriptions of the labeled images.\n" + \ "\n".join(f"Image {n}: {d}" for n, d in enumerate(descs, 1)) context_parts = [] if img_context: context_parts.append(img_context) if use_text: context_parts.append("Existing prompt to rework and incorporate:\n" + input_text) if images and cat_lines and not use_text: base_instruction = preset.get( "image_text_instruction", "You are given one or more images labeled Image 1, Image 2, etc. " "Combine the main subject and the style, lighting and mood of EACH " "labeled image with the following keywords into ONE single coherent " "image generation prompt. Do not ignore any image. Output only the prompt." ) elif context_parts: base_instruction = ( "Build ONE single coherent image generation prompt, following your " "style rules, that combines ALL of the materials below into one scene. " "Output only the prompt." ) else: base_instruction = None # catégories seules : ligne passée telle quelle n_img = len(images) context_str = "\n\n".join(context_parts) if cat_lines: # Un appel LLM par ligne de catégories, format multi-lignes préservé, # le contexte image/texte est constant d'une ligne à l'autre enhanced_lines = [] for idx, line in enumerate(cat_lines): if base_instruction: checklist = enhancer_merge_checklist(n_img, with_keywords=True, with_text=use_text) text_input = f"{base_instruction}\n\n{context_str}\n\nKeywords: {line}\n{checklist}" else: text_input = line enhanced = enhancer_run( text_input, model_name, precision, system_prompt, gen_params, max_tokens, seed + idx ) enhanced = enhanced.replace("\n", " ").strip() if enhanced else line enhanced_lines.append(enhanced) print(f"✨CreaPrompt enhanced: {enhanced}") if enhanced_lines: final_values = "\n".join(enhanced_lines) else: # Texte seul, ou image(s) + texte, sans catégories : # un appel LLM par prompt demandé checklist = enhancer_merge_checklist(n_img, with_keywords=False, with_text=use_text) text_input = f"{base_instruction}\n\n{context_str}\n{checklist}" enhanced_lines = [] for idx in range(prompts_count): enhanced = enhancer_run( text_input, model_name, precision, system_prompt, gen_params, max_tokens, seed + idx ) if enhanced: enhanced = enhanced.replace("\n", " ").strip() enhanced_lines.append(enhanced) print(f"✨CreaPrompt enhanced: {enhanced}") if enhanced_lines: final_values = "\n".join(enhanced_lines) # sinon : aucune source activée, le prompt brut est conservé except Exception as e: print(f"⚠️ CreaPrompt Enhancer error, raw prompt returned: {e}") finally: if kwargs.get("Unload_after_generation", False): ENHANCER_MANAGER.unload(f"{model_name}_{precision}") # ==================================================== seeds_list = ", ".join(str(seed + i) for i in range(len(final_values.split("\n")))) print(f"➡️CreaPrompt Seed: {seeds_list}") return (final_values, seed) class CreaPrompt: RETURN_TYPES = ( "STRING", "INT", ) RETURN_NAMES = ( "prompt", "seed", ) FUNCTION = "create_prompt" CATEGORY = "CreaPrompt" def __init__(self, seed=None): self.rng = random.Random(seed) @classmethod def IS_CHANGED(self, **kwargs): return float("NaN") @classmethod def INPUT_TYPES(cls): required = {} for filename in sorted(os.listdir(folder_path)): if filename.endswith(".csv"): file_path = os.path.join(folder_path, filename) lines = [] with open(file_path, 'r', encoding='utf-8') as file: lines = file.readlines() required[filename [3:-4]] = (["disabled"] + ["🎲random"] + lines, {"default": "disabled"}) return { "required": required, "optional": { "Prompt_count":("INT", {"default": 1, "min": 1, "max": 1000}), "CreaPrompt_Collection": (["disabled"] + ["enabled"], {"default": "disabled"}), "seed": ("INT", {"default": 0, "min": 0, "max": 1125899906842624}), } } def create_prompt(self, **kwargs): name_of_files = getfilename(folder_path) seed = kwargs.get("seed", 0) prompts_count = kwargs.get("Prompt_count", 0) concatenated_values = "" prompt_value = "" final_values = "" values = [] values = [""] * len(name_of_files) if kwargs.get("CreaPrompt_Collection", 0) == "enabled": for c in range(prompts_count): prompt_value = select_random_line_from_collection() print(f"➡️CreaPrompt prompt: {prompt_value}") final_values += prompt_value + "\n" prompt_value = "" final_values = final_values.strip() print(f"➡️CreaPrompt Seed: {seed}") return ( final_values, seed, ) else: for c in range(prompts_count): for i, filename in enumerate(name_of_files): if kwargs.get(filename, 0) == "🎲random": values[i] = select_random_line_from_csv_file(filename, folder_path) else: values[i] = kwargs.get(filename, 0) values[i] = values[i].strip() for value in values: if value != "disabled": concatenated_values += value + "," print(f"➡️CreaPrompt prompt: {concatenated_values [:-1]}") final_values += concatenated_values [:-1] + "\n" concatenated_values = "" final_values = final_values.strip() print(f"➡️CreaPrompt Seed: {seed}") return ( final_values, seed, ) class CreaPrompt_1: RETURN_TYPES = ( "STRING", "INT", ) RETURN_NAMES = ( "prompt", "seed", ) FUNCTION = "create_prompt" CATEGORY = "CreaPrompt" def __init__(self, seed=None): self.rng = random.Random(seed) @classmethod def IS_CHANGED(self, **kwargs): return float("NaN") @classmethod def INPUT_TYPES(cls): required = {} for filename in os.listdir(folder_path_1): if filename.endswith(".csv"): file_path = os.path.join(folder_path_1, filename) lines = [] with open(file_path, 'r', encoding='utf-8') as file: lines = file.readlines() required[filename [3:-4]] = (["disabled"] + ["🎲random"] + lines, {"default": "disabled"}) return { "required": required, "optional": { "Prompt_count":("INT", {"default": 1, "min": 1, "max": 1000}), "CreaPrompt_Collection": (["disabled"] + ["enabled"], {"default": "disabled"}), "seed": ("INT", {"default": 0, "min": 0, "max": 1125899906842624}), } } def create_prompt(self, **kwargs): name_of_files = getfilename(folder_path_1) seed = kwargs.get("seed", 0) prompts_count = kwargs.get("Prompt_count", 0) concatenated_values = "" prompt_value = "" final_values = "" values = [] values = [""] * len(name_of_files) if kwargs.get("CreaPrompt_Collection", 0) == "enabled": for c in range(prompts_count): prompt_value = select_random_line_from_collection() print(f"➡️CreaPrompt prompt: {prompt_value}") final_values += prompt_value + "\n" prompt_value = "" final_values = final_values.strip() print(f"➡️CreaPrompt Seed: {seed}") return ( final_values, seed, ) else: for c in range(prompts_count): for i, filename in enumerate(name_of_files): if kwargs.get(filename, 0) == "🎲random": values[i] = select_random_line_from_csv_file(filename, folder_path_1) else: values[i] = kwargs.get(filename, 0) values[i] = values[i].strip() for value in values: if value != "disabled": concatenated_values += value + "," print(f"➡️CreaPrompt prompt: {concatenated_values [:-1]}") final_values += concatenated_values [:-1] + "\n" concatenated_values = "" final_values = final_values.strip() print(f"➡️CreaPrompt Seed: {seed}") return ( final_values, seed, ) class CreaPrompt_2: RETURN_TYPES = ( "STRING", "INT", ) RETURN_NAMES = ( "prompt", "seed", ) FUNCTION = "create_prompt" CATEGORY = "CreaPrompt" def __init__(self, seed=None): self.rng = random.Random(seed) @classmethod def IS_CHANGED(self, **kwargs): return float("NaN") @classmethod def INPUT_TYPES(cls): required = {} for filename in os.listdir(folder_path_2): if filename.endswith(".csv"): file_path = os.path.join(folder_path_2, filename) lines = [] with open(file_path, 'r', encoding='utf-8') as file: lines = file.readlines() required[filename [3:-4]] = (["disabled"] + ["🎲random"] + lines, {"default": "disabled"}) return { "required": required, "optional": { "Prompt_count":("INT", {"default": 1, "min": 1, "max": 1000}), "CreaPrompt_Collection": (["disabled"] + ["enabled"], {"default": "disabled"}), "seed": ("INT", {"default": 0, "min": 0, "max": 1125899906842624}), } } def create_prompt(self, **kwargs): name_of_files = getfilename(folder_path_2) seed = kwargs.get("seed", 0) prompts_count = kwargs.get("Prompt_count", 0) concatenated_values = "" prompt_value = "" final_values = "" values = [] values = [""] * len(name_of_files) if kwargs.get("CreaPrompt_Collection", 0) == "enabled": for c in range(prompts_count): prompt_value = select_random_line_from_collection() print(f"➡️CreaPrompt prompt: {prompt_value}") final_values += prompt_value + "\n" prompt_value = "" final_values = final_values.strip() print(f"➡️CreaPrompt Seed: {seed}") return ( final_values, seed, ) else: for c in range(prompts_count): for i, filename in enumerate(name_of_files): if kwargs.get(filename, 0) == "🎲random": values[i] = select_random_line_from_csv_file(filename, folder_path_2) else: values[i] = kwargs.get(filename, 0) values[i] = values[i].strip() for value in values: if value != "disabled": concatenated_values += value + "," print(f"➡️CreaPrompt prompt: {concatenated_values [:-1]}") final_values += concatenated_values [:-1] + "\n" concatenated_values = "" final_values = final_values.strip() print(f"➡️CreaPrompt Seed: {seed}") return ( final_values, seed, ) class CreaPrompt_3: RETURN_TYPES = ( "STRING", "INT", ) RETURN_NAMES = ( "prompt", "seed", ) FUNCTION = "create_prompt" CATEGORY = "CreaPrompt" def __init__(self, seed=None): self.rng = random.Random(seed) @classmethod def IS_CHANGED(self, **kwargs): return float("NaN") @classmethod def INPUT_TYPES(cls): required = {} for filename in os.listdir(folder_path_3): if filename.endswith(".csv"): file_path = os.path.join(folder_path_3, filename) lines = [] with open(file_path, 'r', encoding='utf-8') as file: lines = file.readlines() required[filename [3:-4]] = (["disabled"] + ["🎲random"] + lines, {"default": "disabled"}) return { "required": required, "optional": { "Prompt_count":("INT", {"default": 1, "min": 1, "max": 1000}), "CreaPrompt_Collection": (["disabled"] + ["enabled"], {"default": "disabled"}), "seed": ("INT", {"default": 0, "min": 0, "max": 1125899906842624}), } } def create_prompt(self, **kwargs): name_of_files = getfilename(folder_path_3) seed = kwargs.get("seed", 0) prompts_count = kwargs.get("Prompt_count", 0) concatenated_values = "" prompt_value = "" final_values = "" values = [] values = [""] * len(name_of_files) if kwargs.get("CreaPrompt_Collection", 0) == "enabled": for c in range(prompts_count): prompt_value = select_random_line_from_collection() print(f"➡️CreaPrompt prompt: {prompt_value}") final_values += prompt_value + "\n" prompt_value = "" final_values = final_values.strip() print(f"➡️CreaPrompt Seed: {seed}") return ( final_values, seed, ) else: for c in range(prompts_count): for i, filename in enumerate(name_of_files): if kwargs.get(filename, 0) == "🎲random": values[i] = select_random_line_from_csv_file(filename, folder_path_3) else: values[i] = kwargs.get(filename, 0) values[i] = values[i].strip() for value in values: if value != "disabled": concatenated_values += value + "," print(f"➡️CreaPrompt prompt: {concatenated_values [:-1]}") final_values += concatenated_values [:-1] + "\n" concatenated_values = "" final_values = final_values.strip() print(f"➡️CreaPrompt Seed: {seed}") return ( final_values, seed, ) class CreaPrompt_4: RETURN_TYPES = ( "STRING", "INT", ) RETURN_NAMES = ( "prompt", "seed", ) FUNCTION = "create_prompt" CATEGORY = "CreaPrompt" def __init__(self, seed=None): self.rng = random.Random(seed) @classmethod def IS_CHANGED(self, **kwargs): return float("NaN") @classmethod def INPUT_TYPES(cls): required = {} for filename in os.listdir(folder_path_4): if filename.endswith(".csv"): file_path = os.path.join(folder_path_4, filename) lines = [] with open(file_path, 'r', encoding='utf-8') as file: lines = file.readlines() required[filename [3:-4]] = (["disabled"] + ["🎲random"] + lines, {"default": "disabled"}) required[filename [3:-4] + ": Weight"] = ("FLOAT", {"default": 1, "min": -3, "max": 3}) return { "required": required, "optional": { "Prompt_count":("INT", {"default": 1, "min": 1, "max": 1000}), "CreaPrompt_Collection": (["disabled"] + ["enabled"], {"default": "disabled"}), "seed": ("INT", {"default": 0, "min": 0, "max": 1125899906842624}), } } def create_prompt(self, **kwargs): name_of_files = getfilename(folder_path_4) seed = kwargs.get("seed", 0) prompts_count = kwargs.get("Prompt_count", 0) concatenated_values = "" prompt_value = "" final_values = "" values = [] values = [""] * len(name_of_files) if kwargs.get("CreaPrompt_Collection", 0) == "enabled": for c in range(prompts_count): prompt_value = select_random_line_from_collection() print(f"➡️CreaPrompt prompt: {prompt_value}") final_values += prompt_value + "\n" prompt_value = "" final_values = final_values.strip() print(f"➡️CreaPrompt Seed: {seed}") return ( final_values, seed, ) else: for c in range(prompts_count): for i, filename in enumerate(name_of_files): if kwargs.get(filename, 0) == "🎲random": values[i] = select_random_line_from_csv_file(filename, folder_path_4) if kwargs.get(filename + ": Weight", 0) != 0 and kwargs.get(filename + ": Weight", 0) != 1: values[i] = f"({values[i]}:{kwargs.get(filename + ': Weight', 0):.1f})" else: values[i] = kwargs.get(filename, 0) values[i] = values[i].strip() if kwargs.get(filename + ": Weight", 0) != 0 and kwargs.get(filename + ": Weight", 0) != 1: values[i] = f"({values[i]}:{kwargs.get(filename + ': Weight', 0):.1f})" for value in values: if value != "disabled": concatenated_values += value + "," print(f"➡️CreaPrompt prompt: {concatenated_values [:-1]}") final_values += concatenated_values [:-1] + "\n" concatenated_values = "" final_values = final_values.strip() print(f"➡️CreaPrompt Seed: {seed}") return ( final_values, seed, ) class CreaPrompt_list: @classmethod def INPUT_TYPES(s): return {"required": {"Multi_prompts": ("STRING", {"multiline": True, "default": "body_text"}), "seed_base": ("INT", {"forceInput": True}), "prefix": ("STRING", {"multiline": True, "default": ""}), "suffix": ("STRING", {"multiline": True, "default": ""}), } } RETURN_TYPES = ("STRING", "INT", "STRING",) RETURN_NAMES = ("prompt", "seed", "prompt_debug",) OUTPUT_IS_LIST = (True, True, False) FUNCTION = "create_list" CATEGORY = "CreaPrompt" def create_list(self, Multi_prompts, seed_base, prefix="", suffix=""): Multi_prompts = Multi_prompts.strip() lines = Multi_prompts.split('\n') if prefix == "" and suffix == "": prompt_list_out = lines else: if prefix != "" and suffix != "": prompt_list_out = [prefix + "," + line + "," + suffix for line in lines] else: if prefix != "": prompt_list_out = [prefix + "," + line for line in lines] if suffix != "": prompt_list_out = [line + "," + suffix for line in lines] if prefix == "" and suffix == "": prompt_list_debug = ["➡️" + line for line in lines] else: if prefix != "" and suffix != "": prompt_list_debug = ["➡️" + prefix + "," + line + "," + suffix for line in lines] else: if prefix != "": prompt_list_debug = ["➡️" + prefix + "," + line for line in lines] if suffix != "": prompt_list_debug = ["➡️" + line + "," + suffix for line in lines] seed_list_out = [seed_base + i for i in range(len(prompt_list_out))] print(f"➡️ Batch synchronisé : {len(prompt_list_out)} prompts / {len(seed_list_out)} seeds") debug_prompts = '\n'.join(prompt_list_debug) return (prompt_list_out, seed_list_out, debug_prompts) NODE_CLASS_MAPPINGS = { "CreaPrompt_0": CreaPrompt_0, "CreaPrompt": CreaPrompt, "CreaPrompt_1": CreaPrompt_1, "CreaPrompt_2": CreaPrompt_2, "CreaPrompt_3": CreaPrompt_3, "CreaPrompt_4": CreaPrompt_4, "CreaPrompt List": CreaPrompt_list, } NODE_DISPLAY_NAME_MAPPINGS = { "CreaPrompt_0": "CreaPrompt Dynamic node", "CreaPrompt": "CreaPrompt complete node", "CreaPrompt_1": "CreaPrompt node 1", "CreaPrompt_2": "CreaPrompt node 2", "CreaPrompt_3": "CreaPrompt node 3", "CreaPrompt_4": "CreaPrompt node with weight", "CreaPrompt List": "CreaPrompt Multi Prompts", "CSL": "Comma Separated List", }