Suppression du .git embarqué dans agent_v0/ — le code est maintenant tracké normalement dans le repo principal. Inclut : agent_v1 (client), server_v1 (streaming), lea_ui (chat client) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
219 lines
6.1 KiB
Python
219 lines
6.1 KiB
Python
# agent_v0/lea_ui/launcher.py
|
|
"""
|
|
Point d'entree pour le panneau Lea.
|
|
|
|
Lancement autonome :
|
|
python -m agent_v0.lea_ui.launcher
|
|
|
|
Ou integre dans agent_v0/agent_v1/main.py avec flag --ui lea.
|
|
|
|
Ce module :
|
|
1. Cree l'application Qt
|
|
2. Instancie LeaServerClient
|
|
3. Instancie LeaMainWindow
|
|
4. Enregistre un raccourci global (Ctrl+Shift+L) via keyboard hook
|
|
5. Lance la boucle Qt
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import logging
|
|
import os
|
|
import sys
|
|
from typing import Optional
|
|
|
|
logger = logging.getLogger("lea_ui.launcher")
|
|
|
|
|
|
def _setup_logging(verbose: bool = False) -> None:
|
|
"""Configurer le logging pour le panneau Lea."""
|
|
level = logging.DEBUG if verbose else logging.INFO
|
|
logging.basicConfig(
|
|
level=level,
|
|
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
|
|
datefmt="%H:%M:%S",
|
|
)
|
|
|
|
|
|
def _setup_global_hotkey(window) -> Optional[object]:
|
|
"""Enregistrer le raccourci global Ctrl+Shift+L pour afficher/cacher le panneau.
|
|
|
|
Utilise la librairie keyboard si disponible (Windows/Linux).
|
|
Retourne le hook pour pouvoir le desinscrire a l'arret.
|
|
"""
|
|
try:
|
|
import keyboard
|
|
|
|
def on_hotkey():
|
|
# Appeler toggle_visibility dans le thread Qt
|
|
from PyQt5.QtCore import QTimer
|
|
QTimer.singleShot(0, window.toggle_visibility)
|
|
|
|
keyboard.add_hotkey("ctrl+shift+l", on_hotkey)
|
|
logger.info("Raccourci global Ctrl+Shift+L enregistre")
|
|
return True
|
|
except ImportError:
|
|
logger.info(
|
|
"Librairie 'keyboard' non disponible — "
|
|
"raccourci global Ctrl+Shift+L non enregistre. "
|
|
"Installez-la avec: pip install keyboard"
|
|
)
|
|
return None
|
|
except Exception as e:
|
|
logger.warning("Impossible d'enregistrer le raccourci global : %s", e)
|
|
return None
|
|
|
|
|
|
def _load_environment() -> None:
|
|
"""Charger les variables d'environnement depuis .env.local."""
|
|
env_paths = [
|
|
os.path.join(os.path.dirname(__file__), "..", "..", ".env.local"),
|
|
os.path.join(os.path.dirname(__file__), "..", ".env.local"),
|
|
]
|
|
for env_path in env_paths:
|
|
env_path = os.path.abspath(env_path)
|
|
if os.path.exists(env_path):
|
|
try:
|
|
from dotenv import load_dotenv
|
|
load_dotenv(env_path)
|
|
logger.info("Variables d'environnement chargees depuis %s", env_path)
|
|
return
|
|
except ImportError:
|
|
# Fallback : chargement manuel
|
|
with open(env_path, "r", encoding="utf-8") as f:
|
|
for line in f:
|
|
line = line.strip()
|
|
if line and not line.startswith("#") and "=" in line:
|
|
key, value = line.split("=", 1)
|
|
value = value.strip("\"'")
|
|
os.environ[key.strip()] = value
|
|
logger.info("Variables chargees manuellement depuis %s", env_path)
|
|
return
|
|
|
|
|
|
def launch_lea(
|
|
server_host: Optional[str] = None,
|
|
chat_port: int = 5004,
|
|
stream_port: int = 5005,
|
|
verbose: bool = False,
|
|
session_id: Optional[str] = None,
|
|
) -> None:
|
|
"""Lancer le panneau Lea.
|
|
|
|
Args:
|
|
server_host: adresse du serveur Linux (None = auto-detection)
|
|
chat_port: port du serveur chat
|
|
stream_port: port du serveur streaming
|
|
verbose: mode debug
|
|
session_id: identifiant de session pour le polling replay
|
|
"""
|
|
_setup_logging(verbose)
|
|
_load_environment()
|
|
|
|
# Import PyQt5 ici pour un message d'erreur clair si absent
|
|
try:
|
|
from PyQt5.QtWidgets import QApplication
|
|
from PyQt5.QtCore import Qt
|
|
except ImportError:
|
|
logger.error(
|
|
"PyQt5 n'est pas installe. Installez-le avec :\n"
|
|
" pip install PyQt5"
|
|
)
|
|
sys.exit(1)
|
|
|
|
from .server_client import LeaServerClient
|
|
from .main_window import LeaMainWindow
|
|
|
|
# Creer ou recuperer l'application Qt
|
|
app = QApplication.instance()
|
|
if app is None:
|
|
app = QApplication(sys.argv)
|
|
app.setQuitOnLastWindowClosed(False)
|
|
|
|
# Client serveur
|
|
client = LeaServerClient(
|
|
server_host=server_host,
|
|
chat_port=chat_port,
|
|
stream_port=stream_port,
|
|
)
|
|
|
|
# Fenetre principale
|
|
window = LeaMainWindow(server_client=client)
|
|
window.show()
|
|
|
|
# Raccourci global
|
|
hotkey = _setup_global_hotkey(window)
|
|
|
|
# Polling replay (si session_id fourni)
|
|
if session_id:
|
|
client.start_polling(session_id)
|
|
|
|
logger.info(
|
|
"Panneau Lea demarre — serveur=%s, chat_port=%d, stream_port=%d",
|
|
client.server_host, chat_port, stream_port,
|
|
)
|
|
|
|
# Boucle Qt
|
|
try:
|
|
exit_code = app.exec_()
|
|
finally:
|
|
window.shutdown()
|
|
if hotkey:
|
|
try:
|
|
import keyboard
|
|
keyboard.unhook_all()
|
|
except Exception:
|
|
pass
|
|
|
|
sys.exit(exit_code)
|
|
|
|
|
|
def main() -> None:
|
|
"""Point d'entree CLI."""
|
|
parser = argparse.ArgumentParser(
|
|
description="Panneau Lea — Interface utilisateur RPA Vision V3",
|
|
)
|
|
parser.add_argument(
|
|
"--server", "-s",
|
|
dest="server_host",
|
|
default=None,
|
|
help="Adresse du serveur Linux (defaut: RPA_SERVER_HOST ou localhost)",
|
|
)
|
|
parser.add_argument(
|
|
"--chat-port",
|
|
type=int,
|
|
default=5004,
|
|
help="Port du serveur chat (defaut: 5004)",
|
|
)
|
|
parser.add_argument(
|
|
"--stream-port",
|
|
type=int,
|
|
default=5005,
|
|
help="Port du serveur streaming (defaut: 5005)",
|
|
)
|
|
parser.add_argument(
|
|
"--session-id",
|
|
default=None,
|
|
help="Identifiant de session pour le polling replay",
|
|
)
|
|
parser.add_argument(
|
|
"--verbose", "-v",
|
|
action="store_true",
|
|
help="Mode debug (logs verbeux)",
|
|
)
|
|
|
|
args = parser.parse_args()
|
|
|
|
launch_lea(
|
|
server_host=args.server_host,
|
|
chat_port=args.chat_port,
|
|
stream_port=args.stream_port,
|
|
verbose=args.verbose,
|
|
session_id=args.session_id,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|