202 lines
7.4 KiB
Python
202 lines
7.4 KiB
Python
import os
|
|
import random
|
|
import logging
|
|
|
|
class BoyoPromptLoop:
|
|
"""Prompt iteration management for bastard loops"""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
# Try to find prompts folder, create if doesn't exist
|
|
prompts_folder = os.path.join(os.path.dirname(__file__), 'prompts')
|
|
if not os.path.exists(prompts_folder):
|
|
os.makedirs(prompts_folder)
|
|
# Create a sample file
|
|
sample_file = os.path.join(prompts_folder, 'sample_prompts.txt')
|
|
with open(sample_file, 'w', encoding='utf-8') as f:
|
|
f.write("a beautiful landscape\n")
|
|
f.write("a majestic mountain\n")
|
|
f.write("a serene lake\n")
|
|
f.write("a vibrant sunset\n")
|
|
f.write("a mystical forest\n")
|
|
|
|
# Get available text files
|
|
text_files = []
|
|
try:
|
|
text_files = [f for f in os.listdir(prompts_folder) if f.endswith('.txt')]
|
|
except:
|
|
text_files = ["No .txt files found"]
|
|
|
|
if not text_files:
|
|
text_files = ["No .txt files found"]
|
|
|
|
return {
|
|
"required": {
|
|
"text_file": (text_files,),
|
|
"mode": (["sequential", "random", "single"],),
|
|
"start_seed": ("INT", {
|
|
"default": 0,
|
|
"min": 0,
|
|
"max": 0xFFFFFFFF
|
|
}),
|
|
"loop_count": ("INT", {
|
|
"default": 5,
|
|
"min": 1,
|
|
"max": 100,
|
|
"step": 1
|
|
}),
|
|
},
|
|
"optional": {
|
|
"prefix": ("STRING", {"default": ""}),
|
|
"suffix": ("STRING", {"default": ""}),
|
|
"single_prompt": ("STRING", {
|
|
"default": "",
|
|
"multiline": True
|
|
}),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("PROMPT_LOOP_CONFIG",)
|
|
RETURN_NAMES = ("prompt_config",)
|
|
FUNCTION = "setup_prompt_loop"
|
|
CATEGORY = "boyo/bastardloops"
|
|
|
|
def setup_prompt_loop(self, text_file, mode, start_seed, loop_count, prefix="", suffix="", single_prompt=""):
|
|
"""Set up the prompt loop configuration"""
|
|
|
|
prompts = []
|
|
|
|
if mode == "single":
|
|
# Use the single prompt for all iterations
|
|
if single_prompt.strip():
|
|
base_prompt = single_prompt.strip()
|
|
for i in range(loop_count):
|
|
prompts.append(f"{prefix}{base_prompt}{suffix}")
|
|
else:
|
|
# Fallback to a default prompt
|
|
for i in range(loop_count):
|
|
prompts.append(f"{prefix}a beautiful image{suffix}")
|
|
|
|
else:
|
|
# Load prompts from file
|
|
prompts_folder = os.path.join(os.path.dirname(__file__), 'prompts')
|
|
file_path = os.path.join(prompts_folder, text_file)
|
|
|
|
try:
|
|
with open(file_path, 'r', encoding='utf-8') as f:
|
|
lines = [line.strip() for line in f if line.strip()]
|
|
|
|
if not lines:
|
|
# Fallback if file is empty
|
|
lines = ["a beautiful image", "a stunning artwork", "a masterpiece"]
|
|
|
|
total_lines = len(lines)
|
|
|
|
if mode == "sequential":
|
|
# Sequential mode: start from seed position and loop through
|
|
for i in range(loop_count):
|
|
line_index = (start_seed + i) % total_lines
|
|
base_prompt = lines[line_index]
|
|
prompts.append(f"{prefix}{base_prompt}{suffix}")
|
|
|
|
elif mode == "random":
|
|
# Random mode: use seed for reproducible randomness
|
|
random.seed(start_seed)
|
|
for i in range(loop_count):
|
|
# Re-seed for each iteration to get different but reproducible results
|
|
random.seed(start_seed + i)
|
|
base_prompt = random.choice(lines)
|
|
prompts.append(f"{prefix}{base_prompt}{suffix}")
|
|
|
|
except Exception as e:
|
|
logging.warning(f"BoyoPromptLoop: Could not read file {text_file}: {e}")
|
|
# Fallback prompts
|
|
for i in range(loop_count):
|
|
prompts.append(f"{prefix}a beautiful image (iteration {i+1}){suffix}")
|
|
|
|
# Create configuration object
|
|
config = {
|
|
"prompts": prompts,
|
|
"mode": mode,
|
|
"start_seed": start_seed,
|
|
"loop_count": loop_count,
|
|
"prefix": prefix,
|
|
"suffix": suffix,
|
|
"text_file": text_file if mode != "single" else None,
|
|
"single_prompt": single_prompt if mode == "single" else None
|
|
}
|
|
|
|
logging.info(f"BoyoPromptLoop: Generated {len(prompts)} prompts in {mode} mode")
|
|
for i, prompt in enumerate(prompts[:3]): # Log first 3 prompts
|
|
logging.info(f" Prompt {i+1}: {prompt}")
|
|
if len(prompts) > 3:
|
|
logging.info(f" ... and {len(prompts)-3} more")
|
|
|
|
return (config,)
|
|
|
|
@classmethod
|
|
def IS_CHANGED(cls, text_file, mode, start_seed, loop_count, prefix, suffix, single_prompt):
|
|
# Force update when any parameter changes
|
|
return float(f"{start_seed}.{hash((text_file, mode, loop_count, prefix, suffix, single_prompt)) % 1000}")
|
|
|
|
class BoyoPromptInjector:
|
|
"""Injects prompts from the loop configuration into the workflow"""
|
|
|
|
@classmethod
|
|
def INPUT_TYPES(cls):
|
|
return {
|
|
"required": {
|
|
"prompt_config": ("PROMPT_LOOP_CONFIG",),
|
|
"iteration_index": ("INT", {
|
|
"default": 0,
|
|
"min": 0,
|
|
"max": 999
|
|
}),
|
|
},
|
|
"optional": {
|
|
"fallback_prompt": ("STRING", {
|
|
"default": "a beautiful image",
|
|
"multiline": True
|
|
}),
|
|
}
|
|
}
|
|
|
|
RETURN_TYPES = ("STRING",)
|
|
RETURN_NAMES = ("prompt",)
|
|
FUNCTION = "inject_prompt"
|
|
CATEGORY = "boyo/bastardloops"
|
|
|
|
def inject_prompt(self, prompt_config, iteration_index, fallback_prompt="a beautiful image"):
|
|
"""Inject the appropriate prompt for the current iteration"""
|
|
|
|
if not isinstance(prompt_config, dict) or "prompts" not in prompt_config:
|
|
logging.warning("BoyoPromptInjector: Invalid prompt_config received")
|
|
return (fallback_prompt,)
|
|
|
|
prompts = prompt_config["prompts"]
|
|
|
|
if not prompts:
|
|
logging.warning("BoyoPromptInjector: No prompts in config")
|
|
return (fallback_prompt,)
|
|
|
|
# Get the prompt for this iteration
|
|
if iteration_index < len(prompts):
|
|
selected_prompt = prompts[iteration_index]
|
|
else:
|
|
# Loop back if we run out of prompts
|
|
selected_prompt = prompts[iteration_index % len(prompts)]
|
|
|
|
logging.info(f"BoyoPromptInjector: Iteration {iteration_index} -> '{selected_prompt}'")
|
|
|
|
return (selected_prompt,)
|
|
|
|
# Node mappings
|
|
NODE_CLASS_MAPPINGS = {
|
|
"BoyoPromptLoop": BoyoPromptLoop,
|
|
"BoyoPromptInjector": BoyoPromptInjector
|
|
}
|
|
|
|
NODE_DISPLAY_NAME_MAPPINGS = {
|
|
"BoyoPromptLoop": "Boyo Prompt Loop",
|
|
"BoyoPromptInjector": "Boyo Prompt Injector"
|
|
} |