Spaces:
Paused
Paused
File size: 9,498 Bytes
e1c927b | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 | """Evaluation flywheel: confirmed violation -> failure signature -> cluster
-> HITL validation -> regression-test artifact, under dataset-role
governance with a contamination guard.
Roles are immutable once assigned; members of PROTECTED roles (REGRESSION,
LAUNCH) are NEVER training-eligible without a recorded governance override
— semantics mirrored from raremine.lineage (ContaminationError subclasses
raremine's LeakageError when importable so callers can catch either).
`sensorflow.studio2`'s registry is used when it lands (guarded import,
re-checked at integration time); until then the local implementation
persists to runs/rotr/.
"""
from __future__ import annotations
import uuid
from typing import Dict, List, Optional
from sensorflow.rotr import SOFTWARE_VERSION
from sensorflow.rotr import store
from sensorflow.rotr.models import (
HITLReview, PROTECTED_ROLES, Provenance, TrainingCandidate, now_iso,
)
FLYWHEEL_VERSION = "rotr-flywheel-1.0.0"
# Mirror raremine's guard exception when importable (landed package,
# read-only reuse) so platform callers can catch one exception type.
try:
from sensorflow.raremine.lineage import LeakageError as _LeakageBase
except Exception: # pragma: no cover
_LeakageBase = RuntimeError
class ContaminationError(_LeakageBase):
"""Raised when a protected evaluation member would leak into training."""
# Registry backend: studio2 when it lands, else local (recorded on artifacts).
# The local store is ALWAYS written (the guard never depends on the
# concurrent workstream); studio2 registration is a best-effort mirror.
try:
from sensorflow.studio2.registry import get_registry as _studio2_get_registry
REGISTRY_BACKEND = "studio2+local"
except Exception:
_studio2_get_registry = None
REGISTRY_BACKEND = "local"
def _mirror_to_studio2(candidate: "TrainingCandidate") -> Optional[str]:
"""Best-effort mirror of the regression artifact into the studio2
entity registry (guarded: studio2 is a concurrent workstream)."""
if _studio2_get_registry is None:
return None
try:
entity = _studio2_get_registry().register_dataset(
name=f"rotr-regression-{candidate.candidate_id}",
role="REGRESSION",
provenance=candidate.provenance.model_dump(),
meta={"violation_id": candidate.violation_id,
"cluster_id": candidate.cluster_id,
"run_id": candidate.run_id, "source": "sensorflow.rotr"},
actor="rotr-flywheel")
return entity.get("entity_id")
except Exception:
return None
def _audit(event: str, subject: str, actor: str, detail: str = "") -> None:
doc = store.read_json("flywheel", "audit.json") or {"events": []}
doc["events"].append({"at": now_iso(), "event": event, "subject": subject,
"actor": actor, "detail": detail})
store.write_json(doc, "flywheel", "audit.json")
# ------------------------------------------------------------ HITL queue
def build_queue(run_id: str, violations: List[Dict],
clusters: List[Dict]) -> List[HITLReview]:
vid_to_cluster = {}
for c in clusters:
for vid in c["member_violation_ids"]:
vid_to_cluster[vid] = c["cluster_id"]
reviews = []
for v in violations:
rid = f"rev-{v['violation_id']}"
reviews.append(HITLReview(
review_id=rid, run_id=run_id, violation_id=v["violation_id"],
cluster_id=vid_to_cluster.get(v["violation_id"]),
status="PENDING",
provenance=Provenance(
scenario_id=v.get("scenario_id"),
software_version=f"{SOFTWARE_VERSION}/{FLYWHEEL_VERSION}",
source="SYNTHETIC")))
store.write_json({"run_id": run_id,
"reviews": [r.model_dump() for r in reviews]},
"flywheel", f"queue-{run_id}.json")
return reviews
def get_queue(run_id: str) -> List[Dict]:
doc = store.read_json("flywheel", f"queue-{run_id}.json")
return doc["reviews"] if doc else []
def act(run_id: str, review_id: str, action: str, actor: str,
notes: str = "") -> Dict:
"""VALIDATE turns a confirmed violation into a regression-test artifact
(protected role, not training-eligible). REJECT closes the review."""
doc = store.read_json("flywheel", f"queue-{run_id}.json")
if not doc:
raise KeyError(f"no HITL queue for run {run_id}")
review = next((r for r in doc["reviews"] if r["review_id"] == review_id),
None)
if review is None:
raise KeyError(f"unknown review {review_id}")
if review["status"] != "PENDING":
raise ValueError(f"review {review_id} already {review['status']}")
action = action.upper()
if action not in ("VALIDATE", "REJECT"):
raise ValueError("action must be VALIDATE or REJECT")
review["status"] = "VALIDATED" if action == "VALIDATE" else "REJECTED"
review["action"] = action
review["actor"] = actor
review["notes"] = notes
store.write_json(doc, "flywheel", f"queue-{run_id}.json")
_audit(f"hitl_{action.lower()}", review_id, actor, notes)
candidate = None
if action == "VALIDATE":
candidate = _create_candidate(run_id, review, actor)
_append_suite_member(run_id, review, candidate)
return {"review": review,
"candidate": candidate.model_dump() if candidate else None}
def _create_candidate(run_id: str, review: Dict, actor: str) -> TrainingCandidate:
cid = f"tc-{uuid.uuid4().hex[:10]}"
cand = TrainingCandidate(
candidate_id=cid, run_id=run_id,
violation_id=review["violation_id"],
cluster_id=review.get("cluster_id"),
dataset_role="REGRESSION", training_eligible=False,
guard_state="PROTECTED",
provenance=Provenance(
software_version=f"{SOFTWARE_VERSION}/{FLYWHEEL_VERSION}",
source="SYNTHETIC"))
doc = cand.model_dump()
doc["studio2_entity_id"] = _mirror_to_studio2(cand)
doc["registry_backend"] = REGISTRY_BACKEND
store.write_json(doc, "flywheel", "candidates", f"{cid}.json")
_audit("candidate_created", cid, actor,
f"role=REGRESSION (protected) violation={review['violation_id']} "
f"registry_backend={REGISTRY_BACKEND}")
return cand
def _append_suite_member(run_id: str, review: Dict,
candidate: TrainingCandidate) -> None:
suite = store.read_json("flywheel", "regression-suite.json") or {
"suite_id": "rotr-regression-suite", "members": [],
"registry_backend": REGISTRY_BACKEND}
suite["members"].append({
"candidate_id": candidate.candidate_id,
"violation_id": review["violation_id"],
"cluster_id": review.get("cluster_id"),
"run_id": run_id, "role": "REGRESSION",
"added_at": now_iso(), "added_by": review.get("actor"),
"immutable_role_note": "REGRESSION members re-run against every "
"candidate; never training-eligible without "
"a recorded governance override"})
store.write_json(suite, "flywheel", "regression-suite.json")
def get_suite() -> Dict:
return store.read_json("flywheel", "regression-suite.json") or {
"suite_id": "rotr-regression-suite", "members": [],
"registry_backend": REGISTRY_BACKEND}
# ------------------------------------------------------------ contamination guard
def list_candidates() -> List[Dict]:
out = []
for name in store.list_dir("flywheel", "candidates"):
doc = store.read_json("flywheel", "candidates", name)
if doc:
out.append(doc)
return out
def get_candidate(candidate_id: str) -> Optional[Dict]:
return store.read_json("flywheel", "candidates", f"{candidate_id}.json")
def promote_to_training(candidate_id: str, actor: str) -> Dict:
"""Refused for protected roles unless a governance override is recorded
(raremine guard semantics)."""
doc = get_candidate(candidate_id)
if doc is None:
raise KeyError(f"unknown candidate {candidate_id}")
if doc["dataset_role"] in PROTECTED_ROLES and not doc.get("override"):
_audit("promotion_refused", candidate_id, actor,
f"protected role {doc['dataset_role']} without override")
raise ContaminationError(
f"candidate {candidate_id} holds protected role "
f"{doc['dataset_role']}: training promotion refused without a "
"recorded governance override")
doc["training_eligible"] = True
doc["guard_state"] = "OVERRIDDEN" if doc.get("override") else "ELIGIBLE"
store.write_json(doc, "flywheel", "candidates", f"{candidate_id}.json")
_audit("promoted_to_training", candidate_id, actor,
f"guard_state={doc['guard_state']}")
return doc
def governance_override(candidate_id: str, actor: str, reason: str) -> Dict:
if not reason.strip():
raise ValueError("a governance override requires a recorded reason")
doc = get_candidate(candidate_id)
if doc is None:
raise KeyError(f"unknown candidate {candidate_id}")
doc["override"] = {"actor": actor, "reason": reason, "at": now_iso()}
doc["guard_state"] = "OVERRIDDEN"
store.write_json(doc, "flywheel", "candidates", f"{candidate_id}.json")
_audit("governance_override", candidate_id, actor, reason)
return doc
|