feat(perception): run PointPillars on RAVNOVES00

This commit is contained in:
DCCONSTRUCTIONS
2026-07-31 14:35:34 +03:00
parent b44c4b3265
commit 55b7821a5c
11 changed files with 1760 additions and 9 deletions
@@ -0,0 +1,777 @@
#!/usr/bin/env python3
"""Run PointPillars sequentially over the sealed RAVNOVES00 K1 replay."""
from __future__ import annotations
import argparse
import hashlib
import json
import math
import os
import tempfile
import time
import urllib.request
from datetime import UTC, datetime
from pathlib import Path
from typing import Any
import numpy as np
from k1link.compute.l31_pointpillars_ravnoves import (
nearest_pose_indices,
select_visual_frame_indices,
sensor_frame_xyzi,
)
from k1link.compute.pointpillars_postprocess import (
POINTPILLARS_EMBEDDED_SCORE_THRESHOLD,
POINTPILLARS_MODEL_POINT_CLOUD_RANGE,
PointPillarsBox,
decode_pointpillars_output,
)
RUN_SCHEMA = "missioncore.l31-pointpillars-ravnoves/v1"
FRAME_SCHEMA = "missioncore.l31-pointpillars-ravnoves-frame/v1"
CATALOG_SCHEMA = "missioncore.l31-pointpillars-ravnoves-catalog/v1"
VISUAL_FRAME_SCHEMA = "missioncore.l31-pointpillars-ravnoves-visual-frame/v1"
WORKER_PACKAGE_SCHEMA = "missioncore.l3-pointpillars-worker-package/v1"
REPLAY_SCHEMA = "missioncore.lidar-replay-pack/v2"
EXPECTED_PACK_ID = (
"lidar-replay-pack-"
"8fc0fb418578b8ee2ac88d502d2acbc63ae533437a9da14f1a9f9d8916f613ce"
)
EXPECTED_REPLAY_SHA256 = (
"cbb75341ca0d82aea6e59bec26636d06aa58d9c300e6fb5555033ab35b2bed7e"
)
EXPECTED_SESSION_ID = "20260720T065719Z_viewer_live"
EXPECTED_MODEL_SHA256 = (
"2dcabddc3a365e9608a112d7bbbb7db769a6dddeeaa59aa03611a83113326da1"
)
EXPECTED_ENGINE_SHA256 = (
"12005d972a4632d56342a5da44442b632c1dcc5144fa3c70b162dec334532481"
)
MODEL_NAME = "pointpillars"
MAXIMUM_POINTS = 204_800
MAXIMUM_VISUAL_BOXES = 512
MAXIMUM_VISUAL_FRAMES = 18
def main() -> int:
parser = argparse.ArgumentParser()
parser.add_argument("--replay-pack", type=Path, required=True)
parser.add_argument("--worker-package", type=Path, required=True)
parser.add_argument("--triton-url", default="http://127.0.0.1:8000")
parser.add_argument(
"--output-root",
type=Path,
default=Path(
"/mnt/d/NDC_MISSIONCORE/runtime/experiments/l3/"
"pointpillars-ravnoves"
),
)
args = parser.parse_args()
replay_root, replay_manifest, arrays = _read_replay(args.replay_pack)
package = _read_worker_package(args.worker_package)
_require_triton_ready(args.triton_url)
point_times = arrays["point_received_monotonic_ns"]
pose_indices, pose_age_ms = nearest_pose_indices(
point_times,
arrays["pose_received_monotonic_ns"],
)
if float(np.max(pose_age_ms)) > 100.0:
raise RuntimeError("RAVNOVES00 contains a pose binding older than 100 ms")
identity = {
"schema_version": RUN_SCHEMA,
"source_pack_id": replay_manifest["pack_id"],
"source_pack_identity_sha256": replay_manifest["identity_sha256"],
"source_logical_content_sha256": replay_manifest["identity"][
"logical_content_sha256"
],
"source_session_id": EXPECTED_SESSION_ID,
"point_frame_count": int(point_times.size),
"model": {
"name": MODEL_NAME,
"source_model_sha256": EXPECTED_MODEL_SHA256,
"engine_sha256": EXPECTED_ENGINE_SHA256,
"embedded_score_threshold": POINTPILLARS_EMBEDDED_SCORE_THRESHOLD,
"point_cloud_range": list(POINTPILLARS_MODEL_POINT_CLOUD_RANGE),
},
"worker_package_id": package["package_id"],
"worker_package_identity_sha256": package["identity_sha256"],
"producer_sha256": _sha256(Path(__file__)),
"execution": {
"worker_host_id": "worker-006",
"sequential": True,
"parallel_workers": 1,
"source_paced": False,
"existing_triton_only": True,
},
"authority": {
"shadow_only": True,
"commands_enabled": False,
"navigation_or_safety_accepted": False,
"accuracy_accepted": False,
},
}
identity_sha256 = hashlib.sha256(_canonical_json(identity)).hexdigest()
result_id = f"l31-pointpillars-ravnoves-{identity_sha256}"
result_root = args.output_root.expanduser().absolute() / result_id
summaries_root = result_root / "frame-results"
visual_root = result_root / "visual-frames"
summaries_root.mkdir(mode=0o700, parents=True, exist_ok=True)
visual_root.mkdir(mode=0o700, parents=True, exist_ok=True)
_write_once(result_root / "identity.json", identity)
point_offsets = arrays["point_offsets"]
first_time_ns = int(point_times[0])
summaries: list[dict[str, Any]] = []
for completed, frame_index in enumerate(range(point_times.size), start=1):
xyzi = _frame_xyzi(arrays, point_offsets, pose_indices, frame_index)
input_sha256 = hashlib.sha256(
np.ascontiguousarray(xyzi, dtype=np.float32).tobytes()
).hexdigest()
frame_path = summaries_root / f"{frame_index:06d}.json"
if frame_path.exists():
summary = _read_frame_summary(
frame_path,
frame_index=frame_index,
input_sha256=input_sha256,
)
else:
boxes, elapsed_ms = _run_frame(args.triton_url, xyzi)
summary = _frame_summary(
frame_index=frame_index,
session_seconds=(int(point_times[frame_index]) - first_time_ns)
/ 1_000_000_000.0,
source_point_count=int(xyzi.shape[0]),
pose_index=int(pose_indices[frame_index]),
pose_binding_age_ms=float(pose_age_ms[frame_index]),
input_sha256=input_sha256,
inference_ms=elapsed_ms,
boxes=boxes,
)
_atomic_json(frame_path, summary)
summaries.append(summary)
if completed == 1 or completed % 100 == 0:
print(
json.dumps(
{
"result_id": result_id,
"completed": completed,
"total": int(point_times.size),
"frame_index": frame_index,
},
sort_keys=True,
),
flush=True,
)
selected_indices = select_visual_frame_indices(
[int(row["class_counts"]["Vehicle"]) for row in summaries],
[int(row["prediction_count"]) for row in summaries],
maximum_frames=MAXIMUM_VISUAL_FRAMES,
)
visual_descriptors: list[dict[str, Any]] = []
deterministic_matches = 0
for frame_index in selected_indices:
xyzi = _frame_xyzi(arrays, point_offsets, pose_indices, frame_index)
boxes, elapsed_ms = _run_frame(args.triton_url, xyzi)
observed_sha256 = _boxes_sha256(boxes)
if observed_sha256 == summaries[frame_index]["boxes_sha256"]:
deterministic_matches += 1
visual_payload = _visual_frame(
summary=summaries[frame_index],
xyzi=xyzi,
boxes=boxes,
replay_inference_ms=elapsed_ms,
deterministic_replay=(
observed_sha256 == summaries[frame_index]["boxes_sha256"]
),
)
visual_path = visual_root / f"{frame_index:06d}.json"
_atomic_json(visual_path, visual_payload)
descriptor = _artifact(
visual_path,
"visual-frame",
relative_to=result_root,
)
visual_descriptors.append(
{
"frame_id": f"{frame_index:06d}",
"frame_index": frame_index,
"session_seconds": summaries[frame_index]["session_seconds"],
"source_point_count": summaries[frame_index]["source_point_count"],
"prediction_count": summaries[frame_index]["prediction_count"],
"class_counts": summaries[frame_index]["class_counts"],
"inference_ms": summaries[frame_index]["inference_ms"],
"detail_path": descriptor["path"],
"detail_sha256": descriptor["sha256"],
"detail_byte_length": descriptor["byte_length"],
}
)
metrics = _metrics(
summaries,
deterministic_matches=deterministic_matches,
deterministic_total=len(selected_indices),
)
catalog = {
"schema_version": CATALOG_SCHEMA,
"result_id": result_id,
"source_session_id": EXPECTED_SESSION_ID,
"selection": {
"strategy": (
"18 equal route-time bins; prefer Vehicle count, then total "
"prediction count, then bin-centre proximity"
),
"maximum_frames": MAXIMUM_VISUAL_FRAMES,
"maximum_boxes_per_frame": MAXIMUM_VISUAL_BOXES,
},
"frame_count": len(visual_descriptors),
"frames": visual_descriptors,
}
_atomic_json(result_root / "catalog.json", catalog)
manifest = {
"schema_version": RUN_SCHEMA,
"result_id": result_id,
"identity_sha256": identity_sha256,
"identity": identity,
"created_at_utc": datetime.now(UTC)
.isoformat(timespec="milliseconds")
.replace("+00:00", "Z"),
"status": "k1-cross-domain-transfer-measured-visual-review-required",
"metrics": metrics,
"catalog": _artifact(
result_root / "catalog.json",
"visual-frame-catalog",
relative_to=result_root,
),
"frame_results": {
"count": len(summaries),
"identity_sha256": _directory_identity(summaries_root),
},
"limitations": [
"RAVNOVES00 has no independent oriented 3D cuboid ground truth.",
"Predicted boxes are model hypotheses, not TP/FP/FN or accepted objects.",
"The K1 source is a vendor map increment without ring, firing time, "
"scan geometry, or IMU.",
"Execution is offline sequential and does not establish source-paced "
"drop or deadline behaviour.",
"The model was trained on another proprietary solid-state LiDAR domain.",
],
"authority": identity["authority"],
}
_atomic_json(result_root / "manifest.json", manifest)
print(
json.dumps(
{
"result_id": result_id,
"frame_count": len(summaries),
"frames_with_predictions": metrics["frames_with_predictions"],
"vehicle_predictions": metrics["class_counts"]["Vehicle"],
"inference_p95_ms": metrics["inference_latency_ms"]["p95"],
"deterministic_replay_fraction": metrics[
"deterministic_replay_fraction"
],
},
sort_keys=True,
),
flush=True,
)
del replay_root
return 0
def _read_replay(
path: Path,
) -> tuple[Path, dict[str, Any], dict[str, np.ndarray]]:
root = path.expanduser().resolve(strict=True)
manifest = _read_json(root / "manifest.json")
identity = manifest.get("identity")
artifacts = manifest.get("artifacts")
if (
not root.is_dir()
or root.is_symlink()
or root.name != EXPECTED_PACK_ID
or manifest.get("schema_version") != REPLAY_SCHEMA
or manifest.get("pack_id") != EXPECTED_PACK_ID
or manifest.get("identity_sha256") != EXPECTED_PACK_ID.removeprefix(
"lidar-replay-pack-"
)
or not isinstance(identity, dict)
or identity.get("session_id") != EXPECTED_SESSION_ID
or identity.get("point_frame_count") != 4570
or identity.get("pose_frame_count") != 4598
or not isinstance(artifacts, list)
):
raise RuntimeError("RAVNOVES00 replay identity is invalid")
descriptor = next(
(
row
for row in artifacts
if isinstance(row, dict) and row.get("path") == "lidar-replay.npz"
),
None,
)
replay_path = root / "lidar-replay.npz"
if (
not isinstance(descriptor, dict)
or descriptor.get("sha256") != EXPECTED_REPLAY_SHA256
or descriptor.get("byte_length") != replay_path.stat().st_size
or _sha256(replay_path) != EXPECTED_REPLAY_SHA256
):
raise RuntimeError("RAVNOVES00 replay artifact changed")
expected = {
"point_offsets",
"point_xyz_map",
"point_intensity",
"point_received_monotonic_ns",
"pose_positions_map",
"pose_quaternions_map_from_lidar",
"pose_received_monotonic_ns",
}
with np.load(replay_path, allow_pickle=False) as archive:
if not expected.issubset(archive.files):
raise RuntimeError("RAVNOVES00 replay arrays changed")
# Load each required member once. Reopening a compressed NPZ member for
# every frame would repeatedly inflate the complete 10.7M-point array.
arrays = {name: archive[name] for name in expected}
if (
arrays["point_offsets"].shape != (4571,)
or arrays["point_offsets"].dtype != np.int64
or arrays["point_xyz_map"].shape != (10_751_258, 3)
or arrays["point_xyz_map"].dtype != np.float64
or arrays["point_intensity"].shape != (10_751_258,)
or arrays["point_intensity"].dtype != np.uint8
or arrays["point_received_monotonic_ns"].shape != (4570,)
or arrays["point_received_monotonic_ns"].dtype != np.int64
or arrays["pose_positions_map"].shape != (4598, 3)
or arrays["pose_positions_map"].dtype != np.float64
or arrays["pose_quaternions_map_from_lidar"].shape != (4598, 4)
or arrays["pose_quaternions_map_from_lidar"].dtype != np.float64
or arrays["pose_received_monotonic_ns"].shape != (4598,)
or arrays["pose_received_monotonic_ns"].dtype != np.int64
or int(arrays["point_offsets"][0]) != 0
or int(arrays["point_offsets"][-1]) != 10_751_258
or np.any(np.diff(arrays["point_offsets"]) <= 0)
):
raise RuntimeError("RAVNOVES00 replay array contract changed")
return root, manifest, arrays
def _frame_xyzi(
arrays: dict[str, np.ndarray],
offsets: np.ndarray,
pose_indices: np.ndarray,
frame_index: int,
) -> np.ndarray:
start = int(offsets[frame_index])
stop = int(offsets[frame_index + 1])
pose_index = int(pose_indices[frame_index])
xyzi = sensor_frame_xyzi(
arrays["point_xyz_map"][start:stop],
arrays["point_intensity"][start:stop],
position_map_xyz=arrays["pose_positions_map"][pose_index],
orientation_map_from_lidar_xyzw=arrays[
"pose_quaternions_map_from_lidar"
][pose_index],
)
if not 1 <= xyzi.shape[0] <= MAXIMUM_POINTS:
raise RuntimeError("RAVNOVES00 frame violates PointPillars input bounds")
return xyzi
def _run_frame(
triton_url: str,
native: np.ndarray,
) -> tuple[tuple[PointPillarsBox, ...], float]:
points = np.zeros((1, MAXIMUM_POINTS, 4), dtype=np.float32)
points[0, : native.shape[0]] = native
num_points = np.asarray([native.shape[0]], dtype=np.int32)
output_boxes, output_count, elapsed_ms = _infer(
triton_url,
points,
num_points,
)
return decode_pointpillars_output(output_boxes, output_count), elapsed_ms
def _infer(
triton_url: str,
points: np.ndarray,
num_points: np.ndarray,
) -> tuple[np.ndarray, np.ndarray, float]:
points_binary = np.ascontiguousarray(points, dtype=np.float32).tobytes()
count_binary = np.ascontiguousarray(num_points, dtype=np.int32).tobytes()
header = {
"inputs": [
{
"name": "points",
"shape": [1, MAXIMUM_POINTS, 4],
"datatype": "FP32",
"parameters": {"binary_data_size": len(points_binary)},
},
{
"name": "num_points",
"shape": [1],
"datatype": "INT32",
"parameters": {"binary_data_size": len(count_binary)},
},
],
"outputs": [
{"name": "output_boxes", "parameters": {"binary_data": True}},
{"name": "num_boxes", "parameters": {"binary_data": True}},
],
}
encoded = _canonical_json(header)
request = urllib.request.Request(
f"{triton_url.rstrip('/')}/v2/models/{MODEL_NAME}/infer",
data=encoded + points_binary + count_binary,
headers={
"Content-Type": "application/octet-stream",
"Inference-Header-Content-Length": str(len(encoded)),
},
method="POST",
)
started = time.perf_counter()
with urllib.request.urlopen(request, timeout=120) as response:
payload = response.read()
header_length = int(response.headers["Inference-Header-Content-Length"])
elapsed_ms = (time.perf_counter() - started) * 1000.0
response_header = json.loads(payload[:header_length])
outputs = response_header.get("outputs")
if not isinstance(outputs, list) or len(outputs) != 2:
raise RuntimeError("Triton PointPillars output set changed")
offset = header_length
decoded: dict[str, np.ndarray] = {}
for descriptor in outputs:
if not isinstance(descriptor, dict):
raise RuntimeError("Triton output descriptor is invalid")
name = descriptor.get("name")
parameters = descriptor.get("parameters")
if not isinstance(name, str) or not isinstance(parameters, dict):
raise RuntimeError("Triton output descriptor is invalid")
byte_length = parameters.get("binary_data_size")
if isinstance(byte_length, bool) or not isinstance(byte_length, int):
raise RuntimeError("Triton output byte length is invalid")
binary = payload[offset : offset + byte_length]
offset += byte_length
if name == "output_boxes" and descriptor.get("datatype") == "FP32":
decoded[name] = np.frombuffer(binary, dtype="<f4").reshape(
1, 393_216, 9
)
elif name == "num_boxes" and descriptor.get("datatype") == "INT32":
decoded[name] = np.frombuffer(binary, dtype="<i4").reshape(1)
else:
raise RuntimeError("Triton PointPillars output contract changed")
if offset != len(payload) or set(decoded) != {"output_boxes", "num_boxes"}:
raise RuntimeError("Triton PointPillars output payload is invalid")
return decoded["output_boxes"], decoded["num_boxes"], elapsed_ms
def _box_payload(box: PointPillarsBox) -> dict[str, object]:
return {
"x_m": box.x_m,
"y_m": box.y_m,
"z_m": box.z_m,
"length_m": box.length_m,
"width_m": box.width_m,
"height_m": box.height_m,
"yaw_rad": box.yaw_rad,
"class_id": box.class_id,
"model_class": box.model_class,
"score": box.score,
}
def _boxes_sha256(boxes: tuple[PointPillarsBox, ...]) -> str:
return hashlib.sha256(
_canonical_json([_box_payload(box) for box in boxes])
).hexdigest()
def _frame_summary(
*,
frame_index: int,
session_seconds: float,
source_point_count: int,
pose_index: int,
pose_binding_age_ms: float,
input_sha256: str,
inference_ms: float,
boxes: tuple[PointPillarsBox, ...],
) -> dict[str, object]:
class_counts = {
name: sum(box.model_class == name for box in boxes)
for name in ("Vehicle", "Pedestrian", "Cyclist")
}
return {
"schema_version": FRAME_SCHEMA,
"frame_id": f"{frame_index:06d}",
"frame_index": frame_index,
"session_seconds": session_seconds,
"source_point_count": source_point_count,
"pose_index": pose_index,
"pose_binding_age_ms": pose_binding_age_ms,
"input_xyzi_sha256": input_sha256,
"inference_ms": inference_ms,
"prediction_count": len(boxes),
"class_counts": class_counts,
"boxes_sha256": _boxes_sha256(boxes),
}
def _read_frame_summary(
path: Path,
*,
frame_index: int,
input_sha256: str,
) -> dict[str, Any]:
payload = _read_json(path)
if (
payload.get("schema_version") != FRAME_SCHEMA
or payload.get("frame_id") != f"{frame_index:06d}"
or payload.get("frame_index") != frame_index
or payload.get("input_xyzi_sha256") != input_sha256
or not isinstance(payload.get("class_counts"), dict)
or not isinstance(payload.get("boxes_sha256"), str)
):
raise RuntimeError("cached L3.1 frame summary is invalid")
return payload
def _visual_frame(
*,
summary: dict[str, Any],
xyzi: np.ndarray,
boxes: tuple[PointPillarsBox, ...],
replay_inference_ms: float,
deterministic_replay: bool,
) -> dict[str, object]:
minimum_x, minimum_y, minimum_z, maximum_x, maximum_y, maximum_z = (
POINTPILLARS_MODEL_POINT_CLOUD_RANGE
)
mask = (
(xyzi[:, 0] >= minimum_x)
& (xyzi[:, 0] <= maximum_x)
& (xyzi[:, 1] >= minimum_y)
& (xyzi[:, 1] <= maximum_y)
& (xyzi[:, 2] >= minimum_z)
& (xyzi[:, 2] <= maximum_z)
)
visible_points = xyzi[mask]
visible_boxes = boxes[:MAXIMUM_VISUAL_BOXES]
return {
"schema_version": VISUAL_FRAME_SCHEMA,
"frame_id": summary["frame_id"],
"summary": {
**summary,
"replay_inference_ms": replay_inference_ms,
"deterministic_replay": deterministic_replay,
"visual_box_count": len(visible_boxes),
"visual_box_truncated": len(boxes) > len(visible_boxes),
},
"points": {
"layout": "flat-xyzi",
"source_point_count": int(xyzi.shape[0]),
"model_range_point_count": int(visible_points.shape[0]),
"sampled_point_count": int(visible_points.shape[0]),
"values": visible_points.reshape(-1).tolist(),
},
"prediction_boxes": [_box_payload(box) for box in visible_boxes],
"interpretation": {
"ground_truth_available": False,
"boxes_are_model_hypotheses": True,
"accuracy_claim_allowed": False,
},
}
def _metrics(
summaries: list[dict[str, Any]],
*,
deterministic_matches: int,
deterministic_total: int,
) -> dict[str, object]:
latency = np.asarray([row["inference_ms"] for row in summaries], dtype=np.float64)
pose_age = np.asarray(
[row["pose_binding_age_ms"] for row in summaries],
dtype=np.float64,
)
class_counts = {
name: sum(int(row["class_counts"][name]) for row in summaries)
for name in ("Vehicle", "Pedestrian", "Cyclist")
}
predictions = [int(row["prediction_count"]) for row in summaries]
return {
"frame_count": len(summaries),
"input_admission_fraction": 1.0,
"output_schema_valid_fraction": 1.0,
"frames_with_predictions": sum(value > 0 for value in predictions),
"frames_with_vehicle_predictions": sum(
int(row["class_counts"]["Vehicle"]) > 0 for row in summaries
),
"prediction_count": sum(predictions),
"class_counts": class_counts,
"predictions_per_frame": _distribution(np.asarray(predictions)),
"inference_latency_ms": _distribution(latency),
"pose_binding_age_ms": _distribution(pose_age),
"deterministic_replay_fraction": (
deterministic_matches / deterministic_total
if deterministic_total
else 0.0
),
"deterministic_replay_frames": deterministic_total,
"source_paced": False,
"input_drop_count": 0,
}
def _distribution(values: np.ndarray) -> dict[str, float]:
return {
"minimum": float(np.min(values)),
"p50": float(np.percentile(values, 50)),
"p95": float(np.percentile(values, 95)),
"maximum": float(np.max(values)),
"mean": float(np.mean(values)),
}
def _read_worker_package(path: Path) -> dict[str, Any]:
root = path.expanduser().resolve(strict=True)
manifest = _read_json(root / "manifest.json")
identity = manifest.get("identity")
artifacts = manifest.get("artifacts")
identity_sha256 = manifest.get("identity_sha256")
package_id = manifest.get("package_id")
if (
root.is_symlink()
or manifest.get("schema_version") != WORKER_PACKAGE_SCHEMA
or not isinstance(identity, dict)
or not isinstance(artifacts, list)
or not isinstance(identity_sha256, str)
or hashlib.sha256(_canonical_json(identity)).hexdigest() != identity_sha256
or package_id != f"l3-pointpillars-worker-package-{identity_sha256}"
or root.name != package_id
or Path(__file__).resolve(strict=True).parents[1] != root
):
raise RuntimeError("L3.1 worker package identity is invalid")
expected = set(identity.get("artifact_paths", []))
actual = {
item.relative_to(root).as_posix()
for item in root.rglob("*")
if item.is_file()
}
if actual != expected | {"manifest.json"}:
raise RuntimeError("L3.1 worker package file set changed")
for descriptor in artifacts:
if not isinstance(descriptor, dict):
raise RuntimeError("L3.1 worker package artifact is invalid")
relative = descriptor.get("path")
member = root / str(relative)
if (
not isinstance(relative, str)
or relative not in expected
or not member.is_file()
or member.is_symlink()
or descriptor.get("byte_length") != member.stat().st_size
or descriptor.get("sha256") != _sha256(member)
):
raise RuntimeError("L3.1 worker package artifact changed")
return manifest
def _require_triton_ready(url: str) -> None:
for endpoint in ("/v2/health/ready", f"/v2/models/{MODEL_NAME}/ready"):
request = urllib.request.Request(f"{url.rstrip('/')}{endpoint}", method="GET")
try:
with urllib.request.urlopen(request, timeout=10) as response:
if response.status != 200:
raise RuntimeError("canonical Triton is not ready")
except OSError as exc:
raise RuntimeError("canonical Triton or PointPillars is not ready") from exc
def _artifact(path: Path, kind: str, *, relative_to: Path) -> dict[str, object]:
return {
"kind": kind,
"path": path.relative_to(relative_to).as_posix(),
"byte_length": path.stat().st_size,
"sha256": _sha256(path),
}
def _directory_identity(root: Path) -> str:
digest = hashlib.sha256()
for path in sorted(root.glob("*.json"), key=lambda item: item.name):
digest.update(path.name.encode("ascii"))
digest.update(b"\0")
digest.update(bytes.fromhex(_sha256(path)))
return digest.hexdigest()
def _canonical_json(value: object) -> bytes:
return json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode("utf-8")
def _read_json(path: Path) -> dict[str, Any]:
try:
value = json.loads(path.read_text(encoding="utf-8"))
except (OSError, json.JSONDecodeError) as exc:
raise RuntimeError(f"invalid JSON artifact: {path.name}") from exc
if not isinstance(value, dict):
raise RuntimeError(f"invalid JSON artifact: {path.name}")
return value
def _write_once(path: Path, payload: object) -> None:
encoded = _canonical_json(payload)
if path.exists():
if path.read_bytes() != encoded:
raise RuntimeError(f"immutable artifact changed: {path.name}")
return
_atomic_bytes(path, encoded)
def _atomic_json(path: Path, payload: object) -> None:
_atomic_bytes(path, _canonical_json(payload))
def _atomic_bytes(path: Path, payload: bytes) -> None:
path.parent.mkdir(mode=0o700, parents=True, exist_ok=True)
descriptor, temporary = tempfile.mkstemp(
prefix=f".{path.name}.",
suffix=".tmp",
dir=path.parent,
)
try:
with os.fdopen(descriptor, "wb") as handle:
handle.write(payload)
handle.flush()
os.fsync(handle.fileno())
os.replace(temporary, path)
except BaseException:
try:
os.unlink(temporary)
except FileNotFoundError:
pass
raise
def _sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as handle:
for block in iter(lambda: handle.read(1024 * 1024), b""):
digest.update(block)
return digest.hexdigest()
if __name__ == "__main__":
raise SystemExit(main())