fix(poc-agent): ouvrir le chat Lea DGX si Tk est indisponible
Some checks failed
tests / Lint (ruff + black) (push) Failing after 1m43s
tests / Tests unitaires (sans GPU) (push) Failing after 1m46s
tests / Tests sécurité (critique) (push) Has been skipped

This commit is contained in:
Dom
2026-06-15 21:32:54 +02:00
parent 48879fb849
commit 2b1743c206

View File

@@ -5,6 +5,9 @@ Fenetre de chat Lea integree au systray — version tkinter native.
Remplace l'approche Edge browser par une vraie fenetre tkinter integree.
Design professionnel, theme clair, ancree en bas a droite de l'ecran.
Tourne dans son propre thread daemon pour ne pas bloquer pystray.
Le runtime Python embedded Windows ne contient pas toujours Tcl/Tk. Dans ce
cas, le menu "Discuter avec Lea" ouvre le chat DGX dans le navigateur.
"""
import logging
@@ -13,6 +16,8 @@ import math
import threading
import time
from datetime import datetime
from pathlib import Path
from urllib.parse import urlparse
from typing import Any, Callable, Dict, Optional
logger = logging.getLogger(__name__)
@@ -219,7 +224,10 @@ class ChatWindow:
def toggle(self) -> None:
"""Afficher/masquer la fenetre de chat."""
if self._destroyed or self._root is None:
if self._destroyed:
return
if self._root is None:
self._open_browser_fallback()
return
if self._visible:
self.hide()
@@ -228,7 +236,10 @@ class ChatWindow:
def show(self) -> None:
"""Afficher la fenetre."""
if self._destroyed or self._root is None:
if self._destroyed:
return
if self._root is None:
self._open_browser_fallback()
return
self._root.after(0, self._do_show)
@@ -257,6 +268,79 @@ class ChatWindow:
"""Mettre a jour le client serveur (appele si cree apres la fenetre)."""
self._server_client = server_client
def _chat_url(self) -> str:
"""Retourne l'URL web du chat, derivee de la config serveur."""
configured_url = self._chat_url_from_server_url(self._configured_server_url())
if self._server_client is not None:
chat_base = getattr(self._server_client, "_chat_base", None)
if chat_base:
chat_base = str(chat_base).rstrip("/")
if not self._is_local_url(chat_base):
return chat_base
if configured_url:
return configured_url
if configured_url:
return configured_url
host = (self._server_host or "localhost").strip()
if host.startswith(("http://", "https://")):
parsed = urlparse(host)
scheme = parsed.scheme or "http"
hostname = parsed.hostname or "localhost"
return f"{scheme}://{hostname}:{self._chat_port}"
return f"http://{host}:{self._chat_port}"
@staticmethod
def _is_local_url(url: str) -> bool:
try:
host = urlparse(url).hostname
except Exception:
return False
return host in {"localhost", "127.0.0.1", "::1"}
def _chat_url_from_server_url(self, server_url: Optional[str]) -> Optional[str]:
if not server_url:
return None
try:
parsed = urlparse(server_url.strip())
except Exception:
return None
if not parsed.hostname or parsed.hostname in {"localhost", "127.0.0.1", "::1"}:
return None
scheme = parsed.scheme or "http"
return f"{scheme}://{parsed.hostname}:{self._chat_port}"
def _configured_server_url(self) -> Optional[str]:
env_url = os.environ.get("RPA_SERVER_URL", "").strip()
if env_url:
return env_url
try:
# Installed layout: <app>/agent_v1/ui/chat_window.py.
for parent in Path(__file__).resolve().parents:
cfg = parent / "config.txt"
if cfg.exists():
for line in cfg.read_text(encoding="utf-8", errors="ignore").splitlines():
if line.startswith("RPA_SERVER_URL="):
return line.split("=", 1)[1].strip()
except Exception:
logger.debug("Lecture config.txt pour chat_url impossible", exc_info=True)
return None
def _open_browser_fallback(self) -> None:
"""Fallback POC quand tkinter est absent du Python embedded."""
url = self._chat_url()
try:
import webbrowser
if webbrowser.open(url, new=1):
logger.info("ChatWindow indisponible, chat ouvert dans le navigateur: %s", url)
else:
logger.warning("ChatWindow indisponible, ouverture navigateur refusee: %s", url)
except Exception as exc:
logger.error("Impossible d'ouvrir le chat dans le navigateur (%s): %s", url, exc)
def _on_shared_state_change(self, state) -> None:
"""Callback appele quand l'etat partage change (depuis le systray ou ailleurs).