Document AI Inference architecture and freeze current baseline

This commit is contained in:
DCCONSTRUCTIONS
2026-09-05 10:05:01 +03:00
parent e56eb42507
commit 57f2537af3
19 changed files with 5280 additions and 53 deletions
@@ -53,7 +53,7 @@ export const LABORATORY_REPORT_TEMPLATE_VERSION =
const EXECUTION_LABELS: Record<LaboratoryExecutionClass, string> = {
deterministic: "Детерминированный",
"ai-inference": "AI inference",
"ai-inference": "AI Inference",
hybrid: "Гибридный",
};
@@ -177,7 +177,7 @@ export async function renameAICompositionRunProjection(
displayName: string,
): Promise<string> {
if (!/^ai-composition-[a-f0-9]{64}$/.test(runId)) {
throw new Error("Некорректная идентичность результата AI inference.");
throw new Error("Некорректная идентичность результата AI Inference.");
}
const normalized = displayName.trim();
if (!normalized || normalized.length > 160) {
@@ -193,19 +193,19 @@ export async function renameAICompositionRunProjection(
});
const body = await bodyOf(response);
if (!response.ok) throw apiError(body, response.status);
const row = record(body, "результат AI inference");
const row = record(body, "результат AI Inference");
exactKeys(row, ["display_name", "run_id", "schema_version"]);
if (
row.schema_version !== "missioncore.observatory-ai-composition-run-projection/v1"
|| row.run_id !== runId
|| row.display_name !== normalized
) throw new Error("Сервер не подтвердил переименование результата AI inference.");
) throw new Error("Сервер не подтвердил переименование результата AI Inference.");
return normalized;
}
export async function deleteAICompositionRunProjection(runId: string): Promise<void> {
if (!/^ai-composition-[a-f0-9]{64}$/.test(runId)) {
throw new Error("Некорректная идентичность результата AI inference.");
throw new Error("Некорректная идентичность результата AI Inference.");
}
const response = await fetch(`/api/v1/observatory/ai-composition-runs/${encodeURIComponent(runId)}`, {
method: "DELETE",
@@ -96,7 +96,7 @@ export async function deleteObservatoryLabProjection(
headers: { Accept: "application/json" },
signal,
},
"Не удалось удалить лабораторный результат из Обсерватории.",
"Не удалось удалить лабораторный результат из AI Inference.",
);
const body = await responseBody(response);
if (!response.ok || response.status !== 204) {
@@ -132,7 +132,7 @@ function admittedProjectionId(binding: ObservatoryRecordedRunBinding): string {
|| !SAFE_SESSION_ID.test(sessionId)
) {
throw new ObservatoryCatalogMutationError(
"Выбранный результат не допущен к изменению каталога Обсерватории.",
"Выбранный результат не допущен к изменению каталога AI Inference.",
);
}
return sessionId;
@@ -39,7 +39,7 @@ type ObservatoryCatalogMutationDraft =
function errorMessage(error: unknown): string {
return error instanceof Error && error.message.trim()
? error.message
: "Каталог Обсерватории недоступен.";
: "Каталог AI Inference недоступен.";
}
function isAbortError(error: unknown): boolean {
+3 -3
View File
@@ -170,9 +170,9 @@ export const workspaces: WorkspaceDefinition[] = [
{
id: "observatory",
root: "polygon",
label: "Обсерватория",
title: "AI inference",
eyebrow: "ТЕСТОВЫЙ КОНТУР / ОБСЕРВАТОРИЯ",
label: "AI Inference",
title: "AI Inference",
eyebrow: "ТЕСТОВЫЙ КОНТУР / AI INFERENCE",
description: "Сессии и квалификация без доступа к управлению.",
icon: "eye",
kind: "observatory",
@@ -165,7 +165,7 @@ function compositionProducedEvidence(
function mutationErrorMessage(error: unknown): string {
return error instanceof Error && error.message.trim()
? error.message
: "Не удалось изменить лабораторный результат в Обсерватории.";
: "Не удалось изменить лабораторный результат в AI Inference.";
}
function evidenceConfigurationLabel(
@@ -276,12 +276,12 @@ function aiJobStageDetail(
return "Не удалось запустить модель. Конфигурацию можно отправить повторно";
}
if (job.publication.state === "failed") {
return "Результат рассчитан, но не сохранён в Обсерватории";
return "Результат рассчитан, но не сохранён в AI Inference";
}
if (job.state === "succeeded") {
if (job.publication.state === "pending") return "Завершаем сохранение результата";
if (job.publication.state === "not-required") return "Публикация результата не требуется";
return "Результат сохранён в Обсерватории";
return "Результат сохранён в AI Inference";
}
if (!progress) {
return "Готовим конфигурацию и место в очереди";
@@ -862,7 +862,7 @@ export function ObservatoryWorkspace({
{run.state === "ready" ? (
<>
<IconButton
label={`Удалить ${run.displayName ?? run.presentation.configurationLabel} из Обсерватории`}
label={`Удалить ${run.displayName ?? run.presentation.configurationLabel} из AI Inference`}
onClick={() => openCompositionDelete(run)}
>
<Icon name="trash" size={16} />
@@ -952,7 +952,7 @@ export function ObservatoryWorkspace({
{evidence.recordedRun ? (
<>
<IconButton
label={`Удалить ${evidence.lab.labId} из Обсерватории`}
label={`Удалить ${evidence.lab.labId} из AI Inference`}
onClick={() => openDelete(evidence)}
>
<Icon name="trash" size={16} />
@@ -1094,7 +1094,7 @@ export function ObservatoryWorkspace({
<Window
open={renameTarget !== null || compositionRenameTarget !== null}
title="Переименовать лабораторный результат"
subtitle="Меняется только отображаемое название в Обсерватории"
subtitle="Меняется только отображаемое название в AI Inference"
size="sm"
closeOnBackdrop={mutationPending !== "rename"}
closeOnEscape={mutationPending !== "rename"}
@@ -1161,12 +1161,12 @@ export function ObservatoryWorkspace({
<ConfirmationModal
open={deleteTarget !== null || compositionDeleteTarget !== null}
title="Удалить результат из Обсерватории?"
title="Удалить результат из AI Inference?"
description={deleteTarget || compositionDeleteTarget ? (
<div className="observatory-delete-confirmation">
<p>
Будут удалены только каталожная проекция <strong>{deleteTarget?.label ?? compositionDeleteTarget?.displayName ?? compositionDeleteTarget?.presentation.configurationLabel}</strong>
{" "}и её отображаемое название в Обсерватории.
{" "}и её отображаемое название в AI Inference.
</p>
<p>
Исходная сессия, запечатанный лабораторный результат и файлы доказательств
@@ -1177,7 +1177,7 @@ export function ObservatoryWorkspace({
) : null}
</div>
) : null}
confirmLabel="Удалить из Обсерватории"
confirmLabel="Удалить из AI Inference"
pendingLabel="Удаляем…"
danger
onClose={() => {
@@ -114,10 +114,10 @@ test("Observatory mutations reject response drift and preserve API detail", asyn
);
await assert.rejects(
deleteObservatoryLabProjection(binding, {
fetcher: async () => Response.json({ detail: "Проекция не принадлежит Обсерватории." }, { status: 409 }),
fetcher: async () => Response.json({ detail: "Проекция не принадлежит AI Inference." }, { status: 409 }),
}),
(error) => error instanceof ObservatoryCatalogMutationError
&& error.status === 409
&& error.message === "Проекция не принадлежит Обсерватории.",
&& error.message === "Проекция не принадлежит AI Inference.",
);
});
@@ -34,9 +34,9 @@ test("Observatory is the third independent Polygon workspace", () => {
{
id: "observatory",
root: "polygon",
label: "Обсерватория",
title: "AI inference",
eyebrow: "ТЕСТОВЫЙ КОНТУР / ОБСЕРВАТОРИЯ",
label: "AI Inference",
title: "AI Inference",
eyebrow: "ТЕСТОВЫЙ КОНТУР / AI INFERENCE",
description: "Сессии и квалификация без доступа к управлению.",
icon: "eye",
kind: "observatory",
@@ -200,7 +200,7 @@ test("Observatory rename and delete use admitted projection mutations and canoni
assert.match(workspace, /<Window[\s\S]*title="Переименовать лабораторный результат"/);
assert.match(workspace, /<TextField[\s\S]*label="Название"[\s\S]*maxLength=\{160\}/);
assert.match(workspace, /<WindowFooterActions>[\s\S]*Сохранить/);
assert.match(workspace, /<ConfirmationModal[\s\S]*title="Удалить результат из Обсерватории\?"/);
assert.match(workspace, /<ConfirmationModal[\s\S]*title="Удалить результат из AI Inference\?"/);
assert.match(workspace, /Исходная сессия, запечатанный лабораторный результат и файлы доказательств/);
assert.match(workspace, /renameAICompositionRunProjection/);
assert.match(workspace, /deleteAICompositionRunProjection/);