"""Dataclasses du Validator — Verdict, FailureCategory, ValidationResult. Cf. SPEC_VALIDATOR_MATRICE.md §1 et AXE_B2_DEEP_VALIDATOR.md §3.1. """ from __future__ import annotations from dataclasses import dataclass, field from enum import Enum from typing import Any, Dict, Optional class Verdict(str, Enum): """Trois verdicts possibles (calque Skyvern complete/terminate/continue).""" COMPLETE = "complete" # l'action a eu l'effet voulu CONTINUE = "continue" # effet pas encore visible → recheck/wait TERMINATE = "terminate" # échec irrécupérable → pause supervisée class FailureCategory(str, Enum): """Classification des échecs (restreinte au contexte rpa_vision_v3).""" WRONG_TARGET = "wrong_target" WRONG_APPLICATION = "wrong_application" # bug step 10 (clic hors-app) NO_VISUAL_CHANGE = "no_visual_change" UNEXPECTED_DIALOG = "unexpected_dialog" OCR_TEXT_MISSING = "ocr_text_missing" SCHEMA_INVALID = "schema_invalid" UI_LOADING = "ui_loading" UNKNOWN = "unknown" @dataclass class ValidationResult: """Résultat d'un check. Toujours sérialisable JSON.""" verdict: Verdict confidence: float check_used: str elapsed_ms: float reasoning: str = "" failure_category: Optional[FailureCategory] = None raw_evidence: Dict[str, Any] = field(default_factory=dict) def to_dict(self) -> Dict[str, Any]: return { "verdict": self.verdict.value, "confidence": round(self.confidence, 3), "check_used": self.check_used, "elapsed_ms": round(self.elapsed_ms, 1), "reasoning": self.reasoning, "failure_category": ( self.failure_category.value if self.failure_category else None ), "raw_evidence": self.raw_evidence, }