diff --git a/core/execution/input_handler.py b/core/execution/input_handler.py index b9aab44f3..bd0bd9e5b 100644 --- a/core/execution/input_handler.py +++ b/core/execution/input_handler.py @@ -450,8 +450,7 @@ def _grounding_ocr(target_text: str, anchor_bbox: Optional[Dict] = None) -> Opti - Si anchor_bbox fourni → le plus proche de la position originale - Sinon → le plus proche du centre de l'écran (zone contenu) """ - logger.warning(f"[OCR-V2] Entrée _grounding_ocr target='{target_text}' bbox={anchor_bbox}") - import sys; sys.stdout.write(f" [OCR-V2] target='{target_text}' bbox={anchor_bbox}\n"); sys.stdout.flush() + logger.debug(f"[Grounding/OCR] target='{target_text}' bbox={anchor_bbox}") if not target_text: return None diff --git a/core/execution/observe_reason_act.py b/core/execution/observe_reason_act.py index d32c33030..4de925ed6 100644 --- a/core/execution/observe_reason_act.py +++ b/core/execution/observe_reason_act.py @@ -17,7 +17,7 @@ import base64 import os import json import re -from dataclasses import dataclass, field +from dataclasses import dataclass from typing import Any, Callable, Dict, List, Optional logger = logging.getLogger(__name__) @@ -135,10 +135,12 @@ class ORALoop: - auto : pHash toujours, VLM seulement si confiance < 0.95 """ - def __init__(self, max_retries: int = 2, max_steps: int = 50, verify_level: str = 'auto'): + def __init__(self, max_retries: int = 2, max_steps: int = 50, verify_level: str = 'auto', + should_continue: Optional[Callable[[], bool]] = None): self.max_retries = max_retries self.max_steps = max_steps self.verify_level = verify_level + self._should_continue = should_continue or (lambda: True) # Variables runtime injectées par le workflow self._variables: Dict[str, Any] = {} @@ -450,6 +452,11 @@ Règles: logger.warning("🧠 [ORA/instruction] CognitiveContext non disponible — mode dégradé") for step_num in range(self.max_steps): + if not self._should_continue(): + logger.info("⛔ [ORA/instruction] Arrêt demandé") + return LoopResult(success=False, steps_completed=step_num, + total_steps=self.max_steps, reason="stopped") + logger.info(f"\n{'='*60}") logger.info(f"📋 [ORA/instruction] Étape {step_num + 1}/{self.max_steps}") @@ -746,6 +753,10 @@ Règles: logger.info(f"🚀 [ORA] Démarrage workflow: {total} étapes, verify={self.verify_level}, retries={self.max_retries}") for i, step in enumerate(steps): + if not self._should_continue(): + logger.info("⛔ [ORA] Arrêt demandé") + return LoopResult(success=False, steps_completed=i, total_steps=total, reason="stopped") + logger.info(f"\n{'='*60}") logger.info(f"📋 [ORA] Étape {i+1}/{total}: {step.get('action_type', '?')} — {step.get('label', '')}") diff --git a/visual_workflow_builder/backend/api_v3/execute.py b/visual_workflow_builder/backend/api_v3/execute.py index 6804a5de7..93d922a07 100644 --- a/visual_workflow_builder/backend/api_v3/execute.py +++ b/visual_workflow_builder/backend/api_v3/execute.py @@ -1430,7 +1430,10 @@ def run_workflow_verified(execution_id: str, workflow_id: str, app): # --- Créer et lancer la boucle ORA --- from core.execution.observe_reason_act import ORALoop - ora = ORALoop(max_retries=2, max_steps=50, verify_level='auto') + ora = ORALoop( + max_retries=2, max_steps=50, verify_level='auto', + should_continue=lambda: not _execution_state.get('should_stop', False) + ) ora._variables = _execution_state.get('variables', {}) # Créer les ExecutionStep en amont pour le suivi @@ -1955,6 +1958,7 @@ def execute_instruction(): max_retries=2, max_steps=max_steps, verify_level=verify_level, + should_continue=lambda: not _execution_state.get('should_stop', False), ) logger.info(f"🚀 [Instruction] Démarrage: '{instruction}' (exec_id={exec_id})")