Spaces:
Sleeping
Sleeping
| 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) | |