feat(vwb): execute wait for state

This commit is contained in:
Dom
2026-05-29 17:22:35 +02:00
parent 7b1f30af1a
commit e66bc6d452
10 changed files with 491 additions and 9 deletions

View File

@@ -4,10 +4,13 @@ Auteur : Dom, Claude - 14 janvier 2026
Ce module contient les actions de contrôle de flux :
- keyboard_shortcut : Raccourcis clavier
- wait_for_state : Attente d'etat semantique
"""
from .keyboard_shortcut import VWBKeyboardShortcutAction
from .wait_for_state import VWBWaitForStateAction
__all__ = [
'VWBKeyboardShortcutAction',
'VWBWaitForStateAction',
]

View File

@@ -0,0 +1,113 @@
"""VWB action that waits for a semantic screen state."""
from __future__ import annotations
from datetime import datetime
from typing import Any, Dict, List, Optional
from ..base_action import BaseVWBAction, VWBActionResult, VWBActionStatus
from ...contracts.error import VWBErrorType, create_vwb_error
from ...services.wait_for_state import wait_for_expected_state, StateProvider
class VWBWaitForStateAction(BaseVWBAction):
"""Wait until the foreground window/process matches an expected state."""
SUPPORTED_EVIDENCE = {"window_or_process"}
def __init__(
self,
action_id: str,
parameters: Dict[str, Any],
screen_capturer=None,
state_provider: Optional[StateProvider] = None,
):
super().__init__(
action_id=action_id,
name="Attendre un etat",
description="Attend un etat semantique d'ecran",
parameters=parameters,
screen_capturer=screen_capturer,
)
self.expected_state = parameters.get("expected_state") or {}
self.timeout_ms = int(parameters.get("timeout_ms", 5000))
self.poll_interval_ms = int(parameters.get("poll_interval_ms", 250))
self.evidence_required = parameters.get(
"evidence_required", "window_or_process"
)
self.state_provider = state_provider or parameters.get("_state_provider")
def validate_parameters(self) -> List[str]:
errors: List[str] = []
if not isinstance(self.expected_state, dict) or not self.expected_state:
errors.append("expected_state requis")
criteria = (
"window_title_in",
"window_title_contains",
"process_active",
)
if isinstance(self.expected_state, dict) and not any(
self.expected_state.get(key) for key in criteria
):
errors.append(
"expected_state doit definir window_title_in, "
"window_title_contains ou process_active"
)
if self.timeout_ms < 100:
errors.append("timeout_ms doit etre >= 100")
if self.poll_interval_ms < 50:
errors.append("poll_interval_ms doit etre >= 50")
if self.evidence_required not in self.SUPPORTED_EVIDENCE:
errors.append(
f"evidence_required non supporte: {self.evidence_required}"
)
return errors
def execute_core(self, step_id: str) -> VWBActionResult:
start_time = datetime.now()
wait_result = wait_for_expected_state(
expected_state=self.expected_state,
timeout_ms=self.timeout_ms,
poll_interval_ms=self.poll_interval_ms,
evidence_required=self.evidence_required,
state_provider=self.state_provider,
)
end_time = datetime.now()
execution_time = (end_time - start_time).total_seconds() * 1000
output = wait_result.to_dict()
if wait_result.matched:
return VWBActionResult(
action_id=self.action_id,
step_id=step_id,
status=VWBActionStatus.SUCCESS,
start_time=start_time,
end_time=end_time,
execution_time_ms=execution_time,
output_data=output,
evidence_list=self.evidence_list.copy(),
)
error = create_vwb_error(
error_type=VWBErrorType.WAIT_TIMEOUT,
message="Etat attendu non observe avant timeout",
action_id=self.action_id,
step_id=step_id,
technical_details=output,
execution_time_ms=execution_time,
)
return VWBActionResult(
action_id=self.action_id,
step_id=step_id,
status=VWBActionStatus.TIMEOUT,
start_time=start_time,
end_time=end_time,
execution_time_ms=execution_time,
output_data=output,
evidence_list=self.evidence_list.copy(),
error=error,
)