import os import random import json import requests from datetime import datetime class PromptCreatorNode: NODE_VERSION = "1.12.1" @classmethod def INPUT_TYPES(cls): base_path = os.path.dirname(__file__) json_dir = os.path.join(base_path, "JSON_DATA") json_files = [f for f in os.listdir(json_dir) if f.endswith(".json")] if os.path.isdir(json_dir) else [] # --- Camera Angles (optional) from camera_angles.json --- camera_angle_list = ["none"] camera_angles_path = os.path.join(base_path, "camera_angles.json") if os.path.exists(camera_angles_path): try: with open(camera_angles_path, "r", encoding="utf-8") as f: camera_data = json.load(f) ids = camera_data.get("ids", []) if isinstance(ids, list): camera_angle_list += [str(x) for x in ids if str(x).strip()] except Exception: pass # --- Camera Light (new) from camera_light.json --- camera_light_list = ["none"] camera_light_path = os.path.join(base_path, "camera_light.json") if os.path.exists(camera_light_path): try: with open(camera_light_path, "r", encoding="utf-8") as f: light_data = json.load(f) ids = light_data.get("ids", []) if isinstance(ids, list): camera_light_list += [str(x) for x in ids if str(x).strip()] except Exception: pass # --- Daytime (new) from daytime.json --- daytime_list = ["none"] daytime_path = os.path.join(base_path, "daytime.json") if os.path.exists(daytime_path): try: with open(daytime_path, "r", encoding="utf-8") as f: dt_data = json.load(f) ids = dt_data.get("ids", []) if isinstance(ids, list): daytime_list += [str(x) for x in ids if str(x).strip()] except Exception: pass # --- system_prompt.json (enhancer modes) --- system_prompt_path = os.path.join(base_path, "system_prompt.json") if os.path.exists(system_prompt_path): try: with open(system_prompt_path, "r", encoding="utf-8") as f: system_prompts = json.load(f) enhancer_modes = list(system_prompts.keys()) except Exception: enhancer_modes = ["standard"] else: enhancer_modes = ["standard"] # --- identities.json --- identities_path = os.path.join(base_path, "identities.json") if os.path.exists(identities_path): try: with open(identities_path, "r", encoding="utf-8") as f: identities = json.load(f) identity_profiles = ["none", "external"] + list(identities.keys()) except Exception: identity_profiles = ["none", "external"] else: identity_profiles = ["none", "external"] return { "required": { "json_name": (sorted(json_files),), "camera_angle": (camera_angle_list,), "camera_light": (camera_light_list,), # ✅ NEW "daytime": (daytime_list,), # ✅ NEW "use_enhancer": (["none", "ollama", "llamacpp", "openai", "cohere", "gemini", "openrouter"],), "enhancer_mode": (enhancer_modes,), "system_prompt_lock": (["auto", "external"], {"default": "auto"}), # ✅ NEW: enhancer words controls "enhancer_words_mode": (["auto", "min", "max"], {"default": "auto"}), "enhancer_words_min": ("INT", {"default": 140, "min": 40, "max": 400, "step": 5}), "enhancer_words_max": ("INT", {"default": 220, "min": 40, "max": 600, "step": 5}), "add_symbols": (["no", "yes"],), "seed": ("INT", {"default": 0}), "gender": (["neutral", "female", "2 female", "3 female", "female vampire", "anime woman", "male", "custom"],), "identity_profile": (identity_profiles,), "external_identity": ("STRING", {"default": "", "multiline": True, "forceInput": True}), "lock_identity": (["no", "yes"], {"default": "yes"}), "custom_intro": ("STRING", {"default": "", "multiline": True}), "custom_intro_id": (["Random", "0", "1", "2", "3", "4", "5", "6"], {"default": "Random"}), "horror_intensity": (["auto"] + [str(i) for i in range(11)],), "sensuality_level": (["auto", "0", "1", "2", "3"],), "pose_mode": (["random", "world_pick", "lock"], {"default": "random"}), "pose_index": ("INT", {"default": 0, "min": 0, "max": 200, "step": 1}), "show_pose_preview": ("BOOLEAN", {"default": True}), "lora_triggers": ("STRING", {"default": ""}), "subject_count": (["1", "2", "3"],), "lock_last_prompt": (["no", "yes"], {"default": "no"}), "multi_object_count": ("INT", {"default": 1, "min": 1, "max": 5}), # Ollama settings "ollama_host": ("STRING", {"default": "http://10.10.10.2:11434"}), "ollama_model": ("STRING", {"default": "qwen3:8b"}), "openrouter_model": ("STRING", {"default": "openai/gpt-4o-mini"}) } } RETURN_TYPES = ("STRING", "STRING") RETURN_NAMES = ("prompt", "pose_preview") FUNCTION = "generate_prompt" CATEGORY = "Prompt Creator" # ---------- Loaders ---------- def _system_prompts(self): system_prompt_path = os.path.join(os.path.dirname(__file__), "system_prompt.json") if os.path.exists(system_prompt_path): try: with open(system_prompt_path, "r", encoding="utf-8") as f: data = json.load(f) if isinstance(data, dict) and data: return data except Exception: pass # fallback return { "standard": ( "You are a professional prompt enhancer for AI image generation. " "Return ONE long, multi-clause sentence (~120–160 words), richly descriptive. " "Cover: genre/style; era; foreground/midground/background; materials & textures; color palette (3–5 tones); " "lighting (type/direction/intensity); atmosphere; camera angle & lens (mm); composition rule; 3–5 post-process keywords. " "No story, no dialogue, no lists, no headings." ) } def _identity_profiles(self): identities_path = os.path.join(os.path.dirname(__file__), "identities.json") if os.path.exists(identities_path): try: with open(identities_path, "r", encoding="utf-8") as f: data = json.load(f) return data if isinstance(data, dict) else {} except Exception: return {} return {} def _identity_to_text(self, identity_obj): if not isinstance(identity_obj, dict): return "" order = ["age", "face_type", "eyes", "nose", "mouth", "hair", "skin", "expression_base"] chunks = [] for k in order: v = identity_obj.get(k) or identity_obj.get(k.upper()) if isinstance(v, str) and v.strip(): chunks.append(v.strip()) return ", ".join(chunks) def _load_map_json(self, filename, default_ids=None): """ Loads a json shaped like: { "ids": [...], "map": { "id": "text", ... } } """ base = os.path.dirname(__file__) path = os.path.join(base, filename) if os.path.exists(path): try: with open(path, "r", encoding="utf-8") as f: data = json.load(f) if isinstance(data, dict): ids = data.get("ids", []) mp = data.get("map", {}) if not isinstance(ids, list): ids = [] if not isinstance(mp, dict): mp = {} return {"ids": ids, "map": mp} except Exception: pass return {"ids": default_ids or [], "map": {}} @staticmethod def _is_none(v): return (not v) or (str(v).strip().lower() == "none") def _resolve_mapped_value(self, ui_value, world_data, mapper_dict, world_keys): """ Generic resolver: - If UI value is set (not none) => use it - Else try world keys in order - Map id through mapper_dict["map"] if available """ chosen = "" if not self._is_none(ui_value): chosen = str(ui_value).strip() else: if isinstance(world_data, dict): for k in world_keys: v = world_data.get(k) if isinstance(v, str) and v.strip(): chosen = v.strip() break if not chosen: return "" mapped = (mapper_dict or {}).get("map", {}).get(chosen) return mapped or chosen def _sanitize_world_keys(self, world_data, ui_value, keys_to_remove): if not self._is_none(ui_value) and isinstance(world_data, dict): wd = dict(world_data) for k in keys_to_remove: wd.pop(k, None) return wd return world_data # ---------- Prompt builder ---------- def _build_prompt_from_json( self, data, gender, custom_intro, custom_intro_id, horror_intensity, sensuality_level, subject_count, multi_object_count, camera_angle_txt="", camera_light_txt="", daytime_txt="", ): parts = [] # 1) Director-level controls in head (order matters) if isinstance(camera_angle_txt, str) and camera_angle_txt.strip(): parts.append(camera_angle_txt.strip()) if isinstance(camera_light_txt, str) and camera_light_txt.strip(): parts.append(camera_light_txt.strip()) if isinstance(daytime_txt, str) and daytime_txt.strip(): parts.append(daytime_txt.strip()) # ✅ FIX: if camera_light is active, world LIGHTING must be ignored camera_light_active = isinstance(camera_light_txt, str) and camera_light_txt.strip() # 2) Gender / intro if gender == "custom": ci = (custom_intro or "").strip() def _get_custom_intro_pool(world_data): pool = {} raw = None if isinstance(world_data, dict): raw = world_data.get("CUSTOM_INTRO") or world_data.get("custom_intro") if isinstance(raw, dict): for k in [str(i) for i in range(7)]: v = raw.get(k) if isinstance(v, str) and v.strip(): pool[k] = v.strip() elif isinstance(raw, list): for i in range(min(7, len(raw))): v = raw[i] if isinstance(v, str) and v.strip(): pool[str(i)] = v.strip() return pool if not ci: pool = _get_custom_intro_pool(data) if custom_intro_id and custom_intro_id != "Random": chosen = pool.get(str(custom_intro_id)) if chosen: ci = chosen if not ci and pool: ci = random.choice(list(pool.values())) if ci: parts.append(ci) else: gender_inserted = False if isinstance(data, dict) and gender in data: values = data[gender] if isinstance(values, list) and values: parts.append(random.choice(values)) gender_inserted = True if not gender_inserted: gender_defaults = { "male": "a mysterious man", "female": "a beautiful woman", "2 female": "two beautiful women", "3 female": "three unique beautiful women", "female vampire": "a beautiful vampire woman", "anime woman": "a beautiful anime woman", } parts.append(gender_defaults.get(gender, "a striking figure")) # Optional color realm color_realm_value = None if isinstance(data, dict) and "COLOR_REALM" in data: possible_realms = data["COLOR_REALM"] if isinstance(possible_realms, list) and possible_realms: color_realm_value = random.choice(possible_realms) parts.append(color_realm_value) multi_keys = ["OBJECTS", "ACCESSORIES"] # Pick values according to realm or flat lists if color_realm_value and isinstance(data, dict): for key in ["OUTFITS", "LIGHTING", "BACKGROUNDS", "OBJECTS", "ACCESSORIES", "ATMOSPHERES"]: # ✅ FIX: skip world LIGHTING if director camera_light is active if key == "LIGHTING" and camera_light_active: continue values_by_realm = data.get(key, {}).get(color_realm_value, []) if isinstance(values_by_realm, list) and values_by_realm: if key in multi_keys: sampled = random.sample(values_by_realm, min(multi_object_count, len(values_by_realm))) parts.extend(sampled) else: parts.append(random.choice(values_by_realm)) else: if isinstance(data, dict): for key, values in data.items(): if key.lower() in [ "male", "female", "neutral", "horror_intensity", "sensuality_level", "color_realm", "system_prompt", "world_name", "custom_intro", "camera_angles", "camera_angle", "camera_light", "daytime", "poses" ]: continue # ✅ FIX: skip world LIGHTING if director camera_light is active if key == "LIGHTING" and camera_light_active: continue if isinstance(values, list) and values: if key in multi_keys: sampled = random.sample(values, min(multi_object_count, len(values))) parts.extend(sampled) else: parts.append(random.choice(values)) # Horror intensity if horror_intensity != "auto" and isinstance(data, dict) and "HORROR_INTENSITY" in data: try: horror_level = int(horror_intensity) intensity_entries = data["HORROR_INTENSITY"] if isinstance(intensity_entries, dict): matching = intensity_entries.get(str(horror_level)) if matching: parts.append(matching) except ValueError: pass # Sensuality level if sensuality_level != "auto" and isinstance(data, dict) and "SENSUALITY_LEVEL" in data: try: s_level = int(sensuality_level) s_entries = data["SENSUALITY_LEVEL"] if isinstance(s_entries, dict): matching = s_entries.get(str(s_level)) if matching: parts.append(matching) except ValueError: pass if subject_count != "1": parts.append(f"{subject_count} subjects present") return ", ".join([p for p in parts if isinstance(p, str) and p.strip()]) # ---------- API Keys ---------- def _read_api_keys(self, base_path): keys_path = os.path.join(base_path, "api_keys.txt") keys = {} if os.path.exists(keys_path): with open(keys_path, "r", encoding="utf-8") as f: for line in f: if "=" in line: k, v = line.strip().split("=", 1) keys[k.strip()] = v.strip() return keys # ---------- Enhancers ---------- def _enhance_with_ollama(self, host, model, system_prompt, user_prompt): r = requests.post( f"{host}/api/chat", json={ "model": model, "messages": [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt} ], "stream": False }, timeout=120 ) r.raise_for_status() return r.json()["message"]["content"].strip() def _enhance_with_openai(self, base_path, system_prompt, user_prompt): import openai keys = self._read_api_keys(base_path) openai.api_key = keys.get("openai", "") resp = openai.ChatCompletion.create( model="gpt-3.5-turbo", messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt} ] ) return resp.choices[0].message.content.strip() def _enhance_with_llamacpp(self, host, system_prompt, user_prompt, temperature=0.7, top_p=0.9, n_predict=220): """ llama.cpp server backend. Tries OpenAI-compatible endpoint first (/v1/chat/completions), then falls back to (/completion). host example: http://127.0.0.1:11434 """ host = (host or "").rstrip("/") # 1) Try OpenAI-compatible chat endpoint try: r = requests.post( f"{host}/v1/chat/completions", json={ "model": "llama", "messages": [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ], "temperature": float(temperature), "top_p": float(top_p), "max_tokens": int(n_predict), "stream": False, }, timeout=120, ) if r.status_code == 200: j = r.json() content = ( j.get("choices", [{}])[0] .get("message", {}) .get("content", "") ) content = (content or "").strip() if content: return content except Exception: pass # 2) Fallback to llama.cpp native /completion r = requests.post( f"{host}/completion", json={ "prompt": f"{system_prompt}\n\n{user_prompt}", "temperature": float(temperature), "top_p": float(top_p), "n_predict": int(n_predict), "stream": False, }, timeout=120, ) r.raise_for_status() j = r.json() content = (j.get("content") or j.get("completion") or "").strip() if not content: raise RuntimeError(f"llama.cpp returned no content. Keys: {list(j.keys())}") return content def _enhance_with_cohere(self, base_path, system_prompt, user_prompt): import re try: import cohere except ImportError: print("[PromptCreator] Cohere SDK not installed, skipping enhance.") return user_prompt keys = self._read_api_keys(base_path) api_key = keys.get("cohere", "").strip() if not api_key: print("[PromptCreator] No Cohere API key found, skipping enhance.") return user_prompt try: co = cohere.ClientV2(api_key=api_key) except Exception as e: print(f"[PromptCreator] Cohere ClientV2 not available: {e}") return user_prompt user_msg = ( f"Seed: {user_prompt}\n\n" "Return exactly one long sentence (no line breaks)." ) try: resp = co.chat( model="command-r-08-2024", messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_msg}, ], max_tokens=700, temperature=0.9, ) content = "" if hasattr(resp, "message") and getattr(resp.message, "content", None): parts = resp.message.content texts = [getattr(p, "text", "") for p in parts if getattr(p, "text", None)] content = " ".join(texts).strip() if not content: return user_prompt content = re.sub(r"\s*\n+\s*", " ", content).strip() return content except Exception as e: print(f"[PromptCreator] Error calling Cohere chat: {e}") return user_prompt def _enhance_with_gemini(self, base_path, system_prompt, user_prompt): import google.generativeai as genai keys = self._read_api_keys(base_path) genai.configure(api_key=keys.get("gemini", "")) model = genai.GenerativeModel("models/gemini-2.5-pro") txt = ( f"SYSTEM: {system_prompt}\n" f"USER: {user_prompt}\n" "Return only the final prompt as a single paragraph." ) r = model.generate_content(txt) return r.candidates[0].content.parts[0].text.strip() def _enhance_with_openrouter(self, base_path, model, system_prompt, user_prompt): keys = self._read_api_keys(base_path) api_key = keys.get("openrouter", "").strip() if not api_key: print("[PromptCreator] No OpenRouter API key found.") return user_prompt headers = { "Authorization": f"Bearer {api_key}", "Content-Type": "application/json" } payload = { "model": model, "messages": [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt} ] } r = requests.post( "https://openrouter.ai/api/v1/chat/completions", headers=headers, json=payload, timeout=120 ) r.raise_for_status() print(headers) print(payload) return r.json()["choices"][0]["message"]["content"].strip() # ---------- Logging ---------- @staticmethod def log_prompt_run( json_name, enhancer_mode, gender, custom_intro, lora_triggers, final_prompt, source="generated", node_version="1.12.1", ): log_dir = os.path.join(os.path.dirname(__file__), "logs") os.makedirs(log_dir, exist_ok=True) log_path = os.path.join(log_dir, "prompt_history.jsonl") entry = { "timestamp": datetime.now().isoformat(timespec="seconds"), "json_world": json_name, "system_prompt": enhancer_mode, "gender": gender, "custom_intro": custom_intro.strip() if (gender == "custom" and isinstance(custom_intro, str) and custom_intro.strip()) else None, "lora_triggers": lora_triggers, "final_prompt": final_prompt, "source": source, "node_version": node_version } with open(log_path, "a", encoding="utf-8") as f: f.write(json.dumps(entry, ensure_ascii=False) + "\n") # ---------- Enhancer words ---------- @staticmethod def _build_enhancer_user_prompt(seed_prompt, words_mode, wmin, wmax): """ words_mode: - auto: don't force range - min : force shorter range around wmin - max : force longer range up to wmax """ base = (seed_prompt or "").strip() if not base: return "Enhance this prompt." # sanitize ints try: wmin = int(wmin) except Exception: wmin = 140 try: wmax = int(wmax) except Exception: wmax = 220 wmin = max(20, min(wmin, 600)) wmax = max(20, min(wmax, 800)) if wmax < wmin: wmax = wmin mode = (words_mode or "auto").strip().lower() if mode == "min": lo = max(20, wmin - 10) hi = max(lo + 10, wmin + 20) return ( f"Enhance this prompt into ONE single long sentence (~{lo}–{hi} words), " f"no lists and no line breaks: {base}" ) if mode == "max": lo = max(30, wmax - 30) hi = max(lo + 10, wmax) return ( f"Enhance this prompt into ONE single long sentence (~{lo}–{hi} words), " f"no lists and no line breaks: {base}" ) # auto return ( "Enhance the following visual prompt into ONE long cinematic sentence. " "Structure the description in this order: camera perspective, lighting, environment, subject, materials and textures, atmosphere and mood. " "Do not create lists or line breaks. " f"Seed prompt: {base}" ) def _compress_prompt(self, text): import re if not text: return text # normalizza spazi text = re.sub(r"\s+", " ", text) # rimuove parole duplicate consecutive text = re.sub(r"\b(\w+)( \1\b)+", r"\1", text, flags=re.IGNORECASE) # pulisce virgole doppie text = re.sub(r",\s*,+", ", ", text) # rimuove spazi prima delle virgole text = re.sub(r"\s+,", ",", text) return text.strip() # ---------- Main ---------- def generate_prompt( self, json_name, camera_angle, camera_light, # ✅ NEW daytime, # ✅ NEW use_enhancer, enhancer_mode, system_prompt_lock, enhancer_words_mode, # ✅ NEW enhancer_words_min, # ✅ NEW enhancer_words_max, # ✅ NEW add_symbols, seed, gender, identity_profile, external_identity, lock_identity, custom_intro, custom_intro_id, horror_intensity, sensuality_level, pose_mode, pose_index, show_pose_preview, lora_triggers, subject_count, lock_last_prompt, multi_object_count, ollama_host, ollama_model, openrouter_model ): base_path = os.path.dirname(__file__) json_path = os.path.join(base_path, "JSON_DATA", json_name) # history lock history_dir = os.path.join(base_path, "history") os.makedirs(history_dir, exist_ok=True) history_path = os.path.join(history_dir, f"last_prompt_{json_name}.txt") if lock_last_prompt == "yes" and os.path.exists(history_path): with open(history_path, "r", encoding="utf-8") as f: prompt = f.read().strip() pose_preview = "" # best-effort (history has no pose preview) self.log_prompt_run( json_name=json_name, enhancer_mode=enhancer_mode, gender=gender, custom_intro=custom_intro, lora_triggers=lora_triggers, final_prompt=prompt, source="history_lock", node_version=self.NODE_VERSION ) print("[PromptCreator] Prompt locked and loaded from history") return (prompt, pose_preview) try: with open(json_path, "r", encoding="utf-8") as f: data = json.load(f) except Exception as e: print(f"[PromptCreator] Errore nel caricamento del JSON: {e}") return ("", "") # world-level SYSTEM_PROMPT (unified JSON) if present world_system_prompt = None if isinstance(data, dict): wsp = data.get("SYSTEM_PROMPT") or data.get("system_prompt") if isinstance(wsp, str) and wsp.strip(): world_system_prompt = wsp.strip() # Load director maps angle_dict = self._load_map_json("camera_angles.json", default_ids=["front"]) light_dict = self._load_map_json("camera_light.json", default_ids=[]) daytime_dict = self._load_map_json("daytime.json", default_ids=[]) # Resolve director values (UI overrides world) camera_angle_txt = self._resolve_mapped_value(camera_angle, data, angle_dict, ["camera_angle", "CAMERA_ANGLE"]) camera_light_txt = self._resolve_mapped_value(camera_light, data, light_dict, ["camera_light", "CAMERA_LIGHT"]) daytime_txt = self._resolve_mapped_value(daytime, data, daytime_dict, ["daytime", "DAYTIME"]) # Sanitize world keys if overridden by UI data = self._sanitize_world_keys(data, camera_angle, ["camera_angle", "CAMERA_ANGLE"]) data = self._sanitize_world_keys(data, camera_light, ["camera_light", "CAMERA_LIGHT"]) data = self._sanitize_world_keys(data, daytime, ["daytime", "DAYTIME"]) # Seed if seed: random.seed(seed) # Identity profile (prompt-only) identity_txt = "" if lock_identity == "yes": if identity_profile == "external": identity_txt = (external_identity or "").strip() elif identity_profile != "none": identities = self._identity_profiles() identity_txt = self._identity_to_text(identities.get(identity_profile, {})) # Build base prompt prompt = self._build_prompt_from_json( data=data, gender=gender, custom_intro=custom_intro, custom_intro_id=custom_intro_id, horror_intensity=horror_intensity, sensuality_level=sensuality_level, subject_count=subject_count, multi_object_count=multi_object_count, camera_angle_txt=camera_angle_txt, camera_light_txt=camera_light_txt, daytime_txt=daytime_txt, ) # --- POSE CONTROL --- poses = [] if isinstance(data, dict): poses = data.get("POSES") or data.get("poses") or [] poses = poses if isinstance(poses, list) else [] pose_preview = "" chosen_pose = "" pose_path = os.path.join(history_dir, f"last_pose_{json_name}.txt") def pick_pose_by_index(i): if not poses: return "" i = max(0, min(int(i), len(poses) - 1)) return str(poses[i]).strip() if pose_mode == "world_pick": chosen_pose = pick_pose_by_index(pose_index) elif pose_mode == "lock": if os.path.exists(pose_path): with open(pose_path, "r", encoding="utf-8") as f: chosen_pose = f.read().strip() if not chosen_pose: chosen_pose = pick_pose_by_index(pose_index) else: # random if poses: chosen_pose = random.choice(poses).strip() if chosen_pose: prompt = prompt + ", " + chosen_pose with open(pose_path, "w", encoding="utf-8") as f: f.write(chosen_pose) if show_pose_preview: if poses: lines = [f"POSES ({len(poses)}):"] for i, p in enumerate(poses): marker = " ->" if (chosen_pose and str(p).strip() == chosen_pose) else " " lines.append(f"{marker} {i}: {p}") pose_preview = "\n".join(lines) else: pose_preview = "POSES: (none found in this world JSON)" # Identity appended after pose (keeps face consistency late in chain) if identity_txt: prompt = prompt + ", " + identity_txt # System prompt selection system_prompts = self._system_prompts() effective_world_system_prompt = None if system_prompt_lock == "external" else world_system_prompt system_prompt = effective_world_system_prompt or system_prompts.get(enhancer_mode, system_prompts.get("standard", "")) # ✅ NEW: words-controlled user prompt for enhancer user_prompt = self._build_enhancer_user_prompt( seed_prompt=prompt, words_mode=enhancer_words_mode, wmin=enhancer_words_min, wmax=enhancer_words_max ) # Enhancer backends if use_enhancer != "none": try: if use_enhancer == "ollama": prompt = self._enhance_with_ollama(ollama_host, ollama_model, system_prompt, user_prompt) elif use_enhancer == "llamacpp": prompt = self._enhance_with_llamacpp(ollama_host, system_prompt, user_prompt) elif use_enhancer == "openai": prompt = self._enhance_with_openai(base_path, system_prompt, user_prompt) elif use_enhancer == "cohere": prompt = self._enhance_with_cohere(base_path, system_prompt, user_prompt) elif use_enhancer == "gemini": prompt = self._enhance_with_gemini(base_path, system_prompt, user_prompt) elif use_enhancer == "openrouter": prompt = self._enhance_with_openrouter(base_path, openrouter_model, system_prompt, user_prompt) # original gemini branch removed below if False: prompt = self._enhance_with_gemini(base_path, system_prompt, user_prompt) except Exception as e: print(f"[PromptCreator] Errore nell'enhancer ({use_enhancer}): {e}") # LoRA triggers if isinstance(lora_triggers, str) and lora_triggers.strip(): lts = [x.strip() for x in lora_triggers.split(",") if x.strip()] if lts: prompt += ", " + ", ".join(lts) if add_symbols == "yes": prompt = f"[{prompt}]" # Persist history with open(history_path, "w", encoding="utf-8") as f: f.write(prompt.strip()) self.log_prompt_run( json_name=json_name, enhancer_mode=enhancer_mode, gender=gender, custom_intro=custom_intro, lora_triggers=lora_triggers, final_prompt=prompt, source="generated", node_version=self.NODE_VERSION ) prompt = self._compress_prompt(prompt) print(f"[PromptCreator] Prompt finale: {prompt}") return (prompt, pose_preview)