feat(vwb): Ajouter SeeClick, Self-Healing interactif et Dashboard confiance

## Nouvelles fonctionnalités

### 1. SeeClick Adapter (visual grounding fallback)
- Nouvel adapter pour le modèle SeeClick (HuggingFace)
- Intégré dans la chaîne de fallback: CLIP → Template → SeeClick → Static
- Localise les éléments GUI à partir de descriptions textuelles

### 2. Self-Healing Interactif
- Dialogue qui propose des alternatives quand l'ancre n'est pas trouvée
- L'utilisateur peut choisir: candidat alternatif, coords statiques, ou sauter
- Nouveaux endpoints: /healing/status, /healing/choose, /healing/candidates
- État "waiting_for_choice" pour mettre l'exécution en pause

### 3. Dashboard Confiance (temps réel)
- Affiche les scores de confiance pendant l'exécution
- Montre: méthode utilisée, distance, taux de succès
- Interface pliable en bas à droite
- Visible uniquement en mode intelligent/debug

## Fichiers ajoutés
- core/detection/seeclick_adapter.py
- frontend_v4/src/components/SelfHealingDialog.tsx
- frontend_v4/src/components/ConfidenceDashboard.tsx

## Fichiers modifiés
- core/detection/__init__.py
- backend/services/intelligent_executor.py
- backend/api_v3/execute.py
- frontend_v4/src/App.tsx
- frontend_v4/src/services/api.ts
- docs/VISION_RPA_INTELLIGENT.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Dom
2026-01-24 02:34:01 +01:00
parent f04f156144
commit 21bfa3b337
9 changed files with 1656 additions and 13 deletions

View File

@@ -6,11 +6,18 @@ Utilise UI-DETR-1 pour la détection et le matching d'ancres visuelles
import time
import base64
import io
import sys
import os
from typing import Dict, Any, Optional, List, Tuple
from dataclasses import dataclass
from PIL import Image
import numpy as np
# Ajouter le chemin racine pour les imports de core
RPA_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
if RPA_ROOT not in sys.path:
sys.path.insert(0, RPA_ROOT)
# Import du service de détection UI
from .ui_detection_service import detect_ui_elements, DetectionResult, UIElement
@@ -764,7 +771,53 @@ def find_and_click(
global_result['search_time_ms'] = (_time.time() - start_time) * 1000
return global_result
# === STRATÉGIE 4: Coordonnées statiques (dernier recours) ===
# === STRATÉGIE 4: SeeClick (visual grounding) ===
# Essayer SeeClick si les autres méthodes ont échoué
try:
print("🎯 [Vision] Essai SeeClick (visual grounding)...")
from core.detection.seeclick_adapter import get_seeclick
seeclick = get_seeclick()
if seeclick.available:
# Utiliser une description générique basée sur l'ancre
# TODO: Améliorer avec une description plus précise
description = "the clickable element or button"
grounding_result = seeclick.ground(screen_image, description, return_pixels=True)
if grounding_result.found:
found_x = grounding_result.x_pixel
found_y = grounding_result.y_pixel
# Vérifier la distance à la position originale si anchor_bbox existe
accept_seeclick = True
if anchor_bbox:
orig_x = anchor_bbox.get('x', 0) + anchor_bbox.get('width', 0) // 2
orig_y = anchor_bbox.get('y', 0) + anchor_bbox.get('height', 0) // 2
distance = np.sqrt((found_x - orig_x)**2 + (found_y - orig_y)**2)
MAX_SEECLICK_DISTANCE = 200 # Plus permissif car c'est un fallback
if distance > MAX_SEECLICK_DISTANCE:
print(f"⛔ [Vision] SeeClick rejeté: distance {distance:.0f}px > {MAX_SEECLICK_DISTANCE}px max")
accept_seeclick = False
if accept_seeclick:
print(f"✅ [Vision] SeeClick réussi! Coordonnées: ({found_x}, {found_y})")
return {
'found': True,
'confidence': grounding_result.confidence,
'coordinates': {'x': found_x, 'y': found_y},
'bbox': anchor_bbox,
'method': 'seeclick_grounding',
'search_time_ms': (_time.time() - start_time) * 1000,
'raw_output': grounding_result.raw_output
}
except ImportError:
print(" [Vision] SeeClick non disponible (module non trouvé)")
except Exception as seeclick_err:
print(f"⚠️ [Vision] Erreur SeeClick: {seeclick_err}")
# === STRATÉGIE 5: Coordonnées statiques (dernier recours) ===
if anchor_bbox:
best_conf = max(global_result.get('confidence', 0), 0)