@barivia/barmesh-mcp 0.6.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/job_monitor.js +1 -1
- package/dist/shared.js +1 -1
- package/dist/tools/barmesh_results_explorer.js +4 -1
- package/dist/tools/cfd.js +1 -0
- package/dist/tools/training_monitor.js +6 -3
- package/dist/training_monitor_curve.js +31 -1
- package/dist/training_review.js +49 -9
- package/dist/views/src/views/barmesh-training-monitor/index.html +10 -10
- package/package.json +1 -1
package/dist/job_monitor.js
CHANGED
|
@@ -92,7 +92,7 @@ export function formatSnapshotLine(s) {
|
|
|
92
92
|
if (s.qe != null)
|
|
93
93
|
parts.push(`QE ${s.qe.toFixed(4)}`);
|
|
94
94
|
if (s.te != null)
|
|
95
|
-
parts.push(`TE ${s.te.toFixed(4)}`);
|
|
95
|
+
parts.push(`Epoch TE ${s.te.toFixed(4)}`);
|
|
96
96
|
if (s.eta_sec != null)
|
|
97
97
|
parts.push(`ETA ~${s.eta_sec}s`);
|
|
98
98
|
if (s.ordering_errors_tail?.length) {
|
package/dist/shared.js
CHANGED
|
@@ -22,7 +22,7 @@ export const FETCH_TIMEOUT_MS = parseInt(process.env.BARIVIA_FETCH_TIMEOUT_MS ??
|
|
|
22
22
|
export const MAX_RETRIES = 2;
|
|
23
23
|
export const RETRYABLE_STATUS = new Set([502, 503, 504]);
|
|
24
24
|
/** Single source of truth for the proxy version. Keep in sync with package.json on bump. */
|
|
25
|
-
export const CLIENT_VERSION = "0.
|
|
25
|
+
export const CLIENT_VERSION = "0.6.1";
|
|
26
26
|
export const PUBLIC_SITE_ORIGIN = "https://barivia.se";
|
|
27
27
|
/** Large per-cell CSV uploads may exceed the default fetch timeout. */
|
|
28
28
|
export const UPLOAD_DATASET_TIMEOUT_MS = 180_000;
|
|
@@ -163,7 +163,10 @@ async function handleResultsExplorer(job_id) {
|
|
|
163
163
|
`This localhost port is assigned per MCP session and changes if the proxy restarts — re-run barmesh_results_explorer for a fresh URL, or set BARIVIA_VIZ_PORT for a persistent port.`,
|
|
164
164
|
});
|
|
165
165
|
}
|
|
166
|
-
return
|
|
166
|
+
return {
|
|
167
|
+
...structuredTextResult(payload, undefined, content),
|
|
168
|
+
_meta: { ui: { resourceUri: RESULTS_EXPLORER_URI } },
|
|
169
|
+
};
|
|
167
170
|
}
|
|
168
171
|
export function registerResultsExplorerTool(server) {
|
|
169
172
|
const toolConfig = {
|
package/dist/tools/cfd.js
CHANGED
|
@@ -25,6 +25,7 @@ COMMON MISTAKES: omitting feature_columns (required); choosing a reference_mesh
|
|
|
25
25
|
backend: z.enum(["auto", "cpu", "gpu", "gpu_graphs"]).optional().describe("Compute backend (default auto / preset)"),
|
|
26
26
|
stratify_scale: z.number().optional().describe("[0,1] per-mesh training-row cap; 1 uses all cells (default 1)"),
|
|
27
27
|
emd_method: z.enum(["exact", "sinkhorn"]).optional().describe("EMD solver: exact LP (default) or sinkhorn (fast approximation for large grids)"),
|
|
28
|
+
te_inner_samples: z.number().int().optional().describe("Inner statistical sample count for per-batch topographic error estimates during SOM training (default clamp(grid_nodes*6, 500, 10000); display cap remains ≤1000 batch points/phase)"),
|
|
28
29
|
component_planes_physical: z.boolean().optional().describe("Physical-scale component-plane colorbars (default true)"),
|
|
29
30
|
figures: z.boolean().optional().describe("Generate publication figures (default true)"),
|
|
30
31
|
transforms: z.record(z.enum(["log", "log1p", "log10", "sqrt", "square", "abs", "invert", "none"])).optional().describe("Per-feature transform applied before normalization (e.g. log1p to compress k/epsilon/omega). Same preprocessing engine as barsom training."),
|
|
@@ -2,7 +2,7 @@ import { z } from "zod";
|
|
|
2
2
|
import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
|
|
3
3
|
import { runMcpToolAudit } from "../audit.js";
|
|
4
4
|
import { apiCall, getClientSupportsMcpApps, getVizPort, structuredTextResult, } from "../shared.js";
|
|
5
|
-
import { enrichWithTrainingLog,
|
|
5
|
+
import { enrichWithTrainingLog, needsTrainingLogEnrichment } from "../training_review.js";
|
|
6
6
|
export const TRAINING_MONITOR_URI = "ui://barmesh/training-monitor";
|
|
7
7
|
export const TRAINING_MONITOR_REFRESH_MS = 5000;
|
|
8
8
|
function buildStructuredContent(job_id, data) {
|
|
@@ -33,7 +33,7 @@ export function registerTrainingMonitorTool(server) {
|
|
|
33
33
|
let data = (await apiCall("GET", `/v1/jobs/${job_id}`));
|
|
34
34
|
const jobStatus = String(data.status ?? "");
|
|
35
35
|
const terminal = jobStatus === "completed" || jobStatus === "failed";
|
|
36
|
-
if (fetch_training_log || (
|
|
36
|
+
if (fetch_training_log || needsTrainingLogEnrichment(data)) {
|
|
37
37
|
data = await enrichWithTrainingLog(job_id, data);
|
|
38
38
|
}
|
|
39
39
|
const structuredContent = buildStructuredContent(job_id, data);
|
|
@@ -71,6 +71,9 @@ export function registerTrainingMonitorTool(server) {
|
|
|
71
71
|
text: `Interactive training monitor: [Open training monitor](${standaloneUrl})`,
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
|
-
return
|
|
74
|
+
return {
|
|
75
|
+
...structuredTextResult(structuredContent, text, content),
|
|
76
|
+
_meta: { ui: { resourceUri: TRAINING_MONITOR_URI } },
|
|
77
|
+
};
|
|
75
78
|
}));
|
|
76
79
|
}
|
|
@@ -26,5 +26,35 @@ export function formatCurveSourceNote(data) {
|
|
|
26
26
|
}
|
|
27
27
|
if (parts.length === 0)
|
|
28
28
|
return null;
|
|
29
|
-
return `Displaying uniformly subsampled curves (≤1000 points/phase): ${parts.join("; ")}.`;
|
|
29
|
+
return `Displaying uniformly subsampled QE+TE curves (≤1000 points/phase, joint indices): ${parts.join("; ")}.`;
|
|
30
|
+
}
|
|
31
|
+
/** Map TE onto QE batch axis; when batch-aligned (same length), use values directly. */
|
|
32
|
+
export function alignTeToQeAxis(te, qeLen) {
|
|
33
|
+
if (qeLen <= 0)
|
|
34
|
+
return [];
|
|
35
|
+
if (te.length === qeLen)
|
|
36
|
+
return te;
|
|
37
|
+
if (te.length === 0)
|
|
38
|
+
return Array(qeLen).fill(null);
|
|
39
|
+
if (te.length >= qeLen)
|
|
40
|
+
return te.slice(0, qeLen);
|
|
41
|
+
const pad = qeLen - te.length;
|
|
42
|
+
return [...Array(pad).fill(null), ...te];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Last sampled TE point from the (batch-aligned) live TE curve. This is the most
|
|
46
|
+
* recent per-epoch TE estimate, distinct from the final trained-map TE in the summary.
|
|
47
|
+
*/
|
|
48
|
+
export function lastEpochTeFromCurves(data) {
|
|
49
|
+
const conv = data.convergence_topographic_errors;
|
|
50
|
+
if (Array.isArray(conv) && conv.length > 0) {
|
|
51
|
+
const v = Number(conv[conv.length - 1]);
|
|
52
|
+
return Number.isFinite(v) ? v : null;
|
|
53
|
+
}
|
|
54
|
+
const ord = data.ordering_topographic_errors;
|
|
55
|
+
if (Array.isArray(ord) && ord.length > 0) {
|
|
56
|
+
const v = Number(ord[ord.length - 1]);
|
|
57
|
+
return Number.isFinite(v) ? v : null;
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
30
60
|
}
|
package/dist/training_review.js
CHANGED
|
@@ -5,35 +5,67 @@
|
|
|
5
5
|
import { apiCall } from "./shared.js";
|
|
6
6
|
import { formatSnapshotLine, snapshotFromJob, } from "./job_monitor.js";
|
|
7
7
|
import { formatJobStatusText } from "./job_status_format.js";
|
|
8
|
+
import { lastEpochTeFromCurves } from "./training_monitor_curve.js";
|
|
8
9
|
export const REVIEW_MAX_SNAPSHOTS = 16;
|
|
9
10
|
function isTerminalStatus(status) {
|
|
10
11
|
return status === "completed" || status === "failed" || status === "cancelled";
|
|
11
12
|
}
|
|
13
|
+
export function hasTeCurveArrays(data) {
|
|
14
|
+
const ord = data.ordering_topographic_errors;
|
|
15
|
+
const conv = data.convergence_topographic_errors;
|
|
16
|
+
return ((Array.isArray(ord) && ord.length > 0) || (Array.isArray(conv) && conv.length > 0));
|
|
17
|
+
}
|
|
12
18
|
function hasCurveArrays(data) {
|
|
13
19
|
const ord = data.ordering_errors;
|
|
14
20
|
const conv = data.convergence_errors;
|
|
15
|
-
return ((Array.isArray(ord) && ord.length > 0) ||
|
|
21
|
+
return ((Array.isArray(ord) && ord.length > 0) ||
|
|
22
|
+
(Array.isArray(conv) && conv.length > 0) ||
|
|
23
|
+
hasTeCurveArrays(data));
|
|
24
|
+
}
|
|
25
|
+
/** True when a terminal job still needs training-log curves or the final map TE. */
|
|
26
|
+
export function needsTrainingLogEnrichment(data) {
|
|
27
|
+
const status = String(data.status ?? "");
|
|
28
|
+
if (!isTerminalStatus(status))
|
|
29
|
+
return false;
|
|
30
|
+
if (!hasCurveArrays(data))
|
|
31
|
+
return true;
|
|
32
|
+
if (!hasTeCurveArrays(data))
|
|
33
|
+
return true;
|
|
34
|
+
if (data.map_topographic_error == null && data.topographic_error == null)
|
|
35
|
+
return true;
|
|
36
|
+
return false;
|
|
16
37
|
}
|
|
17
38
|
export async function enrichWithTrainingLog(job_id, data) {
|
|
18
|
-
|
|
19
|
-
|
|
39
|
+
const status = String(data.status ?? "");
|
|
40
|
+
const epochTe = data.epoch_topographic_error ??
|
|
41
|
+
data.topographic_error ??
|
|
42
|
+
lastEpochTeFromCurves(data);
|
|
20
43
|
try {
|
|
21
44
|
const log = (await apiCall("GET", `/v1/results/${job_id}/training-log`));
|
|
22
|
-
|
|
45
|
+
const merged = {
|
|
23
46
|
...data,
|
|
24
47
|
ordering_errors: log.ordering_errors ?? data.ordering_errors,
|
|
25
48
|
convergence_errors: log.convergence_errors ?? data.convergence_errors,
|
|
26
49
|
ordering_topographic_errors: log.ordering_topographic_errors ?? data.ordering_topographic_errors,
|
|
27
50
|
convergence_topographic_errors: log.convergence_topographic_errors ?? data.convergence_topographic_errors,
|
|
28
51
|
quantization_error: log.quantization_error ?? data.quantization_error,
|
|
29
|
-
topographic_error: log.topographic_error ?? data.topographic_error,
|
|
30
52
|
grid: log.grid ?? data.grid,
|
|
31
53
|
epochs: log.epochs ?? data.epochs,
|
|
32
54
|
training_duration_seconds: log.training_duration_seconds ?? data.training_duration_seconds,
|
|
33
55
|
training_curve_source_batches: log.training_curve_source_batches ?? data.training_curve_source_batches,
|
|
34
56
|
};
|
|
57
|
+
const mapTe = log.topographic_error ?? data.map_topographic_error ?? data.topographic_error;
|
|
58
|
+
merged.epoch_topographic_error = epochTe ?? lastEpochTeFromCurves(merged);
|
|
59
|
+
merged.map_topographic_error = mapTe;
|
|
60
|
+
if (isTerminalStatus(status)) {
|
|
61
|
+
merged.topographic_error = mapTe;
|
|
62
|
+
}
|
|
63
|
+
return merged;
|
|
35
64
|
}
|
|
36
65
|
catch {
|
|
66
|
+
if (epochTe != null) {
|
|
67
|
+
return { ...data, epoch_topographic_error: epochTe };
|
|
68
|
+
}
|
|
37
69
|
return data;
|
|
38
70
|
}
|
|
39
71
|
}
|
|
@@ -134,11 +166,19 @@ export function formatReviewMonitorText(result, review) {
|
|
|
134
166
|
if (review.timing)
|
|
135
167
|
lines.push(`Timing: ${review.timing}`);
|
|
136
168
|
const qe = result.data.quantization_error;
|
|
137
|
-
const
|
|
138
|
-
|
|
169
|
+
const mapTe = result.data.map_topographic_error ?? result.data.topographic_error;
|
|
170
|
+
const epochTe = result.data.epoch_topographic_error;
|
|
171
|
+
if (qe != null || mapTe != null || epochTe != null) {
|
|
139
172
|
const qeS = qe != null ? `QE ${Number(qe).toFixed(4)}` : "";
|
|
140
|
-
const
|
|
141
|
-
|
|
173
|
+
const mapS = mapTe != null ? `Map TE ${Number(mapTe).toFixed(4)}` : "";
|
|
174
|
+
const epochS = epochTe != null &&
|
|
175
|
+
mapTe != null &&
|
|
176
|
+
Math.abs(Number(epochTe) - Number(mapTe)) > 0.0005
|
|
177
|
+
? `Last epoch TE ${Number(epochTe).toFixed(4)}`
|
|
178
|
+
: epochTe != null && mapTe == null
|
|
179
|
+
? `Epoch TE ${Number(epochTe).toFixed(4)}`
|
|
180
|
+
: "";
|
|
181
|
+
lines.push(`Final: ${[qeS, mapS, epochS].filter(Boolean).join(", ")}`);
|
|
142
182
|
}
|
|
143
183
|
lines.push("");
|
|
144
184
|
lines.push("Training curve (sampled from ordering_errors / convergence_errors):");
|