refactor(lab): объединить RAV004 в единый Rerun replay
This commit is contained in:
@@ -24,8 +24,10 @@ from pydantic import BaseModel, ConfigDict, Field
|
||||
from k1link.laboratory.canonical_rerun_overlay import (
|
||||
CanonicalLabOverlayArtifact,
|
||||
CanonicalLabOverlayError,
|
||||
CanonicalLabReplayArtifact,
|
||||
_mask_component_boxes,
|
||||
canonical_lab_overlay,
|
||||
canonical_lab_replay,
|
||||
canonical_recording_id,
|
||||
)
|
||||
from k1link.laboratory.evidence_registry import LaboratoryEvidenceDefinition
|
||||
@@ -352,6 +354,59 @@ def _build_vegetation_lab_router(
|
||||
) from exc
|
||||
return artifact
|
||||
|
||||
async def canonical_rerun_replay_artifact(
|
||||
result_id: str,
|
||||
*,
|
||||
expected_base_generation_sha256: str | None = None,
|
||||
) -> CanonicalLabReplayArtifact:
|
||||
"""Return one immutable RRD containing base geometry and LAB perception."""
|
||||
|
||||
if (
|
||||
canonical_recording_provider is None
|
||||
or jobs_root is None
|
||||
or rerun_overlay_cache_root is None
|
||||
or ffmpeg_path is None
|
||||
):
|
||||
raise HTTPException(status_code=503, detail="Canonical LAB Rerun replay unavailable")
|
||||
candidate = _resolve_candidate(root_provider, definition, result_id)
|
||||
manifest = _read_verified(candidate, definition)
|
||||
route, _ = _full_route_context(candidate, manifest)
|
||||
recording = canonical_recording_provider(str(route["session_id"]))
|
||||
if recording is None:
|
||||
raise HTTPException(status_code=409, detail="Canonical spatial recording is not ready")
|
||||
recording_path, generation_sha256 = recording
|
||||
if (
|
||||
expected_base_generation_sha256 is not None
|
||||
and expected_base_generation_sha256 != generation_sha256
|
||||
):
|
||||
raise HTTPException(status_code=412, detail="Canonical recording generation changed")
|
||||
try:
|
||||
recording_id = await run_in_threadpool(canonical_recording_id, recording_path)
|
||||
overlay = await run_in_threadpool(
|
||||
canonical_lab_overlay,
|
||||
candidate,
|
||||
manifest,
|
||||
recording_id=recording_id,
|
||||
base_generation_sha256=generation_sha256,
|
||||
jobs_root=jobs_root,
|
||||
cache_root=rerun_overlay_cache_root,
|
||||
ffmpeg_path=ffmpeg_path,
|
||||
)
|
||||
return await run_in_threadpool(
|
||||
canonical_lab_replay,
|
||||
recording_path,
|
||||
base_generation_sha256=generation_sha256,
|
||||
overlay=overlay,
|
||||
result_id=result_id,
|
||||
recording_id=recording_id,
|
||||
cache_root=rerun_overlay_cache_root,
|
||||
)
|
||||
except CanonicalLabOverlayError as exc:
|
||||
raise HTTPException(
|
||||
status_code=503,
|
||||
detail="Canonical LAB Rerun replay failed verification",
|
||||
) from exc
|
||||
|
||||
def canonical_rerun_overlay_file_response(
|
||||
artifact: CanonicalLabOverlayArtifact,
|
||||
) -> FileResponse:
|
||||
@@ -365,6 +420,19 @@ def _build_vegetation_lab_router(
|
||||
},
|
||||
)
|
||||
|
||||
def canonical_rerun_replay_file_response(
|
||||
artifact: CanonicalLabReplayArtifact,
|
||||
) -> FileResponse:
|
||||
return FileResponse(
|
||||
artifact.path,
|
||||
media_type="application/vnd.rerun.rrd",
|
||||
headers={
|
||||
"Cache-Control": "private, max-age=31536000, immutable",
|
||||
"ETag": f'"{artifact.sha256}"',
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
},
|
||||
)
|
||||
|
||||
@router.post("/{result_id}/canonical-overlay.rrd")
|
||||
async def get_canonical_rerun_overlay(
|
||||
result_id: str,
|
||||
@@ -446,6 +514,41 @@ def _build_vegetation_lab_router(
|
||||
raise HTTPException(status_code=412, detail="Canonical overlay generation changed")
|
||||
return canonical_rerun_overlay_file_response(artifact)
|
||||
|
||||
@router.head("/{result_id}/canonical-replay.rrd")
|
||||
async def describe_canonical_rerun_replay(
|
||||
result_id: str,
|
||||
base_generation: Annotated[str, Query(pattern=r"^[a-f0-9]{64}$")],
|
||||
) -> Response:
|
||||
"""Build once and describe the single RRD consumed by the LAB viewer."""
|
||||
|
||||
artifact = await canonical_rerun_replay_artifact(
|
||||
result_id,
|
||||
expected_base_generation_sha256=base_generation,
|
||||
)
|
||||
return Response(
|
||||
status_code=200,
|
||||
media_type="application/vnd.rerun.rrd",
|
||||
headers={
|
||||
"Cache-Control": "private, no-store",
|
||||
"Content-Length": str(artifact.byte_length),
|
||||
"ETag": f'"{artifact.sha256}"',
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
"X-Rerun-Format": "RRF2",
|
||||
},
|
||||
)
|
||||
|
||||
@router.get("/{result_id}/canonical-replay.rrd")
|
||||
async def stream_canonical_rerun_replay(
|
||||
result_id: str,
|
||||
generation: Annotated[str, Query(pattern=r"^[a-f0-9]{64}$")],
|
||||
) -> FileResponse:
|
||||
"""Stream the digest-bound merged replay through one native receiver."""
|
||||
|
||||
artifact = await canonical_rerun_replay_artifact(result_id)
|
||||
if generation != artifact.sha256:
|
||||
raise HTTPException(status_code=412, detail="Canonical replay generation changed")
|
||||
return canonical_rerun_replay_file_response(artifact)
|
||||
|
||||
@router.get("/{result_id}/timeline")
|
||||
def get_canonical_route_timeline(result_id: str) -> dict[str, object]:
|
||||
candidate = _resolve_candidate(root_provider, definition, result_id)
|
||||
|
||||
Reference in New Issue
Block a user