@barivia/barsom-mcp 0.22.1 → 0.22.2
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 +6 -3
- package/dist/index.js +1 -1
- package/dist/job_status_format.js +23 -0
- package/dist/prepare_training_prompt.js +14 -5
- package/dist/shared.js +1 -1
- package/dist/tools/account.js +9 -76
- package/dist/tools/datasets.js +13 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -163,12 +163,11 @@ All actions use a frozen trained map — no retraining. Derived columns use **`d
|
|
|
163
163
|
| Action | Use when |
|
|
164
164
|
|--------|----------|
|
|
165
165
|
| `status` | Before large jobs — plan tier, GPU availability, queue depth, credit balance, training time estimates |
|
|
166
|
-
| `request_compute` | Upgrading to cloud burst. Leave tier blank to list options. |
|
|
167
|
-
| `compute_status` | Checking active lease time remaining |
|
|
168
|
-
| `release_compute` | Manually stopping a lease to stop billing |
|
|
169
166
|
| `history` | Viewing recent compute usage and spend |
|
|
170
167
|
| `add_funds` | Getting instructions to add credits |
|
|
171
168
|
|
|
169
|
+
Capacity is shared LOCAL/GKE worker pools (no per-key cloud burst lease).
|
|
170
|
+
|
|
172
171
|
### MCP App tools (optional UIs)
|
|
173
172
|
|
|
174
173
|
| Tool | Role |
|
|
@@ -191,6 +190,10 @@ The right viewer depends on **(MCP App support)** **and** **(can the human reach
|
|
|
191
190
|
|
|
192
191
|
### Migration notes
|
|
193
192
|
|
|
193
|
+
- **Account surface (0.22.0, breaking):** `account(request_compute|compute_status|release_compute)` removed — AWS cloud burst leases are gone. Use `account(status|history|add_funds)`. Capacity is shared LOCAL/GKE pools.
|
|
194
|
+
- **Structured results + train hints (0.21.x, non-breaking):**
|
|
195
|
+
- **`results(action=get)`** returns **`structuredContent`** (job id/type, label, summary metrics, file list) alongside the text summary — same pattern as `training_monitor` / `results_explorer`. Agent hosts can read fields without parsing prose.
|
|
196
|
+
- **`train`** submit responses may include a **`results_hint`** string pointing agents to the next post-train step.
|
|
194
197
|
- **Fixed-panel live TE (0.20.4):** mid-training TE curves use a fixed evaluation panel (`te_panel_size` on `train`). Monitors show **Panel TE** and **Map TE** separately; curve tail no longer snaps to map TE.
|
|
195
198
|
- **Features (0.20.0, non-breaking):**
|
|
196
199
|
- **FLooP-SIOM maps are now projectable.** `inference(action=project_columns)`, `inference(action=predict)`, and `datasets(action=add_expression, project_onto_job=<floop_job>)` work on FLooP-SIOM (free/chain) maps — values render onto the FLooP Voronoi layout instead of failing. Grid-only ops (`inference(action=transition_flow | impute_column | render_variant)`) return a clear `unsupported_topology_for_inference` message; use a fixed-grid SOM/SIOM for those. No client change required.
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{McpServer as e}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as t}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as o}from"zod";import{getUiCapability as r,registerAppResource as s,RESOURCE_MIME_TYPE as n}from"@modelcontextprotocol/ext-apps/server";import{startVizServer as i}from"./viz-server.js";import{API_KEY as a,apiCall as l,apiRawCall as c,loadViewHtml as p,setVizPort as m,setClientSupportsMcpApps as u,CLIENT_VERSION as d}from"./shared.js";import{registerDatasetsTool as f}from"./tools/datasets.js";import{registerTrainTool as g}from"./tools/train.js";import{registerJobsTool as h,JOBS_DESCRIPTION_BASE as _}from"./tools/jobs.js";import{registerResultsTool as b}from"./tools/results.js";import{registerExploreMapTool as w,RESULTS_EXPLORER_URI as y}from"./tools/explore_map.js";import{registerAccountTool as j}from"./tools/account.js";import{registerInferenceTool as v}from"./tools/inference.js";import{registerGuideBarsomTool as x}from"./tools/guide_barsom.js";import{registerTrainingGuidanceTool as P}from"./tools/training_guidance.js";import{registerFeedbackTool as k}from"./tools/feedback.js";import{registerTrainingMonitorTool as I,TRAINING_MONITOR_URI as A}from"./tools/training_monitor.js";import{resolvePrepareTrainingPromptText as M}from"./prepare_training_prompt.js";a||(console.error("Error: BARIVIA_API_KEY not set. Set it in your MCP client config."),process.exit(1));const T=new e({name:"analytics-engine",version:d,instructions:'# Barivia Mapping Analytics Engine\n\nSelf-organizing map (SOM) analytics: project high-dimensional data to a 2D grid for clusters, gradients, and anomalies.\n\n## Workflow (short)\n\nUpload (`datasets(upload)`) → `datasets(preview)` and `datasets(analyze)` before train → submit one of `train(action=map)`, `train(action=siom_map)`, or `train(action=floop_siom)` (only if plan allows FLooP) → poll `jobs(status)` every 10–15s until `completed` → `results(get)` for metrics and figures (there is no separate analyze tool), then `results_explorer(job_id)` to browse the figures interactively. Then `jobs(compare)`, `results(download/recolor)`, or `inference` (predict / compare / project_columns / impute_column / transition_flow) as needed.\n\n**Full detail:** Call `guide_barsom_workflow` for plan-scoped tool map, training modes, async rules, optional MCP App UIs, and step-by-step SOP (from the Barivia API when online).\n\n## Tool taxonomy (one tool per stage — do not mix)\n\n| Stage | Tool(s) | Contract |\n|-------|---------|----------|\n| Data | `datasets` | upload, preview, analyze, list, **get** (status/staging), subset, add_expression, reduce_spectral, delete — ingest + feature engineering, no map |\n| Train submit | `train` | map, siom_map, impute (sparse → map + dense imputed.csv), floop_siom (entitled). Async, returns job_id. Submit only |\n| Lifecycle | `jobs` | status, list, compare (**training runs**), cancel, delete. No training/scoring here |\n| Artifacts | `results` | get (default=combined only; figures="none" for metrics-only sweeps; figures="all" or results_explorer/download for every plot), export, download, recolor (async). Read/render completed jobs |\n| Frozen-map ops | `inference` | predict (regime-aware; "compact"|"annotated"), batch_predict, impute_column (neighbor-pool fill for a non-training column), compare (cohort density-diff), project_columns, transition_flow (time-ordered rows only), report. No weight updates |\n| Account | `account` | status, burst/compute actions, history, add_funds |\n| Bootstrap | `guide_barsom_workflow`, `train`, `training_guidance`, `prepare_training` prompt | orientation, submit map/siom/impute/floop, parameter hints, narrative checklist (API/tier-scoped) |\n| Explore (UI) | `results_explorer`, `training_monitor` | richer UX over the same API; `jobs(status)`+`results(get)` still suffice headless |\n| Other | `send_feedback` | only after user agrees |\n\n### Avoid these mis-routes\n- `jobs(compare)` = metrics table across **training runs** (pick best in a sweep). `inference(compare)` = density-diff of one map vs a **second dataset** (drift/cohort). Different tools.\n- Post-training map analysis (predict, compare, project_columns, impute_column, **transition_flow**) is all **inference** — not `results`. `results` is presentation/artifacts only (`recolor` stays here: it re-renders, it doesn\'t touch data).\n- Project a quantity onto the map: existing columns → `inference(project_columns)`; a formula → `datasets(add_expression, project_onto_job=<job>)`.\n\n## Async pattern\n\n- **Manual poll:** Training submits return `job_id` immediately — poll `jobs(status)` every 10–15s. **Running is not failed**; large grids or FLooP-SIOM can take many minutes. `max_nodes` (FLooP) is a total node budget, not grid side length.\n- **Often auto-polled:** `inference` actions (incl. `transition_flow`) and `results(recolor)` may wait in-proxy; if you get a `job_id`, poll `jobs(status)` the same way.\n\nCredits: jobs consume compute credits; check `account(status)` before big runs. Per-request HTTP timeout defaults to 60s (`datasets(analyze)` is async + auto-polled, so it is not bound by it); on slow networks raise `BARIVIA_FETCH_TIMEOUT_MS`. Timeouts are NOT auto-retried (avoids duplicating slow/expensive work).\n\n## Constraints\n\n- Prep ladder: `prepare_training` prompt = narrative checklist; `training_guidance` = structured hints (presets, resolved normalization/transforms, grid/epochs). Review these, then submit with `train`. Do not guess tiers or FLooP entitlement.\n- `inference(predict)`: prefer `dataset_id` for batch and for SIOM/irregular maps; single-row `rows` uses a fast path that can fail on some topologies — retry with `dataset_id`. FLooP-SIOM: if predict jobs fail while grid SIOM works, capture errors + `job_id`.\n- Column names are case-sensitive — match `datasets(preview)`.\n- Default training path is numeric/cyclic/temporal; use explicit `categorical_features` for baseline categoricals. `predict` must match the model contract.\n- After `results(recolor)`, `inference(transition_flow)`, or `inference(project_columns)`, use the **new** `job_id` returned for follow-up `results` if applicable.\n- **Large results / MCP timeouts:** For sweeps, `jobs(compare)`, or metrics-only reads use `results(action=get, figures="none")`. Default `get` inlines combined only. For every plot use `results_explorer` or `results(action=download)`, not `figures="all"` in chat unless the user wants all images inline.'});s(T,y,y,{mimeType:n},async()=>{const e=await p("results-explorer");return{contents:[{uri:y,mimeType:n,text:e??"<html><body>Results Explorer view not built yet. Run: npm run build:views</body></html>"}]}}),s(T,A,A,{mimeType:n},async()=>{const e=await p("training-monitor");return{contents:[{uri:A,mimeType:n,text:e??"<html><body>Training Monitor view not built yet.</body></html>"}]}}),x(T),w(T),I(T),f(T),g(T),h(T,_),b(T),j(T),v(T),P(T),k(T),T.prompt("info","Short orientation for the Barivia Mapping MCP. For full plan-scoped workflow, tool map, and SOP, the model should call guide_barsom_workflow. Use when the user asks what this MCP can do or how to get started.",{},()=>({messages:[{role:"user",content:{type:"text",text:["Give a concise, scannable answer (headers + bullets):","","**What it is:** MCP client to the Barivia mapping engine (2D SOM / SIOM / FLooP-SIOM when entitled) over HTTPS.","","**First step:** Call `guide_barsom_workflow` for plan-scoped bootstrap (full tool list, async rules, training modes, optional MCP Apps, SOP).","","**Core path:** `datasets(upload)` → `datasets(preview)` + `datasets(analyze)` → choose training action → poll `jobs(status)` every 10–15s until completed → `results(get)` (all main figures/metrics; no separate analyze tool) → `results_explorer(job_id)` to browse figures.","",'**Key tools:** `datasets` (data; reduce_spectral for spectra/long blocks), `train` (map/siom_map/impute/floop_siom; pass `label` for readable compare rows; preset=quick for a fast first map), `jobs` (status/list/compare/cancel/delete — lifecycle only), `results` (get/download/export/recolor; figures="none" for metrics-only), `inference` (predict; batch_predict; impute_column for topology-neighbor pool fill; compare; project_columns; transition_flow; report), `account` (status/credits/queue).',"","**Prep help:** `prepare_training` prompt (checklist) · `training_guidance` (presets/JSON hints + resolved normalization/params) — review, then submit with `train`.","","**Browse results:** `results_explorer(job_id)` is the preferred way to explore figures after a first `results(get)` glance; `training_monitor` shows live progress. Both optional — `results` + `jobs(status)` suffice headless.","","**After training:** `jobs(compare)` across runs, `results(recolor)`, `inference(project_columns)` for variables not in training, `inference(transition_flow)` only if rows are time-ordered.","","**Rules:** Running ≠ failed. Column names must match `datasets(preview)` exactly. Do not call `_fetch_figure` from chat (host/UI only); use `results(get)` or `results_explorer`.","","Offer `send_feedback` only after asking the user."].join("\n")}}]})),T.prompt("prepare_training","Narrative pre-training checklist (prompt). Use after upload and before train. Content is tier-scoped from the API when online. Prep ladder: this prompt = story checklist; training_guidance tool = JSON presets/parameter hints. Review, then submit with train.",{dataset_id:o.string().describe("Dataset ID to prepare for training")},async({dataset_id:e})=>({messages:[{role:"user",content:{type:"text",text:await M(e)}}]}));const S=new t;(async function(){try{const e=await i(l,c,p);m(e)}catch(e){process.env.BARIVIA_VIZ_PORT&&console.error("Barivia viz server failed to start:",e)}const e=T.server;e.oninitialized=()=>{const t=e.getClientCapabilities(),o=r(t);u(!!o?.mimeTypes?.includes(n))},await T.connect(S)})().catch(console.error);
|
|
2
|
+
import{McpServer as e}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as t}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as o}from"zod";import{getUiCapability as r,registerAppResource as s,RESOURCE_MIME_TYPE as n}from"@modelcontextprotocol/ext-apps/server";import{startVizServer as i}from"./viz-server.js";import{API_KEY as a,apiCall as l,apiRawCall as c,loadViewHtml as p,setVizPort as m,setClientSupportsMcpApps as u,CLIENT_VERSION as d}from"./shared.js";import{registerDatasetsTool as f}from"./tools/datasets.js";import{registerTrainTool as g}from"./tools/train.js";import{registerJobsTool as h,JOBS_DESCRIPTION_BASE as _}from"./tools/jobs.js";import{registerResultsTool as b}from"./tools/results.js";import{registerExploreMapTool as w,RESULTS_EXPLORER_URI as y}from"./tools/explore_map.js";import{registerAccountTool as j}from"./tools/account.js";import{registerInferenceTool as v}from"./tools/inference.js";import{registerGuideBarsomTool as x}from"./tools/guide_barsom.js";import{registerTrainingGuidanceTool as P}from"./tools/training_guidance.js";import{registerFeedbackTool as k}from"./tools/feedback.js";import{registerTrainingMonitorTool as I,TRAINING_MONITOR_URI as A}from"./tools/training_monitor.js";import{resolvePrepareTrainingPrompt as M}from"./prepare_training_prompt.js";a||(console.error("Error: BARIVIA_API_KEY not set. Set it in your MCP client config."),process.exit(1));const T=new e({name:"analytics-engine",version:d,instructions:'# Barivia Mapping Analytics Engine\n\nSelf-organizing map (SOM) analytics: project high-dimensional data to a 2D grid for clusters, gradients, and anomalies.\n\n## Workflow (short)\n\nUpload (`datasets(upload)`) → `datasets(preview)` and `datasets(analyze)` before train → submit one of `train(action=map)`, `train(action=siom_map)`, or `train(action=floop_siom)` (only if plan allows FLooP) → poll `jobs(status)` every 10–15s until `completed` → `results(get)` for metrics and figures (there is no separate analyze tool), then `results_explorer(job_id)` to browse the figures interactively. Then `jobs(compare)`, `results(download/recolor)`, or `inference` (predict / compare / project_columns / impute_column / transition_flow) as needed.\n\n**Full detail:** Call `guide_barsom_workflow` for plan-scoped tool map, training modes, async rules, optional MCP App UIs, and step-by-step SOP (from the Barivia API when online).\n\n## Tool taxonomy (one tool per stage — do not mix)\n\n| Stage | Tool(s) | Contract |\n|-------|---------|----------|\n| Data | `datasets` | upload, preview, analyze, list, **get** (status/staging), subset, add_expression, reduce_spectral, delete — ingest + feature engineering, no map |\n| Train submit | `train` | map, siom_map, impute (sparse → map + dense imputed.csv), floop_siom (entitled). Async, returns job_id. Submit only |\n| Lifecycle | `jobs` | status, list, compare (**training runs**), cancel, delete. No training/scoring here |\n| Artifacts | `results` | get (default=combined only; figures="none" for metrics-only sweeps; figures="all" or results_explorer/download for every plot), export, download, recolor (async). Read/render completed jobs |\n| Frozen-map ops | `inference` | predict (regime-aware; "compact"|"annotated"), batch_predict, impute_column (neighbor-pool fill for a non-training column), compare (cohort density-diff), project_columns, transition_flow (time-ordered rows only), report. No weight updates |\n| Account | `account` | status, history, add_funds |\n| Bootstrap | `guide_barsom_workflow`, `train`, `training_guidance`, `prepare_training` prompt | orientation, submit map/siom/impute/floop, parameter hints, narrative checklist (API/tier-scoped) |\n| Explore (UI) | `results_explorer`, `training_monitor` | richer UX over the same API; `jobs(status)`+`results(get)` still suffice headless |\n| Other | `send_feedback` | only after user agrees |\n\n### Avoid these mis-routes\n- `jobs(compare)` = metrics table across **training runs** (pick best in a sweep). `inference(compare)` = density-diff of one map vs a **second dataset** (drift/cohort). Different tools.\n- Post-training map analysis (predict, compare, project_columns, impute_column, **transition_flow**) is all **inference** — not `results`. `results` is presentation/artifacts only (`recolor` stays here: it re-renders, it doesn\'t touch data).\n- Project a quantity onto the map: existing columns → `inference(project_columns)`; a formula → `datasets(add_expression, project_onto_job=<job>)`.\n\n## Async pattern\n\n- **Manual poll:** Training submits return `job_id` immediately — poll `jobs(status)` every 10–15s. **Running is not failed**; large grids or FLooP-SIOM can take many minutes. `max_nodes` (FLooP) is a total node budget, not grid side length.\n- **Often auto-polled:** `inference` actions (incl. `transition_flow`) and `results(recolor)` may wait in-proxy; if you get a `job_id`, poll `jobs(status)` the same way.\n\nCredits: jobs consume compute credits; check `account(status)` before big runs. Per-request HTTP timeout defaults to 60s (`datasets(analyze)` is async + auto-polled, so it is not bound by it); on slow networks raise `BARIVIA_FETCH_TIMEOUT_MS`. Timeouts are NOT auto-retried (avoids duplicating slow/expensive work).\n\n## Constraints\n\n- Prep ladder: `prepare_training` prompt = narrative checklist; `training_guidance` = structured hints (presets, resolved normalization/transforms, grid/epochs). Review these, then submit with `train`. Do not guess tiers or FLooP entitlement.\n- `inference(predict)`: prefer `dataset_id` for batch and for SIOM/irregular maps; single-row `rows` uses a fast path that can fail on some topologies — retry with `dataset_id`. FLooP-SIOM: if predict jobs fail while grid SIOM works, capture errors + `job_id`.\n- Column names are case-sensitive — match `datasets(preview)`.\n- Default training path is numeric/cyclic/temporal; use explicit `categorical_features` for baseline categoricals. `predict` must match the model contract.\n- After `results(recolor)`, `inference(transition_flow)`, or `inference(project_columns)`, use the **new** `job_id` returned for follow-up `results` if applicable.\n- **Large results / MCP timeouts:** For sweeps, `jobs(compare)`, or metrics-only reads use `results(action=get, figures="none")`. Default `get` inlines combined only. For every plot use `results_explorer` or `results(action=download)`, not `figures="all"` in chat unless the user wants all images inline.'});s(T,y,y,{mimeType:n},async()=>{const e=await p("results-explorer");return{contents:[{uri:y,mimeType:n,text:e??"<html><body>Results Explorer view not built yet. Run: npm run build:views</body></html>"}]}}),s(T,A,A,{mimeType:n},async()=>{const e=await p("training-monitor");return{contents:[{uri:A,mimeType:n,text:e??"<html><body>Training Monitor view not built yet.</body></html>"}]}}),x(T),w(T),I(T),f(T),g(T),h(T,_),b(T),j(T),v(T),P(T),k(T),T.prompt("info","Short orientation for the Barivia Mapping MCP. For full plan-scoped workflow, tool map, and SOP, the model should call guide_barsom_workflow. Use when the user asks what this MCP can do or how to get started.",{},()=>({messages:[{role:"user",content:{type:"text",text:["Give a concise, scannable answer (headers + bullets):","","**What it is:** MCP client to the Barivia mapping engine (2D SOM / SIOM / FLooP-SIOM when entitled) over HTTPS.","","**First step:** Call `guide_barsom_workflow` for plan-scoped bootstrap (full tool list, async rules, training modes, optional MCP Apps, SOP).","","**Core path:** `datasets(upload)` → `datasets(preview)` + `datasets(analyze)` → choose training action → poll `jobs(status)` every 10–15s until completed → `results(get)` (all main figures/metrics; no separate analyze tool) → `results_explorer(job_id)` to browse figures.","",'**Key tools:** `datasets` (data; reduce_spectral for spectra/long blocks), `train` (map/siom_map/impute/floop_siom; pass `label` for readable compare rows; preset=quick for a fast first map), `jobs` (status/list/compare/cancel/delete — lifecycle only), `results` (get/download/export/recolor; figures="none" for metrics-only), `inference` (predict; batch_predict; impute_column for topology-neighbor pool fill; compare; project_columns; transition_flow; report), `account` (status/credits/queue).',"","**Prep help:** `prepare_training` prompt (checklist) · `training_guidance` (presets/JSON hints + resolved normalization/params) — review, then submit with `train`.","","**Browse results:** `results_explorer(job_id)` is the preferred way to explore figures after a first `results(get)` glance; `training_monitor` shows live progress. Both optional — `results` + `jobs(status)` suffice headless.","","**After training:** `jobs(compare)` across runs, `results(recolor)`, `inference(project_columns)` for variables not in training, `inference(transition_flow)` only if rows are time-ordered.","","**Rules:** Running ≠ failed. Column names must match `datasets(preview)` exactly. Do not call `_fetch_figure` from chat (host/UI only); use `results(get)` or `results_explorer`.","","Offer `send_feedback` only after asking the user."].join("\n")}}]})),T.prompt("prepare_training","Narrative pre-training checklist (prompt). Use after upload and before train. Content is tier-scoped from the API when online. Prep ladder: this prompt = story checklist; training_guidance tool = JSON presets/parameter hints. Review, then submit with train.",{dataset_id:o.string().describe("Dataset ID to prepare for training")},async({dataset_id:e})=>{const t=await M(e);return{messages:[{role:"user",content:{type:"text",text:(t.used_fallback?`[offline fallback ${t.fallback_version}]\n\n`:"")+t.text}}]}});const S=new t;(async function(){try{const e=await i(l,c,p);m(e)}catch(e){process.env.BARIVIA_VIZ_PORT&&console.error("Barivia viz server failed to start:",e)}const e=T.server;e.oninitialized=()=>{const t=e.getClientCapabilities(),o=r(t);u(!!o?.mimeTypes?.includes(n))},await T.connect(S)})().catch(console.error);
|
|
@@ -70,6 +70,17 @@ export function formatJobStatusText(job_id, data) {
|
|
|
70
70
|
if (epoch != null && totalEpochs != null && totalEpochs > 0) {
|
|
71
71
|
parts.push(`epoch ${epoch}/${totalEpochs}`);
|
|
72
72
|
}
|
|
73
|
+
const panelTe = data.panel_te != null && !Number.isNaN(Number(data.panel_te))
|
|
74
|
+
? Number(data.panel_te)
|
|
75
|
+
: data.panel_topographic_error != null && !Number.isNaN(Number(data.panel_topographic_error))
|
|
76
|
+
? Number(data.panel_topographic_error)
|
|
77
|
+
: null;
|
|
78
|
+
if (panelTe != null) {
|
|
79
|
+
parts.push(`panel_te: ${panelTe.toFixed(4)}`);
|
|
80
|
+
}
|
|
81
|
+
if (data.kernel_complete === true) {
|
|
82
|
+
parts.push("kernel_complete");
|
|
83
|
+
}
|
|
73
84
|
}
|
|
74
85
|
const datasetRows = data.dataset_rows != null ? Number(data.dataset_rows) : null;
|
|
75
86
|
const datasetName = data.dataset_name != null ? String(data.dataset_name) : null;
|
|
@@ -120,6 +131,18 @@ export function formatJobStatusText(job_id, data) {
|
|
|
120
131
|
else if (status === "cancelled") {
|
|
121
132
|
parts.push(`Cancelled. Poll again to confirm; dataset and partial worker state are unchanged.`);
|
|
122
133
|
}
|
|
134
|
+
const suggested = data.suggested_next_step != null && String(data.suggested_next_step) !== ""
|
|
135
|
+
? String(data.suggested_next_step)
|
|
136
|
+
: status === "completed"
|
|
137
|
+
? `results(action=get, job_id="${job_id}") then results_explorer(job_id="${job_id}")`
|
|
138
|
+
: status === "failed"
|
|
139
|
+
? `Inspect failure_stage/error; fix data or params; resubmit train or jobs.`
|
|
140
|
+
: status === "running" || status === "pending"
|
|
141
|
+
? `jobs(action=status, job_id="${job_id}") again in 10–15s (or training_monitor).`
|
|
142
|
+
: null;
|
|
143
|
+
if (suggested) {
|
|
144
|
+
parts.push(`suggested_next_step: ${suggested}`);
|
|
145
|
+
}
|
|
123
146
|
return parts.join(" | ");
|
|
124
147
|
}
|
|
125
148
|
export function formatJobCancelText(job_id, data) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { apiCall } from "./shared.js";
|
|
2
|
+
export const PREPARE_TRAINING_FALLBACK_VERSION = "2026.07-offline";
|
|
2
3
|
const FALLBACK_LINES = [
|
|
3
4
|
"Before upload: for train(action=map) ensure training columns have no NaNs; for train(action=impute) missing cells in training columns are OK (plain numeric only).",
|
|
4
5
|
'Please run datasets(action=preview, dataset_id="{id}") to inspect columns, then datasets(action=analyze, dataset_id="{id}") to see which columns and temporal periods are most informative.',
|
|
@@ -9,16 +10,24 @@ const FALLBACK_LINES = [
|
|
|
9
10
|
'- train(action=floop_siom, dataset_id="{id}", ...) for FLooP-SIOM (default topology=free / CHL; optional topology=chain)',
|
|
10
11
|
];
|
|
11
12
|
/** Used by the `prepare_training` MCP prompt; prefers tier-scoped text from the API when online. */
|
|
12
|
-
export async function
|
|
13
|
-
|
|
13
|
+
export async function resolvePrepareTrainingPrompt(datasetId) {
|
|
14
|
+
const fallbackText = FALLBACK_LINES.join("\n").replaceAll("{id}", datasetId);
|
|
14
15
|
try {
|
|
15
16
|
const data = (await apiCall("GET", `/v1/docs/prepare_training?dataset_id=${datasetId}`));
|
|
16
17
|
if (typeof data.prompt === "string" && data.prompt.trim()) {
|
|
17
|
-
|
|
18
|
+
return { text: data.prompt.trim(), used_fallback: false, fallback_version: null };
|
|
18
19
|
}
|
|
19
20
|
}
|
|
20
21
|
catch {
|
|
21
|
-
//
|
|
22
|
+
// fall through
|
|
22
23
|
}
|
|
23
|
-
return
|
|
24
|
+
return {
|
|
25
|
+
text: fallbackText,
|
|
26
|
+
used_fallback: true,
|
|
27
|
+
fallback_version: PREPARE_TRAINING_FALLBACK_VERSION,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/** @deprecated Prefer resolvePrepareTrainingPrompt for structured fallback metadata. */
|
|
31
|
+
export async function resolvePrepareTrainingPromptText(datasetId) {
|
|
32
|
+
return (await resolvePrepareTrainingPrompt(datasetId)).text;
|
|
24
33
|
}
|
package/dist/shared.js
CHANGED
|
@@ -35,7 +35,7 @@ function newRequestId() {
|
|
|
35
35
|
* X-Barsom-Client-Version so the server can annotate tool guidance with the
|
|
36
36
|
* wrapper version each action requires. Keep in sync with package.json on bump.
|
|
37
37
|
*/
|
|
38
|
-
export const CLIENT_VERSION = "0.22.
|
|
38
|
+
export const CLIENT_VERSION = "0.22.2";
|
|
39
39
|
/** User-facing links; keep aligned with barivia.se / api.barivia.se. */
|
|
40
40
|
export const PUBLIC_SITE_ORIGIN = "https://barivia.se";
|
|
41
41
|
/** Self-serve account dashboard (manage plan, billing, and API keys). */
|
package/dist/tools/account.js
CHANGED
|
@@ -2,28 +2,23 @@ import { z } from "zod";
|
|
|
2
2
|
import { registerAuditedTool } from "../audit.js";
|
|
3
3
|
import { apiCall } from "../shared.js";
|
|
4
4
|
export function registerAccountTool(server) {
|
|
5
|
-
registerAuditedTool(server, "account", `Manage your Barivia account — check plan/license info,
|
|
5
|
+
registerAuditedTool(server, "account", `Manage your Barivia account — check plan/license info, view usage history, add funds.
|
|
6
6
|
|
|
7
7
|
| Action | Use when |
|
|
8
8
|
|--------|----------|
|
|
9
9
|
| status | Before large jobs — see plan tier, GPU availability, queue depth, training time estimates, credit balance |
|
|
10
|
-
|
|
|
11
|
-
| compute_status | Checking if a burst lease is active and how much time remains |
|
|
12
|
-
| release_compute | Manually terminating an active lease to stop billing |
|
|
13
|
-
| history | Viewing recent compute leases and credit spend |
|
|
10
|
+
| history | Viewing recent compute usage and credit spend |
|
|
14
11
|
| add_funds | Getting instructions to add credits |
|
|
15
12
|
|
|
16
13
|
action=status: Returns plan tier, compute class (CPU/GPU), usage limits, live queue state, training time estimates, and credit balance.
|
|
17
14
|
Use BEFORE large jobs to check GPU availability and estimate wait time.
|
|
18
|
-
|
|
19
|
-
NOT FOR: Training itself — use train(action=map). This tool only manages the account
|
|
15
|
+
Capacity is shared LOCAL/GKE worker pools — there is no per-key cloud burst lease.
|
|
16
|
+
NOT FOR: Training itself — use train(action=map). This tool only manages the account.`, {
|
|
20
17
|
action: z
|
|
21
|
-
.enum(["status", "
|
|
22
|
-
.describe("status: plan/license/queue info;
|
|
23
|
-
tier: z.string().optional().describe("action=request_compute: tier ID — CPU: cpu-mini, cpu-2, cpu-8..cpu-48 (C7i); GPU: gpu-t4-small, gpu-t4..gpu-t4xxx, gpu-t4-12x (G4dn), gpu-p4d (P4d), gpu-l4, gpu-a10. Omit to list options."),
|
|
24
|
-
duration_minutes: z.number().optional().describe("action=request_compute: lease duration in minutes (default: 60)"),
|
|
18
|
+
.enum(["status", "history", "add_funds"])
|
|
19
|
+
.describe("status: plan/license/queue info; history: recent compute usage; add_funds: instructions"),
|
|
25
20
|
limit: z.number().optional().describe("action=history: number of records to return (default: 10)"),
|
|
26
|
-
}, async ({ action,
|
|
21
|
+
}, async ({ action, limit }) => {
|
|
27
22
|
if (action === "status") {
|
|
28
23
|
const data = (await apiCall("GET", "/v1/system/info"));
|
|
29
24
|
const plan = data.plan ?? {};
|
|
@@ -35,7 +30,6 @@ NOT FOR: Training itself — use train(action=map). This tool only manages the a
|
|
|
35
30
|
const computeDesc = gpuEnabled ? (backend.gpu_model ? `GPU (${backend.gpu_model}${backend.gpu_vram_gb ? `, ${backend.gpu_vram_gb}GB` : ""})` : "GPU") : "CPU only";
|
|
36
31
|
const fmtLimit = (v) => v === -1 || v === "-1" ? "unlimited" : String(v ?? "?");
|
|
37
32
|
const historyData = await apiCall("GET", "/v1/compute/history?limit=5").catch(() => null);
|
|
38
|
-
const leaseData = await apiCall("GET", "/v1/compute/lease").catch(() => null);
|
|
39
33
|
const algoNames = {
|
|
40
34
|
train_som: "SOM",
|
|
41
35
|
train_impute: "Imputation",
|
|
@@ -62,7 +56,6 @@ NOT FOR: Training itself — use train(action=map). This tool only manages the a
|
|
|
62
56
|
lines.push(` Credits: $${(historyData.credit_balance_cents / 100).toFixed(2)} remaining`);
|
|
63
57
|
}
|
|
64
58
|
else {
|
|
65
|
-
// Unknown billing mode: avoid misleading $0.00 for postpaid accounts
|
|
66
59
|
const cents = historyData.credit_balance_cents ?? 0;
|
|
67
60
|
if (cents === 0) {
|
|
68
61
|
lines.push(` Credits: N/A (postpaid or zero balance — check plan)`);
|
|
@@ -74,9 +67,6 @@ NOT FOR: Training itself — use train(action=map). This tool only manages the a
|
|
|
74
67
|
}
|
|
75
68
|
if (backend.memory_gb)
|
|
76
69
|
lines.push(` Backend Memory: ${backend.memory_gb} GB`);
|
|
77
|
-
if (leaseData && leaseData.lease_id) {
|
|
78
|
-
lines.push(``, `Active Burst Lease: ${leaseData.tier} | ${Math.round(leaseData.time_remaining_ms / 60000)} min left`);
|
|
79
|
-
}
|
|
80
70
|
const running = Number(status.running_jobs ?? data.running_jobs ?? 0);
|
|
81
71
|
const pending = Number(status.pending_jobs ?? data.pending_jobs ?? 0);
|
|
82
72
|
const compute_eta = Number(estimates?.total || 0);
|
|
@@ -92,63 +82,6 @@ NOT FOR: Training itself — use train(action=map). This tool only manages the a
|
|
|
92
82
|
}
|
|
93
83
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
94
84
|
}
|
|
95
|
-
if (action === "request_compute") {
|
|
96
|
-
if (!tier) {
|
|
97
|
-
try {
|
|
98
|
-
const tiersData = await apiCall("GET", "/v1/compute/tiers");
|
|
99
|
-
const lines = ["Available Compute Tiers:"];
|
|
100
|
-
for (const [tId, tData] of Object.entries(tiersData.tiers)) {
|
|
101
|
-
lines.push(` ${tId}: ${tData.desc}`);
|
|
102
|
-
}
|
|
103
|
-
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
104
|
-
}
|
|
105
|
-
catch (err) {
|
|
106
|
-
const msg = err?.message ?? "Failed to fetch compute tiers. Ensure you have burst access enabled.";
|
|
107
|
-
return { content: [{ type: "text", text: msg }] };
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
const data = await apiCall("POST", "/v1/compute/lease", { tier, duration_minutes });
|
|
111
|
-
const billingLine = data.billing_mode === "postpaid"
|
|
112
|
-
? `Billing: Postpaid (usage logged, billed retrospectively)\nAccrued Balance: $${(data.credit_balance_cents / 100).toFixed(2)}`
|
|
113
|
-
: `Credits Remaining After Reserve: $${(data.credit_balance_cents / 100).toFixed(2)}`;
|
|
114
|
-
return {
|
|
115
|
-
content: [{ type: "text", text: `Compute Lease Requested:
|
|
116
|
-
Lease ID: ${data.lease_id}
|
|
117
|
-
Status: ${data.status}
|
|
118
|
-
Estimated Wait: ${data.estimated_wait_minutes} minutes
|
|
119
|
-
Estimated Cost: $${(data.estimated_cost_cents / 100).toFixed(2)}
|
|
120
|
-
${billingLine}
|
|
121
|
-
|
|
122
|
-
IMPORTANT: Cloud burst active. Data is pulled from shared Cloudflare R2, so you do NOT need to re-upload datasets. Just wait ~3 minutes and check status.` }]
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
if (action === "compute_status") {
|
|
126
|
-
const data = await apiCall("GET", "/v1/compute/lease");
|
|
127
|
-
if (data.status === "none" || !data.lease_id) {
|
|
128
|
-
return { content: [{ type: "text", text: "No active lease -- running on default Primary Server." }] };
|
|
129
|
-
}
|
|
130
|
-
return {
|
|
131
|
-
content: [{ type: "text", text: `Active Compute Lease:
|
|
132
|
-
Lease ID: ${data.lease_id}
|
|
133
|
-
Status: ${data.status}
|
|
134
|
-
Tier: ${data.tier} (${data.instance_type})
|
|
135
|
-
Time Remaining: ${Math.round(data.time_remaining_ms / 60000)} minutes` }]
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
if (action === "release_compute") {
|
|
139
|
-
const data = await apiCall("DELETE", "/v1/compute/lease");
|
|
140
|
-
const costLine = data.billing_mode === "postpaid"
|
|
141
|
-
? `Cost Logged: $${(data.cost_cents / 100).toFixed(2)} (postpaid — billed retrospectively)`
|
|
142
|
-
: `Credits Deducted: $${((data.credits_deducted || data.cost_cents) / 100).toFixed(2)}`;
|
|
143
|
-
return {
|
|
144
|
-
content: [{ type: "text", text: `Compute Released:
|
|
145
|
-
Duration Billed: ${data.duration_minutes} minutes
|
|
146
|
-
${costLine}
|
|
147
|
-
Balance: $${(data.final_balance_cents / 100).toFixed(2)}
|
|
148
|
-
|
|
149
|
-
Routing reverted to default Primary Server.` }]
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
85
|
if (action === "history") {
|
|
153
86
|
const data = await apiCall("GET", `/v1/compute/history?limit=${limit || 10}`);
|
|
154
87
|
const history = data.history.map((h) => `- ${h.started_at} | ${h.tier} | ${h.duration_minutes} min | $${(h.credits_charged / 100).toFixed(2)}`).join("\n");
|
|
@@ -159,11 +92,11 @@ Routing reverted to default Primary Server.` }]
|
|
|
159
92
|
if (action === "add_funds") {
|
|
160
93
|
return {
|
|
161
94
|
content: [{ type: "text", text: `To add funds to your account, please visit the Barivia Billing Portal (integration pending) or ask your administrator to use the CLI tool:
|
|
162
|
-
bash scripts/manage-credits.sh add <org_id> <amount_usd>` }]
|
|
95
|
+
bash scripts/billing/manage-credits.sh add <org_id> <amount_usd>` }]
|
|
163
96
|
};
|
|
164
97
|
}
|
|
165
98
|
return {
|
|
166
|
-
content: [{ type: "text", text: `Unknown action: ${action}. Valid: status,
|
|
99
|
+
content: [{ type: "text", text: `Unknown action: ${action}. Valid: status, history, add_funds.` }]
|
|
167
100
|
};
|
|
168
101
|
});
|
|
169
102
|
}
|
package/dist/tools/datasets.js
CHANGED
|
@@ -4,7 +4,7 @@ import { gzipSync } from "node:zlib";
|
|
|
4
4
|
import { createHash } from "node:crypto";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import { registerAuditedTool } from "../audit.js";
|
|
7
|
-
import { apiCall, getWorkspaceRootAsync, resolveFilePathForUpload, textResult, pollUntilComplete, POLL_DERIVE_MAX_MS, POLL_ANALYZE_MAX_MS, UPLOAD_DATASET_TIMEOUT_MS, LARGE_UPLOAD_BYTES, PRESIGNED_PUT_TIMEOUT_MS, POLL_STAGE_MAX_MS, streamFileSha256, putPresignedStream, resolveUploadContentType, suggestDatasetPreview, } from "../shared.js";
|
|
7
|
+
import { apiCall, getWorkspaceRootAsync, resolveFilePathForUpload, textResult, structuredTextResult, pollUntilComplete, POLL_DERIVE_MAX_MS, POLL_ANALYZE_MAX_MS, UPLOAD_DATASET_TIMEOUT_MS, LARGE_UPLOAD_BYTES, PRESIGNED_PUT_TIMEOUT_MS, POLL_STAGE_MAX_MS, streamFileSha256, putPresignedStream, resolveUploadContentType, suggestDatasetPreview, } from "../shared.js";
|
|
8
8
|
import { GZIP_UPLOAD_HINT } from "../job_status_format.js";
|
|
9
9
|
/**
|
|
10
10
|
* Normalize a nullable string field from the API. Returns "" for absent values,
|
|
@@ -481,10 +481,20 @@ ESCALATION: If upload fails with column errors, open the file locally and verify
|
|
|
481
481
|
throw new Error("analyze did not return a job id");
|
|
482
482
|
const poll = await pollUntilComplete(jobId, POLL_ANALYZE_MAX_MS);
|
|
483
483
|
if (poll.status === "failed") {
|
|
484
|
-
|
|
484
|
+
const rid = poll.request_id;
|
|
485
|
+
const errText = `datasets(analyze) job ${jobId} failed: ${poll.error ?? "unknown error"}`;
|
|
486
|
+
return structuredTextResult({
|
|
487
|
+
error: true,
|
|
488
|
+
action: "analyze",
|
|
489
|
+
job_id: jobId,
|
|
490
|
+
request_id: rid ?? null,
|
|
491
|
+
message: errText,
|
|
492
|
+
suggested_next_step: "If the dataset is very large, try datasets(action=subset, sample_n=10000) then analyze again.",
|
|
493
|
+
}, errText + (rid ? ` (request_id=${rid})` : ""));
|
|
485
494
|
}
|
|
486
495
|
if (poll.status !== "completed") {
|
|
487
|
-
|
|
496
|
+
const text = `datasets(analyze) job ${jobId} is still running. Poll with jobs(action=status, job_id="${jobId}") then results(action=get, job_id="${jobId}").`;
|
|
497
|
+
return structuredTextResult({ error: false, action: "analyze", job_id: jobId, status: poll.status, message: text }, text);
|
|
488
498
|
}
|
|
489
499
|
const results = (await apiCall("GET", `/v1/results/${jobId}`));
|
|
490
500
|
data = (results.summary ?? results);
|