fix(lab): restore semantic overlay in M4.7

This commit is contained in:
DCCONSTRUCTIONS
2026-08-23 22:40:07 +03:00
parent 34ed8a0f5f
commit a55ef475d9
11 changed files with 289 additions and 32 deletions
@@ -360,27 +360,65 @@ function parseResult(value: unknown): E47SemanticSlamResult {
}
export async function fetchE47SemanticSlamResult({
resultId: requestedResultId,
fetcher = fetch,
signal,
}: {
resultId?: string;
fetcher?: LaboratoryFetch;
signal?: AbortSignal;
} = {}): Promise<E47SemanticSlamResult | null> {
const response = await fetcher("/api/v1/laboratory/e47-semantic-slam/results?limit=1", {
headers: { Accept: "application/json" },
signal,
});
if (requestedResultId !== undefined) resultId(requestedResultId);
let response = await fetcher(
requestedResultId === undefined
? "/api/v1/laboratory/e47-semantic-slam/results?limit=1"
: `/api/v1/laboratory/e47-semantic-slam/results/${requestedResultId}`,
{ headers: { Accept: "application/json" }, signal },
);
let catalogFallback = false;
if (requestedResultId !== undefined && response.status === 404) {
response = await fetcher("/api/v1/laboratory/e47-semantic-slam/results?limit=10", {
headers: { Accept: "application/json" },
signal,
});
catalogFallback = true;
}
if (!response.ok) {
throw new E47SemanticSlamContractError(`E47 LAB недоступен: HTTP ${response.status}.`);
}
const payload = object(await response.json(), "E47 catalog");
exact(
payload.schema_version,
"missioncore.e47-semantic-slam-catalog/v1",
"E47 catalog schema",
);
const items = array(payload.items, "E47 catalog items");
return items.length ? parseResult(items[0]) : null;
const payload = await response.json();
if (requestedResultId === undefined || catalogFallback) {
const catalog = object(payload, "E47 catalog");
exact(
catalog.schema_version,
"missioncore.e47-semantic-slam-catalog/v1",
"E47 catalog schema",
);
const items = array(catalog.items, "E47 catalog items");
if (!items.length) return null;
const selected = requestedResultId === undefined
? items[0]
: items.find((candidate) => (
typeof candidate === "object"
&& candidate !== null
&& (candidate as Record<string, unknown>).result_id === requestedResultId
));
if (selected === undefined) {
throw new E47SemanticSlamContractError(
"E47 requested result: точный semantic-result отсутствует в каталоге.",
);
}
const parsed = parseResult(selected);
if (requestedResultId !== undefined && parsed.resultId !== requestedResultId) {
throw new E47SemanticSlamContractError("E47 requested result: нарушена идентичность.");
}
return parsed;
}
const parsed = parseResult(payload);
if (parsed.resultId !== requestedResultId) {
throw new E47SemanticSlamContractError("E47 requested result: нарушена идентичность.");
}
return parsed;
}
function parseFrame(
@@ -6,6 +6,10 @@ import {
fetchM4ThreatReplayResult,
type M4ThreatReplayResult,
} from "./m4ReplayThreat";
import {
fetchE47SemanticSlamResult,
type E47SemanticSlamResult,
} from "./e47SemanticSlam";
export interface M47ReferenceGraphLabResult {
resultId: string;
@@ -41,6 +45,7 @@ export interface M47ReferenceGraphLabResult {
>>;
limitations: readonly string[];
visual: M4ThreatReplayResult;
semantic: E47SemanticSlamResult;
}
type LaboratoryFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
@@ -154,7 +159,7 @@ export async function fetchM47ReferenceGraphLab({
const report = evidence.rawReport;
exact(
report.schema_version,
"missioncore.reference-perception-graph-lab-report/v1",
"missioncore.reference-perception-graph-lab-report/v2",
"M4.7 report schema",
);
exact(report.result_id, resultId, "M4.7 report result");
@@ -176,6 +181,16 @@ export async function fetchM47ReferenceGraphLab({
true,
"M4.7 visual evidence",
);
exact(
visualEvidence.camera_semantic_overlay_available,
true,
"M4.7 camera semantic overlay",
);
exact(
visualEvidence.semantic_binding,
"exact-sequence-and-session-time",
"M4.7 semantic binding",
);
const linkedVisualResultId = text(
visualEvidence.linked_result_id,
"M4.7 linked visual result",
@@ -193,6 +208,31 @@ export async function fetchM47ReferenceGraphLab({
"M4.7 visual binding: сервер вернул другой replay result.",
);
}
const semanticResultId = text(
visualEvidence.semantic_result_id,
"M4.7 linked semantic result",
);
if (!/^e47-semantic-slam-[a-f0-9]{64}$/.test(semanticResultId)) {
throw new M47ReferenceGraphContractError(
"M4.7 semantic binding: нарушена идентичность.",
);
}
const semantic = await fetchE47SemanticSlamResult({
resultId: semanticResultId,
fetcher,
signal,
});
if (
!semantic
|| semantic.resultId !== semanticResultId
|| semantic.baseM4ResultId !== linkedVisualResultId
|| semantic.metrics.frames.total !== 4489
|| semantic.metrics.frames.maskAvailable !== 4489
) {
throw new M47ReferenceGraphContractError(
"M4.7 semantic binding: сервер вернул несвязанный semantic-result.",
);
}
const graphResultId = text(source.graph_result_id, "M4.7 graph result");
if (!/^m47-reference-graph-[a-f0-9]{64}$/.test(graphResultId)) {
throw new M47ReferenceGraphContractError("M4.7 graph result: нарушена идентичность.");
@@ -219,5 +259,6 @@ export async function fetchM47ReferenceGraphLab({
queueHighWatermarks: queueWatermarks(metrics.queue_high_watermarks),
limitations: stringArray(report.limitations, "M4.7 limitations"),
visual,
semantic,
};
}