@barivia/barmesh-mcp 0.6.2 → 0.7.0
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/README.md +2 -1
- package/dist/audit.js +3 -1
- package/dist/shared.js +22 -2
- package/dist/tools/cfd.js +5 -1
- package/dist/training_monitor_curve.js +16 -31
- package/dist/training_review.js +4 -4
- package/dist/views/src/views/barmesh-training-monitor/index.html +14 -14
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -79,12 +79,13 @@ API key; otherwise the analysis calls return HTTP 403. Contact Barivia to enable
|
|
|
79
79
|
- **Uploads:** large CSVs use presigned PUT with explicit `Content-Length`; `.csv.gz` /
|
|
80
80
|
`.tsv.gz` accepted. Pin `@barivia/barmesh-mcp@0.5.2` (clear `~/.npm/_npx` if stale).
|
|
81
81
|
- **Live progress:** `barmesh_training_monitor(job_id)` or `barmesh_jobs(action=monitor)` block
|
|
82
|
-
server-side with compact snapshots (phase, epoch, QE/TE
|
|
82
|
+
server-side with compact snapshots (phase, epoch, QE, **panel/map TE**, ETA, ordering_errors tail) until
|
|
83
83
|
terminal or `block_until_sec` (default 900). Waits for `cfd_finalize` by default. One-shot:
|
|
84
84
|
`barmesh_jobs(action=status)`.
|
|
85
85
|
|
|
86
86
|
### Migration notes
|
|
87
87
|
|
|
88
|
+
- **Fixed-panel live TE (0.6.3 / barsom 0.20.4):** mid-training TE uses a fixed evaluation panel (`te_panel_size`; `te_inner_samples` alias). Curves stay on panel TE; monitors show **Panel TE** and **Map TE** separately — no snap-to-map on the curve tail.
|
|
88
89
|
- **`barmesh_training_monitor` (0.5.3):** server-side blocking monitor with throttled snapshots — preferred after job submit instead of manual `barmesh_jobs(status)` loops. Equivalent to `barmesh_jobs(action=monitor)`.
|
|
89
90
|
- **`send_feedback` → `barmesh_send_feedback` (0.3.0):** the feedback tool was renamed so it no longer collides with the `@barivia/barsom-mcp` tool of the same name when both servers are enabled in one client. Update any direct call sites; the behavior is unchanged.
|
|
90
91
|
|
package/dist/audit.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* MCP tool audit wrapper — tool name, action, latency, outcome; no secrets.
|
|
3
3
|
*/
|
|
4
4
|
import { logAudit } from "./logger.js";
|
|
5
|
+
import { runWithTrace } from "./shared.js";
|
|
5
6
|
const AUDIT_PARAM_KEYS = new Set([
|
|
6
7
|
"action",
|
|
7
8
|
"preset",
|
|
@@ -87,6 +88,7 @@ export function registerAuditedTool(server, name, description, schema, handler)
|
|
|
87
88
|
server.tool(name, description, schema, (async (args) => {
|
|
88
89
|
const rec = args;
|
|
89
90
|
const action = typeof rec.action === "string" && rec.action.length > 0 ? rec.action : "default";
|
|
90
|
-
|
|
91
|
+
// One trace per tool invocation (every apiCall inside shares one trace_id).
|
|
92
|
+
return runWithTrace(() => runMcpToolAudit(name, action, rec, () => handler(args)));
|
|
91
93
|
}));
|
|
92
94
|
}
|
package/dist/shared.js
CHANGED
|
@@ -7,6 +7,7 @@ import fs from "node:fs/promises";
|
|
|
7
7
|
import { createReadStream, createWriteStream } from "node:fs";
|
|
8
8
|
import { createGzip } from "node:zlib";
|
|
9
9
|
import { createHash, randomUUID } from "node:crypto";
|
|
10
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
10
11
|
import { Readable } from "node:stream";
|
|
11
12
|
import { pipeline } from "node:stream/promises";
|
|
12
13
|
import os from "node:os";
|
|
@@ -22,7 +23,7 @@ export const FETCH_TIMEOUT_MS = parseInt(process.env.BARIVIA_FETCH_TIMEOUT_MS ??
|
|
|
22
23
|
export const MAX_RETRIES = 2;
|
|
23
24
|
export const RETRYABLE_STATUS = new Set([502, 503, 504]);
|
|
24
25
|
/** Single source of truth for the proxy version. Keep in sync with package.json on bump. */
|
|
25
|
-
export const CLIENT_VERSION = "0.
|
|
26
|
+
export const CLIENT_VERSION = "0.7.0";
|
|
26
27
|
export const PUBLIC_SITE_ORIGIN = "https://barivia.se";
|
|
27
28
|
/** Large per-cell CSV uploads may exceed the default fetch timeout. */
|
|
28
29
|
export const UPLOAD_DATASET_TIMEOUT_MS = 180_000;
|
|
@@ -260,6 +261,24 @@ function throwApiError(status, bodyText, requestId) {
|
|
|
260
261
|
err.httpStatus = status;
|
|
261
262
|
throw err;
|
|
262
263
|
}
|
|
264
|
+
// ---- Distributed-trace context (W3C traceparent) ----
|
|
265
|
+
// One trace per logical tool action (scoped via AsyncLocalStorage in registerAuditedTool),
|
|
266
|
+
// so the API + job chain reconstruct end-to-end. Fresh span per API call; falls back to a
|
|
267
|
+
// per-call trace if no scope is set. Mirrors the barsom proxy.
|
|
268
|
+
const _traceStore = new AsyncLocalStorage();
|
|
269
|
+
function _newTraceId() {
|
|
270
|
+
return randomUUID().replace(/-/g, "");
|
|
271
|
+
}
|
|
272
|
+
function _newSpanId() {
|
|
273
|
+
return randomUUID().replace(/-/g, "").slice(0, 16);
|
|
274
|
+
}
|
|
275
|
+
/** Run `fn` within a fresh trace scope so all apiCall()s inside share one trace_id. */
|
|
276
|
+
export function runWithTrace(fn) {
|
|
277
|
+
return _traceStore.run(_newTraceId(), fn);
|
|
278
|
+
}
|
|
279
|
+
function _traceparentHeader() {
|
|
280
|
+
return `00-${_traceStore.getStore() ?? _newTraceId()}-${_newSpanId()}-01`;
|
|
281
|
+
}
|
|
263
282
|
export async function apiCall(method, pathPart, body, extraHeaders, requestTimeoutMs) {
|
|
264
283
|
const url = `${API_URL}${pathPart}`;
|
|
265
284
|
const contentType = extraHeaders?.["Content-Type"] ?? "application/json";
|
|
@@ -268,6 +287,7 @@ export async function apiCall(method, pathPart, body, extraHeaders, requestTimeo
|
|
|
268
287
|
Authorization: `Bearer ${API_KEY}`,
|
|
269
288
|
"Content-Type": contentType,
|
|
270
289
|
"X-Request-ID": requestId,
|
|
290
|
+
traceparent: _traceparentHeader(),
|
|
271
291
|
"X-Barmesh-Client-Version": CLIENT_VERSION,
|
|
272
292
|
...extraHeaders,
|
|
273
293
|
};
|
|
@@ -321,7 +341,7 @@ export async function apiRawCall(pathPart, requestTimeoutMs) {
|
|
|
321
341
|
let lastError;
|
|
322
342
|
for (let attempt = 0; attempt <= MAX_RETRIES; attempt++) {
|
|
323
343
|
try {
|
|
324
|
-
const resp = await fetchWithTimeout(url, { method: "GET", headers: { Authorization: `Bearer ${API_KEY}`, "X-Request-ID": requestId } }, effectiveTimeout);
|
|
344
|
+
const resp = await fetchWithTimeout(url, { method: "GET", headers: { Authorization: `Bearer ${API_KEY}`, "X-Request-ID": requestId, traceparent: _traceparentHeader() } }, effectiveTimeout);
|
|
325
345
|
if (!resp.ok) {
|
|
326
346
|
if (attempt < MAX_RETRIES && isTransientError(null, resp.status)) {
|
|
327
347
|
await new Promise((r) => setTimeout(r, 1000 * 2 ** attempt));
|
package/dist/tools/cfd.js
CHANGED
|
@@ -25,7 +25,8 @@ 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
|
-
|
|
28
|
+
te_panel_size: z.number().int().optional().describe("Fixed evaluation-panel size for live topographic error during training (default clamp(grid_nodes*6, 500, 10000); curve stays on this panel end-to-end)"),
|
|
29
|
+
te_inner_samples: z.number().int().optional().describe("Deprecated alias for te_panel_size"),
|
|
29
30
|
component_planes_physical: z.boolean().optional().describe("Physical-scale component-plane colorbars (default true)"),
|
|
30
31
|
figures: z.boolean().optional().describe("Generate publication figures (default true)"),
|
|
31
32
|
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."),
|
|
@@ -40,6 +41,9 @@ COMMON MISTAKES: omitting feature_columns (required); choosing a reference_mesh
|
|
|
40
41
|
if (v !== undefined && v !== null)
|
|
41
42
|
params[k] = v;
|
|
42
43
|
}
|
|
44
|
+
if (params.te_panel_size == null && params.te_inner_samples != null) {
|
|
45
|
+
params.te_panel_size = params.te_inner_samples;
|
|
46
|
+
}
|
|
43
47
|
const body = { dataset_id, params };
|
|
44
48
|
if (typeof label === "string" && label.length > 0)
|
|
45
49
|
body.label = label;
|
|
@@ -16,25 +16,8 @@ export function isKernelTrainingComplete(data, status) {
|
|
|
16
16
|
return true;
|
|
17
17
|
return status === "completed";
|
|
18
18
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
const mapTe = data.map_topographic_error ??
|
|
22
|
-
(data.kernel_complete === true ? data.topographic_error : null);
|
|
23
|
-
if (mapTe == null || !Number.isFinite(Number(mapTe)))
|
|
24
|
-
return data;
|
|
25
|
-
const out = { ...data };
|
|
26
|
-
for (const key of ["ordering_topographic_errors", "convergence_topographic_errors"]) {
|
|
27
|
-
const arr = out[key];
|
|
28
|
-
if (Array.isArray(arr) && arr.length > 0) {
|
|
29
|
-
const copy = arr.slice();
|
|
30
|
-
copy[copy.length - 1] = Number(mapTe);
|
|
31
|
-
out[key] = copy;
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
return out;
|
|
35
|
-
}
|
|
36
|
-
export function teCurveLabel(base, kernelComplete) {
|
|
37
|
-
return kernelComplete ? `${base} (→ map TE)` : `${base} (sampled)`;
|
|
19
|
+
export function teCurveLabel(base, _kernelComplete = false) {
|
|
20
|
+
return `${base} (panel)`;
|
|
38
21
|
}
|
|
39
22
|
export function formatCurveSourceNote(data) {
|
|
40
23
|
const src = data.training_curve_source_batches;
|
|
@@ -49,17 +32,20 @@ export function formatCurveSourceNote(data) {
|
|
|
49
32
|
if (convTotal != null && convTotal > convShown) {
|
|
50
33
|
parts.push(`${convShown} of ${convTotal.toLocaleString()} convergence batch samples`);
|
|
51
34
|
}
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
Math.abs(Number(epochTe) - Number(mapTe)) > 0.0005) {
|
|
59
|
-
parts.push(`Live TE is a subsampled batch estimate during training; final point snaps to map TE ${Number(mapTe).toFixed(4)} (last sampled ${Number(epochTe).toFixed(4)})`);
|
|
35
|
+
const teEval = data.te_evaluation;
|
|
36
|
+
const panelM = teEval?.te_panel_size;
|
|
37
|
+
const panelN = teEval?.te_panel_n_train;
|
|
38
|
+
if (typeof panelM === "number" && typeof panelN === "number" && panelN > 0) {
|
|
39
|
+
const strat = teEval?.te_panel_stratified === true ? ", stratified by mesh" : "";
|
|
40
|
+
parts.push(`TE curve uses a fixed panel of ${panelM.toLocaleString()} of ${panelN.toLocaleString()} training rows${strat}`);
|
|
60
41
|
}
|
|
61
|
-
|
|
62
|
-
|
|
42
|
+
const panelTe = data.panel_topographic_error;
|
|
43
|
+
const mapTe = data.map_topographic_error;
|
|
44
|
+
if (panelTe != null &&
|
|
45
|
+
mapTe != null &&
|
|
46
|
+
Number.isFinite(Number(panelTe)) &&
|
|
47
|
+
Number.isFinite(Number(mapTe))) {
|
|
48
|
+
parts.push(`Panel TE ${Number(panelTe).toFixed(4)} · Map TE ${Number(mapTe).toFixed(4)} (full training set)`);
|
|
63
49
|
}
|
|
64
50
|
if (parts.length === 0)
|
|
65
51
|
return null;
|
|
@@ -82,8 +68,7 @@ export function alignTeToQeAxis(te, qeLen) {
|
|
|
82
68
|
return [...Array(pad).fill(null), ...te];
|
|
83
69
|
}
|
|
84
70
|
/**
|
|
85
|
-
* Last
|
|
86
|
-
* recent per-epoch TE estimate, distinct from the final trained-map TE in the summary.
|
|
71
|
+
* Last panel TE point from the (batch-aligned) live TE curve.
|
|
87
72
|
*/
|
|
88
73
|
export function lastEpochTeFromCurves(data) {
|
|
89
74
|
const conv = data.convergence_topographic_errors;
|
package/dist/training_review.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
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
|
|
8
|
+
import { lastEpochTeFromCurves } from "./training_monitor_curve.js";
|
|
9
9
|
export const REVIEW_MAX_SNAPSHOTS = 16;
|
|
10
10
|
function isTerminalStatus(status) {
|
|
11
11
|
return status === "completed" || status === "failed" || status === "cancelled";
|
|
@@ -60,13 +60,13 @@ export async function enrichWithTrainingLog(job_id, data) {
|
|
|
60
60
|
if (isTerminalStatus(status)) {
|
|
61
61
|
merged.topographic_error = mapTe;
|
|
62
62
|
}
|
|
63
|
-
return
|
|
63
|
+
return merged;
|
|
64
64
|
}
|
|
65
65
|
catch {
|
|
66
66
|
if (epochTe != null) {
|
|
67
|
-
return
|
|
67
|
+
return { ...data, epoch_topographic_error: epochTe };
|
|
68
68
|
}
|
|
69
|
-
return data
|
|
69
|
+
return data;
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
/** Evenly sample indices for a compact epoch/QE timeline in review mode. */
|