Initial commit — nexus v2.0

This commit is contained in:
2026-03-09 09:01:33 +00:00
commit 6cd701d673
12 changed files with 798 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
"""
Skill DELEGATE — déléguer une tâche à un agent spécialisé via MQTT.
Usage LLM : SKILL:delegate ARGS:<agent_id> | <tâche>
"""
DESCRIPTION = "Déléguer une tâche à un agent spécialisé"
USAGE = "SKILL:delegate ARGS:<agent_id> | <tâche>"
def run(args: str, context) -> str:
if "|" not in args:
return "Format invalide. Usage : SKILL:delegate ARGS:<agent_id> | <tâche>"
agent_id, task = args.split("|", 1)
agent_id = agent_id.strip()
task = task.strip()
# Vérifier que l'agent est connu
caps = context.registry.get(agent_id)
if caps is None:
known = [a.agent_id for a in context.registry.all_agents()]
return f"Agent '{agent_id}' inconnu. Agents connus : {', '.join(known)}"
# Envoyer la tâche via MQTT
sent = context.mqtt.send_to(
recipient_id=agent_id,
payload=task,
reply_to=context.mqtt.topic_inbox(),
)
return f"Tâche déléguée à {agent_id} (id={sent.correlation_id[:8]}). Attente de la réponse..."
+59
View File
@@ -0,0 +1,59 @@
"""
Skill MEMORY — mémorisation persistante clé/valeur (SQLite).
Usage LLM :
SKILL:memory ARGS:set | <clé> | <valeur>
SKILL:memory ARGS:get | <clé>
SKILL:memory ARGS:list
SKILL:memory ARGS:delete | <clé>
"""
import sqlite3
import os
DESCRIPTION = "Mémorisation persistante d'informations clé/valeur"
USAGE = "SKILL:memory ARGS:set|<clé>|<valeur> ou get|<clé> ou list"
DB_PATH = os.path.join(os.path.dirname(__file__), "..", "data", "memory.db")
def _connect():
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
conn = sqlite3.connect(DB_PATH)
conn.execute("""
CREATE TABLE IF NOT EXISTS memory (
key TEXT PRIMARY KEY,
value TEXT NOT NULL,
updated_at TEXT DEFAULT (datetime('now'))
)
""")
return conn
def run(args: str, context) -> str:
parts = [p.strip() for p in args.split("|")]
action = parts[0].lower() if parts else ""
with _connect() as conn:
if action == "set" and len(parts) >= 3:
key, value = parts[1], "|".join(parts[2:])
conn.execute(
"INSERT OR REPLACE INTO memory (key, value, updated_at) VALUES (?, ?, datetime('now'))",
(key, value)
)
return f"Mémorisé : {key} = {value}"
if action == "get" and len(parts) >= 2:
row = conn.execute("SELECT value FROM memory WHERE key = ?", (parts[1],)).fetchone()
return row[0] if row else f"Clé '{parts[1]}' introuvable."
if action == "list":
rows = conn.execute("SELECT key, value FROM memory ORDER BY key").fetchall()
if not rows:
return "Mémoire vide."
return "\n".join(f" {k}: {v}" for k, v in rows)
if action == "delete" and len(parts) >= 2:
conn.execute("DELETE FROM memory WHERE key = ?", (parts[1],))
return f"Clé '{parts[1]}' supprimée."
return "Usage : SKILL:memory ARGS:set|clé|valeur ou get|clé ou list ou delete|clé"
+24
View File
@@ -0,0 +1,24 @@
"""
Skill MQTT_SEND — publier un message sur n'importe quel topic MQTT.
Permet au LLM (et à l'utilisateur) de publier librement sur le bus.
Usage LLM : SKILL:mqtt_send ARGS:<topic> | <message>
"""
DESCRIPTION = "Publier un message sur un topic MQTT arbitraire"
USAGE = "SKILL:mqtt_send ARGS:<topic> | <message>"
def run(args: str, context) -> str:
if "|" not in args:
return "Format invalide. Usage : SKILL:mqtt_send ARGS:<topic> | <message>"
topic, message = args.split("|", 1)
topic = topic.strip()
message = message.strip()
if not topic:
return "Topic vide."
context.mqtt.publish_raw(topic, message)
return f"Message publié sur '{topic}'."
+27
View File
@@ -0,0 +1,27 @@
"""
Skill WEB_READ — lire le contenu d'une URL.
Usage LLM : SKILL:web_read ARGS:<url>
"""
DESCRIPTION = "Lire le contenu d'une page web"
USAGE = "SKILL:web_read ARGS:<url>"
def run(args: str, context) -> str:
url = args.strip()
if not url:
return "URL vide."
try:
import requests
from bs4 import BeautifulSoup
resp = requests.get(url, timeout=15, headers={"User-Agent": "Mozilla/5.0"})
resp.raise_for_status()
soup = BeautifulSoup(resp.text, "html.parser")
# Supprime scripts et styles
for tag in soup(["script", "style", "nav", "footer"]):
tag.decompose()
text = soup.get_text(separator="\n", strip=True)
# Tronqué à 3000 caractères
return text[:3000] + ("..." if len(text) > 3000 else "")
except Exception as e:
return f"Erreur lecture URL : {e}"
+24
View File
@@ -0,0 +1,24 @@
"""
Skill WEB_SEARCH — recherche DuckDuckGo.
Usage LLM : SKILL:web_search ARGS:<requête>
"""
DESCRIPTION = "Recherche web via DuckDuckGo"
USAGE = "SKILL:web_search ARGS:<requête de recherche>"
def run(args: str, context) -> str:
query = args.strip()
if not query:
return "Requête vide."
try:
from duckduckgo_search import DDGS
results = []
with DDGS() as ddgs:
for r in ddgs.text(query, max_results=5):
results.append(f"- {r['title']}\n {r['href']}\n {r['body'][:200]}")
return "\n\n".join(results) if results else "Aucun résultat."
except ImportError:
return "Module duckduckgo_search non installé (pip install duckduckgo-search)"
except Exception as e:
return f"Erreur recherche : {e}"