From 494f5d0c856fa5bb2011eb6f2af600ba88e06d15 Mon Sep 17 00:00:00 2001 From: Kai Chappell Date: Sat, 8 Mar 2025 15:30:31 +0000 Subject: [PATCH] add validation result types Implement ValidationContext, CheckResult, and ValidationResult models using Pydantic with frozen (immutable) configuration. --- src/veritext/core/types.py | 64 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 src/veritext/core/types.py diff --git a/src/veritext/core/types.py b/src/veritext/core/types.py new file mode 100644 index 0000000..ea02d2f --- /dev/null +++ b/src/veritext/core/types.py @@ -0,0 +1,64 @@ +"""Core types for validation context and results.""" + +from typing import Any + +from pydantic import BaseModel, ConfigDict, Field + + +class ValidationContext(BaseModel): + """Context for validation, containing reference text and metadata.""" + + model_config = ConfigDict(frozen=True) + + reference: str | list[str] | None = None + """Reference text(s) for comparison. Required for comparison metrics.""" + + metadata: dict[str, Any] = Field(default_factory=dict) + """Additional metadata for validation (e.g., source, timestamp).""" + + +class CheckResult(BaseModel): + """Result of a single validation check.""" + + model_config = ConfigDict(frozen=True) + + name: str + """Name of the check that produced this result.""" + + passed: bool + """Whether the check passed.""" + + actual: Any + """The actual value computed by the check.""" + + threshold: Any | None = None + """The threshold the actual value was compared against, if applicable.""" + + message: str + """Human-readable description of the result.""" + + +class ValidationResult(BaseModel): + """Aggregate result of multiple validation checks.""" + + model_config = ConfigDict(frozen=True) + + passed: bool + """Whether all checks passed.""" + + checks: list[CheckResult] + """Individual check results.""" + + @property + def failed_checks(self) -> list[CheckResult]: + return [check for check in self.checks if not check.passed] + + @property + def failure_summary(self) -> str: + failed = self.failed_checks + if not failed: + return "All checks passed." + lines = [f"Validation failed ({len(failed)} check(s)):"] + for check in failed: + lines.append(f" - {check.name}: {check.message}") + return "\n".join(lines)