import yaml import os def load_persona(path): """ Load a mental health persona from YAML file. Validates required fields for OT simulation. """ 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 keys for mental health personas required_keys = [ "persona_name", "age", "role", "system_prompt", "facts", "default_state" ] for key in required_keys: if key not in persona: raise ValueError(f"Missing required key in persona: {key}") # Ensure default_state has required emotional metrics state = persona.get("default_state", {}) required_state_keys = ["anxiety", "trust", "openness", "mode"] for key in required_state_keys: if key not in state: print(f"Warning: Missing state key '{key}' in persona. Using default value.") if key == "mode": state[key] = "baseline" else: state[key] = 0.5 # Initialize emotional_memory if not present if "emotional_memory" not in state: state["emotional_memory"] = [] # 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"] = [] return persona def validate_persona(persona): """ Validate that a persona has all necessary components for simulation. Returns (is_valid, error_message) """ # Check persona name if not persona.get("persona_name"): return False, "Persona must have a name" # Check default state state = persona.get("default_state", {}) if not state: return False, "Persona must have a default_state" # Check that anxiety, trust, openness are numeric 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" # Check tone_guidance exists if not persona.get("tone_guidance"): return False, "Persona must have tone_guidance" # Check that tone_guidance has mode entries tone_guidance = persona.get("tone_guidance", {}) recommended_modes = ["baseline", "guarded", "triggered", "trusting", "decompensating"] missing_modes = [mode for mode in recommended_modes if mode not in tone_guidance] if missing_modes: print(f"Warning: tone_guidance missing modes: {missing_modes}") 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 persona template for development/testing. """ 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}" ], "triggers": [ "criticism", "pressure", "isolation" ], "reasoning_style": "Tends to analyze situations carefully before responding.", "tone_guidance": { "baseline": { "voice": "Calm and thoughtful", "example": "I'm doing okay today, thanks for asking." }, "guarded": { "voice": "Brief and cautious", "example": "I'd rather not talk about that right now." }, "triggered": { "voice": "Defensive or withdrawn", "example": "I don't see how that's relevant." }, "trusting": { "voice": "Open and reflective", "example": "You know, I've been thinking about what you said last time..." }, "decompensating": { "voice": "Fragmented and overwhelmed", "example": "I just... I can't... it's too much." } }, "default_state": { "anxiety": 0.5, "trust": 0.5, "openness": 0.5, "mode": "baseline", "emotional_memory": [] }, "scripts": { "crisis": "I'm not feeling safe right now. I need to step away.", "deflection": "It's fine. I don't want to make a big deal out of it.", "testing_trust": "Why are you asking about that?", "resistance": "I don't see how talking about this helps." }, "resilience_hooks": [ f"{name} has coping strategies they've used before", f"{name} values certain relationships in their life" ] } 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