diff --git a/visual_workflow_builder/backend/services/intelligent_executor.py b/visual_workflow_builder/backend/services/intelligent_executor.py index fc0d73841..fe1d54911 100644 --- a/visual_workflow_builder/backend/services/intelligent_executor.py +++ b/visual_workflow_builder/backend/services/intelligent_executor.py @@ -227,8 +227,8 @@ class IntelligentExecutor: # VWB workflows manuels : l'ancre peut être loin de la position d'origine # (résolution différente, écran différent, fenêtre déplacée) MAX_DISTANCE_PX = 500 # Tolérance large pour VWB cross-résolution - MIN_CLIP_SCORE = 0.50 # Score CLIP minimum (0.50 = ressemblance basique) - MIN_COMBINED_SCORE = 0.45 # Score combiné minimum pour accepter un match + MIN_CLIP_SCORE = 0.40 # Score CLIP minimum (baissé pour icônes génériques) + MIN_COMBINED_SCORE = 0.20 # Score combiné minimum (baissé pour cross-résolution) try: # Essayer d'importer et utiliser CLIP @@ -652,6 +652,17 @@ def zoned_template_match( } +_cached_executor: Optional['IntelligentExecutor'] = None + + +def _get_executor(detection_threshold: float = 0.35) -> 'IntelligentExecutor': + """Singleton IntelligentExecutor — modèles chargés une seule fois.""" + global _cached_executor + if _cached_executor is None: + _cached_executor = IntelligentExecutor(detection_threshold=detection_threshold) + return _cached_executor + + def find_and_click( anchor_image_base64: str, anchor_bbox: Optional[Dict[str, int]] = None, @@ -703,7 +714,7 @@ def find_and_click( if method == 'clip': print("🧠 [Vision] Essai UI-DETR-1 + CLIP (matching sémantique)...") try: - executor = IntelligentExecutor(detection_threshold=detection_threshold) + executor = _get_executor(detection_threshold) clip_result = executor.find_anchor_in_screen( screen_image=screen_image, anchor_image=anchor_image,