""" 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: """ import importlib import re from pathlib import Path SKILLS_DIR = Path(__file__).parent # Map trigger -> fonction d'exécution _REGISTRY: dict = {} def load_skills(): """Charge tous les skills disponibles dans le dossier skills/.""" _REGISTRY.clear() for py_file in 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 un seul trigger (ex: SEARCH:, READ:) if hasattr(mod, "TRIGGER") and mod.TRIGGER and hasattr(mod, "execute"): _REGISTRY[mod.TRIGGER] = mod.execute print("[Skills] Chargé : {}".format(mod.TRIGGER)) # Skill memory : deux triggers if py_file.stem == "memory": if hasattr(mod, "remember"): _REGISTRY["REMEMBER:"] = mod.remember print("[Skills] Chargé : REMEMBER:") if hasattr(mod, "recall"): _REGISTRY["RECALL:"] = mod.recall print("[Skills] Chargé : RECALL:") 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(): line = line.strip() for trigger, fn in _REGISTRY.items(): if line.upper().startswith(trigger): args = line[len(trigger):].strip() result = fn(args) return True, result return False, llm_response