""" User persona — manages the optional user identity injected into the prompt. The persona is a simple dict with five optional string fields: name : the user's character name in the roleplay age : character age gender : character gender personality : brief personality description (e.g. "shy but curious") background : brief backstory (e.g. "a journalist investigating the case") All fields are optional. Blank fields are omitted from the prompt entirely. Injected into the system prompt via build_persona_prompt() in prompt_builder.py. UI form is added in Phase 9. This module handles backend logic only. """ PERSONA_FIELDS: tuple[str, ...] = ("name", "age", "gender", "personality", "background") def get_empty_persona() -> dict[str, str]: """Return a blank persona dict with all fields set to empty string.""" return {field: "" for field in PERSONA_FIELDS} def is_persona_empty(persona: dict[str, str] | None) -> bool: """Return True if persona is None or every field is blank.""" if not persona: return True return not any(persona.get(field, "").strip() for field in PERSONA_FIELDS) def validate_persona(raw: dict) -> dict[str, str]: """ Sanitise a persona input dict. - Strips whitespace from all values. - Fills missing fields with empty string. - Ignores unknown fields. Returns a clean persona dict guaranteed to have exactly PERSONA_FIELDS keys. """ return { field: str(raw.get(field, "")).strip() for field in PERSONA_FIELDS } def persona_summary(persona: dict[str, str] | None) -> str: """ Return a compact one-line display string for the UI (Phase 9). Examples: {"name": "Alex", "age": "28", "gender": ""} → "Alex · age 28" {} → "" """ if is_persona_empty(persona): return "" parts: list[str] = [] if persona.get("name"): parts.append(persona["name"]) if persona.get("age"): parts.append(f"age {persona['age']}") if persona.get("gender"): parts.append(persona["gender"]) return " · ".join(parts) if parts else "Custom persona active"