@barivia/barsom-mcp 0.11.1 → 0.13.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 +5 -6
- package/dist/index.js +1 -1
- package/dist/shared.js +1 -1
- package/dist/tools/datasets.js +53 -6
- package/dist/tools/explore_map.js +6 -7
- package/dist/tools/inference.js +3 -5
- package/dist/tools/jobs.js +11 -22
- package/dist/tools/training_prep.js +28 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @barivia/barsom-mcp
|
|
2
2
|
|
|
3
|
-
MCP proxy for the Barivia Analytics Engine — connects any stdio MCP client (Cursor, Claude Desktop, etc.) to the barSOM cloud API. The npm package is **`@barivia/barsom-mcp`**; many configs label the server **`analytics-engine`** (the MCP server name in the client JSON). **`guide_barsom_workflow`** is the canonical bootstrap: it loads plan-scoped workflow text from the Barivia API when online (tool map, async rules, training modes, SOP). If the API is unreachable, it returns a short offline stub. Core tools are `datasets`, `jobs` (`train_map`, `train_siom_map`, `train_floop_siom` where entitled
|
|
3
|
+
MCP proxy for the Barivia Analytics Engine — connects any stdio MCP client (Cursor, Claude Desktop, etc.) to the barSOM cloud API. The npm package is **`@barivia/barsom-mcp`**; many configs label the server **`analytics-engine`** (the MCP server name in the client JSON). **`guide_barsom_workflow`** is the canonical bootstrap: it loads plan-scoped workflow text from the Barivia API when online (tool map, async rules, training modes, SOP). If the API is unreachable, it returns a short offline stub. Core tools are `datasets`, `jobs` (`train_map`, `train_siom_map`, `train_floop_siom` where entitled), `results`, and `inference`. **Optional MCP App tools** (`training_prep`, `training_monitor`, `results_explorer`) add embedded or localhost viz; they are **not required** to complete upload → train → poll → results.
|
|
4
4
|
|
|
5
5
|
**Pre-training help (pick one):** **`prepare_training`** prompt = narrative checklist (tier-scoped from the API when online); **`training_guidance`** tool = structured presets and parameter hints; **`training_prep`** tool = interactive UI plus **`submit_prepared_training`**.
|
|
6
6
|
|
|
@@ -75,7 +75,7 @@ All multi-action tools follow the `datasets` / `jobs` / `results` / `inference`
|
|
|
75
75
|
| Inference | `inference` |
|
|
76
76
|
| Account | `account` |
|
|
77
77
|
| Optional UI | `training_prep`, `training_monitor`, `results_explorer` |
|
|
78
|
-
| Advanced
|
|
78
|
+
| Advanced | `training_guidance` |
|
|
79
79
|
|
|
80
80
|
Agents should not call **`_fetch_figure`** from chat; it exists for the Results Explorer MCP App host. Use `results(action=get)` or `results_explorer` instead.
|
|
81
81
|
|
|
@@ -103,7 +103,6 @@ Call at the **start of mapping work** (or when the user asks what the MCP can do
|
|
|
103
103
|
| `train_impute` | Sparse training data: accelerated missSOM / SIOM missSOM — trains a map and imputes missing cells in one job. Defaults: `model=auto`, `cv_folds=5`. Returns map artifacts + `imputed.csv`, `imputation_mask.csv`, optional `quality.csv` / `imputation_uncertainty.csv`. `quality.csv` reports two labeled numbers per column: `kfold_holdout_pool` (honest held-out) and `resubstitution_optimistic` (in-sample, optimistic — not validation). Plain numeric columns only. Valid parent for `inference(impute_column)`. |
|
|
104
104
|
| `train_siom_map` | Submitting a self-interacting map training job — same grid-map workflow plus SIOM controls such as `gamma`, `siom_decay`, and penalty selection. |
|
|
105
105
|
| `train_floop_siom` | Submitting a FLooP-SIOM job — growing node-budget manifold; default `topology=free` (CHL); optional `topology=chain` for strict 1D linked list. |
|
|
106
|
-
| `train_floop_chain` | Deprecated alias for `train_floop_siom` — same behavior. |
|
|
107
106
|
| `status` | Polling after any async job — every 10–15s; map jobs expose `progress_phase` (ordering, convergence, cv, artifacts) |
|
|
108
107
|
| `list` | Finding job IDs, checking pipeline state |
|
|
109
108
|
| `compare` | Picking the best run from a set (QE, TE, silhouette table) |
|
|
@@ -154,7 +153,6 @@ All actions use a frozen trained map — no retraining. Derived columns use **`d
|
|
|
154
153
|
| `submit_prepared_training` | Submit the reviewed prep (`review_token` + `explicit_confirm=true`) |
|
|
155
154
|
| `training_monitor` | Visual progress for a `job_id`; optional — `jobs(action=status)` is enough |
|
|
156
155
|
| `results_explorer` | Browse metrics and figures after training completes |
|
|
157
|
-
| `explore_map` | **Deprecated** — alias of `results_explorer`; migrate configs to `results_explorer` |
|
|
158
156
|
| `_fetch_figure` | **Host / App only** — Results Explorer invokes this for one raster figure; not for agent chat |
|
|
159
157
|
|
|
160
158
|
When the client does not advertise MCP Apps support, the proxy starts **`viz-server`** on **`127.0.0.1`** (localhost-only). Tool responses can include `http://127.0.0.1:PORT/viz/...` links. See **Local viz fallback** under Environment Variables.
|
|
@@ -171,7 +169,8 @@ The right viewer depends on **(MCP App support)** **and** **(can the human reach
|
|
|
171
169
|
|
|
172
170
|
### Migration notes
|
|
173
171
|
|
|
174
|
-
- **`explore_map`
|
|
172
|
+
- **`explore_map` removed (0.13.0):** the deprecated alias of `results_explorer` is gone. Update any Cursor / Claude Desktop / MCP config or call site to use **`results_explorer`**.
|
|
173
|
+
- **`train_floop_chain` removed (0.13.0):** the deprecated alias of `jobs(action=train_floop_siom)` is gone. Use **`jobs(action=train_floop_siom)`**.
|
|
175
174
|
- **Shorter `info` prompt:** Clients that relied on the old long `info` text should use **`guide_barsom_workflow`** or server **instructions** for the full story.
|
|
176
175
|
|
|
177
176
|
### `send_feedback`
|
|
@@ -219,7 +218,7 @@ npm run build # Compile to dist/
|
|
|
219
218
|
npm test # Vitest
|
|
220
219
|
```
|
|
221
220
|
|
|
222
|
-
**Post-change spot-check (real MCP client):** confirm `guide_barsom_workflow` appears near the top of the tool list, `explore_map`
|
|
221
|
+
**Post-change spot-check (real MCP client):** confirm `guide_barsom_workflow` appears near the top of the tool list, the removed `explore_map` / `train_floop_chain` aliases are no longer listed, and Results Explorer can still load figures (it uses `_fetch_figure` internally — agents should rely on `results` / `results_explorer`).
|
|
223
222
|
|
|
224
223
|
For local development against a local API stack:
|
|
225
224
|
|
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 n,RESOURCE_MIME_TYPE as s}from"@modelcontextprotocol/ext-apps/server";import{startVizServer as a}from"./viz-server.js";import{API_KEY as i,apiCall as l,apiRawCall as p,loadViewHtml as c,setVizPort as m,setClientSupportsMcpApps as d,CLIENT_VERSION as u}from"./shared.js";import{registerDatasetsTool as f}from"./tools/datasets.js";import{registerJobsTool as g,JOBS_DESCRIPTION_BASE as _}from"./tools/jobs.js";import{registerResultsTool as b}from"./tools/results.js";import{registerExploreMapTool as h,RESULTS_EXPLORER_URI as y}from"./tools/explore_map.js";import{registerAccountTool as w}from"./tools/account.js";import{registerInferenceTool as j}from"./tools/inference.js";import{registerGuideBarsomTool as v}from"./tools/guide_barsom.js";import{registerTrainingGuidanceTool as P}from"./tools/training_guidance.js";import{registerFeedbackTool as x}from"./tools/feedback.js";import{registerTrainingPrepTools as I,TRAINING_PREP_URI as k}from"./tools/training_prep.js";import{registerTrainingMonitorTool as T,TRAINING_MONITOR_URI as M}from"./tools/training_monitor.js";import{resolvePrepareTrainingPromptText as O}from"./prepare_training_prompt.js";i||(console.error("Error: BARIVIA_API_KEY not set. Set it in your MCP client config."),process.exit(1));const A=new e({name:"analytics-engine",version:u,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 `jobs(train_map)`, `jobs(train_siom_map)`, or `jobs(train_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 `jobs(compare)`, `results(download/recolor/transition_flow)`, or `inference` 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 map (compact)\n\n| Area | Tool | Notes |\n|------|------|--------|\n| Data | `datasets` | upload, preview, analyze, list, subset, add_expression, reduce_spectral (pca/log_sample/uniform_sample/stats for long ordered numeric blocks), delete |\n| Jobs | `jobs` | train_map, train_impute (sparse data: map + dense imputed.csv), train_siom_map, train_floop_siom (entitled), status, list, compare, cancel, delete, batch_predict, run_baseline_study
|
|
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 n,RESOURCE_MIME_TYPE as s}from"@modelcontextprotocol/ext-apps/server";import{startVizServer as a}from"./viz-server.js";import{API_KEY as i,apiCall as l,apiRawCall as p,loadViewHtml as c,setVizPort as m,setClientSupportsMcpApps as d,CLIENT_VERSION as u}from"./shared.js";import{registerDatasetsTool as f}from"./tools/datasets.js";import{registerJobsTool as g,JOBS_DESCRIPTION_BASE as _}from"./tools/jobs.js";import{registerResultsTool as b}from"./tools/results.js";import{registerExploreMapTool as h,RESULTS_EXPLORER_URI as y}from"./tools/explore_map.js";import{registerAccountTool as w}from"./tools/account.js";import{registerInferenceTool as j}from"./tools/inference.js";import{registerGuideBarsomTool as v}from"./tools/guide_barsom.js";import{registerTrainingGuidanceTool as P}from"./tools/training_guidance.js";import{registerFeedbackTool as x}from"./tools/feedback.js";import{registerTrainingPrepTools as I,TRAINING_PREP_URI as k}from"./tools/training_prep.js";import{registerTrainingMonitorTool as T,TRAINING_MONITOR_URI as M}from"./tools/training_monitor.js";import{resolvePrepareTrainingPromptText as O}from"./prepare_training_prompt.js";i||(console.error("Error: BARIVIA_API_KEY not set. Set it in your MCP client config."),process.exit(1));const A=new e({name:"analytics-engine",version:u,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 `jobs(train_map)`, `jobs(train_siom_map)`, or `jobs(train_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 `jobs(compare)`, `results(download/recolor/transition_flow)`, or `inference` 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 map (compact)\n\n| Area | Tool | Notes |\n|------|------|--------|\n| Data | `datasets` | upload, preview, analyze, list, subset, add_expression, reduce_spectral (pca/log_sample/uniform_sample/stats for long ordered numeric blocks), delete |\n| Jobs | `jobs` | train_map, train_impute (sparse data: map + dense imputed.csv), train_siom_map, train_floop_siom (entitled), status, list, compare, cancel, delete, batch_predict, run_baseline_study |\n| Results | `results` | get (figures="none" for metrics-only), export, download, recolor (async), transition_flow (async; time-ordered rows only) |\n| Inference | `inference` | predict (regime-aware; output="compact"|"annotated"), impute_column (neighbor-pool fill for a non-training column), compare, project_columns, report |\n| Account | `account` | status, burst/compute actions, history, add_funds |\n| Bootstrap | `guide_barsom_workflow` | orientation + SOP |\n| Parameters | `training_guidance` | presets and field hints (API-scoped) |\n| Prep | `prepare_training` prompt, `training_prep` + `submit_prepared_training` | checklist / interactive UI |\n| Explore | `results_explorer`, `training_monitor` | optional MCP Apps; `jobs(status)` and `results(get)` suffice without them |\n| Other | `send_feedback` | only after user agrees |\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, `results(recolor)`, `results(transition_flow)` 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; `training_prep` = UI + guarded submit. 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 `recolor`, `transition_flow`, or `project_columns`, use the **new** `job_id` returned for follow-up `results` if applicable.'});n(A,y,y,{mimeType:s},async()=>{const e=await c("results-explorer");return{contents:[{uri:y,mimeType:s,text:e??"<html><body>Results Explorer view not built yet. Run: npm run build:views</body></html>"}]}}),n(A,k,k,{mimeType:s},async()=>{const e=await c("training-prep");return{contents:[{uri:k,mimeType:s,text:e??"<html><body>Training Preparation view not built yet.</body></html>"}]}}),n(A,M,M,{mimeType:s},async()=>{const e=await c("training-monitor");return{contents:[{uri:M,mimeType:s,text:e??"<html><body>Training Monitor view not built yet.</body></html>"}]}}),v(A),h(A),I(A),T(A),f(A),g(A,_),b(A),w(A),j(A),P(A),x(A),A.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).","",'**Key tools:** `datasets` (data; reduce_spectral for spectra/long blocks), `jobs` (train/poll/compare/…; train_map accepts an optional `label` for readable compare rows), `results` (get/download/export/recolor/transition_flow; figures="none" for metrics-only), `inference` (predict; impute_column for topology-neighbor pool fill; compare; project_columns; report), `account` (status/credits/queue).',"","**Prep help:** `prepare_training` prompt (checklist) · `training_guidance` (presets/JSON hints) · `training_prep` + `submit_prepared_training` (interactive UI).","","**Optional UI:** `results_explorer`, `training_monitor` — nice for browsing; not required if you use `results` + `jobs(status)`.","","**After training:** `jobs(compare)` across runs, `results(recolor)`, `inference(project_columns)` for variables not in training, `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")}}]})),A.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; training_prep tool = interactive UI + submit_prepared_training.",{dataset_id:o.string().describe("Dataset ID to prepare for training")},async({dataset_id:e})=>({messages:[{role:"user",content:{type:"text",text:await O(e)}}]}));const S=new t;(async function(){try{const e=await a(l,p,c);m(e)}catch(e){process.env.BARIVIA_VIZ_PORT&&console.error("Barivia viz server failed to start:",e)}const e=A.server;e.oninitialized=()=>{const t=e.getClientCapabilities(),o=r(t);d(!!o?.mimeTypes?.includes(s))},await A.connect(S)})().catch(console.error);
|
package/dist/shared.js
CHANGED
|
@@ -24,7 +24,7 @@ export const RETRYABLE_STATUS = new Set([502, 503, 504]);
|
|
|
24
24
|
* X-Barsom-Client-Version so the server can annotate tool guidance with the
|
|
25
25
|
* wrapper version each action requires. Keep in sync with package.json on bump.
|
|
26
26
|
*/
|
|
27
|
-
export const CLIENT_VERSION = "0.
|
|
27
|
+
export const CLIENT_VERSION = "0.13.0";
|
|
28
28
|
/** User-facing links; keep aligned with barivia.se / api.barivia.se. */
|
|
29
29
|
export const PUBLIC_SITE_ORIGIN = "https://barivia.se";
|
|
30
30
|
/** Self-serve account dashboard (manage plan, billing, and API keys). */
|
package/dist/tools/datasets.js
CHANGED
|
@@ -102,14 +102,14 @@ export function registerDatasetsTool(server) {
|
|
|
102
102
|
| analyze | Pre-training column analysis: correlation matrix, periodicity ranking, column recommendations (train / drop / project later). Run after preview, before train_map. |
|
|
103
103
|
| list | Finding dataset IDs for train_map, preview, or subset — see all available datasets |
|
|
104
104
|
| subset | Creating a filtered/sliced view without re-uploading the full CSV |
|
|
105
|
-
| add_expression | Add a computed column from an expression (
|
|
105
|
+
| add_expression | Add a computed column from an expression (dataset_id + name + expression). With project_onto_job=<training_job_id>, instead projects the computed expression onto that map as a component plane in one step (no column added). |
|
|
106
106
|
| reduce_spectral | Collapse a long ordered numeric block (e.g. 1000-point spectrum, time series, sensor fingerprint) into a small per-row feature set so the SOM can train on signal-dense features. Methods: pca, log_sample, uniform_sample, stats. |
|
|
107
107
|
| delete | Cleaning up after experiments or freeing the dataset slot |
|
|
108
108
|
|
|
109
109
|
action=upload: PREFER file_path — server reads from workspace root (token-efficient; no file content in context). Use csv_data only for small inline pastes (e.g. <10KB). Returns dataset ID. Then use datasets(action=preview) before jobs(action=train_map).
|
|
110
110
|
|
|
111
111
|
Step 0 (before upload): (1) CSV with header; one row per observation. (2) Columns you will use for training must be numeric (or datetime if you will use temporal extraction). (3) Complete-case path (jobs action=train_map): no NaNs in training columns. Sparse path (jobs action=train_impute): missing cells in training columns are OK — the job trains missSOM and returns dense imputed.csv; plain numeric columns only. (4) Categoricals: encode (e.g. one-hot, label) or exclude — do not use raw categorical columns as training features; preview shows which columns are non-numeric. (5) Optional: if you plan to use transition_flow, sort rows chronologically before upload.
|
|
112
|
-
Step 1 (after upload):
|
|
112
|
+
Step 1 (after upload): always datasets(action=preview) to verify column types and spot cyclics/datetime, then choose train_map (complete-case) or train_impute (sparse) before submit.
|
|
113
113
|
|
|
114
114
|
action=preview: Show columns, stats, sample rows, cyclic/datetime detections. ALWAYS preview before jobs(action=train_map) on an unfamiliar dataset.
|
|
115
115
|
action=analyze: Pre-training analysis on numeric columns — Pearson correlation, autocorrelation periodicity scores, and column recommendations (train, consider_dropping, project_later, low_variance). Use to choose which columns to include in training and which to project onto the map after training.
|
|
@@ -135,7 +135,7 @@ NOT FOR: Real-time data streams or binary files — upload a snapshot CSV instea
|
|
|
135
135
|
ESCALATION: If upload fails with column errors, open the file locally and verify the header row. If preview shows nulls in training columns: use jobs(action=train_impute) for sparse data, or clean/subset for jobs(action=train_map).`, {
|
|
136
136
|
action: z
|
|
137
137
|
.enum(["upload", "preview", "analyze", "list", "subset", "delete", "add_expression", "reduce_spectral"])
|
|
138
|
-
.describe("upload: add CSV; preview: inspect columns/stats; analyze: pre-training correlation and periodicity; list: see all datasets; subset: create filtered subset; delete: remove dataset; add_expression: add derived column from expression; reduce_spectral: collapse a long ordered numeric block (e.g. spectrum, time series) into a small per-row feature set via PCA / log_sample / uniform_sample / stats"),
|
|
138
|
+
.describe("upload: add CSV; preview: inspect columns/stats; analyze: pre-training correlation and periodicity; list: see all datasets; subset: create filtered subset; delete: remove dataset; add_expression: add derived column from expression (or, with project_onto_job, project the expression onto a trained map as a component plane); reduce_spectral: collapse a long ordered numeric block (e.g. spectrum, time series) into a small per-row feature set via PCA / log_sample / uniform_sample / stats"),
|
|
139
139
|
name: z.string().optional().describe("Dataset name (required for action=upload and subset)"),
|
|
140
140
|
file_path: z.string().optional().describe("Path to local CSV (PREFERRED): absolute path, file:// URI, or relative to workspace root. Token-efficient; server reads file."),
|
|
141
141
|
csv_data: z.string().optional().describe("Inline CSV string for small pastes only (<10KB). Avoid for large files — use file_path instead to avoid token explosion."),
|
|
@@ -179,6 +179,18 @@ ESCALATION: If upload fails with column errors, open the file locally and verify
|
|
|
179
179
|
})
|
|
180
180
|
.optional()
|
|
181
181
|
.describe("action=add_expression: evaluation options (missing, window for rolling)"),
|
|
182
|
+
project_onto_job: z
|
|
183
|
+
.string()
|
|
184
|
+
.optional()
|
|
185
|
+
.describe("action=add_expression: a COMPLETED training job_id. When set, compute the expression and project it onto that map as a component plane in one step (instead of adding a column to the dataset). Columns in the expression are read from dataset_id when given, else the training dataset. Returns a projection job; get the figure via results(action=download)."),
|
|
186
|
+
aggregation: z
|
|
187
|
+
.enum(["mean", "median", "sum", "min", "max", "std", "count"])
|
|
188
|
+
.optional()
|
|
189
|
+
.describe("action=add_expression with project_onto_job: per-node aggregation of the derived value (default mean)."),
|
|
190
|
+
output_format: z
|
|
191
|
+
.enum(["png", "pdf", "svg"])
|
|
192
|
+
.optional()
|
|
193
|
+
.describe("action=add_expression with project_onto_job: component-plane image format (default pdf)."),
|
|
182
194
|
method: z
|
|
183
195
|
.enum(["pca", "log_sample", "uniform_sample", "stats"])
|
|
184
196
|
.optional()
|
|
@@ -204,7 +216,7 @@ ESCALATION: If upload fails with column errors, open the file locally and verify
|
|
|
204
216
|
.int()
|
|
205
217
|
.optional()
|
|
206
218
|
.describe("action=subset: RNG seed for sample_n (default 42; same seed + dataset reproduces the same sample)."),
|
|
207
|
-
}, async ({ action, name, file_path, csv_data, dataset_id, n_rows, row_range, filters, filter, expression, options, method, columns_block, k, sample_n, sample_seed }) => {
|
|
219
|
+
}, async ({ action, name, file_path, csv_data, dataset_id, n_rows, row_range, filters, filter, expression, options, project_onto_job, aggregation, output_format, method, columns_block, k, sample_n, sample_seed }) => {
|
|
208
220
|
if (action === "upload") {
|
|
209
221
|
if (!name)
|
|
210
222
|
throw new Error("datasets(upload) requires name");
|
|
@@ -402,12 +414,47 @@ ESCALATION: If upload fails with column errors, open the file locally and verify
|
|
|
402
414
|
return { content: [{ type: "text", text: formatAnalyzeResult(data, dataset_id) }] };
|
|
403
415
|
}
|
|
404
416
|
if (action === "add_expression") {
|
|
405
|
-
if (!dataset_id)
|
|
406
|
-
throw new Error("datasets(add_expression) requires dataset_id");
|
|
407
417
|
if (!name)
|
|
408
418
|
throw new Error("datasets(add_expression) requires name");
|
|
409
419
|
if (!expression)
|
|
410
420
|
throw new Error("datasets(add_expression) requires expression");
|
|
421
|
+
// One-step formula -> component plane: compute the expression and project
|
|
422
|
+
// it onto a trained map without first materializing a dataset column.
|
|
423
|
+
if (project_onto_job) {
|
|
424
|
+
const body = { name, expression };
|
|
425
|
+
if (aggregation)
|
|
426
|
+
body.aggregation = aggregation;
|
|
427
|
+
if (output_format)
|
|
428
|
+
body.output_format = output_format;
|
|
429
|
+
if (dataset_id)
|
|
430
|
+
body.dataset_id = dataset_id; // optional: read expression columns from a different dataset
|
|
431
|
+
if (options)
|
|
432
|
+
body.options = options;
|
|
433
|
+
const data = (await apiCall("POST", `/v1/results/${project_onto_job}/derive`, body));
|
|
434
|
+
const projJobId = data.id;
|
|
435
|
+
const poll = await pollUntilComplete(projJobId, POLL_DERIVE_MAX_MS);
|
|
436
|
+
if (poll.status === "completed") {
|
|
437
|
+
return {
|
|
438
|
+
content: [
|
|
439
|
+
{
|
|
440
|
+
type: "text",
|
|
441
|
+
text: [
|
|
442
|
+
`Projected derived variable "${name}" onto map (training job ${project_onto_job}).`,
|
|
443
|
+
`Expression: ${expression} | Aggregation: ${aggregation ?? "mean"}`,
|
|
444
|
+
`Component plane ready. Get the figure with results(action=download, job_id="${projJobId}").`,
|
|
445
|
+
].join("\n"),
|
|
446
|
+
},
|
|
447
|
+
],
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
if (poll.status === "failed")
|
|
451
|
+
return { content: [{ type: "text", text: `datasets(add_expression, project_onto_job) job ${projJobId} failed: ${poll.error ?? "unknown error"}` }] };
|
|
452
|
+
return {
|
|
453
|
+
content: [{ type: "text", text: `datasets(add_expression, project_onto_job) job ${projJobId} submitted. Poll with jobs(action=status, job_id="${projJobId}").` }],
|
|
454
|
+
};
|
|
455
|
+
}
|
|
456
|
+
if (!dataset_id)
|
|
457
|
+
throw new Error("datasets(add_expression) requires dataset_id (or project_onto_job to project onto a map)");
|
|
411
458
|
const body = { name, expression };
|
|
412
459
|
if (options)
|
|
413
460
|
body.options = options;
|
|
@@ -3,7 +3,6 @@ import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
|
|
|
3
3
|
import { registerAuditedTool, runMcpToolAudit } from "../audit.js";
|
|
4
4
|
import { apiCall, apiRawCall, getClientSupportsMcpApps, getVizPort, getCaptionForImage, getResultsImagesToFetch, mimeForFilename, structuredTextResult, tryAttachImage, } from "../shared.js";
|
|
5
5
|
export const RESULTS_EXPLORER_URI = "ui://barsom/results-explorer";
|
|
6
|
-
export const MAP_EXPLORER_URI = RESULTS_EXPLORER_URI;
|
|
7
6
|
function labelForFigure(filename) {
|
|
8
7
|
const base = filename.replace(/\.(png|pdf|svg)$/i, "");
|
|
9
8
|
if (base === "combined")
|
|
@@ -166,12 +165,12 @@ export function registerExploreMapTool(server) {
|
|
|
166
165
|
_meta: { ui: { resourceUri: RESULTS_EXPLORER_URI } },
|
|
167
166
|
};
|
|
168
167
|
registerAppTool(server, "results_explorer", toolConfig, async (args) => runMcpToolAudit("results_explorer", "default", args, () => handleResultsExplorer(String(args.job_id))));
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
registerAuditedTool(server, "_fetch_figure", "
|
|
168
|
+
// NOTE: this MUST stay a registered tool — the Results Explorer MCP App view
|
|
169
|
+
// calls it through the App bridge (mcpApp.callServerTool) to lazy-load non-default
|
|
170
|
+
// raster figures, and the bridge can only reach registered server tools. It cannot
|
|
171
|
+
// be hidden from tools/list without breaking figure-switching in the App. The guard
|
|
172
|
+
// below + SERVER_INSTRUCTIONS keep agents from calling it directly.
|
|
173
|
+
registerAuditedTool(server, "_fetch_figure", "[INTERNAL — host / MCP App only; agents MUST NOT call this.] The Results Explorer App view uses it to lazy-load one raster figure as base64. From chat, use results(action=get) or results_explorer instead; calling this directly just returns a single image with no context.", {
|
|
175
174
|
job_id: z.string(),
|
|
176
175
|
filename: z.string(),
|
|
177
176
|
}, async ({ job_id, filename }) => {
|
package/dist/tools/inference.js
CHANGED
|
@@ -37,7 +37,7 @@ export function registerInferenceTool(server) {
|
|
|
37
37
|
|--------|----------|--------|
|
|
38
38
|
| predict | Scoring rows against the trained map (new data OR the training set itself) | 5–120s |
|
|
39
39
|
| impute_column | Fill a numeric column (not used in training) by pooling observed values on the BMU plus topology neighbors (typically 6 on hex; periodic maps wrap) | 5–120s |
|
|
40
|
-
| compare | Comparing hit distributions of a second dataset against training (drift, A/B) | 30–120s |
|
|
40
|
+
| compare | Comparing hit distributions of a second dataset against training (drift, A/B); needs one trained job_id + dataset B. NOT jobs(compare), which tabulates metrics across multiple training runs. | 30–120s |
|
|
41
41
|
| project_columns | Project one or more dataset columns onto the map (component planes); dataset can be training set or partial-feature set | 10–90s |
|
|
42
42
|
| report | Get a report manifest (artifact keys + URLs) to build your own report in Quarto/Notebook/script | Immediate (sync) |
|
|
43
43
|
|
|
@@ -54,16 +54,14 @@ action=predict: Score rows against the trained map.
|
|
|
54
54
|
Output style (output param, default "compact"):
|
|
55
55
|
- "compact" → predictions.csv (row_id, bmu_x, bmu_y, bmu_node_index, cluster_id [, quantization_error, potential_anomaly]).
|
|
56
56
|
- "annotated" → annotated.csv (full source CSV with bmu_x, bmu_y, bmu_node_index, cluster_id appended). Requires a dataset (no inline rows).
|
|
57
|
-
Regime auto-
|
|
58
|
-
- If the resolved dataset matches the parent training dataset, regime="training" and QE / qe_p95 / potential_anomaly fields are omitted from the compact output. QE on training data is fitting error, not a generalisation metric, and the p95 anomaly flag would be circular. Use a held-out dataset for quality assessment.
|
|
59
|
-
- Otherwise regime="new" and the full QE columns are returned.
|
|
57
|
+
Regime (auto): if the resolved dataset is the parent training dataset, regime="training" and QE / qe_p95 / potential_anomaly are omitted (QE on training data is fitting error, not generalisation — use a held-out dataset for quality). Otherwise regime="new" and full QE columns are returned.
|
|
60
58
|
Schema rules: dataset / batch rows must match the trained feature set including cyclic-expanded columns (e.g. key_cos, key_sin, not raw key). For a single inline row, the proxy uses the stateless model endpoint — raw categorical strings are allowed for baseline categorical_features models, and raw cyclic inputs are expanded from training config.
|
|
61
59
|
Routing: prefer dataset_id for many rows or whenever the map uses irregular SIOM / GeneralTopology layouts — the async worker path is the supported batch scorer. Single-row rows take a fast stateless path that may return invalid_inference_input on some topologies; if so, retry with dataset_id (a one-row dataset is fine). FLooP-SIOM: use dataset_id predict first.
|
|
62
60
|
When the scored set has at most ${PREDICT_PREVIEW_ROW_CAP} rows, completed responses include a short per-line preview in the tool text for chat agents.
|
|
63
61
|
|
|
64
62
|
action=impute_column: Map-local imputation on the frozen trained map (read-only; not a held-out validity claim). Requires dataset_id + target_column; the dataset must contain all training features (same names + cyclic expansion as predict) plus the target column. target_column must NOT have been a training feature. Pools finite target values from rows on this row's BMU and its topology neighbors (BMU + neighbors; ~7 on hex interior, fewer on non-periodic borders), neighbourhood-distance-weighted by default (weighting="uniform" for a flat pool); excludes the current row. only_missing (default true) keeps observed values; impute_aggregation mean|median. Optional cv_folds (2-20) → quality.csv (held-out MAE/RMSE/R2). target_column_kind: categorical (mode) / cumulative (warns). Output imputed.csv columns: row_id, target_original, target_imputed, impute_source (observed|imputed|insufficient_data), bmu_node_index, n_patch_nodes, n_pool_rows, pool_std, pool_p5, pool_p95.
|
|
65
63
|
|
|
66
|
-
action=compare: dataset_id must refer to a dataset with the same feature set as training (same column names and preprocessing, including cyclic expansion). A = training dataset; B = cohort to compare. Density-diff: positive = B gained vs A; negative = A had more. Returns density-diff heatmap (e.g. density_diff.png).
|
|
64
|
+
action=compare: density-diff of a cohort dataset against the training set on ONE trained map — NOT jobs(action=compare) (which tabulates QE/TE/EV metrics across multiple training runs to pick the best). dataset_id must refer to a dataset with the same feature set as training (same column names and preprocessing, including cyclic expansion). A = training dataset; B = cohort to compare. Density-diff: positive = B gained vs A; negative = A had more. Returns density-diff heatmap (e.g. density_diff.png).
|
|
67
65
|
action=project_columns: Project one or more columns from a dataset onto the trained map. Pass dataset_id (the dataset containing the columns) and columns (array of column names). Uses cached BMUs when dataset is the training set; supports partial-feature mapping when dataset has only a subset of training features. Returns one component plane image per column. Get files via results(action=download, job_id=<returned_job_id>).
|
|
68
66
|
action=report: Returns a report manifest for the given job_id (job must be completed). Includes figure_manifest (logical names → filenames), download_urls for all artifacts, cluster_summary when available, and summary metrics. Stakeholder report PDF (if generated) is available via results(action=download, job_id=<training_job_id>), filename e.g. report.pdf.`, {
|
|
69
67
|
action: z
|
package/dist/tools/jobs.js
CHANGED
|
@@ -7,12 +7,11 @@ export const JOBS_DESCRIPTION_BASE = `Manage and inspect jobs.
|
|
|
7
7
|
|--------|----------|
|
|
8
8
|
| status | Polling after any async job submission — call every 10–15s |
|
|
9
9
|
| list | Finding job IDs, checking what is pending/completed, reviewing hyperparameters. Response includes job_type (train_map, report, recolor, project, transition_flow, compare, predict, impute_column, annotated_dataset, reduce_spectral) to filter or display. |
|
|
10
|
-
| compare | Picking the best
|
|
10
|
+
| compare | Picking the best TRAINING run from a set of completed jobs (metrics table). NOT inference(compare), which diffs one dataset's distribution against the training set. |
|
|
11
11
|
| train_map | Submitting a new map training job — returns job_id for polling |
|
|
12
12
|
| train_impute | Sparse training data: train a map AND impute missing cells in one job (accelerated missSOM) — returns map + imputed.csv |
|
|
13
13
|
| train_siom_map | Submitting a self-interacting map training job — same map flow with SIOM coverage control |
|
|
14
14
|
| train_floop_siom | Submitting a FLooP-SIOM job (growing manifold; default topology=free / CHL) — requires Premium or Enterprise plan (all_algorithms) |
|
|
15
|
-
| train_floop_chain | Deprecated alias for train_floop_siom — same behavior; prefer train_floop_siom |
|
|
16
15
|
| cancel | Stopping a running or pending job to free the worker |
|
|
17
16
|
| delete | Permanently removing a job and all its S3 result files |
|
|
18
17
|
|
|
@@ -32,19 +31,12 @@ ESCALATION (action=status):
|
|
|
32
31
|
- column missing: verify with datasets(action=preview)
|
|
33
32
|
- NaN error on train_map: use jobs(action=train_impute) for sparse training columns, or clean the CSV for complete-case train_map
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
label (optional, ≤120 chars): user-supplied run name; appears in jobs(list) and jobs(compare) output. Strongly recommended for sweeps so compare rows stay readable.
|
|
42
|
-
train_siom_map only: siom_feature_geometry l2 (default) | mixed | auto — torus distance on cyclic (cos,sin) pairs when mixed or auto with cyclic encodings. siom_qe_backend cpu|cuda|auto and siom_qe_batch_size align siom_qe with training geometry when mixed.
|
|
43
|
-
action=train_floop_siom (preferred) or train_floop_chain (deprecated alias): Submits FLooP-SIOM — a growing node-budget manifold instead of a fixed hex grid. **Only if the API key’s plan includes all_algorithms** (Premium or Enterprise); otherwise the API returns a clear upgrade message. Default topology is free (CHL dynamic graph); topology=chain is optional for a strict 1D linked-list backbone.
|
|
44
|
-
Key params: topology (default free), max_nodes, effort, gamma, siom_decay.
|
|
45
|
-
max_nodes is a total node budget, not a grid width or area. If omitted, the backend uses a dataset-size heuristic (roughly 2*sqrt(n_samples), capped for stability).
|
|
46
|
-
effort controls passes only: quick≈15, standard≈30, thorough≈60. If coverage collapses, reduce max_nodes before increasing effort.
|
|
47
|
-
action=compare: Returns a metrics table (QE, TE, explained variance, silhouette) for 2+ jobs. The table only shows hyperparameter columns that actually differ across the compared runs (preset, backend, batch_size, normalize, periodic, n_features, etc.) so multi-run sweeps stay readable. Tip: pass label="sweep_a" / "sweep_b" on train_map to make rows self-explanatory.
|
|
34
|
+
Parameter details (grid, epochs, batch, model, presets, normalize, categorical_features, backend, FLooP max_nodes/effort, SIOM geometry) live in the **training_guidance** tool / **prepare_training** prompt — call them instead of inlining here. Backend strings are "cpu" | "gpu" | "gpu_graphs" (no colon); on CPU-only hosts pass backend=cpu.
|
|
35
|
+
|
|
36
|
+
action=train_map / train_siom_map: Submits a grid-map training job. Returns job_id — poll with jobs(action=status, job_id=...). train_siom_map adds SIOM coverage control.
|
|
37
|
+
action=train_impute: Missing-tolerant map training (accelerated missSOM / SIOM). Use when many cells are missing across training columns; use inference(impute_column) when you already have a complete-case map and need to fill one held-out column. Plain numeric columns only (no cyclic/temporal/categorical). Defaults model=auto, cv_folds=5 → quality.csv with two labeled numbers per column: kfold_holdout_pool (honest held-out) and resubstitution_optimistic (in-sample, optimistic). status returns progress_phase (ordering/convergence/cv/upload). Artifacts: imputed.csv, imputation_mask.csv, optional imputation_uncertainty.csv. A completed train_impute job_id is a valid parent for inference(impute_column).
|
|
38
|
+
action=train_floop_siom: growing node-budget manifold instead of a fixed hex grid. Requires a plan with all_algorithms (Premium/Enterprise); otherwise the API returns a clear upgrade message. Default topology=free (CHL); topology=chain for a strict 1D backbone. max_nodes is a TOTAL node budget (not grid width/area). If coverage collapses, reduce max_nodes before increasing effort.
|
|
39
|
+
action=compare: metrics table (QE, TE, explained variance, silhouette) for 2+ COMPLETED TRAINING jobs — pick the best run in a sweep. NOT inference(action=compare) (single trained job + second dataset → density-diff heatmap). Only hyperparameter columns that differ across runs are shown; pass label="sweep_a"/"sweep_b" on train_map for readable rows.
|
|
48
40
|
action=cancel: Not instant — worker checks between phases. Expect up to 30s delay.
|
|
49
41
|
action=delete: WARNING — job ID will no longer work with results or any other tool.`;
|
|
50
42
|
export async function fetchTrainingPresets() {
|
|
@@ -227,8 +219,8 @@ export function buildTrainMapParams(args, presets) {
|
|
|
227
219
|
export function registerJobsTool(server, description) {
|
|
228
220
|
registerAuditedTool(server, "jobs", description, {
|
|
229
221
|
action: z
|
|
230
|
-
.enum(["status", "list", "compare", "cancel", "delete", "train_map", "train_impute", "train_siom_map", "train_floop_siom", "
|
|
231
|
-
.describe("status: check progress; list: see all jobs; compare: metrics table; cancel: stop job; delete: remove job + files; train_map: submit standard map training; train_siom_map: submit SIOM map training; train_floop_siom: submit FLooP-SIOM
|
|
222
|
+
.enum(["status", "list", "compare", "cancel", "delete", "train_map", "train_impute", "train_siom_map", "train_floop_siom", "batch_predict", "run_baseline_study"])
|
|
223
|
+
.describe("status: check progress; list: see all jobs; compare: metrics table; cancel: stop job; delete: remove job + files; train_map: submit standard map training; train_siom_map: submit SIOM map training; train_floop_siom: submit FLooP-SIOM; batch_predict: submit multiple predict jobs at once; run_baseline_study: auto-configure and train a baseline SOM"),
|
|
232
224
|
job_id: z
|
|
233
225
|
.string()
|
|
234
226
|
.optional()
|
|
@@ -474,7 +466,7 @@ export function registerJobsTool(server, description) {
|
|
|
474
466
|
}
|
|
475
467
|
return textResult(data);
|
|
476
468
|
}
|
|
477
|
-
if (action === "train_floop_siom"
|
|
469
|
+
if (action === "train_floop_siom") {
|
|
478
470
|
const { columns, cyclic_features, temporal_features, feature_weights, transforms, auto_log_transforms, normalize, row_range, output_format, output_dpi, colormap, topology, max_nodes, effort, n_initial, n_passes, growth_interval, neighborhood_size, edge_max_age, max_degree, gamma, siom_decay, siom_penalty, penalty_alpha, error_threshold, error_decay, ring_close_threshold, sigma_0, sigma_f, eta_0, eta_f, elastic_lambda, elastic_mu, elastic_anchor, anchor_percentile, label, } = args;
|
|
479
471
|
if (!dataset_id)
|
|
480
472
|
throw new Error("jobs(train_floop_siom) requires dataset_id");
|
|
@@ -565,10 +557,7 @@ export function registerJobsTool(server, description) {
|
|
|
565
557
|
data.message =
|
|
566
558
|
`Job submitted (${paramSummary}). Poll with jobs(action=status, job_id="${newJobId}"). ` +
|
|
567
559
|
`When status is completed, use results(action=get, job_id="${newJobId}") to view FLooP-SIOM figures and metrics. ` +
|
|
568
|
-
`Optional: training_monitor(job_id="${newJobId}") for charts—not required
|
|
569
|
-
(action === "train_floop_chain"
|
|
570
|
-
? " Note: action=train_floop_chain is deprecated; prefer train_floop_siom."
|
|
571
|
-
: "");
|
|
560
|
+
`Optional: training_monitor(job_id="${newJobId}") for charts—not required.`;
|
|
572
561
|
return textResult(data);
|
|
573
562
|
}
|
|
574
563
|
if (action === "status") {
|
|
@@ -1,10 +1,36 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { registerAppTool } from "@modelcontextprotocol/ext-apps/server";
|
|
3
3
|
import { registerAuditedTool, runMcpToolAudit } from "../audit.js";
|
|
4
|
-
import { apiCall, fetchTrainingGuidanceFromApi, getClientSupportsMcpApps, getVizPort, structuredTextResult, } from "../shared.js";
|
|
4
|
+
import { apiCall, fetchTrainingGuidanceFromApi, getClientSupportsMcpApps, getVizPort, pollUntilComplete, POLL_DERIVE_MAX_MS, structuredTextResult, } from "../shared.js";
|
|
5
5
|
import { buildTrainMapParams, fetchTrainingPresets, } from "./jobs.js";
|
|
6
6
|
import { attachTrainingReviewPayload, consumeTrainingReview, getTrainingReview, storeTrainingReview, } from "./training_review_store.js";
|
|
7
7
|
export const TRAINING_PREP_URI = "ui://barsom/training-prep";
|
|
8
|
+
/**
|
|
9
|
+
* Fetch column analysis the same way datasets(action=analyze) does: enqueue the
|
|
10
|
+
* async sampled analyze job, poll, then read the summary. Falls back to the legacy
|
|
11
|
+
* synchronous GET on older APIs (404). Analysis is advisory in prep, so a job that
|
|
12
|
+
* is still running / failed degrades to an empty analysis (preview + presets still
|
|
13
|
+
* render) rather than failing the whole prep call.
|
|
14
|
+
*/
|
|
15
|
+
async function fetchAnalyzeData(datasetId) {
|
|
16
|
+
try {
|
|
17
|
+
const submit = (await apiCall("POST", `/v1/datasets/${datasetId}/analyze`));
|
|
18
|
+
const jobId = (submit.id ?? submit.job_id);
|
|
19
|
+
if (!jobId)
|
|
20
|
+
return {};
|
|
21
|
+
const poll = await pollUntilComplete(jobId, POLL_DERIVE_MAX_MS);
|
|
22
|
+
if (poll.status !== "completed")
|
|
23
|
+
return {};
|
|
24
|
+
const results = (await apiCall("GET", `/v1/results/${jobId}`));
|
|
25
|
+
return (results.summary ?? results);
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
if (err?.httpStatus === 404) {
|
|
29
|
+
return (await apiCall("GET", `/v1/datasets/${datasetId}/analyze`));
|
|
30
|
+
}
|
|
31
|
+
throw err;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
8
34
|
function getNumericColumns(preview) {
|
|
9
35
|
const stats = preview.column_stats ?? [];
|
|
10
36
|
return stats
|
|
@@ -139,7 +165,7 @@ function buildGuidanceLines(guidanceText) {
|
|
|
139
165
|
async function buildTrainingPrepPayload(datasetId, input) {
|
|
140
166
|
const [preview, analyze, presets, guidanceText, preparePrompt] = await Promise.all([
|
|
141
167
|
apiCall("GET", `/v1/datasets/${datasetId}/preview?n_rows=5`),
|
|
142
|
-
|
|
168
|
+
fetchAnalyzeData(datasetId),
|
|
143
169
|
fetchTrainingPresets().catch(() => ({})),
|
|
144
170
|
fetchTrainingGuidanceFromApi().catch(() => "No training guidance available."),
|
|
145
171
|
fetchPreparePrompt(datasetId),
|