feat(agent_chat): Intégration exécution réelle avec ActionExecutor
- Import des composants d'exécution (ActionExecutor, ExecutionLoop, etc.) - Initialisation complète du pipeline d'exécution au démarrage - Remplacement de la simulation par exécution réelle : - Capture d'écran avec ScreenCapturer - Exécution des actions avec ActionExecutor - Gestion des erreurs et fallback en mode simulé - Mode dégradé automatique si composants non disponibles Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -48,6 +48,17 @@ try:
|
||||
except ImportError:
|
||||
GPU_AVAILABLE = False
|
||||
|
||||
# Execution components (optional - pour exécution réelle)
|
||||
try:
|
||||
from core.execution import ActionExecutor, TargetResolver, ErrorHandler
|
||||
from core.execution.execution_loop import ExecutionLoop, ExecutionMode as ExecMode, ExecutionState
|
||||
from core.pipeline.workflow_pipeline import WorkflowPipeline
|
||||
from core.capture import ScreenCapturer
|
||||
EXECUTION_AVAILABLE = True
|
||||
except ImportError as e:
|
||||
logger.warning(f"Composants d'exécution non disponibles: {e}")
|
||||
EXECUTION_AVAILABLE = False
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -63,6 +74,12 @@ confirmation_loop: Optional[ConfirmationLoop] = None
|
||||
response_generator: Optional[ResponseGenerator] = None
|
||||
conversation_manager: Optional[ConversationManager] = None
|
||||
|
||||
# Execution components
|
||||
workflow_pipeline = None
|
||||
action_executor = None
|
||||
execution_loop = None
|
||||
screen_capturer = None
|
||||
|
||||
execution_status = {
|
||||
"running": False,
|
||||
"workflow": None,
|
||||
@@ -110,6 +127,47 @@ def init_system():
|
||||
response_generator = ResponseGenerator()
|
||||
conversation_manager = ConversationManager()
|
||||
|
||||
# 4. Composants d'exécution réelle
|
||||
global workflow_pipeline, action_executor, execution_loop, screen_capturer
|
||||
if EXECUTION_AVAILABLE:
|
||||
try:
|
||||
# Pipeline de workflow (matching + actions)
|
||||
workflow_pipeline = WorkflowPipeline()
|
||||
logger.info("✓ WorkflowPipeline initialisé")
|
||||
|
||||
# Capture d'écran
|
||||
screen_capturer = ScreenCapturer()
|
||||
logger.info("✓ ScreenCapturer initialisé")
|
||||
|
||||
# Résolveur de cibles et gestionnaire d'erreurs
|
||||
target_resolver = TargetResolver()
|
||||
error_handler = ErrorHandler()
|
||||
|
||||
# Exécuteur d'actions
|
||||
action_executor = ActionExecutor(
|
||||
target_resolver=target_resolver,
|
||||
error_handler=error_handler,
|
||||
verify_postconditions=True
|
||||
)
|
||||
logger.info("✓ ActionExecutor initialisé")
|
||||
|
||||
# Boucle d'exécution (pour mode automatique)
|
||||
execution_loop = ExecutionLoop(
|
||||
pipeline=workflow_pipeline,
|
||||
action_executor=action_executor,
|
||||
screen_capturer=screen_capturer
|
||||
)
|
||||
logger.info("✓ ExecutionLoop initialisé")
|
||||
|
||||
except Exception as e:
|
||||
logger.warning(f"⚠ Composants d'exécution partiels: {e}")
|
||||
# Mode dégradé: simulation uniquement
|
||||
workflow_pipeline = None
|
||||
action_executor = None
|
||||
execution_loop = None
|
||||
else:
|
||||
logger.info("ℹ Mode simulation (composants d'exécution non disponibles)")
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Routes Web
|
||||
@@ -580,34 +638,34 @@ def handle_cancel():
|
||||
# =============================================================================
|
||||
|
||||
def execute_workflow(match, params):
|
||||
"""Exécuter un workflow avec le vrai système."""
|
||||
"""Exécuter un workflow avec le vrai système d'exécution."""
|
||||
global execution_status
|
||||
|
||||
|
||||
import time
|
||||
|
||||
|
||||
try:
|
||||
# Charger le workflow
|
||||
with open(match.workflow_path, 'r') as f:
|
||||
workflow_data = json.load(f)
|
||||
|
||||
|
||||
# Créer le VariableManager et injecter les paramètres
|
||||
var_manager = VariableManager()
|
||||
var_manager.set_variables(params)
|
||||
|
||||
|
||||
# Substituer les variables
|
||||
workflow_data = var_manager.substitute_dict(workflow_data)
|
||||
|
||||
|
||||
# Obtenir les étapes (edges)
|
||||
edges = workflow_data.get("edges", [])
|
||||
total_steps = len(edges) if edges else 5
|
||||
|
||||
total_steps = len(edges) if edges else 1
|
||||
|
||||
# Étape 1: Initialisation
|
||||
update_progress(10, "Initialisation", 1, total_steps + 2)
|
||||
time.sleep(0.5)
|
||||
|
||||
time.sleep(0.3)
|
||||
|
||||
# Étape 2: Préparation GPU (si disponible)
|
||||
if gpu_manager and GPU_AVAILABLE:
|
||||
update_progress(20, "Préparation GPU...", 2, total_steps + 2)
|
||||
update_progress(15, "Préparation GPU...", 2, total_steps + 2)
|
||||
try:
|
||||
loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(loop)
|
||||
@@ -615,36 +673,187 @@ def execute_workflow(match, params):
|
||||
loop.close()
|
||||
except Exception as e:
|
||||
logger.warning(f"GPU mode change failed: {e}")
|
||||
|
||||
# Exécuter chaque étape du workflow
|
||||
for i, edge in enumerate(edges):
|
||||
if not execution_status["running"]:
|
||||
break
|
||||
|
||||
action = edge.get("action", {})
|
||||
action_type = action.get("type", "unknown")
|
||||
|
||||
progress = int(20 + (i + 1) / total_steps * 70)
|
||||
step_name = f"Étape {i+1}: {action_type}"
|
||||
|
||||
update_progress(progress, step_name, i + 3, total_steps + 2)
|
||||
|
||||
# Simuler l'exécution de l'action
|
||||
# TODO: Connecter au vrai ActionExecutor
|
||||
time.sleep(0.8)
|
||||
|
||||
|
||||
# Vérifier si l'exécution réelle est disponible
|
||||
use_real_execution = (
|
||||
EXECUTION_AVAILABLE and
|
||||
action_executor is not None and
|
||||
screen_capturer is not None
|
||||
)
|
||||
|
||||
if use_real_execution:
|
||||
logger.info(f"🚀 Exécution RÉELLE du workflow: {match.workflow_name}")
|
||||
_execute_workflow_real(workflow_data, edges, total_steps, params)
|
||||
else:
|
||||
logger.info(f"🎭 Exécution SIMULÉE du workflow: {match.workflow_name}")
|
||||
_execute_workflow_simulated(edges, total_steps)
|
||||
|
||||
# Finalisation
|
||||
update_progress(95, "Finalisation...", total_steps + 1, total_steps + 2)
|
||||
time.sleep(0.3)
|
||||
|
||||
# Terminé avec succès
|
||||
finish_execution(match.workflow_name, True, "Workflow terminé avec succès")
|
||||
|
||||
if execution_status["running"]:
|
||||
update_progress(95, "Finalisation...", total_steps + 1, total_steps + 2)
|
||||
time.sleep(0.2)
|
||||
finish_execution(match.workflow_name, True, "Workflow terminé avec succès")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Execution error: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
finish_execution(match.workflow_name, False, f"Erreur: {str(e)}")
|
||||
|
||||
|
||||
def _execute_workflow_real(workflow_data, edges, total_steps, params):
|
||||
"""Exécution réelle avec ActionExecutor."""
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
|
||||
# Capturer l'écran initial
|
||||
update_progress(20, "Capture écran initial...", 2, total_steps + 2)
|
||||
|
||||
try:
|
||||
screenshot_path = screen_capturer.capture()
|
||||
logger.info(f"📸 Screenshot capturé: {screenshot_path}")
|
||||
except Exception as e:
|
||||
logger.warning(f"Capture écran échouée: {e}, utilisation mode dégradé")
|
||||
_execute_workflow_simulated(edges, total_steps)
|
||||
return
|
||||
|
||||
# Créer le ScreenState pour l'exécution
|
||||
try:
|
||||
from core.models import ScreenState
|
||||
screen_state = ScreenState.from_screenshot(screenshot_path)
|
||||
except Exception as e:
|
||||
logger.warning(f"Création ScreenState échouée: {e}")
|
||||
# Créer un ScreenState minimal
|
||||
screen_state = type('ScreenState', (), {
|
||||
'screenshot_path': screenshot_path,
|
||||
'detected_elements': [],
|
||||
'timestamp': datetime.now()
|
||||
})()
|
||||
|
||||
# Exécuter chaque edge avec ActionExecutor
|
||||
success_count = 0
|
||||
fail_count = 0
|
||||
|
||||
for i, edge in enumerate(edges):
|
||||
if not execution_status["running"]:
|
||||
logger.info("⏹️ Exécution annulée par l'utilisateur")
|
||||
break
|
||||
|
||||
action = edge.get("action", {})
|
||||
action_type = action.get("type", "unknown")
|
||||
|
||||
progress = int(20 + (i + 1) / total_steps * 70)
|
||||
step_name = f"Étape {i+1}/{total_steps}: {action_type}"
|
||||
|
||||
update_progress(progress, step_name, i + 3, total_steps + 2)
|
||||
logger.info(f"▶️ Exécution: {step_name}")
|
||||
|
||||
try:
|
||||
# Créer un objet Edge compatible avec ActionExecutor
|
||||
workflow_edge = _create_workflow_edge(edge, params)
|
||||
|
||||
# Exécuter l'action réelle
|
||||
result = action_executor.execute_edge(workflow_edge, screen_state)
|
||||
|
||||
if result.status.value == "success":
|
||||
success_count += 1
|
||||
logger.info(f"✅ {step_name} - Succès")
|
||||
|
||||
# Recapturer l'écran après chaque action réussie
|
||||
try:
|
||||
time.sleep(0.3) # Petit délai pour laisser l'UI se mettre à jour
|
||||
screenshot_path = screen_capturer.capture()
|
||||
screen_state = ScreenState.from_screenshot(screenshot_path)
|
||||
except:
|
||||
pass # Continuer même si la recapture échoue
|
||||
else:
|
||||
fail_count += 1
|
||||
logger.warning(f"⚠️ {step_name} - {result.status.value}: {result.message}")
|
||||
|
||||
# Continuer malgré l'échec (mode best-effort)
|
||||
if fail_count >= 3:
|
||||
logger.error("❌ Trop d'échecs, arrêt de l'exécution")
|
||||
break
|
||||
|
||||
except Exception as e:
|
||||
fail_count += 1
|
||||
logger.error(f"❌ Erreur lors de {step_name}: {e}")
|
||||
|
||||
if fail_count >= 3:
|
||||
logger.error("❌ Trop d'erreurs, arrêt de l'exécution")
|
||||
break
|
||||
|
||||
logger.info(f"📊 Résultat: {success_count} succès, {fail_count} échecs sur {total_steps} étapes")
|
||||
|
||||
|
||||
def _execute_workflow_simulated(edges, total_steps):
|
||||
"""Exécution simulée (fallback)."""
|
||||
import time
|
||||
|
||||
for i, edge in enumerate(edges):
|
||||
if not execution_status["running"]:
|
||||
break
|
||||
|
||||
action = edge.get("action", {})
|
||||
action_type = action.get("type", "unknown")
|
||||
|
||||
progress = int(20 + (i + 1) / total_steps * 70)
|
||||
step_name = f"Étape {i+1}: {action_type} (simulé)"
|
||||
|
||||
update_progress(progress, step_name, i + 3, total_steps + 2)
|
||||
|
||||
# Simulation avec délai
|
||||
time.sleep(0.5)
|
||||
logger.info(f"🎭 Simulé: {step_name}")
|
||||
|
||||
|
||||
def _create_workflow_edge(edge_dict, params):
|
||||
"""Créer un objet WorkflowEdge depuis un dictionnaire."""
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Optional, Dict, Any, List
|
||||
|
||||
@dataclass
|
||||
class Action:
|
||||
type: str
|
||||
target: Optional[Dict] = None
|
||||
value: Optional[str] = None
|
||||
parameters: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
@dataclass
|
||||
class WorkflowEdge:
|
||||
id: str
|
||||
source: str
|
||||
target: str
|
||||
action: Action
|
||||
pre_conditions: List[Dict] = field(default_factory=list)
|
||||
post_conditions: List[Dict] = field(default_factory=list)
|
||||
|
||||
action_dict = edge_dict.get("action", {})
|
||||
|
||||
# Substituer les paramètres dans l'action
|
||||
action_value = action_dict.get("value", "")
|
||||
if action_value and isinstance(action_value, str):
|
||||
for key, val in params.items():
|
||||
action_value = action_value.replace(f"${{{key}}}", str(val))
|
||||
action_value = action_value.replace(f"${key}", str(val))
|
||||
|
||||
action = Action(
|
||||
type=action_dict.get("type", "unknown"),
|
||||
target=action_dict.get("target"),
|
||||
value=action_value,
|
||||
parameters=action_dict.get("parameters", {})
|
||||
)
|
||||
|
||||
return WorkflowEdge(
|
||||
id=edge_dict.get("id", f"edge_{id(edge_dict)}"),
|
||||
source=edge_dict.get("source", ""),
|
||||
target=edge_dict.get("target", ""),
|
||||
action=action,
|
||||
pre_conditions=edge_dict.get("pre_conditions", []),
|
||||
post_conditions=edge_dict.get("post_conditions", [])
|
||||
)
|
||||
|
||||
|
||||
def update_progress(progress: int, message: str, current: int, total: int):
|
||||
"""Mettre à jour la progression."""
|
||||
global execution_status
|
||||
|
||||
Reference in New Issue
Block a user