Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
- uses: actions/checkout@v7
- uses: actions/setup-python@v7
with:
python-version: "3.11"
python-version: "3.12"
cache: pip
- name: Installer les dépendances
run: |
Expand Down
14 changes: 7 additions & 7 deletions agents/antigravity_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@
(comme HACommandAgent)
"""

import os
import logging
import os

from agents.executor import ExecutorAgent
from core.state import TaskPayload, StateUpdate
from core.llm_gateway import LLMGateway
from core.state import StateUpdate, TaskPayload
from tools.tool_registry import ToolRegistry

logger = logging.getLogger(__name__)
Expand All @@ -28,10 +28,10 @@
class AntigravityAgent(ExecutorAgent):
"""
Agent expert spécialisé en raisonnement avancé, design LVGL et architecture.

Check failure on line 31 in agents/antigravity_agent.py

View workflow job for this annotation

GitHub Actions / 🔍 Ruff (informatif)

ruff (W293)

agents/antigravity_agent.py:31:1: W293 Blank line contains whitespace help: Remove whitespace from blank line
Hérite d'ExecutorAgent pour bénéficier de la boucle ReAct multi-tours
avec accès complet aux outils (read_file, write_file, run_terminal_command, etc.).

Check failure on line 34 in agents/antigravity_agent.py

View workflow job for this annotation

GitHub Actions / 🔍 Ruff (informatif)

ruff (W293)

agents/antigravity_agent.py:34:1: W293 Blank line contains whitespace help: Remove whitespace from blank line
Passage de BaseAgent → ExecutorAgent (A4 Audit).
"""

Expand All @@ -41,7 +41,7 @@
# Si pas de registry fourni, on en crée un vide (mode dégradé sans outils)
if tool_registry is None:
tool_registry = ToolRegistry()

super().__init__(
llm_gateway=llm_gateway,
tool_registry=tool_registry,
Expand All @@ -50,9 +50,9 @@
)
# Override du nom et du system prompt hérité d'ExecutorAgent
self.name = "antigravity_agent"

import sys

import os
import sys
is_windows = (sys.platform == 'win32' or os.name == 'nt')
if is_windows:
os_rule = "5. COMPATIBILITÉ WINDOWS : N'utilise jamais de commandes Unix (ls, grep, cat) via le terminal. Utilise à la place les outils de manipulation de fichiers de Python ('read_file', 'write_file') ou des commandes Windows natives."
Expand All @@ -77,14 +77,14 @@
async def invoke(self, payload: TaskPayload) -> StateUpdate:
"""
Exécution de l'agent expert avec injection contextuelle des templates LVGL.

Check failure on line 80 in agents/antigravity_agent.py

View workflow job for this annotation

GitHub Actions / 🔍 Ruff (informatif)

ruff (W293)

agents/antigravity_agent.py:80:1: W293 Blank line contains whitespace help: Remove whitespace from blank line
Si la tâche concerne le design LVGL/UI, les directives Premium sont
automatiquement injectées dans le contexte avant la boucle ReAct.
"""
# Chargement conditionnel des directives LVGL Premium
objective_lower = payload.task_objective.lower()
lvgl_keywords = ["lvgl", "ecran", "écran", "ui", "design", "layout", "widget", "dashboard"]

if any(kw in objective_lower for kw in lvgl_keywords):
lvgl_context = self._load_lvgl_templates()
if lvgl_context:
Expand All @@ -105,7 +105,7 @@
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
templates_path = os.path.join(project_root, "docs", "LVGL_PREMIUM_TEMPLATES.md")
if os.path.exists(templates_path):
with open(templates_path, "r", encoding="utf-8") as f:
with open(templates_path, encoding="utf-8") as f:
return "\n--- DIRECTIVES DESIGN LVGL PREMIUM ---\n" + f.read()
except Exception as le:
logger.warning(f"Impossible de charger les templates LVGL : {le}")
Expand Down
51 changes: 48 additions & 3 deletions agents/base_agent.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,64 @@
"""
agents/base_agent.py — Interface abstraite commune à tous les agents du moteur.

