feat(agent_v1): toast paused supervisée Tkinter + Plan B + threshold FIND-TEXT 0.75
Some checks failed
tests / Lint (ruff + black) (push) Successful in 16s
tests / Tests unitaires (sans GPU) (push) Failing after 13s
tests / Tests sécurité (critique) (push) Has been skipped

Démo GHT 8 mai 2026 — Dom utilise UNIQUEMENT Léa V1 sur Windows pendant
la démo (pas le frontend VWB Linux), donc les pause_message du serveur
doivent être visuellement évidents sur l'écran Windows. Modifications
client validées par Dom + redéployées via SCP (procédure 2026-04-28).

1. ui/paused_toast.py (NEW) — Toast Tkinter custom autonome :
   Toplevel topmost overrideredirect, fond bleu Léa (#2563EB), 380px,
   haut-droite, auto-close 15s, click-to-close. Re-pin -topmost à
   100/500/2000 ms (Windows démet le flag quand le focus part). Rate
   limit 3s sur message identique. Aucune dépendance externe (tkinter
   stdlib uniquement). Thread-safe : root.after si Tk root existe,
   sinon Tk dédié dans un daemon thread. Remplace plyer qui s'avère
   silencieux sur Windows 11 (Focus Assist + manque app-id COM).

2. ui/chat_window.py — _add_paused_bubble force la visibilité :
   La fenêtre Léa démarrait avec root.withdraw() — la bulle paused
   était bien rendue mais invisible. Ajout deiconify+lift+focus_force
   avant render, plus appel à show_paused_toast en complément.

3. ui/notifications.py — niveau BLOCAGE déclenche aussi le toast :
   Quand notify_message reçoit un MessageUtilisateur.BLOCAGE (cible
   non trouvée, mode apprentissage, fenêtre incorrecte), appelle
   show_paused_toast en plus de plyer. Couvre la branche supervision
   client (executor.py:1012) qui ne passe pas par Plan B serveur.

4. core/executor.py — Plan B replay_paused (lignes 1812-1850) :
   Intercepte data["replay_paused"]=True dans la réponse /replay/next,
   appelle chat_window._add_paused_bubble si _chat_window_ref défini,
   sinon fallback notifier.notify. Idempotence via _last_pause_msg_shown
   pour ne pas spammer (1 toast par (replay_id, message) unique).
   Threshold FIND-TEXT _find_text_on_screen : 0.50 → 0.75 pour rejeter
   les faux positifs (placeholders italiques, tabs voisins) et tomber
   en mode apprentissage humain plutôt qu'un clic au pif.

5. main.py — Wiring ChatWindow → Executor pour Plan B.

6. tools/test_lea_toast.py + ui/_test_paused_toast.py (NEW) — Scripts
   de test isolé pour validation visuelle rapide sans relancer un
   replay complet (commande dans les docstrings).

Validé visuellement sur DESKTOP-58D5CAC. Toasts apparaissent en haut-
droite, fond bleu, auto-close 15s. Test isolé Dom : 3 toasts successifs
visibles sans accroc.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dom
2026-05-07 22:03:51 +02:00
parent 40440f1ca0
commit 7847a0e829
8 changed files with 551 additions and 3 deletions

View File

View File

@@ -0,0 +1,87 @@
# agent_v1/tools/test_lea_toast.py
"""
Test visuel rapide du toast Léa (démo GHT 8 mai 2026).
Lance trois scénarios de toast successifs pour valider l'affichage Windows :
1. Toast simple « pause supervisée »
2. Toast avec message long (vérifier wraplength)
3. Toast type BLOCAGE (= ce que voit l'utilisateur quand Léa est perdue)
Usage Windows :
C:\\rpa_vision\\.venv\\Scripts\\python.exe C:\\rpa_vision\\agent_v1\\tools\\test_lea_toast.py
Le script s'attend à voir trois toasts successifs en haut-droite de l'écran
principal, espacés de ~6 s, fond bleu Léa, autodismiss après 15 s ou clic.
"""
from __future__ import annotations
import sys
import time
from pathlib import Path
def _bootstrap_path() -> None:
"""Autoriser l'exécution directe sans -m : ajouter C:\\rpa_vision au sys.path."""
here = Path(__file__).resolve()
# On remonte : tools -> agent_v1 -> rpa_vision (parent du package agent_v1)
rpa_root = here.parent.parent.parent
if str(rpa_root) not in sys.path:
sys.path.insert(0, str(rpa_root))
def main() -> int:
_bootstrap_path()
# Import après ajout du path (les deux variantes fonctionnent)
try:
from agent_v1.ui.paused_toast import show_paused_toast
except Exception as e: # pragma: no cover (debug only)
print(f"[TEST] ERREUR import agent_v1.ui.paused_toast : {e}")
return 1
scenarios = [
(
"Toast 1/3 : pause simple",
"Léa a besoin de votre aide",
"Test 1/3 — Pause supervisée. Cliquez sur 'Continuer' dans la chat.",
),
(
"Toast 2/3 : message long",
"Léa — j'attends votre validation",
(
"Test 2/3 — J'ai trouvé 11 dossiers correspondant à vos critères "
"(UHCD, Forfait 1, PE2). Je vais traiter le dossier de M. DUPONT "
"Jean en premier. Pouvez-vous valider que c'est le bon ordre "
"avant que je continue ?"
),
),
(
"Toast 3/3 : blocage cible non trouvée",
"Léa — je ne vois pas l'élément",
(
"Test 3/3 — Je n'arrive pas à trouver « Examens cliniques » à "
"l'écran. Pouvez-vous me montrer où cliquer ?"
),
),
]
for label, title, message in scenarios:
print(f"[TEST] {label}")
ok = show_paused_toast(title=title, message=message)
print(f" show_paused_toast() = {ok}")
if not ok:
print(f" ECHEC : {label}")
# Espacer pour que Dom voit chaque toast distinctement
# (rate limit interne = 3s pour message identique, mais ici les
# messages diffèrent, le rate limit ne s'applique pas)
time.sleep(6)
print("[TEST] Attente 12s supplémentaires pour laisser le dernier toast vivre...")
time.sleep(12)
print("[TEST] OK — fin du test. Si vous avez vu 3 toasts bleus en haut-droite,")
print(" le mécanisme Léa pause est validé.")
return 0
if __name__ == "__main__":
sys.exit(main())