feat(lab): publish M4.8T quality evidence

This commit is contained in:
DCCONSTRUCTIONS
2026-08-25 23:44:27 +03:00
parent 9a956c318a
commit 3ff3b9b587
20 changed files with 1419 additions and 4 deletions
+14
View File
@@ -128,6 +128,7 @@ from k1link.web.m48_object_quality_api import build_m48_object_quality_router
from k1link.web.m48s_fixed_class_detector_lab_api import (
build_m48s_fixed_class_detector_lab_router,
)
from k1link.web.m48t_risk_quality_lab_api import build_m48t_risk_quality_lab_router
from k1link.web.map_api import (
MapGatewayConfiguration,
MapGatewayProxy,
@@ -151,6 +152,7 @@ from k1link.web.runtime_readiness import (
build_runtime_readiness,
)
from k1link.web.session_api import build_session_router
from k1link.web.simulation_world_provider_api import build_simulation_world_provider_router
from k1link.web.system_telemetry_api import build_system_telemetry_router
from k1link.web.viewer_diagnostics_api import build_viewer_diagnostics_router
@@ -964,6 +966,17 @@ app.include_router(
),
)
)
app.include_router(
build_m48t_risk_quality_lab_router(
root_provider=lambda: (
REPOSITORY_ROOT
/ ".runtime"
/ "compute-experiments"
/ "m48t-risk-quality"
/ "lab-results"
),
)
)
app.include_router(
build_e47_semantic_slam_router(
root_provider=lambda: (
@@ -1296,6 +1309,7 @@ app.include_router(
root_provider=lambda: REPOSITORY_ROOT / ".runtime" / "system",
)
)
app.include_router(build_simulation_world_provider_router())
frontend_dist = REPOSITORY_ROOT / "apps" / "control-station" / "dist"
app.include_router(
build_viewer_diagnostics_router(
+301
View File
@@ -0,0 +1,301 @@
"""Read-only API for the sealed M4.8T quality and temporal LAB."""
from __future__ import annotations
import copy
import hashlib
import json
import re
from collections.abc import Callable
from pathlib import Path, PurePosixPath
from typing import Any, Final
from fastapi import APIRouter, HTTPException, Query
from fastapi.responses import FileResponse
from k1link.laboratory.evidence_registry import LaboratoryEvidenceDefinition
from k1link.laboratory.evidence_report import (
LaboratoryEvidenceReportError,
verify_laboratory_evidence_result,
)
from k1link.laboratory.m48t_risk_quality_lab import (
CATALOG_SCHEMA,
LAB_SCHEMA,
REPORT_SCHEMA,
RESULT_PREFIX,
)
from k1link.perception.fixed_class_detector_tournament import false_authority
RootProvider = Callable[[], Path | None]
RESULT_ID: Final = re.compile(rf"^{re.escape(RESULT_PREFIX)}[a-f0-9]{{64}}$")
CASE_ID: Final = re.compile(r"^[0-9]{12}$")
VIEW_SCHEMA: Final = "missioncore.m48t-risk-quality-temporal-view/v1"
CATALOG_VIEW_SCHEMA: Final = "missioncore.m48t-risk-quality-temporal-catalog/v1"
_DEFINITION: Final = LaboratoryEvidenceDefinition(
work_id="m48t-risk-quality-temporal",
runtime_relative_root=PurePosixPath("m48t-risk-quality/lab-results"),
result_id_prefix="m48t-risk-quality-temporal-lab",
document_name="manifest.json",
result_schema_version=LAB_SCHEMA,
)
def build_m48t_risk_quality_lab_router(
*, root_provider: RootProvider = lambda: None
) -> APIRouter:
router = APIRouter(
prefix="/api/v1/laboratory/m48t/risk-quality",
tags=["laboratory"],
)
@router.get("/results")
def list_results(limit: int = Query(default=1, ge=1, le=10)) -> dict[str, object]:
root = _configured_root(root_provider)
if root is None:
return _empty_catalog(False)
candidates = _candidates(root)
items: list[dict[str, object]] = []
invalid_total = 0
for candidate in candidates:
try:
items.append(_project_result(candidate))
except RuntimeError:
invalid_total += 1
items.sort(
key=lambda item: (str(item["created_at_utc"]), str(item["result_id"])),
reverse=True,
)
return {
"schema_version": CATALOG_VIEW_SCHEMA,
"configured": True,
"items": items[:limit],
"candidate_total": len(candidates),
"invalid_total": invalid_total,
"access": "read-only",
}
@router.get("/results/{result_id}")
def get_result(result_id: str) -> dict[str, object]:
try:
return _project_result(_resolve_candidate(root_provider, result_id))
except RuntimeError:
raise HTTPException(status_code=404, detail="M4.8T result not found") from None
@router.get("/results/{result_id}/review/{case_id}.jpg")
def get_review_image(result_id: str, case_id: str) -> FileResponse:
if CASE_ID.fullmatch(case_id) is None:
raise HTTPException(status_code=404, detail="M4.8T review case not found")
try:
loaded = _load_result(_resolve_candidate(root_provider, result_id))
except RuntimeError:
raise HTTPException(status_code=404, detail="M4.8T result not found") from None
descriptor = next(
(
item
for item in loaded["catalog"]["cases"]
if isinstance(item, dict) and item.get("case_id") == case_id
),
None,
)
if not isinstance(descriptor, dict):
raise HTTPException(status_code=404, detail="M4.8T review case not found")
candidate = loaded["root"]
path = (candidate / str(descriptor["path"])).resolve()
if (
not path.is_relative_to(candidate)
or path.is_symlink()
or not path.is_file()
or descriptor.get("byte_length") != path.stat().st_size
or descriptor.get("sha256") != _sha256(path)
):
raise HTTPException(status_code=404, detail="M4.8T review case not found")
return FileResponse(
path,
media_type="image/jpeg",
headers={
"Cache-Control": "public, max-age=31536000, immutable",
"ETag": f'"{descriptor["sha256"]}"',
"X-Content-Type-Options": "nosniff",
},
)
return router
def _project_result(candidate: Path) -> dict[str, object]:
loaded = _load_result(candidate)
manifest = loaded["manifest"]
report = loaded["report"]
catalog = loaded["catalog"]
result_id = str(manifest["result_id"])
review_cases = [
{
"case_id": item["case_id"],
"image_id": item["image_id"],
"image_url": (
f"/api/v1/laboratory/m48t/risk-quality/results/{result_id}"
f"/review/{item['case_id']}.jpg"
),
"media_type": item["media_type"],
"byte_length": item["byte_length"],
"sha256": item["sha256"],
}
for item in catalog["cases"]
]
return {
"schema_version": VIEW_SCHEMA,
"result_id": result_id,
"created_at_utc": manifest["created_at_utc"],
"status": manifest["status"],
"source": copy.deepcopy(report["source"]),
"configuration": copy.deepcopy(report["configuration"]),
"method": copy.deepcopy(report["method"]),
"execution": copy.deepcopy(report["execution"]),
"metrics": copy.deepcopy(report["metrics"]),
"acceptance": copy.deepcopy(report["acceptance"]),
"decision": copy.deepcopy(report["decision"]),
"limitations": copy.deepcopy(report["limitations"]),
"review": {
"legend": copy.deepcopy(catalog["legend"]),
"cases": review_cases,
},
"ground_truth": False,
"authority": copy.deepcopy(report["authority"]),
"access": "read-only",
}
def _load_result(candidate: Path) -> dict[str, Any]:
if (
not candidate.is_dir()
or candidate.is_symlink()
or RESULT_ID.fullmatch(candidate.name) is None
):
raise RuntimeError("M4.8T result candidate is invalid")
try:
verify_laboratory_evidence_result(_DEFINITION, candidate)
manifest = _read_object(candidate / "manifest.json")
report = _read_object(candidate / "report.json")
catalog = _read_object(candidate / "catalog.json")
except (LaboratoryEvidenceReportError, OSError, ValueError) as exc:
raise RuntimeError("M4.8T result integrity failed") from exc
identity = manifest.get("identity")
if (
manifest.get("schema_version") != LAB_SCHEMA
or manifest.get("result_id") != candidate.name
or manifest.get("status")
!= "complete-quality-gate-failed-temporal-invariant-passed"
or manifest.get("completed") is not True
or manifest.get("bounded_question_accepted") is not False
or manifest.get("ground_truth") is not False
or not isinstance(manifest.get("created_at_utc"), str)
or not isinstance(identity, dict)
or manifest.get("identity_sha256") != _canonical_sha256(identity)
or not candidate.name.endswith(str(manifest.get("identity_sha256")))
or identity.get("authority") != false_authority()
or manifest.get("authority") != false_authority()
or report.get("schema_version") != REPORT_SCHEMA
or report.get("result_id") != candidate.name
or report.get("authority") != false_authority()
or report.get("decision", {}).get("quality_accepted") is not False
or report.get("decision", {}).get("temporal_invariant_passed") is not True
or catalog.get("schema_version") != CATALOG_SCHEMA
or catalog.get("result_id") != candidate.name
or catalog.get("case_count") != 16
or not isinstance(catalog.get("cases"), list)
or len(catalog["cases"]) != 16
or any(not _valid_case(item) for item in catalog["cases"])
):
raise RuntimeError("M4.8T result contract changed")
return {"root": candidate, "manifest": manifest, "report": report, "catalog": catalog}
def _valid_case(value: object) -> bool:
return (
isinstance(value, dict)
and isinstance(value.get("case_id"), str)
and CASE_ID.fullmatch(value["case_id"]) is not None
and value.get("image_id") == int(value["case_id"])
and value.get("path") == f"review/review-{value['case_id']}.jpg"
and value.get("media_type") == "image/jpeg"
and isinstance(value.get("byte_length"), int)
and value["byte_length"] > 0
and isinstance(value.get("sha256"), str)
and re.fullmatch(r"[a-f0-9]{64}", value["sha256"]) is not None
)
def _resolve_candidate(provider: RootProvider, result_id: str) -> Path:
if RESULT_ID.fullmatch(result_id) is None:
raise HTTPException(status_code=404, detail="M4.8T result not found")
root = _configured_root(provider)
if root is None:
raise HTTPException(status_code=404, detail="M4.8T result not found")
candidate = (root / result_id).resolve()
if candidate.parent != root or candidate.is_symlink() or not candidate.is_dir():
raise HTTPException(status_code=404, detail="M4.8T result not found")
return candidate
def _configured_root(provider: RootProvider) -> Path | None:
value = provider()
if value is None:
return None
candidate = value.expanduser().absolute()
if candidate.is_symlink():
return None
try:
root = candidate.resolve(strict=True)
except OSError:
return None
return root if root.is_dir() else None
def _candidates(root: Path) -> list[Path]:
return sorted(
(
item
for item in root.iterdir()
if item.is_dir() and not item.is_symlink() and RESULT_ID.fullmatch(item.name)
),
key=lambda item: item.stat().st_mtime_ns,
reverse=True,
)
def _empty_catalog(configured: bool) -> dict[str, object]:
return {
"schema_version": CATALOG_VIEW_SCHEMA,
"configured": configured,
"items": [],
"candidate_total": 0,
"invalid_total": 0,
"access": "read-only",
}
def _read_object(path: Path) -> dict[str, Any]:
value = json.loads(path.read_text("utf-8"))
if not isinstance(value, dict):
raise ValueError("JSON evidence must be an object")
return value
def _canonical_sha256(value: object) -> str:
return hashlib.sha256(
json.dumps(
value,
ensure_ascii=False,
sort_keys=True,
separators=(",", ":"),
allow_nan=False,
).encode("utf-8")
).hexdigest()
def _sha256(path: Path) -> str:
digest = hashlib.sha256()
with path.open("rb") as stream:
for chunk in iter(lambda: stream.read(1024 * 1024), b""):
digest.update(chunk)
return digest.hexdigest()