feat(agent_v1): brancher FeedbackBusClient dans ChatWindow tkinter
- 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) <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,15 @@ from typing import Any, Callable, Dict, Optional
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
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
|
# Theme — palette professionnelle claire
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -91,6 +100,7 @@ class ChatWindow:
|
|||||||
self._root = None
|
self._root = None
|
||||||
self._ready = threading.Event()
|
self._ready = threading.Event()
|
||||||
self._messages = [] # historique local
|
self._messages = [] # historique local
|
||||||
|
self._bus: Optional[Any] = None # FeedbackBusClient (J3.3, peut rester None)
|
||||||
|
|
||||||
# S'abonner aux changements de l'etat partage
|
# S'abonner aux changements de l'etat partage
|
||||||
if self._shared_state is not None:
|
if self._shared_state is not None:
|
||||||
@@ -266,6 +276,9 @@ class ChatWindow:
|
|||||||
# Signaler que la fenetre est prete
|
# Signaler que la fenetre est prete
|
||||||
self._ready.set()
|
self._ready.set()
|
||||||
|
|
||||||
|
# Demarrer le bus feedback Lea (events 'lea:*' temps reel)
|
||||||
|
self._start_feedback_bus()
|
||||||
|
|
||||||
# Boucle tkinter
|
# Boucle tkinter
|
||||||
root.mainloop()
|
root.mainloop()
|
||||||
|
|
||||||
@@ -608,6 +621,12 @@ class ChatWindow:
|
|||||||
|
|
||||||
def _do_destroy(self) -> None:
|
def _do_destroy(self) -> None:
|
||||||
"""Detruit la fenetre (appele dans le thread tkinter)."""
|
"""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:
|
if self._root is not None:
|
||||||
try:
|
try:
|
||||||
self._root.quit()
|
self._root.quit()
|
||||||
@@ -617,6 +636,39 @@ class ChatWindow:
|
|||||||
self._root = None
|
self._root = None
|
||||||
self._visible = False
|
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
|
# Ajout de messages dans la zone de chat
|
||||||
# ======================================================================
|
# ======================================================================
|
||||||
|
|||||||
Reference in New Issue
Block a user