feat(vwb): log supervised competence verdicts
This commit is contained in:
@@ -103,6 +103,33 @@ def _execute_wait_for_state(params: Dict[str, Any]) -> dict:
|
||||
output = result.to_dict()
|
||||
if result.matched:
|
||||
return {"success": True, "output": output}
|
||||
if params.get("supervised_popup_detection", False):
|
||||
detected_popup = None
|
||||
try:
|
||||
detected_popup = _check_screen_for_patterns()
|
||||
except Exception as exc:
|
||||
print(f"⚠️ [wait_for_state] Detection popup indisponible: {exc}")
|
||||
|
||||
from visual_workflow_builder.backend.services.supervised_popup_guard import (
|
||||
build_unexpected_popup_pause,
|
||||
)
|
||||
|
||||
pause_payload = build_unexpected_popup_pause(
|
||||
detected_popup,
|
||||
expected_state=expected_state,
|
||||
competence_id=str(params.get("competence_id") or ""),
|
||||
source_method_id=str(params.get("source_method_id") or ""),
|
||||
)
|
||||
if pause_payload:
|
||||
return {
|
||||
"success": True,
|
||||
"needs_human_pause": True,
|
||||
"output": {
|
||||
"wait_for_state": output,
|
||||
"human_pause": pause_payload,
|
||||
"write_back_enabled": False,
|
||||
},
|
||||
}
|
||||
return {
|
||||
"success": False,
|
||||
"error": "Etat attendu non observe avant timeout",
|
||||
@@ -110,6 +137,23 @@ def _execute_wait_for_state(params: Dict[str, Any]) -> dict:
|
||||
}
|
||||
|
||||
|
||||
def _execute_pause_for_human(params: Dict[str, Any]) -> dict:
|
||||
return {
|
||||
"success": True,
|
||||
"needs_human_pause": True,
|
||||
"output": {
|
||||
"needs_human": True,
|
||||
"pause_reason": "supervised_pause",
|
||||
"message": params.get("message", "Validation humaine requise"),
|
||||
"phase": params.get("phase"),
|
||||
"verdict_required": bool(params.get("verdict_required", False)),
|
||||
"verdict_endpoint": params.get("verdict_endpoint"),
|
||||
"competence_id": params.get("competence_id"),
|
||||
"write_back_enabled": False,
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def minimize_active_window():
|
||||
"""Minimise le navigateur VWB et active la fenêtre suivante (VM, app cible)."""
|
||||
try:
|
||||
@@ -168,7 +212,8 @@ _execution_state = {
|
||||
'pending_action': None, # Action en attente de choix utilisateur
|
||||
'candidates': [], # Candidats proposés
|
||||
'user_choice': None, # Choix de l'utilisateur (coordonnées ou 'skip' ou 'static')
|
||||
'current_step_info': None # Info sur l'étape en cours pour affichage
|
||||
'current_step_info': None, # Info sur l'étape en cours pour affichage
|
||||
'human_pause': None,
|
||||
}
|
||||
|
||||
|
||||
@@ -307,6 +352,35 @@ def execute_workflow_thread(execution_id: str, workflow_id: str, app):
|
||||
params['_step_label'] = step.label
|
||||
result = execute_action(step.action_type, params)
|
||||
|
||||
if result.get('needs_human_pause'):
|
||||
pause_payload = result.get('output', {})
|
||||
print(f"⏸️ [Supervision] Pause humaine étape {index + 1}: {pause_payload}")
|
||||
with _execution_lock:
|
||||
_execution_state['is_paused'] = True
|
||||
_execution_state['human_pause'] = pause_payload
|
||||
_execution_state['current_step_info'] = {
|
||||
'index': index,
|
||||
'total': len(steps),
|
||||
'step_id': step.id,
|
||||
'action_type': step.action_type,
|
||||
'label': step.label,
|
||||
'human_pause': pause_payload,
|
||||
}
|
||||
execution.status = 'paused'
|
||||
db.session.commit()
|
||||
|
||||
while _execution_state['is_paused'] and not _execution_state['should_stop']:
|
||||
time.sleep(0.1)
|
||||
|
||||
if _execution_state['should_stop']:
|
||||
execution.status = 'cancelled'
|
||||
break
|
||||
|
||||
with _execution_lock:
|
||||
_execution_state['human_pause'] = None
|
||||
execution.status = 'running'
|
||||
db.session.commit()
|
||||
|
||||
# === SELF-HEALING INTERACTIF ===
|
||||
# Si l'action nécessite un choix utilisateur, attendre
|
||||
if result.get('needs_user_choice'):
|
||||
@@ -456,6 +530,7 @@ def execute_workflow_thread(execution_id: str, workflow_id: str, app):
|
||||
with _execution_lock:
|
||||
_execution_state['is_running'] = False
|
||||
_execution_state['current_execution_id'] = None
|
||||
_execution_state['human_pause'] = None
|
||||
|
||||
|
||||
def execute_ai_analyze(params: dict) -> dict:
|
||||
@@ -1198,6 +1273,9 @@ def execute_action(action_type: str, params: dict) -> dict:
|
||||
elif action_type == 'wait_for_state':
|
||||
return _execute_wait_for_state(params)
|
||||
|
||||
elif action_type == 'pause_for_human':
|
||||
return _execute_pause_for_human(params)
|
||||
|
||||
elif action_type == 'keyboard_shortcut':
|
||||
keys = params.get('keys', [])
|
||||
if not keys:
|
||||
@@ -1631,6 +1709,7 @@ def start_execution():
|
||||
_execution_state['current_execution_id'] = execution.id
|
||||
_execution_state['execution_mode'] = execution_mode
|
||||
_execution_state['variables'] = {} # Reset des variables
|
||||
_execution_state['human_pause'] = None
|
||||
|
||||
print(f"🎯 [API v3] Mode d'exécution: {execution_mode}")
|
||||
|
||||
@@ -1790,7 +1869,15 @@ def get_execution_status():
|
||||
# Self-healing interactif
|
||||
'waiting_for_choice': _execution_state.get('waiting_for_choice', False),
|
||||
'candidates': _execution_state.get('candidates', []) if _execution_state.get('waiting_for_choice') else [],
|
||||
'current_step_info': _execution_state.get('current_step_info') if _execution_state.get('waiting_for_choice') else None
|
||||
'current_step_info': (
|
||||
_execution_state.get('current_step_info')
|
||||
if (
|
||||
_execution_state.get('waiting_for_choice')
|
||||
or _execution_state.get('human_pause')
|
||||
)
|
||||
else None
|
||||
),
|
||||
'human_pause': _execution_state.get('human_pause'),
|
||||
})
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user