""" Gestion des profils de personnalité pour l'assistant. Chaque profil est un fichier YAML dans le dossier profiles/ contenant : - name : nom affiché - description : courte description - system_prompt : prompt système (obligatoire) - documents : liste de fichiers texte/markdown à injecter dans le contexte (optionnel) - voice_language : surcharge de VOICE_LANGUAGE (optionnel) """ from __future__ import annotations from dataclasses import dataclass, field from pathlib import Path import yaml PROFILES_DIR = Path(__file__).parent.parent / "profiles" @dataclass class Profile: name: str system_prompt: str description: str = "" documents: list[str] = field(default_factory=list) voice_language: str | None = None mcp_servers: list[dict] = field(default_factory=list) def build_system_prompt(self) -> str: """Retourne le prompt système enrichi des documents de contexte.""" parts = [self.system_prompt.strip()] for doc_path in self.documents: full_path = PROFILES_DIR / doc_path if full_path.exists(): content = full_path.read_text(encoding="utf-8").strip() parts.append( f"\n---\n## Contexte : {full_path.stem}\n\n{content}" ) else: print(f"[Profile] Document introuvable : {full_path}") return "\n".join(parts) def list_profiles() -> list[tuple[str, str, str]]: """Retourne [(slug, name, description), ...] pour tous les profils disponibles.""" results = [] for yaml_file in sorted(PROFILES_DIR.glob("*.yaml")): slug = yaml_file.stem try: data = yaml.safe_load(yaml_file.read_text(encoding="utf-8")) results.append((slug, data.get("name", slug), data.get("description", ""))) except Exception as e: print(f"[Profile] Erreur lecture {yaml_file.name} : {e}") return results def load_profile(slug: str) -> Profile: """Charge un profil par son slug (nom de fichier sans extension).""" yaml_file = PROFILES_DIR / f"{slug}.yaml" if not yaml_file.exists(): raise FileNotFoundError(f"Profil introuvable : {slug} (cherché dans {PROFILES_DIR})") data = yaml.safe_load(yaml_file.read_text(encoding="utf-8")) return Profile( name=data.get("name", slug), description=data.get("description", ""), system_prompt=data.get("system_prompt", ""), documents=data.get("documents", []), voice_language=data.get("voice_language"), mcp_servers=data.get("mcp_servers", []), ) def apply_profile(slug: str) -> Profile: """Charge un profil et l'applique à la configuration active.""" from . import config, llm, mcp_client profile = load_profile(slug) config.SYSTEM_PROMPT = profile.build_system_prompt() if profile.voice_language: config.VOICE_LANGUAGE = profile.voice_language import assistant.tts as tts tts._default_voice_id = None llm.reset_history() # Réinitialise les serveurs MCP et charge ceux du profil manager = mcp_client.reset_manager() if profile.mcp_servers: from .mcp_client import MCPServerConfig, get_manager configs = [MCPServerConfig(**s) for s in profile.mcp_servers] get_manager().load_servers(configs) return profile