68 lines
2.4 KiB
Python
68 lines
2.4 KiB
Python
"""
|
|
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: <requête>
|
|
READ: <url>
|
|
REMEMBER: <clé> | <valeur>
|
|
RECALL: <clé>
|
|
PROMPT_SAVE: <nom> | <texte>
|
|
PROMPT_GET: <nom>
|
|
PROMPT_LIST:
|
|
PROMPT_DEL: <nom>
|
|
|
|
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
|