@barivia/barmesh-mcp 0.3.0 → 0.4.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 +9 -2
- package/dist/cfd_finalize.js +23 -0
- package/dist/cfd_prepare.js +19 -0
- package/dist/shared.js +1 -1
- package/dist/tools/cfd.js +10 -2
- package/dist/tools/jobs.js +15 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,14 @@ form on a shared self-organizing map (SOM)**:
|
|
|
13
13
|
- **`barmesh_mesh_convergence`** — trains one SOM on all meshes (joint-normalized), projects
|
|
14
14
|
each mesh to a volume-weighted fingerprint, and computes **symmetric KL** and
|
|
15
15
|
**Wasserstein-1 (EMD)** distances stepwise and against a reference mesh, with publication
|
|
16
|
-
figures and an advisory convergence reading.
|
|
16
|
+
figures and an advisory convergence reading. The SOM features are preprocessed by the
|
|
17
|
+
same staged pipeline as barsom training, so any dataset (small or large, CSV/gzip/Parquet)
|
|
18
|
+
is handled out-of-core by default; optional `transforms`, `normalize`,
|
|
19
|
+
`normalization_methods`, and `row_range` give the same preprocessing controls. Submit
|
|
20
|
+
enqueues **`prepare_training_matrix`** on worker-io when the dataset is staged; the proxy
|
|
21
|
+
auto-polls `prepare_job_id` before the mesh job runs. Default **`defer_figures=true`** →
|
|
22
|
+
**`cfd_finalize`** on worker-io; **`barmesh_jobs(status)`** auto-polls **`finalize_job_id`**
|
|
23
|
+
when figures are deferred.
|
|
17
24
|
- **`barmesh_richardson`** — classical three-level Richardson extrapolation / Grid
|
|
18
25
|
Convergence Index (GCI) on scalar quantities of interest.
|
|
19
26
|
|
|
@@ -48,7 +55,7 @@ API key; otherwise the analysis calls return HTTP 403. Contact Barivia to enable
|
|
|
48
55
|
| `barmesh_datasets` | Upload / preview / list the mesh CSV. |
|
|
49
56
|
| `barmesh_mesh_convergence` | SOM fingerprint distances (async job). |
|
|
50
57
|
| `barmesh_richardson` | Richardson/GCI on scalar QoIs (async job). |
|
|
51
|
-
| `barmesh_jobs` | Poll job status / list jobs. |
|
|
58
|
+
| `barmesh_jobs` | Poll job status / list jobs (auto-polls CFD prepare + finalize when applicable). |
|
|
52
59
|
| `barmesh_results` | Distances, convergence reading, and figures. |
|
|
53
60
|
| `barmesh_send_feedback` | Send a short note or bug report to the Barivia team. |
|
|
54
61
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { apiCall, pollUntilComplete } from "./shared.js";
|
|
2
|
+
/**
|
|
3
|
+
* When CFD compute finishes with defer_figures, cfd_finalize may still be rendering on worker-io.
|
|
4
|
+
*/
|
|
5
|
+
export async function pollCfdFinalizeIfPresent(jobId, data, timeoutMs = 600_000) {
|
|
6
|
+
const finalizeJobId = data.finalize_job_id;
|
|
7
|
+
if (!finalizeJobId)
|
|
8
|
+
return { finalizeJobId: null, note: null };
|
|
9
|
+
const poll = await pollUntilComplete(finalizeJobId, timeoutMs);
|
|
10
|
+
if (poll.status === "failed") {
|
|
11
|
+
throw new Error(`CFD job ${jobId}: cfd_finalize ${finalizeJobId} failed: ${poll.error ?? "unknown error"}`);
|
|
12
|
+
}
|
|
13
|
+
if (poll.status !== "completed") {
|
|
14
|
+
throw new Error(`CFD job ${jobId}: cfd_finalize ${finalizeJobId} did not complete (status=${poll.status})`);
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
finalizeJobId,
|
|
18
|
+
note: `Finalize job ${finalizeJobId} completed (figures uploaded).`,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export async function refreshJobAfterFinalize(jobId) {
|
|
22
|
+
return (await apiCall("GET", `/v1/jobs/${jobId}`));
|
|
23
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { pollUntilComplete } from "./shared.js";
|
|
2
|
+
/**
|
|
3
|
+
* Mesh-convergence on a staged dataset enqueues a preprocessing job
|
|
4
|
+
* (prepare_training_matrix) first; the submit response carries its id as
|
|
5
|
+
* `prepare_job_id`. Poll it to completion before the mesh job runs.
|
|
6
|
+
*/
|
|
7
|
+
export async function pollCfdPrepareIfPresent(data, label, timeoutMs = 600_000) {
|
|
8
|
+
const prepareJobId = data.prepare_job_id;
|
|
9
|
+
if (!prepareJobId)
|
|
10
|
+
return null;
|
|
11
|
+
const poll = await pollUntilComplete(prepareJobId, timeoutMs);
|
|
12
|
+
if (poll.status === "failed") {
|
|
13
|
+
throw new Error(`${label}: data preparation job ${prepareJobId} failed: ${poll.error ?? "unknown error"}`);
|
|
14
|
+
}
|
|
15
|
+
if (poll.status !== "completed") {
|
|
16
|
+
throw new Error(`${label}: data preparation job ${prepareJobId} did not complete (status=${poll.status})`);
|
|
17
|
+
}
|
|
18
|
+
return prepareJobId;
|
|
19
|
+
}
|
package/dist/shared.js
CHANGED
|
@@ -20,7 +20,7 @@ export const FETCH_TIMEOUT_MS = parseInt(process.env.BARIVIA_FETCH_TIMEOUT_MS ??
|
|
|
20
20
|
export const MAX_RETRIES = 2;
|
|
21
21
|
export const RETRYABLE_STATUS = new Set([502, 503, 504]);
|
|
22
22
|
/** Single source of truth for the proxy version. Keep in sync with package.json on bump. */
|
|
23
|
-
export const CLIENT_VERSION = "0.
|
|
23
|
+
export const CLIENT_VERSION = "0.4.0";
|
|
24
24
|
export const PUBLIC_SITE_ORIGIN = "https://barivia.se";
|
|
25
25
|
/** Large per-cell CSV uploads may exceed the default fetch timeout. */
|
|
26
26
|
export const UPLOAD_DATASET_TIMEOUT_MS = 180_000;
|
package/dist/tools/cfd.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { registerAuditedTool } from "../audit.js";
|
|
3
3
|
import { apiCall, textResult } from "../shared.js";
|
|
4
|
+
import { pollCfdPrepareIfPresent } from "../cfd_prepare.js";
|
|
4
5
|
export function registerCfdTools(server) {
|
|
5
6
|
registerAuditedTool(server, "barmesh_mesh_convergence", `Run SOM-based mesh-convergence analysis on an uploaded combined per-cell CSV.
|
|
6
7
|
|
|
@@ -26,6 +27,10 @@ COMMON MISTAKES: omitting feature_columns (required); choosing a reference_mesh
|
|
|
26
27
|
emd_method: z.enum(["exact", "sinkhorn"]).optional().describe("EMD solver: exact LP (default) or sinkhorn (fast approximation for large grids)"),
|
|
27
28
|
component_planes_physical: z.boolean().optional().describe("Physical-scale component-plane colorbars (default true)"),
|
|
28
29
|
figures: z.boolean().optional().describe("Generate publication figures (default true)"),
|
|
30
|
+
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."),
|
|
31
|
+
normalize: z.union([z.enum(["all", "auto", "mad", "sigmoidal", "sepd"]), z.array(z.string())]).optional().describe("Normalization mode for SOM features (default auto = joint z-score of non-cyclic features). Use mad for heavy-tailed channels."),
|
|
32
|
+
normalization_methods: z.record(z.enum(["zscore", "mad", "sigmoidal", "sepd", "none"])).optional().describe("Per-feature normalization override; keys must be in feature_columns."),
|
|
33
|
+
row_range: z.tuple([z.number().int().min(1), z.number().int().min(1)]).optional().describe("1-based inclusive [start, end] row slice applied during preprocessing (and to mesh labels / cell volumes)."),
|
|
29
34
|
label: z.string().optional().describe("Optional job label"),
|
|
30
35
|
}, async (args) => {
|
|
31
36
|
const { dataset_id, label, ...rest } = args;
|
|
@@ -38,9 +43,12 @@ COMMON MISTAKES: omitting feature_columns (required); choosing a reference_mesh
|
|
|
38
43
|
if (typeof label === "string" && label.length > 0)
|
|
39
44
|
body.label = label;
|
|
40
45
|
const data = (await apiCall("POST", "/v1/cfd/mesh-convergence", body));
|
|
46
|
+
await pollCfdPrepareIfPresent(data, "barmesh_mesh_convergence");
|
|
41
47
|
const id = data.id;
|
|
42
|
-
if (id != null)
|
|
43
|
-
data.
|
|
48
|
+
if (id != null) {
|
|
49
|
+
const prep = data.prepare_job_id != null ? " (dataset prepare complete)" : "";
|
|
50
|
+
data.suggested_next_step = `Poll barmesh_jobs(action=status, job_id=${id})${prep}; on completion call barmesh_results(action=get, job_id=${id}).`;
|
|
51
|
+
}
|
|
44
52
|
return textResult(data);
|
|
45
53
|
});
|
|
46
54
|
registerAuditedTool(server, "barmesh_richardson", `Run classical Richardson extrapolation / Grid Convergence Index (GCI) on scalar quantities of interest.
|
package/dist/tools/jobs.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { registerAuditedTool } from "../audit.js";
|
|
3
3
|
import { apiCall, textResult } from "../shared.js";
|
|
4
|
+
import { pollCfdFinalizeIfPresent, refreshJobAfterFinalize } from "../cfd_finalize.js";
|
|
4
5
|
export function registerJobsTool(server) {
|
|
5
6
|
registerAuditedTool(server, "barmesh_jobs", `Check job status or list jobs.
|
|
6
7
|
|
|
@@ -14,7 +15,20 @@ ESCALATION: status=failed returns an error message and (when available) a failur
|
|
|
14
15
|
if (action === "status") {
|
|
15
16
|
if (!job_id)
|
|
16
17
|
throw new Error("barmesh_jobs(status) requires job_id.");
|
|
17
|
-
|
|
18
|
+
let data = (await apiCall("GET", `/v1/jobs/${job_id}`));
|
|
19
|
+
const status = String(data.status ?? "");
|
|
20
|
+
if (status === "completed" && data.finalize_job_id) {
|
|
21
|
+
const { note } = await pollCfdFinalizeIfPresent(job_id, data);
|
|
22
|
+
data = await refreshJobAfterFinalize(job_id);
|
|
23
|
+
const lines = [
|
|
24
|
+
`Job ${job_id}: ${String(data.status ?? "unknown")}`,
|
|
25
|
+
data.label != null ? `Label: ${String(data.label)}` : null,
|
|
26
|
+
data.progress != null ? `Progress: ${String(data.progress)}` : null,
|
|
27
|
+
data.result_ref != null ? `Results: ${String(data.result_ref)}` : null,
|
|
28
|
+
note,
|
|
29
|
+
].filter(Boolean);
|
|
30
|
+
return textResult({ ...data, status_text: lines.join("\n") });
|
|
31
|
+
}
|
|
18
32
|
return textResult(data);
|
|
19
33
|
}
|
|
20
34
|
const data = await apiCall("GET", "/v1/jobs");
|