Add dry_run_mode/src/dry_run_mode/core.py
This commit is contained in:
parent
1bb54018c5
commit
b1d3675653
1 changed files with 94 additions and 0 deletions
94
dry_run_mode/src/dry_run_mode/core.py
Normal file
94
dry_run_mode/src/dry_run_mode/core.py
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
import json
|
||||||
|
import hashlib
|
||||||
|
import os
|
||||||
|
from typing import Dict, Any, List, TypedDict
|
||||||
|
|
||||||
|
|
||||||
|
class DryRunResult(TypedDict):
|
||||||
|
stratum: str
|
||||||
|
dry_decision: str
|
||||||
|
reason: str
|
||||||
|
|
||||||
|
|
||||||
|
def _validate_drift_report(drift_report: Dict[str, Any]) -> None:
|
||||||
|
if not isinstance(drift_report, dict):
|
||||||
|
raise ValueError("drift_report must be a dict")
|
||||||
|
if "strata" not in drift_report:
|
||||||
|
raise ValueError("drift_report missing 'strata' key")
|
||||||
|
if not isinstance(drift_report["strata"], list):
|
||||||
|
raise ValueError("'strata' must be a list in drift_report")
|
||||||
|
|
||||||
|
|
||||||
|
def _load_policy_constants(path: str) -> Dict[str, Any]:
|
||||||
|
if not os.path.exists(path):
|
||||||
|
raise FileNotFoundError(f"Policy constants file not found: {path}")
|
||||||
|
with open(path, "r", encoding="utf-8") as f:
|
||||||
|
try:
|
||||||
|
constants = json.load(f)
|
||||||
|
except json.JSONDecodeError as e:
|
||||||
|
raise ValueError(f"Invalid JSON in policy constants: {e}")
|
||||||
|
if not all(k in constants for k in ("alpha", "offset")):
|
||||||
|
raise ValueError("Policy constants file must contain 'alpha' and 'offset'")
|
||||||
|
return constants
|
||||||
|
|
||||||
|
|
||||||
|
def _compute_policy_hash(constants: Dict[str, Any]) -> str:
|
||||||
|
constants_str = json.dumps(constants, sort_keys=True)
|
||||||
|
return hashlib.sha256(constants_str.encode("utf-8")).hexdigest()
|
||||||
|
|
||||||
|
|
||||||
|
def dry_run_evaluation(drift_report: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
|
"""Führt eine Dry-Run-Evaluierung des Drift-Reports durch und gibt Entscheidungen mit Begründung und Policy-Hash zurück."""
|
||||||
|
_validate_drift_report(drift_report)
|
||||||
|
|
||||||
|
constants_path = drift_report.get("policy_constants_path", "config/policy_constants.json")
|
||||||
|
constants = _load_policy_constants(constants_path)
|
||||||
|
policy_hash = _compute_policy_hash(constants)
|
||||||
|
|
||||||
|
results: List[DryRunResult] = []
|
||||||
|
alpha = constants["alpha"]
|
||||||
|
offset = constants["offset"]
|
||||||
|
|
||||||
|
for stratum_info in drift_report["strata"]:
|
||||||
|
stratum_id = stratum_info.get("name") or "unknown"
|
||||||
|
metric_value = stratum_info.get("metric", 0.0)
|
||||||
|
|
||||||
|
if not isinstance(metric_value, (int, float)):
|
||||||
|
results.append(
|
||||||
|
DryRunResult(
|
||||||
|
stratum=stratum_id,
|
||||||
|
dry_decision="FAIL",
|
||||||
|
reason="Invalid metric type"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if metric_value < alpha - offset:
|
||||||
|
decision = "PASS"
|
||||||
|
reason = f"Metric {metric_value} < threshold {alpha - offset}"
|
||||||
|
elif metric_value < alpha + offset:
|
||||||
|
decision = "WARN"
|
||||||
|
reason = f"Metric {metric_value} near threshold (±{offset})"
|
||||||
|
else:
|
||||||
|
decision = "FAIL"
|
||||||
|
reason = f"Metric {metric_value} exceeds threshold {alpha + offset}"
|
||||||
|
|
||||||
|
results.append(
|
||||||
|
DryRunResult(
|
||||||
|
stratum=stratum_id,
|
||||||
|
dry_decision=decision,
|
||||||
|
reason=reason
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
output: Dict[str, Any] = {
|
||||||
|
"policy_hash": policy_hash,
|
||||||
|
"evaluations": results,
|
||||||
|
}
|
||||||
|
|
||||||
|
assert isinstance(output, dict), "Dry run output must be a dict"
|
||||||
|
assert all(set(r.keys()) == {"stratum", "dry_decision", "reason"} for r in results), (
|
||||||
|
"Each result must contain required fields"
|
||||||
|
)
|
||||||
|
|
||||||
|
return output
|
||||||
Loading…
Reference in a new issue