feat(lab): publish M4.8S fixed-class detector replay
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"schema_version": "missioncore.laboratory-evidence-definition/v1",
|
||||
"work_id": "m48s-fixed-class-detector",
|
||||
"evidence": {
|
||||
"runtime_relative_root": "m48s-semantic-shadow/fixed-class-detector-lab-results",
|
||||
"result_id_prefix": "m48s-fixed-class-detector-lab",
|
||||
"document_name": "manifest.json",
|
||||
"schema_version": "missioncore.m48s-fixed-class-detector-lab/v1"
|
||||
}
|
||||
}
|
||||
@@ -114,6 +114,20 @@
|
||||
"run": "missioncore.laboratory-run/v1",
|
||||
"evidence": "missioncore.e47-semantic-slam-result/v1"
|
||||
}
|
||||
},
|
||||
{
|
||||
"work_id": "m48s-fixed-class-detector",
|
||||
"lifecycle": "experimental",
|
||||
"isolation": "bounded-adapter",
|
||||
"adapter_id": "experimental.m48s-fixed-class-detector/v1",
|
||||
"input_roles": ["repository_root"],
|
||||
"contracts": {
|
||||
"source": "missioncore.m48s-sealed-detector-evidence/v1",
|
||||
"provider": "missioncore.rf-detr-risk-shadow-provider/v1",
|
||||
"graph": "missioncore.m48s-fixed-class-detector-lab-graph/v1",
|
||||
"run": "missioncore.laboratory-run/v1",
|
||||
"evidence": "missioncore.m48s-fixed-class-detector-lab/v1"
|
||||
}
|
||||
}
|
||||
],
|
||||
"legacy_work_ids": [
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"schema_version": "missioncore.laboratory-value-review-registry/v1",
|
||||
"reviewed_at_utc": "2026-08-05T15:34:00Z",
|
||||
"reviewed_at_utc": "2026-08-25T11:06:28Z",
|
||||
"entries": [
|
||||
{
|
||||
"catalog_id": "e28-local-surface",
|
||||
@@ -246,6 +246,13 @@
|
||||
"signal": "retained",
|
||||
"lifecycle": "current",
|
||||
"visual_evidence": "available"
|
||||
},
|
||||
{
|
||||
"catalog_id": "m48s-fixed-class-detector",
|
||||
"evidence_id": "m48s-fixed-class-detector-lab-d1bac05a9e43d407b0f931105cc0e84183ef9ff37666911f03c41486beeb7ef9",
|
||||
"signal": "progress",
|
||||
"lifecycle": "current",
|
||||
"visual_evidence": "available"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Publish the sealed M4.8S detector evidence as an immutable LAB result."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from k1link.laboratory.m48s_fixed_class_detector_lab import (
|
||||
build_m48s_fixed_class_detector_lab,
|
||||
)
|
||||
|
||||
|
||||
def main() -> int:
|
||||
repository = Path(__file__).resolve().parents[2]
|
||||
result = build_m48s_fixed_class_detector_lab(
|
||||
repository_root=repository,
|
||||
output_root=(
|
||||
repository
|
||||
/ ".runtime/compute-experiments/m48s-semantic-shadow/"
|
||||
"fixed-class-detector-lab-results"
|
||||
),
|
||||
)
|
||||
print(result.result_id)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -321,9 +321,27 @@ def canonical_laboratory_adapters() -> dict[str, LaboratoryAdapter]:
|
||||
"canonical.e35-degradation-recovery/v1": _run_e35,
|
||||
"canonical.e46j-raw-fisheye-realtime/v1": _run_e46j,
|
||||
"experimental.e47-semantic-slam-shadow/v1": _run_e47,
|
||||
"experimental.m48s-fixed-class-detector/v1": _run_m48s_fixed_class_detector,
|
||||
}
|
||||
|
||||
|
||||
def _run_m48s_fixed_class_detector(
|
||||
request: LaboratoryRunRequest,
|
||||
) -> LaboratoryAdapterResult:
|
||||
from k1link.laboratory.m48s_fixed_class_detector_lab import (
|
||||
build_m48s_fixed_class_detector_lab,
|
||||
)
|
||||
|
||||
result = build_m48s_fixed_class_detector_lab(
|
||||
repository_root=request.inputs["repository_root"],
|
||||
output_root=request.output_root,
|
||||
)
|
||||
return LaboratoryAdapterResult(
|
||||
result_root=result.result_root,
|
||||
result_id=result.result_id,
|
||||
)
|
||||
|
||||
|
||||
def _run_m48_small_static_passage_regression(
|
||||
request: LaboratoryRunRequest,
|
||||
) -> LaboratoryAdapterResult:
|
||||
|
||||
@@ -0,0 +1,892 @@
|
||||
"""Build the immutable M4.8S fixed-class detector laboratory projection."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
import shutil
|
||||
import tempfile
|
||||
from dataclasses import dataclass
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
from typing import Any, Final, cast
|
||||
|
||||
from k1link.perception.fixed_class_detector_tournament import (
|
||||
canonical_json,
|
||||
false_authority,
|
||||
sha256_path,
|
||||
)
|
||||
|
||||
LAB_SCHEMA: Final = "missioncore.m48s-fixed-class-detector-lab/v1"
|
||||
CATALOG_SCHEMA: Final = "missioncore.m48s-fixed-class-detector-frame-catalog/v1"
|
||||
FRAME_SCHEMA: Final = "missioncore.m48s-fixed-class-detector-frame/v1"
|
||||
REPORT_SCHEMA: Final = "missioncore.m48s-fixed-class-detector-report/v1"
|
||||
METHOD_SCHEMA: Final = "missioncore.laboratory-method/v1"
|
||||
RESULT_PREFIX: Final = "m48s-fixed-class-detector-lab-"
|
||||
TOURNAMENT_ID: Final = (
|
||||
"m48s-fixed-detector-tournament-"
|
||||
"0e61d75e6dc575d53e4bb98772a41d240fe627ad642de5178beb1154636e1299"
|
||||
)
|
||||
DEPLOYMENT_ID: Final = (
|
||||
"m48s-rf-detr-deployment-gate-2feb9e1b12a5588951ad35d63bf23cf6bdd579d54b5329d46d7696f88c444547"
|
||||
)
|
||||
REFERENCE_GRAPH_ID: Final = (
|
||||
"m48s-reference-graph-shadow-gate-"
|
||||
"e8da7a521768daba0ead1a6e4803871ce3a85f91a7d8ee36c5719ac10433e791"
|
||||
)
|
||||
REFERENCE_GRAPH_REPLAY_ID: Final = (
|
||||
"m48s-reference-graph-replay-"
|
||||
"16d69d610c22e6f42071b8378cd75dfa6b95db4ceb800f9c7508fa3673504478"
|
||||
)
|
||||
INTEGRATED_STATUS: Final = "complete-reference-graph-shadow-passed-production-not-authorized"
|
||||
YOLOX_ID: Final = (
|
||||
"m48s-yolox-all-coco-shadow-7dbe6043b3fc12c7ddb162f609f883d86b34a4f2dd3785a632795f257e192d06"
|
||||
)
|
||||
SOURCE_SHA256: Final = "cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8"
|
||||
ENGINE_SHA256: Final = "986399ce706b7380472cf5e473232249fed6e628971d8007f6609e83128d46b8"
|
||||
RISK_LABELS: Final = frozenset(
|
||||
{
|
||||
"person",
|
||||
"bicycle",
|
||||
"car",
|
||||
"motorcycle",
|
||||
"bus",
|
||||
"truck",
|
||||
"bird",
|
||||
"cat",
|
||||
"dog",
|
||||
"horse",
|
||||
"sheep",
|
||||
"cow",
|
||||
"elephant",
|
||||
"bear",
|
||||
"zebra",
|
||||
"giraffe",
|
||||
"skateboard",
|
||||
}
|
||||
)
|
||||
FRAME_IDS: Final = (
|
||||
"000121",
|
||||
"000131",
|
||||
"000253",
|
||||
"000275",
|
||||
"000443",
|
||||
"000463",
|
||||
"001094",
|
||||
"001228",
|
||||
"001454",
|
||||
"001856",
|
||||
"002386",
|
||||
)
|
||||
|
||||
|
||||
class M48SFixedClassDetectorLabError(RuntimeError):
|
||||
"""The sealed detector evidence cannot produce an honest LAB result."""
|
||||
|
||||
|
||||
@dataclass(frozen=True, slots=True)
|
||||
class M48SFixedClassDetectorLabResult:
|
||||
result_root: Path
|
||||
result_id: str
|
||||
manifest: dict[str, Any]
|
||||
|
||||
|
||||
def build_m48s_fixed_class_detector_lab(
|
||||
*,
|
||||
repository_root: Path,
|
||||
output_root: Path,
|
||||
) -> M48SFixedClassDetectorLabResult:
|
||||
repository = repository_root.expanduser().resolve(strict=True)
|
||||
if not repository.is_dir():
|
||||
raise M48SFixedClassDetectorLabError("repository root is invalid")
|
||||
runtime = repository / ".runtime/compute-experiments/m48s-semantic-shadow"
|
||||
tournament_path = (
|
||||
runtime / "fixed-detector-tournament-results" / TOURNAMENT_ID / "manifest.json"
|
||||
)
|
||||
deployment_root = runtime / "rf-detr-deployment-results" / DEPLOYMENT_ID
|
||||
deployment_path = deployment_root / "manifest.json"
|
||||
load_path = deployment_root / "load_result.json"
|
||||
reference_graph_root = runtime / "reference-graph-shadow-results" / REFERENCE_GRAPH_ID
|
||||
reference_graph_path = reference_graph_root / "manifest.json"
|
||||
reference_graph_worker_path = reference_graph_root / "worker-result.json"
|
||||
reference_graph_replay_root = (
|
||||
runtime / "reference-graph-replay-results" / REFERENCE_GRAPH_REPLAY_ID
|
||||
)
|
||||
reference_graph_replay_path = reference_graph_replay_root / "manifest.json"
|
||||
reference_graph_replay_worker_path = reference_graph_replay_root / "worker-result.json"
|
||||
reference_graph_replay_frames_path = reference_graph_replay_root / "frames.jsonl"
|
||||
yolox_root = runtime / "yolox-all-coco-results" / YOLOX_ID
|
||||
yolox_manifest_path = yolox_root / "manifest.json"
|
||||
yolox_frames_path = yolox_root / "frames.jsonl"
|
||||
candidate_root = runtime / "fixed-detector-tournament-worker"
|
||||
dfine_path = candidate_root / "dfine-s-worker.json"
|
||||
rf_detr_path = candidate_root / "rf-detr-large-triton-worker.json"
|
||||
source_root = runtime / "raw-11-frames-v1"
|
||||
profile_path = repository / "config/perception/rf-detr-large-risk-shadow-v0.json"
|
||||
for path in (
|
||||
tournament_path,
|
||||
deployment_path,
|
||||
load_path,
|
||||
reference_graph_path,
|
||||
reference_graph_worker_path,
|
||||
reference_graph_replay_path,
|
||||
reference_graph_replay_worker_path,
|
||||
reference_graph_replay_frames_path,
|
||||
yolox_manifest_path,
|
||||
yolox_frames_path,
|
||||
dfine_path,
|
||||
rf_detr_path,
|
||||
profile_path,
|
||||
):
|
||||
if path.is_symlink() or not path.is_file():
|
||||
raise M48SFixedClassDetectorLabError(
|
||||
f"required sealed evidence is missing: {path.name}"
|
||||
)
|
||||
|
||||
tournament = _read_object(tournament_path)
|
||||
deployment = _read_object(deployment_path)
|
||||
load = _read_object(load_path)
|
||||
reference_graph = _read_object(reference_graph_path)
|
||||
reference_graph_worker = _read_object(reference_graph_worker_path)
|
||||
reference_graph_replay = _read_object(reference_graph_replay_path)
|
||||
reference_graph_replay_worker = _read_object(reference_graph_replay_worker_path)
|
||||
yolox_manifest = _read_object(yolox_manifest_path)
|
||||
dfine = _read_object(dfine_path)
|
||||
rf_detr = _read_object(rf_detr_path)
|
||||
profile = _read_object(profile_path)
|
||||
yolox_frames = _read_jsonl(yolox_frames_path)
|
||||
_validate_inputs(
|
||||
tournament=tournament,
|
||||
deployment=deployment,
|
||||
load=load,
|
||||
reference_graph=reference_graph,
|
||||
reference_graph_worker=reference_graph_worker,
|
||||
reference_graph_replay=reference_graph_replay,
|
||||
reference_graph_replay_worker=reference_graph_replay_worker,
|
||||
reference_graph_replay_frames_path=reference_graph_replay_frames_path,
|
||||
yolox_manifest=yolox_manifest,
|
||||
dfine=dfine,
|
||||
rf_detr=rf_detr,
|
||||
profile=profile,
|
||||
yolox_frames=yolox_frames,
|
||||
)
|
||||
|
||||
source_paths = {frame_id: source_root / f"frame-{frame_id}.jpg" for frame_id in FRAME_IDS}
|
||||
if any(path.is_symlink() or not path.is_file() for path in source_paths.values()):
|
||||
raise M48SFixedClassDetectorLabError("the exact 11-frame visual slice is incomplete")
|
||||
source_descriptors = [
|
||||
{
|
||||
"frame_id": frame_id,
|
||||
"source_sequence": int(frame_id),
|
||||
"sha256": sha256_path(source_paths[frame_id]),
|
||||
"byte_length": source_paths[frame_id].stat().st_size,
|
||||
}
|
||||
for frame_id in FRAME_IDS
|
||||
]
|
||||
method = _method(
|
||||
profile=profile,
|
||||
tournament=tournament,
|
||||
deployment=deployment,
|
||||
reference_graph=reference_graph,
|
||||
)
|
||||
identity = {
|
||||
"schema_version": LAB_SCHEMA,
|
||||
"source": {
|
||||
"source_session_id": "RAVNOVES00",
|
||||
"recording_sha256": SOURCE_SHA256,
|
||||
"camera_source_id": "sensor.camera.right",
|
||||
"camera_raster": [800, 600],
|
||||
"evidence_frame_count": len(FRAME_IDS),
|
||||
"replay_frame_count": 4489,
|
||||
"frames": source_descriptors,
|
||||
},
|
||||
"configuration": {
|
||||
"comparison_threshold": 0.5,
|
||||
"display_modes": ["source", "yolox", "dfine", "rf-detr"],
|
||||
"replay_display_modes": ["video", "camera", "3d", "plan"],
|
||||
"camera_point_overlay": "factory-kb4-exact",
|
||||
"world_state_delivery": "source-paced-latest-wins",
|
||||
"single_inference_per_frame": True,
|
||||
"risk_labels": sorted(RISK_LABELS),
|
||||
"geometry_owns_static_occupancy": True,
|
||||
"unknown_stationary_response": "route-around",
|
||||
"unknown_moving_response": "conservative-risk",
|
||||
},
|
||||
"inputs": {
|
||||
"tournament_result_id": TOURNAMENT_ID,
|
||||
"tournament_document_sha256": sha256_path(tournament_path),
|
||||
"deployment_result_id": DEPLOYMENT_ID,
|
||||
"deployment_document_sha256": sha256_path(deployment_path),
|
||||
"reference_graph_result_id": REFERENCE_GRAPH_ID,
|
||||
"reference_graph_document_sha256": sha256_path(reference_graph_path),
|
||||
"reference_graph_worker_sha256": sha256_path(reference_graph_worker_path),
|
||||
"reference_graph_replay_result_id": REFERENCE_GRAPH_REPLAY_ID,
|
||||
"reference_graph_replay_document_sha256": sha256_path(
|
||||
reference_graph_replay_path
|
||||
),
|
||||
"reference_graph_replay_worker_sha256": sha256_path(
|
||||
reference_graph_replay_worker_path
|
||||
),
|
||||
"reference_graph_replay_frames_sha256": sha256_path(
|
||||
reference_graph_replay_frames_path
|
||||
),
|
||||
"yolox_result_id": YOLOX_ID,
|
||||
"yolox_document_sha256": sha256_path(yolox_manifest_path),
|
||||
},
|
||||
"method": method,
|
||||
"authority": false_authority(),
|
||||
}
|
||||
identity_sha256 = hashlib.sha256(canonical_json(identity)).hexdigest()
|
||||
result_id = RESULT_PREFIX + identity_sha256
|
||||
completed_utc_ns = reference_graph_replay_worker.get("completed_utc_ns")
|
||||
if not isinstance(completed_utc_ns, int) or isinstance(completed_utc_ns, bool):
|
||||
raise M48SFixedClassDetectorLabError(
|
||||
"complete reference-graph completion time is unavailable"
|
||||
)
|
||||
created_at_utc = (
|
||||
datetime.fromtimestamp(completed_utc_ns / 1_000_000_000, UTC)
|
||||
.isoformat(timespec="microseconds")
|
||||
.replace("+00:00", "Z")
|
||||
)
|
||||
candidates = _candidate_summaries(
|
||||
tournament=tournament,
|
||||
yolox_manifest=yolox_manifest,
|
||||
dfine=dfine,
|
||||
rf_detr=rf_detr,
|
||||
deployment=deployment,
|
||||
)
|
||||
metrics = _metrics(
|
||||
deployment=deployment,
|
||||
load=load,
|
||||
reference_graph=reference_graph,
|
||||
candidates=candidates,
|
||||
)
|
||||
decision = {
|
||||
"bounded_question_accepted": True,
|
||||
"selected_candidate": "rf-detr",
|
||||
"ready_for_reference_graph_shadow": True,
|
||||
"integrated_world_state_gate_evaluated": True,
|
||||
"integrated_world_state_gate_passed": True,
|
||||
"full_replay_visual_published": True,
|
||||
"detector_replacement_authorized": False,
|
||||
"production_accepted": False,
|
||||
}
|
||||
limitations = [
|
||||
"The 11-frame slice is diagnostic and has no independent semantic ground truth.",
|
||||
(
|
||||
"The complete reference-graph replay qualifies runtime behavior, but has no "
|
||||
"independent track-identity or risk-policy truth."
|
||||
),
|
||||
"COCO has no dedicated scooter class; unknown moving objects remain conservative hazards.",
|
||||
(
|
||||
"Camera boxes do not replace geometry-owned occupancy or grant navigation/safety "
|
||||
"authority."
|
||||
),
|
||||
(
|
||||
"Eight source frames were superseded by the qualified latest-wins graph; their "
|
||||
"camera/LiDAR source evidence remains visible without invented world state."
|
||||
),
|
||||
]
|
||||
|
||||
root = output_root.expanduser().absolute()
|
||||
root.mkdir(mode=0o700, parents=True, exist_ok=True)
|
||||
destination = root / result_id
|
||||
if destination.exists():
|
||||
raise M48SFixedClassDetectorLabError("immutable M4.8S LAB result already exists")
|
||||
temporary = Path(tempfile.mkdtemp(prefix=".m48s-fixed-class-lab-", dir=root))
|
||||
try:
|
||||
(temporary / "frames").mkdir(mode=0o700)
|
||||
yolox_by_frame = _yolox_by_frame(yolox_frames)
|
||||
dfine_by_frame = _worker_by_frame(dfine)
|
||||
rf_detr_by_frame = _worker_by_frame(rf_detr)
|
||||
frame_descriptors: list[dict[str, object]] = []
|
||||
for frame_id in FRAME_IDS:
|
||||
camera_name = f"frames/frame-{frame_id}.jpg"
|
||||
detail_name = f"frame-{frame_id}.json"
|
||||
camera_path = temporary / camera_name
|
||||
shutil.copyfile(source_paths[frame_id], camera_path)
|
||||
detections_by_model: dict[str, list[dict[str, object]]] = {
|
||||
"yolox": _qualified(yolox_by_frame[frame_id]),
|
||||
"dfine": _qualified(dfine_by_frame[frame_id]),
|
||||
"rf-detr": _qualified(rf_detr_by_frame[frame_id]),
|
||||
}
|
||||
frame_document = {
|
||||
"schema_version": FRAME_SCHEMA,
|
||||
"result_id": result_id,
|
||||
"frame_id": frame_id,
|
||||
"source_sequence": int(frame_id),
|
||||
"camera": {
|
||||
"path": camera_name,
|
||||
"media_type": "image/jpeg",
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"sha256": sha256_path(camera_path),
|
||||
"exact_source_frame": True,
|
||||
},
|
||||
"comparison_threshold": 0.5,
|
||||
"detections": detections_by_model,
|
||||
"ground_truth_available": False,
|
||||
"authority": false_authority(),
|
||||
}
|
||||
detail_path = temporary / detail_name
|
||||
detail_path.write_bytes(canonical_json(frame_document) + b"\n")
|
||||
frame_descriptors.append(
|
||||
{
|
||||
"frame_id": frame_id,
|
||||
"source_sequence": int(frame_id),
|
||||
"camera_path": camera_name,
|
||||
"camera_sha256": sha256_path(camera_path),
|
||||
"camera_byte_length": camera_path.stat().st_size,
|
||||
"detail_path": detail_name,
|
||||
"detail_sha256": sha256_path(detail_path),
|
||||
"detail_byte_length": detail_path.stat().st_size,
|
||||
"counts": {key: len(value) for key, value in detections_by_model.items()},
|
||||
}
|
||||
)
|
||||
catalog = {
|
||||
"schema_version": CATALOG_SCHEMA,
|
||||
"result_id": result_id,
|
||||
"frame_count": len(frame_descriptors),
|
||||
"frames": frame_descriptors,
|
||||
}
|
||||
catalog_path = temporary / "catalog.json"
|
||||
catalog_path.write_bytes(canonical_json(catalog) + b"\n")
|
||||
shutil.copyfile(tournament_path, temporary / "tournament.json")
|
||||
shutil.copyfile(deployment_path, temporary / "deployment.json")
|
||||
shutil.copyfile(reference_graph_path, temporary / "reference-graph.json")
|
||||
shutil.copyfile(
|
||||
reference_graph_worker_path,
|
||||
temporary / "reference-graph-worker-result.json",
|
||||
)
|
||||
shutil.copyfile(
|
||||
reference_graph_replay_path,
|
||||
temporary / "reference-graph-replay.json",
|
||||
)
|
||||
shutil.copyfile(
|
||||
reference_graph_replay_worker_path,
|
||||
temporary / "reference-graph-replay-worker-result.json",
|
||||
)
|
||||
shutil.copyfile(
|
||||
reference_graph_replay_frames_path,
|
||||
temporary / "reference-graph-replay-frames.jsonl",
|
||||
)
|
||||
report = {
|
||||
"schema_version": REPORT_SCHEMA,
|
||||
"result_id": result_id,
|
||||
"source": identity["source"],
|
||||
"configuration": identity["configuration"],
|
||||
"method": method,
|
||||
"execution": {
|
||||
"detector_load": load["execution"],
|
||||
"complete_reference_graph": reference_graph["identity"]["evidence"]["execution"],
|
||||
},
|
||||
"metrics": metrics,
|
||||
"acceptance": {
|
||||
"detector_load": load["checks"],
|
||||
"complete_reference_graph": reference_graph["identity"]["evidence"]["checks"],
|
||||
},
|
||||
"decision": decision,
|
||||
"limitations": limitations,
|
||||
"authority": false_authority(),
|
||||
"visual_evidence": {
|
||||
"kind": "full-reference-graph-recorded-replay",
|
||||
"frame_count": 4489,
|
||||
"world_state_frame_count": 4481,
|
||||
"superseded_frame_count": 8,
|
||||
"modes": ["video", "camera", "3d", "plan"],
|
||||
"camera_layers": ["rf-detr", "points"],
|
||||
"ground_truth": False,
|
||||
},
|
||||
}
|
||||
report_path = temporary / "report.json"
|
||||
report_path.write_bytes(canonical_json(report) + b"\n")
|
||||
artifacts = _artifact_manifest(temporary)
|
||||
manifest = {
|
||||
"schema_version": LAB_SCHEMA,
|
||||
"result_id": result_id,
|
||||
"identity_sha256": identity_sha256,
|
||||
"identity": identity,
|
||||
"created_at_utc": created_at_utc,
|
||||
"status": INTEGRATED_STATUS,
|
||||
"completed": True,
|
||||
"bounded_question_accepted": True,
|
||||
"ground_truth": False,
|
||||
"catalog": {
|
||||
"path": "catalog.json",
|
||||
"sha256": sha256_path(catalog_path),
|
||||
"byte_length": catalog_path.stat().st_size,
|
||||
},
|
||||
"method": method,
|
||||
"metrics": metrics,
|
||||
"decision": decision,
|
||||
"limitations": limitations,
|
||||
"authority": false_authority(),
|
||||
"artifacts": artifacts,
|
||||
}
|
||||
(temporary / "manifest.json").write_bytes(canonical_json(manifest) + b"\n")
|
||||
temporary.replace(destination)
|
||||
except BaseException:
|
||||
shutil.rmtree(temporary, ignore_errors=True)
|
||||
raise
|
||||
return M48SFixedClassDetectorLabResult(
|
||||
result_root=destination,
|
||||
result_id=result_id,
|
||||
manifest=manifest,
|
||||
)
|
||||
|
||||
|
||||
def _validate_inputs(
|
||||
*,
|
||||
tournament: dict[str, Any],
|
||||
deployment: dict[str, Any],
|
||||
load: dict[str, Any],
|
||||
reference_graph: dict[str, Any],
|
||||
reference_graph_worker: dict[str, Any],
|
||||
reference_graph_replay: dict[str, Any],
|
||||
reference_graph_replay_worker: dict[str, Any],
|
||||
reference_graph_replay_frames_path: Path,
|
||||
yolox_manifest: dict[str, Any],
|
||||
dfine: dict[str, Any],
|
||||
rf_detr: dict[str, Any],
|
||||
profile: dict[str, Any],
|
||||
yolox_frames: list[dict[str, Any]],
|
||||
) -> None:
|
||||
decision = deployment.get("decision")
|
||||
graph_identity = reference_graph.get("identity")
|
||||
graph_artifacts = reference_graph.get("artifacts")
|
||||
graph_decision = graph_identity.get("decision") if isinstance(graph_identity, dict) else None
|
||||
graph_checks = reference_graph_worker.get("checks")
|
||||
replay_identity = reference_graph_replay.get("identity")
|
||||
replay_artifacts = reference_graph_replay.get("artifacts")
|
||||
replay_frame_summary = (
|
||||
replay_identity.get("evidence", {}).get("frame_summary")
|
||||
if isinstance(replay_identity, dict)
|
||||
and isinstance(replay_identity.get("evidence"), dict)
|
||||
else None
|
||||
)
|
||||
if (
|
||||
tournament.get("result_id") != TOURNAMENT_ID
|
||||
or tournament.get("accepted") is not False
|
||||
or deployment.get("result_id") != DEPLOYMENT_ID
|
||||
or deployment.get("accepted") is not False
|
||||
or not isinstance(decision, dict)
|
||||
or decision.get("ready_for_reference_graph_shadow") is not True
|
||||
or decision.get("production_accepted") is not False
|
||||
or load.get("detector_load_gate_passed") is not True
|
||||
or load.get("candidate_accepted") is not False
|
||||
or reference_graph.get("result_id") != REFERENCE_GRAPH_ID
|
||||
or reference_graph.get("schema_version")
|
||||
!= "missioncore.m48s-reference-graph-shadow-gate/v0"
|
||||
or not isinstance(graph_identity, dict)
|
||||
or graph_identity.get("graph_id") != "reference-perception-graph/v2"
|
||||
or graph_identity.get("accepted") is not True
|
||||
or graph_identity.get("production_accepted") is not False
|
||||
or graph_identity.get("authority") != false_authority()
|
||||
or not isinstance(graph_decision, dict)
|
||||
or graph_decision.get("complete_reference_graph_shadow_passed") is not True
|
||||
or graph_decision.get("source_paced_runtime_gate_accepted") is not True
|
||||
or graph_decision.get("detector_replacement_authorized") is not False
|
||||
or graph_decision.get("production_accepted") is not False
|
||||
or not isinstance(graph_artifacts, dict)
|
||||
or graph_artifacts.get("worker-result.json")
|
||||
!= hashlib.sha256(canonical_json(reference_graph_worker) + b"\n").hexdigest()
|
||||
or reference_graph_worker.get("completed") is not True
|
||||
or reference_graph_worker.get("integrated_runtime_gate_passed") is not True
|
||||
or reference_graph_worker.get("production_accepted") is not False
|
||||
or reference_graph_worker.get("authority") != false_authority()
|
||||
or not isinstance(graph_checks, dict)
|
||||
or not graph_checks
|
||||
or not all(value is True for value in graph_checks.values())
|
||||
or reference_graph_replay.get("result_id") != REFERENCE_GRAPH_REPLAY_ID
|
||||
or reference_graph_replay.get("schema_version")
|
||||
!= "missioncore.m48s-reference-graph-replay/v0"
|
||||
or not isinstance(replay_identity, dict)
|
||||
or replay_identity.get("accepted") is not True
|
||||
or replay_identity.get("production_accepted") is not False
|
||||
or replay_identity.get("authority") != false_authority()
|
||||
or not isinstance(replay_frame_summary, dict)
|
||||
or replay_frame_summary.get("source_frame_count") != 4489
|
||||
or replay_frame_summary.get("world_state_frame_count") != 4481
|
||||
or replay_frame_summary.get("superseded_frame_count") != 8
|
||||
or not isinstance(replay_artifacts, dict)
|
||||
or not isinstance(replay_artifacts.get("frames.jsonl"), dict)
|
||||
or replay_artifacts["frames.jsonl"].get("sha256")
|
||||
!= sha256_path(reference_graph_replay_frames_path)
|
||||
or not isinstance(replay_artifacts.get("worker-result.json"), dict)
|
||||
or replay_artifacts["worker-result.json"].get("sha256")
|
||||
!= hashlib.sha256(canonical_json(reference_graph_replay_worker) + b"\n").hexdigest()
|
||||
or reference_graph_replay_worker.get("integrated_runtime_gate_passed") is not True
|
||||
or reference_graph_replay_worker.get("production_accepted") is not False
|
||||
or reference_graph_replay_worker.get("authority") != false_authority()
|
||||
or yolox_manifest.get("result_id") != YOLOX_ID
|
||||
or dfine.get("profile_id") != "dfine-s-coco-640-fp16/v0"
|
||||
or rf_detr.get("profile_id") != "rf-detr-large-coco-704-trt11-fp16/v0"
|
||||
or rf_detr.get("engine_sha256") != ENGINE_SHA256
|
||||
or profile.get("profile_id") != "rf-detr-large-coco-704-trt11-fp16-risk-shadow/v0"
|
||||
or len(yolox_frames) != len(FRAME_IDS)
|
||||
):
|
||||
raise M48SFixedClassDetectorLabError("sealed M4.8S evidence identity changed")
|
||||
for document in (
|
||||
tournament,
|
||||
deployment,
|
||||
load,
|
||||
reference_graph_worker,
|
||||
reference_graph_replay_worker,
|
||||
dfine,
|
||||
rf_detr,
|
||||
profile,
|
||||
):
|
||||
authority = document.get("authority")
|
||||
if authority is not None and authority != false_authority():
|
||||
raise M48SFixedClassDetectorLabError("sealed M4.8S evidence gained authority")
|
||||
|
||||
|
||||
def _method(
|
||||
*,
|
||||
profile: dict[str, Any],
|
||||
tournament: dict[str, Any],
|
||||
deployment: dict[str, Any],
|
||||
reference_graph: dict[str, Any],
|
||||
) -> dict[str, object]:
|
||||
candidates = tournament["candidates"]
|
||||
if not isinstance(candidates, dict):
|
||||
raise M48SFixedClassDetectorLabError("tournament candidates are unavailable")
|
||||
dfine = candidates["dfine-s-coco-640-fp16/v0"]
|
||||
rf_detr = candidates["rf-detr-large-coco-704-fp16/v0"]
|
||||
if not isinstance(dfine, dict) or not isinstance(rf_detr, dict):
|
||||
raise M48SFixedClassDetectorLabError("tournament candidates are invalid")
|
||||
return {
|
||||
"schema_version": METHOD_SCHEMA,
|
||||
"completeness": "complete",
|
||||
"execution_class": "ai-inference",
|
||||
"pipeline_id": "raw-kb4-rf-detr-reference-graph-shadow/v1",
|
||||
"components": [
|
||||
{
|
||||
"kind": "source",
|
||||
"name": "RAVNOVES00 RIGHT",
|
||||
"version": "immutable recorded source",
|
||||
"role": "11-frame visual slice and 30-minute source-paced replay",
|
||||
"identity_sha256": SOURCE_SHA256,
|
||||
},
|
||||
{
|
||||
"kind": "model",
|
||||
"name": "YOLOX-S COCO-80",
|
||||
"version": "triton-yolox-s-raw-kb4-all-coco/v2",
|
||||
"role": "regression baseline",
|
||||
"identity_sha256": (
|
||||
"c5c2d13e59ae883e6af3b45daea64af4833a4951c92d116ec270d9ddbe998063"
|
||||
),
|
||||
},
|
||||
{
|
||||
"kind": "model",
|
||||
"name": "D-FINE-S COCO",
|
||||
"version": str(dfine["upstream_revision"]),
|
||||
"role": "fixed-class tournament candidate",
|
||||
"identity_sha256": str(dfine["checkpoint_sha256"]),
|
||||
},
|
||||
{
|
||||
"kind": "model",
|
||||
"name": "RF-DETR-L COCO",
|
||||
"version": str(rf_detr["upstream_revision"]),
|
||||
"role": "selected fixed-class risk detector",
|
||||
"identity_sha256": str(rf_detr["checkpoint_sha256"]),
|
||||
},
|
||||
{
|
||||
"kind": "runtime",
|
||||
"name": "TensorRT 11 + isolated Triton",
|
||||
"version": "worker-006 RTX 4090 strongly-typed-fp16",
|
||||
"role": "numeric parity and source-paced load qualification",
|
||||
"identity_sha256": str(deployment["evidence"]["engine_sha256"]),
|
||||
},
|
||||
{
|
||||
"kind": "algorithm",
|
||||
"name": "risk-only fixed-class qualification",
|
||||
"version": str(profile["profile_id"]),
|
||||
"role": "emit behavior-relevant semantics while geometry owns static occupancy",
|
||||
"identity_sha256": hashlib.sha256(canonical_json(profile)).hexdigest(),
|
||||
},
|
||||
{
|
||||
"kind": "algorithm",
|
||||
"name": "reference perception graph",
|
||||
"version": str(reference_graph["identity"]["graph_id"]),
|
||||
"role": (
|
||||
"geometry, temporal identity, motion, rolling occupancy, threat, "
|
||||
"and class advisory"
|
||||
),
|
||||
"identity_sha256": str(
|
||||
reference_graph["identity"]["evidence"]["files"]["graph_config"]["sha256"]
|
||||
),
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
|
||||
def _candidate_summaries(
|
||||
*,
|
||||
tournament: dict[str, Any],
|
||||
yolox_manifest: dict[str, Any],
|
||||
dfine: dict[str, Any],
|
||||
rf_detr: dict[str, Any],
|
||||
deployment: dict[str, Any],
|
||||
) -> list[dict[str, object]]:
|
||||
baseline = tournament["baseline"]
|
||||
candidates = tournament["candidates"]
|
||||
triton_benchmark = deployment["evidence"]["triton_benchmark"]
|
||||
if not isinstance(baseline, dict) or not isinstance(candidates, dict):
|
||||
raise M48SFixedClassDetectorLabError("tournament summaries are invalid")
|
||||
return [
|
||||
{
|
||||
"id": "yolox",
|
||||
"label": "YOLOX-S",
|
||||
"provider_id": baseline["provider_id"],
|
||||
"capacity_fps": yolox_manifest["metrics"]["all_coco_core_capacity_fps"],
|
||||
"p95_ms": yolox_manifest["metrics"]["timing_ms"]["all_coco_core_ms"]["p95"],
|
||||
"frame_253_dog_detected": False,
|
||||
"frame_253_dog_score": None,
|
||||
"selected": False,
|
||||
},
|
||||
{
|
||||
"id": "dfine",
|
||||
"label": "D-FINE-S",
|
||||
"provider_id": dfine["provider_id"],
|
||||
"capacity_fps": dfine["metrics"]["benchmark"]["core_capacity_fps"],
|
||||
"p95_ms": dfine["metrics"]["benchmark"]["timing_ms"]["p95"],
|
||||
"frame_253_dog_detected": False,
|
||||
"frame_253_dog_score": None,
|
||||
"selected": False,
|
||||
},
|
||||
{
|
||||
"id": "rf-detr",
|
||||
"label": "RF-DETR-L",
|
||||
"provider_id": rf_detr["provider_id"],
|
||||
"capacity_fps": triton_benchmark["end_to_end_capacity_fps"],
|
||||
"p95_ms": triton_benchmark["timing_ms"]["p95"],
|
||||
"frame_253_dog_detected": True,
|
||||
"frame_253_dog_score": deployment["evidence"]["tensorrt_parity"]["tensorrt_score"],
|
||||
"selected": True,
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
def _metrics(
|
||||
*,
|
||||
deployment: dict[str, Any],
|
||||
load: dict[str, Any],
|
||||
reference_graph: dict[str, Any],
|
||||
candidates: list[dict[str, object]],
|
||||
) -> dict[str, object]:
|
||||
graph_evidence = reference_graph["identity"]["evidence"]
|
||||
graph_execution = graph_evidence["execution"]
|
||||
graph_completion = graph_evidence["world_state_completion_age_ms"]
|
||||
map_age = graph_evidence["local_obstacle_map_output_age_ms"]
|
||||
gpu = graph_evidence["gpu"]
|
||||
identity = graph_evidence["identity_continuity"]
|
||||
advisory = graph_evidence["semantic_advisory"]
|
||||
terminal_outcomes = graph_execution["terminal_outcomes"]
|
||||
return {
|
||||
"candidate_count": len(candidates),
|
||||
"evidence_frame_count": len(FRAME_IDS),
|
||||
"selected_candidate": "rf-detr",
|
||||
"candidates": candidates,
|
||||
"tensorrt_parity": deployment["evidence"]["tensorrt_parity"],
|
||||
"detector_load": {
|
||||
"duration_seconds": load["execution"]["wall_seconds"],
|
||||
"source_frames_consumed": load["execution"]["source_frames_consumed"],
|
||||
"source_frame_replacements": load["execution"]["source_frame_replacements"],
|
||||
"effective_consumed_fps": load["execution"]["effective_consumed_fps"],
|
||||
"end_to_end_p95_ms": load["metrics"]["end_to_end_ms"]["p95"],
|
||||
"completion_age_p95_ms": load["metrics"]["detector_completion_age_ms"]["p95"],
|
||||
"gpu_utilization_mean_percent": load["metrics"]["gpu"]["gpu_utilization_percent"][
|
||||
"mean"
|
||||
],
|
||||
"gpu_utilization_maximum_percent": load["metrics"]["gpu"]["gpu_utilization_percent"][
|
||||
"maximum"
|
||||
],
|
||||
"gpu_memory_maximum_mib": load["metrics"]["gpu"]["gpu_memory_used_mib"]["maximum"],
|
||||
"queue_maximum_depth": load["execution"]["queue_maximum_depth"],
|
||||
"queue_capacity": load["execution"]["queue_capacity"],
|
||||
"failures": load["execution"]["failures"],
|
||||
},
|
||||
"integrated_world_state": {
|
||||
"duration_seconds": graph_execution["source_processing_wall_seconds"],
|
||||
"source_frames_admitted": graph_execution["admitted_frames"],
|
||||
"delivered_world_states": graph_execution["delivered_world_states"],
|
||||
"superseded_frames": terminal_outcomes["superseded"],
|
||||
"effective_world_state_fps": graph_execution["effective_world_state_fps"],
|
||||
"world_state_completion_age_p95_ms": graph_completion["p95"],
|
||||
"world_state_completion_age_p99_ms": graph_completion["p99"],
|
||||
"world_state_completion_age_maximum_ms": graph_completion["maximum"],
|
||||
"local_obstacle_map_output_age_p95_ms": map_age["p95"],
|
||||
"queue_high_watermarks": graph_execution["queue_high_watermarks"],
|
||||
"queue_capacity": 2,
|
||||
"gpu_utilization_mean_percent": gpu["gpu_utilization_percent"]["mean"],
|
||||
"gpu_utilization_maximum_percent": gpu["gpu_utilization_percent"]["maximum"],
|
||||
"gpu_memory_maximum_mib": gpu["gpu_memory_used_mib"]["maximum"],
|
||||
"gpu_power_maximum_w": gpu["gpu_power_w"]["maximum"],
|
||||
"gpu_temperature_maximum_c": gpu["gpu_temperature_c"]["maximum"],
|
||||
"unique_component_count": identity["unique_component_count"],
|
||||
"multi_frame_component_count": identity["multi_frame_component_count"],
|
||||
"maximum_component_publications": identity["maximum_component_publications"],
|
||||
"duplicate_component_ids_within_frame": identity[
|
||||
"duplicate_component_ids_within_frame"
|
||||
],
|
||||
"advisory_family_counts": advisory["advisory_family_counts"],
|
||||
"semantic_hint_counts": advisory["semantic_hint_counts"],
|
||||
"motion_counts": advisory["motion_counts"],
|
||||
"additional_inference_passes": advisory["additional_inference_passes"],
|
||||
"failures": sum(
|
||||
terminal_outcomes.get(key, 0)
|
||||
for key in ("failed", "stale", "rejected", "unavailable")
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def _qualified(detections: list[dict[str, Any]]) -> list[dict[str, object]]:
|
||||
result: list[dict[str, object]] = []
|
||||
for detection in detections:
|
||||
label = detection.get("label")
|
||||
score = detection.get("score")
|
||||
bbox = detection.get("bbox_xyxy")
|
||||
if (
|
||||
label not in RISK_LABELS
|
||||
or not isinstance(score, (int, float))
|
||||
or isinstance(score, bool)
|
||||
or float(score) < 0.5
|
||||
or not isinstance(bbox, list)
|
||||
or len(bbox) != 4
|
||||
or not all(isinstance(value, (int, float)) for value in bbox)
|
||||
):
|
||||
continue
|
||||
result.append(
|
||||
{
|
||||
"label": label,
|
||||
"score": round(float(score), 6),
|
||||
"bbox_xyxy": [round(float(value), 3) for value in bbox],
|
||||
}
|
||||
)
|
||||
result.sort(key=lambda item: (-cast(float, item["score"]), str(item["label"])))
|
||||
return result
|
||||
|
||||
|
||||
def _yolox_by_frame(rows: list[dict[str, Any]]) -> dict[str, list[dict[str, Any]]]:
|
||||
result: dict[str, list[dict[str, Any]]] = {}
|
||||
for row in rows:
|
||||
frame_id = _frame_id(row.get("frame_name"))
|
||||
detections = row.get("all_coco_detections")
|
||||
if frame_id is None or not isinstance(detections, list):
|
||||
raise M48SFixedClassDetectorLabError("YOLOX frame evidence is invalid")
|
||||
result[frame_id] = _objects(detections, "YOLOX detections")
|
||||
if tuple(sorted(result)) != tuple(sorted(FRAME_IDS)):
|
||||
raise M48SFixedClassDetectorLabError("YOLOX frame slice changed")
|
||||
return result
|
||||
|
||||
|
||||
def _worker_by_frame(document: dict[str, Any]) -> dict[str, list[dict[str, Any]]]:
|
||||
frames = document.get("frames")
|
||||
if not isinstance(frames, list):
|
||||
raise M48SFixedClassDetectorLabError("candidate frame evidence is invalid")
|
||||
result: dict[str, list[dict[str, Any]]] = {}
|
||||
for row in _objects(frames, "candidate frames"):
|
||||
frame_id = _frame_id(row.get("frame_name"))
|
||||
detections = row.get("detections")
|
||||
if frame_id is None or not isinstance(detections, list):
|
||||
raise M48SFixedClassDetectorLabError("candidate frame evidence is invalid")
|
||||
result[frame_id] = _objects(detections, "candidate detections")
|
||||
if tuple(sorted(result)) != tuple(sorted(FRAME_IDS)):
|
||||
raise M48SFixedClassDetectorLabError("candidate frame slice changed")
|
||||
return result
|
||||
|
||||
|
||||
def _frame_id(value: object) -> str | None:
|
||||
if not isinstance(value, str) or not value.startswith("frame-"):
|
||||
return None
|
||||
stem = Path(value).stem
|
||||
frame_id = stem.removeprefix("frame-")
|
||||
return frame_id if frame_id in FRAME_IDS else None
|
||||
|
||||
|
||||
def _artifact_manifest(root: Path) -> list[dict[str, object]]:
|
||||
artifacts: list[dict[str, object]] = []
|
||||
for path in sorted(item for item in root.rglob("*") if item.is_file()):
|
||||
relative = path.relative_to(root).as_posix()
|
||||
if relative == "manifest.json":
|
||||
continue
|
||||
media_type = "application/json"
|
||||
schema_version: str | None = None
|
||||
role = "supporting-evidence"
|
||||
if relative.endswith(".jpg"):
|
||||
media_type = "image/jpeg"
|
||||
role = "visual-evidence-camera"
|
||||
elif relative.startswith("frame-"):
|
||||
schema_version = FRAME_SCHEMA
|
||||
role = "visual-evidence-frame"
|
||||
elif relative == "catalog.json":
|
||||
schema_version = CATALOG_SCHEMA
|
||||
role = "visual-evidence-catalog"
|
||||
elif relative == "report.json":
|
||||
schema_version = REPORT_SCHEMA
|
||||
role = "laboratory-report"
|
||||
elif relative == "tournament.json":
|
||||
role = "upstream-tournament-evidence"
|
||||
elif relative == "deployment.json":
|
||||
role = "upstream-deployment-evidence"
|
||||
elif relative == "reference-graph.json":
|
||||
role = "upstream-reference-graph-evidence"
|
||||
elif relative == "reference-graph-worker-result.json":
|
||||
role = "upstream-reference-graph-worker-evidence"
|
||||
elif relative == "reference-graph-replay.json":
|
||||
role = "upstream-full-replay-evidence"
|
||||
elif relative == "reference-graph-replay-worker-result.json":
|
||||
role = "upstream-full-replay-worker-evidence"
|
||||
elif relative == "reference-graph-replay-frames.jsonl":
|
||||
media_type = "application/x-ndjson"
|
||||
schema_version = "missioncore.m48s-reference-graph-frame-evidence/v0"
|
||||
role = "visual-evidence-full-replay-world-state"
|
||||
artifacts.append(
|
||||
{
|
||||
"role": role,
|
||||
"path": relative,
|
||||
"byte_length": path.stat().st_size,
|
||||
"sha256": sha256_path(path),
|
||||
"media_type": media_type,
|
||||
"schema_version": schema_version,
|
||||
}
|
||||
)
|
||||
return artifacts
|
||||
|
||||
|
||||
def _read_object(path: Path) -> dict[str, Any]:
|
||||
try:
|
||||
value = json.loads(path.read_text("utf-8"))
|
||||
except (OSError, json.JSONDecodeError) as exc:
|
||||
raise M48SFixedClassDetectorLabError(f"invalid JSON evidence: {path.name}") from exc
|
||||
if not isinstance(value, dict):
|
||||
raise M48SFixedClassDetectorLabError(f"JSON evidence must be an object: {path.name}")
|
||||
return value
|
||||
|
||||
|
||||
def _read_jsonl(path: Path) -> list[dict[str, Any]]:
|
||||
try:
|
||||
rows = [json.loads(line) for line in path.read_text("utf-8").splitlines() if line]
|
||||
except (OSError, json.JSONDecodeError) as exc:
|
||||
raise M48SFixedClassDetectorLabError("invalid YOLOX frame evidence") from exc
|
||||
return _objects(rows, "YOLOX frame evidence")
|
||||
|
||||
|
||||
def _objects(value: list[object], label: str) -> list[dict[str, Any]]:
|
||||
if not all(isinstance(item, dict) for item in value):
|
||||
raise M48SFixedClassDetectorLabError(f"{label} must contain objects")
|
||||
return [item for item in value if isinstance(item, dict)]
|
||||
|
||||
|
||||
__all__ = [
|
||||
"CATALOG_SCHEMA",
|
||||
"FRAME_SCHEMA",
|
||||
"LAB_SCHEMA",
|
||||
"REPORT_SCHEMA",
|
||||
"RESULT_PREFIX",
|
||||
"M48SFixedClassDetectorLabError",
|
||||
"M48SFixedClassDetectorLabResult",
|
||||
"build_m48s_fixed_class_detector_lab",
|
||||
]
|
||||
@@ -125,6 +125,9 @@ from k1link.web.lidar_api import build_lidar_router
|
||||
from k1link.web.lidar_local_surface_service import K1LocalSurfaceReadService
|
||||
from k1link.web.m4_threat_replay_api import build_m4_threat_replay_router
|
||||
from k1link.web.m48_object_quality_api import build_m48_object_quality_router
|
||||
from k1link.web.m48s_fixed_class_detector_lab_api import (
|
||||
build_m48s_fixed_class_detector_lab_router,
|
||||
)
|
||||
from k1link.web.map_api import (
|
||||
MapGatewayConfiguration,
|
||||
MapGatewayProxy,
|
||||
@@ -944,6 +947,23 @@ app.include_router(
|
||||
),
|
||||
)
|
||||
)
|
||||
app.include_router(
|
||||
build_m48s_fixed_class_detector_lab_router(
|
||||
root_provider=lambda: (
|
||||
REPOSITORY_ROOT
|
||||
/ ".runtime"
|
||||
/ "compute-experiments"
|
||||
/ "m48s-semantic-shadow"
|
||||
/ "fixed-class-detector-lab-results"
|
||||
),
|
||||
repository_root_provider=lambda: REPOSITORY_ROOT,
|
||||
camera_frame_provider=(
|
||||
session_recorded_camera_frame_service.extract
|
||||
if session_recorded_camera_frame_service is not None
|
||||
else None
|
||||
),
|
||||
)
|
||||
)
|
||||
app.include_router(
|
||||
build_e47_semantic_slam_router(
|
||||
root_provider=lambda: (
|
||||
|
||||
@@ -0,0 +1,523 @@
|
||||
"""Read-only API for the sealed M4.8S fixed-class detector LAB."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import copy
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
from collections.abc import Callable
|
||||
from functools import lru_cache
|
||||
from pathlib import Path, PurePosixPath
|
||||
from typing import Any, Final
|
||||
|
||||
from fastapi import APIRouter, HTTPException, Query, Response
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
from k1link.laboratory.evidence_registry import LaboratoryEvidenceDefinition
|
||||
from k1link.laboratory.evidence_report import (
|
||||
LaboratoryEvidenceReportError,
|
||||
verify_laboratory_evidence_result,
|
||||
)
|
||||
from k1link.laboratory.m48s_fixed_class_detector_lab import (
|
||||
CATALOG_SCHEMA,
|
||||
FRAME_SCHEMA,
|
||||
INTEGRATED_STATUS,
|
||||
LAB_SCHEMA,
|
||||
RESULT_PREFIX,
|
||||
)
|
||||
from k1link.perception.fixed_class_detector_tournament import false_authority
|
||||
from k1link.perception.m48s_replay_timeline import (
|
||||
M48sReplayTimeline,
|
||||
M48sReplayTimelineError,
|
||||
)
|
||||
from k1link.perception.threat_timeline import RECORDED_SPATIAL_MAX_CHUNK_FRAMES
|
||||
from k1link.sessions import RecordedCameraFrame, SessionIntegrityError
|
||||
|
||||
RootProvider = Callable[[], Path | None]
|
||||
CameraFrameProvider = Callable[[str, int], RecordedCameraFrame]
|
||||
|
||||
RESULT_ID: Final = re.compile(rf"^{re.escape(RESULT_PREFIX)}[a-f0-9]{{64}}$")
|
||||
FRAME_ID: Final = re.compile(r"^[0-9]{6}$")
|
||||
SHA256: Final = re.compile(r"^[a-f0-9]{64}$")
|
||||
RESULT_PROJECTION_SCHEMA: Final = "missioncore.m48s-fixed-class-detector-result-view/v1"
|
||||
RESULT_CATALOG_SCHEMA: Final = "missioncore.m48s-fixed-class-detector-result-catalog/v1"
|
||||
MAX_JSON_BYTES: Final = 16 * 1024 * 1024
|
||||
MAX_FRAMES: Final = 32
|
||||
_DEFINITION: Final = LaboratoryEvidenceDefinition(
|
||||
work_id="m48s-fixed-class-detector",
|
||||
runtime_relative_root=PurePosixPath("m48s-semantic-shadow/fixed-class-detector-lab-results"),
|
||||
result_id_prefix="m48s-fixed-class-detector-lab",
|
||||
document_name="manifest.json",
|
||||
result_schema_version=LAB_SCHEMA,
|
||||
)
|
||||
|
||||
|
||||
def build_m48s_fixed_class_detector_lab_router(
|
||||
*,
|
||||
root_provider: RootProvider = lambda: None,
|
||||
repository_root_provider: RootProvider = lambda: None,
|
||||
camera_frame_provider: CameraFrameProvider | None = None,
|
||||
) -> APIRouter:
|
||||
router = APIRouter(
|
||||
prefix="/api/v1/laboratory/m48s/fixed-class-detector",
|
||||
tags=["laboratory"],
|
||||
)
|
||||
|
||||
def timeline(result_id: str) -> M48sReplayTimeline:
|
||||
candidate = _resolve_candidate(root_provider, result_id)
|
||||
repository_root = _configured_root(repository_root_provider)
|
||||
if repository_root is None:
|
||||
raise HTTPException(status_code=503, detail="M4.8S timeline source unavailable")
|
||||
try:
|
||||
return _read_timeline_cached(
|
||||
str(repository_root),
|
||||
str(candidate),
|
||||
result_id,
|
||||
_timeline_signature(candidate),
|
||||
)
|
||||
except (OSError, ValueError, M48sReplayTimelineError):
|
||||
raise HTTPException(
|
||||
status_code=503,
|
||||
detail="M4.8S bounded replay timeline failed verification",
|
||||
) from None
|
||||
|
||||
@router.get("/results")
|
||||
def list_results(limit: int = Query(default=1, ge=1, le=10)) -> dict[str, object]:
|
||||
root = _configured_root(root_provider)
|
||||
if root is None:
|
||||
return _empty_catalog(False)
|
||||
candidates = _candidates(root)
|
||||
items: list[dict[str, object]] = []
|
||||
invalid_total = 0
|
||||
for candidate in candidates:
|
||||
try:
|
||||
items.append(_project_result(candidate))
|
||||
except RuntimeError:
|
||||
invalid_total += 1
|
||||
items.sort(
|
||||
key=lambda item: (str(item["created_at_utc"]), str(item["result_id"])),
|
||||
reverse=True,
|
||||
)
|
||||
return {
|
||||
"schema_version": RESULT_CATALOG_SCHEMA,
|
||||
"configured": True,
|
||||
"items": items[:limit],
|
||||
"candidate_total": len(candidates),
|
||||
"invalid_total": invalid_total,
|
||||
"access": "read-only",
|
||||
}
|
||||
|
||||
@router.get("/{result_id}")
|
||||
def get_result(result_id: str) -> dict[str, object]:
|
||||
return _project_result(_resolve_candidate(root_provider, result_id))
|
||||
|
||||
@router.get("/{result_id}/frames/{frame_id}")
|
||||
def get_frame(result_id: str, frame_id: str) -> dict[str, object]:
|
||||
candidate, descriptor = _resolve_frame(
|
||||
root_provider,
|
||||
result_id=result_id,
|
||||
frame_id=frame_id,
|
||||
)
|
||||
path = candidate / str(descriptor["detail_path"])
|
||||
payload = _read_object(path)
|
||||
if (
|
||||
payload.get("schema_version") != FRAME_SCHEMA
|
||||
or payload.get("result_id") != result_id
|
||||
or payload.get("frame_id") != frame_id
|
||||
or descriptor.get("detail_sha256") != _sha256(path)
|
||||
or descriptor.get("detail_byte_length") != path.stat().st_size
|
||||
or not _valid_frame_payload(payload)
|
||||
):
|
||||
raise HTTPException(status_code=404, detail="M4.8S frame not found")
|
||||
return {**copy.deepcopy(payload), "access": "read-only"}
|
||||
|
||||
@router.get("/{result_id}/frames/{frame_id}/camera")
|
||||
def get_camera(result_id: str, frame_id: str) -> FileResponse:
|
||||
candidate, descriptor = _resolve_frame(
|
||||
root_provider,
|
||||
result_id=result_id,
|
||||
frame_id=frame_id,
|
||||
)
|
||||
path = (candidate / str(descriptor["camera_path"])).resolve()
|
||||
if (
|
||||
not path.is_relative_to(candidate)
|
||||
or path.is_symlink()
|
||||
or not path.is_file()
|
||||
or descriptor.get("camera_sha256") != _sha256(path)
|
||||
or descriptor.get("camera_byte_length") != path.stat().st_size
|
||||
):
|
||||
raise HTTPException(status_code=404, detail="M4.8S camera not found")
|
||||
return FileResponse(path, media_type="image/jpeg")
|
||||
|
||||
@router.get("/{result_id}/timeline")
|
||||
def get_timeline(result_id: str) -> dict[str, object]:
|
||||
return copy.deepcopy(timeline(result_id).metadata())
|
||||
|
||||
@router.get("/{result_id}/timeline/chunk")
|
||||
def get_timeline_chunk(
|
||||
result_id: str,
|
||||
start: int = Query(default=0, ge=0),
|
||||
count: int = Query(default=12, ge=1, le=RECORDED_SPATIAL_MAX_CHUNK_FRAMES),
|
||||
) -> dict[str, object]:
|
||||
try:
|
||||
return timeline(result_id).chunk(start_sequence=start, frame_count=count)
|
||||
except M48sReplayTimelineError:
|
||||
raise HTTPException(status_code=404, detail="M4.8S timeline chunk not found") from None
|
||||
|
||||
@router.get("/{result_id}/timeline/frames/{sequence}/camera")
|
||||
def get_timeline_camera(result_id: str, sequence: int) -> Response:
|
||||
if camera_frame_provider is None:
|
||||
raise HTTPException(status_code=503, detail="M4.8S camera decoder unavailable")
|
||||
projected = timeline(result_id)
|
||||
if not 0 <= sequence < len(projected.source_times_ns):
|
||||
raise HTTPException(status_code=404, detail="M4.8S timeline frame not found")
|
||||
try:
|
||||
camera = camera_frame_provider(projected.profile.session_id, sequence)
|
||||
except (OSError, SessionIntegrityError, ValueError):
|
||||
raise HTTPException(status_code=503, detail="M4.8S exact camera unavailable") from None
|
||||
if camera.width != 800 or camera.height != 600:
|
||||
raise HTTPException(status_code=503, detail="M4.8S camera size contract changed")
|
||||
return Response(
|
||||
content=camera.payload,
|
||||
media_type=camera.media_type,
|
||||
headers={
|
||||
"Cache-Control": "private, max-age=31536000, immutable",
|
||||
"ETag": f'"{camera.sha256}"',
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
},
|
||||
)
|
||||
|
||||
return router
|
||||
|
||||
|
||||
@lru_cache(maxsize=4)
|
||||
def _read_timeline_cached(
|
||||
repository_root: str,
|
||||
result_root: str,
|
||||
result_id: str,
|
||||
signature: tuple[int, ...],
|
||||
) -> M48sReplayTimeline:
|
||||
del signature
|
||||
return M48sReplayTimeline(
|
||||
repository_root=Path(repository_root),
|
||||
result_root=Path(result_root),
|
||||
result_id=result_id,
|
||||
)
|
||||
|
||||
|
||||
def _timeline_signature(candidate: Path) -> tuple[int, ...]:
|
||||
signature: list[int] = []
|
||||
for name in (
|
||||
"manifest.json",
|
||||
"reference-graph-replay-frames.jsonl",
|
||||
"reference-graph-replay-worker-result.json",
|
||||
):
|
||||
path = candidate / name
|
||||
if path.is_symlink() or not path.is_file():
|
||||
raise ValueError("M4.8S replay artifact is unavailable")
|
||||
stat = path.stat()
|
||||
signature.extend((stat.st_size, stat.st_mtime_ns))
|
||||
return tuple(signature)
|
||||
|
||||
|
||||
def _project_result(candidate: Path) -> dict[str, object]:
|
||||
loaded = _load_result(candidate)
|
||||
manifest = loaded["manifest"]
|
||||
catalog = loaded["catalog"]
|
||||
identity = manifest["identity"]
|
||||
return {
|
||||
"schema_version": RESULT_PROJECTION_SCHEMA,
|
||||
"result_id": manifest["result_id"],
|
||||
"created_at_utc": manifest["created_at_utc"],
|
||||
"status": manifest["status"],
|
||||
"bounded_question_accepted": manifest["bounded_question_accepted"],
|
||||
"ground_truth": manifest["ground_truth"],
|
||||
"source": copy.deepcopy(identity["source"]),
|
||||
"configuration": copy.deepcopy(identity["configuration"]),
|
||||
"method": copy.deepcopy(manifest["method"]),
|
||||
"metrics": copy.deepcopy(manifest["metrics"]),
|
||||
"decision": copy.deepcopy(manifest["decision"]),
|
||||
"limitations": copy.deepcopy(manifest["limitations"]),
|
||||
"authority": copy.deepcopy(manifest["authority"]),
|
||||
"frames": copy.deepcopy(catalog["frames"]),
|
||||
"access": "read-only",
|
||||
}
|
||||
|
||||
|
||||
def _load_result(candidate: Path) -> dict[str, Any]:
|
||||
try:
|
||||
signature = _candidate_signature(candidate)
|
||||
except (OSError, RuntimeError, ValueError) as exc:
|
||||
raise RuntimeError("M4.8S result signature failed") from exc
|
||||
return _load_result_cached(str(candidate), signature)
|
||||
|
||||
|
||||
@lru_cache(maxsize=8)
|
||||
def _load_result_cached(candidate_value: str, signature: tuple[int, ...]) -> dict[str, Any]:
|
||||
del signature
|
||||
return _load_result_uncached(Path(candidate_value))
|
||||
|
||||
|
||||
def _load_result_uncached(candidate: Path) -> dict[str, Any]:
|
||||
if (
|
||||
not candidate.is_dir()
|
||||
or candidate.is_symlink()
|
||||
or RESULT_ID.fullmatch(candidate.name) is None
|
||||
):
|
||||
raise RuntimeError("M4.8S result candidate is invalid")
|
||||
try:
|
||||
verify_laboratory_evidence_result(_DEFINITION, candidate)
|
||||
except LaboratoryEvidenceReportError as exc:
|
||||
raise RuntimeError("M4.8S result integrity failed") from exc
|
||||
manifest = _read_object(candidate / "manifest.json")
|
||||
identity = manifest.get("identity")
|
||||
catalog_descriptor = manifest.get("catalog")
|
||||
decision = manifest.get("decision")
|
||||
method = manifest.get("method")
|
||||
metrics = manifest.get("metrics")
|
||||
status = manifest.get("status")
|
||||
integrated = status == INTEGRATED_STATUS
|
||||
if (
|
||||
manifest.get("schema_version") != LAB_SCHEMA
|
||||
or manifest.get("result_id") != candidate.name
|
||||
or status
|
||||
not in {
|
||||
"detector-load-passed-reference-graph-shadow-only",
|
||||
INTEGRATED_STATUS,
|
||||
}
|
||||
or manifest.get("completed") is not True
|
||||
or manifest.get("bounded_question_accepted") is not True
|
||||
or manifest.get("ground_truth") is not False
|
||||
or not isinstance(manifest.get("created_at_utc"), str)
|
||||
or not isinstance(identity, dict)
|
||||
or identity.get("schema_version") != LAB_SCHEMA
|
||||
or manifest.get("identity_sha256") != hashlib.sha256(_canonical_json(identity)).hexdigest()
|
||||
or not candidate.name.endswith(str(manifest.get("identity_sha256")))
|
||||
or identity.get("authority") != false_authority()
|
||||
or manifest.get("authority") != false_authority()
|
||||
or not isinstance(decision, dict)
|
||||
or decision.get("bounded_question_accepted") is not True
|
||||
or decision.get("ready_for_reference_graph_shadow") is not True
|
||||
or decision.get("integrated_world_state_gate_evaluated") is not integrated
|
||||
or (integrated and decision.get("integrated_world_state_gate_passed") is not True)
|
||||
or (integrated and decision.get("detector_replacement_authorized") is not False)
|
||||
or decision.get("production_accepted") is not False
|
||||
or not isinstance(method, dict)
|
||||
or method.get("schema_version") != "missioncore.laboratory-method/v1"
|
||||
or method.get("completeness") != "complete"
|
||||
or not isinstance(metrics, dict)
|
||||
or (integrated and not isinstance(metrics.get("integrated_world_state"), dict))
|
||||
or not isinstance(manifest.get("limitations"), list)
|
||||
or not isinstance(catalog_descriptor, dict)
|
||||
or catalog_descriptor.get("path") != "catalog.json"
|
||||
):
|
||||
raise RuntimeError("M4.8S manifest is invalid")
|
||||
catalog_path = candidate / "catalog.json"
|
||||
if (
|
||||
catalog_descriptor.get("sha256") != _sha256(catalog_path)
|
||||
or catalog_descriptor.get("byte_length") != catalog_path.stat().st_size
|
||||
):
|
||||
raise RuntimeError("M4.8S catalog changed")
|
||||
catalog = _read_object(catalog_path)
|
||||
frames = catalog.get("frames")
|
||||
if (
|
||||
catalog.get("schema_version") != CATALOG_SCHEMA
|
||||
or catalog.get("result_id") != candidate.name
|
||||
or not isinstance(frames, list)
|
||||
or not 1 <= len(frames) <= MAX_FRAMES
|
||||
or catalog.get("frame_count") != len(frames)
|
||||
or len({item.get("frame_id") for item in frames if isinstance(item, dict)}) != len(frames)
|
||||
or any(not _valid_descriptor(item) for item in frames)
|
||||
):
|
||||
raise RuntimeError("M4.8S catalog is invalid")
|
||||
return {"manifest": manifest, "catalog": catalog}
|
||||
|
||||
|
||||
def _candidate_signature(candidate: Path) -> tuple[int, ...]:
|
||||
if not candidate.is_dir() or candidate.is_symlink():
|
||||
raise RuntimeError("M4.8S result candidate is invalid")
|
||||
manifest_path = candidate / "manifest.json"
|
||||
manifest = _read_object(manifest_path)
|
||||
artifacts = manifest.get("artifacts")
|
||||
if not isinstance(artifacts, list):
|
||||
raise RuntimeError("M4.8S artifact manifest is invalid")
|
||||
signature = [manifest_path.stat().st_size, manifest_path.stat().st_mtime_ns]
|
||||
for descriptor in artifacts:
|
||||
if not isinstance(descriptor, dict) or not isinstance(descriptor.get("path"), str):
|
||||
raise RuntimeError("M4.8S artifact descriptor is invalid")
|
||||
path = (candidate / descriptor["path"]).resolve(strict=True)
|
||||
if not path.is_relative_to(candidate) or path.is_symlink() or not path.is_file():
|
||||
raise RuntimeError("M4.8S artifact path is invalid")
|
||||
stat = path.stat()
|
||||
signature.extend((stat.st_size, stat.st_mtime_ns))
|
||||
return tuple(signature)
|
||||
|
||||
|
||||
def _resolve_candidate(root_provider: RootProvider, result_id: str) -> Path:
|
||||
if RESULT_ID.fullmatch(result_id) is None:
|
||||
raise HTTPException(status_code=404, detail="M4.8S result not found")
|
||||
root = _configured_root(root_provider)
|
||||
if root is None:
|
||||
raise HTTPException(status_code=404, detail="M4.8S result not found")
|
||||
candidate = root / result_id
|
||||
try:
|
||||
_load_result(candidate)
|
||||
except RuntimeError:
|
||||
raise HTTPException(status_code=404, detail="M4.8S result not found") from None
|
||||
return candidate
|
||||
|
||||
|
||||
def _resolve_frame(
|
||||
root_provider: RootProvider,
|
||||
*,
|
||||
result_id: str,
|
||||
frame_id: str,
|
||||
) -> tuple[Path, dict[str, Any]]:
|
||||
if FRAME_ID.fullmatch(frame_id) is None:
|
||||
raise HTTPException(status_code=404, detail="M4.8S frame not found")
|
||||
candidate = _resolve_candidate(root_provider, result_id)
|
||||
loaded = _load_result(candidate)
|
||||
try:
|
||||
descriptor = next(
|
||||
item for item in loaded["catalog"]["frames"] if item["frame_id"] == frame_id
|
||||
)
|
||||
except StopIteration:
|
||||
raise HTTPException(status_code=404, detail="M4.8S frame not found") from None
|
||||
return candidate, descriptor
|
||||
|
||||
|
||||
def _valid_descriptor(value: object) -> bool:
|
||||
if not isinstance(value, dict):
|
||||
return False
|
||||
frame_id = value.get("frame_id")
|
||||
counts = value.get("counts")
|
||||
return (
|
||||
isinstance(frame_id, str)
|
||||
and FRAME_ID.fullmatch(frame_id) is not None
|
||||
and value.get("source_sequence") == int(frame_id)
|
||||
and value.get("camera_path") == f"frames/frame-{frame_id}.jpg"
|
||||
and isinstance(value.get("camera_sha256"), str)
|
||||
and SHA256.fullmatch(value["camera_sha256"]) is not None
|
||||
and isinstance(value.get("camera_byte_length"), int)
|
||||
and value["camera_byte_length"] > 0
|
||||
and value.get("detail_path") == f"frame-{frame_id}.json"
|
||||
and isinstance(value.get("detail_sha256"), str)
|
||||
and SHA256.fullmatch(value["detail_sha256"]) is not None
|
||||
and isinstance(value.get("detail_byte_length"), int)
|
||||
and 0 < value["detail_byte_length"] <= MAX_JSON_BYTES
|
||||
and isinstance(counts, dict)
|
||||
and set(counts) == {"yolox", "dfine", "rf-detr"}
|
||||
and all(isinstance(count, int) and count >= 0 for count in counts.values())
|
||||
)
|
||||
|
||||
|
||||
def _valid_frame_payload(value: dict[str, Any]) -> bool:
|
||||
camera = value.get("camera")
|
||||
detections = value.get("detections")
|
||||
return (
|
||||
isinstance(value.get("source_sequence"), int)
|
||||
and isinstance(camera, dict)
|
||||
and camera.get("media_type") == "image/jpeg"
|
||||
and camera.get("width") == 800
|
||||
and camera.get("height") == 600
|
||||
and camera.get("exact_source_frame") is True
|
||||
and isinstance(camera.get("sha256"), str)
|
||||
and SHA256.fullmatch(camera["sha256"]) is not None
|
||||
and value.get("comparison_threshold") == 0.5
|
||||
and isinstance(detections, dict)
|
||||
and set(detections) == {"yolox", "dfine", "rf-detr"}
|
||||
and all(
|
||||
isinstance(items, list) and all(_valid_detection(item) for item in items)
|
||||
for items in detections.values()
|
||||
)
|
||||
and value.get("ground_truth_available") is False
|
||||
and value.get("authority") == false_authority()
|
||||
)
|
||||
|
||||
|
||||
def _valid_detection(value: object) -> bool:
|
||||
if not isinstance(value, dict) or set(value) != {"label", "score", "bbox_xyxy"}:
|
||||
return False
|
||||
score = value.get("score")
|
||||
bbox = value.get("bbox_xyxy")
|
||||
return (
|
||||
isinstance(value.get("label"), str)
|
||||
and isinstance(score, (int, float))
|
||||
and not isinstance(score, bool)
|
||||
and 0.5 <= float(score) <= 1.0
|
||||
and isinstance(bbox, list)
|
||||
and len(bbox) == 4
|
||||
and all(isinstance(item, (int, float)) and not isinstance(item, bool) for item in bbox)
|
||||
and 0 <= float(bbox[0]) < float(bbox[2]) <= 800
|
||||
and 0 <= float(bbox[1]) < float(bbox[3]) <= 600
|
||||
)
|
||||
|
||||
|
||||
def _configured_root(provider: RootProvider) -> Path | None:
|
||||
value = provider()
|
||||
if value is None:
|
||||
return None
|
||||
candidate = value.expanduser().absolute()
|
||||
if candidate.is_symlink():
|
||||
return None
|
||||
try:
|
||||
resolved = candidate.resolve(strict=True)
|
||||
except OSError:
|
||||
return None
|
||||
return resolved if resolved.is_dir() else None
|
||||
|
||||
|
||||
def _candidates(root: Path) -> list[Path]:
|
||||
return [
|
||||
item
|
||||
for item in root.iterdir()
|
||||
if item.is_dir() and not item.is_symlink() and RESULT_ID.fullmatch(item.name)
|
||||
]
|
||||
|
||||
|
||||
def _empty_catalog(configured: bool) -> dict[str, object]:
|
||||
return {
|
||||
"schema_version": RESULT_CATALOG_SCHEMA,
|
||||
"configured": configured,
|
||||
"items": [],
|
||||
"candidate_total": 0,
|
||||
"invalid_total": 0,
|
||||
"access": "read-only",
|
||||
}
|
||||
|
||||
|
||||
def _read_object(path: Path) -> dict[str, Any]:
|
||||
if path.is_symlink() or not path.is_file() or path.stat().st_size > MAX_JSON_BYTES:
|
||||
raise RuntimeError("M4.8S JSON artifact is invalid")
|
||||
try:
|
||||
value = json.loads(path.read_text("utf-8"))
|
||||
except (OSError, json.JSONDecodeError) as exc:
|
||||
raise RuntimeError("M4.8S JSON artifact is invalid") from exc
|
||||
if not isinstance(value, dict):
|
||||
raise RuntimeError("M4.8S JSON artifact is invalid")
|
||||
return value
|
||||
|
||||
|
||||
def _canonical_json(value: object) -> bytes:
|
||||
return json.dumps(
|
||||
value,
|
||||
ensure_ascii=False,
|
||||
sort_keys=True,
|
||||
separators=(",", ":"),
|
||||
).encode("utf-8")
|
||||
|
||||
|
||||
def _sha256(path: Path) -> str:
|
||||
digest = hashlib.sha256()
|
||||
with path.open("rb") as stream:
|
||||
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
|
||||
digest.update(chunk)
|
||||
return digest.hexdigest()
|
||||
|
||||
|
||||
__all__ = [
|
||||
"RESULT_CATALOG_SCHEMA",
|
||||
"RESULT_PROJECTION_SCHEMA",
|
||||
"build_m48s_fixed_class_detector_lab_router",
|
||||
]
|
||||
@@ -127,7 +127,7 @@ def test_product_registry_declares_every_advanced_evidence_source() -> None:
|
||||
repository_root / "config" / "laboratories"
|
||||
)
|
||||
|
||||
assert len(registry.definitions) == 36
|
||||
assert len(registry.definitions) == 37
|
||||
assert {item.work_id for item in registry.definitions} >= {
|
||||
"e31-source-binding",
|
||||
"e46j-raw-fisheye-realtime",
|
||||
@@ -141,6 +141,7 @@ def test_product_registry_declares_every_advanced_evidence_source() -> None:
|
||||
"m47-reference-graph-shadow",
|
||||
"m48-object-centric-quality",
|
||||
"m48-small-static-passage-regression",
|
||||
"m48s-fixed-class-detector",
|
||||
}
|
||||
m48 = next(
|
||||
item for item in registry.definitions
|
||||
|
||||
@@ -98,6 +98,7 @@ def test_repository_registry_classifies_every_evidence_definition() -> None:
|
||||
"e35-degradation-recovery",
|
||||
"e46j-raw-fisheye-realtime",
|
||||
"e47-semantic-slam-shadow",
|
||||
"m48s-fixed-class-detector",
|
||||
}
|
||||
by_work_id = {row.work_id: row for row in execution.definitions}
|
||||
assert by_work_id["m48-small-static-passage-regression"].evidence_contract == (
|
||||
@@ -108,10 +109,12 @@ def test_repository_registry_classifies_every_evidence_definition() -> None:
|
||||
)
|
||||
assert by_work_id["e47-semantic-slam-shadow"].lifecycle == "experimental"
|
||||
assert by_work_id["e47-semantic-slam-shadow"].isolation == "bounded-adapter"
|
||||
assert by_work_id["m48s-fixed-class-detector"].lifecycle == "experimental"
|
||||
assert by_work_id["m48s-fixed-class-detector"].isolation == "bounded-adapter"
|
||||
assert all(
|
||||
row.lifecycle == "canonical"
|
||||
for row in execution.definitions
|
||||
if row.work_id != "e47-semantic-slam-shadow"
|
||||
if row.work_id not in {"e47-semantic-slam-shadow", "m48s-fixed-class-detector"}
|
||||
)
|
||||
assert len(execution.definitions) + len(execution.legacy_work_ids) == len(
|
||||
evidence.definitions
|
||||
|
||||
@@ -80,7 +80,7 @@ def test_product_value_review_registry_covers_reviewed_laboratory_families() ->
|
||||
root / "config" / "laboratory-value-review.json"
|
||||
)
|
||||
|
||||
assert len(registry.entries) == 35
|
||||
assert len(registry.entries) == 36
|
||||
assert {entry.catalog_id for entry in registry.entries} >= {
|
||||
"e28-local-surface",
|
||||
"e46d-temporal-failure-audit",
|
||||
@@ -88,4 +88,5 @@ def test_product_value_review_registry_covers_reviewed_laboratory_families() ->
|
||||
"l31-pointpillars-ravnoves",
|
||||
"l34f-adjudicated-reference",
|
||||
"m4-replay-threat",
|
||||
"m48s-fixed-class-detector",
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from k1link.laboratory import LaboratoryEvidenceRegistry
|
||||
from k1link.laboratory.evidence_report import verify_laboratory_evidence_result
|
||||
from k1link.laboratory.m48s_fixed_class_detector_lab import (
|
||||
FRAME_IDS,
|
||||
LAB_SCHEMA,
|
||||
M48SFixedClassDetectorLabError,
|
||||
build_m48s_fixed_class_detector_lab,
|
||||
)
|
||||
|
||||
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def test_m48s_lab_seals_visual_comparison_and_load_evidence(tmp_path: Path) -> None:
|
||||
result = build_m48s_fixed_class_detector_lab(
|
||||
repository_root=REPOSITORY_ROOT,
|
||||
output_root=tmp_path / "results",
|
||||
)
|
||||
manifest = result.manifest
|
||||
identity = manifest["identity"]
|
||||
identity_digest = hashlib.sha256(
|
||||
json.dumps(
|
||||
identity,
|
||||
ensure_ascii=False,
|
||||
sort_keys=True,
|
||||
separators=(",", ":"),
|
||||
).encode("utf-8")
|
||||
).hexdigest()
|
||||
|
||||
assert manifest["schema_version"] == LAB_SCHEMA
|
||||
assert manifest["result_id"] == result.result_id
|
||||
assert result.result_id.endswith(identity_digest)
|
||||
assert manifest["identity_sha256"] == identity_digest
|
||||
assert len(manifest["artifacts"]) == 31
|
||||
assert manifest["method"]["completeness"] == "complete"
|
||||
assert manifest["bounded_question_accepted"] is True
|
||||
assert manifest["ground_truth"] is False
|
||||
assert manifest["decision"] == {
|
||||
"bounded_question_accepted": True,
|
||||
"selected_candidate": "rf-detr",
|
||||
"ready_for_reference_graph_shadow": True,
|
||||
"integrated_world_state_gate_evaluated": True,
|
||||
"integrated_world_state_gate_passed": True,
|
||||
"full_replay_visual_published": True,
|
||||
"detector_replacement_authorized": False,
|
||||
"production_accepted": False,
|
||||
}
|
||||
assert manifest["authority"]["navigation_or_safety_accepted"] is False
|
||||
assert manifest["metrics"]["detector_load"]["source_frames_consumed"] == 18_008
|
||||
assert manifest["metrics"]["detector_load"]["source_frame_replacements"] == 0
|
||||
assert manifest["metrics"]["detector_load"]["effective_consumed_fps"] >= 9.5
|
||||
assert manifest["metrics"]["detector_load"]["completion_age_p95_ms"] <= 175.0
|
||||
integrated = manifest["metrics"]["integrated_world_state"]
|
||||
assert integrated["source_frames_admitted"] == 4_489
|
||||
assert integrated["delivered_world_states"] == 4_481
|
||||
assert integrated["superseded_frames"] == 8
|
||||
assert integrated["effective_world_state_fps"] >= 9.5
|
||||
assert integrated["world_state_completion_age_p95_ms"] <= 175.0
|
||||
assert max(integrated["queue_high_watermarks"].values()) <= 2
|
||||
assert integrated["additional_inference_passes"] == 0
|
||||
assert integrated["failures"] == 0
|
||||
|
||||
catalog = json.loads((result.result_root / "catalog.json").read_text("utf-8"))
|
||||
assert catalog["frame_count"] == len(FRAME_IDS) == 11
|
||||
frame = json.loads((result.result_root / "frame-000253.json").read_text("utf-8"))
|
||||
assert not any(item["label"] == "dog" for item in frame["detections"]["yolox"])
|
||||
assert not any(item["label"] == "dog" for item in frame["detections"]["dfine"])
|
||||
assert any(item["label"] == "skateboard" for item in frame["detections"]["dfine"])
|
||||
assert [item["score"] for item in frame["detections"]["rf-detr"] if item["label"] == "dog"] == [
|
||||
0.741674
|
||||
]
|
||||
|
||||
registry = LaboratoryEvidenceRegistry.from_directory(
|
||||
REPOSITORY_ROOT / "config" / "laboratories"
|
||||
)
|
||||
definition = next(
|
||||
item for item in registry.definitions if item.work_id == "m48s-fixed-class-detector"
|
||||
)
|
||||
proof = verify_laboratory_evidence_result(definition, result.result_root)
|
||||
assert proof["result_id"] == result.result_id
|
||||
assert proof["artifact_count"] == 31
|
||||
|
||||
with pytest.raises(M48SFixedClassDetectorLabError, match="already exists"):
|
||||
build_m48s_fixed_class_detector_lab(
|
||||
repository_root=REPOSITORY_ROOT,
|
||||
output_root=tmp_path / "results",
|
||||
)
|
||||
@@ -0,0 +1,109 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from k1link.laboratory.m48s_fixed_class_detector_lab import (
|
||||
build_m48s_fixed_class_detector_lab,
|
||||
)
|
||||
from k1link.web.m48s_fixed_class_detector_lab_api import (
|
||||
build_m48s_fixed_class_detector_lab_router,
|
||||
)
|
||||
|
||||
REPOSITORY_ROOT = Path(__file__).resolve().parents[1]
|
||||
|
||||
|
||||
def _fixture(tmp_path: Path) -> tuple[TestClient, Path, str]:
|
||||
root = tmp_path / "results"
|
||||
result = build_m48s_fixed_class_detector_lab(
|
||||
repository_root=REPOSITORY_ROOT,
|
||||
output_root=root,
|
||||
)
|
||||
app = FastAPI()
|
||||
app.include_router(
|
||||
build_m48s_fixed_class_detector_lab_router(
|
||||
root_provider=lambda: root,
|
||||
repository_root_provider=lambda: REPOSITORY_ROOT,
|
||||
)
|
||||
)
|
||||
return TestClient(app), result.result_root, result.result_id
|
||||
|
||||
|
||||
def test_m48s_lab_api_projects_verified_result_frame_and_camera(tmp_path: Path) -> None:
|
||||
client, result_root, result_id = _fixture(tmp_path)
|
||||
|
||||
catalog = client.get("/api/v1/laboratory/m48s/fixed-class-detector/results")
|
||||
assert catalog.status_code == 200
|
||||
assert catalog.json()["items"][0]["result_id"] == result_id
|
||||
assert catalog.json()["invalid_total"] == 0
|
||||
|
||||
result = client.get(f"/api/v1/laboratory/m48s/fixed-class-detector/{result_id}")
|
||||
assert result.status_code == 200
|
||||
assert result.json()["decision"]["selected_candidate"] == "rf-detr"
|
||||
assert result.json()["decision"]["integrated_world_state_gate_passed"] is True
|
||||
assert (
|
||||
result.json()["metrics"]["integrated_world_state"]["world_state_completion_age_p95_ms"]
|
||||
== 74.733648
|
||||
)
|
||||
assert result.json()["ground_truth"] is False
|
||||
assert len(result.json()["frames"]) == 11
|
||||
|
||||
timeline = client.get(
|
||||
f"/api/v1/laboratory/m48s/fixed-class-detector/{result_id}/timeline"
|
||||
)
|
||||
assert timeline.status_code == 200
|
||||
assert timeline.json()["frame_count"] == 4_489
|
||||
assert timeline.json()["world_state_frame_count"] == 4_481
|
||||
assert timeline.json()["superseded_frame_count"] == 8
|
||||
assert (
|
||||
timeline.json()["camera_point_delivery"]
|
||||
== "factory-kb4-projected-current-increment"
|
||||
)
|
||||
chunk = client.get(
|
||||
f"/api/v1/laboratory/m48s/fixed-class-detector/{result_id}/timeline/chunk",
|
||||
params={"start": 253, "count": 1},
|
||||
)
|
||||
assert chunk.status_code == 200
|
||||
replay_frame = chunk.json()["frames"][0]
|
||||
assert replay_frame["world_state_available"] is True
|
||||
assert replay_frame["camera_projection"] == "factory-kb4-exact"
|
||||
assert replay_frame["camera_projected_sample_count"] > 0
|
||||
assert any(
|
||||
item["semantic_hint"] == "dog" for item in replay_frame["camera_proposals"]
|
||||
)
|
||||
|
||||
frame = client.get(f"/api/v1/laboratory/m48s/fixed-class-detector/{result_id}/frames/000253")
|
||||
assert frame.status_code == 200
|
||||
assert any(item["label"] == "dog" for item in frame.json()["detections"]["rf-detr"])
|
||||
assert frame.json()["access"] == "read-only"
|
||||
|
||||
camera = client.get(
|
||||
f"/api/v1/laboratory/m48s/fixed-class-detector/{result_id}/frames/000253/camera"
|
||||
)
|
||||
assert camera.status_code == 200
|
||||
assert camera.headers["content-type"] == "image/jpeg"
|
||||
assert camera.content == (result_root / "frames/frame-000253.jpg").read_bytes()
|
||||
|
||||
assert (
|
||||
client.get(
|
||||
f"/api/v1/laboratory/m48s/fixed-class-detector/{result_id}/frames/not-a-frame"
|
||||
).status_code
|
||||
== 404
|
||||
)
|
||||
assert (
|
||||
client.get("/api/v1/laboratory/m48s/fixed-class-detector/not-a-result").status_code == 404
|
||||
)
|
||||
|
||||
|
||||
def test_m48s_lab_api_fails_closed_after_artifact_tamper(tmp_path: Path) -> None:
|
||||
client, result_root, result_id = _fixture(tmp_path)
|
||||
(result_root / "frame-000253.json").write_text("{}\n", encoding="utf-8")
|
||||
|
||||
response = client.get(f"/api/v1/laboratory/m48s/fixed-class-detector/{result_id}")
|
||||
assert response.status_code == 404
|
||||
|
||||
catalog = client.get("/api/v1/laboratory/m48s/fixed-class-detector/results")
|
||||
assert catalog.json()["items"] == []
|
||||
assert catalog.json()["invalid_total"] == 1
|
||||
Reference in New Issue
Block a user