File size: 5,968 Bytes
467c85e de132df 467c85e de132df 467c85e de132df 467c85e de132df 467c85e de132df 467c85e de132df 467c85e de132df 467c85e de132df 467c85e de132df 467c85e de132df 467c85e de132df 467c85e de132df 467c85e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | import yaml
import random
import os
import json
import tempfile
import gradio as gr
from .config import FEATURES_FILE, FEATURE_SEQUENCE, SECTIONS, EXAMPLES_DIR
def load_features():
with open(FEATURES_FILE, "r") as f:
return yaml.safe_load(f)
features_data = load_features()
def get_detail(category, subcategory, key):
"""Retrieves the detailed description for a given key in a category/subcategory."""
return features_data.get(category, {}).get(subcategory, {}).get(key, key)
def generate_prompt(*args):
"""
Assembles the prompt based on dropdown selections and extra text info.
"""
character_name = args[0]
num_features = len(FEATURE_SEQUENCE)
feature_keys = args[1:num_features+1]
# Checkboxes are at args[num_features+1 : num_features+1 + num_features]
# Extra info starts after the name, dropdowns, and checkboxes
extra_infos = args[1 + num_features*2 : 1 + num_features*2 + len(SECTIONS)]
template = features_data.get("templates", {}).get("default", "")
context = {}
for i, (cat, subcat, t_key) in enumerate(FEATURE_SEQUENCE):
key = feature_keys[i]
context[t_key] = get_detail(cat, subcat, key)
# Handle multiple accessories dynamically
acc_list = []
# Identify accessory keys in context
for i, (cat, subcat, t_key) in enumerate(FEATURE_SEQUENCE):
if subcat == 'accessory' and feature_keys[i] != "None":
acc_list.append(context[t_key])
if acc_list:
context['accessories'] = ", and has " + " as well as ".join(acc_list)
else:
context['accessories'] = ""
# Inject extra info into the context
if extra_infos[0]: # Identity
context['age'] += f", {extra_infos[0]}"
if extra_infos[1]: # Appearance
context['distinguishing_feature'] += f", also {extra_infos[1]}"
if extra_infos[2]: # Equipment
if context['accessories']:
context['accessories'] += f", further complemented by {extra_infos[2]}"
else:
context['accessories'] = f", complemented by {extra_infos[2]}"
if extra_infos[3]: # Environment
context['atmosphere'] += f", additionally {extra_infos[3]}"
if extra_infos[4]: # Style
context['camera'] += f", art style notes: {extra_infos[4]}"
try:
base_prompt = template.format(**context)
if character_name and character_name.strip() and character_name != "Unnamed Hero":
return f"A portrait of {character_name}, {base_prompt}"
return base_prompt
except Exception as e:
return f"Error building prompt: {e}"
def handle_regeneration(*args):
"""Randomizes checkboxes and returns new values for dropdowns."""
num_features = len(FEATURE_SEQUENCE)
current_values = list(args[:num_features])
checkboxes = args[num_features : num_features*2]
new_values = []
for i, (is_random, (cat, subcat, t_key)) in enumerate(zip(checkboxes, FEATURE_SEQUENCE)):
if is_random:
choices = list(features_data[cat][subcat].keys())
new_values.append(random.choice(choices))
else:
new_values.append(current_values[i])
return new_values
def save_character(*args):
"""Saves all current UI values to a JSON file."""
character_name = args[0]
num_features = len(FEATURE_SEQUENCE)
feature_keys = args[1:num_features+1]
checkboxes = args[num_features+1 : num_features+1 + num_features]
extra_infos = args[num_features+1 + num_features : num_features+1 + num_features + len(SECTIONS)]
data = {
"name": character_name,
"features": {FEATURE_SEQUENCE[i][2]: feature_keys[i] for i in range(num_features)},
"randomization": {FEATURE_SEQUENCE[i][2]: checkboxes[i] for i in range(num_features)},
"extra_info": {SECTIONS[i].lower(): extra_infos[i] for i in range(len(SECTIONS))}
}
# Save to a file with a friendly name in a temp directory
safe_name = "".join([c if c.isalnum() else "_" for c in character_name]).strip("_")
filename = f"{safe_name}_data.json" if safe_name else "rpg_character_data.json"
temp_dir = tempfile.mkdtemp()
path = os.path.join(temp_dir, filename)
with open(path, 'w') as f:
json.dump(data, f, indent=4)
return path
def load_character(file):
"""Loads UI values from a JSON file."""
if file is None:
return [gr.update()] * (1 + len(FEATURE_SEQUENCE) * 2 + len(SECTIONS))
try:
file_path = file.name if hasattr(file, 'name') else file
with open(file_path, 'r') as f:
data = json.load(f)
updates = []
# Update name
updates.append(data.get("name", "Unnamed Hero"))
# Update dropdowns
for _, _, t_key in FEATURE_SEQUENCE:
updates.append(data.get("features", {}).get(t_key, gr.update()))
# Update checkboxes (randomization flags)
for _, _, t_key in FEATURE_SEQUENCE:
updates.append(data.get("randomization", {}).get(t_key, gr.update()))
# Update extra info textboxes
for section in SECTIONS:
updates.append(data.get("extra_info", {}).get(section.lower(), ""))
return updates
except Exception as e:
print(f"Error loading character: {e}")
return [gr.update()] * (1 + len(FEATURE_SEQUENCE) * 2 + len(SECTIONS))
def get_example_list():
"""Returns a list of JSON filenames in the examples directory."""
if not os.path.exists(EXAMPLES_DIR):
return []
return [f for f in os.listdir(EXAMPLES_DIR) if f.endswith('.json')]
def load_example_character(filename):
"""Loads a character config from the examples directory."""
if not filename:
return [gr.update()] * (1 + len(FEATURE_SEQUENCE) * 2 + len(SECTIONS))
path = os.path.join(EXAMPLES_DIR, filename)
return load_character(path)
|