feat(lab): publish M4.8T quality evidence
This commit is contained in:
@@ -43,11 +43,13 @@ import {
|
||||
} from "./m48ObjectCentricQuality";
|
||||
import { fetchM48SmallStaticRegression } from "./m48SmallStaticRegression";
|
||||
import { fetchM48SFixedClassDetectorResult } from "./m48sFixedClassDetector";
|
||||
import { fetchM48TRiskQualityResult } from "./m48tRiskQuality";
|
||||
|
||||
export type AdvancedLaboratoryWorkId =
|
||||
| "m48-object-centric-quality"
|
||||
| "m48-small-static-passage-regression"
|
||||
| "m48s-fixed-class-detector"
|
||||
| "m48t-risk-quality-temporal"
|
||||
| "m47-reference-graph-shadow"
|
||||
| "m4-replay-threat"
|
||||
| "l3-pointpillars-visual-audit"
|
||||
@@ -93,6 +95,7 @@ const WORK_IDS: readonly AdvancedLaboratoryWorkId[] = [
|
||||
"m48-object-centric-quality",
|
||||
"m48-small-static-passage-regression",
|
||||
"m48s-fixed-class-detector",
|
||||
"m48t-risk-quality-temporal",
|
||||
"m47-reference-graph-shadow",
|
||||
"m4-replay-threat",
|
||||
"l3-pointpillars-visual-audit",
|
||||
@@ -133,6 +136,7 @@ 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",
|
||||
"m48s-fixed-class-detector": "m48s-fixed-class-detector-lab",
|
||||
"m48t-risk-quality-temporal": "m48t-risk-quality-temporal-lab",
|
||||
"m47-reference-graph-shadow": "m47-reference-graph-lab",
|
||||
"m4-replay-threat": "m4-threat-replay",
|
||||
"l3-pointpillars-visual-audit": "l3-pointpillars-visual-audit",
|
||||
@@ -181,6 +185,7 @@ export function emptyAdvancedLaboratoryResults(): AdvancedLaboratoryResults {
|
||||
m48: null,
|
||||
m48SmallStatic: null,
|
||||
m48s: null,
|
||||
m48t: null,
|
||||
m4Threat: null,
|
||||
l3: null,
|
||||
l31: null,
|
||||
@@ -308,6 +313,7 @@ export function advancedLaboratoryResultAvailable(
|
||||
return workId === "m48-object-centric-quality" ? results.m48 !== null
|
||||
: workId === "m48-small-static-passage-regression" ? results.m48SmallStatic !== null
|
||||
: workId === "m48s-fixed-class-detector" ? results.m48s !== null
|
||||
: workId === "m48t-risk-quality-temporal" ? results.m48t !== null
|
||||
: workId === "m47-reference-graph-shadow" ? results.m47Graph !== null
|
||||
: workId === "m4-replay-threat" ? results.m4Threat !== null
|
||||
: workId === "l3-pointpillars-visual-audit" ? results.l3 !== null
|
||||
@@ -366,6 +372,9 @@ export async function fetchAdvancedLaboratoryResult(
|
||||
} else if (workId === "m48s-fixed-class-detector") {
|
||||
if (!resultId) throw new AdvancedLaboratoryContractError("M4.8S LAB identity не выбрана.");
|
||||
results.m48s = await fetchM48SFixedClassDetectorResult(resultId, { fetcher, signal });
|
||||
} else if (workId === "m48t-risk-quality-temporal") {
|
||||
if (!resultId) throw new AdvancedLaboratoryContractError("M4.8T LAB identity не выбрана.");
|
||||
results.m48t = await fetchM48TRiskQualityResult(resultId, { fetcher, signal });
|
||||
} else if (workId === "m47-reference-graph-shadow") {
|
||||
if (!resultId) {
|
||||
throw new AdvancedLaboratoryContractError("M4.7 LAB identity не выбрана.");
|
||||
|
||||
@@ -37,12 +37,14 @@ import type { M47ReferenceGraphLabResult } from "./m47ReferenceGraph";
|
||||
import type { M48AdvancedResult } from "./m48ObjectCentricQuality";
|
||||
import type { M48SmallStaticRegressionResult } from "./m48SmallStaticRegression";
|
||||
import type { M48SFixedClassDetectorResult } from "./m48sFixedClassDetector";
|
||||
import type { M48TRiskQualityResult } from "./m48tRiskQuality";
|
||||
|
||||
export interface AdvancedLaboratoryResults {
|
||||
m47Graph: M47ReferenceGraphLabResult | null;
|
||||
m48: M48AdvancedResult | null;
|
||||
m48SmallStatic: M48SmallStaticRegressionResult | null;
|
||||
m48s: M48SFixedClassDetectorResult | null;
|
||||
m48t: M48TRiskQualityResult | 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, m48: null, m48SmallStatic: null, m48s: null, m4Threat: null,
|
||||
m47Graph: null, m48: null, m48SmallStatic: null, m48s: null, m48t: null, m4Threat: null,
|
||||
l3: null, l31: null,
|
||||
l32: null,
|
||||
l33: null,
|
||||
|
||||
@@ -0,0 +1,326 @@
|
||||
export interface M48TLaboratoryMethod {
|
||||
completeness: "complete";
|
||||
executionClass: "hybrid";
|
||||
pipelineId: string;
|
||||
components: readonly {
|
||||
kind: "source" | "model" | "algorithm" | "runtime" | "tool";
|
||||
name: string;
|
||||
version: string;
|
||||
role: string;
|
||||
identitySha256: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface M48TReviewCase {
|
||||
caseId: string;
|
||||
imageId: number;
|
||||
imageUrl: string;
|
||||
byteLength: number;
|
||||
sha256: string;
|
||||
}
|
||||
|
||||
export interface M48TRiskQualityResult {
|
||||
resultId: string;
|
||||
createdAtUtc: string;
|
||||
source: {
|
||||
quality: {
|
||||
datasetId: "coco-2017-val";
|
||||
riskImages: number;
|
||||
truthInstances: number;
|
||||
independentHumanAnnotations: true;
|
||||
ravnovesGroundTruth: false;
|
||||
};
|
||||
temporal: {
|
||||
sourceId: "RAVNOVES00";
|
||||
frames: number;
|
||||
publications: number;
|
||||
independentSemanticTruthAvailable: false;
|
||||
};
|
||||
};
|
||||
candidate: {
|
||||
providerId: string;
|
||||
modelId: "rf_detr_large:1";
|
||||
minimumScore: 0.25;
|
||||
};
|
||||
execution: {
|
||||
durationSeconds: number;
|
||||
effectiveImagesPerSecond: number;
|
||||
imageP95Ms: number;
|
||||
inferenceP95Ms: number;
|
||||
timingIsAdmissionEvidence: false;
|
||||
};
|
||||
quality: {
|
||||
microPrecision: number;
|
||||
microRecall: number;
|
||||
mediumLargeRecall: number;
|
||||
emptyRiskImageFraction: number;
|
||||
families: Readonly<Record<"person" | "animal" | "light-road-user" | "vehicle", number>>;
|
||||
};
|
||||
counts: {
|
||||
truth: number;
|
||||
predictions: number;
|
||||
truePositive: number;
|
||||
falsePositive: number;
|
||||
falseNegative: number;
|
||||
};
|
||||
failures: Readonly<Record<string, number>>;
|
||||
temporal: {
|
||||
rawClassSwitches: number;
|
||||
stableClassSwitches: number;
|
||||
rawFamilySwitches: number;
|
||||
stableFamilySwitches: number;
|
||||
suppressedClassSwitches: number;
|
||||
suppressedFamilySwitches: number;
|
||||
selectedPublications: number;
|
||||
semanticObservations: number;
|
||||
peakActiveComponents: number;
|
||||
maximumActiveComponents: number;
|
||||
};
|
||||
acceptance: {
|
||||
qualityPassed: false;
|
||||
failedQualityGates: readonly [
|
||||
"minimum-micro-precision",
|
||||
"minimum-family-recall:vehicle",
|
||||
];
|
||||
temporalInvariantPassed: true;
|
||||
};
|
||||
review: {
|
||||
truthColor: "green";
|
||||
predictionColor: "yellow";
|
||||
cases: readonly M48TReviewCase[];
|
||||
};
|
||||
method: M48TLaboratoryMethod;
|
||||
limitations: readonly string[];
|
||||
}
|
||||
|
||||
type LaboratoryFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
||||
|
||||
export class M48TContractError extends Error {}
|
||||
|
||||
function object(value: unknown, label: string): Record<string, unknown> {
|
||||
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||
throw new M48TContractError(`${label}: ожидался объект.`);
|
||||
}
|
||||
return value as Record<string, unknown>;
|
||||
}
|
||||
|
||||
function array(value: unknown, label: string): readonly unknown[] {
|
||||
if (!Array.isArray(value)) throw new M48TContractError(`${label}: ожидался массив.`);
|
||||
return value;
|
||||
}
|
||||
|
||||
function text(value: unknown, label: string): string {
|
||||
if (typeof value !== "string" || !value.trim()) {
|
||||
throw new M48TContractError(`${label}: ожидалась строка.`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function number(value: unknown, label: string): number {
|
||||
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
||||
throw new M48TContractError(`${label}: ожидалось неотрицательное число.`);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
function integer(value: unknown, label: string): number {
|
||||
const parsed = number(value, label);
|
||||
if (!Number.isSafeInteger(parsed)) {
|
||||
throw new M48TContractError(`${label}: ожидалось целое число.`);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function exact<T extends string | number | boolean>(
|
||||
value: unknown,
|
||||
expected: T,
|
||||
label: string,
|
||||
): T {
|
||||
if (value !== expected) throw new M48TContractError(`${label}: нарушен контракт.`);
|
||||
return expected;
|
||||
}
|
||||
|
||||
function sha(value: unknown, label: string): string {
|
||||
const parsed = text(value, label);
|
||||
if (!/^[a-f0-9]{64}$/.test(parsed)) {
|
||||
throw new M48TContractError(`${label}: нарушена SHA-256 идентичность.`);
|
||||
}
|
||||
return parsed;
|
||||
}
|
||||
|
||||
function method(value: unknown): M48TLaboratoryMethod {
|
||||
const raw = object(value, "M4.8T method");
|
||||
exact(raw.schema_version, "missioncore.laboratory-method/v1", "M4.8T method schema");
|
||||
exact(raw.completeness, "complete", "M4.8T method completeness");
|
||||
exact(raw.execution_class, "hybrid", "M4.8T method execution");
|
||||
const allowedKinds = new Set(["source", "model", "algorithm", "runtime", "tool"]);
|
||||
return {
|
||||
completeness: "complete",
|
||||
executionClass: "hybrid",
|
||||
pipelineId: text(raw.pipeline_id, "M4.8T pipeline"),
|
||||
components: array(raw.components, "M4.8T components").map((value) => {
|
||||
const component = object(value, "M4.8T component");
|
||||
const kind = text(component.kind, "M4.8T component kind");
|
||||
if (!allowedKinds.has(kind)) throw new M48TContractError("M4.8T component kind: неизвестное значение.");
|
||||
return {
|
||||
kind: kind as "source" | "model" | "algorithm" | "runtime" | "tool",
|
||||
name: text(component.name, "M4.8T component name"),
|
||||
version: text(component.version, "M4.8T component version"),
|
||||
role: text(component.role, "M4.8T component role"),
|
||||
identitySha256: sha(component.identity_sha256, "M4.8T component identity"),
|
||||
};
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
function parseResult(value: unknown, expectedResultId: string): M48TRiskQualityResult {
|
||||
const raw = object(value, "M4.8T result");
|
||||
exact(raw.schema_version, "missioncore.m48t-risk-quality-temporal-view/v1", "M4.8T schema");
|
||||
exact(raw.result_id, expectedResultId, "M4.8T identity");
|
||||
exact(raw.status, "complete-quality-gate-failed-temporal-invariant-passed", "M4.8T status");
|
||||
exact(raw.access, "read-only", "M4.8T access");
|
||||
exact(raw.ground_truth, false, "M4.8T ground truth");
|
||||
|
||||
const source = object(raw.source, "M4.8T source");
|
||||
const qualitySource = object(source.quality, "M4.8T quality source");
|
||||
const temporalSource = object(source.temporal, "M4.8T temporal source");
|
||||
const configuration = object(raw.configuration, "M4.8T configuration");
|
||||
const candidate = object(configuration.candidate, "M4.8T candidate");
|
||||
const execution = object(raw.execution, "M4.8T execution");
|
||||
const imageTiming = object(execution.image_timing_ms, "M4.8T image timing");
|
||||
const inferenceTiming = object(execution.triton_inference_ms, "M4.8T inference timing");
|
||||
const metrics = object(raw.metrics, "M4.8T metrics");
|
||||
const quality = object(metrics.quality, "M4.8T quality metrics");
|
||||
const families = object(quality.families, "M4.8T family metrics");
|
||||
const familyRecall = (name: "person" | "animal" | "light-road-user" | "vehicle") =>
|
||||
number(object(families[name], `M4.8T ${name}`).family_recall, `M4.8T ${name} recall`);
|
||||
const counts = object(metrics.counts, "M4.8T counts");
|
||||
const failures = object(metrics.failure_buckets, "M4.8T failures");
|
||||
const temporal = object(metrics.temporal, "M4.8T temporal metrics");
|
||||
const snapshot = object(temporal.snapshot, "M4.8T temporal snapshot");
|
||||
const acceptance = object(raw.acceptance, "M4.8T acceptance");
|
||||
const qualityGate = object(acceptance.quality, "M4.8T quality gate");
|
||||
const temporalGate = object(acceptance.temporal_invariant, "M4.8T temporal gate");
|
||||
const failed = array(qualityGate.failed, "M4.8T failed gates").map((item) => text(item, "M4.8T failed gate"));
|
||||
if (failed.length !== 2 || failed[0] !== "minimum-micro-precision" || failed[1] !== "minimum-family-recall:vehicle") {
|
||||
throw new M48TContractError("M4.8T failed gates: изменён зафиксированный результат.");
|
||||
}
|
||||
exact(qualityGate.passed, false, "M4.8T quality gate");
|
||||
exact(temporalGate.passed, true, "M4.8T temporal invariant");
|
||||
exact(temporalGate.semantic_quality_accepted, false, "M4.8T temporal semantic acceptance");
|
||||
|
||||
const review = object(raw.review, "M4.8T review");
|
||||
const legend = object(review.legend, "M4.8T review legend");
|
||||
exact(legend.ground_truth, "green", "M4.8T truth legend");
|
||||
exact(legend.rf_detr_prediction, "yellow", "M4.8T prediction legend");
|
||||
const cases = array(review.cases, "M4.8T review cases").map((value) => {
|
||||
const item = object(value, "M4.8T review case");
|
||||
const caseId = text(item.case_id, "M4.8T case id");
|
||||
if (!/^[0-9]{12}$/.test(caseId)) throw new M48TContractError("M4.8T case identity нарушена.");
|
||||
exact(item.media_type, "image/jpeg", "M4.8T case media");
|
||||
return {
|
||||
caseId,
|
||||
imageId: integer(item.image_id, "M4.8T image id"),
|
||||
imageUrl: text(item.image_url, "M4.8T image URL"),
|
||||
byteLength: integer(item.byte_length, "M4.8T image bytes"),
|
||||
sha256: sha(item.sha256, "M4.8T image SHA"),
|
||||
};
|
||||
});
|
||||
if (cases.length !== 16 || new Set(cases.map(({ caseId }) => caseId)).size !== 16) {
|
||||
throw new M48TContractError("M4.8T review catalog: нарушен размер.");
|
||||
}
|
||||
|
||||
const parsedFailures: Record<string, number> = {};
|
||||
for (const [key, count] of Object.entries(failures)) parsedFailures[key] = integer(count, `M4.8T failure ${key}`);
|
||||
const temporalConfiguration = object(configuration.temporal, "M4.8T temporal configuration");
|
||||
return {
|
||||
resultId: expectedResultId,
|
||||
createdAtUtc: text(raw.created_at_utc, "M4.8T created at"),
|
||||
source: {
|
||||
quality: {
|
||||
datasetId: exact(qualitySource.dataset_id, "coco-2017-val", "M4.8T dataset"),
|
||||
riskImages: integer(qualitySource.risk_images, "M4.8T risk images"),
|
||||
truthInstances: integer(qualitySource.truth_instances, "M4.8T truth instances"),
|
||||
independentHumanAnnotations: exact(qualitySource.independent_human_annotations, true, "M4.8T independent truth"),
|
||||
ravnovesGroundTruth: exact(qualitySource.ravnoves_ground_truth, false, "M4.8T RAVNOVES truth"),
|
||||
},
|
||||
temporal: {
|
||||
sourceId: exact(temporalSource.source_id, "RAVNOVES00", "M4.8T temporal source"),
|
||||
frames: integer(temporalSource.frames, "M4.8T temporal frames"),
|
||||
publications: integer(temporalSource.publications, "M4.8T temporal publications"),
|
||||
independentSemanticTruthAvailable: exact(temporalSource.independent_semantic_truth_available, false, "M4.8T temporal truth"),
|
||||
},
|
||||
},
|
||||
candidate: {
|
||||
providerId: text(candidate.provider_id, "M4.8T provider"),
|
||||
modelId: exact(candidate.model_id, "rf_detr_large:1", "M4.8T model"),
|
||||
minimumScore: exact(candidate.minimum_score, 0.25, "M4.8T threshold"),
|
||||
},
|
||||
execution: {
|
||||
durationSeconds: number(execution.duration_seconds, "M4.8T duration"),
|
||||
effectiveImagesPerSecond: number(execution.effective_images_per_second, "M4.8T throughput"),
|
||||
imageP95Ms: number(imageTiming.p95, "M4.8T image p95"),
|
||||
inferenceP95Ms: number(inferenceTiming.p95, "M4.8T inference p95"),
|
||||
timingIsAdmissionEvidence: exact(execution.timing_is_admission_evidence, false, "M4.8T timing authority"),
|
||||
},
|
||||
quality: {
|
||||
microPrecision: number(quality.micro_precision, "M4.8T precision"),
|
||||
microRecall: number(quality.micro_recall, "M4.8T recall"),
|
||||
mediumLargeRecall: number(quality.medium_large_recall, "M4.8T medium-large recall"),
|
||||
emptyRiskImageFraction: number(quality.empty_prediction_risk_image_fraction, "M4.8T empty fraction"),
|
||||
families: {
|
||||
person: familyRecall("person"),
|
||||
animal: familyRecall("animal"),
|
||||
"light-road-user": familyRecall("light-road-user"),
|
||||
vehicle: familyRecall("vehicle"),
|
||||
},
|
||||
},
|
||||
counts: {
|
||||
truth: integer(qualitySource.truth_instances, "M4.8T truth count"),
|
||||
predictions: integer(counts.predictions, "M4.8T predictions"),
|
||||
truePositive: integer(counts.true_positive, "M4.8T TP"),
|
||||
falsePositive: integer(counts.false_positive, "M4.8T FP"),
|
||||
falseNegative: integer(counts.false_negative, "M4.8T FN"),
|
||||
},
|
||||
failures: parsedFailures,
|
||||
temporal: {
|
||||
rawClassSwitches: integer(temporal.raw_class_switches, "M4.8T raw class switches"),
|
||||
stableClassSwitches: integer(temporal.stable_class_switches, "M4.8T stable class switches"),
|
||||
rawFamilySwitches: integer(temporal.raw_family_switches, "M4.8T raw family switches"),
|
||||
stableFamilySwitches: integer(temporal.stable_family_switches, "M4.8T stable family switches"),
|
||||
suppressedClassSwitches: integer(temporal.suppressed_or_deferred_class_switches, "M4.8T suppressed class switches"),
|
||||
suppressedFamilySwitches: integer(temporal.suppressed_or_deferred_family_switches, "M4.8T suppressed family switches"),
|
||||
selectedPublications: integer(temporal.selected_publications, "M4.8T selected publications"),
|
||||
semanticObservations: integer(temporal.semantic_current_observations, "M4.8T semantic observations"),
|
||||
peakActiveComponents: integer(snapshot.peak_active_components, "M4.8T active peak"),
|
||||
maximumActiveComponents: integer(temporalConfiguration.maximum_active_components, "M4.8T active bound"),
|
||||
},
|
||||
acceptance: {
|
||||
qualityPassed: false,
|
||||
failedQualityGates: ["minimum-micro-precision", "minimum-family-recall:vehicle"],
|
||||
temporalInvariantPassed: true,
|
||||
},
|
||||
review: { truthColor: "green", predictionColor: "yellow", cases },
|
||||
method: method(raw.method),
|
||||
limitations: array(raw.limitations, "M4.8T limitations").map((item) => text(item, "M4.8T limitation")),
|
||||
};
|
||||
}
|
||||
|
||||
export async function fetchM48TRiskQualityResult(
|
||||
resultId: string,
|
||||
{
|
||||
fetcher = fetch,
|
||||
signal,
|
||||
}: { fetcher?: LaboratoryFetch; signal?: AbortSignal } = {},
|
||||
): Promise<M48TRiskQualityResult> {
|
||||
if (!/^m48t-risk-quality-temporal-lab-[a-f0-9]{64}$/.test(resultId)) {
|
||||
throw new M48TContractError("M4.8T result identity недопустима.");
|
||||
}
|
||||
const response = await fetcher(`/api/v1/laboratory/m48t/risk-quality/results/${resultId}`, {
|
||||
method: "GET",
|
||||
headers: { Accept: "application/json" },
|
||||
signal,
|
||||
});
|
||||
if (!response.ok) throw new M48TContractError(`M4.8T недоступен: HTTP ${response.status}.`);
|
||||
return parseResult(await response.json(), resultId);
|
||||
}
|
||||
@@ -45,6 +45,7 @@ import { M47ReferenceGraphResultView } from "./M47ReferenceGraphResult";
|
||||
import { M48ObjectCentricQualityResultView } from "./M48ObjectCentricQualityResult";
|
||||
import { M48SmallStaticPassageRegressionResultView } from "./M48SmallStaticPassageRegressionResult";
|
||||
import { M48SFixedClassDetectorResultView } from "./M48SFixedClassDetectorResult";
|
||||
import { M48TRiskQualityResultView } from "./M48TRiskQualityResult";
|
||||
|
||||
export { isAdvancedLaboratoryWorkId };
|
||||
export type { AdvancedLaboratoryWorkId };
|
||||
@@ -96,6 +97,9 @@ export function AdvancedLaboratoryResult({
|
||||
if (workId === "m48s-fixed-class-detector" && results.m48s) {
|
||||
return <M48SFixedClassDetectorResultView rigLabel={rigLabel} result={results.m48s} />;
|
||||
}
|
||||
if (workId === "m48t-risk-quality-temporal" && results.m48t) {
|
||||
return <M48TRiskQualityResultView rigLabel={rigLabel} result={results.m48t} />;
|
||||
}
|
||||
if (workId === "m47-reference-graph-shadow" && results.m47Graph) {
|
||||
return <M47ReferenceGraphResultView rigLabel={rigLabel} result={results.m47Graph} />;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import {
|
||||
LaboratoryEvidence,
|
||||
LaboratoryResultSummary,
|
||||
LaboratorySummary,
|
||||
LaboratoryWorkTemplate,
|
||||
} from "../../components/laboratory/LaboratoryPresentation";
|
||||
import type { M48TRiskQualityResult } from "../../core/laboratory/m48tRiskQuality";
|
||||
import { M48TRiskQualityVisual } from "./M48TRiskQualityVisual";
|
||||
|
||||
function percent(value: number, digits = 1): string {
|
||||
return `${(value * 100).toLocaleString("ru-RU", { maximumFractionDigits: digits })}%`;
|
||||
}
|
||||
|
||||
function decimal(value: number, digits = 1): string {
|
||||
return value.toLocaleString("ru-RU", { maximumFractionDigits: digits });
|
||||
}
|
||||
|
||||
export function M48TRiskQualityResultView({
|
||||
rigLabel,
|
||||
result,
|
||||
}: {
|
||||
rigLabel: string;
|
||||
result: M48TRiskQualityResult;
|
||||
}) {
|
||||
return (
|
||||
<LaboratoryWorkTemplate
|
||||
summary={(
|
||||
<LaboratorySummary
|
||||
title="M4.8T · semantic quality + temporal identity"
|
||||
description="RF-DETR-L проверен на полном независимом COCO val2017 по заранее записанным risk-family гейтам. Отдельно тот же advisory-класс стабилизирован на geometry-owned ID полного RAVNOVES00 replay; семантика не участвует в association или occupancy."
|
||||
status="Quality gate failed · temporal invariant passed"
|
||||
statusTone="warning"
|
||||
facts={[
|
||||
{ label: "Quality truth", value: `COCO val2017 · ${result.source.quality.truthInstances.toLocaleString("ru-RU")} risk instances` },
|
||||
{ label: "Candidate", value: `${result.candidate.modelId} · threshold ${decimal(result.candidate.minimumScore, 2)}` },
|
||||
{ label: "Temporal source", value: `${rigLabel} · ${result.source.temporal.frames.toLocaleString("ru-RU")} world states` },
|
||||
{ label: "Authority", value: "SHADOW ONLY · geometry-owned occupancy · production NO" },
|
||||
]}
|
||||
brief={{
|
||||
question: "Достаточно ли качественна текущая risk-семантика RF-DETR и можно ли убрать кадровое мерцание класса без влияния на геометрию?",
|
||||
approach: `Все ${result.source.quality.riskImages.toLocaleString("ru-RU")} COCO-изображений с risk-классами оценены при неизменном score ${decimal(result.candidate.minimumScore, 2)}. Затем bounded history 5 / confirm 2 / switch 3 применена только к advisory-классу уже существующих component ID.`,
|
||||
principalResult: `Recall прошёл: ${percent(result.quality.microRecall)} overall и ${percent(result.quality.mediumLargeRecall)} medium+large. Precision ${percent(result.quality.microPrecision)} и vehicle recall ${percent(result.quality.families.vehicle)} не прошли гейты. Temporal shadow сократил class switches ${result.temporal.rawClassSwitches} → ${result.temporal.stableClassSwitches} и family switches ${result.temporal.rawFamilySwitches} → ${result.temporal.stableFamilySwitches}.`,
|
||||
limitation: "COCO не является truth городского маршрута RAVNOVES00, а temporal replay не имеет независимой track/class truth. Batch timing не принимается как realtime-гейт.",
|
||||
}}
|
||||
method={result.method}
|
||||
/>
|
||||
)}
|
||||
evidence={(
|
||||
<LaboratoryEvidence
|
||||
eyebrow="M4.8T VISUAL EVIDENCE · INDEPENDENT COCO TRUTH"
|
||||
title="16 hash-bound review cases: human truth против RF-DETR"
|
||||
kind="diagnostic-model"
|
||||
resizable
|
||||
>
|
||||
<M48TRiskQualityVisual result={result} />
|
||||
</LaboratoryEvidence>
|
||||
)}
|
||||
result={(
|
||||
<LaboratoryResultSummary
|
||||
title="Temporal anti-flicker принят как инвариант; detector quality не принят"
|
||||
status="2/10 predeclared quality checks failed"
|
||||
statusTone="warning"
|
||||
metrics={[
|
||||
{ label: "Precision / recall", value: `${percent(result.quality.microPrecision)} / ${percent(result.quality.microRecall)}`, hint: "gates ≥ 80% / ≥ 75%" },
|
||||
{ label: "Vehicle family", value: percent(result.quality.families.vehicle), hint: "gate ≥ 85% · failed" },
|
||||
{ label: "TP / FP / FN", value: `${result.counts.truePositive.toLocaleString("ru-RU")} / ${result.counts.falsePositive.toLocaleString("ru-RU")} / ${result.counts.falseNegative.toLocaleString("ru-RU")}`, hint: `${result.counts.predictions.toLocaleString("ru-RU")} predictions` },
|
||||
{ label: "Class / family switches", value: `${result.temporal.rawClassSwitches}→${result.temporal.stableClassSwitches} / ${result.temporal.rawFamilySwitches}→${result.temporal.stableFamilySwitches}`, hint: `peak active ${result.temporal.peakActiveComponents}/${result.temporal.maximumActiveComponents}` },
|
||||
{ label: "Batch throughput", value: `${decimal(result.execution.effectiveImagesPerSecond, 2)} image/s`, hint: `image p95 ${decimal(result.execution.imageP95Ms, 2)} ms · non-admission` },
|
||||
]}
|
||||
conclusion={{
|
||||
proved: `На независимой COCO truth текущий RF-DETR сохраняет высокий recall: ${percent(result.quality.microRecall)} overall и ${percent(result.quality.mediumLargeRecall)} medium+large. Bounded temporal state подавил или отложил ${result.temporal.suppressedClassSwitches} class-switch и ${result.temporal.suppressedFamilySwitches} family-switch, сохранив association и occupancy class-independent; active state ${result.temporal.peakActiveComponents}/${result.temporal.maximumActiveComponents}, eviction 0.`,
|
||||
notProved: `Semantic candidate не принят: precision ${percent(result.quality.microPrecision)} при gate 80%, vehicle recall ${percent(result.quality.families.vehicle)} при gate 85%. Не доказаны RAVNOVES class truth, physical track identity, child/adult, unknown moving hazards, risk policy и realtime admission этой batch-командой.`,
|
||||
decision: "Не менять зафиксированные гейты и не добавлять второй detector. RF-DETR остаётся shadow-кандидатом; temporal stabilizer допустим только как advisory-слой поверх geometry-owned ID.",
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
import { useState } from "react";
|
||||
import { Icon, IconButton } from "@nodedc/ui-react";
|
||||
|
||||
import { LaboratoryEvidenceViewer } from "../../components/laboratory/LaboratoryEvidenceViewer";
|
||||
import type { M48TRiskQualityResult } from "../../core/laboratory/m48tRiskQuality";
|
||||
|
||||
export function M48TRiskQualityVisual({ result }: { result: M48TRiskQualityResult }) {
|
||||
const [index, setIndex] = useState(0);
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
const item = result.review.cases[index] ?? null;
|
||||
const navigate = (offset: -1 | 1) => {
|
||||
setIndex((current) => (
|
||||
current + offset + result.review.cases.length
|
||||
) % result.review.cases.length);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="l3-visual-audit">
|
||||
<LaboratoryEvidenceViewer
|
||||
label="M4.8T independent COCO quality review"
|
||||
mode="quality"
|
||||
modes={[{ value: "quality", label: "COCO TRUTH" }]}
|
||||
expanded={expanded}
|
||||
onModeChange={() => undefined}
|
||||
onExpandedChange={setExpanded}
|
||||
actions={(
|
||||
<div className="l3-visual-audit__pagination">
|
||||
<IconButton label="Предыдущий M4.8T review case" onClick={() => navigate(-1)}>
|
||||
<Icon name="chevron-left" size={16} />
|
||||
</IconButton>
|
||||
<IconButton label="Следующий M4.8T review case" onClick={() => navigate(1)}>
|
||||
<Icon name="chevron-right" size={16} />
|
||||
</IconButton>
|
||||
</div>
|
||||
)}
|
||||
overlay={item ? (
|
||||
<div className="l3-visual-audit__overlay">
|
||||
<div>
|
||||
<span>COCO val2017 · independent human truth</span>
|
||||
<strong>case {index + 1}/{result.review.cases.length} · image {item.imageId}</strong>
|
||||
<small>Зелёный — truth · жёлтый — RF-DETR prediction · score ≥ 0,25</small>
|
||||
</div>
|
||||
</div>
|
||||
) : null}
|
||||
>
|
||||
{item ? (
|
||||
<div className="l32-camera-scene">
|
||||
<img
|
||||
src={item.imageUrl}
|
||||
alt={`M4.8T COCO review image ${item.imageId}: truth and RF-DETR boxes`}
|
||||
draggable={false}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<div className="l3-visual-audit__state" role="alert">
|
||||
<Icon name="alert" size={18} />
|
||||
M4.8T visual evidence недоступно.
|
||||
</div>
|
||||
)}
|
||||
</LaboratoryEvidenceViewer>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -84,6 +84,13 @@ const KNOWN_WORKS: Readonly<Record<Exclude<LaboratoryWorkId, `session:${string}`
|
||||
experimentName: "RAVNOVES00 fixed-class risk detector",
|
||||
variantName: "M4.8S · RF-DETR-L TensorRT/Triton shadow",
|
||||
},
|
||||
"m48t-risk-quality-temporal": {
|
||||
profileId: "rig-ravnoves-perception-gate-v1",
|
||||
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · COCO quality + bounded temporal identity`,
|
||||
experimentId: "m48t-risk-quality-temporal",
|
||||
experimentName: "RF-DETR independent semantic quality and temporal identity",
|
||||
variantName: "M4.8T · COCO val2017 truth + RAVNOVES00 temporal shadow",
|
||||
},
|
||||
"m47-reference-graph-shadow": {
|
||||
profileId: "rig-dual-evidence-virtual-corridor-v1",
|
||||
profileName: (rigLabel) => `${rig(rigLabel)} RIGHT · Camera + LiDAR dual evidence`,
|
||||
|
||||
@@ -22,6 +22,7 @@ function mergeResults(
|
||||
m48: next.m48 ?? current.m48,
|
||||
m48SmallStatic: next.m48SmallStatic ?? current.m48SmallStatic,
|
||||
m48s: next.m48s ?? current.m48s,
|
||||
m48t: next.m48t ?? current.m48t,
|
||||
m4Threat: next.m4Threat ?? current.m4Threat,
|
||||
l3: next.l3 ?? current.l3,
|
||||
l31: next.l31 ?? current.l31,
|
||||
|
||||
Reference in New Issue
Block a user