36 lines
1.6 KiB
Python
36 lines
1.6 KiB
Python
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
|