""" Chargeur de skills. Détecte les commandes dans une réponse LLM et exécute le skill correspondant. Format attendu dans la réponse du LLM : SEARCH: READ: REMEMBER: | RECALL: PROMPT_SAVE: | PROMPT_GET: PROMPT_LIST: PROMPT_DEL: Interface d'un skill : - Trigger unique → TRIGGER = "CMD:" + execute(args) -> str - Multi-triggers → TRIGGERS = {"CMD1:": "fn1", "CMD2:": "fn2"} """ import importlib from pathlib import Path SKILLS_DIR = Path(__file__).parent # Map trigger (uppercase) -> callable _REGISTRY: dict = {} def load_skills(): """Charge tous les skills disponibles dans le dossier skills/.""" _REGISTRY.clear() for py_file in sorted(SKILLS_DIR.glob("*.py")): if py_file.name.startswith("_") or py_file.name == "loader.py": continue module_name = "skills.{}".format(py_file.stem) try: mod = importlib.import_module(module_name) except Exception as e: print("[Skills] Impossible de charger {} : {}".format(py_file.name, e)) continue # Skill avec trigger unique : TRIGGER = "CMD:" + execute() if hasattr(mod, "TRIGGER") and mod.TRIGGER and hasattr(mod, "execute"): _REGISTRY[mod.TRIGGER.upper()] = mod.execute print("[Skills] Chargé : {}".format(mod.TRIGGER)) # Skill avec plusieurs triggers : TRIGGERS = {"CMD1:": "fn_name", ...} if hasattr(mod, "TRIGGERS") and isinstance(mod.TRIGGERS, dict): for trigger, fn_name in mod.TRIGGERS.items(): fn = getattr(mod, fn_name, None) if fn: _REGISTRY[trigger.upper()] = fn print("[Skills] Chargé : {}".format(trigger)) def run_skills(llm_response: str) -> tuple[bool, str]: """ Parcourt la réponse du LLM ligne par ligne. Si une commande est détectée, exécute le skill et retourne (True, résultat). Sinon retourne (False, réponse originale). """ for line in llm_response.splitlines(): stripped = line.strip() for trigger, fn in _REGISTRY.items(): if stripped.upper().startswith(trigger): args = stripped[len(trigger):].strip() result = fn(args) return True, result return False, llm_response