feat(ui): stabilize shared M4.8 review viewer

This commit is contained in:
DCCONSTRUCTIONS
2026-08-24 22:36:08 +03:00
parent 992c5a8b74
commit 4fa6597b18
44 changed files with 6463 additions and 147 deletions
@@ -38,8 +38,14 @@ import { fetchE46JRawFisheyeRealtime } from "./e46jRawFisheyeRealtime";
import { fetchE47SemanticSlamResult } from "./e47SemanticSlam";
import { fetchM4ThreatReplayResult } from "./m4ReplayThreat";
import { fetchM47ReferenceGraphLab } from "./m47ReferenceGraph";
import {
fetchM48LifecycleResult,
} from "./m48ObjectCentricQuality";
import { fetchM48SmallStaticRegression } from "./m48SmallStaticRegression";
export type AdvancedLaboratoryWorkId =
| "m48-object-centric-quality"
| "m48-small-static-passage-regression"
| "m47-reference-graph-shadow"
| "m4-replay-threat"
| "l3-pointpillars-visual-audit"
@@ -82,6 +88,8 @@ export interface AdvancedLaboratoryIndexItem {
}
const WORK_IDS: readonly AdvancedLaboratoryWorkId[] = [
"m48-object-centric-quality",
"m48-small-static-passage-regression",
"m47-reference-graph-shadow",
"m4-replay-threat",
"l3-pointpillars-visual-audit",
@@ -119,6 +127,8 @@ const WORK_IDS: readonly AdvancedLaboratoryWorkId[] = [
];
const RESULT_PREFIX: Readonly<Record<AdvancedLaboratoryWorkId, string>> = {
"m48-object-centric-quality": "m48-object-quality-(?:pack|result)",
"m48-small-static-passage-regression": "m48-small-static-passage-regression",
"m47-reference-graph-shadow": "m47-reference-graph-lab",
"m4-replay-threat": "m4-threat-replay",
"l3-pointpillars-visual-audit": "l3-pointpillars-visual-audit",
@@ -164,6 +174,8 @@ export function isAdvancedLaboratoryWorkId(
export function emptyAdvancedLaboratoryResults(): AdvancedLaboratoryResults {
return {
m47Graph: null,
m48: null,
m48SmallStatic: null,
m4Threat: null,
l3: null,
l31: null,
@@ -288,7 +300,9 @@ export function advancedLaboratoryResultAvailable(
workId: AdvancedLaboratoryWorkId,
results: AdvancedLaboratoryResults,
): boolean {
return workId === "m47-reference-graph-shadow" ? results.m47Graph !== null
return workId === "m48-object-centric-quality" ? results.m48 !== null
: workId === "m48-small-static-passage-regression" ? results.m48SmallStatic !== null
: workId === "m47-reference-graph-shadow" ? results.m47Graph !== null
: workId === "m4-replay-threat" ? results.m4Threat !== null
: workId === "l3-pointpillars-visual-audit" ? results.l3 !== null
: workId === "l31-pointpillars-ravnoves" ? results.l31 !== null
@@ -337,7 +351,13 @@ export async function fetchAdvancedLaboratoryResult(
} = {},
): Promise<AdvancedLaboratoryResults> {
const results = emptyAdvancedLaboratoryResults();
if (workId === "m47-reference-graph-shadow") {
if (workId === "m48-object-centric-quality") {
if (!resultId) throw new AdvancedLaboratoryContractError("M4.8 lifecycle evidence identity не выбрана.");
results.m48 = await fetchM48LifecycleResult(resultId, { fetcher, signal });
} else if (workId === "m48-small-static-passage-regression") {
if (!resultId) throw new AdvancedLaboratoryContractError("M4.8R1 regression identity не выбрана.");
results.m48SmallStatic = await fetchM48SmallStaticRegression(resultId, { fetcher, signal });
} else if (workId === "m47-reference-graph-shadow") {
if (!resultId) {
throw new AdvancedLaboratoryContractError("M4.7 LAB identity не выбрана.");
}
@@ -34,9 +34,13 @@ import type { E46JRawFisheyeRealtimeResult } from "./e46jRawFisheyeRealtime";
import type { E47SemanticSlamResult } from "./e47SemanticSlam";
import type { M4ThreatReplayResult } from "./m4ReplayThreat";
import type { M47ReferenceGraphLabResult } from "./m47ReferenceGraph";
import type { M48AdvancedResult } from "./m48ObjectCentricQuality";
import type { M48SmallStaticRegressionResult } from "./m48SmallStaticRegression";
export interface AdvancedLaboratoryResults {
m47Graph: M47ReferenceGraphLabResult | null;
m48: M48AdvancedResult | null;
m48SmallStatic: M48SmallStaticRegressionResult | null;
m4Threat: M4ThreatReplayResult | null;
l3: L3PointPillarsVisualAuditResult | null;
l31: L31PointPillarsRavnovesResult | null;
@@ -967,7 +967,7 @@ export async function fetchAdvancedLaboratoryResults({
const e39 = settledCatalogValue(settled[7]);
const e40 = settledCatalogValue(settled[8]);
return {
m47Graph: null, m4Threat: null,
m47Graph: null, m48: null, m48SmallStatic: null, m4Threat: null,
l3: null, l31: null,
l32: null,
l33: null,
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,364 @@
import type { LaboratoryFetch } from "./advancedResults";
import type {
M48Authority,
M48Freshness,
M48GeometryAssociation,
M48Motion,
M48Threat,
M48Visibility,
} from "./m48ObjectCentricQuality";
const RESULT_ID = /^m48-small-static-passage-regression-[a-f0-9]{64}$/;
const PACK_ID = /^m48-object-quality-pack-[a-f0-9]{64}$/;
const ANCHOR_ID = /^anchor-[a-f0-9]{24}$/;
const SAFE_URL = /^\/api\/v1\/laboratory\/m48\/[A-Za-z0-9/_?&=.%:-]+$/;
export interface M48SmallStaticRegressionMetrics {
assistedAnchorCount: number;
assistedTrackletCount: number;
anchorClipCount: number;
requiresAvoidanceOrClearanceCount: number;
workerRecalledAnchorCount: number;
workerMissedAnchorCount: number;
assistedAnchorRecall: number;
extentIouThreshold: number;
minimumAssistedAnchorRecall: number;
}
export interface M48SmallStaticRegressionResult {
resultId: string;
packId: string;
createdAtUtc: string;
runLabel: "M4.8R1";
pipelineId: "m48-class-free-object-quality/v1";
experimentId: "m48-small-static-passage-regression/v1";
accepted: boolean;
metrics: M48SmallStaticRegressionMetrics;
gates: {
anchorSetNonEmpty: boolean;
developmentAnchorRecallTarget: boolean;
independentTruthAvailable: false;
};
decision: {
state: "accepted-development-regression-baseline" | "failed-development-regression-baseline";
summary: string;
nextAction: string;
};
groundTruth: false;
independentTruth: false;
authority: M48Authority;
}
export interface M48SmallStaticRegressionCaseSummary {
anchorId: string;
clipId: string;
sequence: number;
requiresAvoidanceOrClearance: boolean;
workerCandidateCount: number;
bestIou: number;
matchedAtThreshold: boolean;
outcome: "recalled" | "missed-assisted-anchor";
}
export interface M48SmallStaticRegressionObject {
predictionId: string;
extentXyxy: readonly [number, number, number, number];
geometryAssociation: M48GeometryAssociation;
freshness: M48Freshness;
motion: M48Motion;
threat: M48Threat;
}
export interface M48SmallStaticRegressionCase {
resultId: string;
packId: string;
anchor: {
anchorId: string;
clipId: string;
objectId: string;
sequence: number;
extentXyxy: readonly [number, number, number, number];
visibility: M48Visibility;
geometryAssociation: M48GeometryAssociation;
freshness: M48Freshness;
motion: M48Motion;
threat: M48Threat;
requiresAvoidanceOrClearance: boolean;
};
comparison: M48SmallStaticRegressionCaseSummary & {
sourceTimeNs: number;
anchorExtentXyxy: readonly [number, number, number, number];
workerObjects: readonly M48SmallStaticRegressionObject[];
bestPredictionId: string | null;
extentIouThreshold: number;
};
cameraUrl: string | null;
spatialUrl: string | null;
groundTruth: false;
authority: M48Authority;
}
export class M48SmallStaticRegressionContractError extends Error {}
function objectValue(value: unknown, label: string): Record<string, unknown> {
if (!value || typeof value !== "object" || Array.isArray(value)) {
throw new M48SmallStaticRegressionContractError(`${label}: ожидался объект.`);
}
return value as Record<string, unknown>;
}
function arrayValue(value: unknown, label: string): readonly unknown[] {
if (!Array.isArray(value)) {
throw new M48SmallStaticRegressionContractError(`${label}: ожидался список.`);
}
return value;
}
function exact(value: unknown, expected: string | boolean, label: string): void {
if (value !== expected) {
throw new M48SmallStaticRegressionContractError(`${label}: нарушен контракт.`);
}
}
function textValue(value: unknown, label: string): string {
if (typeof value !== "string" || !value.trim()) {
throw new M48SmallStaticRegressionContractError(`${label}: ожидалась строка.`);
}
return value;
}
function numberValue(value: unknown, label: string): number {
if (typeof value !== "number" || !Number.isFinite(value)) {
throw new M48SmallStaticRegressionContractError(`${label}: ожидалось число.`);
}
return value;
}
function integerValue(value: unknown, label: string): number {
const result = numberValue(value, label);
if (!Number.isInteger(result) || result < 0) {
throw new M48SmallStaticRegressionContractError(`${label}: ожидалось целое число.`);
}
return result;
}
function booleanValue(value: unknown, label: string): boolean {
if (typeof value !== "boolean") {
throw new M48SmallStaticRegressionContractError(`${label}: ожидался флаг.`);
}
return value;
}
function extentValue(
value: unknown,
label: string,
): readonly [number, number, number, number] {
const rows = arrayValue(value, label).map((item, index) => numberValue(item, `${label}[${index}]`));
if (
rows.length !== 4
|| rows.some((item) => item < 0 || item > 1)
|| rows[0]! >= rows[2]!
|| rows[1]! >= rows[3]!
) {
throw new M48SmallStaticRegressionContractError(`${label}: рамка недопустима.`);
}
return rows as unknown as readonly [number, number, number, number];
}
function authorityValue(value: unknown): M48Authority {
const authority = objectValue(value, "M4.8R1.authority");
exact(authority.mode, "replay-simulated", "M4.8R1.authority.mode");
exact(authority.physical_live, false, "M4.8R1.authority.physical_live");
exact(authority.commands_enabled, false, "M4.8R1.authority.commands_enabled");
exact(authority.actuation_allowed, false, "M4.8R1.authority.actuation_allowed");
exact(authority.navigation_or_safety_accepted, false, "M4.8R1.authority.navigation_or_safety_accepted");
return {
mode: "replay-simulated",
physicalLive: false,
commandsEnabled: false,
actuationAllowed: false,
navigationOrSafetyAccepted: false,
};
}
function enumValue<T extends string>(value: unknown, allowed: readonly T[], label: string): T {
if (typeof value !== "string" || !allowed.includes(value as T)) {
throw new M48SmallStaticRegressionContractError(`${label}: неизвестное значение.`);
}
return value as T;
}
function parseSummary(value: unknown): M48SmallStaticRegressionCaseSummary {
const row = objectValue(value, "M4.8R1.case");
const anchorId = textValue(row.anchor_id, "M4.8R1.case.anchor_id");
if (!ANCHOR_ID.test(anchorId)) {
throw new M48SmallStaticRegressionContractError("M4.8R1.case.anchor_id: нарушена идентичность.");
}
return {
anchorId,
clipId: textValue(row.clip_id, "M4.8R1.case.clip_id"),
sequence: integerValue(row.sequence, "M4.8R1.case.sequence"),
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"),
matchedAtThreshold: booleanValue(row.matched_at_threshold, "M4.8R1.case.matched_at_threshold"),
outcome: enumValue(row.outcome, ["recalled", "missed-assisted-anchor"] as const, "M4.8R1.case.outcome"),
};
}
export async function fetchM48SmallStaticRegression(
resultId: string,
{ fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<M48SmallStaticRegressionResult> {
if (!RESULT_ID.test(resultId)) {
throw new M48SmallStaticRegressionContractError("M4.8R1 result identity недопустима.");
}
const response = await fetcher(`/api/v1/laboratory/m48/regressions/small-static/${encodeURIComponent(resultId)}`, {
method: "GET",
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) throw new M48SmallStaticRegressionContractError(`M4.8R1 недоступен: HTTP ${response.status}.`);
const payload = objectValue(await response.json(), "M4.8R1");
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 metrics = objectValue(payload.metrics, "M4.8R1.metrics");
const gates = objectValue(payload.gates, "M4.8R1.gates");
const decision = objectValue(payload.decision, "M4.8R1.decision");
exact(payload.run_label, "M4.8R1", "M4.8R1.run_label");
exact(payload.pipeline_id, "m48-class-free-object-quality/v1", "M4.8R1.pipeline_id");
exact(payload.experiment_id, "m48-small-static-passage-regression/v1", "M4.8R1.experiment_id");
exact(payload.ground_truth, false, "M4.8R1.ground_truth");
exact(payload.independent_truth, false, "M4.8R1.independent_truth");
exact(gates.independent_truth_available, false, "M4.8R1.gates.independent_truth_available");
return {
resultId,
packId,
createdAtUtc: textValue(payload.created_at_utc, "M4.8R1.created_at_utc"),
runLabel: "M4.8R1",
pipelineId: "m48-class-free-object-quality/v1",
experimentId: "m48-small-static-passage-regression/v1",
accepted: booleanValue(payload.accepted, "M4.8R1.accepted"),
metrics: {
assistedAnchorCount: integerValue(metrics.assisted_anchor_count, "M4.8R1.metrics.assisted_anchor_count"),
assistedTrackletCount: integerValue(metrics.assisted_tracklet_count, "M4.8R1.metrics.assisted_tracklet_count"),
anchorClipCount: integerValue(metrics.anchor_clip_count, "M4.8R1.metrics.anchor_clip_count"),
requiresAvoidanceOrClearanceCount: integerValue(metrics.requires_avoidance_or_clearance_count, "M4.8R1.metrics.requires_avoidance_or_clearance_count"),
workerRecalledAnchorCount: integerValue(metrics.worker_recalled_anchor_count, "M4.8R1.metrics.worker_recalled_anchor_count"),
workerMissedAnchorCount: integerValue(metrics.worker_missed_anchor_count, "M4.8R1.metrics.worker_missed_anchor_count"),
assistedAnchorRecall: numberValue(metrics.assisted_anchor_recall, "M4.8R1.metrics.assisted_anchor_recall"),
extentIouThreshold: numberValue(metrics.extent_iou_threshold, "M4.8R1.metrics.extent_iou_threshold"),
minimumAssistedAnchorRecall: numberValue(metrics.minimum_assisted_anchor_recall, "M4.8R1.metrics.minimum_assisted_anchor_recall"),
},
gates: {
anchorSetNonEmpty: booleanValue(gates.anchor_set_non_empty, "M4.8R1.gates.anchor_set_non_empty"),
developmentAnchorRecallTarget: booleanValue(gates.development_anchor_recall_target, "M4.8R1.gates.development_anchor_recall_target"),
independentTruthAvailable: false,
},
decision: {
state: enumValue(decision.state, ["accepted-development-regression-baseline", "failed-development-regression-baseline"] as const, "M4.8R1.decision.state"),
summary: textValue(decision.summary, "M4.8R1.decision.summary"),
nextAction: textValue(decision.next_action, "M4.8R1.decision.next_action"),
},
groundTruth: false,
independentTruth: false,
authority: authorityValue(payload.authority),
};
}
export async function fetchM48SmallStaticRegressionCases(
resultId: string,
{ fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<readonly M48SmallStaticRegressionCaseSummary[]> {
if (!RESULT_ID.test(resultId)) throw new M48SmallStaticRegressionContractError("M4.8R1 result identity недопустима.");
const response = await fetcher(`/api/v1/laboratory/m48/regressions/small-static/${encodeURIComponent(resultId)}/cases`, {
method: "GET",
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) throw new M48SmallStaticRegressionContractError(`M4.8R1 cases недоступны: HTTP ${response.status}.`);
const payload = objectValue(await response.json(), "M4.8R1 cases");
exact(payload.schema_version, "missioncore.m48-small-static-passage-regression-case-catalog/v1", "M4.8R1 cases.schema_version");
const cases = arrayValue(payload.cases, "M4.8R1 cases.items").map(parseSummary);
if (integerValue(payload.case_count, "M4.8R1 cases.case_count") !== cases.length) {
throw new M48SmallStaticRegressionContractError("M4.8R1 cases: размер изменился.");
}
exact(payload.ground_truth, false, "M4.8R1 cases.ground_truth");
authorityValue(payload.authority);
return cases;
}
export async function fetchM48SmallStaticRegressionCase(
resultId: string,
anchorId: string,
{ fetcher = fetch, signal }: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
): Promise<M48SmallStaticRegressionCase> {
if (!RESULT_ID.test(resultId) || !ANCHOR_ID.test(anchorId)) {
throw new M48SmallStaticRegressionContractError("M4.8R1 case identity недопустима.");
}
const response = await fetcher(`/api/v1/laboratory/m48/regressions/small-static/${encodeURIComponent(resultId)}/cases/${encodeURIComponent(anchorId)}`, {
method: "GET",
headers: { Accept: "application/json" },
signal,
});
if (!response.ok) throw new M48SmallStaticRegressionContractError(`M4.8R1 case недоступен: HTTP ${response.status}.`);
const payload = objectValue(await response.json(), "M4.8R1 case");
exact(payload.schema_version, "missioncore.m48-small-static-passage-regression-case-view/v1", "M4.8R1 case.schema_version");
const anchor = objectValue(payload.anchor, "M4.8R1 case.anchor");
const comparison = objectValue(payload.comparison, "M4.8R1 case.comparison");
const summary = parseSummary(comparison);
const workerObjects = arrayValue(comparison.worker_objects, "M4.8R1 case.worker_objects").map((raw) => {
const object = objectValue(raw, "M4.8R1 worker object");
return {
predictionId: textValue(object.prediction_id, "M4.8R1 worker object.prediction_id"),
extentXyxy: extentValue(object.extent_xyxy, "M4.8R1 worker object.extent_xyxy"),
geometryAssociation: enumValue(object.geometry_association, ["associated", "unavailable", "ineligible", "unknown"] as const, "M4.8R1 worker object.geometry_association"),
freshness: enumValue(object.freshness, ["current", "held", "stale", "unavailable"] as const, "M4.8R1 worker object.freshness"),
motion: enumValue(object.motion, ["moving", "static", "unknown", "unsupported"] as const, "M4.8R1 worker object.motion"),
threat: enumValue(object.threat, ["threat", "not-threat", "unknown"] as const, "M4.8R1 worker object.threat"),
};
});
const cameraUrl = payload.camera_url === null ? null : textValue(payload.camera_url, "M4.8R1 case.camera_url");
const spatialUrl = payload.spatial_url === null ? null : textValue(payload.spatial_url, "M4.8R1 case.spatial_url");
if ((cameraUrl && !SAFE_URL.test(cameraUrl)) || (spatialUrl && !SAFE_URL.test(spatialUrl))) {
throw new M48SmallStaticRegressionContractError("M4.8R1 case evidence URL недопустим.");
}
exact(payload.ground_truth, false, "M4.8R1 case.ground_truth");
const packId = textValue(payload.pack_id, "M4.8R1 case.pack_id");
if (!PACK_ID.test(packId)) {
throw new M48SmallStaticRegressionContractError("M4.8R1 case.pack_id: нарушена идентичность.");
}
if (textValue(anchor.anchor_id, "M4.8R1 case.anchor.anchor_id") !== anchorId) {
throw new M48SmallStaticRegressionContractError("M4.8R1 case.anchor_id: ответ не соответствует запросу.");
}
return {
resultId,
packId,
anchor: {
anchorId: textValue(anchor.anchor_id, "M4.8R1 case.anchor.anchor_id"),
clipId: textValue(anchor.clip_id, "M4.8R1 case.anchor.clip_id"),
objectId: textValue(anchor.object_id, "M4.8R1 case.anchor.object_id"),
sequence: integerValue(anchor.sequence, "M4.8R1 case.anchor.sequence"),
extentXyxy: extentValue(anchor.extent_xyxy, "M4.8R1 case.anchor.extent_xyxy"),
visibility: enumValue(anchor.visibility, ["visible", "partial", "occluded"] as const, "M4.8R1 case.anchor.visibility"),
geometryAssociation: enumValue(anchor.geometry_association, ["associated", "unavailable", "ineligible", "unknown"] as const, "M4.8R1 case.anchor.geometry_association"),
freshness: enumValue(anchor.freshness, ["current", "held", "stale", "unavailable"] as const, "M4.8R1 case.anchor.freshness"),
motion: enumValue(anchor.motion, ["moving", "static", "unknown", "unsupported"] as const, "M4.8R1 case.anchor.motion"),
threat: enumValue(anchor.threat, ["threat", "not-threat", "unknown"] as const, "M4.8R1 case.anchor.threat"),
requiresAvoidanceOrClearance: booleanValue(anchor.requires_avoidance_or_clearance, "M4.8R1 case.anchor.requires_avoidance_or_clearance"),
},
comparison: {
...summary,
sourceTimeNs: integerValue(comparison.source_time_ns, "M4.8R1 case.comparison.source_time_ns"),
anchorExtentXyxy: extentValue(comparison.anchor_extent_xyxy, "M4.8R1 case.comparison.anchor_extent_xyxy"),
workerObjects,
bestPredictionId: comparison.best_prediction_id === null ? null : textValue(comparison.best_prediction_id, "M4.8R1 case.comparison.best_prediction_id"),
extentIouThreshold: numberValue(comparison.extent_iou_threshold, "M4.8R1 case.comparison.extent_iou_threshold"),
},
cameraUrl,
spatialUrl,
groundTruth: false,
authority: authorityValue(payload.authority),
};
}