72 lines
2.6 KiB
Python
72 lines
2.6 KiB
Python
"""Active-persona selection — which script file Gemini loads as its system
|
|
prompt.
|
|
|
|
The operator can keep several persona variants in scripts/ (e.g.
|
|
``sanad_script.txt``, ``sanad_script_v1.txt``, ``sanad_script_v2.txt``) and pick
|
|
which one is live. The selection is a single basename stored in
|
|
``data/active_persona.txt``; the DEFAULT (and reset target) is always the
|
|
configured persona (``sanad_script.txt``). The Gemini child resolves this at
|
|
session start, so a new selection takes effect on the next voice (re)connect.
|
|
|
|
A missing/blank/stale pointer transparently falls back to the default, so this
|
|
can never break the voice — worst case it loads ``sanad_script.txt``.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
from Project.Sanad.config import DATA_DIR, SCRIPTS_DIR
|
|
|
|
ACTIVE_PERSONA_FILE = DATA_DIR / "active_persona.txt"
|
|
|
|
|
|
def default_persona_name() -> str:
|
|
"""The configured default persona filename (core.script_files.persona)."""
|
|
try:
|
|
from Project.Sanad.core.config_loader import section as _section
|
|
name = (_section("core", "script_files") or {}).get("persona")
|
|
return (name or "sanad_script.txt").strip() or "sanad_script.txt"
|
|
except Exception:
|
|
return "sanad_script.txt"
|
|
|
|
|
|
def active_persona_name() -> str:
|
|
"""Selected persona basename — the chosen variant if set AND still exists,
|
|
otherwise the default. Never raises."""
|
|
default = default_persona_name()
|
|
try:
|
|
sel = ACTIVE_PERSONA_FILE.read_text(encoding="utf-8").strip()
|
|
except Exception:
|
|
sel = ""
|
|
if sel:
|
|
cand = SCRIPTS_DIR / Path(sel).name # basename only — no traversal
|
|
if cand.is_file():
|
|
return cand.name
|
|
return default
|
|
|
|
|
|
def active_persona_path() -> Path:
|
|
"""Full path to the persona script Gemini should load right now."""
|
|
return SCRIPTS_DIR / active_persona_name()
|
|
|
|
|
|
def set_active_persona(name: str | None) -> str:
|
|
"""Persist the selected persona basename. Passing None/"" or the default
|
|
name clears the pointer (revert to default). Returns the effective active
|
|
name. Raises FileNotFoundError if a non-default name doesn't exist."""
|
|
nm = (Path(str(name)).name if name else "").strip()
|
|
default = default_persona_name()
|
|
if not nm or nm == default:
|
|
try:
|
|
ACTIVE_PERSONA_FILE.unlink()
|
|
except FileNotFoundError:
|
|
pass
|
|
except Exception:
|
|
pass
|
|
return default
|
|
if not (SCRIPTS_DIR / nm).is_file():
|
|
raise FileNotFoundError(nm)
|
|
DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|
ACTIVE_PERSONA_FILE.write_text(nm, encoding="utf-8")
|
|
return nm
|