fix(lab): enforce canonical replay runtime

This commit is contained in:
DCCONSTRUCTIONS
2026-08-29 22:42:30 +03:00
parent 525ab74168
commit bd2892140f
17 changed files with 1765 additions and 507 deletions
+71
View File
@@ -37,6 +37,7 @@ from k1link.sessions import (
SessionStore,
validate_recorded_media_timeline,
)
from k1link.sessions.canonical_lab_spatial import canonical_lab_spatial_frame
from k1link.sessions.plugin_contract import RecordedPointColorRenderer
from k1link.viewer.recorded import (
APPLICATION_ID as RECORDED_APPLICATION_ID,
@@ -824,6 +825,76 @@ def build_session_router(
**response_kwargs,
)
@router.get(
"/api/v1/observation-sessions/{session_id}/canonical-lab/spatial-frame"
)
async def get_observation_session_canonical_lab_spatial_frame(
session_id: str,
generation: Annotated[str, Query(min_length=64, max_length=64)],
time_ns: Annotated[int, Query(ge=0, le=MAX_SAFE_INTEGER)],
) -> JSONResponse:
"""Serve one body-frame sample for the canonical recorded-LAB clock.
The camera timeline owns playback. Spatial evidence is sampled from
the same immutable recording instead of starting a second Rerun clock.
"""
if SAFE_SHA256.fullmatch(generation) is None:
raise HTTPException(
status_code=412,
detail="Поколение spatial-записи не совпадает.",
)
if recording_preparation_manager is None:
raise HTTPException(
status_code=503,
detail="Сервис canonical LAB spatial playback не настроен.",
)
snapshot = recording_preparation_manager.status(session_id)
if snapshot is None or snapshot.state != "ready" or snapshot.recording is None:
raise HTTPException(
status_code=409,
detail="Запись canonical LAB ещё не подготовлена.",
)
_require_matching_recording_generation(snapshot.recording.sha256, generation)
pinned = recording_preparation_manager.pin_ready(
session_id,
preparation_id=snapshot.preparation_id,
)
if pinned is None:
raise HTTPException(
status_code=412,
detail="Подготовленная spatial-запись была заменена.",
)
pinned_snapshot, release_recording = pinned
try:
recording = pinned_snapshot.recording
if recording is None:
raise HTTPException(
status_code=500,
detail="Подготовленная spatial-запись недоступна.",
)
payload = await run_in_threadpool(
canonical_lab_spatial_frame,
recording.path,
generation,
time_ns,
)
except (OSError, ValueError) as exc:
raise HTTPException(
status_code=503,
detail="Canonical LAB spatial frame не прошёл проверку.",
) from exc
finally:
release_recording()
return JSONResponse(
payload,
headers={
"Cache-Control": "private, max-age=31536000, immutable",
"ETag": f'"{generation}:{payload["source_time_ns"]}"',
"X-Content-Type-Options": "nosniff",
},
)
@router.post("/api/v1/observation-sessions/{session_id}/blueprint.rrd")
async def get_observation_session_blueprint(
session_id: str,