101 lines
3.3 KiB
Python
101 lines
3.3 KiB
Python
"""
|
|
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
|