diff --git a/core/prompt_engine.py b/core/prompt_engine.py index 9554f2f..069cfa3 100644 --- a/core/prompt_engine.py +++ b/core/prompt_engine.py @@ -1,7 +1,7 @@ from __future__ import annotations import random -from typing import Any, Dict, List, Optional +from typing import Any, Dict, List, Optional, Tuple def _pick(arr: Any, fallback: str = "") -> str: @@ -14,33 +14,57 @@ def _join_nonempty(parts: List[str]) -> str: return ", ".join([p.strip() for p in parts if p and p.strip()]) +def _select_custom_intro( + world: Dict[str, Any], + mode: str = "auto", + index: Optional[int] = None, +) -> str: + ci = world.get("CUSTOM_INTRO") + if not isinstance(ci, dict) or not ci: + return "" + + # Normalizza keys/values in stringhe + keys = list(ci.keys()) + values = [str(ci[k]) for k in keys if ci.get(k) is not None] + + if not values: + return "" + + mode = (mode or "auto").lower().strip() + + if mode == "index": + if index is None: + return "" + k = str(index) + return str(ci.get(k, "")) if k in ci else "" + + if mode == "random": + return str(random.choice(values)) + + # auto: prova 0..6, altrimenti random + for k in ["0", "1", "2", "3", "4", "5", "6"]: + if k in ci: + return str(ci[k]) + return str(random.choice(values)) + + def build_prompt_from_world( world: Dict[str, Any], seed: Optional[int] = None, + custom_intro_mode: str = "auto", custom_intro_index: Optional[int] = None, -) -> str: +) -> Tuple[str, str]: if seed is not None: random.seed(seed) - # 1) system prompt (se ti serve per debug/log o per ritorni futuri) system_prompt = str(world.get("SYSTEM_PROMPT", "")).strip() - # 2) prendi un custom_intro (se c’è) - custom_intro = "" - ci = world.get("CUSTOM_INTRO") - if isinstance(ci, dict) and ci: - if custom_intro_index is not None and str(custom_intro_index) in ci: - custom_intro = str(ci[str(custom_intro_index)]) - else: - # prova 0..6, altrimenti random tra valori - for k in ["0", "1", "2", "3", "4", "5", "6"]: - if k in ci: - custom_intro = str(ci[k]) - break - if not custom_intro: - custom_intro = str(random.choice(list(ci.values()))) + custom_intro = _select_custom_intro( + world, + mode=custom_intro_mode, + index=custom_intro_index, + ) - # 3) selezioni modulari (liste) outfits = _pick(world.get("OUTFITS"), "simple dark outfit") lighting = _pick(world.get("LIGHTING"), "low ambient light") backgrounds = _pick(world.get("BACKGROUNDS"), "intimate interior") @@ -51,8 +75,7 @@ def build_prompt_from_world( atmos = _pick(world.get("ATMOSPHERES"), "quiet cinematic mood") accessories = _pick(world.get("ACCESSORIES"), "") - # 4) compone una frase unica (poi la raffiniamo nello step successivo) - core = _join_nonempty([ + prompt = _join_nonempty([ custom_intro, outfits, lighting, @@ -65,5 +88,4 @@ def build_prompt_from_world( accessories, ]) - # system_prompt lo teniamo separato: qui ritorniamo solo il prompt “artistico” - return core.strip(), system_prompt + return (prompt.strip(), system_prompt) diff --git a/nodes/director.py b/nodes/director.py index 4decb70..4c9071c 100644 --- a/nodes/director.py +++ b/nodes/director.py @@ -1,3 +1,7 @@ +from __future__ import annotations + +import random + from ..core.world_registry import WorldRegistry from ..core.prompt_engine import build_prompt_from_world @@ -13,22 +17,56 @@ class DirectorNode: "required": { "world": (names,), "seed": ("INT", {"default": 0, "min": 0, "max": 2**31 - 1}), - }, - "optional": { + "lock": ("BOOLEAN", {"default": False}), + "control_after_generate": (["keep", "increment", "randomize"],), + "custom_intro_mode": (["auto", "index", "random"],), "custom_intro_index": ("INT", {"default": -1, "min": -1, "max": 6}), - }, + } } - RETURN_TYPES = ("STRING", "STRING") - RETURN_NAMES = ("prompt", "system_prompt") + RETURN_TYPES = ("STRING", "STRING", "INT") + RETURN_NAMES = ("prompt", "system_prompt", "next_seed") FUNCTION = "run" CATEGORY = "PFN/Director" - def run(self, world: str, seed: int, custom_intro_index: int = -1): + def run( + self, + world: str, + seed: int, + lock: bool, + control_after_generate: str, + custom_intro_mode: str, + custom_intro_index: int, + ): w = WorldRegistry.get(world) if not w: - return ("(world not found)", "") + return ("(world not found)", "", seed) - idx = None if custom_intro_index < 0 else int(custom_intro_index) - prompt, system_prompt = build_prompt_from_world(w, seed=seed, custom_intro_index=idx) - return (prompt, system_prompt) + # Seed effettivo per generare + effective_seed = int(seed) + + # Custom intro index valido solo in modalità index + idx = None + if (custom_intro_mode == "index") and (custom_intro_index is not None) and (int(custom_intro_index) >= 0): + idx = int(custom_intro_index) + + prompt, system_prompt = build_prompt_from_world( + w, + seed=effective_seed, + custom_intro_mode=custom_intro_mode, + custom_intro_index=idx, + ) + + # Next seed (utile da collegare ad altri nodi) + if lock: + next_seed = effective_seed + else: + mode = (control_after_generate or "keep").lower().strip() + if mode == "increment": + next_seed = (effective_seed + 1) % (2**31) + elif mode == "randomize": + next_seed = random.randint(0, 2**31 - 1) + else: + next_seed = effective_seed + + return (prompt, system_prompt, next_seed)