From 6154423a9165706d5f339f89fb74010e985614e2 Mon Sep 17 00:00:00 2001 From: Dom Date: Tue, 28 Apr 2026 09:19:41 +0200 Subject: [PATCH] feat(agent_v1): brancher FeedbackBusClient dans ChatWindow tkinter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Import fail-safe : si python-socketio manquant (ancienne install Pauline), _HAS_FEEDBACK_BUS=False, ChatWindow tourne normalement sans bus - Bus démarré à la fin de _run_tk_loop si LEA_FEEDBACK_BUS=1 dans l'env - Callback _on_lea_event → _add_lea_message (thread-safe via root.after) - Cleanup : _bus.stop() ajouté dans _do_destroy avant la destruction tkinter Formatage des bulles minimal pour J3.3 (texte brut "[event] key=value"). Le style mixte métier+tech viendra en J3.4. La bulle paused interactive J3.5. Aucun crash si bus indisponible. Aucun changement de comportement si flag off. Co-Authored-By: Claude Opus 4.7 (1M context) --- agent_v0/agent_v1/ui/chat_window.py | 52 +++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/agent_v0/agent_v1/ui/chat_window.py b/agent_v0/agent_v1/ui/chat_window.py index 12f97c0b2..f393f3d72 100644 --- a/agent_v0/agent_v1/ui/chat_window.py +++ b/agent_v0/agent_v1/ui/chat_window.py @@ -16,6 +16,15 @@ from typing import Any, Callable, Dict, Optional logger = logging.getLogger(__name__) +# FeedbackBus : import fail-safe (le ChatWindow doit tourner même si python-socketio +# n'est pas installé sur le poste client, par exemple ancienne installation Pauline) +try: + from ..network.feedback_bus import FeedbackBusClient + _HAS_FEEDBACK_BUS = True +except Exception: + FeedbackBusClient = None # type: ignore + _HAS_FEEDBACK_BUS = False + # --------------------------------------------------------------------------- # Theme — palette professionnelle claire # --------------------------------------------------------------------------- @@ -91,6 +100,7 @@ class ChatWindow: self._root = None self._ready = threading.Event() self._messages = [] # historique local + self._bus: Optional[Any] = None # FeedbackBusClient (J3.3, peut rester None) # S'abonner aux changements de l'etat partage if self._shared_state is not None: @@ -266,6 +276,9 @@ class ChatWindow: # Signaler que la fenetre est prete self._ready.set() + # Demarrer le bus feedback Lea (events 'lea:*' temps reel) + self._start_feedback_bus() + # Boucle tkinter root.mainloop() @@ -608,6 +621,12 @@ class ChatWindow: def _do_destroy(self) -> None: """Detruit la fenetre (appele dans le thread tkinter).""" + if self._bus is not None: + try: + self._bus.stop() + except Exception: + pass + self._bus = None if self._root is not None: try: self._root.quit() @@ -617,6 +636,39 @@ class ChatWindow: self._root = None self._visible = False + # ====================================================================== + # FeedbackBus — bulles temps reel pendant l'execution (J3.3) + # ====================================================================== + + def _start_feedback_bus(self) -> None: + """Demarrer la connexion au bus 'lea:*' si flag actif et lib disponible.""" + if not _HAS_FEEDBACK_BUS: + logger.debug("FeedbackBus non disponible (python-socketio manquant)") + return + flag = os.environ.get("LEA_FEEDBACK_BUS", "0").lower() + if flag not in ("1", "true", "yes", "on"): + return + try: + url = f"http://{self._server_host}:{self._chat_port}" + token = os.environ.get("RPA_API_TOKEN", "") or None + self._bus = FeedbackBusClient(url, token=token, on_event=self._on_lea_event) + self._bus.start() + logger.info("FeedbackBus demarre : %s", url) + except Exception: + logger.debug("FeedbackBus init silenced", exc_info=True) + self._bus = None + + def _on_lea_event(self, event: str, payload: Dict[str, Any]) -> None: + """Callback bus → bulle Lea. Thread-safe : _add_lea_message utilise root.after.""" + short = event.removeprefix("lea:") if event.startswith("lea:") else event + parts = [] + for key in ("workflow", "step", "reason", "message", "failed_action"): + v = (payload or {}).get(key) + if v not in (None, ""): + parts.append(f"{key}={v}") + suffix = " — " + ", ".join(parts) if parts else "" + self._add_lea_message(f"[{short}]{suffix}") + # ====================================================================== # Ajout de messages dans la zone de chat # ======================================================================