feat(perception): run PointPillars on RAVNOVES00
This commit is contained in:
@@ -53,6 +53,36 @@ def test_advanced_index_is_empty_when_not_configured() -> None:
|
||||
}
|
||||
|
||||
|
||||
def test_advanced_index_publishes_l31_ravnoves_identity(
|
||||
tmp_path: Path,
|
||||
monkeypatch: MonkeyPatch,
|
||||
) -> None:
|
||||
result_id = f"l31-pointpillars-ravnoves-{'b' * 64}"
|
||||
|
||||
def fake_latest(provider: object) -> dict[str, str]:
|
||||
assert callable(provider)
|
||||
assert provider() == tmp_path # type: ignore[operator]
|
||||
return {
|
||||
"result_id": result_id,
|
||||
"created_at_utc": "2026-07-31T10:56:45.861Z",
|
||||
}
|
||||
|
||||
monkeypatch.setattr(advanced_api, "latest_l31_identity", fake_latest)
|
||||
router = build_advanced_laboratory_router(
|
||||
l31_ravnoves_root_provider=lambda: tmp_path,
|
||||
)
|
||||
route = _endpoint(router, "/api/v1/laboratory/advanced-index")
|
||||
|
||||
assert route()["items"] == [ # type: ignore[index,operator]
|
||||
{
|
||||
"work_id": "l31-pointpillars-ravnoves",
|
||||
"result_id": result_id,
|
||||
"created_at_utc": "2026-07-31T10:56:45.861Z",
|
||||
"access": "read-only",
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def test_advanced_index_reads_only_bounded_identity_documents(
|
||||
tmp_path: Path,
|
||||
) -> None:
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
import pytest
|
||||
|
||||
from k1link.compute.l31_pointpillars_ravnoves import (
|
||||
L31PointPillarsRavnovesError,
|
||||
nearest_pose_indices,
|
||||
select_visual_frame_indices,
|
||||
sensor_frame_xyzi,
|
||||
)
|
||||
|
||||
|
||||
def test_nearest_pose_binding_prefers_earlier_pose_on_tie() -> None:
|
||||
indices, age_ms = nearest_pose_indices(
|
||||
np.asarray([10, 20, 31], dtype=np.int64) * 1_000_000,
|
||||
np.asarray([5, 15, 30], dtype=np.int64) * 1_000_000,
|
||||
)
|
||||
assert indices.tolist() == [0, 1, 2]
|
||||
assert age_ms.tolist() == [5.0, 5.0, 1.0]
|
||||
|
||||
|
||||
def test_sensor_frame_xyzi_inverts_map_pose() -> None:
|
||||
points = np.asarray([[11.0, 20.0, 30.0], [10.0, 22.0, 30.0]])
|
||||
intensity = np.asarray([255, 0], dtype=np.uint8)
|
||||
result = sensor_frame_xyzi(
|
||||
points,
|
||||
intensity,
|
||||
position_map_xyz=np.asarray([10.0, 20.0, 30.0]),
|
||||
orientation_map_from_lidar_xyzw=np.asarray([0.0, 0.0, 0.0, 1.0]),
|
||||
)
|
||||
np.testing.assert_allclose(
|
||||
result,
|
||||
np.asarray([[1.0, 0.0, 0.0, 1.0], [0.0, 2.0, 0.0, 0.0]]),
|
||||
)
|
||||
|
||||
|
||||
def test_visual_selection_prefers_vehicles_with_route_coverage() -> None:
|
||||
selected = select_visual_frame_indices(
|
||||
[0, 3, 1, 0, 0, 2],
|
||||
[5, 3, 9, 0, 4, 2],
|
||||
maximum_frames=3,
|
||||
)
|
||||
assert selected == (1, 2, 5)
|
||||
|
||||
|
||||
def test_visual_selection_rejects_impossible_class_count() -> None:
|
||||
with pytest.raises(L31PointPillarsRavnovesError):
|
||||
select_visual_frame_indices([2], [1])
|
||||
@@ -0,0 +1,202 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi.routing import APIRoute
|
||||
from pytest import raises
|
||||
|
||||
from k1link.web.l31_pointpillars_ravnoves_api import (
|
||||
build_l31_pointpillars_ravnoves_router,
|
||||
)
|
||||
|
||||
|
||||
def _endpoint(router: APIRouter, path: str) -> object:
|
||||
for route in router.routes:
|
||||
if (
|
||||
isinstance(route, APIRoute)
|
||||
and route.path == path
|
||||
and route.methods is not None
|
||||
and "GET" in route.methods
|
||||
):
|
||||
return route.endpoint
|
||||
raise AssertionError(f"GET {path} route is missing")
|
||||
|
||||
|
||||
def _canonical(payload: object) -> bytes:
|
||||
return json.dumps(
|
||||
payload,
|
||||
ensure_ascii=False,
|
||||
separators=(",", ":"),
|
||||
sort_keys=True,
|
||||
).encode("utf-8")
|
||||
|
||||
|
||||
def _write(path: Path, payload: object) -> dict[str, object]:
|
||||
content = _canonical(payload)
|
||||
path.parent.mkdir(parents=True, exist_ok=True)
|
||||
path.write_bytes(content)
|
||||
return {
|
||||
"sha256": hashlib.sha256(content).hexdigest(),
|
||||
"byte_length": len(content),
|
||||
}
|
||||
|
||||
|
||||
def _result(root: Path) -> tuple[str, str]:
|
||||
frame_id = "000075"
|
||||
identity = {
|
||||
"source_session_id": "20260720T065719Z_viewer_live",
|
||||
"source_pack_id": f"lidar-replay-pack-{'1' * 64}",
|
||||
"source_logical_content_sha256": "2" * 64,
|
||||
"model": {"name": "pointpillars", "source_model_sha256": "3" * 64},
|
||||
"execution": {
|
||||
"worker_host_id": "worker-006",
|
||||
"sequential": True,
|
||||
"parallel_workers": 1,
|
||||
},
|
||||
"authority": {
|
||||
"shadow_only": True,
|
||||
"commands_enabled": False,
|
||||
"navigation_or_safety_accepted": False,
|
||||
"accuracy_accepted": False,
|
||||
},
|
||||
}
|
||||
identity_sha = hashlib.sha256(_canonical(identity)).hexdigest()
|
||||
result_id = f"l31-pointpillars-ravnoves-{identity_sha}"
|
||||
candidate = root / result_id
|
||||
detail = {
|
||||
"schema_version": "missioncore.l31-pointpillars-ravnoves-visual-frame/v1",
|
||||
"frame_id": frame_id,
|
||||
"points": {
|
||||
"layout": "flat-xyzi",
|
||||
"sampled_point_count": 1,
|
||||
"values": [1.0, 2.0, 3.0, 0.5],
|
||||
},
|
||||
"prediction_boxes": [
|
||||
{
|
||||
"x_m": 1.0,
|
||||
"y_m": 2.0,
|
||||
"z_m": 0.5,
|
||||
"length_m": 4.0,
|
||||
"width_m": 2.0,
|
||||
"height_m": 1.5,
|
||||
"yaw_rad": 0.1,
|
||||
"class_id": 0,
|
||||
"model_class": "Vehicle",
|
||||
"score": 0.8,
|
||||
}
|
||||
],
|
||||
"interpretation": {
|
||||
"ground_truth_available": False,
|
||||
"boxes_are_model_hypotheses": True,
|
||||
"accuracy_claim_allowed": False,
|
||||
},
|
||||
}
|
||||
detail_descriptor = _write(
|
||||
candidate / "visual-frames" / f"{frame_id}.json",
|
||||
detail,
|
||||
)
|
||||
frame = {
|
||||
"frame_id": frame_id,
|
||||
"frame_index": 75,
|
||||
"session_seconds": 7.5,
|
||||
"source_point_count": 2143,
|
||||
"prediction_count": 1,
|
||||
"class_counts": {"Vehicle": 1, "Pedestrian": 0, "Cyclist": 0},
|
||||
"inference_ms": 26.3,
|
||||
"detail_path": f"visual-frames/{frame_id}.json",
|
||||
"detail_sha256": detail_descriptor["sha256"],
|
||||
"detail_byte_length": detail_descriptor["byte_length"],
|
||||
}
|
||||
catalog = {
|
||||
"schema_version": "missioncore.l31-pointpillars-ravnoves-catalog/v1",
|
||||
"result_id": result_id,
|
||||
"source_session_id": identity["source_session_id"],
|
||||
"frame_count": 1,
|
||||
"frames": [frame],
|
||||
}
|
||||
catalog_descriptor = _write(candidate / "catalog.json", catalog)
|
||||
manifest = {
|
||||
"schema_version": "missioncore.l31-pointpillars-ravnoves/v1",
|
||||
"result_id": result_id,
|
||||
"identity_sha256": identity_sha,
|
||||
"identity": identity,
|
||||
"created_at_utc": "2026-07-31T10:00:00Z",
|
||||
"status": "k1-cross-domain-transfer-measured-visual-review-required",
|
||||
"metrics": {
|
||||
"frame_count": 4570,
|
||||
"input_admission_fraction": 1.0,
|
||||
"output_schema_valid_fraction": 1.0,
|
||||
},
|
||||
"catalog": {
|
||||
"path": "catalog.json",
|
||||
"kind": "visual-frame-catalog",
|
||||
**catalog_descriptor,
|
||||
},
|
||||
"limitations": ["Independent 3D ground truth is unavailable."],
|
||||
"authority": identity["authority"],
|
||||
}
|
||||
_write(candidate / "manifest.json", manifest)
|
||||
return result_id, frame_id
|
||||
|
||||
|
||||
def test_l31_catalog_and_visual_frame_are_read_only(tmp_path: Path) -> None:
|
||||
result_id, frame_id = _result(tmp_path)
|
||||
router = build_l31_pointpillars_ravnoves_router(
|
||||
root_provider=lambda: tmp_path,
|
||||
)
|
||||
catalog_route = _endpoint(
|
||||
router,
|
||||
"/api/v1/laboratory/l31/pointpillars-ravnoves/results",
|
||||
)
|
||||
frame_route = _endpoint(
|
||||
router,
|
||||
(
|
||||
"/api/v1/laboratory/l31/pointpillars-ravnoves/"
|
||||
"{result_id}/frames/{frame_id}"
|
||||
),
|
||||
)
|
||||
|
||||
catalog = catalog_route(limit=1) # type: ignore[operator]
|
||||
assert catalog["configured"] is True
|
||||
assert catalog["invalid_total"] == 0
|
||||
assert catalog["items"][0]["result_id"] == result_id
|
||||
assert catalog["items"][0]["status"] == (
|
||||
"cross-domain-transfer-measured-visual-review-required"
|
||||
)
|
||||
assert catalog["items"][0]["frames"][0]["frame_id"] == frame_id
|
||||
|
||||
frame = frame_route( # type: ignore[operator]
|
||||
result_id=result_id,
|
||||
frame_id=frame_id,
|
||||
)
|
||||
assert frame["prediction_boxes"][0]["model_class"] == "Vehicle"
|
||||
assert frame["interpretation"]["ground_truth_available"] is False
|
||||
assert frame["access"] == "read-only"
|
||||
|
||||
|
||||
def test_l31_visual_frame_fails_closed_after_mutation(tmp_path: Path) -> None:
|
||||
result_id, frame_id = _result(tmp_path)
|
||||
router = build_l31_pointpillars_ravnoves_router(
|
||||
root_provider=lambda: tmp_path,
|
||||
)
|
||||
frame_route = _endpoint(
|
||||
router,
|
||||
(
|
||||
"/api/v1/laboratory/l31/pointpillars-ravnoves/"
|
||||
"{result_id}/frames/{frame_id}"
|
||||
),
|
||||
)
|
||||
(tmp_path / result_id / "visual-frames" / f"{frame_id}.json").write_text(
|
||||
"{}",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
with raises(Exception) as caught:
|
||||
frame_route( # type: ignore[operator]
|
||||
result_id=result_id,
|
||||
frame_id=frame_id,
|
||||
)
|
||||
assert getattr(caught.value, "status_code", None) == 404
|
||||
Reference in New Issue
Block a user