Files

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"
}