from jsonschema import Draft202012Validator, FormatChecker from pydantic import ValidationError from app.domain.schemas import PlannerDecision, ValidationResult from app.ai.tool_registry import ToolRegistry class ActionValidator: def __init__(self, registry: ToolRegistry) -> None: self.registry = registry def validate(self, decision: PlannerDecision) -> ValidationResult: if decision.intent == "no_action": return ValidationResult(valid=False, errors=["No actionable intent detected."]) if not decision.tool: return ValidationResult(valid=False, errors=["Planner did not select a tool."]) tool = self.registry.get(decision.tool) if not tool: return ValidationResult(valid=False, errors=[f"Unknown tool: {decision.tool}"]) try: Draft202012Validator(tool.schema, format_checker=FormatChecker()).validate(decision.arguments) except Exception as exc: return ValidationResult(valid=False, errors=[str(exc)]) extra = set(decision.arguments) - set(tool.schema.get("properties", {})) if extra: return ValidationResult(valid=False, errors=[f"Hallucinated parameters: {sorted(extra)}"]) try: self._validate_dates(decision.arguments) except ValidationError as exc: return ValidationResult(valid=False, errors=[str(exc)]) return ValidationResult(valid=True) def _validate_dates(self, args: dict) -> None: # JSON Schema catches shape; FastAPI/Pydantic validates API models at execution time. return None