File size: 13,217 Bytes
6342806
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
"""Closed-loop demo: one seeded scenario moves through the whole system.

Stitches the REAL engines end-to-end:

  1. bevfusion scenario suite (deterministic multi-sensor scenes)
  2. baseline vs candidate perception comparison (bevfusion engines)
  3. seqeval anytime-valid regression verdict (with a planted night-stratum
     regression so governance has something to catch)
  4. nextgen counterfactual + closed-loop causal replay (when importable)
  5. safety layered gates + evidence package (reusing the latest published
     megaeval candidate/baseline runs)
  6. megaeval distribution-shift report
  7. studio2 ReleaseGate composition -> ReleaseDecision with the evidence tuple
  8. flywheel step: the failure is registered as a REGRESSION-role dataset
     (protected by the contamination guard)

Every step records availability; steps whose engine/store is absent are
skipped with the reason, and the release gate degrades accordingly (that IS
the demo of graceful degradation). Deterministic for a fixed seed.
"""

from __future__ import annotations

import os
from typing import Dict, Optional

from sensorflow.studio2 import store
from sensorflow.studio2.registry import Registry, content_hash, get_registry
from sensorflow.studio2.release_gate import ReleaseGate
from sensorflow.studio2 import hardware as hw_mod

DEMO_SEED = 977


def _step(name: str, available: bool, detail: Dict) -> Dict:
    return {"step": name, "available": available, **detail}


