Spaces:
Sleeping
Sleeping
| import yaml | |
| import os | |
| def load_persona(path): | |
| """ | |
| Load a LitDigitalTwin persona from YAML file. | |
| Ensures required fields and initializes missing defaults. | |
| """ | |
| if not os.path.exists(path): | |
| raise FileNotFoundError(f"Persona file not found: {path}") | |
| with open(path, "r", encoding="utf-8") as f: | |
| persona = yaml.safe_load(f) | |
| # Required top-level keys | |
| required_keys = [ | |
| "persona_name", | |
| "age", | |
| "role", | |
| "system_prompt", | |
| "facts" | |
| ] | |
| for key in required_keys: | |
| if key not in persona: | |
| raise ValueError(f"Missing required key in persona: {key}") | |
| # Ensure facts is a list | |
| if not isinstance(persona.get("facts"), list): | |
| print("Warning: 'facts' should be a list. Converting.") | |
| facts = persona.get("facts", []) | |
| if isinstance(facts, dict): | |
| persona["facts"] = list(facts.values()) | |
| else: | |
| persona["facts"] = [] | |
| # Initialize default_state if missing | |
| if "default_state" not in persona: | |
| persona["default_state"] = {} | |
| state = persona["default_state"] | |
| default_state_keys = { | |
| "anxiety": 0.5, | |
| "trust": 0.5, | |
| "openness": 0.5, | |
| "mode": "baseline", | |
| "emotional_memory": [] | |
| } | |
| for key, default in default_state_keys.items(): | |
| if key not in state: | |
| print(f"Warning: Missing state key '{key}' in persona. Using default value.") | |
| state[key] = default | |
| # Ensure tone_guidance exists and has at least 'baseline' | |
| if "tone_guidance" not in persona: | |
| persona["tone_guidance"] = { | |
| "baseline": { | |
| "voice": "Natural and authentic", | |
| "example": "I'm doing okay today, thanks for asking." | |
| } | |
| } | |
| return persona | |
| def validate_persona(persona): | |
| """ | |
| Validate that a persona has all necessary components for simulation. | |
| Returns (is_valid, error_message) | |
| """ | |
| if not persona.get("persona_name"): | |
| return False, "Persona must have a name" | |
| if not persona.get("system_prompt"): | |
| return False, "Persona must have a system_prompt" | |
| if not isinstance(persona.get("facts"), list): | |
| return False, "Persona 'facts' must be a list" | |
| state = persona.get("default_state", {}) | |
| for key in ["anxiety", "trust", "openness"]: | |
| value = state.get(key) | |
| if value is None: | |
| return False, f"default_state missing required key: {key}" | |
| if not isinstance(value, (int, float)): | |
| return False, f"default_state.{key} must be numeric" | |
| if not 0 <= value <= 1: | |
| return False, f"default_state.{key} must be between 0 and 1" | |
| return True, "Persona is valid" | |
| def save_persona(persona, path): | |
| """ | |
| Save a persona to YAML file. | |
| """ | |
| with open(path, "w", encoding="utf-8") as f: | |
| yaml.dump(persona, f, sort_keys=False, default_flow_style=False) | |
| return path | |
| def create_default_persona(name, age, role): | |
| """ | |
| Create a basic LitDigitalTwin persona template. | |
| """ | |
| persona = { | |
| "persona_name": name, | |
| "age": age, | |
| "role": role, | |
| "system_prompt": f"You are {name}, a {age}-year-old {role}. Respond naturally and stay in character.", | |
| "facts": [ | |
| f"{name} is {age} years old.", | |
| f"{name} works as a {role}." | |
| ], | |
| "tone_guidance": { | |
| "baseline": { | |
| "voice": "Natural and authentic", | |
| "example": "I'm doing okay today, thanks for asking." | |
| } | |
| }, | |
| "default_state": { | |
| "anxiety": 0.5, | |
| "trust": 0.5, | |
| "openness": 0.5, | |
| "mode": "baseline", | |
| "emotional_memory": [] | |
| } | |
| } | |
| return persona | |
| def list_available_personas(persona_dir="./personas"): | |
| """ | |
| List all available persona files. | |
| """ | |
| if not os.path.exists(persona_dir): | |
| return [] | |
| personas = [] | |
| for filename in os.listdir(persona_dir): | |
| if filename.endswith(".yml") or filename.endswith(".yaml"): | |
| path = os.path.join(persona_dir, filename) | |
| try: | |
| persona = load_persona(path) | |
| personas.append({ | |
| "filename": filename, | |
| "name": persona.get("persona_name", "Unknown"), | |
| "age": persona.get("age", ""), | |
| "role": persona.get("role", "") | |
| }) | |
| except Exception as e: | |
| print(f"Error loading {filename}: {e}") | |
| return personas |