refactor: nettoyage agent + fix SomEngine review (singleton partagé, cache, thread-safe)
Nettoyage Windows agent : - Suppression lea_ui inutilisés (chat_widget, overlay, styles, etc. — -1991 lignes) - Suppression window_info*.py dupliqués (racine + core/ — -494 lignes) - build/ + dist/ supprimés (48 MB PyInstaller abandonné, gitignorés) Fix SomEngine (review quality guardian) : - Singleton GPU partagé via get_shared_engine() (1 instance au lieu de 2) - Thread-safe avec threading.Lock (double-checked locking) - Cache SomResult par screenshot_id (max 50, évite YOLO+OCR redondants) - Fuite fichier temp docTR corrigée (finally block) - Chemin YOLO configurable via SOM_YOLO_WEIGHTS env var - Guard som_image None avant VLM - Match texte partiel : len(label) >= 3 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -431,21 +431,17 @@ def _needs_post_wait(action: dict) -> int:
|
||||
# SomEngine — enrichissement Set-of-Mark des clics pendant le build_replay
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
_som_engine = None # Singleton, chargé à la demande
|
||||
_som_cache: Dict[str, Any] = {} # screenshot_id -> SomResult (cache build_replay)
|
||||
_SOM_CACHE_MAX = 50
|
||||
|
||||
|
||||
def _get_som_engine():
|
||||
"""Singleton SomEngine (lazy-loaded, GPU)."""
|
||||
global _som_engine
|
||||
if _som_engine is None:
|
||||
try:
|
||||
from core.detection.som_engine import SomEngine
|
||||
_som_engine = SomEngine(device="cuda")
|
||||
logger.info("SomEngine initialisé (lazy singleton)")
|
||||
except Exception as e:
|
||||
logger.warning("SomEngine non disponible : %s", e)
|
||||
_som_engine = False # Marqueur "indisponible"
|
||||
return _som_engine if _som_engine is not False else None
|
||||
"""Singleton SomEngine partagé."""
|
||||
try:
|
||||
from core.detection.som_engine import get_shared_engine
|
||||
return get_shared_engine()
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
|
||||
def _som_identify_clicked_element(
|
||||
@@ -486,19 +482,38 @@ def _som_identify_clicked_element(
|
||||
if not full_path.is_file():
|
||||
return None
|
||||
|
||||
try:
|
||||
from PIL import Image
|
||||
img = Image.open(full_path).convert("RGB")
|
||||
except Exception as e:
|
||||
logger.debug("SoM: impossible de charger %s : %s", full_path, e)
|
||||
return None
|
||||
# Vérifier le cache SomResult par (session_dir, screenshot_id)
|
||||
cache_key = f"{session_dir}:{screenshot_id}"
|
||||
if cache_key in _som_cache:
|
||||
result = _som_cache[cache_key]
|
||||
else:
|
||||
try:
|
||||
from PIL import Image
|
||||
img = Image.open(full_path).convert("RGB")
|
||||
except Exception as e:
|
||||
logger.debug("SoM: impossible de charger %s : %s", full_path, e)
|
||||
return None
|
||||
|
||||
# Lancer SomEngine
|
||||
try:
|
||||
result = engine.analyze(img)
|
||||
except Exception as e:
|
||||
logger.warning("SoM: erreur d'analyse : %s", e)
|
||||
return None
|
||||
# Lancer SomEngine
|
||||
try:
|
||||
result = engine.analyze(img)
|
||||
except Exception as e:
|
||||
logger.warning("SoM: erreur d'analyse : %s", e)
|
||||
return None
|
||||
|
||||
# Stocker dans le cache (éléments seulement, pas l'image annotée)
|
||||
from core.detection.som_engine import SomResult
|
||||
cached = SomResult(
|
||||
elements=result.elements,
|
||||
width=result.width,
|
||||
height=result.height,
|
||||
analysis_time_ms=result.analysis_time_ms,
|
||||
)
|
||||
if len(_som_cache) >= _SOM_CACHE_MAX:
|
||||
# Supprimer la plus ancienne entrée (FIFO)
|
||||
oldest_key = next(iter(_som_cache))
|
||||
del _som_cache[oldest_key]
|
||||
_som_cache[cache_key] = cached
|
||||
|
||||
if not result.elements:
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user