Un seul contrat : `invoke(payload: TaskPayload) -> StateUpdate`. Tous les agents
métier (executor, planner, reviewer, ha_agent, dreamer, antigravity, tool_maker,
prompt_engineer) en héritent directement ou via `ExecutorAgent`.

[#T296] Chaque `invoke()` est automatiquement encadré par la pose du nom de
l'agent dans une ContextVar (`core/agent_trace.py`), lue plus bas par
`token_tracker.record_usage()` pour renseigner `token_usage.agent_name`.
L'enveloppe est posée ici, sur la classe de base, plutôt que sur les quinze
call-sites de `.invoke()` répartis dans `dag_runner`, `swarm_dispatcher`,
`engine`, `healing` et `review_loop` : un agent qui oublierait de le faire
redeviendrait invisible dans la comptabilité, et c'est exactement le défaut
qu'on corrige.
"""
import functools
from abc import ABC, abstractmethod
from core.state import TaskPayload, StateUpdate

from core.agent_trace import poser_agent_courant, restaurer_agent_courant
from core.state import StateUpdate, TaskPayload


class BaseAgent(ABC):
"""
Classe abstraite fondamentale (Single Responsibility Principle).
Définit l'interface standard pour tous les agents du moteur.
"""

def __init__(self, name: str, system_prompt: str):
"""
Initialise l'agent avec son nom unique et son prompt système (règles métier).
"""
self.name = name
self.system_prompt = system_prompt


def __init_subclass__(cls, **kwargs):
"""
[#T296] Enveloppe l'`invoke()` de chaque sous-classe pour tracer l'agent courant.

Seules les classes qui définissent leur PROPRE `invoke` sont enveloppées :
une sous-classe qui hérite de celui de son parent (cas des agents dérivés
d'`ExecutorAgent`) réutilise l'enveloppe déjà posée, sans double pose.
"""
super().__init_subclass__(**kwargs)

fonction = cls.__dict__.get("invoke")
if fonction is None or getattr(fonction, "_trace_agent_posee", False):
return

@functools.wraps(fonction)
async def invoke_trace(self, payload, *args, **kwargs_appel):
jeton = poser_agent_courant(getattr(self, "name", cls.__name__))
try:
return await fonction(self, payload, *args, **kwargs_appel)
finally:
restaurer_agent_courant(jeton)

invoke_trace._trace_agent_posee = True
cls.invoke = invoke_trace

@abstractmethod
async def invoke(self, payload: TaskPayload) -> StateUpdate:
"""
Expand Down
13 changes: 10 additions & 3 deletions agents/dreamer_agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@
Exécute la consolidation mémoire en appelant les méthodes existantes.
Réutilise exactement le même code que engine.py::_consolidate_memory().
Exécuté de manière non-bloquante pour la boucle d'événements.

Check failure on line 170 in agents/dreamer_agent.py

View workflow job for this annotation

GitHub Actions / 🔍 Ruff (informatif)

ruff (W293)

agents/dreamer_agent.py:170:1: W293 Blank line contains whitespace help: Remove whitespace from blank line
Retourne un dict avec les compteurs d'actions effectuées.
"""
actions = {"decayed": 0, "gc_summarized": 0, "gc_archived": 0, "lessons_added": 0}
Expand Down Expand Up @@ -221,8 +221,8 @@

episode_store = EpisodeStore()
now = datetime.now()
cutoff_compress = now - timedelta(days=7) # Épisodes > 7j = candidats

Check failure on line 224 in agents/dreamer_agent.py

View workflow job for this annotation

GitHub Actions / 🔍 Ruff (informatif)

ruff (F841)

agents/dreamer_agent.py:224:9: F841 Local variable `cutoff_compress` is assigned to but never used help: Remove assignment to unused variable `cutoff_compress`
cutoff_purge = now - timedelta(days=EPISODE_TTL_DAYS) # > 30j = à purger

Check failure on line 225 in agents/dreamer_agent.py

View workflow job for this annotation

GitHub Actions / 🔍 Ruff (informatif)

ruff (F841)

agents/dreamer_agent.py:225:9: F841 Local variable `cutoff_purge` is assigned to but never used help: Remove assignment to unused variable `cutoff_purge`

# Charger TOUS les épisodes (y compris les expirés pour la purge)
all_episodes = await asyncio.to_thread(
Expand Down Expand Up @@ -323,7 +323,7 @@
pa_config: dict) -> dict | None:
"""
Envoie un prompt structuré au LLM pour analyser la journée.

Check failure on line 326 in agents/dreamer_agent.py

View workflow job for this annotation

GitHub Actions / 🔍 Ruff (informatif)

ruff (W293)

agents/dreamer_agent.py:326:1: W293 Blank line contains whitespace help: Remove whitespace from blank line
Le LLM retourne :
- Nouvelles leçons apprises
- Faits obsolètes à marquer
Expand Down Expand Up @@ -616,7 +616,9 @@
)

router = _get_app_state().get_shared_router()
initial_payload, starting_agent = await router.analyze_request(task["description"])
initial_payload, starting_agent = await router.analyze_request(
task["description"], session_id=str(task_id)
)

async def sse_callback(event_type, data, engine_inst):
pass
Expand Down Expand Up @@ -859,7 +861,7 @@
async def run_dreamer_cycle(pa_config: dict) -> dict[str, Any]:
"""
Exécute un cycle complet de consolidation mémoire.

Check failure on line 864 in agents/dreamer_agent.py

View workflow job for this annotation

GitHub Actions / 🔍 Ruff (informatif)

ruff (W293)

agents/dreamer_agent.py:864:1: W293 Blank line contains whitespace help: Remove whitespace from blank line
Pipeline :
1. Extraction des sessions de la veille (Thread)
2. Consolidation automatique (decay, GC) (Thread)
Expand Down Expand Up @@ -974,9 +976,14 @@
f"{ep_res.get('migrated', 0)} épisodes + "
f"{fact_res.get('migrated', 0)} faits migrés"
)
except ImportError:
logger.info("[DREAMER] [Etape 2.8] chromadb non installé — migration ignorée (non bloquant)")
report["actions"]["chroma_migration"] = {"skipped": True, "reason": "chromadb_absent"}
except Exception as _che:
logger.info(f"[DREAMER] [Etape 2.8] ChromaDB non disponible (non bloquant) : {_che}")
report["actions"]["chroma_migration"] = {"skipped": True}
# #T252 : ne plus masquer une vraie erreur de migration sous un faux
# « ChromaDB non disponible » — on nomme la cause réelle.
logger.warning(f"[DREAMER] [Etape 2.8] Erreur de migration ChromaDB (non bloquant) : {_che}")
report["actions"]["chroma_migration"] = {"skipped": True, "error": str(_che)}

# 2.9. Push du rapport d'anomalies vers le Tab5 M5Stack
logger.info("[DREAMER] [Etape 2.9] Push du rapport d'anomalies vers le Tab5 M5Stack...")
Expand Down Expand Up @@ -1117,11 +1124,11 @@
async def dreamer_main_loop():
"""
Boucle cron asyncio pour le dreamer.

Check failure on line 1127 in agents/dreamer_agent.py

View workflow job for this annotation

GitHub Actions / 🔍 Ruff (informatif)

ruff (W293)

agents/dreamer_agent.py:1127:1: W293 Blank line contains whitespace help: Remove whitespace from blank line
Double déclenchement :
1. Horaire fixe (dreamer_schedule, défaut 02:00)
2. Période d'inactivité (dreamer_idle_trigger_hours, défaut 3h)

Check failure on line 1131 in agents/dreamer_agent.py

View workflow job for this annotation

GitHub Actions / 🔍 Ruff (informatif)

ruff (W293)

agents/dreamer_agent.py:1131:1: W293 Blank line contains whitespace help: Remove whitespace from blank line
Vérifie toutes les 5 minutes si l'une des conditions est remplie.
"""
logger.info("[DREAMER] 🌙 Démarrage de la boucle autoDream")
Expand Down
Loading
Loading