canary.py 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. """Trusted Canary verification based on engine state and bounded comparisons."""
  2. from __future__ import annotations
  3. TERMINAL_FAILURE_STATES = {
  4. "FAILED",
  5. "KILLED",
  6. "CANCELLED",
  7. "WARNING",
  8. }
  9. SAFE_METRICS = {
  10. "row_count_delta",
  11. "duration_delta_seconds",
  12. "error_count_delta",
  13. "output_hash_match",
  14. }
  15. class CanaryVerifier:
  16. def __init__(
  17. self,
  18. *,
  19. plans,
  20. engine,
  21. comparator=None,
  22. verifier_id="dataops-canary-verifier",
  23. ):
  24. self.plans = plans
  25. self.engine = engine
  26. self.comparator = comparator
  27. self.verifier_id = verifier_id
  28. def verify(self, candidate_id, *, baseline_execution_id=None):
  29. evidence = self.plans.get_canary_evidence(candidate_id)
  30. if not isinstance(evidence, dict):
  31. raise ValueError("canary evidence does not exist")
  32. if evidence.get("status") != "started":
  33. raise ValueError("canary evidence is already finalized")
  34. execution_id = evidence.get("execution_id")
  35. execution = self.engine.get_execution(execution_id)
  36. state = str((execution.get("state") or {}).get("current") or "").upper()
  37. if state not in {"SUCCESS", *TERMINAL_FAILURE_STATES}:
  38. return evidence
  39. equivalent = state == "SUCCESS"
  40. metrics = {}
  41. if state == "SUCCESS" and baseline_execution_id is not None:
  42. if self.comparator is None:
  43. raise RuntimeError("canary comparator is not configured")
  44. comparison = self.comparator.compare(baseline_execution_id, execution_id)
  45. equivalent = bool(comparison.get("equivalent", False))
  46. metrics = {
  47. key: value
  48. for key, value in dict(comparison.get("metrics") or {}).items()
  49. if key in SAFE_METRICS
  50. and (value is None or isinstance(value, (bool, int, float)))
  51. }
  52. status = "passed" if state == "SUCCESS" and equivalent else "failed"
  53. summary = {
  54. "engine_state": state,
  55. "equivalent": equivalent,
  56. "metrics": metrics,
  57. }
  58. finalized = {
  59. "candidate_id": candidate_id,
  60. "execution_id": execution_id,
  61. "status": status,
  62. "sample_runs": 1,
  63. "verified_by": self.verifier_id,
  64. "verification_summary": summary,
  65. }
  66. return self.plans.record_canary_verification(candidate_id, finalized)