fix(telemetry): restore worker agent delivery and truthful system status

This commit is contained in:
DCCONSTRUCTIONS
2026-09-02 17:35:20 +03:00
parent 03e8e71bea
commit c570f7d938
15 changed files with 254 additions and 31 deletions
@@ -38,6 +38,8 @@ export function useWorkerTelemetry(
setError(null);
return;
}
setTelemetry(null);
setError(null);
const controller = new AbortController();
const stopPolling = startSequentialPolling(async () => {
setLoading(true);
@@ -48,6 +50,7 @@ export function useWorkerTelemetry(
setError(null);
} catch (reason: unknown) {
if (controller.signal.aborted) return;
setTelemetry(null);
setError(
reason instanceof Error
? reason.message
@@ -114,6 +114,9 @@ export interface WorkerTelemetry {
power_watts?: number;
temperature_celsius?: number;
memory_used_percent?: number;
sm_clock_mhz?: number | null;
memory_clock_mhz?: number | null;
driver_version?: string | null;
} | null;
triton: {
ready: boolean;
@@ -131,6 +134,13 @@ export interface WorkerTelemetry {
completed_runs: number | null;
failed_runs: number | null;
model_load_seconds: number | null;
profile_name?: string | null;
input_state?: string | null;
wait_reason?: string | null;
live_children?: number | null;
input_pauses?: number | null;
pending_bundles?: number | null;
buffer_bytes?: number | null;
stages: WorkerPipelineStage[];
};
network: {
@@ -189,7 +199,9 @@ export async function fetchWorkerTelemetry(
`/api/v1/system/contours/${encodeURIComponent(contourId)}/telemetry?history=90`,
{
method: "GET",
signal,
signal: signal
? AbortSignal.any([signal, AbortSignal.timeout(5_000)])
: AbortSignal.timeout(5_000),
},
),
"missioncore.worker-telemetry/v1",
@@ -17,9 +17,14 @@ import {
} from "../../core/system/useWorkerTelemetry";
function pipelineStateLabel(state: string): string {
if (state === "busy") return "Выполняет задачу";
if (state === "busy" || state === "running") return "Выполняет задачу";
if (state === "waiting") return "Ожидает восстановления данных";
if (state === "synchronizing") return "Синхронизирует поток";
if (state === "starting") return "Загружает профиль";
if (state === "stopped" || state === "cancelled") return "Профиль остановлен";
if (state === "failed") return "Ошибка профиля";
if (state === "ready") return "Готов к задаче";
return "Нет live-состояния";
return "Нет свежих данных о задаче";
}
function formatResourcePair(
@@ -116,7 +121,7 @@ export function ComputeModulesWorkspace() {
{!legacyDiagnostic && !agentTelemetry ? (
<GlassSurface className="system-workspace__notice" padding="md" tone="soft">
<StatusBadge tone="warning">
Агентный data-plane ещё не опубликовал нормализованный срез этого контура.
Ожидаем свежую телеметрию выбранного узла.
</StatusBadge>
</GlassSurface>
) : null}
@@ -187,6 +192,8 @@ export function ComputeModulesWorkspace() {
? `${node.gpu.temperature_celsius} °C`
: "—"
}</dd></div>
<div><dt>Частота GPU</dt><dd>{node?.gpu?.sm_clock_mhz == null ? "—" : `${node.gpu.sm_clock_mhz} МГц`}</dd></div>
<div><dt>Частота памяти GPU</dt><dd>{node?.gpu?.memory_clock_mhz == null ? "—" : `${node.gpu.memory_clock_mhz} МГц`}</dd></div>
<div><dt>Мощность</dt><dd>{
typeof node?.gpu?.power_watts === "number"
? `${node.gpu.power_watts.toFixed(1)} Вт`
@@ -214,7 +221,7 @@ export function ComputeModulesWorkspace() {
<div>
<span className="section-eyebrow">PROCESSING RUNTIME</span>
<h3>Контейнеры Mission Core</h3>
<p>Два ограниченных runtime: inference server и прикладной perception pipeline.</p>
<p>Состояние контейнера и готовность вычислительного профиля проверяются отдельно.</p>
</div>
<StatusBadge tone={node?.triton.ready ? "success" : "danger"}>
{node?.triton.ready ? "Triton ready" : "Triton недоступен"}
@@ -233,19 +240,31 @@ export function ComputeModulesWorkspace() {
<span className="section-eyebrow">ТЕКУЩАЯ ЗАДАЧА</span>
<h3>{pipelineStateLabel(telemetry?.pipeline.service_state ?? "unavailable")}</h3>
<p>
{telemetry?.pipeline.profile_name ?? "Perception"}
{" · "}
{telemetry?.pipeline.active_request_id
? `Run ${telemetry.pipeline.active_request_id}`
: "Очередь свободна; модели остаются загруженными в persistent worker."}
: telemetry?.pipeline.service_state === "ready"
? "Готов к приёму данных."
: "Готовность моделей не подтверждена."}
</p>
</div>
<StatusBadge tone={
telemetry?.pipeline.service_state === "busy" ? "warning"
["busy", "running", "waiting", "synchronizing", "starting"].includes(telemetry?.pipeline.service_state ?? "") ? "warning"
: telemetry?.pipeline.service_state === "ready" ? "success"
: "danger"
}>
{telemetry?.pipeline.service_state ?? "unavailable"}
{pipelineStateLabel(telemetry?.pipeline.service_state ?? "unavailable")}
</StatusBadge>
</header>
{telemetry?.pipeline.profile_name ? (
<div className="worker-pipeline__summary">
<div><span>Процессы профиля</span><strong>{telemetry.pipeline.live_children ?? "—"}</strong></div>
<div><span>Ожиданий потока</span><strong>{telemetry.pipeline.input_pauses ?? "—"}</strong></div>
<div><span>Пакетов в очереди</span><strong>{telemetry.pipeline.pending_bundles ?? "—"}</strong></div>
<div><span>Входные буферы</span><strong>{formatBytes(telemetry.pipeline.buffer_bytes)}</strong></div>
</div>
) : null}
<div className="worker-pipeline__summary">
<div><span>Завершено запусков</span><strong>{telemetry?.pipeline.completed_runs ?? "—"}</strong></div>
<div><span>Ошибок запусков</span><strong>{telemetry?.pipeline.failed_runs ?? "—"}</strong></div>
@@ -124,6 +124,11 @@ test("Compute-contour telemetry remains a bounded system feature slice", async (
assert.match(pollIntervalContract, /MIN_TELEMETRY_POLL_INTERVAL_SECONDS\s*=\s*1/);
assert.match(telemetryPolling, /normalizeWorkerTelemetryPollMilliseconds/);
assert.match(telemetryPolling, /startSequentialPolling/);
assert.match(telemetryPolling, /catch \(reason: unknown\)[\s\S]*?setTelemetry\(null\)/);
assert.match(computeWorkspace, /sm_clock_mhz/);
assert.match(computeWorkspace, /memory_clock_mhz/);
assert.match(computeWorkspace, /Ожидает восстановления данных/);
assert.doesNotMatch(computeWorkspace, /Очередь свободна; модели остаются загруженными/);
assert.doesNotMatch(telemetryPolling, /if\s*\(\s*!enabled\s*\|\|\s*loading\s*\)/);
assert.match(pollingScheduler, /A transient failure must not stop polling/);
assert.match(styles, /system-telemetry\.css/);