refactor(perception): decouple replay from lab layout
This commit is contained in:
@@ -14,6 +14,7 @@ from .baseline import (
|
||||
load_m4_baseline,
|
||||
validate_reuse_inventory,
|
||||
verify_m4_baseline,
|
||||
verify_m4_execution_source,
|
||||
)
|
||||
from .contracts import (
|
||||
LOCAL_OBSTACLE_MAP_SCHEMA,
|
||||
@@ -120,6 +121,7 @@ __all__ = [
|
||||
"BaselineVerification",
|
||||
"load_m4_baseline",
|
||||
"validate_reuse_inventory",
|
||||
"verify_m4_execution_source",
|
||||
"verify_m4_baseline",
|
||||
"PerceptionAdapterError",
|
||||
"observations_from_track_geometry",
|
||||
|
||||
@@ -24,15 +24,27 @@ BASELINE_RECORDED_JOB_ID: Final = "recorded-camera-602ac89026ed12978619801d"
|
||||
BASELINE_CAMERA_STREAM_SHA256: Final = (
|
||||
"cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8"
|
||||
)
|
||||
BASELINE_CAMERA_SUMMARY_SHA256: Final = (
|
||||
"b280f40b198aad5d5335819107fb1405ad65d5695d187c61a2c027ad853a3181"
|
||||
)
|
||||
BASELINE_CAMERA_INDEX_SHA256: Final = (
|
||||
"e029815a60ad9fbfedb6169142c7449df2b119a51d1ce001f08806e04eb0be14"
|
||||
)
|
||||
BASELINE_SOURCE_PACK_ID: Final = (
|
||||
"e10-lidar-pack-576c994a6c814e2592dd6240ace3902a5db94843312c759a73ba0c9166157d2b"
|
||||
)
|
||||
BASELINE_SOURCE_PACK_MANIFEST_SHA256: Final = (
|
||||
"7e4af82eb7dfd47e242dc5207d55456cd0a5ab4c6780c584d4685ef854a73bf5"
|
||||
)
|
||||
BASELINE_SOURCE_PACK_SHA256: Final = (
|
||||
"0685d24219d8236caf8b7f1685e93f6d6b59e7fd015a768d88a92bbe8b154944"
|
||||
)
|
||||
BASELINE_PREPROCESS_PROFILE_SHA256: Final = (
|
||||
"19c17dbc23f2b1c539eb6b8214e69fa487ea3fec8db9d88768dffc714895fb88"
|
||||
)
|
||||
BASELINE_FILE_SHA256: Final = (
|
||||
"ea10359339e6cce31b5780a2710299771cab7cc0c1c2a2b56a1621f786b31fa8"
|
||||
)
|
||||
|
||||
_SHA256 = re.compile(r"^[a-f0-9]{64}$")
|
||||
_EXPECTED_EVIDENCE_ROLES: Final = {
|
||||
@@ -232,6 +244,8 @@ def load_m4_baseline(path: Path) -> BaselineProfile:
|
||||
rollback = _object(document.get("rollback"), "rollback")
|
||||
if rollback != _EXPECTED_ROLLBACK:
|
||||
raise BaselineContractError("rollback E15 identity changed")
|
||||
if _file_sha256(path) != BASELINE_FILE_SHA256:
|
||||
raise BaselineContractError("baseline file bytes changed")
|
||||
|
||||
return BaselineProfile(path=path, document=document, evidence=evidence)
|
||||
|
||||
@@ -240,7 +254,15 @@ def verify_m4_baseline(repository_root: Path, profile: BaselineProfile) -> Basel
|
||||
"""Resolve every immutable evidence document and verify its exact digest."""
|
||||
|
||||
root = repository_root.resolve()
|
||||
verified = list(_verify_source_artifacts(root, profile))
|
||||
source_paths = _repository_source_paths(root, profile)
|
||||
verify_m4_execution_source(
|
||||
profile,
|
||||
camera_summary_path=source_paths[0],
|
||||
camera_index_path=source_paths[1],
|
||||
source_pack_manifest_path=source_paths[2],
|
||||
source_pack_path=source_paths[3],
|
||||
)
|
||||
verified = [str(path.relative_to(root)) for path in source_paths]
|
||||
for item in profile.evidence:
|
||||
evidence_path = (root / item.relative_path).resolve()
|
||||
if root not in evidence_path.parents:
|
||||
@@ -270,22 +292,34 @@ def verify_m4_baseline(repository_root: Path, profile: BaselineProfile) -> Basel
|
||||
)
|
||||
|
||||
|
||||
def _verify_source_artifacts(root: Path, profile: BaselineProfile) -> tuple[str, ...]:
|
||||
def verify_m4_execution_source(
|
||||
profile: BaselineProfile,
|
||||
*,
|
||||
camera_summary_path: Path,
|
||||
camera_index_path: Path,
|
||||
source_pack_manifest_path: Path,
|
||||
source_pack_path: Path,
|
||||
) -> tuple[Path, ...]:
|
||||
"""Verify only immutable source inputs required by a deployed replay runner."""
|
||||
|
||||
source = _object(profile.document.get("source"), "source")
|
||||
recorded_job_id = _string(source.get("recorded_job_id"), "recorded job id")
|
||||
camera_source_id = _string(source.get("camera_source_id"), "camera source id")
|
||||
source_pack_id = _string(source.get("source_pack_id"), "source pack id")
|
||||
expected_frames = _integer(source.get("frame_count"), "source frame count")
|
||||
camera_root = (
|
||||
root
|
||||
/ ".runtime/compute-jobs"
|
||||
/ recorded_job_id
|
||||
/ "input/camera"
|
||||
/ camera_source_id
|
||||
/ "epoch-1"
|
||||
source_inputs = (
|
||||
camera_summary_path,
|
||||
camera_index_path,
|
||||
source_pack_manifest_path,
|
||||
source_pack_path,
|
||||
)
|
||||
summary_path = camera_root / "summary.json"
|
||||
index_path = camera_root / "index.jsonl"
|
||||
if any(path.is_symlink() or not path.is_file() for path in source_inputs):
|
||||
raise BaselineContractError("execution source inputs must be regular non-symlink files")
|
||||
paths = tuple(path.resolve(strict=True) for path in source_inputs)
|
||||
summary_path, index_path, pack_manifest_path, pack_artifact_path = paths
|
||||
if _file_sha256(summary_path) != BASELINE_CAMERA_SUMMARY_SHA256:
|
||||
raise BaselineContractError("camera recording summary digest changed")
|
||||
if _file_sha256(index_path) != BASELINE_CAMERA_INDEX_SHA256:
|
||||
raise BaselineContractError("camera recording index digest changed")
|
||||
summary = _read_object(summary_path)
|
||||
if summary.get("schema_version") != "missioncore.camera-recording/v1":
|
||||
raise BaselineContractError("camera recording summary schema changed")
|
||||
@@ -300,9 +334,8 @@ def _verify_source_artifacts(root: Path, profile: BaselineProfile) -> tuple[str,
|
||||
if _file_sha256(index_path) != summary.get("index_sha256"):
|
||||
raise BaselineContractError("camera recording index digest changed")
|
||||
|
||||
pack_root = root / ".runtime/compute-experiments/e10/lidar-packs" / source_pack_id
|
||||
pack_manifest_path = pack_root / "manifest.json"
|
||||
pack_artifact_path = pack_root / "lidar-pack.npz"
|
||||
if _file_sha256(pack_manifest_path) != BASELINE_SOURCE_PACK_MANIFEST_SHA256:
|
||||
raise BaselineContractError("source pack manifest file digest changed")
|
||||
pack_manifest = _read_object(pack_manifest_path)
|
||||
if pack_manifest.get("schema_version") != "missioncore.e10-lidar-replay-pack/v1":
|
||||
raise BaselineContractError("source pack schema changed")
|
||||
@@ -320,9 +353,28 @@ def _verify_source_artifacts(root: Path, profile: BaselineProfile) -> tuple[str,
|
||||
raise BaselineContractError("source pack manifest digest changed")
|
||||
if _file_sha256(pack_artifact_path) != expected_pack_sha:
|
||||
raise BaselineContractError("source pack artifact digest changed")
|
||||
return tuple(
|
||||
str(path.relative_to(root))
|
||||
for path in (summary_path, index_path, pack_manifest_path, pack_artifact_path)
|
||||
return paths
|
||||
|
||||
|
||||
def _repository_source_paths(root: Path, profile: BaselineProfile) -> tuple[Path, ...]:
|
||||
source = _object(profile.document.get("source"), "source")
|
||||
recorded_job_id = _string(source.get("recorded_job_id"), "recorded job id")
|
||||
camera_source_id = _string(source.get("camera_source_id"), "camera source id")
|
||||
source_pack_id = _string(source.get("source_pack_id"), "source pack id")
|
||||
camera_root = (
|
||||
root
|
||||
/ ".runtime/compute-jobs"
|
||||
/ recorded_job_id
|
||||
/ "input/camera"
|
||||
/ camera_source_id
|
||||
/ "epoch-1"
|
||||
)
|
||||
pack_root = root / ".runtime/compute-experiments/e10/lidar-packs" / source_pack_id
|
||||
return (
|
||||
camera_root / "summary.json",
|
||||
camera_root / "index.jsonl",
|
||||
pack_root / "manifest.json",
|
||||
pack_root / "lidar-pack.npz",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ from k1link.compute.yolox_object_detector import (
|
||||
load_valid_fov_mask,
|
||||
)
|
||||
|
||||
from .baseline import load_m4_baseline, verify_m4_baseline
|
||||
from .baseline import load_m4_baseline, verify_m4_execution_source
|
||||
from .detector import FrozenYoloxDetectorProvider
|
||||
from .detector_replay import run_detector_replay
|
||||
from .detector_replay_result import (
|
||||
@@ -34,7 +34,11 @@ def _arguments() -> argparse.Namespace:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Run the product-neutral M4 detector replay/capacity gate."
|
||||
)
|
||||
parser.add_argument("--repository-root", type=Path, required=True)
|
||||
parser.add_argument("--baseline", type=Path, required=True)
|
||||
parser.add_argument("--camera-summary", type=Path, required=True)
|
||||
parser.add_argument("--camera-index", type=Path, required=True)
|
||||
parser.add_argument("--source-pack-manifest", type=Path, required=True)
|
||||
parser.add_argument("--source-pack", type=Path, required=True)
|
||||
parser.add_argument("--video", type=Path, required=True)
|
||||
parser.add_argument("--valid-fov-mask", type=Path, required=True)
|
||||
parser.add_argument("--triton-origin", required=True, type=_loopback_triton_origin)
|
||||
@@ -59,16 +63,21 @@ def _loopback_triton_origin(value: str) -> str:
|
||||
|
||||
def main() -> int:
|
||||
args = _arguments()
|
||||
repository_root = args.repository_root.resolve(strict=True)
|
||||
baseline = load_m4_baseline(
|
||||
repository_root / "config/perception/m4-recorded-realtime-baseline-v1.json"
|
||||
baseline = load_m4_baseline(args.baseline)
|
||||
source_paths = verify_m4_execution_source(
|
||||
baseline,
|
||||
camera_summary_path=args.camera_summary,
|
||||
camera_index_path=args.camera_index,
|
||||
source_pack_manifest_path=args.source_pack_manifest,
|
||||
source_pack_path=args.source_pack,
|
||||
)
|
||||
verify_m4_baseline(repository_root, baseline)
|
||||
_, camera_index_path, _, source_pack_path = source_paths
|
||||
runtime_value = json.loads(args.runtime_identity.resolve(strict=True).read_text("utf-8"))
|
||||
runtime = DetectorRuntimeIdentity.from_dict(runtime_value)
|
||||
source = DecodedRecordedSource(
|
||||
source=RecordedRavnoves00Source.from_repository(
|
||||
repository_root,
|
||||
source=RecordedRavnoves00Source(
|
||||
camera_index_path=camera_index_path,
|
||||
source_pack_path=source_pack_path,
|
||||
pacing=ReplayPacing.UNCAPPED,
|
||||
),
|
||||
decoder=PyAvRecordedImageDecoder(args.video),
|
||||
|
||||
@@ -20,6 +20,7 @@ from k1link.compute.yolox_object_detector import (
|
||||
|
||||
from .baseline import (
|
||||
BASELINE_CAMERA_STREAM_SHA256,
|
||||
BASELINE_FILE_SHA256,
|
||||
BASELINE_PREPROCESS_PROFILE_SHA256,
|
||||
BASELINE_PROFILE_ID,
|
||||
BASELINE_RECORDED_JOB_ID,
|
||||
@@ -83,6 +84,7 @@ def seal_detector_replay_result(
|
||||
identity = {
|
||||
"schema_version": DETECTOR_REPLAY_RESULT_SCHEMA,
|
||||
"baseline_profile_id": BASELINE_PROFILE_ID,
|
||||
"baseline_profile_sha256": BASELINE_FILE_SHA256,
|
||||
"source": {
|
||||
"provider_id": RECORDED_SOURCE_PROVIDER_ID,
|
||||
"source_id": BASELINE_SOURCE_ID,
|
||||
|
||||
@@ -16,6 +16,7 @@ from k1link.compute.yolox_object_detector import (
|
||||
|
||||
from .baseline import (
|
||||
BASELINE_CAMERA_STREAM_SHA256,
|
||||
BASELINE_FILE_SHA256,
|
||||
BASELINE_PREPROCESS_PROFILE_SHA256,
|
||||
BASELINE_PROFILE_ID,
|
||||
BASELINE_RECORDED_JOB_ID,
|
||||
@@ -179,6 +180,7 @@ def _validate_identity(identity: dict[str, object]) -> None:
|
||||
{
|
||||
"schema_version",
|
||||
"baseline_profile_id",
|
||||
"baseline_profile_sha256",
|
||||
"source",
|
||||
"detector",
|
||||
"runtime",
|
||||
@@ -196,6 +198,7 @@ def _validate_identity(identity: dict[str, object]) -> None:
|
||||
if (
|
||||
identity.get("schema_version") != DETECTOR_REPLAY_RESULT_SCHEMA
|
||||
or identity.get("baseline_profile_id") != BASELINE_PROFILE_ID
|
||||
or identity.get("baseline_profile_sha256") != BASELINE_FILE_SHA256
|
||||
or source
|
||||
!= {
|
||||
"provider_id": RECORDED_SOURCE_PROVIDER_ID,
|
||||
|
||||
Reference in New Issue
Block a user