def run_demo(seed: int = DEMO_SEED, registry: Optional[Registry] = None,
             repo_root: str = ".", persist: bool = True) -> Dict:
    registry = registry or get_registry()
    steps = []

    # ---- 1+2. scenario + perception comparison (bevfusion, landed) --------
    scenario_entity = None
    perception = None
    try:
        from sensorflow.bevfusion.evaluate import run_comparison
        report = run_comparison(n_sequences=3, frames_per_sequence=16,
                                seed=seed, persist=persist)
        params = report["params"]
        scenario_entity = registry.register_scenario(
            name=f"demo-suite-seed{seed}",
            generator="bevfusion.scenes.generate_sequences",
            seed=seed, recipe=params,
            provenance={"source_package": "bevfusion",
                        "run_id": report["run_id"]})
        dataset_entity = registry.register_dataset(
            name=f"demo-scenes-seed{seed}", role="TEST",
            provenance={"source_package": "bevfusion",
                        "generator_seed": seed})
        model_entity = registry.register_model(
            name=report["engines"]["candidate"],
            version=report["engines"]["candidate"],
            provenance={"source_package": "bevfusion"})
        baseline_entity = registry.register_model(
            name=report["engines"]["baseline"],
            version=report["engines"]["baseline"],
            provenance={"source_package": "bevfusion"})
        experiment = registry.register_experiment(
            name=f"studio2-demo-seed{seed}",
            candidate_model_id=model_entity["entity_id"],
            baseline_model_id=baseline_entity["entity_id"])
        gate_policy_entity = registry.register_policy(
            name="studio2-demo-run-config",
            doc={"n_sequences": 3, "frames_per_sequence": 16, "seed": seed})
        run_entity = registry.register_run(
            name=f"demo-bev-{report['run_id']}", engine="bevfusion",
            experiment_id=experiment["entity_id"],
            tuple_components={
                "model_version_id": model_entity["entity_id"],
                "dataset_version_id": dataset_entity["entity_id"],
                "scenario_version_id": scenario_entity["entity_id"],
                "config_hash": content_hash(params, exclude=()),
                # bevfusion sensor models are pinned in code; version the pin
                "calibration_version": "bevfusion-sensors-v1",
                "seed": seed,
                "policy_version_id": gate_policy_entity["entity_id"],
            },
            results={"recommendation": report["recommendation"],
                     "headline_deltas": report["headline_deltas"]},
            provenance={"source_package": "bevfusion",
                        "run_id": report["run_id"]})
        perception = report
        steps.append(_step("scenario_and_perception", True, {
            "bevfusion_run_id": report["run_id"],
            "scenario_version": scenario_entity["entity_id"],
            "evaluation_run": run_entity["entity_id"],
            "reproducibility": run_entity["reproducibility"],
            "recommendation": report["recommendation"]}))
    except Exception as e:
        steps.append(_step("scenario_and_perception", False,
                           {"reason": f"{type(e).__name__}: {e}"}))

    # ---- 3. seqeval verdict (landed; needs a megaeval population) ---------
    seq_verdict = None
    try:
        pop_dir = os.path.join(repo_root, "runs", "megaeval", "populations")
        pops = sorted(os.listdir(pop_dir)) if os.path.isdir(pop_dir) else []
        if not pops:
            raise RuntimeError("no megaeval population available")
        from sensorflow.seqeval import evaluate_regression
        # planted regression in the pedestrian|night stratum: the point of
        # the demo is to show governance catching a real per-stratum failure
        planted = {"pedestrian|night": -0.05}
        seq_verdict = evaluate_regression(
            pops[-1],
            baseline={"model_version": "demo-baseline"},
            candidate={"model_version": "demo-candidate", "effects": planted},
            seed=seed)
        steps.append(_step("sequential_regression", True, {
            "seqeval_run_id": seq_verdict["run_id"],
            "decision": seq_verdict["decision"],
            "affected_strata": seq_verdict["affected_strata"],
            "samples_used": seq_verdict["samples_used"],
            "planted_effect": planted}))
    except Exception as e:
        steps.append(_step("sequential_regression", False,
                           {"reason": f"{type(e).__name__}: {e}"}))

    # ---- 4. nextgen counterfactual + closed loop (in-flight, guarded) -----
    closed_loop = None
    try:
        from sensorflow.nextgen.causal import causal_replay
        from sensorflow.nextgen.closedloop import demo_emergence_scenario
        from sensorflow.nextgen.models import DataLabel
        scn = demo_emergence_scenario()
        result = causal_replay(scn["actors"], scn["environment"],
                               scn["scenario_id"], DataLabel.COUNTERFACTUAL,
                               seed=seed)
        closed_loop = {"scenario_id": result.scenario_id,
                       "verdict": result.verdict,
                       "diffs": result.diffs}
        steps.append(_step("closed_loop_replay", True, {
            "scenario_id": result.scenario_id, "verdict": result.verdict}))
    except Exception as e:
        steps.append(_step("closed_loop_replay", False,
                           {"reason": f"nextgen unavailable or failed: "
                                      f"{type(e).__name__}: {e}"}))

    # ---- 5+6. safety gates + shift on the latest published megaeval runs --
    safety_result = None
    shift_report = None
    try:
        from sensorflow.megaeval.runs import get_mega_store
        from sensorflow.megaeval import analysis as mega_analysis
        from sensorflow.safety import gates as safety_gates
        mega = get_mega_store()
        published = sorted(
            [r for r in mega.runs.values() if getattr(r, "status", "") == "published"],
            key=lambda r: getattr(r, "published_at", "") or "")
        if len(published) < 2:
            raise RuntimeError("fewer than two published megaeval runs")
        candidate, baseline = published[-1], published[-2]
        cached = safety_gates.latest_gate_result(candidate.run_id)
        safety_result = cached or safety_gates.evaluate_gates(
            mega, candidate, baseline)
        shift_report = mega_analysis.distribution_shift(mega, candidate)
        steps.append(_step("safety_gates_and_shift", True, {
            "candidate_run_id": candidate.run_id,
            "baseline_run_id": baseline.run_id,
            "safety_decision": safety_result["decision"],
            "blocking_gates": safety_result["blocking_gates"],
            "shifts_reported": len(shift_report.get("shifts") or []),
            "reused_cached_gate_result": bool(cached)}))
    except Exception as e:
        steps.append(_step("safety_gates_and_shift", False,
                           {"reason": f"{type(e).__name__}: {e}"}))

    # ---- hardware matrix (studio2, best-effort) ----------------------------
    hardware_matrix = None
    try:
        hardware_matrix = hw_mod.gate_matrix(persist=persist)
        steps.append(_step("hardware_matrix", True, {
            "status": hardware_matrix["status"],
            "n_combinations": hardware_matrix["n_combinations"],
            "n_insufficient": hardware_matrix["n_insufficient"]}))
    except Exception as e:
        steps.append(_step("hardware_matrix", False,
                           {"reason": f"{type(e).__name__}: {e}"}))

    # ---- agentic policy outcome (in-flight, guarded) -----------------------
    agentic_outcome = None
    try:
        from sensorflow.agentic.policy import PolicyInput, evaluate as agentic_eval
        night_regressed = bool(seq_verdict and any(
            "night" in str(s) for s in (seq_verdict.get("affected_strata") or [])))
        agentic_outcome = agentic_eval(PolicyInput(
            failure_id=f"demo-failure-seed{seed}",
            safety_critical_class=False,
            behavioral_evidence="uncertain",
            significant=night_regressed,
            novelty="known_regression" if night_regressed else "unknown",
            evidence_confidence="CONFIRMED" if night_regressed else "LIKELY",
            concentration_dimension="lighting",
            concentrated=night_regressed))
        steps.append(_step("agentic_policy", True, {
            "outcome": agentic_outcome["outcome"],
            "severity": agentic_outcome["severity"],
            "policy_version": agentic_outcome["policy_version"]}))
    except Exception as e:
        steps.append(_step("agentic_policy", False,
                           {"reason": f"agentic unavailable or failed: "
                                      f"{type(e).__name__}: {e}"}))

    # ---- 7. release decision ----------------------------------------------
    gate = ReleaseGate(registry)
    decision = gate.evaluate(
        safety_metrics=safety_result,
        regression_results=seq_verdict,
        distribution_shift=shift_report,
        agentic_outcome=agentic_outcome,
        closed_loop=closed_loop,
        hardware_matrix=hardware_matrix,
        context={"demo_seed": seed,
                 "scenario_version": (scenario_entity or {}).get("entity_id"),
                 "perception_run": (perception or {}).get("run_id")})
    steps.append(_step("release_decision", True, {
        "decision_id": decision["entity_id"],
        "status": decision["status"],
        "blocking_conditions": decision["blocking_conditions"],
        "degraded_inputs": decision["degraded_inputs"],
        "evidence_completeness": decision["evidence_completeness"]}))

    # ---- 8. flywheel: failure -> REGRESSION-role dataset -------------------
    regression_dataset = None
    if decision["status"] in ("NO_GO", "REVIEW"):
        parents = [p for p in [
            (scenario_entity or {}).get("entity_id"),
            (seq_verdict or {}).get("run_id"),
            decision["entity_id"]] if p]
        regression_dataset = registry.register_dataset(
            name=f"demo-regression-suite-seed{seed}", role="REGRESSION",
            lineage_parents=parents,
            provenance={"source": "studio2.demo",
                        "release_decision": decision["entity_id"],
                        "seqeval_run": (seq_verdict or {}).get("run_id")},
            meta={"affected_strata": (seq_verdict or {}).get("affected_strata"),
                  "note": "failure registered for perpetual re-evaluation; "
                          "protected from training by the contamination guard"})
        steps.append(_step("flywheel_registration", True, {
            "dataset_id": regression_dataset["entity_id"],
            "role": regression_dataset["role"],
            "protected_evaluation": regression_dataset["protected_evaluation"]}))
    else:
        steps.append(_step("flywheel_registration", False,
                           {"reason": "decision was GO; nothing to register"}))

    demo = {
        "demo_id": f"demo-{content_hash({'seed': seed}, exclude=())}",
        "seed": seed,
        "steps": steps,
        "decision": decision,
        "regression_dataset": regression_dataset,
        "generated_at": store.now_iso(),
    }
    if persist:
        store.write_json(demo, "demo", f"{demo['demo_id']}.json")
        store.write_json(demo, "demo", "latest.json")
    return demo


def latest_demo() -> Optional[Dict]:
    return store.read_json("demo", "latest.json")