IIIF-Studio / backend /app /services /ai /prompt_loader.py
Claude
fix: self-audit — 7 issues found and corrected
99f23f1 unverified
Raw
History Blame Contribute Delete
1.95 kB
"""
Chargement et rendu des templates de prompts depuis le système de fichiers (R04).
Les prompts vivent dans prompts/{profile_id}/{famille}_v{n}.txt.
Le code charge le fichier, substitue les variables {{nom}}, envoie à l'API.
"""
# 1. stdlib
import logging
import re
from functools import lru_cache
from pathlib import Path
logger = logging.getLogger(__name__)
@lru_cache(maxsize=32)
def _read_template(path_str: str) -> str:
"""Lit un template depuis le disque avec cache LRU."""
path = Path(path_str)
if not path.exists():
raise FileNotFoundError(f"Template introuvable : {path_str}")
return path.read_text(encoding="utf-8")
def load_and_render_prompt(template_path: str | Path, context: dict[str, str]) -> str:
"""Charge un template de prompt depuis un fichier et substitue les variables.
Les variables du template ont la forme {{nom_variable}}.
Toutes les clés de `context` sont substituées ; les clés absentes du template
sont ignorées silencieusement.
Args:
template_path: chemin vers le fichier template (.txt), absolu ou relatif au CWD.
context: dictionnaire {nom_variable: valeur} pour la substitution.
Returns:
Texte du prompt avec toutes les variables substituées.
Raises:
FileNotFoundError: si le fichier template n'existe pas.
"""
path = Path(template_path).resolve()
template = _read_template(str(path))
rendered = template
for key, value in context.items():
rendered = rendered.replace("{{" + key + "}}", value)
# Vérifier qu'il ne reste pas de variables non résolues (CLAUDE.md §8)
unresolved = re.findall(r"\{\{\w+\}\}", rendered)
if unresolved:
raise ValueError(f"Variables non résolues dans le prompt : {unresolved}")
logger.debug(
"Prompt chargé et rendu",
extra={"template": str(path), "variables": list(context.keys())},
)
return rendered