feat(lab): bind M4.8 regression to reference graph
This commit is contained in:
@@ -10,6 +10,7 @@ import type {
|
||||
|
||||
const RESULT_ID = /^m48-small-static-passage-regression-[a-f0-9]{64}$/;
|
||||
const PACK_ID = /^m48-object-quality-pack-[a-f0-9]{64}$/;
|
||||
const REFERENCE_GRAPH_LAB_RESULT_ID = /^m47-reference-graph-lab-[a-f0-9]{64}$/;
|
||||
const ANCHOR_ID = /^anchor-[a-f0-9]{24}$/;
|
||||
const SAFE_URL = /^\/api\/v1\/laboratory\/m48\/[A-Za-z0-9/_?&=.%:-]+$/;
|
||||
|
||||
@@ -28,6 +29,7 @@ export interface M48SmallStaticRegressionMetrics {
|
||||
export interface M48SmallStaticRegressionResult {
|
||||
resultId: string;
|
||||
packId: string;
|
||||
referenceGraphLabResultId: string;
|
||||
createdAtUtc: string;
|
||||
runLabel: "M4.8R1";
|
||||
pipelineId: "m48-class-free-object-quality/v1";
|
||||
@@ -53,6 +55,7 @@ export interface M48SmallStaticRegressionCaseSummary {
|
||||
anchorId: string;
|
||||
clipId: string;
|
||||
sequence: number;
|
||||
anchorExtentXyxy: readonly [number, number, number, number];
|
||||
requiresAvoidanceOrClearance: boolean;
|
||||
workerCandidateCount: number;
|
||||
bestIou: number;
|
||||
@@ -198,6 +201,7 @@ function parseSummary(value: unknown): M48SmallStaticRegressionCaseSummary {
|
||||
anchorId,
|
||||
clipId: textValue(row.clip_id, "M4.8R1.case.clip_id"),
|
||||
sequence: integerValue(row.sequence, "M4.8R1.case.sequence"),
|
||||
anchorExtentXyxy: extentValue(row.anchor_extent_xyxy, "M4.8R1.case.anchor_extent_xyxy"),
|
||||
requiresAvoidanceOrClearance: booleanValue(row.requires_avoidance_or_clearance, "M4.8R1.case.requires_avoidance_or_clearance"),
|
||||
workerCandidateCount: integerValue(row.worker_candidate_count, "M4.8R1.case.worker_candidate_count"),
|
||||
bestIou: numberValue(row.best_iou, "M4.8R1.case.best_iou"),
|
||||
@@ -223,6 +227,15 @@ export async function fetchM48SmallStaticRegression(
|
||||
exact(payload.schema_version, "missioncore.m48-small-static-passage-regression-result-view/v1", "M4.8R1.schema_version");
|
||||
const packId = textValue(payload.pack_id, "M4.8R1.pack_id");
|
||||
if (!PACK_ID.test(packId)) throw new M48SmallStaticRegressionContractError("M4.8R1.pack_id: нарушена идентичность.");
|
||||
const referenceGraphLabResultId = textValue(
|
||||
payload.reference_graph_lab_result_id,
|
||||
"M4.8R1.reference_graph_lab_result_id",
|
||||
);
|
||||
if (!REFERENCE_GRAPH_LAB_RESULT_ID.test(referenceGraphLabResultId)) {
|
||||
throw new M48SmallStaticRegressionContractError(
|
||||
"M4.8R1.reference_graph_lab_result_id: нарушена идентичность.",
|
||||
);
|
||||
}
|
||||
const metrics = objectValue(payload.metrics, "M4.8R1.metrics");
|
||||
const gates = objectValue(payload.gates, "M4.8R1.gates");
|
||||
const decision = objectValue(payload.decision, "M4.8R1.decision");
|
||||
@@ -235,6 +248,7 @@ export async function fetchM48SmallStaticRegression(
|
||||
return {
|
||||
resultId,
|
||||
packId,
|
||||
referenceGraphLabResultId,
|
||||
createdAtUtc: textValue(payload.created_at_utc, "M4.8R1.created_at_utc"),
|
||||
runLabel: "M4.8R1",
|
||||
pipelineId: "m48-class-free-object-quality/v1",
|
||||
|
||||
@@ -57,6 +57,7 @@ _SMALL_STATIC_RESULT_ID = re.compile(
|
||||
r"^m48-small-static-passage-regression-[a-f0-9]{64}$"
|
||||
)
|
||||
_SMALL_STATIC_ANCHOR_ID = re.compile(r"^anchor-[a-f0-9]{24}$")
|
||||
_M47_LAB_RESULT_ID = re.compile(r"^m47-reference-graph-lab-[a-f0-9]{64}$")
|
||||
_FAILURE_ID = re.compile(r"^m48-failure-[a-f0-9]{64}$")
|
||||
_REVIEW_SESSION_ID = re.compile(r"^m48-review-session-[a-f0-9]{64}$")
|
||||
_CORRECTION_SESSION_ID = re.compile(r"^m48-correction-session-[a-f0-9]{64}$")
|
||||
@@ -1478,6 +1479,17 @@ def build_m48_object_quality_router(
|
||||
metrics = _object(result.report.get("metrics"), "M4.8 regression metrics")
|
||||
gates = _object(result.report.get("gates"), "M4.8 regression gates")
|
||||
decision = _object(result.report.get("decision"), "M4.8 regression decision")
|
||||
pack_identity = _object(pack.manifest.get("identity"), "M4.8 pack identity")
|
||||
pack_source = _object(pack_identity.get("source"), "M4.8 pack source")
|
||||
reference_graph_lab_result_id = pack_source.get("m47_lab_result_id")
|
||||
if (
|
||||
not isinstance(reference_graph_lab_result_id, str)
|
||||
or _M47_LAB_RESULT_ID.fullmatch(reference_graph_lab_result_id) is None
|
||||
):
|
||||
raise HTTPException(
|
||||
status_code=409,
|
||||
detail="M4.8 regression lost its canonical reference graph binding",
|
||||
)
|
||||
return {
|
||||
"schema_version": _SMALL_STATIC_RESULT_VIEW_SCHEMA,
|
||||
"result_id": result.result_id,
|
||||
@@ -1486,6 +1498,7 @@ def build_m48_object_quality_router(
|
||||
"run_label": result.manifest["identity"].get("run_label"),
|
||||
"pipeline_id": result.manifest["identity"].get("pipeline_id"),
|
||||
"experiment_id": result.manifest["identity"].get("experiment_id"),
|
||||
"reference_graph_lab_result_id": reference_graph_lab_result_id,
|
||||
"accepted": result.manifest.get("accepted"),
|
||||
"metrics": copy.deepcopy(metrics),
|
||||
"gates": copy.deepcopy(gates),
|
||||
@@ -1513,6 +1526,7 @@ def build_m48_object_quality_router(
|
||||
"anchor_id": row["anchor_id"],
|
||||
"clip_id": row["clip_id"],
|
||||
"sequence": row["sequence"],
|
||||
"anchor_extent_xyxy": copy.deepcopy(row["anchor_extent_xyxy"]),
|
||||
"requires_avoidance_or_clearance": row[
|
||||
"requires_avoidance_or_clearance"
|
||||
],
|
||||
|
||||
@@ -92,7 +92,10 @@ def _fixture(
|
||||
"identity_sha256": "a" * 64,
|
||||
"created_at_utc": "2026-08-23T20:00:00Z",
|
||||
"identity": {
|
||||
"source": {"source_session_id": "recorded-session"},
|
||||
"source": {
|
||||
"source_session_id": "recorded-session",
|
||||
"m47_lab_result_id": f"m47-reference-graph-lab-{'c' * 64}",
|
||||
},
|
||||
"freeze": {"prediction_rows_sha256": "8" * 64},
|
||||
},
|
||||
},
|
||||
@@ -1101,6 +1104,9 @@ def test_m48_small_static_regression_is_separate_read_only_assisted_evidence(
|
||||
assert summary.json()["experiment_id"] == (
|
||||
"m48-small-static-passage-regression/v1"
|
||||
)
|
||||
assert summary.json()["reference_graph_lab_result_id"] == (
|
||||
f"m47-reference-graph-lab-{'c' * 64}"
|
||||
)
|
||||
assert summary.json()["independent_truth"] is False
|
||||
assert summary.json()["metrics"]["worker_missed_anchor_count"] == 1
|
||||
|
||||
@@ -1110,6 +1116,7 @@ def test_m48_small_static_regression_is_separate_read_only_assisted_evidence(
|
||||
assert catalog.status_code == 200
|
||||
assert catalog.json()["case_count"] == 1
|
||||
assert catalog.json()["cases"][0]["outcome"] == "missed-assisted-anchor"
|
||||
assert catalog.json()["cases"][0]["anchor_extent_xyxy"] == [0.2, 0.2, 0.3, 0.4]
|
||||
|
||||
case = client.get(
|
||||
f"/api/v1/laboratory/m48/regressions/small-static/{result_id}/cases/{anchor_id}"
|
||||
|
||||
Reference in New Issue
Block a user