@barivia/barsom-mcp 0.23.5 → 0.23.9
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 +4 -3
- package/dist/job_monitor.js +31 -7
- package/dist/job_status_format.js +26 -4
- package/dist/shared.js +213 -23
- package/dist/tools/account.js +27 -6
- package/dist/tools/datasets.js +21 -13
- package/dist/tools/inference.js +40 -8
- package/dist/tools/results.js +10 -9
- package/dist/tools/train.js +2 -1
- package/dist/train_submit_message.js +2 -1
- package/dist/training_monitor_curve.js +2 -1
- package/dist/views/src/views/training-monitor/index.html +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,7 +32,7 @@ MCP clients typically run it with **`npx`** (downloads on first use):
|
|
|
32
32
|
1. **Get an API key** — Sign up at [barivia.se/dashboard](https://barivia.se/dashboard) (self-serve) or ask your admin for a `bv_…` key.
|
|
33
33
|
2. **Add the MCP server** — Paste the JSON block above into your client’s MCP config (`mcp.json` in Cursor). Set `BARIVIA_API_KEY` and keep `BARIVIA_API_URL=https://api.barivia.se`.
|
|
34
34
|
3. **Orient** — In chat, invoke **`guide_barsom_workflow`** (no parameters). It returns your plan-scoped tool map and async rules.
|
|
35
|
-
4. **Upload data** — `datasets(action=upload, file_path="your.csv")` → note `dataset_id`
|
|
35
|
+
4. **Upload data** — `datasets(action=upload, file_path="your.csv")` → note `dataset_id`. The proxy waits for staging on upload; confirm with `datasets(action=get)` → `train_ready=yes` before train if you bypass the proxy wait.
|
|
36
36
|
5. **Train** — `train(action=map, dataset_id=…, preset=quick)` → returns `job_id`. Poll with `jobs(action=status, job_id=…)` every 60–120s, or use **`training_monitor(job_id=…)`** for live curves.
|
|
37
37
|
6. **Results** — When status is `completed`, `results(action=get, job_id=…)` for metrics + headline figure; **`results_explorer(job_id=…)`** to browse all figures.
|
|
38
38
|
|
|
@@ -53,10 +53,11 @@ MCP clients typically run it with **`npx`** (downloads on first use):
|
|
|
53
53
|
| `BARIVIA_API_KEY` | Yes | -- | API key (starts with `bv_`) |
|
|
54
54
|
| `BARIVIA_API_URL` | No | `https://api.barivia.se` | API base URL |
|
|
55
55
|
| `BARIVIA_WORKSPACE_ROOT` | No | `process.cwd()` or `PWD` | Directory for relative `file_path` and `save_to_disk`. In Cursor MCP, `process.cwd()` is often the MCP install dir — add `BARIVIA_WORKSPACE_ROOT` to your MCP config `env` with your project path (e.g. `/home/user/myproject`). Absolute paths and `file://` URIs work without it. |
|
|
56
|
+
| `BARIVIA_WORKSPACE_EXTRA_ROOTS` | No | (empty) | Comma- or colon-separated absolute directories whose **realpath** targets are also allowed for uploads when the sandbox is on. Use for shared fixture trees reached via symlinks under the case workspace (e.g. `…/barivia-testing/data`). Does not weaken the default sandbox for paths outside these roots. |
|
|
56
57
|
| `BARIVIA_FETCH_TIMEOUT_MS` | No | `60000` | Per-request HTTP timeout (ms) to the API. Increase (e.g. `120000`) on slow networks or when the API does long-running work. Large dataset uploads use a separate longer timeout internally, and `datasets(analyze)` runs asynchronously (auto-polled) so it is not bound by this. A timeout is not auto-retried (re-firing slow work would only multiply load / risk duplicates). |
|
|
57
58
|
| `BARIVIA_VIZ_PORT` | No | OS-assigned | Local viz server on `127.0.0.1`. Port is **per MCP session** unless pinned — standalone `…/viz/…` URLs go stale after proxy restart. Set a fixed port for stable bookmark URLs. Diagnostic: `GET http://127.0.0.1:<port>/api/health?job_id=<id>`. |
|
|
58
59
|
| `BARIVIA_UI_DELIVERY` | No | `auto` | Client-capability override for App tools: `auto` \| `apps` \| `localhost` \| `inline`. Tool responses always lead with a standalone localhost URL (hosts may list `ui://` without mounting widgets) and include a structured `ui_delivery` block. `account(status)` reports detected Apps support, viz port, and active tier. |
|
|
59
|
-
| `BARIVIA_ENFORCE_WORKSPACE_SANDBOX` | No | `1` (enabled) | File uploads are constrained to the MCP workspace root by default. Set to `0` or `false` to allow absolute paths anywhere on the machine (high trust).
|
|
60
|
+
| `BARIVIA_ENFORCE_WORKSPACE_SANDBOX` | No | `1` (enabled) | File uploads are constrained to the MCP workspace root (and optional `BARIVIA_WORKSPACE_EXTRA_ROOTS`) by default. Set to `0` or `false` to allow absolute paths anywhere on the machine (high trust). If uploads fail with "symlink resolves outside workspace", either point `BARIVIA_WORKSPACE_ROOT` / `BARIVIA_WORKSPACE_EXTRA_ROOTS` at the real data dir, copy the file into the workspace, or opt out with `BARIVIA_ENFORCE_WORKSPACE_SANDBOX=0`. |
|
|
60
61
|
|
|
61
62
|
Legacy `BARSOM_API_KEY` / `BARSOM_API_URL` / `BARSOM_WORKSPACE_ROOT` are also accepted as fallbacks.
|
|
62
63
|
|
|
@@ -102,7 +103,7 @@ Call at the **start of mapping work** (or when the user asks what the MCP can do
|
|
|
102
103
|
| `preview` | Before training — inspect columns, stats, cyclic/datetime hints |
|
|
103
104
|
| `analyze` | Pre-training recommendations (correlation, columns to consider dropping, etc.) |
|
|
104
105
|
| `list` | Finding dataset IDs |
|
|
105
|
-
| `get` | One dataset by id — status, staging fields, `stage_job_id`, ingest errors |
|
|
106
|
+
| `get` | One dataset by id — status, **`train_ready`**, staging fields, `stage_job_id`, ingest errors |
|
|
106
107
|
| `subset` | Creating a filtered/sliced copy (row_range, filter, `sample_n`) — **only when the user asks**; agents must not auto-downsample before train/analyze |
|
|
107
108
|
| `add_expression` | Add a derived column from an expression (formula → new column on the dataset) |
|
|
108
109
|
| `reduce_spectral` | Pre-training reducer for long ordered numeric blocks (spectra, time series, sensor fingerprints, gene panels). Methods: **pca** (top-k principal components), **log_sample** (k columns at log-spaced indices — scattering, audio bands), **uniform_sample** (k columns at evenly-spaced indices — regularly-sampled time series), **stats** (6 fixed per-row summary columns). All produce one feature vector per row; appends derived columns to the dataset. |
|
package/dist/job_monitor.js
CHANGED
|
@@ -34,14 +34,30 @@ function tailOrderingErrors(data, n = 4) {
|
|
|
34
34
|
export function snapshotFromJob(data, elapsedSec, note) {
|
|
35
35
|
const status = String(data.status ?? "unknown");
|
|
36
36
|
const progress_pct = (data.progress ?? 0) * 100;
|
|
37
|
+
const kernelComplete = data.kernel_complete === true;
|
|
38
|
+
const awaitingFinalize = kernelComplete &&
|
|
39
|
+
status !== "completed" &&
|
|
40
|
+
status !== "failed" &&
|
|
41
|
+
status !== "cancelled";
|
|
37
42
|
const snap = {
|
|
38
43
|
elapsed_sec: elapsedSec,
|
|
39
44
|
status,
|
|
40
|
-
|
|
45
|
+
// Kernel progress can hit 100% before finalize; do not present as job-done.
|
|
46
|
+
progress_pct: awaitingFinalize
|
|
47
|
+
? Math.min(Math.round(progress_pct * 10) / 10, 99.0)
|
|
48
|
+
: Math.round(progress_pct * 10) / 10,
|
|
41
49
|
};
|
|
42
|
-
const
|
|
43
|
-
|
|
50
|
+
const phaseRaw = str(data, "progress_phase");
|
|
51
|
+
const phase = phaseRaw && phaseRaw !== "missing" ? phaseRaw : undefined;
|
|
52
|
+
if (awaitingFinalize) {
|
|
53
|
+
snap.phase = phase && phase !== "complete" ? `${phase}+awaiting_finalize` : "awaiting_finalize";
|
|
54
|
+
}
|
|
55
|
+
else if (phase) {
|
|
44
56
|
snap.phase = phase;
|
|
57
|
+
}
|
|
58
|
+
else if (str(data, "prepare_job_id") && String(data.status ?? "") !== "completed") {
|
|
59
|
+
snap.phase = "waiting_for_prepare";
|
|
60
|
+
}
|
|
45
61
|
const epoch = num(data, "epoch");
|
|
46
62
|
const total = num(data, "total_epochs");
|
|
47
63
|
if (epoch != null)
|
|
@@ -57,7 +73,7 @@ export function snapshotFromJob(data, elapsedSec, note) {
|
|
|
57
73
|
const panelTe = num(data, "panel_topographic_error");
|
|
58
74
|
if (panelTe != null)
|
|
59
75
|
snap.panel_te = Math.round(panelTe * 10_000) / 10_000;
|
|
60
|
-
if (
|
|
76
|
+
if (kernelComplete)
|
|
61
77
|
snap.kernel_complete = true;
|
|
62
78
|
const failureStage = str(data, "failure_stage");
|
|
63
79
|
if (failureStage)
|
|
@@ -68,8 +84,12 @@ export function snapshotFromJob(data, elapsedSec, note) {
|
|
|
68
84
|
const tail = tailOrderingErrors(data);
|
|
69
85
|
if (tail && tail.length > 0)
|
|
70
86
|
snap.ordering_errors_tail = tail;
|
|
71
|
-
if (note)
|
|
87
|
+
if (note) {
|
|
72
88
|
snap.note = note;
|
|
89
|
+
}
|
|
90
|
+
else if (awaitingFinalize) {
|
|
91
|
+
snap.note = "kernel complete — awaiting finalize (not fully done)";
|
|
92
|
+
}
|
|
73
93
|
return snap;
|
|
74
94
|
}
|
|
75
95
|
/** True when a new snapshot is worth recording (phase/epoch/progress/status change). */
|
|
@@ -95,7 +115,10 @@ export function shouldRecordSnapshot(prev, next) {
|
|
|
95
115
|
return false;
|
|
96
116
|
}
|
|
97
117
|
export function formatSnapshotLine(s) {
|
|
98
|
-
const
|
|
118
|
+
const progressLabel = s.kernel_complete && s.status !== "completed" && s.status !== "failed" && s.status !== "cancelled"
|
|
119
|
+
? `kernel ${s.progress_pct.toFixed(1)}% (awaiting finalize)`
|
|
120
|
+
: `${s.progress_pct.toFixed(1)}%`;
|
|
121
|
+
const parts = [`[+${s.elapsed_sec}s] ${s.status} ${progressLabel}`];
|
|
99
122
|
if (s.phase)
|
|
100
123
|
parts.push(`phase ${s.phase}`);
|
|
101
124
|
if (s.epoch != null && s.total_epochs != null)
|
|
@@ -106,8 +129,9 @@ export function formatSnapshotLine(s) {
|
|
|
106
129
|
parts.push(`Epoch TE ${s.te.toFixed(4)}`);
|
|
107
130
|
if (s.panel_te != null)
|
|
108
131
|
parts.push(`Panel TE ${s.panel_te.toFixed(4)}`);
|
|
109
|
-
if (s.kernel_complete)
|
|
132
|
+
if (s.kernel_complete && (s.status === "completed" || s.status === "failed" || s.status === "cancelled")) {
|
|
110
133
|
parts.push("kernel complete");
|
|
134
|
+
}
|
|
111
135
|
if (s.failure_stage)
|
|
112
136
|
parts.push(`failure_stage ${s.failure_stage}`);
|
|
113
137
|
if (s.eta_sec != null)
|
|
@@ -36,22 +36,41 @@ export function formatJobStatusText(job_id, data) {
|
|
|
36
36
|
const progress = (data.progress ?? 0) * 100;
|
|
37
37
|
const label = data.label != null && data.label !== "" ? String(data.label) : null;
|
|
38
38
|
const jobDesc = label ? `Job ${label} (id: ${job_id})` : `Job ${job_id}`;
|
|
39
|
-
const
|
|
39
|
+
const kernelComplete = data.kernel_complete === true;
|
|
40
|
+
const awaitingFinalize = kernelComplete && status !== "completed" && status !== "failed" && status !== "cancelled";
|
|
41
|
+
const progressBit = awaitingFinalize
|
|
42
|
+
? `kernel ${Math.min(progress, 99).toFixed(1)}% — awaiting finalize`
|
|
43
|
+
: `${progress.toFixed(1)}%`;
|
|
44
|
+
const parts = [`${jobDesc}: ${status} (${progressBit})`];
|
|
40
45
|
const attempt = data.attempt != null ? Number(data.attempt) : null;
|
|
41
46
|
if (attempt != null && attempt > 1) {
|
|
42
47
|
parts.push(`attempt ${attempt} (job was requeued after worker timeout — not stuck from scratch)`);
|
|
43
48
|
}
|
|
44
|
-
const
|
|
49
|
+
const rawPhase = data.progress_phase != null && String(data.progress_phase) !== ""
|
|
45
50
|
? String(data.progress_phase)
|
|
46
51
|
: null;
|
|
47
|
-
|
|
52
|
+
// API historically serialized SQL NULL as the literal "missing" (Julia missing).
|
|
53
|
+
const phase = rawPhase && rawPhase !== "missing" ? rawPhase : null;
|
|
54
|
+
const prepareJobId = data.prepare_job_id != null && String(data.prepare_job_id) !== ""
|
|
55
|
+
? String(data.prepare_job_id)
|
|
56
|
+
: null;
|
|
57
|
+
if (status === "pending" && prepareJobId) {
|
|
58
|
+
parts.push(`phase: waiting_for_prepare (prepare_job_id=${prepareJobId})`);
|
|
59
|
+
}
|
|
60
|
+
else if (status === "running") {
|
|
48
61
|
if (phase) {
|
|
49
62
|
parts.push(`phase: ${phase}`);
|
|
50
63
|
}
|
|
64
|
+
else if (prepareJobId) {
|
|
65
|
+
parts.push(`phase: waiting_for_prepare (prepare_job_id=${prepareJobId})`);
|
|
66
|
+
}
|
|
51
67
|
else {
|
|
52
68
|
parts.push(`phase: preprocessing (not set yet — often download/parse before first progress tick)`);
|
|
53
69
|
}
|
|
54
70
|
}
|
|
71
|
+
else if (prepareJobId && (status === "completed" || status === "failed")) {
|
|
72
|
+
parts.push(`prepare_job_id: ${prepareJobId}`);
|
|
73
|
+
}
|
|
55
74
|
const startedAt = data.started_at != null ? String(data.started_at) : null;
|
|
56
75
|
if (startedAt) {
|
|
57
76
|
parts.push(`started_at: ${startedAt}`);
|
|
@@ -78,7 +97,10 @@ export function formatJobStatusText(job_id, data) {
|
|
|
78
97
|
if (panelTe != null) {
|
|
79
98
|
parts.push(`panel_te: ${panelTe.toFixed(4)}`);
|
|
80
99
|
}
|
|
81
|
-
if (
|
|
100
|
+
if (awaitingFinalize) {
|
|
101
|
+
parts.push("kernel_complete — figures/finalize still pending (not fully done)");
|
|
102
|
+
}
|
|
103
|
+
else if (kernelComplete) {
|
|
82
104
|
parts.push("kernel_complete");
|
|
83
105
|
}
|
|
84
106
|
}
|
package/dist/shared.js
CHANGED
|
@@ -39,7 +39,7 @@ function newRequestId() {
|
|
|
39
39
|
* X-Barsom-Client-Version so the server can annotate tool guidance with the
|
|
40
40
|
* wrapper version each action requires. Keep in sync with package.json on bump.
|
|
41
41
|
*/
|
|
42
|
-
export const CLIENT_VERSION = "0.23.
|
|
42
|
+
export const CLIENT_VERSION = "0.23.9";
|
|
43
43
|
/** User-facing links; keep aligned with barivia.se / api.barivia.se. */
|
|
44
44
|
export const PUBLIC_SITE_ORIGIN = "https://barivia.se";
|
|
45
45
|
/** Self-serve account dashboard (manage plan, billing, and API keys). */
|
|
@@ -77,6 +77,97 @@ export const LARGE_UPLOAD_BYTES = 64 * 1024 * 1024; // 64 MB
|
|
|
77
77
|
export const PRESIGNED_PUT_TIMEOUT_MS = 30 * 60_000; // 30 min
|
|
78
78
|
/** Poll window for the async stage_dataset job (large files take minutes). */
|
|
79
79
|
export const POLL_STAGE_MAX_MS = 30 * 60_000; // 30 min
|
|
80
|
+
/** Normalize upload JSON after staging completes — never leave staging:true with train_ready. */
|
|
81
|
+
export function finalizeStagedUploadResponse(data, ready) {
|
|
82
|
+
const merged = ready
|
|
83
|
+
? { ...data, ...ready }
|
|
84
|
+
: { ...data };
|
|
85
|
+
const trainReady = merged.train_ready === true || String(merged.status ?? "") === "ready";
|
|
86
|
+
if (trainReady) {
|
|
87
|
+
merged.status = ready?.status ?? merged.status ?? "ready";
|
|
88
|
+
merged.train_ready = true;
|
|
89
|
+
merged.staging = false;
|
|
90
|
+
// stage_job_id is only useful while staging is pending
|
|
91
|
+
if (ready?.stage_job_id == null)
|
|
92
|
+
delete merged.stage_job_id;
|
|
93
|
+
}
|
|
94
|
+
return merged;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* After a small POST /v1/datasets upload, poll stage_job_id until the dataset is
|
|
98
|
+
* train-ready (mirrors the large-upload finalize path).
|
|
99
|
+
*/
|
|
100
|
+
export async function awaitDatasetStageIfNeeded(data) {
|
|
101
|
+
const stageJobId = data.stage_job_id != null ? String(data.stage_job_id) : "";
|
|
102
|
+
if (!stageJobId) {
|
|
103
|
+
if (data.train_ready === true || data.status === "ready") {
|
|
104
|
+
return finalizeStagedUploadResponse(data);
|
|
105
|
+
}
|
|
106
|
+
return data;
|
|
107
|
+
}
|
|
108
|
+
if (data.train_ready === true || data.status === "ready") {
|
|
109
|
+
return finalizeStagedUploadResponse(data);
|
|
110
|
+
}
|
|
111
|
+
const poll = await pollUntilComplete(stageJobId, POLL_STAGE_MAX_MS);
|
|
112
|
+
if (poll.status === "failed") {
|
|
113
|
+
throw new Error(`Dataset staging failed (stage_job_id=${stageJobId}): ${poll.error ?? "unknown error"}`);
|
|
114
|
+
}
|
|
115
|
+
if (poll.status === "timeout") {
|
|
116
|
+
throw new Error(`Dataset staging timed out after ${POLL_STAGE_MAX_MS / 60000} min (stage_job_id=${stageJobId}). Poll jobs(action=status, job_id="${stageJobId}").`);
|
|
117
|
+
}
|
|
118
|
+
const datasetId = String(data.id ?? data.dataset_id ?? "");
|
|
119
|
+
if (datasetId) {
|
|
120
|
+
try {
|
|
121
|
+
const ready = (await apiCall("GET", `/v1/datasets/${datasetId}`));
|
|
122
|
+
return finalizeStagedUploadResponse(data, ready);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
/* fall through */
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return finalizeStagedUploadResponse({ ...data, status: "ready", train_ready: true });
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Before train / CFD submit: ensure the dataset has finished staging.
|
|
132
|
+
* Prefer train_ready from GET; otherwise poll stage_job_id.
|
|
133
|
+
*/
|
|
134
|
+
export async function ensureDatasetTrainReady(datasetId) {
|
|
135
|
+
const ds = (await apiCall("GET", `/v1/datasets/${datasetId}`));
|
|
136
|
+
if (ds.train_ready === true)
|
|
137
|
+
return;
|
|
138
|
+
if (String(ds.status) === "failed") {
|
|
139
|
+
const err = ds.ingest_error != null ? String(ds.ingest_error) : "staging failed";
|
|
140
|
+
throw new Error(`Dataset ${datasetId} staging failed: ${err}`);
|
|
141
|
+
}
|
|
142
|
+
const stageJobId = ds.stage_job_id != null ? String(ds.stage_job_id) : "";
|
|
143
|
+
if (stageJobId) {
|
|
144
|
+
const poll = await pollUntilComplete(stageJobId, POLL_STAGE_MAX_MS);
|
|
145
|
+
if (poll.status === "failed") {
|
|
146
|
+
throw new Error(`Dataset staging failed (stage_job_id=${stageJobId}): ${poll.error ?? "unknown error"}`);
|
|
147
|
+
}
|
|
148
|
+
if (poll.status === "timeout") {
|
|
149
|
+
throw new Error(`Dataset staging timed out (stage_job_id=${stageJobId}). Poll jobs(action=status, job_id="${stageJobId}").`);
|
|
150
|
+
}
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
// No stage job listed — brief poll on GET for race windows
|
|
154
|
+
const deadline = Date.now() + 60_000;
|
|
155
|
+
while (Date.now() < deadline) {
|
|
156
|
+
await new Promise((r) => setTimeout(r, 1000));
|
|
157
|
+
const again = (await apiCall("GET", `/v1/datasets/${datasetId}`));
|
|
158
|
+
if (again.train_ready === true)
|
|
159
|
+
return;
|
|
160
|
+
if (String(again.status) === "failed") {
|
|
161
|
+
const err = again.ingest_error != null ? String(again.ingest_error) : "staging failed";
|
|
162
|
+
throw new Error(`Dataset ${datasetId} staging failed: ${err}`);
|
|
163
|
+
}
|
|
164
|
+
if (again.stage_job_id != null) {
|
|
165
|
+
await ensureDatasetTrainReady(datasetId);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
throw new Error(`Dataset ${datasetId} is not train_ready (status=${ds.status ?? "?"}). Wait for staging or poll datasets(action=get).`);
|
|
170
|
+
}
|
|
80
171
|
export const BARSOM_VIZ_VIEWS = {
|
|
81
172
|
trainingMonitor: "training-monitor",
|
|
82
173
|
resultsExplorer: "results-explorer",
|
|
@@ -288,11 +379,51 @@ function enforceWorkspaceSandboxUpload() {
|
|
|
288
379
|
return false;
|
|
289
380
|
return true;
|
|
290
381
|
}
|
|
382
|
+
/** Extra realpath roots allowed for uploads (comma- or colon-separated absolute paths). */
|
|
383
|
+
export function getWorkspaceExtraRoots() {
|
|
384
|
+
const raw = process.env.BARIVIA_WORKSPACE_EXTRA_ROOTS ?? "";
|
|
385
|
+
if (!raw.trim())
|
|
386
|
+
return [];
|
|
387
|
+
return raw
|
|
388
|
+
.split(/[,:]/)
|
|
389
|
+
.map((s) => s.trim())
|
|
390
|
+
.filter(Boolean)
|
|
391
|
+
.map((s) => path.resolve(s));
|
|
392
|
+
}
|
|
393
|
+
async function realpathIfExists(p) {
|
|
394
|
+
try {
|
|
395
|
+
return await fs.realpath(p);
|
|
396
|
+
}
|
|
397
|
+
catch {
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
/** True when `realPath` is under workspace root or any BARIVIA_WORKSPACE_EXTRA_ROOTS entry. */
|
|
402
|
+
export async function isPathUnderSandboxRoots(realPath, workspaceRoot) {
|
|
403
|
+
const candidates = [workspaceRoot, ...getWorkspaceExtraRoots()];
|
|
404
|
+
for (const root of candidates) {
|
|
405
|
+
const realRoot = await realpathIfExists(root);
|
|
406
|
+
if (!realRoot)
|
|
407
|
+
continue;
|
|
408
|
+
if (realPath === realRoot || realPath.startsWith(realRoot + path.sep))
|
|
409
|
+
return true;
|
|
410
|
+
}
|
|
411
|
+
return false;
|
|
412
|
+
}
|
|
413
|
+
function sandboxEscapeError(realPath, workspaceRoot) {
|
|
414
|
+
const extras = getWorkspaceExtraRoots();
|
|
415
|
+
const extraHint = extras.length > 0
|
|
416
|
+
? ` Also checked BARIVIA_WORKSPACE_EXTRA_ROOTS (${extras.join(", ")}).`
|
|
417
|
+
: " Set BARIVIA_WORKSPACE_EXTRA_ROOTS to allow shared fixture dirs (e.g. a data/ sibling), or copy the file into the workspace.";
|
|
418
|
+
return new Error(`Resolved path escapes workspace — symlink resolves outside BARIVIA_WORKSPACE_ROOT` +
|
|
419
|
+
` (${workspaceRoot}). Target: ${realPath}.${extraHint}`);
|
|
420
|
+
}
|
|
291
421
|
/**
|
|
292
422
|
* Resolves file_path for dataset upload. Security: auth before read, generic errors, reject "..".
|
|
293
423
|
* Accepts: absolute paths, file:// URIs, relative paths (resolved against workspace root).
|
|
294
424
|
* Pre-security-patch behavior: absolute paths worked without BARIVIA_WORKSPACE_ROOT.
|
|
295
|
-
* Set BARIVIA_ENFORCE_WORKSPACE_SANDBOX=1 to require absolute paths to lie under the resolved workspace root
|
|
425
|
+
* Set BARIVIA_ENFORCE_WORKSPACE_SANDBOX=1 to require absolute paths to lie under the resolved workspace root
|
|
426
|
+
* or an entry in BARIVIA_WORKSPACE_EXTRA_ROOTS (after realpath, so symlinks are allowed when the target is under an allowed root).
|
|
296
427
|
*/
|
|
297
428
|
export async function resolveFilePathForUpload(filePath, mcpServer) {
|
|
298
429
|
const trimmed = filePath.trim();
|
|
@@ -309,9 +440,8 @@ export async function resolveFilePathForUpload(filePath, mcpServer) {
|
|
|
309
440
|
if (enforceWorkspaceSandboxUpload()) {
|
|
310
441
|
const root = await getWorkspaceRootAsync(mcpServer);
|
|
311
442
|
const real = await fs.realpath(p);
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
throw new Error(`file:// paths outside the workspace are disabled (BARIVIA_ENFORCE_WORKSPACE_SANDBOX). Workspace: ${realRoot}`);
|
|
443
|
+
if (!(await isPathUnderSandboxRoots(real, root))) {
|
|
444
|
+
throw sandboxEscapeError(real, root);
|
|
315
445
|
}
|
|
316
446
|
const stat = await fs.stat(real);
|
|
317
447
|
if (!stat.isFile()) {
|
|
@@ -324,7 +454,7 @@ export async function resolveFilePathForUpload(filePath, mcpServer) {
|
|
|
324
454
|
catch (err) {
|
|
325
455
|
if (err instanceof Error && err.message.includes("remote hosts"))
|
|
326
456
|
throw err;
|
|
327
|
-
if (err instanceof Error && err.message.includes("
|
|
457
|
+
if (err instanceof Error && err.message.includes("symlink resolves outside"))
|
|
328
458
|
throw err;
|
|
329
459
|
if (err instanceof Error && err.message.includes("regular file"))
|
|
330
460
|
throw err;
|
|
@@ -339,11 +469,10 @@ export async function resolveFilePathForUpload(filePath, mcpServer) {
|
|
|
339
469
|
real = await fs.realpath(trimmed);
|
|
340
470
|
}
|
|
341
471
|
catch {
|
|
342
|
-
throw new Error(`File not accessible at absolute path. With BARIVIA_ENFORCE_WORKSPACE_SANDBOX, use a path under the workspace root (${root}).`);
|
|
472
|
+
throw new Error(`File not accessible at absolute path. With BARIVIA_ENFORCE_WORKSPACE_SANDBOX, use a path under the workspace root (${root}) or BARIVIA_WORKSPACE_EXTRA_ROOTS.`);
|
|
343
473
|
}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
throw new Error(`Absolute paths outside the workspace are disabled (BARIVIA_ENFORCE_WORKSPACE_SANDBOX). Workspace root: ${realRoot}`);
|
|
474
|
+
if (!(await isPathUnderSandboxRoots(real, root))) {
|
|
475
|
+
throw sandboxEscapeError(real, root);
|
|
347
476
|
}
|
|
348
477
|
const stat = await fs.stat(real);
|
|
349
478
|
if (!stat.isFile()) {
|
|
@@ -355,14 +484,14 @@ export async function resolveFilePathForUpload(filePath, mcpServer) {
|
|
|
355
484
|
}
|
|
356
485
|
const root = await getWorkspaceRootAsync(mcpServer);
|
|
357
486
|
const resolved = path.resolve(root, trimmed);
|
|
487
|
+
// Lexical check against workspace root only (relative paths must stay under the case workspace).
|
|
358
488
|
if (resolved !== root && !resolved.startsWith(root + path.sep)) {
|
|
359
489
|
throw new Error("Path must be within the workspace directory.");
|
|
360
490
|
}
|
|
361
491
|
try {
|
|
362
492
|
const real = await fs.realpath(resolved);
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
throw new Error("Resolved path escapes workspace (e.g. symlink).");
|
|
493
|
+
if (!(await isPathUnderSandboxRoots(real, root))) {
|
|
494
|
+
throw sandboxEscapeError(real, root);
|
|
366
495
|
}
|
|
367
496
|
const stat = await fs.stat(real);
|
|
368
497
|
if (!stat.isFile()) {
|
|
@@ -371,7 +500,8 @@ export async function resolveFilePathForUpload(filePath, mcpServer) {
|
|
|
371
500
|
return real;
|
|
372
501
|
}
|
|
373
502
|
catch (err) {
|
|
374
|
-
if (err instanceof Error &&
|
|
503
|
+
if (err instanceof Error &&
|
|
504
|
+
(err.message.includes("symlink resolves outside") || err.message.includes("regular file"))) {
|
|
375
505
|
throw err;
|
|
376
506
|
}
|
|
377
507
|
throw new Error(`File not accessible. Easiest fix: pass an ABSOLUTE path (e.g. "/home/you/project/data.csv" or ` +
|
|
@@ -444,7 +574,15 @@ export function classifyApiError(status, bodyText, requestId) {
|
|
|
444
574
|
}
|
|
445
575
|
else if (status === 409) {
|
|
446
576
|
cause = "client";
|
|
447
|
-
|
|
577
|
+
if (apiCode === "staging_not_ready") {
|
|
578
|
+
const stageId = parsed?.stage_job_id != null ? String(parsed.stage_job_id) : "";
|
|
579
|
+
hint = stageId
|
|
580
|
+
? `Wait for stage_dataset to finish: jobs(action=monitor, job_id="${stageId}"), then retry train. Prefer datasets(action=get) train_ready=true.`
|
|
581
|
+
: "Wait for dataset staging (status=ready with staged_prefix / train_ready=true), then retry train.";
|
|
582
|
+
}
|
|
583
|
+
else {
|
|
584
|
+
hint = "The job may not be in the expected state.";
|
|
585
|
+
}
|
|
448
586
|
retryable = false;
|
|
449
587
|
}
|
|
450
588
|
else if (status === 429) {
|
|
@@ -824,15 +962,30 @@ export function structuredTextResult(structuredContent, text, content) {
|
|
|
824
962
|
}
|
|
825
963
|
export async function pollUntilComplete(jobId, maxWaitMs = 30_000, intervalMs = 1000) {
|
|
826
964
|
const start = Date.now();
|
|
965
|
+
/** Brief 404s happen when finalize_job_id is visible before the child row is readable. */
|
|
966
|
+
let notFoundStreak = 0;
|
|
967
|
+
const maxNotFoundRetries = 5;
|
|
827
968
|
while (Date.now() - start < maxWaitMs) {
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
969
|
+
try {
|
|
970
|
+
const data = (await apiCall("GET", `/v1/jobs/${jobId}`));
|
|
971
|
+
notFoundStreak = 0;
|
|
972
|
+
const status = data.status;
|
|
973
|
+
if (status === "completed" || status === "failed" || status === "cancelled") {
|
|
974
|
+
return {
|
|
975
|
+
status,
|
|
976
|
+
result_ref: data.result_ref,
|
|
977
|
+
error: data.error,
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
}
|
|
981
|
+
catch (err) {
|
|
982
|
+
const status = err?.httpStatus;
|
|
983
|
+
if (status === 404 && notFoundStreak < maxNotFoundRetries) {
|
|
984
|
+
notFoundStreak += 1;
|
|
985
|
+
await new Promise((r) => setTimeout(r, intervalMs));
|
|
986
|
+
continue;
|
|
987
|
+
}
|
|
988
|
+
throw err;
|
|
836
989
|
}
|
|
837
990
|
await new Promise((r) => setTimeout(r, intervalMs));
|
|
838
991
|
}
|
|
@@ -986,6 +1139,43 @@ export function inlineAttachBytesUsed() {
|
|
|
986
1139
|
export function shouldFetchAllRemainingFigures(figures) {
|
|
987
1140
|
return figures === "all" || figures === "images";
|
|
988
1141
|
}
|
|
1142
|
+
/** Enum tokens accepted by results(figures). Prefer string "all"/"none"; arrays are for names/filenames. */
|
|
1143
|
+
const RESULTS_FIGURE_ENUMS = new Set([
|
|
1144
|
+
"default",
|
|
1145
|
+
"combined_only",
|
|
1146
|
+
"all",
|
|
1147
|
+
"images",
|
|
1148
|
+
"none",
|
|
1149
|
+
]);
|
|
1150
|
+
/**
|
|
1151
|
+
* Coerce host-stringified figures args (e.g. `'["all"]'`) and singleton enum arrays
|
|
1152
|
+
* (`["all"]` → `"all"`) before Zod validation. Real filename arrays stay arrays.
|
|
1153
|
+
*/
|
|
1154
|
+
export function coerceFiguresArg(v) {
|
|
1155
|
+
if (v === undefined || v === null)
|
|
1156
|
+
return v;
|
|
1157
|
+
let val = v;
|
|
1158
|
+
if (typeof val === "string") {
|
|
1159
|
+
const trimmed = val.trim();
|
|
1160
|
+
if ((trimmed.startsWith("[") && trimmed.endsWith("]")) ||
|
|
1161
|
+
(trimmed.startsWith('"') && trimmed.endsWith('"'))) {
|
|
1162
|
+
try {
|
|
1163
|
+
val = JSON.parse(trimmed);
|
|
1164
|
+
}
|
|
1165
|
+
catch {
|
|
1166
|
+
return v;
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
if (Array.isArray(val)) {
|
|
1171
|
+
const asStrings = val.map((x) => String(x));
|
|
1172
|
+
if (asStrings.length === 1 && RESULTS_FIGURE_ENUMS.has(asStrings[0])) {
|
|
1173
|
+
return asStrings[0];
|
|
1174
|
+
}
|
|
1175
|
+
return asStrings;
|
|
1176
|
+
}
|
|
1177
|
+
return val;
|
|
1178
|
+
}
|
|
989
1179
|
/** Attach result figures with captions; tracks inlined names for dedup. */
|
|
990
1180
|
export async function attachResultsImages(content, jobId, jobType, summary, figures, includeIndividual, inlinedImages) {
|
|
991
1181
|
for (const name of getResultsImagesToFetch(jobType, summary, figures, includeIndividual)) {
|
package/dist/tools/account.js
CHANGED
|
@@ -38,7 +38,7 @@ export function registerAccountTool(server) {
|
|
|
38
38
|
| Action | Use when |
|
|
39
39
|
|--------|----------|
|
|
40
40
|
| status | Before large jobs — see plan tier, GPU availability, queue depth, training time estimates, credit balance. When the API is down, returns calm \`{ok:false, retry_after_sec}\` instead of HTML. |
|
|
41
|
-
| history | Viewing recent compute usage and credit spend |
|
|
41
|
+
| history | Viewing recent compute usage and credit spend (gateway-only; local/direct API soft-degrades to "unavailable" — use inventory/jobs list) |
|
|
42
42
|
| inventory | Rebuild a durable markdown overview of datasets + recent jobs (agent-friendly org catalog) |
|
|
43
43
|
| add_funds | Getting instructions to add credits |
|
|
44
44
|
| health | Diagnose API reachability during outages — GET /health + /ready; does **not** probe R2/object storage |
|
|
@@ -157,11 +157,32 @@ NOT FOR: Training itself — use train(action=map). This tool only manages the a
|
|
|
157
157
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
158
158
|
}
|
|
159
159
|
if (action === "history") {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
160
|
+
// Gateway serves /v1/compute/history (D1 leases). Local direct API has no handler → 404.
|
|
161
|
+
try {
|
|
162
|
+
const data = await apiCall("GET", `/v1/compute/history?limit=${limit || 10}`);
|
|
163
|
+
const rows = Array.isArray(data?.history) ? data.history : [];
|
|
164
|
+
const history = rows.length === 0
|
|
165
|
+
? "(none)"
|
|
166
|
+
: rows.map((h) => `- ${h.started_at} | ${h.tier} | ${h.duration_minutes} min | $${(h.credits_charged / 100).toFixed(2)}`).join("\n");
|
|
167
|
+
const bal = data?.credit_balance_cents != null
|
|
168
|
+
? `Credit Balance: $${(Number(data.credit_balance_cents) / 100).toFixed(2)}\n\n`
|
|
169
|
+
: "";
|
|
170
|
+
return {
|
|
171
|
+
content: [{ type: "text", text: `${bal}Recent Usage:\n${history}` }]
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
catch (e) {
|
|
175
|
+
const status = e?.httpStatus;
|
|
176
|
+
if (status === 404 || status === 410) {
|
|
177
|
+
return {
|
|
178
|
+
content: [{
|
|
179
|
+
type: "text",
|
|
180
|
+
text: "Usage history unavailable on this API endpoint (compute-lease history is gateway-only). Use account(action=inventory) or jobs(action=list) for recent job activity.",
|
|
181
|
+
}]
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
throw e;
|
|
185
|
+
}
|
|
165
186
|
}
|
|
166
187
|
if (action === "inventory") {
|
|
167
188
|
const jobLimit = limit && limit > 0 ? Math.min(Math.floor(limit), 100) : 50;
|
package/dist/tools/datasets.js
CHANGED
|
@@ -4,10 +4,14 @@ 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, 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";
|
|
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, awaitDatasetStageIfNeeded, streamFileSha256, putPresignedStream, resolveUploadContentType, suggestDatasetPreview, } from "../shared.js";
|
|
8
8
|
import { GZIP_UPLOAD_HINT } from "../job_status_format.js";
|
|
9
9
|
import { formatDatasetInventoryLine, formatTags } from "../inventory_format.js";
|
|
10
10
|
import { PERIODICITY_ROW_ORDER_NOTE, SMALL_N_SOFT, buildCpuScaleGuidanceLines, buildSmallNGuardrailLines, } from "../training_scale_guidance.js";
|
|
11
|
+
/** Percent-encode header values so Node fetch accepts Unicode (ByteString limit). */
|
|
12
|
+
function encodeHeaderValue(s) {
|
|
13
|
+
return encodeURIComponent(s);
|
|
14
|
+
}
|
|
11
15
|
/**
|
|
12
16
|
* Normalize a nullable string field from the API. Returns "" for absent values,
|
|
13
17
|
* empty strings, and the literal SQL/serialization sentinels "missing"/"null"
|
|
@@ -165,7 +169,7 @@ Step 1 (after upload): always datasets(action=preview) to verify column types an
|
|
|
165
169
|
action=preview: Show columns, stats, sample rows, cyclic/datetime detections. ALWAYS preview before train(action=map) on an unfamiliar dataset.
|
|
166
170
|
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.
|
|
167
171
|
action=list: List all datasets belonging to the organisation with id, name, rows, cols, created_at, description, tags (use tags/description or id to distinguish datasets with the same name).
|
|
168
|
-
action=get: Fetch one dataset by dataset_id — returns status, staged_prefix, staged_version, ingest_error, description/tags, and stage_job_id when staging is pending.
|
|
172
|
+
action=get: Fetch one dataset by dataset_id — returns status, train_ready (true only when status=ready AND staged_prefix is set), staged_prefix, staged_version, ingest_error, description/tags, and stage_job_id when staging is pending. Train only when train_ready=true (do not treat bare status=ready as enough).
|
|
169
173
|
action=update: PATCH name/description/tags on an existing dataset.
|
|
170
174
|
action=upload: Optional description and tags help later inventory (also settable via update).
|
|
171
175
|
DO NOT AUTO-SUBSET: Call action=subset only when the user explicitly requests a filter, slice, or random sample. Default path is upload → preview/analyze → train on the full dataset_id (GPU handles scale). Never downsample to "save time" or "be helpful".
|
|
@@ -230,7 +234,7 @@ ESCALATION: If upload fails with column errors, open the file locally and verify
|
|
|
230
234
|
})
|
|
231
235
|
.optional()
|
|
232
236
|
.describe("Deprecated — use filters instead. Single filter condition."),
|
|
233
|
-
expression: z.string().optional().describe("action=add_expression: math expression referencing column names (e.g. revenue/cost, log(price), rolling_mean(vol, 20))"),
|
|
237
|
+
expression: z.string().optional().describe("action=add_expression: math expression referencing column names (e.g. revenue/cost, log(price), rolling_mean(vol, 20)). Awkward names (spaces/parens): quote with backticks, e.g. `P1 (bar)` / 2"),
|
|
234
238
|
options: z
|
|
235
239
|
.object({
|
|
236
240
|
missing: z.enum(["skip", "zero", "interpolate"]).optional(),
|
|
@@ -368,16 +372,16 @@ ESCALATION: If upload fails with column errors, open the file locally and verify
|
|
|
368
372
|
if (isGzipInput) {
|
|
369
373
|
const gzBytes = await fs.readFile(resolved);
|
|
370
374
|
const gzHeaders = {
|
|
371
|
-
"X-Dataset-Name": name,
|
|
375
|
+
"X-Dataset-Name": encodeHeaderValue(name),
|
|
372
376
|
"Content-Type": uploadContentType,
|
|
373
377
|
"Content-Encoding": "gzip",
|
|
374
378
|
"Idempotency-Key": createHash("sha256").update(`${name}\n`).update(gzBytes).digest("hex"),
|
|
375
379
|
};
|
|
376
380
|
if (description !== undefined)
|
|
377
|
-
gzHeaders["X-Dataset-Description"] = description;
|
|
381
|
+
gzHeaders["X-Dataset-Description"] = encodeHeaderValue(description);
|
|
378
382
|
if (tags !== undefined)
|
|
379
|
-
gzHeaders["X-Dataset-Tags"] = JSON.stringify(tags);
|
|
380
|
-
const data = (await apiCall("POST", "/v1/datasets", gzBytes, gzHeaders, UPLOAD_DATASET_TIMEOUT_MS));
|
|
383
|
+
gzHeaders["X-Dataset-Tags"] = encodeHeaderValue(JSON.stringify(tags));
|
|
384
|
+
const data = await awaitDatasetStageIfNeeded((await apiCall("POST", "/v1/datasets", gzBytes, gzHeaders, UPLOAD_DATASET_TIMEOUT_MS)));
|
|
381
385
|
const gid = data.id ?? data.dataset_id;
|
|
382
386
|
if (gid != null)
|
|
383
387
|
data.suggested_next_step = `${suggestDatasetPreview(String(gid))} to inspect columns before training.`;
|
|
@@ -395,22 +399,22 @@ ESCALATION: If upload fails with column errors, open the file locally and verify
|
|
|
395
399
|
// cap) small; the API transparently decompresses. Small bodies stay plain.
|
|
396
400
|
const GZIP_THRESHOLD = 1024 * 1024; // 1 MB
|
|
397
401
|
const uploadHeaders = {
|
|
398
|
-
"X-Dataset-Name": name,
|
|
402
|
+
"X-Dataset-Name": encodeHeaderValue(name),
|
|
399
403
|
"Content-Type": uploadContentType,
|
|
400
404
|
// Deterministic key so a timed-out retry of the SAME upload reconciles to
|
|
401
405
|
// the original dataset server-side instead of creating a duplicate.
|
|
402
406
|
"Idempotency-Key": createHash("sha256").update(`${name}\n`).update(body).digest("hex"),
|
|
403
407
|
};
|
|
404
408
|
if (description !== undefined)
|
|
405
|
-
uploadHeaders["X-Dataset-Description"] = description;
|
|
409
|
+
uploadHeaders["X-Dataset-Description"] = encodeHeaderValue(description);
|
|
406
410
|
if (tags !== undefined)
|
|
407
|
-
uploadHeaders["X-Dataset-Tags"] = JSON.stringify(tags);
|
|
411
|
+
uploadHeaders["X-Dataset-Tags"] = encodeHeaderValue(JSON.stringify(tags));
|
|
408
412
|
let uploadBody = body;
|
|
409
413
|
if (Buffer.byteLength(body, "utf-8") > GZIP_THRESHOLD) {
|
|
410
414
|
uploadBody = gzipSync(Buffer.from(body, "utf-8"));
|
|
411
415
|
uploadHeaders["Content-Encoding"] = "gzip";
|
|
412
416
|
}
|
|
413
|
-
const data = (await apiCall("POST", "/v1/datasets", uploadBody, uploadHeaders, UPLOAD_DATASET_TIMEOUT_MS));
|
|
417
|
+
const data = await awaitDatasetStageIfNeeded((await apiCall("POST", "/v1/datasets", uploadBody, uploadHeaders, UPLOAD_DATASET_TIMEOUT_MS)));
|
|
414
418
|
const id = data.id ?? data.dataset_id;
|
|
415
419
|
if (id != null)
|
|
416
420
|
data.suggested_next_step = `${suggestDatasetPreview(String(id))} to inspect columns before training.`;
|
|
@@ -732,16 +736,20 @@ ESCALATION: If upload fails with column errors, open the file locally and verify
|
|
|
732
736
|
throw new Error("datasets(get) requires dataset_id");
|
|
733
737
|
const ds = (await apiCall("GET", `/v1/datasets/${dataset_id}`));
|
|
734
738
|
const tagStr = formatTags(ds.tags);
|
|
739
|
+
const trainReady = ds.train_ready === true;
|
|
735
740
|
const lines = [
|
|
736
741
|
`Dataset: ${ds.name ?? "?"} (${ds.id ?? dataset_id})`,
|
|
737
|
-
`Status: ${ds.status ?? "
|
|
742
|
+
`Status: ${ds.status ?? "unknown"}`,
|
|
743
|
+
`Train ready: ${trainReady ? "yes" : "no"}`,
|
|
738
744
|
`Rows × cols: ${ds.rows ?? "?"} × ${ds.cols ?? "?"}`,
|
|
739
745
|
ds.size_bytes != null ? `Size: ${Number(ds.size_bytes).toLocaleString()} bytes` : "",
|
|
740
746
|
ds.description != null && String(ds.description).trim() !== "" ? `Description: ${String(ds.description)}` : "",
|
|
741
747
|
tagStr ? `Tags: ${tagStr}` : "",
|
|
742
748
|
ds.staged_prefix != null ? `Staged prefix: ${String(ds.staged_prefix)}` : "",
|
|
743
749
|
ds.staged_version != null ? `Staged version: ${String(ds.staged_version)}` : "",
|
|
744
|
-
ds.stage_job_id != null
|
|
750
|
+
!trainReady && ds.stage_job_id != null
|
|
751
|
+
? `Stage job: ${String(ds.stage_job_id)} (poll jobs(action=status) until train_ready)`
|
|
752
|
+
: "",
|
|
745
753
|
cleanNullable(ds.ingest_error) ? `Ingest error: ${cleanNullable(ds.ingest_error)}` : "",
|
|
746
754
|
ds.created_at != null ? `Created: ${String(ds.created_at)}` : "",
|
|
747
755
|
].filter(Boolean);
|
package/dist/tools/inference.js
CHANGED
|
@@ -79,7 +79,7 @@ action=predict: Score rows against the trained map.
|
|
|
79
79
|
- "compact" → predictions.csv (row_id, bmu_x, bmu_y, bmu_node_index, cluster_id [, quantization_error, potential_anomaly]).
|
|
80
80
|
- "annotated" → annotated.csv (full source CSV with bmu_x, bmu_y, bmu_node_index, cluster_id appended). Requires a dataset (no inline rows).
|
|
81
81
|
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.
|
|
82
|
-
Schema rules:
|
|
82
|
+
Schema rules: prefer dataset_id for scoring. Inline rows may use raw cyclic keys (e.g. hour) — the API/worker expands them from training config to hour_cos/hour_sin; pre-expanded cos/sin columns also work. For a single inline row, the proxy uses the stateless model endpoint (same cyclic expansion). Raw categorical strings are allowed for baseline categorical_features models on that single-row path.
|
|
83
83
|
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.
|
|
84
84
|
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.
|
|
85
85
|
|
|
@@ -348,32 +348,64 @@ action=report: Returns a report manifest for the given job_id (job must be compl
|
|
|
348
348
|
const download_urls = (results.download_urls ?? {});
|
|
349
349
|
const figure_manifest = (results.figure_manifest ?? []);
|
|
350
350
|
const cluster_summary = results.cluster_summary;
|
|
351
|
+
const summary = (results.summary ?? {});
|
|
351
352
|
const expires_in = results.expires_in ?? 900;
|
|
353
|
+
const downloadKeys = Object.keys(download_urls);
|
|
354
|
+
const clusterSnippet = Array.isArray(cluster_summary) && cluster_summary.length > 0
|
|
355
|
+
? cluster_summary.slice(0, 3).map((c) => {
|
|
356
|
+
const id = c.cluster_id ?? c.id ?? "?";
|
|
357
|
+
const n = c.n_nodes ?? c.size ?? "?";
|
|
358
|
+
return { cluster_id: id, n_nodes: n };
|
|
359
|
+
})
|
|
360
|
+
: [];
|
|
352
361
|
const lines = [
|
|
353
362
|
`Report manifest — job: ${job_id}`,
|
|
354
363
|
`Use these artifacts to build your own report (Quarto, Jupyter, script). URLs expire in ${expires_in}s.`,
|
|
355
364
|
"",
|
|
356
365
|
"Figures (figure_manifest → download_urls):",
|
|
357
|
-
...figure_manifest.
|
|
366
|
+
...(figure_manifest.length
|
|
367
|
+
? figure_manifest.slice(0, 12).map((e) => ` ${e.logical_name} → ${e.filename}`)
|
|
368
|
+
: [" (none listed — small grids may omit PNGs; use cluster_summary / summary metrics below)"]),
|
|
358
369
|
figure_manifest.length > 12 ? ` ... and ${figure_manifest.length - 12} more` : "",
|
|
359
370
|
"",
|
|
360
371
|
"Key download URLs (use get_result_image or download_urls from results(action=get)):",
|
|
361
|
-
...
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
372
|
+
...(downloadKeys.length
|
|
373
|
+
? downloadKeys
|
|
374
|
+
.filter((k) => !k.endsWith(".json") || k === "cluster_summary.json")
|
|
375
|
+
.slice(0, 8)
|
|
376
|
+
.map((k) => ` ${k}: ${download_urls[k]?.slice(0, 60)}...`)
|
|
377
|
+
: [" (no presigned URLs — artifacts may still be in summary / cluster_summary inline)"]),
|
|
365
378
|
"",
|
|
366
379
|
cluster_summary?.length
|
|
367
|
-
? `Cluster summary: ${cluster_summary.length} clusters (
|
|
380
|
+
? `Cluster summary: ${cluster_summary.length} clusters (inline snippet below; full via GET .../cluster_summary or download_urls.cluster_summary.json).`
|
|
368
381
|
: "Cluster summary: not available for this job (train with current worker to get it).",
|
|
382
|
+
...(clusterSnippet.length
|
|
383
|
+
? ["Inline cluster_summary (first 3):", ...clusterSnippet.map((c) => ` cluster ${c.cluster_id}: n_nodes=${c.n_nodes}`)]
|
|
384
|
+
: []),
|
|
369
385
|
"",
|
|
370
386
|
"Metrics: in summary (quantization_error, topographic_error, explained_variance, silhouette, etc.) or GET .../quality-report.",
|
|
387
|
+
summary.quantization_error != null ? ` QE=${summary.quantization_error}` : "",
|
|
388
|
+
summary.topographic_error != null ? ` TE=${summary.topographic_error}` : "",
|
|
371
389
|
];
|
|
372
390
|
const manifestText = lines.filter(Boolean).join("\n");
|
|
373
391
|
return {
|
|
374
392
|
content: [
|
|
375
393
|
{ type: "text", text: manifestText },
|
|
376
|
-
{
|
|
394
|
+
{
|
|
395
|
+
type: "text",
|
|
396
|
+
text: "Structured manifest (for automation): " +
|
|
397
|
+
JSON.stringify({
|
|
398
|
+
job_id,
|
|
399
|
+
figure_manifest,
|
|
400
|
+
has_cluster_summary: !!cluster_summary?.length,
|
|
401
|
+
cluster_summary_snippet: clusterSnippet,
|
|
402
|
+
download_url_keys: downloadKeys,
|
|
403
|
+
summary_metrics: {
|
|
404
|
+
quantization_error: summary.quantization_error ?? null,
|
|
405
|
+
topographic_error: summary.topographic_error ?? null,
|
|
406
|
+
},
|
|
407
|
+
}),
|
|
408
|
+
},
|
|
377
409
|
],
|
|
378
410
|
};
|
|
379
411
|
});
|
package/dist/tools/results.js
CHANGED
|
@@ -2,7 +2,7 @@ import path from "node:path";
|
|
|
2
2
|
import fs from "node:fs/promises";
|
|
3
3
|
import { z } from "zod";
|
|
4
4
|
import { registerAuditedTool } from "../audit.js";
|
|
5
|
-
import { apiCall, apiRawCall, getWorkspaceRootAsync, sandboxPath, pollUntilComplete, tryAttachImage, resetInlineAttachBudget, attachResultsImages, resolveOutputDpi, POLL_RECOLOR_MAX_MS, fmtDensityDiffNode, getCaptionForImage, shouldFetchAllRemainingFigures, mimeForFilename, structuredTextResult, } from "../shared.js";
|
|
5
|
+
import { apiCall, apiRawCall, getWorkspaceRootAsync, sandboxPath, pollUntilComplete, tryAttachImage, resetInlineAttachBudget, attachResultsImages, resolveOutputDpi, POLL_RECOLOR_MAX_MS, fmtDensityDiffNode, getCaptionForImage, shouldFetchAllRemainingFigures, mimeForFilename, structuredTextResult, coerceFiguresArg, } from "../shared.js";
|
|
6
6
|
export function registerResultsTool(server) {
|
|
7
7
|
registerAuditedTool(server, "results", `Retrieve, recolor, download, or export figures and metrics for a completed map job. Presentation and artifact access only — operations on the frozen map (predict, compare, project, impute_column, transition_flow) live in **inference**.
|
|
8
8
|
|
|
@@ -20,21 +20,21 @@ ONLY call this after jobs(action=status) returns "completed".
|
|
|
20
20
|
ESCALATION: If job not found, verify job_id. If "job not complete", poll with jobs(action=status).
|
|
21
21
|
|
|
22
22
|
action=get: Returns text summary with quality metrics and inline images.
|
|
23
|
-
- figures: omit = combined only. "all" = all published plots (excludes unpublished cyclic cos/sin expansions unless train used viz_upload_cyclic_components=true). "none" = metrics text only, no images (recommended for sweeps and LLM clients to keep tool payloads small). Array = specific logical names (combined, umatrix, hit_histogram, learning_curve, correlation, divergence_kl, component_1..N). Request unpublished cyclic planes via results(recolor, figures=[component_N, ...]).
|
|
23
|
+
- figures: omit = combined only. Prefer string "all" / "none" (not an array). "all" = all published plots (excludes unpublished cyclic cos/sin expansions unless train used viz_upload_cyclic_components=true). "none" = metrics text only, no images (recommended for sweeps and LLM clients to keep tool payloads small). Array = specific logical names (combined, umatrix, hit_histogram, learning_curve, correlation, divergence_kl, component_1..N) or download filenames. Hosts that stringify arrays are coerced. Request unpublished cyclic planes via results(recolor, figures=[component_N, ...]).
|
|
24
24
|
- include_individual: if true (and figures omitted), inlines every component plane.
|
|
25
25
|
- After recolor or project, use the job_id returned by that operation to get the new artifacts, not the original training job_id.
|
|
26
26
|
- After showing results, guide the user: QE interpretation, whether to retrain, which features to explore.
|
|
27
27
|
- METRIC INTERPRETATION: QE lower=better (<1.5 good) | TE<0.1 good | Explained variance>0.7 good | Silhouette higher=better | Davies-Bouldin lower=better.
|
|
28
28
|
|
|
29
|
-
action=export: Structured data exports. Use export_type= to choose what to export.
|
|
30
|
-
- export_type=training_log: learning curve sparklines + plot. Diagnose convergence/plateau/divergence.
|
|
29
|
+
action=export: Structured data exports (not the same as download). Use export_type= to choose what to export.
|
|
30
|
+
- export_type=training_log: learning curve sparklines + plot. Diagnose convergence/plateau/divergence. Prefer this over download for the training log.
|
|
31
31
|
- export_type=weights: full weight matrix + normalization stats. For external analysis or custom viz.
|
|
32
32
|
- export_type=nodes: per-node hit count + feature stats. Profile clusters and operating modes.
|
|
33
33
|
|
|
34
|
-
action=download: Save figures to disk. Use so user can open, share, or version files locally.
|
|
34
|
+
action=download: Save figures/artifacts to disk (deduped file list). Use so user can open, share, or version files locally.
|
|
35
35
|
- folder: e.g. "." or "./results". Relative to the client's working directory (or MCP workspace). Each job lands in a dedicated subfolder under folder: {job_type}_{label} when label was set at train time, otherwise {job_type}_{job_id_prefix} — so parallel downloads never overwrite summary.json or figure names.
|
|
36
|
-
- figures: "all" (default)
|
|
37
|
-
- include_json: when true, also saves summary.json
|
|
36
|
+
- figures: "all" (default) downloads published image files; OR a string array of exact artifact filenames from the job (e.g. ["predictions.csv"], ["combined.png","umatrix.png"]). Enum tokens like "none" are for action=get, not download.
|
|
37
|
+
- include_json: when true, also saves summary.json and other JSON artifacts (use this when you need CSVs listed in summary.files without naming each figure).
|
|
38
38
|
|
|
39
39
|
action=recolor: Change colormap or output format — no retraining. Returns a new job_id; auto-polls 60s.
|
|
40
40
|
AFTER: use results(action=get, job_id=NEW_JOB_ID).
|
|
@@ -47,10 +47,10 @@ NOT FOR: Jobs that haven't completed (use jobs(action=status) first), or tempora
|
|
|
47
47
|
.enum(["get", "recolor", "download", "export", "feature_divergences"])
|
|
48
48
|
.describe("get: inline results + metrics; recolor: new colormap/format (async); download: save to disk; export: structured data; feature_divergences: KL/W1 on component planes (async)."),
|
|
49
49
|
job_id: z.string().describe("Job ID of a completed job"),
|
|
50
|
-
figures: z
|
|
50
|
+
figures: z.preprocess(coerceFiguresArg, z
|
|
51
51
|
.union([z.enum(["default", "combined_only", "all", "images", "none"]), z.array(z.string())])
|
|
52
52
|
.optional()
|
|
53
|
-
.describe("action=get: omit=combined only; 'all'=all plots; 'none'=metrics text only
|
|
53
|
+
.describe("Prefer string 'all'/'none'. action=get: omit=combined only; 'all'=all plots; 'none'=metrics text only; array=logical names (combined,umatrix,…). action=download: 'all'=image files; array=exact filenames (e.g. combined.png). Stringified JSON arrays from hosts are accepted.")),
|
|
54
54
|
include_individual: z
|
|
55
55
|
.boolean()
|
|
56
56
|
.optional()
|
|
@@ -631,6 +631,7 @@ NOT FOR: Jobs that haven't completed (use jobs(action=status) first), or tempora
|
|
|
631
631
|
else {
|
|
632
632
|
toDownload = files.filter(isImage);
|
|
633
633
|
}
|
|
634
|
+
toDownload = [...new Set(toDownload)];
|
|
634
635
|
let resolvedDir = sandboxPath(folder, await getWorkspaceRootAsync(server));
|
|
635
636
|
// Always namespace each job's files into its own subfolder so that
|
|
636
637
|
// downloading multiple jobs (or job types) into the same folder never
|
package/dist/tools/train.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
import { registerAuditedTool } from "../audit.js";
|
|
3
|
-
import { apiCall } from "../shared.js";
|
|
3
|
+
import { apiCall, ensureDatasetTrainReady } from "../shared.js";
|
|
4
4
|
import { buildTrainSubmitExtras } from "../train_submit_message.js";
|
|
5
5
|
import { TRAINING_MONITOR_URI, buildTrainingMonitorResult, } from "./training_monitor.js";
|
|
6
6
|
import { TRAINING_INPUT_SCHEMA, buildTrainMapParams, buildFloopParams, fetchTrainingPresets, assertValidNormalizationArgs, } from "./training_core.js";
|
|
@@ -52,6 +52,7 @@ function assertImputeTrainingArgs(args) {
|
|
|
52
52
|
}
|
|
53
53
|
/** Shared POST /v1/jobs + train submit message extras (map/siom/impute and floop). */
|
|
54
54
|
async function submitTrainJob(opts) {
|
|
55
|
+
await ensureDatasetTrainReady(opts.dataset_id);
|
|
55
56
|
const submitBody = { dataset_id: opts.dataset_id, params: opts.params };
|
|
56
57
|
if (opts.label && opts.label.trim() !== "")
|
|
57
58
|
submitBody.label = opts.label;
|
|
@@ -2,9 +2,10 @@ import { buildVizStandaloneUrl } from "./viz_links.js";
|
|
|
2
2
|
export function buildTrainSubmitExtras(opts) {
|
|
3
3
|
const { newJobId, totalRows, hasTransforms, prepareJobId, variantPrefix, paramSummary, impute, resultsHint } = opts;
|
|
4
4
|
const monitorUrl = buildVizStandaloneUrl("training-monitor", newJobId);
|
|
5
|
+
const prepareKind = impute ? "prepare_impute_matrix" : "prepare_training_matrix";
|
|
5
6
|
let msg = `Job submitted (${variantPrefix}, ${paramSummary}). `;
|
|
6
7
|
if (prepareJobId) {
|
|
7
|
-
msg += `Preprocessing job
|
|
8
|
+
msg += `Preprocessing job ${prepareKind} (${prepareJobId}) runs on worker-io first; train job ${newJobId} starts after it completes. Poll jobs(action=status, job_id="${prepareJobId}") or jobs(action=status, job_id="${newJobId}") (shows prepare_job_id + waiting_for_prepare) until prepare completes, then poll the train job. `;
|
|
8
9
|
}
|
|
9
10
|
msg += `Training monitor opened for job ${newJobId}`;
|
|
10
11
|
if (monitorUrl)
|
|
@@ -25,7 +25,8 @@ export function teCurveLabel(base) {
|
|
|
25
25
|
* epochs=[N,0], so convergence_errors is empty while ordering has many batch points.
|
|
26
26
|
*/
|
|
27
27
|
export function isFloopJob(data) {
|
|
28
|
-
const
|
|
28
|
+
const rawPhase = String(data.training_progress_phase ?? data.progress_phase ?? "").toLowerCase();
|
|
29
|
+
const phase = rawPhase === "missing" ? "" : rawPhase;
|
|
29
30
|
if (phase === "floop")
|
|
30
31
|
return true;
|
|
31
32
|
const jt = String(data.job_type ?? "").toLowerCase();
|
|
@@ -139,7 +139,7 @@ for compatibility with Zod schema generation. Both are functionally equivalent f
|
|
|
139
139
|
- "app": Tool callable by the app from this server only`)});v.object({mimeTypes:v.array(v.string()).optional().describe('Array of supported MIME types for UI resources.\nMust include `"text/html;profile=mcp-app"` for MCP Apps support.')});v.object({method:v.literal("ui/message"),params:v.object({role:v.literal("user").describe('Message role, currently only "user" is supported.'),content:v.array(fo).describe("Message content blocks (text, image, etc.).")})});v.object({method:v.literal("ui/notifications/sandbox-resource-ready"),params:v.object({html:v.string().describe("HTML content to load into the inner iframe."),sandbox:v.string().optional().describe("Optional override for the inner iframe's sandbox attribute."),csp:fd.optional().describe("CSP configuration from resource metadata."),permissions:md.optional().describe("Sandbox permissions from resource metadata.")})});var TT=v.object({method:v.literal("ui/notifications/tool-result"),params:Na.describe("Standard MCP tool execution result.")}),h$=v.object({toolInfo:v.object({id:oo.optional().describe("JSON-RPC id of the tools/call request."),tool:gu.describe("Tool definition including name, inputSchema, etc.")}).optional().describe("Metadata of the tool call that instantiated this App."),theme:mT.optional().describe("Current color theme preference."),styles:kT.optional().describe("Style configuration for theming the app."),displayMode:Qr.optional().describe("How the UI is currently displayed."),availableDisplayModes:v.array(Qr).optional().describe("Display modes the host supports."),containerDimensions:v.union([v.object({height:v.number().describe("Fixed container height in pixels.")}),v.object({maxHeight:v.union([v.number(),v.undefined()]).optional().describe("Maximum container height in pixels.")})]).and(v.union([v.object({width:v.number().describe("Fixed container width in pixels.")}),v.object({maxWidth:v.union([v.number(),v.undefined()]).optional().describe("Maximum container width in pixels.")})])).optional().describe(`Container dimensions. Represents the dimensions of the iframe or other
|
|
140
140
|
container holding the app. Specify either width or maxWidth, and either height or maxHeight.`),locale:v.string().optional().describe("User's language and region preference in BCP 47 format."),timeZone:v.string().optional().describe("User's timezone in IANA format."),userAgent:v.string().optional().describe("Host application identifier."),platform:v.union([v.literal("web"),v.literal("desktop"),v.literal("mobile")]).optional().describe("Platform type for responsive design decisions."),deviceCapabilities:v.object({touch:v.boolean().optional().describe("Whether the device supports touch input."),hover:v.boolean().optional().describe("Whether the device supports hover interactions.")}).optional().describe("Device input capabilities."),safeAreaInsets:v.object({top:v.number().describe("Top safe area inset in pixels."),right:v.number().describe("Right safe area inset in pixels."),bottom:v.number().describe("Bottom safe area inset in pixels."),left:v.number().describe("Left safe area inset in pixels.")}).optional().describe("Mobile safe area boundaries in pixels.")}).passthrough(),ZT=v.object({method:v.literal("ui/notifications/host-context-changed"),params:h$.describe("Partial context update containing only changed fields.")});v.object({method:v.literal("ui/update-model-context"),params:v.object({content:v.array(fo).optional().describe("Context content blocks (text, image, etc.)."),structuredContent:v.record(v.string(),v.unknown().describe("Structured content for machine-readable context data.")).optional().describe("Structured content for machine-readable context data.")})});v.object({method:v.literal("ui/initialize"),params:v.object({appInfo:Ea.describe("App identification (name and version)."),appCapabilities:IT.describe("Features and capabilities this app provides."),protocolVersion:v.string().describe("Protocol version this app supports.")})});var PT=v.object({protocolVersion:v.string().describe('Negotiated protocol version string (e.g., "2025-11-21").'),hostInfo:Ea.describe("Host application identification and version."),hostCapabilities:ST.describe("Features and capabilities provided by the host."),hostContext:h$.describe("Rich context about the host environment.")}).passthrough();class ET extends Ux{_appInfo;_capabilities;options;_hostCapabilities;_hostInfo;_hostContext;constructor(t,i={},r={autoResize:!0}){super(r),this._appInfo=t,this._capabilities=i,this.options=r,this.setRequestHandler(Ua,n=>(console.log("Received ping:",n.params),{})),this.onhostcontextchanged=()=>{}}getHostCapabilities(){return this._hostCapabilities}getHostVersion(){return this._hostInfo}getHostContext(){return this._hostContext}set ontoolinput(t){this.setNotificationHandler(_T,i=>t(i.params))}set ontoolinputpartial(t){this.setNotificationHandler(bT,i=>t(i.params))}set ontoolresult(t){this.setNotificationHandler(TT,i=>t(i.params))}set ontoolcancelled(t){this.setNotificationHandler($T,i=>t(i.params))}set onhostcontextchanged(t){this.setNotificationHandler(ZT,i=>{this._hostContext={...this._hostContext,...i.params},t(i.params)})}set onteardown(t){this.setRequestHandler(wT,(i,r)=>t(i.params,r))}set oncalltool(t){this.setRequestHandler(xp,(i,r)=>t(i.params,r))}set onlisttools(t){this.setRequestHandler(Ip,(i,r)=>t(i.params,r))}assertCapabilityForMethod(t){}assertRequestHandlerCapability(t){switch(t){case"tools/call":case"tools/list":if(!this._capabilities.tools)throw Error(`Client does not support tool capability (required for ${t})`);return;case"ping":case"ui/resource-teardown":return;default:throw Error(`No handler for method ${t} registered`)}}assertNotificationCapability(t){}assertTaskCapability(t){throw Error("Tasks are not supported in MCP Apps")}assertTaskHandlerCapability(t){throw Error("Task handlers are not supported in MCP Apps")}async callServerTool(t,i){return await this.request({method:"tools/call",params:t},Na,i)}sendMessage(t,i){return this.request({method:"ui/message",params:t},vT,i)}sendLog(t){return this.notification({method:"notifications/message",params:t})}updateModelContext(t,i){return this.request({method:"ui/update-model-context",params:t},tu,i)}openLink(t,i){return this.request({method:"ui/open-link",params:t},gT,i)}sendOpenLink=this.openLink;requestDisplayMode(t,i){return this.request({method:"ui/request-display-mode",params:t},xT,i)}sendSizeChanged(t){return this.notification({method:"ui/notifications/size-changed",params:t})}setupSizeChangedNotifications(){let t=!1,i=0,r=0,n=()=>{t||(t=!0,requestAnimationFrame(()=>{t=!1;let a=document.documentElement,s=a.style.width,l=a.style.height;a.style.width="fit-content",a.style.height="fit-content";let c=a.getBoundingClientRect();a.style.width=s,a.style.height=l;let m=window.innerWidth-a.clientWidth,f=Math.ceil(c.width+m),h=Math.ceil(c.height);(f!==i||h!==r)&&(i=f,r=h,this.sendSizeChanged({width:f,height:h}))}))};n();let o=new ResizeObserver(n);return o.observe(document.documentElement),o.observe(document.body),()=>o.disconnect()}async connect(t=new Ox(window.parent,window.parent),i){await super.connect(t);try{let r=await this.request({method:"ui/initialize",params:{appCapabilities:this._capabilities,appInfo:this._appInfo,protocolVersion:jx}},PT,i);if(r===void 0)throw Error(`Server sent invalid initialize result: ${r}`);this._hostCapabilities=r.hostCapabilities,this._hostInfo=r.hostInfo,this._hostContext=r.hostContext,await this.notification({method:"ui/notifications/initialized"}),this.options?.autoResize&&this.setupSizeChangedNotifications()}catch(r){throw this.close(),r}}}const UT=!0,ut="u-",NT="uplot",OT=ut+"hz",jT=ut+"vt",DT=ut+"title",RT=ut+"wrap",AT=ut+"under",CT=ut+"over",LT=ut+"axis",bi=ut+"off",MT=ut+"select",FT=ut+"cursor-x",JT=ut+"cursor-y",qT=ut+"cursor-pt",VT=ut+"legend",WT=ut+"live",HT=ut+"inline",BT=ut+"series",GT=ut+"marker",Wf=ut+"label",KT=ut+"value",Dr="width",Rr="height",Nr="top",Hf="bottom",er="left",rl="right",pd="#000",Bf=pd+"0",ol="mousemove",Gf="mousedown",al="mouseup",Kf="mouseenter",Yf="mouseleave",Xf="dblclick",YT="resize",XT="scroll",Qf="change",wa="dppxchange",hd="--",br=typeof window<"u",zl=br?document:null,ir=br?window:null,QT=br?navigator:null;let we,na;function Tl(){let e=devicePixelRatio;we!=e&&(we=e,na&&Pl(Qf,na,Tl),na=matchMedia(`(min-resolution: ${we-.001}dppx) and (max-resolution: ${we+.001}dppx)`),wi(Qf,na,Tl),ir.dispatchEvent(new CustomEvent(wa)))}function Vt(e,t){if(t!=null){let i=e.classList;!i.contains(t)&&i.add(t)}}function Zl(e,t){let i=e.classList;i.contains(t)&&i.remove(t)}function Ce(e,t,i){e.style[t]=i+"px"}function bn(e,t,i,r){let n=zl.createElement(e);return t!=null&&Vt(n,t),i?.insertBefore(n,r),n}function an(e,t){return bn("div",e,t)}const em=new WeakMap;function Pn(e,t,i,r,n){let o="translate("+t+"px,"+i+"px)",a=em.get(e);o!=a&&(e.style.transform=o,em.set(e,o),t<0||i<0||t>r||i>n?Vt(e,bi):Zl(e,bi))}const tm=new WeakMap;function nm(e,t,i){let r=t+i,n=tm.get(e);r!=n&&(tm.set(e,r),e.style.background=t,e.style.borderColor=i)}const im=new WeakMap;function rm(e,t,i,r){let n=t+""+i,o=im.get(e);n!=o&&(im.set(e,n),e.style.height=i+"px",e.style.width=t+"px",e.style.marginLeft=r?-t/2+"px":0,e.style.marginTop=r?-i/2+"px":0)}const gd={passive:!0},e6={...gd,capture:!0};function wi(e,t,i,r){t.addEventListener(e,i,r?e6:gd)}function Pl(e,t,i,r){t.removeEventListener(e,i,gd)}br&&Tl();function $n(e,t,i,r){let n;i=i||0,r=r||t.length-1;let o=r<=2147483647;for(;r-i>1;)n=o?i+r>>1:Ht((i+r)/2),t[n]<e?i=n:r=n;return e-t[i]<=t[r]-e?i:r}function g$(e){return(i,r,n)=>{let o=-1,a=-1;for(let s=r;s<=n;s++)if(e(i[s])){o=s;break}for(let s=n;s>=r;s--)if(e(i[s])){a=s;break}return[o,a]}}const v$=e=>e!=null,_$=e=>e!=null&&e>0,Is=g$(v$),t6=g$(_$);function n6(e,t,i,r=0,n=!1){let o=n?t6:Is,a=n?_$:v$;[t,i]=o(e,t,i);let s=e[t],l=e[t];if(t>-1)if(r==1)s=e[t],l=e[i];else if(r==-1)s=e[i],l=e[t];else for(let c=t;c<=i;c++){let m=e[c];a(m)&&(m<s?s=m:m>l&&(l=m))}return[s??Ee,l??-Ee]}function xs(e,t,i,r){let n=sm(e),o=sm(t);e==t&&(n==-1?(e*=i,t/=i):(e/=i,t*=i));let a=i==10?Rn:b$,s=n==1?Ht:sn,l=o==1?sn:Ht,c=s(a(lt(e))),m=l(a(lt(t))),f=fr(i,c),h=fr(i,m);return i==10&&(c<0&&(f=Ue(f,-c)),m<0&&(h=Ue(h,-m))),r||i==2?(e=f*n,t=h*o):(e=w$(e,f),t=zs(t,h)),[e,t]}function vd(e,t,i,r){let n=xs(e,t,i,r);return e==0&&(n[0]=0),t==0&&(n[1]=0),n}const _d=.1,om={mode:3,pad:_d},Mr={pad:0,soft:null,mode:0},i6={min:Mr,max:Mr};function Sa(e,t,i,r){return Ts(i)?am(e,t,i):(Mr.pad=i,Mr.soft=r?0:null,Mr.mode=r?3:0,am(e,t,i6))}function ye(e,t){return e??t}function r6(e,t,i){for(t=ye(t,0),i=ye(i,e.length-1);t<=i;){if(e[t]!=null)return!0;t++}return!1}function am(e,t,i){let r=i.min,n=i.max,o=ye(r.pad,0),a=ye(n.pad,0),s=ye(r.hard,-Ee),l=ye(n.hard,Ee),c=ye(r.soft,Ee),m=ye(n.soft,-Ee),f=ye(r.mode,0),h=ye(n.mode,0),$=t-e,S=Rn($),U=Ut(lt(e),lt(t)),D=Rn(U),J=lt(D-S);($<1e-24||J>10)&&($=0,(e==0||t==0)&&($=1e-24,f==2&&c!=Ee&&(o=0),h==2&&m!=-Ee&&(a=0)));let k=$||U||1e3,T=Rn(k),z=fr(10,Ht(T)),ee=k*($==0?e==0?.1:1:o),C=Ue(w$(e-ee,z/10),24),se=e>=c&&(f==1||f==3&&C<=c||f==2&&C>=c)?c:Ee,te=Ut(s,C<se&&e>=se?se:yn(se,C)),pe=k*($==0?t==0?.1:1:a),oe=Ue(zs(t+pe,z/10),24),O=t<=m&&(h==1||h==3&&oe>=m||h==2&&oe<=m)?m:-Ee,ue=yn(l,oe>O&&t<=O?O:Ut(O,oe));return te==ue&&te==0&&(ue=100),[te,ue]}const o6=new Intl.NumberFormat(br?QT.language:"en-US"),bd=e=>o6.format(e),Xt=Math,la=Xt.PI,lt=Xt.abs,Ht=Xt.floor,st=Xt.round,sn=Xt.ceil,yn=Xt.min,Ut=Xt.max,fr=Xt.pow,sm=Xt.sign,Rn=Xt.log10,b$=Xt.log2,a6=(e,t=1)=>Xt.sinh(e)*t,sl=(e,t=1)=>Xt.asinh(e/t),Ee=1/0;function lm(e){return(Rn((e^e>>31)-(e>>31))|0)+1}function El(e,t,i){return yn(Ut(e,t),i)}function $$(e){return typeof e=="function"}function he(e){return $$(e)?e:()=>e}const s6=()=>{},y$=e=>e,k$=(e,t)=>t,l6=e=>null,um=e=>!0,cm=(e,t)=>e==t,u6=/\.\d*?(?=9{6,}|0{6,})/gm,Ti=e=>{if(I$(e)||ni.has(e))return e;const t=`${e}`,i=t.match(u6);if(i==null)return e;let r=i[0].length-1;if(t.indexOf("e-")!=-1){let[n,o]=t.split("e");return+`${Ti(n)}e${o}`}return Ue(e,r)};function vi(e,t){return Ti(Ue(Ti(e/t))*t)}function zs(e,t){return Ti(sn(Ti(e/t))*t)}function w$(e,t){return Ti(Ht(Ti(e/t))*t)}function Ue(e,t=0){if(I$(e))return e;let i=10**t,r=e*i*(1+Number.EPSILON);return st(r)/i}const ni=new Map;function S$(e){return((""+e).split(".")[1]||"").length}function eo(e,t,i,r){let n=[],o=r.map(S$);for(let a=t;a<i;a++){let s=lt(a),l=Ue(fr(e,a),s);for(let c=0;c<r.length;c++){let m=e==10?+`${r[c]}e${a}`:r[c]*l,f=(a>=0?0:s)+(a>=o[c]?0:o[c]),h=e==10?m:Ue(m,f);n.push(h),ni.set(h,f)}}return n}const Fr={},$d=[],mr=[null,null],Gn=Array.isArray,I$=Number.isInteger,c6=e=>e===void 0;function dm(e){return typeof e=="string"}function Ts(e){let t=!1;if(e!=null){let i=e.constructor;t=i==null||i==Object}return t}function d6(e){return e!=null&&typeof e=="object"}const f6=Object.getPrototypeOf(Uint8Array),x$="__proto__";function pr(e,t=Ts){let i;if(Gn(e)){let r=e.find(n=>n!=null);if(Gn(r)||t(r)){i=Array(e.length);for(let n=0;n<e.length;n++)i[n]=pr(e[n],t)}else i=e.slice()}else if(e instanceof f6)i=e.slice();else if(t(e)){i={};for(let r in e)r!=x$&&(i[r]=pr(e[r],t))}else i=e;return i}function rt(e){let t=arguments;for(let i=1;i<t.length;i++){let r=t[i];for(let n in r)n!=x$&&(Ts(e[n])?rt(e[n],pr(r[n])):e[n]=pr(r[n]))}return e}const m6=0,p6=1,h6=2;function g6(e,t,i){for(let r=0,n,o=-1;r<t.length;r++){let a=t[r];if(a>o){for(n=a-1;n>=0&&e[n]==null;)e[n--]=null;for(n=a+1;n<i&&e[n]==null;)e[o=n++]=null}}}function v6(e,t){if($6(e)){let a=e[0].slice();for(let s=1;s<e.length;s++)a.push(...e[s].slice(1));return y6(a[0])||(a=b6(a)),a}let i=new Set;for(let a=0;a<e.length;a++){let l=e[a][0],c=l.length;for(let m=0;m<c;m++)i.add(l[m])}let r=[Array.from(i).sort((a,s)=>a-s)],n=r[0].length,o=new Map;for(let a=0;a<n;a++)o.set(r[0][a],a);for(let a=0;a<e.length;a++){let s=e[a],l=s[0];for(let c=1;c<s.length;c++){let m=s[c],f=Array(n).fill(void 0),h=t?t[a][c]:p6,$=[];for(let S=0;S<m.length;S++){let U=m[S],D=o.get(l[S]);U===null?h!=m6&&(f[D]=U,h==h6&&$.push(D)):f[D]=U}g6(f,$,n),r.push(f)}}return r}const _6=typeof queueMicrotask>"u"?e=>Promise.resolve().then(e):queueMicrotask;function b6(e){let t=e[0],i=t.length,r=Array(i);for(let o=0;o<r.length;o++)r[o]=o;r.sort((o,a)=>t[o]-t[a]);let n=[];for(let o=0;o<e.length;o++){let a=e[o],s=Array(i);for(let l=0;l<i;l++)s[l]=a[r[l]];n.push(s)}return n}function $6(e){let t=e[0][0],i=t.length;for(let r=1;r<e.length;r++){let n=e[r][0];if(n.length!=i)return!1;if(n!=t){for(let o=0;o<i;o++)if(n[o]!=t[o])return!1}}return!0}function y6(e,t=100){const i=e.length;if(i<=1)return!0;let r=0,n=i-1;for(;r<=n&&e[r]==null;)r++;for(;n>=r&&e[n]==null;)n--;if(n<=r)return!0;const o=Ut(1,Ht((n-r+1)/t));for(let a=e[r],s=r+o;s<=n;s+=o){const l=e[s];if(l!=null){if(l<=a)return!1;a=l}}return!0}const z$=["January","February","March","April","May","June","July","August","September","October","November","December"],T$=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];function Z$(e){return e.slice(0,3)}const k6=T$.map(Z$),w6=z$.map(Z$),S6={MMMM:z$,MMM:w6,WWWW:T$,WWW:k6};function Or(e){return(e<10?"0":"")+e}function I6(e){return(e<10?"00":e<100?"0":"")+e}const x6={YYYY:e=>e.getFullYear(),YY:e=>(e.getFullYear()+"").slice(2),MMMM:(e,t)=>t.MMMM[e.getMonth()],MMM:(e,t)=>t.MMM[e.getMonth()],MM:e=>Or(e.getMonth()+1),M:e=>e.getMonth()+1,DD:e=>Or(e.getDate()),D:e=>e.getDate(),WWWW:(e,t)=>t.WWWW[e.getDay()],WWW:(e,t)=>t.WWW[e.getDay()],HH:e=>Or(e.getHours()),H:e=>e.getHours(),h:e=>{let t=e.getHours();return t==0?12:t>12?t-12:t},AA:e=>e.getHours()>=12?"PM":"AM",aa:e=>e.getHours()>=12?"pm":"am",a:e=>e.getHours()>=12?"p":"a",mm:e=>Or(e.getMinutes()),m:e=>e.getMinutes(),ss:e=>Or(e.getSeconds()),s:e=>e.getSeconds(),fff:e=>I6(e.getMilliseconds())};function yd(e,t){t=t||S6;let i=[],r=/\{([a-z]+)\}|[^{]+/gi,n;for(;n=r.exec(e);)i.push(n[0][0]=="{"?x6[n[1]]:n[0]);return o=>{let a="";for(let s=0;s<i.length;s++)a+=typeof i[s]=="string"?i[s]:i[s](o,t);return a}}const z6=new Intl.DateTimeFormat().resolvedOptions().timeZone;function T6(e,t){let i;return t=="UTC"||t=="Etc/UTC"?i=new Date(+e+e.getTimezoneOffset()*6e4):t==z6?i=e:(i=new Date(e.toLocaleString("en-US",{timeZone:t})),i.setMilliseconds(e.getMilliseconds())),i}const P$=e=>e%1==0,Ia=[1,2,2.5,5],Z6=eo(10,-32,0,Ia),E$=eo(10,0,32,Ia),P6=E$.filter(P$),_i=Z6.concat(E$),kd=`
|
|
141
141
|
`,U$="{YYYY}",fm=kd+U$,N$="{M}/{D}",Ar=kd+N$,ia=Ar+"/{YY}",O$="{aa}",E6="{h}:{mm}",tr=E6+O$,mm=kd+tr,pm=":{ss}",Ie=null;function j$(e){let t=e*1e3,i=t*60,r=i*60,n=r*24,o=n*30,a=n*365,l=(e==1?eo(10,0,3,Ia).filter(P$):eo(10,-3,0,Ia)).concat([t,t*5,t*10,t*15,t*30,i,i*5,i*10,i*15,i*30,r,r*2,r*3,r*4,r*6,r*8,r*12,n,n*2,n*3,n*4,n*5,n*6,n*7,n*8,n*9,n*10,n*15,o,o*2,o*3,o*4,o*6,a,a*2,a*5,a*10,a*25,a*50,a*100]);const c=[[a,U$,Ie,Ie,Ie,Ie,Ie,Ie,1],[n*28,"{MMM}",fm,Ie,Ie,Ie,Ie,Ie,1],[n,N$,fm,Ie,Ie,Ie,Ie,Ie,1],[r,"{h}"+O$,ia,Ie,Ar,Ie,Ie,Ie,1],[i,tr,ia,Ie,Ar,Ie,Ie,Ie,1],[t,pm,ia+" "+tr,Ie,Ar+" "+tr,Ie,mm,Ie,1],[e,pm+".{fff}",ia+" "+tr,Ie,Ar+" "+tr,Ie,mm,Ie,1]];function m(f){return(h,$,S,U,D,J)=>{let k=[],T=D>=a,z=D>=o&&D<a,ee=f(S),C=Ue(ee*e,3),se=ll(ee.getFullYear(),T?0:ee.getMonth(),z||T?1:ee.getDate()),te=Ue(se*e,3);if(z||T){let pe=z?D/o:0,oe=T?D/a:0,O=C==te?C:Ue(ll(se.getFullYear()+oe,se.getMonth()+pe,1)*e,3),ue=new Date(st(O/e)),V=ue.getFullYear(),ne=ue.getMonth();for(let K=0;O<=U;K++){let ge=ll(V+oe*K,ne+pe*K,1),G=ge-f(Ue(ge*e,3));O=Ue((+ge+G)*e,3),O<=U&&k.push(O)}}else{let pe=D>=n?n:D,oe=Ht(S)-Ht(C),O=te+oe+zs(C-te,pe);k.push(O);let ue=f(O),V=ue.getHours()+ue.getMinutes()/i+ue.getSeconds()/r,ne=D/r,K=h.axes[$]._space,ge=J/K;for(;O=Ue(O+D,e==1?0:3),!(O>U);)if(ne>1){let G=Ht(Ue(V+ne,6))%24,be=f(O).getHours()-G;be>1&&(be=-1),O-=be*r,V=(V+ne)%24;let ze=k[k.length-1];Ue((O-ze)/D,3)*ge>=.7&&k.push(O)}else k.push(O)}return k}}return[l,c,m]}const[U6,N6,O6]=j$(1),[j6,D6,R6]=j$(.001);eo(2,-53,53,[1]);function hm(e,t){return e.map(i=>i.map((r,n)=>n==0||n==8||r==null?r:t(n==1||i[8]==0?r:i[1]+r)))}function gm(e,t){return(i,r,n,o,a)=>{let s=t.find(S=>a>=S[0])||t[t.length-1],l,c,m,f,h,$;return r.map(S=>{let U=e(S),D=U.getFullYear(),J=U.getMonth(),k=U.getDate(),T=U.getHours(),z=U.getMinutes(),ee=U.getSeconds(),C=D!=l&&s[2]||J!=c&&s[3]||k!=m&&s[4]||T!=f&&s[5]||z!=h&&s[6]||ee!=$&&s[7]||s[1];return l=D,c=J,m=k,f=T,h=z,$=ee,C(U)})}}function A6(e,t){let i=yd(t);return(r,n,o,a,s)=>n.map(l=>i(e(l)))}function ll(e,t,i){return new Date(e,t,i)}function vm(e,t){return t(e)}const C6="{YYYY}-{MM}-{DD} {h}:{mm}{aa}";function _m(e,t){return(i,r,n,o)=>o==null?hd:t(e(r))}function L6(e,t){let i=e.series[t];return i.width?i.stroke(e,t):i.points.width?i.points.stroke(e,t):null}function M6(e,t){return e.series[t].fill(e,t)}const F6={show:!0,live:!0,isolate:!1,mount:s6,markers:{show:!0,width:2,stroke:L6,fill:M6,dash:"solid"},idx:null,idxs:null,values:[]};function J6(e,t){let i=e.cursor.points,r=an(),n=i.size(e,t);Ce(r,Dr,n),Ce(r,Rr,n);let o=n/-2;Ce(r,"marginLeft",o),Ce(r,"marginTop",o);let a=i.width(e,t,n);return a&&Ce(r,"borderWidth",a),r}function q6(e,t){let i=e.series[t].points;return i._fill||i._stroke}function V6(e,t){let i=e.series[t].points;return i._stroke||i._fill}function W6(e,t){return e.series[t].points.size}const ul=[0,0];function H6(e,t,i){return ul[0]=t,ul[1]=i,ul}function ra(e,t,i,r=!0){return n=>{n.button==0&&(!r||n.target==t)&&i(n)}}function cl(e,t,i,r=!0){return n=>{(!r||n.target==t)&&i(n)}}const B6={show:!0,x:!0,y:!0,lock:!1,move:H6,points:{one:!1,show:J6,size:W6,width:0,stroke:V6,fill:q6},bind:{mousedown:ra,mouseup:ra,click:ra,dblclick:ra,mousemove:cl,mouseleave:cl,mouseenter:cl},drag:{setScale:!0,x:!0,y:!1,dist:0,uni:null,click:(e,t)=>{t.stopPropagation(),t.stopImmediatePropagation()},_x:!1,_y:!1},focus:{dist:(e,t,i,r,n)=>r-n,prox:-1,bias:0},hover:{skip:[void 0],prox:null,bias:0},left:-10,top:-10,idx:null,dataIdx:null,idxs:null,event:null},D$={show:!0,stroke:"rgba(0,0,0,0.07)",width:2},wd=rt({},D$,{filter:k$}),R$=rt({},wd,{size:10}),A$=rt({},D$,{show:!1}),Sd='12px system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"',C$="bold "+Sd,L$=1.5,bm={show:!0,scale:"x",stroke:pd,space:50,gap:5,alignTo:1,size:50,labelGap:0,labelSize:30,labelFont:C$,side:2,grid:wd,ticks:R$,border:A$,font:Sd,lineGap:L$,rotate:0},G6="Value",K6="Time",$m={show:!0,scale:"x",auto:!1,sorted:1,min:Ee,max:-Ee,idxs:[]};function Y6(e,t,i,r,n){return t.map(o=>o==null?"":bd(o))}function X6(e,t,i,r,n,o,a){let s=[],l=ni.get(n)||0;i=a?i:Ue(zs(i,n),l);for(let c=i;c<=r;c=Ue(c+n,l))s.push(Object.is(c,-0)?0:c);return s}function Ul(e,t,i,r,n,o,a){const s=[],l=e.scales[e.axes[t].scale].log,c=l==10?Rn:b$,m=Ht(c(i));n=fr(l,m),l==10&&(n=_i[$n(n,_i)]);let f=i,h=n*l;l==10&&(h=_i[$n(h,_i)]);do s.push(f),f=f+n,l==10&&!ni.has(f)&&(f=Ue(f,ni.get(n))),f>=h&&(n=f,h=n*l,l==10&&(h=_i[$n(h,_i)]));while(f<=r);return s}function Q6(e,t,i,r,n,o,a){let l=e.scales[e.axes[t].scale].asinh,c=r>l?Ul(e,t,Ut(l,i),r,n):[l],m=r>=0&&i<=0?[0]:[];return(i<-l?Ul(e,t,Ut(l,-r),-i,n):[l]).reverse().map(h=>-h).concat(m,c)}const M$=/./,eZ=/[12357]/,tZ=/[125]/,ym=/1/,Nl=(e,t,i,r)=>e.map((n,o)=>t==4&&n==0||o%r==0&&i.test(n.toExponential()[n<0?1:0])?n:null);function nZ(e,t,i,r,n){let o=e.axes[i],a=o.scale,s=e.scales[a],l=e.valToPos,c=o._space,m=l(10,a),f=l(9,a)-m>=c?M$:l(7,a)-m>=c?eZ:l(5,a)-m>=c?tZ:ym;if(f==ym){let h=lt(l(1,a)-m);if(h<c)return Nl(t.slice().reverse(),s.distr,f,sn(c/h)).reverse()}return Nl(t,s.distr,f,1)}function iZ(e,t,i,r,n){let o=e.axes[i],a=o.scale,s=o._space,l=e.valToPos,c=lt(l(1,a)-l(2,a));return c<s?Nl(t.slice().reverse(),3,M$,sn(s/c)).reverse():t}function rZ(e,t,i,r){return r==null?hd:t==null?"":bd(t)}const km={show:!0,scale:"y",stroke:pd,space:30,gap:5,alignTo:1,size:50,labelGap:0,labelSize:30,labelFont:C$,side:3,grid:wd,ticks:R$,border:A$,font:Sd,lineGap:L$,rotate:0};function oZ(e,t){let i=3+(e||1)*2;return Ue(i*t,3)}function aZ(e,t){let{scale:i,idxs:r}=e.series[0],n=e._data[0],o=e.valToPos(n[r[0]],i,!0),a=e.valToPos(n[r[1]],i,!0),s=lt(a-o),l=e.series[t],c=s/(l.points.space*we);return r[1]-r[0]<=c}const wm={scale:null,auto:!0,sorted:0,min:Ee,max:-Ee},F$=(e,t,i,r,n)=>n,Sm={show:!0,auto:!0,sorted:0,gaps:F$,alpha:1,facets:[rt({},wm,{scale:"x"}),rt({},wm,{scale:"y"})]},Im={scale:"y",auto:!0,sorted:0,show:!0,spanGaps:!1,gaps:F$,alpha:1,points:{show:aZ,filter:null},values:null,min:Ee,max:-Ee,idxs:[],path:null,clip:null};function sZ(e,t,i,r,n){return i/10}const J$={time:UT,auto:!0,distr:1,log:10,asinh:1,min:null,max:null,dir:1,ori:0},lZ=rt({},J$,{time:!1,ori:1}),xm={};function q$(e,t){let i=xm[e];return i||(i={key:e,plots:[],sub(r){i.plots.push(r)},unsub(r){i.plots=i.plots.filter(n=>n!=r)},pub(r,n,o,a,s,l,c){for(let m=0;m<i.plots.length;m++)i.plots[m]!=n&&i.plots[m].pub(r,n,o,a,s,l,c)}},e!=null&&(xm[e]=i)),i}const hr=1,Ol=2;function Ni(e,t,i){const r=e.mode,n=e.series[t],o=r==2?e._data[t]:e._data,a=e.scales,s=e.bbox;let l=o[0],c=r==2?o[1]:o[t],m=r==2?a[n.facets[0].scale]:a[e.series[0].scale],f=r==2?a[n.facets[1].scale]:a[n.scale],h=s.left,$=s.top,S=s.width,U=s.height,D=e.valToPosH,J=e.valToPosV;return m.ori==0?i(n,l,c,m,f,D,J,h,$,S,U,Ps,$r,Us,W$,B$):i(n,l,c,m,f,J,D,$,h,U,S,Es,yr,zd,H$,G$)}function Id(e,t){let i=0,r=0,n=ye(e.bands,$d);for(let o=0;o<n.length;o++){let a=n[o];a.series[0]==t?i=a.dir:a.series[1]==t&&(a.dir==1?r|=1:r|=2)}return[i,r==1?-1:r==2?1:r==3?2:0]}function uZ(e,t,i,r,n){let o=e.mode,a=e.series[t],s=o==2?a.facets[1].scale:a.scale,l=e.scales[s];return n==-1?l.min:n==1?l.max:l.distr==3?l.dir==1?l.min:l.max:0}function An(e,t,i,r,n,o){return Ni(e,t,(a,s,l,c,m,f,h,$,S,U,D)=>{let J=a.pxRound;const k=c.dir*(c.ori==0?1:-1),T=c.ori==0?$r:yr;let z,ee;k==1?(z=i,ee=r):(z=r,ee=i);let C=J(f(s[z],c,U,$)),se=J(h(l[z],m,D,S)),te=J(f(s[ee],c,U,$)),pe=J(h(o==1?m.max:m.min,m,D,S)),oe=new Path2D(n);return T(oe,te,pe),T(oe,C,pe),T(oe,C,se),oe})}function Zs(e,t,i,r,n,o){let a=null;if(e.length>0){a=new Path2D;const s=t==0?Us:zd;let l=i;for(let f=0;f<e.length;f++){let h=e[f];if(h[1]>h[0]){let $=h[0]-l;$>0&&s(a,l,r,$,r+o),l=h[1]}}let c=i+n-l,m=10;c>0&&s(a,l,r-m/2,c,r+o+m)}return a}function cZ(e,t,i){let r=e[e.length-1];r&&r[0]==t?r[1]=i:e.push([t,i])}function xd(e,t,i,r,n,o,a){let s=[],l=e.length;for(let c=n==1?i:r;c>=i&&c<=r;c+=n)if(t[c]===null){let f=c,h=c;if(n==1)for(;++c<=r&&t[c]===null;)h=c;else for(;--c>=i&&t[c]===null;)h=c;let $=o(e[f]),S=h==f?$:o(e[h]),U=f-n;$=a<=0&&U>=0&&U<l?o(e[U]):$;let J=h+n;S=a>=0&&J>=0&&J<l?o(e[J]):S,S>=$&&s.push([$,S])}return s}function zm(e){return e==0?y$:e==1?st:t=>vi(t,e)}function V$(e){let t=e==0?Ps:Es,i=e==0?(n,o,a,s,l,c)=>{n.arcTo(o,a,s,l,c)}:(n,o,a,s,l,c)=>{n.arcTo(a,o,l,s,c)},r=e==0?(n,o,a,s,l)=>{n.rect(o,a,s,l)}:(n,o,a,s,l)=>{n.rect(a,o,l,s)};return(n,o,a,s,l,c=0,m=0)=>{c==0&&m==0?r(n,o,a,s,l):(c=yn(c,s/2,l/2),m=yn(m,s/2,l/2),t(n,o+c,a),i(n,o+s,a,o+s,a+l,c),i(n,o+s,a+l,o,a+l,m),i(n,o,a+l,o,a,m),i(n,o,a,o+s,a,c),n.closePath())}}const Ps=(e,t,i)=>{e.moveTo(t,i)},Es=(e,t,i)=>{e.moveTo(i,t)},$r=(e,t,i)=>{e.lineTo(t,i)},yr=(e,t,i)=>{e.lineTo(i,t)},Us=V$(0),zd=V$(1),W$=(e,t,i,r,n,o)=>{e.arc(t,i,r,n,o)},H$=(e,t,i,r,n,o)=>{e.arc(i,t,r,n,o)},B$=(e,t,i,r,n,o,a)=>{e.bezierCurveTo(t,i,r,n,o,a)},G$=(e,t,i,r,n,o,a)=>{e.bezierCurveTo(i,t,n,r,a,o)};function K$(e){return(t,i,r,n,o)=>Ni(t,i,(a,s,l,c,m,f,h,$,S,U,D)=>{let{pxRound:J,points:k}=a,T,z;c.ori==0?(T=Ps,z=W$):(T=Es,z=H$);const ee=Ue(k.width*we,3);let C=(k.size-k.width)/2*we,se=Ue(C*2,3),te=new Path2D,pe=new Path2D,{left:oe,top:O,width:ue,height:V}=t.bbox;Us(pe,oe-se,O-se,ue+se*2,V+se*2);const ne=K=>{if(l[K]!=null){let ge=J(f(s[K],c,U,$)),G=J(h(l[K],m,D,S));T(te,ge+C,G),z(te,ge,G,C,0,la*2)}};if(o)o.forEach(ne);else for(let K=r;K<=n;K++)ne(K);return{stroke:ee>0?te:null,fill:te,clip:pe,flags:hr|Ol}})}function Y$(e){return(t,i,r,n,o,a)=>{r!=n&&(o!=r&&a!=r&&e(t,i,r),o!=n&&a!=n&&e(t,i,n),e(t,i,a))}}const dZ=Y$($r),fZ=Y$(yr);function X$(e){const t=ye(e?.alignGaps,0);return(i,r,n,o)=>Ni(i,r,(a,s,l,c,m,f,h,$,S,U,D)=>{[n,o]=Is(l,n,o);let J=a.pxRound,k=V=>J(f(V,c,U,$)),T=V=>J(h(V,m,D,S)),z,ee;c.ori==0?(z=$r,ee=dZ):(z=yr,ee=fZ);const C=c.dir*(c.ori==0?1:-1),se={stroke:new Path2D,fill:null,clip:null,band:null,gaps:null,flags:hr},te=se.stroke;let pe=!1;if(o-n>=U*4){let V=W=>i.posToVal(W,c.key,!0),ne=null,K=null,ge,G,Nt,Je=k(s[C==1?n:o]),be=k(s[n]),ze=k(s[o]),de=V(C==1?be+1:ze-1);for(let W=C==1?n:o;W>=n&&W<=o;W+=C){let ot=s[W],qe=(C==1?ot<de:ot>de)?Je:k(ot),Se=l[W];qe==Je?Se!=null?(G=Se,ne==null?(z(te,qe,T(G)),ge=ne=K=G):G<ne?ne=G:G>K&&(K=G)):Se===null&&(pe=!0):(ne!=null&&ee(te,Je,T(ne),T(K),T(ge),T(G)),Se!=null?(G=Se,z(te,qe,T(G)),ne=K=ge=G):(ne=K=null,Se===null&&(pe=!0)),Je=qe,de=V(Je+C))}ne!=null&&ne!=K&&Nt!=Je&&ee(te,Je,T(ne),T(K),T(ge),T(G))}else for(let V=C==1?n:o;V>=n&&V<=o;V+=C){let ne=l[V];ne===null?pe=!0:ne!=null&&z(te,k(s[V]),T(ne))}let[O,ue]=Id(i,r);if(a.fill!=null||O!=0){let V=se.fill=new Path2D(te),ne=a.fillTo(i,r,a.min,a.max,O),K=T(ne),ge=k(s[n]),G=k(s[o]);C==-1&&([G,ge]=[ge,G]),z(V,G,K),z(V,ge,K)}if(!a.spanGaps){let V=[];pe&&V.push(...xd(s,l,n,o,C,k,t)),se.gaps=V=a.gaps(i,r,n,o,V),se.clip=Zs(V,c.ori,$,S,U,D)}return ue!=0&&(se.band=ue==2?[An(i,r,n,o,te,-1),An(i,r,n,o,te,1)]:An(i,r,n,o,te,ue)),se})}function mZ(e){const t=ye(e.align,1),i=ye(e.ascDesc,!1),r=ye(e.alignGaps,0),n=ye(e.extend,!1);return(o,a,s,l)=>Ni(o,a,(c,m,f,h,$,S,U,D,J,k,T)=>{[s,l]=Is(f,s,l);let z=c.pxRound,{left:ee,width:C}=o.bbox,se=be=>z(S(be,h,k,D)),te=be=>z(U(be,$,T,J)),pe=h.ori==0?$r:yr;const oe={stroke:new Path2D,fill:null,clip:null,band:null,gaps:null,flags:hr},O=oe.stroke,ue=h.dir*(h.ori==0?1:-1);let V=te(f[ue==1?s:l]),ne=se(m[ue==1?s:l]),K=ne,ge=ne;n&&t==-1&&(ge=ee,pe(O,ge,V)),pe(O,ne,V);for(let be=ue==1?s:l;be>=s&&be<=l;be+=ue){let ze=f[be];if(ze==null)continue;let de=se(m[be]),W=te(ze);t==1?pe(O,de,V):pe(O,K,W),pe(O,de,W),V=W,K=de}let G=K;n&&t==1&&(G=ee+C,pe(O,G,V));let[Nt,Je]=Id(o,a);if(c.fill!=null||Nt!=0){let be=oe.fill=new Path2D(O),ze=c.fillTo(o,a,c.min,c.max,Nt),de=te(ze);pe(be,G,de),pe(be,ge,de)}if(!c.spanGaps){let be=[];be.push(...xd(m,f,s,l,ue,se,r));let ze=c.width*we/2,de=i||t==1?ze:-ze,W=i||t==-1?-ze:ze;be.forEach(ot=>{ot[0]+=de,ot[1]+=W}),oe.gaps=be=c.gaps(o,a,s,l,be),oe.clip=Zs(be,h.ori,D,J,k,T)}return Je!=0&&(oe.band=Je==2?[An(o,a,s,l,O,-1),An(o,a,s,l,O,1)]:An(o,a,s,l,O,Je)),oe})}function Tm(e,t,i,r,n,o,a=Ee){if(e.length>1){let s=null;for(let l=0,c=1/0;l<e.length;l++)if(t[l]!==void 0){if(s!=null){let m=lt(e[l]-e[s]);m<c&&(c=m,a=lt(i(e[l],r,n,o)-i(e[s],r,n,o)))}s=l}}return a}function pZ(e){e=e||Fr;const t=ye(e.size,[.6,Ee,1]),i=e.align||0,r=e.gap||0;let n=e.radius;n=n==null?[0,0]:typeof n=="number"?[n,0]:n;const o=he(n),a=1-t[0],s=ye(t[1],Ee),l=ye(t[2],1),c=ye(e.disp,Fr),m=ye(e.each,$=>{}),{fill:f,stroke:h}=c;return($,S,U,D)=>Ni($,S,(J,k,T,z,ee,C,se,te,pe,oe,O)=>{let ue=J.pxRound,V=i,ne=r*we,K=s*we,ge=l*we,G,Nt;z.ori==0?[G,Nt]=o($,S):[Nt,G]=o($,S);const Je=z.dir*(z.ori==0?1:-1);let be=z.ori==0?Us:zd,ze=z.ori==0?m:(L,je,at,Ri,ai,Sn,si)=>{m(L,je,at,ai,Ri,si,Sn)},de=ye($.bands,$d).find(L=>L.series[0]==S),W=de!=null?de.dir:0,ot=J.fillTo($,S,J.min,J.max,W),xt=ue(se(ot,ee,O,pe)),qe,Se,pn,Lt=oe,Be=ue(J.width*we),wn=!1,On=null,Qt=null,Jn=null,Oi=null;f!=null&&(Be==0||h!=null)&&(wn=!0,On=f.values($,S,U,D),Qt=new Map,new Set(On).forEach(L=>{L!=null&&Qt.set(L,new Path2D)}),Be>0&&(Jn=h.values($,S,U,D),Oi=new Map,new Set(Jn).forEach(L=>{L!=null&&Oi.set(L,new Path2D)})));let{x0:ji,size:wr}=c;if(ji!=null&&wr!=null){V=1,k=ji.values($,S,U,D),ji.unit==2&&(k=k.map(at=>$.posToVal(te+at*oe,z.key,!0)));let L=wr.values($,S,U,D);wr.unit==2?Se=L[0]*oe:Se=C(L[0],z,oe,te)-C(0,z,oe,te),Lt=Tm(k,T,C,z,oe,te,Lt),pn=Lt-Se+ne}else Lt=Tm(k,T,C,z,oe,te,Lt),pn=Lt*a+ne,Se=Lt-pn;pn<1&&(pn=0),Be>=Se/2&&(Be=0),pn<5&&(ue=y$);let Ro=pn>0,ri=Lt-pn-(Ro?Be:0);Se=ue(El(ri,ge,K)),qe=(V==0?Se/2:V==Je?0:Se)-V*Je*((V==0?ne/2:0)+(Ro?Be/2:0));const zt={stroke:null,fill:null,clip:null,band:null,gaps:null,flags:0},Di=wn?null:new Path2D;let jn=null;if(de!=null)jn=$.data[de.series[1]];else{let{y0:L,y1:je}=c;L!=null&&je!=null&&(T=je.values($,S,U,D),jn=L.values($,S,U,D))}let oi=G*Se,le=Nt*Se;for(let L=Je==1?U:D;L>=U&&L<=D;L+=Je){let je=T[L];if(je==null)continue;if(jn!=null){let Ot=jn[L]??0;if(je-Ot==0)continue;xt=se(Ot,ee,O,pe)}let at=z.distr!=2||c!=null?k[L]:L,Ri=C(at,z,oe,te),ai=se(ye(je,ot),ee,O,pe),Sn=ue(Ri-qe),si=ue(Ut(ai,xt)),Mt=ue(yn(ai,xt)),en=si-Mt;if(je!=null){let Ot=je<0?le:oi,hn=je<0?oi:le;wn?(Be>0&&Jn[L]!=null&&be(Oi.get(Jn[L]),Sn,Mt+Ht(Be/2),Se,Ut(0,en-Be),Ot,hn),On[L]!=null&&be(Qt.get(On[L]),Sn,Mt+Ht(Be/2),Se,Ut(0,en-Be),Ot,hn)):be(Di,Sn,Mt+Ht(Be/2),Se,Ut(0,en-Be),Ot,hn),ze($,S,L,Sn-Be/2,Mt,Se+Be,en)}}return Be>0?zt.stroke=wn?Oi:Di:wn||(zt._fill=J.width==0?J._fill:J._stroke??J._fill,zt.width=0),zt.fill=wn?Qt:Di,zt})}function hZ(e,t){const i=ye(t?.alignGaps,0);return(r,n,o,a)=>Ni(r,n,(s,l,c,m,f,h,$,S,U,D,J)=>{[o,a]=Is(c,o,a);let k=s.pxRound,T=G=>k(h(G,m,D,S)),z=G=>k($(G,f,J,U)),ee,C,se;m.ori==0?(ee=Ps,se=$r,C=B$):(ee=Es,se=yr,C=G$);const te=m.dir*(m.ori==0?1:-1);let pe=T(l[te==1?o:a]),oe=pe,O=[],ue=[];for(let G=te==1?o:a;G>=o&&G<=a;G+=te)if(c[G]!=null){let Je=l[G],be=T(Je);O.push(oe=be),ue.push(z(c[G]))}const V={stroke:e(O,ue,ee,se,C,k),fill:null,clip:null,band:null,gaps:null,flags:hr},ne=V.stroke;let[K,ge]=Id(r,n);if(s.fill!=null||K!=0){let G=V.fill=new Path2D(ne),Nt=s.fillTo(r,n,s.min,s.max,K),Je=z(Nt);se(G,oe,Je),se(G,pe,Je)}if(!s.spanGaps){let G=[];G.push(...xd(l,c,o,a,te,T,i)),V.gaps=G=s.gaps(r,n,o,a,G),V.clip=Zs(G,m.ori,S,U,D,J)}return ge!=0&&(V.band=ge==2?[An(r,n,o,a,ne,-1),An(r,n,o,a,ne,1)]:An(r,n,o,a,ne,ge)),V})}function gZ(e){return hZ(vZ,e)}function vZ(e,t,i,r,n,o){const a=e.length;if(a<2)return null;const s=new Path2D;if(i(s,e[0],t[0]),a==2)r(s,e[1],t[1]);else{let l=Array(a),c=Array(a-1),m=Array(a-1),f=Array(a-1);for(let h=0;h<a-1;h++)m[h]=t[h+1]-t[h],f[h]=e[h+1]-e[h],c[h]=m[h]/f[h];l[0]=c[0];for(let h=1;h<a-1;h++)c[h]===0||c[h-1]===0||c[h-1]>0!=c[h]>0?l[h]=0:(l[h]=3*(f[h-1]+f[h])/((2*f[h]+f[h-1])/c[h-1]+(f[h]+2*f[h-1])/c[h]),isFinite(l[h])||(l[h]=0));l[a-1]=c[a-2];for(let h=0;h<a-1;h++)n(s,e[h]+f[h]/3,t[h]+l[h]*f[h]/3,e[h+1]-f[h]/3,t[h+1]-l[h+1]*f[h]/3,e[h+1],t[h+1])}return s}const jl=new Set;function Zm(){for(let e of jl)e.syncRect(!0)}br&&(wi(YT,ir,Zm),wi(XT,ir,Zm,!0),wi(wa,ir,()=>{St.pxRatio=we}));const _Z=X$(),bZ=K$();function Pm(e,t,i,r){return(r?[e[0],e[1]].concat(e.slice(2)):[e[0]].concat(e.slice(1))).map((o,a)=>Dl(o,a,t,i))}function $Z(e,t){return e.map((i,r)=>r==0?{}:rt({},t,i))}function Dl(e,t,i,r){return rt({},t==0?i:r,e)}function Q$(e,t,i){return t==null?mr:[t,i]}const yZ=Q$;function kZ(e,t,i){return t==null?mr:Sa(t,i,_d,!0)}function ey(e,t,i,r){return t==null?mr:xs(t,i,e.scales[r].log,!1)}const wZ=ey;function ty(e,t,i,r){return t==null?mr:vd(t,i,e.scales[r].log,!1)}const SZ=ty;function IZ(e,t,i,r,n){let o=Ut(lm(e),lm(t)),a=t-e,s=$n(n/r*a,i);do{let l=i[s],c=r*l/a;if(c>=n&&o+(l<5?ni.get(l):0)<=17)return[l,c]}while(++s<i.length);return[0,0]}function Em(e){let t,i;return e=e.replace(/(\d+)px/,(r,n)=>(t=st((i=+n)*we))+"px"),[e,t,i]}function xZ(e){e.show&&[e.font,e.labelFont].forEach(t=>{let i=Ue(t[2]*we,1);t[0]=t[0].replace(/[0-9.]+px/,i+"px"),t[1]=i})}function St(e,t,i){const r={mode:ye(e.mode,1)},n=r.mode;function o(u,d,p,g){let _=d.valToPct(u);return g+p*(d.dir==-1?1-_:_)}function a(u,d,p,g){let _=d.valToPct(u);return g+p*(d.dir==-1?_:1-_)}function s(u,d,p,g){return d.ori==0?o(u,d,p,g):a(u,d,p,g)}r.valToPosH=o,r.valToPosV=a;let l=!1;r.status=0;const c=r.root=an(NT);if(e.id!=null&&(c.id=e.id),Vt(c,e.class),e.title){let u=an(DT,c);u.textContent=e.title}const m=bn("canvas"),f=r.ctx=m.getContext("2d"),h=an(RT,c);wi("click",h,u=>{u.target===S&&(De!=Bi||Ve!=Gi)&&vt.click(r,u)},!0);const $=r.under=an(AT,h);h.appendChild(m);const S=r.over=an(CT,h);e=pr(e);const U=+ye(e.pxAlign,1),D=zm(U);(e.plugins||[]).forEach(u=>{u.opts&&(e=u.opts(r,e)||e)});const J=e.ms||.001,k=r.series=n==1?Pm(e.series||[],$m,Im,!1):$Z(e.series||[null],Sm),T=r.axes=Pm(e.axes||[],bm,km,!0),z=r.scales={},ee=r.bands=e.bands||[];ee.forEach(u=>{u.fill=he(u.fill||null),u.dir=ye(u.dir,-1)});const C=n==2?k[1].facets[0].scale:k[0].scale,se={axes:Ty,series:wy},te=(e.drawOrder||["axes","series"]).map(u=>se[u]);function pe(u){const d=u.distr==3?p=>Rn(p>0?p:u.clamp(r,p,u.min,u.max,u.key)):u.distr==4?p=>sl(p,u.asinh):u.distr==100?p=>u.fwd(p):p=>p;return p=>{let g=d(p),{_min:_,_max:y}=u,x=y-_;return(g-_)/x}}function oe(u){let d=z[u];if(d==null){let p=(e.scales||Fr)[u]||Fr;if(p.from!=null){oe(p.from);let g=rt({},z[p.from],p,{key:u});g.valToPct=pe(g),z[u]=g}else{d=z[u]=rt({},u==C?J$:lZ,p),d.key=u;let g=d.time,_=d.range,y=Gn(_);if((u!=C||n==2&&!g)&&(y&&(_[0]==null||_[1]==null)&&(_={min:_[0]==null?om:{mode:1,hard:_[0],soft:_[0]},max:_[1]==null?om:{mode:1,hard:_[1],soft:_[1]}},y=!1),!y&&Ts(_))){let x=_;_=(Z,N,R)=>N==null?mr:Sa(N,R,x)}d.range=he(_||(g?yZ:u==C?d.distr==3?wZ:d.distr==4?SZ:Q$:d.distr==3?ey:d.distr==4?ty:kZ)),d.auto=he(y?!1:d.auto),d.clamp=he(d.clamp||sZ),d._min=d._max=null,d.valToPct=pe(d)}}}oe("x"),oe("y"),n==1&&k.forEach(u=>{oe(u.scale)}),T.forEach(u=>{oe(u.scale)});for(let u in e.scales)oe(u);const O=z[C],ue=O.distr;let V,ne;O.ori==0?(Vt(c,OT),V=o,ne=a):(Vt(c,jT),V=a,ne=o);const K={};for(let u in z){let d=z[u];(d.min!=null||d.max!=null)&&(K[u]={min:d.min,max:d.max},d.min=d.max=null)}const ge=e.tzDate||(u=>new Date(st(u/J))),G=e.fmtDate||yd,Nt=J==1?O6(ge):R6(ge),Je=gm(ge,hm(J==1?N6:D6,G)),be=_m(ge,vm(C6,G)),ze=[],de=r.legend=rt({},F6,e.legend),W=r.cursor=rt({},B6,{drag:{y:n==2}},e.cursor),ot=de.show,xt=W.show,qe=de.markers;de.idxs=ze,qe.width=he(qe.width),qe.dash=he(qe.dash),qe.stroke=he(qe.stroke),qe.fill=he(qe.fill);let Se,pn,Lt,Be=[],wn=[],On,Qt=!1,Jn={};if(de.live){const u=k[1]?k[1].values:null;Qt=u!=null,On=Qt?u(r,1,0):{_:0};for(let d in On)Jn[d]=hd}if(ot)if(Se=bn("table",VT,c),Lt=bn("tbody",null,Se),de.mount(r,Se),Qt){pn=bn("thead",null,Se,Lt);let u=bn("tr",null,pn);bn("th",null,u);for(var Oi in On)bn("th",Wf,u).textContent=Oi}else Vt(Se,HT),de.live&&Vt(Se,WT);const ji={show:!0},wr={show:!1};function Ro(u,d){if(d==0&&(Qt||!de.live||n==2))return mr;let p=[],g=bn("tr",BT,Lt,Lt.childNodes[d]);Vt(g,u.class),u.show||Vt(g,bi);let _=bn("th",null,g);if(qe.show){let Z=an(GT,_);if(d>0){let N=qe.width(r,d);N&&(Z.style.border=N+"px "+qe.dash(r,d)+" "+qe.stroke(r,d)),Z.style.background=qe.fill(r,d)}}let y=an(Wf,_);u.label instanceof HTMLElement?y.appendChild(u.label):y.textContent=u.label,d>0&&(qe.show||(y.style.color=u.width>0?qe.stroke(r,d):qe.fill(r,d)),zt("click",_,Z=>{if(W._lock)return;ui(Z);let N=k.indexOf(u);if((Z.ctrlKey||Z.metaKey)!=de.isolate){let R=k.some((A,F)=>F>0&&F!=N&&A.show);k.forEach((A,F)=>{F>0&&xn(F,R?F==N?ji:wr:ji,!0,it.setSeries)})}else xn(N,{show:!u.show},!0,it.setSeries)},!1),Ci&&zt(Kf,_,Z=>{W._lock||(ui(Z),xn(k.indexOf(u),Yi,!0,it.setSeries))},!1));for(var x in On){let Z=bn("td",KT,g);Z.textContent="--",p.push(Z)}return[g,p]}const ri=new Map;function zt(u,d,p,g=!0){const _=ri.get(d)||{},y=W.bind[u](r,d,p,g);y&&(wi(u,d,_[u]=y),ri.set(d,_))}function Di(u,d,p){const g=ri.get(d)||{};for(let _ in g)(u==null||_==u)&&(Pl(_,d,g[_]),delete g[_]);u==null&&ri.delete(d)}let jn=0,oi=0,le=0,L=0,je=0,at=0,Ri=je,ai=at,Sn=le,si=L,Mt=0,en=0,Ot=0,hn=0;r.bbox={};let Ns=!1,Ao=!1,Ai=!1,li=!1,Co=!1,tn=!1;function Os(u,d,p){(p||u!=r.width||d!=r.height)&&Pd(u,d),qi(!1),Ai=!0,Ao=!0,Vi()}function Pd(u,d){r.width=jn=le=u,r.height=oi=L=d,je=at=0,gy(),vy();let p=r.bbox;Mt=p.left=vi(je*we,.5),en=p.top=vi(at*we,.5),Ot=p.width=vi(le*we,.5),hn=p.height=vi(L*we,.5)}const my=3;function py(){let u=!1,d=0;for(;!u;){d++;let p=xy(d),g=zy(d);u=d==my||p&&g,u||(Pd(r.width,r.height),Ao=!0)}}function hy({width:u,height:d}){Os(u,d)}r.setSize=hy;function gy(){let u=!1,d=!1,p=!1,g=!1;T.forEach((_,y)=>{if(_.show&&_._show){let{side:x,_size:Z}=_,N=x%2,R=_.label!=null?_.labelSize:0,A=Z+R;A>0&&(N?(le-=A,x==3?(je+=A,g=!0):p=!0):(L-=A,x==0?(at+=A,u=!0):d=!0))}}),ci[0]=u,ci[1]=p,ci[2]=d,ci[3]=g,le-=qn[1]+qn[3],je+=qn[3],L-=qn[2]+qn[0],at+=qn[0]}function vy(){let u=je+le,d=at+L,p=je,g=at;function _(y,x){switch(y){case 1:return u+=x,u-x;case 2:return d+=x,d-x;case 3:return p-=x,p+x;case 0:return g-=x,g+x}}T.forEach((y,x)=>{if(y.show&&y._show){let Z=y.side;y._pos=_(Z,y._size),y.label!=null&&(y._lpos=_(Z,y.labelSize))}})}if(W.dataIdx==null){let u=W.hover,d=u.skip=new Set(u.skip??[]);d.add(void 0);let p=u.prox=he(u.prox),g=u.bias??=0;W.dataIdx=(_,y,x,Z)=>{if(y==0)return x;let N=x,R=p(_,y,x,Z)??Ee,A=R>=0&&R<Ee,F=O.ori==0?le:L,ie=W.left,ke=t[0],$e=t[y];if(d.has($e[x])){N=null;let fe=null,X=null,H;if(g==0||g==-1)for(H=x;fe==null&&H-- >0;)d.has($e[H])||(fe=H);if(g==0||g==1)for(H=x;X==null&&H++<$e.length;)d.has($e[H])||(X=H);if(fe!=null||X!=null)if(A){let Ae=fe==null?-1/0:V(ke[fe],O,F,0),Qe=X==null?1/0:V(ke[X],O,F,0),ht=ie-Ae,Te=Qe-ie;ht<=Te?ht<=R&&(N=fe):Te<=R&&(N=X)}else N=X==null?fe:fe==null?X:x-fe<=X-x?fe:X}else A&<(ie-V(ke[x],O,F,0))>R&&(N=null);return N}}const ui=u=>{W.event=u};W.idxs=ze,W._lock=!1;let kt=W.points;kt.show=he(kt.show),kt.size=he(kt.size),kt.stroke=he(kt.stroke),kt.width=he(kt.width),kt.fill=he(kt.fill);const In=r.focus=rt({},e.focus||{alpha:.3},W.focus),Ci=In.prox>=0,Li=Ci&&kt.one;let nn=[],Mi=[],Fi=[];function Ed(u,d){let p=kt.show(r,d);if(p instanceof HTMLElement)return Vt(p,qT),Vt(p,u.class),Pn(p,-10,-10,le,L),S.insertBefore(p,nn[d]),p}function Ud(u,d){if(n==1||d>0){let p=n==1&&z[u.scale].time,g=u.value;u.value=p?dm(g)?_m(ge,vm(g,G)):g||be:g||rZ,u.label=u.label||(p?K6:G6)}if(Li||d>0){u.width=u.width==null?1:u.width,u.paths=u.paths||_Z||l6,u.fillTo=he(u.fillTo||uZ),u.pxAlign=+ye(u.pxAlign,U),u.pxRound=zm(u.pxAlign),u.stroke=he(u.stroke||null),u.fill=he(u.fill||null),u._stroke=u._fill=u._paths=u._focus=null;let p=oZ(Ut(1,u.width),1),g=u.points=rt({},{size:p,width:Ut(1,p*.2),stroke:u.stroke,space:p*2,paths:bZ,_stroke:null,_fill:null},u.points);g.show=he(g.show),g.filter=he(g.filter),g.fill=he(g.fill),g.stroke=he(g.stroke),g.paths=he(g.paths),g.pxAlign=u.pxAlign}if(ot){let p=Ro(u,d);Be.splice(d,0,p[0]),wn.splice(d,0,p[1]),de.values.push(null)}if(xt){ze.splice(d,0,null);let p=null;Li?d==0&&(p=Ed(u,d)):d>0&&(p=Ed(u,d)),nn.splice(d,0,p),Mi.splice(d,0,0),Fi.splice(d,0,0)}pt("addSeries",d)}function _y(u,d){d=d??k.length,u=n==1?Dl(u,d,$m,Im):Dl(u,d,{},Sm),k.splice(d,0,u),Ud(k[d],d)}r.addSeries=_y;function by(u){if(k.splice(u,1),ot){de.values.splice(u,1),wn.splice(u,1);let d=Be.splice(u,1)[0];Di(null,d.firstChild),d.remove()}xt&&(ze.splice(u,1),nn.splice(u,1)[0].remove(),Mi.splice(u,1),Fi.splice(u,1)),pt("delSeries",u)}r.delSeries=by;const ci=[!1,!1,!1,!1];function $y(u,d){if(u._show=u.show,u.show){let p=u.side%2,g=z[u.scale];g==null&&(u.scale=p?k[1].scale:C,g=z[u.scale]);let _=g.time;u.size=he(u.size),u.space=he(u.space),u.rotate=he(u.rotate),Gn(u.incrs)&&u.incrs.forEach(x=>{!ni.has(x)&&ni.set(x,S$(x))}),u.incrs=he(u.incrs||(g.distr==2?P6:_?J==1?U6:j6:_i)),u.splits=he(u.splits||(_&&g.distr==1?Nt:g.distr==3?Ul:g.distr==4?Q6:X6)),u.stroke=he(u.stroke),u.grid.stroke=he(u.grid.stroke),u.ticks.stroke=he(u.ticks.stroke),u.border.stroke=he(u.border.stroke);let y=u.values;u.values=Gn(y)&&!Gn(y[0])?he(y):_?Gn(y)?gm(ge,hm(y,G)):dm(y)?A6(ge,y):y||Je:y||Y6,u.filter=he(u.filter||(g.distr>=3&&g.log==10?nZ:g.distr==3&&g.log==2?iZ:k$)),u.font=Em(u.font),u.labelFont=Em(u.labelFont),u._size=u.size(r,null,d,0),u._space=u._rotate=u._incrs=u._found=u._splits=u._values=null,u._size>0&&(ci[d]=!0,u._el=an(LT,h))}}function Sr(u,d,p,g){let[_,y,x,Z]=p,N=d%2,R=0;return N==0&&(Z||y)&&(R=d==0&&!_||d==2&&!x?st(bm.size/3):0),N==1&&(_||x)&&(R=d==1&&!y||d==3&&!Z?st(km.size/2):0),R}const Nd=r.padding=(e.padding||[Sr,Sr,Sr,Sr]).map(u=>he(ye(u,Sr))),qn=r._padding=Nd.map((u,d)=>u(r,d,ci,0));let gt,ct=null,dt=null;const Lo=n==1?k[0].idxs:null;let gn=null,Ir=!1;function Od(u,d){if(t=u??[],r.data=r._data=t,n==2){gt=0;for(let p=1;p<k.length;p++)gt+=t[p][0].length}else{t.length==0&&(r.data=r._data=t=[[]]),gn=t[0],gt=gn.length;let p=t;if(ue==2){p=t.slice();let g=p[0]=Array(gt);for(let _=0;_<gt;_++)g[_]=_}r._data=t=p}if(qi(!0),pt("setData"),ue==2&&(Ai=!0),d!==!1){let p=O;p.auto(r,Ir)?js():Wn(C,p.min,p.max),li=li||W.left>=0,tn=!0,Vi()}}r.setData=Od;function js(){Ir=!0;let u,d;n==1&&(gt>0?(ct=Lo[0]=0,dt=Lo[1]=gt-1,u=t[0][ct],d=t[0][dt],ue==2?(u=ct,d=dt):u==d&&(ue==3?[u,d]=xs(u,u,O.log,!1):ue==4?[u,d]=vd(u,u,O.log,!1):O.time?d=u+st(86400/J):[u,d]=Sa(u,d,_d,!0))):(ct=Lo[0]=u=null,dt=Lo[1]=d=null)),Wn(C,u,d)}let Mo,Ji,Ds,Rs,As,Cs,Ls,Ms,Fs,jt;function jd(u,d,p,g,_,y){u??=Bf,p??=$d,g??="butt",_??=Bf,y??="round",u!=Mo&&(f.strokeStyle=Mo=u),_!=Ji&&(f.fillStyle=Ji=_),d!=Ds&&(f.lineWidth=Ds=d),y!=As&&(f.lineJoin=As=y),g!=Cs&&(f.lineCap=Cs=g),p!=Rs&&f.setLineDash(Rs=p)}function Dd(u,d,p,g){d!=Ji&&(f.fillStyle=Ji=d),u!=Ls&&(f.font=Ls=u),p!=Ms&&(f.textAlign=Ms=p),g!=Fs&&(f.textBaseline=Fs=g)}function Js(u,d,p,g,_=0){if(g.length>0&&u.auto(r,Ir)&&(d==null||d.min==null)){let y=ye(ct,0),x=ye(dt,g.length-1),Z=p.min==null?n6(g,y,x,_,u.distr==3):[p.min,p.max];u.min=yn(u.min,p.min=Z[0]),u.max=Ut(u.max,p.max=Z[1])}}const Rd={min:null,max:null};function yy(){for(let g in z){let _=z[g];K[g]==null&&(_.min==null||K[C]!=null&&_.auto(r,Ir))&&(K[g]=Rd)}for(let g in z){let _=z[g];K[g]==null&&_.from!=null&&K[_.from]!=null&&(K[g]=Rd)}K[C]!=null&&qi(!0);let u={};for(let g in K){let _=K[g];if(_!=null){let y=u[g]=pr(z[g],d6);if(_.min!=null)rt(y,_);else if(g!=C||n==2)if(gt==0&&y.from==null){let x=y.range(r,null,null,g);y.min=x[0],y.max=x[1]}else y.min=Ee,y.max=-Ee}}if(gt>0){k.forEach((g,_)=>{if(n==1){let y=g.scale,x=K[y];if(x==null)return;let Z=u[y];if(_==0){let N=Z.range(r,Z.min,Z.max,y);Z.min=N[0],Z.max=N[1],ct=$n(Z.min,t[0]),dt=$n(Z.max,t[0]),dt-ct>1&&(t[0][ct]<Z.min&&ct++,t[0][dt]>Z.max&&dt--),g.min=gn[ct],g.max=gn[dt]}else g.show&&g.auto&&Js(Z,x,g,t[_],g.sorted);g.idxs[0]=ct,g.idxs[1]=dt}else if(_>0&&g.show&&g.auto){let[y,x]=g.facets,Z=y.scale,N=x.scale,[R,A]=t[_],F=u[Z],ie=u[N];F!=null&&Js(F,K[Z],y,R,y.sorted),ie!=null&&Js(ie,K[N],x,A,x.sorted),g.min=x.min,g.max=x.max}});for(let g in u){let _=u[g],y=K[g];if(_.from==null&&(y==null||y.min==null)){let x=_.range(r,_.min==Ee?null:_.min,_.max==-Ee?null:_.max,g);_.min=x[0],_.max=x[1]}}}for(let g in u){let _=u[g];if(_.from!=null){let y=u[_.from];if(y.min==null)_.min=_.max=null;else{let x=_.range(r,y.min,y.max,g);_.min=x[0],_.max=x[1]}}}let d={},p=!1;for(let g in u){let _=u[g],y=z[g];if(y.min!=_.min||y.max!=_.max){y.min=_.min,y.max=_.max;let x=y.distr;y._min=x==3?Rn(y.min):x==4?sl(y.min,y.asinh):x==100?y.fwd(y.min):y.min,y._max=x==3?Rn(y.max):x==4?sl(y.max,y.asinh):x==100?y.fwd(y.max):y.max,d[g]=p=!0}}if(p){k.forEach((g,_)=>{n==2?_>0&&d.y&&(g._paths=null):d[g.scale]&&(g._paths=null)});for(let g in d)Ai=!0,pt("setScale",g);xt&&W.left>=0&&(li=tn=!0)}for(let g in K)K[g]=null}function ky(u){let d=El(ct-1,0,gt-1),p=El(dt+1,0,gt-1);for(;u[d]==null&&d>0;)d--;for(;u[p]==null&&p<gt-1;)p++;return[d,p]}function wy(){if(gt>0){let u=k.some(d=>d._focus)&&jt!=In.alpha;u&&(f.globalAlpha=jt=In.alpha),k.forEach((d,p)=>{if(p>0&&d.show&&(Ad(p,!1),Ad(p,!0),d._paths==null)){let g=jt;jt!=d.alpha&&(f.globalAlpha=jt=d.alpha);let _=n==2?[0,t[p][0].length-1]:ky(t[p]);d._paths=d.paths(r,p,_[0],_[1]),jt!=g&&(f.globalAlpha=jt=g)}}),k.forEach((d,p)=>{if(p>0&&d.show){let g=jt;jt!=d.alpha&&(f.globalAlpha=jt=d.alpha),d._paths!=null&&Cd(p,!1);{let _=d._paths!=null?d._paths.gaps:null,y=d.points.show(r,p,ct,dt,_),x=d.points.filter(r,p,y,_);(y||x)&&(d.points._paths=d.points.paths(r,p,ct,dt,x),Cd(p,!0))}jt!=g&&(f.globalAlpha=jt=g),pt("drawSeries",p)}}),u&&(f.globalAlpha=jt=1)}}function Ad(u,d){let p=d?k[u].points:k[u];p._stroke=p.stroke(r,u),p._fill=p.fill(r,u)}function Cd(u,d){let p=d?k[u].points:k[u],{stroke:g,fill:_,clip:y,flags:x,_stroke:Z=p._stroke,_fill:N=p._fill,_width:R=p.width}=p._paths;R=Ue(R*we,3);let A=null,F=R%2/2;d&&N==null&&(N=R>0?"#fff":Z);let ie=p.pxAlign==1&&F>0;if(ie&&f.translate(F,F),!d){let ke=Mt-R/2,$e=en-R/2,fe=Ot+R,X=hn+R;A=new Path2D,A.rect(ke,$e,fe,X)}d?qs(Z,R,p.dash,p.cap,N,g,_,x,y):Sy(u,Z,R,p.dash,p.cap,N,g,_,x,A,y),ie&&f.translate(-F,-F)}function Sy(u,d,p,g,_,y,x,Z,N,R,A){let F=!1;N!=0&&ee.forEach((ie,ke)=>{if(ie.series[0]==u){let $e=k[ie.series[1]],fe=t[ie.series[1]],X=($e._paths||Fr).band;Gn(X)&&(X=ie.dir==1?X[0]:X[1]);let H,Ae=null;$e.show&&X&&r6(fe,ct,dt)?(Ae=ie.fill(r,ke)||y,H=$e._paths.clip):X=null,qs(d,p,g,_,Ae,x,Z,N,R,A,H,X),F=!0}}),F||qs(d,p,g,_,y,x,Z,N,R,A)}const Ld=hr|Ol;function qs(u,d,p,g,_,y,x,Z,N,R,A,F){jd(u,d,p,g,_),(N||R||F)&&(f.save(),N&&f.clip(N),R&&f.clip(R)),F?(Z&Ld)==Ld?(f.clip(F),A&&f.clip(A),Jo(_,x),Fo(u,y,d)):Z&Ol?(Jo(_,x),f.clip(F),Fo(u,y,d)):Z&hr&&(f.save(),f.clip(F),A&&f.clip(A),Jo(_,x),f.restore(),Fo(u,y,d)):(Jo(_,x),Fo(u,y,d)),(N||R||F)&&f.restore()}function Fo(u,d,p){p>0&&(d instanceof Map?d.forEach((g,_)=>{f.strokeStyle=Mo=_,f.stroke(g)}):d!=null&&u&&f.stroke(d))}function Jo(u,d){d instanceof Map?d.forEach((p,g)=>{f.fillStyle=Ji=g,f.fill(p)}):d!=null&&u&&f.fill(d)}function Iy(u,d,p,g){let _=T[u],y;if(g<=0)y=[0,0];else{let x=_._space=_.space(r,u,d,p,g),Z=_._incrs=_.incrs(r,u,d,p,g,x);y=IZ(d,p,Z,g,x)}return _._found=y}function Vs(u,d,p,g,_,y,x,Z,N,R){let A=x%2/2;U==1&&f.translate(A,A),jd(Z,x,N,R,Z),f.beginPath();let F,ie,ke,$e,fe=_+(g==0||g==3?-y:y);p==0?(ie=_,$e=fe):(F=_,ke=fe);for(let X=0;X<u.length;X++)d[X]!=null&&(p==0?F=ke=u[X]:ie=$e=u[X],f.moveTo(F,ie),f.lineTo(ke,$e));f.stroke(),U==1&&f.translate(-A,-A)}function xy(u){let d=!0;return T.forEach((p,g)=>{if(!p.show)return;let _=z[p.scale];if(_.min==null){p._show&&(d=!1,p._show=!1,qi(!1));return}else p._show||(d=!1,p._show=!0,qi(!1));let y=p.side,x=y%2,{min:Z,max:N}=_,[R,A]=Iy(g,Z,N,x==0?le:L);if(A==0)return;let F=_.distr==2,ie=p._splits=p.splits(r,g,Z,N,R,A,F),ke=_.distr==2?ie.map(H=>gn[H]):ie,$e=_.distr==2?gn[ie[1]]-gn[ie[0]]:R,fe=p._values=p.values(r,p.filter(r,ke,g,A,$e),g,A,$e);p._rotate=y==2?p.rotate(r,fe,g,A):0;let X=p._size;p._size=sn(p.size(r,fe,g,u)),X!=null&&p._size!=X&&(d=!1)}),d}function zy(u){let d=!0;return Nd.forEach((p,g)=>{let _=p(r,g,ci,u);_!=qn[g]&&(d=!1),qn[g]=_}),d}function Ty(){for(let u=0;u<T.length;u++){let d=T[u];if(!d.show||!d._show)continue;let p=d.side,g=p%2,_,y,x=d.stroke(r,u),Z=p==0||p==3?-1:1,[N,R]=d._found;if(d.label!=null){let Zt=d.labelGap*Z,qt=st((d._lpos+Zt)*we);Dd(d.labelFont[0],x,"center",p==2?Nr:Hf),f.save(),g==1?(_=y=0,f.translate(qt,st(en+hn/2)),f.rotate((p==3?-la:la)/2)):(_=st(Mt+Ot/2),y=qt);let mi=$$(d.label)?d.label(r,u,N,R):d.label;f.fillText(mi,_,y),f.restore()}if(R==0)continue;let A=z[d.scale],F=g==0?Ot:hn,ie=g==0?Mt:en,ke=d._splits,$e=A.distr==2?ke.map(Zt=>gn[Zt]):ke,fe=A.distr==2?gn[ke[1]]-gn[ke[0]]:N,X=d.ticks,H=d.border,Ae=X.show?X.size:0,Qe=st(Ae*we),ht=st((d.alignTo==2?d._size-Ae-d.gap:d.gap)*we),Te=d._rotate*-la/180,et=D(d._pos*we),Ft=(Qe+ht)*Z,Tt=et+Ft;y=g==0?Tt:0,_=g==1?Tt:0;let rn=d.font[0],vn=d.align==1?er:d.align==2?rl:Te>0?er:Te<0?rl:g==0?"center":p==3?rl:er,Tn=Te||g==1?"middle":p==2?Nr:Hf;Dd(rn,x,vn,Tn);let Jt=d.font[1]*d.lineGap,on=ke.map(Zt=>D(s(Zt,A,F,ie))),_n=d._values;for(let Zt=0;Zt<_n.length;Zt++){let qt=_n[Zt];if(qt!=null){g==0?_=on[Zt]:y=on[Zt],qt=""+qt;let mi=qt.indexOf(`
|
|
142
|
-
`)==-1?[qt]:qt.split(/\n/gm);for(let Pt=0;Pt<mi.length;Pt++){let af=mi[Pt];Te?(f.save(),f.translate(_,y+Pt*Jt),f.rotate(Te),f.fillText(af,0,0),f.restore()):f.fillText(af,_,y+Pt*Jt)}}}X.show&&Vs(on,X.filter(r,$e,u,R,fe),g,p,et,Qe,Ue(X.width*we,3),X.stroke(r,u),X.dash,X.cap);let Zn=d.grid;Zn.show&&Vs(on,Zn.filter(r,$e,u,R,fe),g,g==0?2:1,g==0?en:Mt,g==0?hn:Ot,Ue(Zn.width*we,3),Zn.stroke(r,u),Zn.dash,Zn.cap),H.show&&Vs([et],[1],g==0?1:0,g==0?1:2,g==1?en:Mt,g==1?hn:Ot,Ue(H.width*we,3),H.stroke(r,u),H.dash,H.cap)}pt("drawAxes")}function qi(u){k.forEach((d,p)=>{p>0&&(d._paths=null,u&&(n==1?(d.min=null,d.max=null):d.facets.forEach(g=>{g.min=null,g.max=null})))})}let qo=!1,Ws=!1,xr=[];function Zy(){Ws=!1;for(let u=0;u<xr.length;u++)pt(...xr[u]);xr.length=0}function Vi(){qo||(_6(Md),qo=!0)}function Py(u,d=!1){qo=!0,Ws=d,u(r),Md(),d&&xr.length>0&&queueMicrotask(Zy)}r.batch=Py;function Md(){if(Ns&&(yy(),Ns=!1),Ai&&(py(),Ai=!1),Ao){if(Ce($,er,je),Ce($,Nr,at),Ce($,Dr,le),Ce($,Rr,L),Ce(S,er,je),Ce(S,Nr,at),Ce(S,Dr,le),Ce(S,Rr,L),Ce(h,Dr,jn),Ce(h,Rr,oi),m.width=st(jn*we),m.height=st(oi*we),T.forEach(({_el:u,_show:d,_size:p,_pos:g,side:_})=>{if(u!=null)if(d){let y=_===3||_===0?p:0,x=_%2==1;Ce(u,x?"left":"top",g-y),Ce(u,x?"width":"height",p),Ce(u,x?"top":"left",x?at:je),Ce(u,x?"height":"width",x?L:le),Zl(u,bi)}else Vt(u,bi)}),Mo=Ji=Ds=As=Cs=Ls=Ms=Fs=Rs=null,jt=1,Zr(!0),je!=Ri||at!=ai||le!=Sn||L!=si){qi(!1);let u=le/Sn,d=L/si;if(xt&&!li&&W.left>=0){W.left*=u,W.top*=d,Wi&&Pn(Wi,st(W.left),0,le,L),Hi&&Pn(Hi,0,st(W.top),le,L);for(let p=0;p<nn.length;p++){let g=nn[p];g!=null&&(Mi[p]*=u,Fi[p]*=d,Pn(g,sn(Mi[p]),sn(Fi[p]),le,L))}}if(Re.show&&!Co&&Re.left>=0&&Re.width>0){Re.left*=u,Re.width*=u,Re.top*=d,Re.height*=d;for(let p in Xs)Ce(Ki,p,Re[p])}Ri=je,ai=at,Sn=le,si=L}pt("setSize"),Ao=!1}jn>0&&oi>0&&(f.clearRect(0,0,m.width,m.height),pt("drawClear"),te.forEach(u=>u()),pt("draw")),Re.show&&Co&&(Vo(Re),Co=!1),xt&&li&&(fi(null,!0,!1),li=!1),de.show&&de.live&&tn&&(Ks(),tn=!1),l||(l=!0,r.status=1,pt("ready")),Ir=!1,qo=!1}r.redraw=(u,d)=>{Ai=d||!1,u!==!1?Wn(C,O.min,O.max):Vi()};function Hs(u,d){let p=z[u];if(p.from==null){if(gt==0){let g=p.range(r,d.min,d.max,u);d.min=g[0],d.max=g[1]}if(d.min>d.max){let g=d.min;d.min=d.max,d.max=g}if(gt>1&&d.min!=null&&d.max!=null&&d.max-d.min<1e-16)return;u==C&&p.distr==2&>>0&&(d.min=$n(d.min,t[0]),d.max=$n(d.max,t[0]),d.min==d.max&&d.max++),K[u]=d,Ns=!0,Vi()}}r.setScale=Hs;let Bs,Gs,Wi,Hi,Fd,Jd,Bi,Gi,qd,Vd,De,Ve,Vn=!1;const vt=W.drag;let ft=vt.x,mt=vt.y;xt&&(W.x&&(Bs=an(FT,S)),W.y&&(Gs=an(JT,S)),O.ori==0?(Wi=Bs,Hi=Gs):(Wi=Gs,Hi=Bs),De=W.left,Ve=W.top);const Re=r.select=rt({show:!0,over:!0,left:0,width:0,top:0,height:0},e.select),Ki=Re.show?an(MT,Re.over?S:$):null;function Vo(u,d){if(Re.show){for(let p in u)Re[p]=u[p],p in Xs&&Ce(Ki,p,u[p]);d!==!1&&pt("setSelect")}}r.setSelect=Vo;function Ey(u){if(k[u].show)ot&&Zl(Be[u],bi);else if(ot&&Vt(Be[u],bi),xt){let p=Li?nn[0]:nn[u];p!=null&&Pn(p,-10,-10,le,L)}}function Wn(u,d,p){Hs(u,{min:d,max:p})}function xn(u,d,p,g){d.focus!=null&&Dy(u),d.show!=null&&k.forEach((_,y)=>{y>0&&(u==y||u==null)&&(_.show=d.show,Ey(y),n==2?(Wn(_.facets[0].scale,null,null),Wn(_.facets[1].scale,null,null)):Wn(_.scale,null,null),Vi())}),p!==!1&&pt("setSeries",u,d),g&&Pr("setSeries",r,u,d)}r.setSeries=xn;function Uy(u,d){rt(ee[u],d)}function Ny(u,d){u.fill=he(u.fill||null),u.dir=ye(u.dir,-1),d=d??ee.length,ee.splice(d,0,u)}function Oy(u){u==null?ee.length=0:ee.splice(u,1)}r.addBand=Ny,r.setBand=Uy,r.delBand=Oy;function jy(u,d){k[u].alpha=d,xt&&nn[u]!=null&&(nn[u].style.opacity=d),ot&&Be[u]&&(Be[u].style.opacity=d)}let Dn,Hn,di;const Yi={focus:!0};function Dy(u){if(u!=di){let d=u==null,p=In.alpha!=1;k.forEach((g,_)=>{if(n==1||_>0){let y=d||_==0||_==u;g._focus=d?null:y,p&&jy(_,y?1:In.alpha)}}),di=u,p&&Vi()}}ot&&Ci&&zt(Yf,Se,u=>{W._lock||(ui(u),di!=null&&xn(null,Yi,!0,it.setSeries))});function zn(u,d,p){let g=z[d];p&&(u=u/we-(g.ori==1?at:je));let _=le;g.ori==1&&(_=L,u=_-u),g.dir==-1&&(u=_-u);let y=g._min,x=g._max,Z=u/_,N=y+(x-y)*Z,R=g.distr;return R==3?fr(10,N):R==4?a6(N,g.asinh):R==100?g.bwd(N):N}function Ry(u,d){let p=zn(u,C,d);return $n(p,t[0],ct,dt)}r.valToIdx=u=>$n(u,t[0]),r.posToIdx=Ry,r.posToVal=zn,r.valToPos=(u,d,p)=>z[d].ori==0?o(u,z[d],p?Ot:le,p?Mt:0):a(u,z[d],p?hn:L,p?en:0),r.setCursor=(u,d,p)=>{De=u.left,Ve=u.top,fi(null,d,p)};function Wd(u,d){Ce(Ki,er,Re.left=u),Ce(Ki,Dr,Re.width=d)}function Hd(u,d){Ce(Ki,Nr,Re.top=u),Ce(Ki,Rr,Re.height=d)}let zr=O.ori==0?Wd:Hd,Tr=O.ori==1?Wd:Hd;function Ay(){if(ot&&de.live)for(let u=n==2?1:0;u<k.length;u++){if(u==0&&Qt)continue;let d=de.values[u],p=0;for(let g in d)wn[u][p++].firstChild.nodeValue=d[g]}}function Ks(u,d){if(u!=null&&(u.idxs?u.idxs.forEach((p,g)=>{ze[g]=p}):c6(u.idx)||ze.fill(u.idx),de.idx=ze[0]),ot&&de.live){for(let p=0;p<k.length;p++)(p>0||n==1&&!Qt)&&Cy(p,ze[p]);Ay()}tn=!1,d!==!1&&pt("setLegend")}r.setLegend=Ks;function Cy(u,d){let p=k[u],g=u==0&&ue==2?gn:t[u],_;Qt?_=p.values(r,u,d)??Jn:(_=p.value(r,d==null?null:g[d],u,d),_=_==null?Jn:{_}),de.values[u]=_}function fi(u,d,p){qd=De,Vd=Ve,[De,Ve]=W.move(r,De,Ve),W.left=De,W.top=Ve,xt&&(Wi&&Pn(Wi,st(De),0,le,L),Hi&&Pn(Hi,0,st(Ve),le,L));let g,_=ct>dt;Dn=Ee,Hn=null;let y=O.ori==0?le:L,x=O.ori==1?le:L;if(De<0||gt==0||_){g=W.idx=null;for(let Z=0;Z<k.length;Z++){let N=nn[Z];N!=null&&Pn(N,-10,-10,le,L)}Ci&&xn(null,Yi,!0,u==null&&it.setSeries),de.live&&(ze.fill(g),tn=!0)}else{let Z,N,R;n==1&&(Z=O.ori==0?De:Ve,N=zn(Z,C),g=W.idx=$n(N,t[0],ct,dt),R=V(t[0][g],O,y,0));let A=-10,F=-10,ie=0,ke=0,$e=!0,fe="",X="";for(let H=n==2?1:0;H<k.length;H++){let Ae=k[H],Qe=ze[H],ht=Qe==null?null:n==1?t[H][Qe]:t[H][1][Qe],Te=W.dataIdx(r,H,g,N),et=Te==null?null:n==1?t[H][Te]:t[H][1][Te];if(tn=tn||et!=ht||Te!=Qe,ze[H]=Te,H>0&&Ae.show){let Ft=Te==null?-10:Te==g?R:V(n==1?t[0][Te]:t[H][0][Te],O,y,0),Tt=et==null?-10:ne(et,n==1?z[Ae.scale]:z[Ae.facets[1].scale],x,0);if(Ci&&et!=null){let rn=O.ori==1?De:Ve,vn=lt(In.dist(r,H,Te,Tt,rn));if(vn<Dn){let Tn=In.bias;if(Tn!=0){let Jt=zn(rn,Ae.scale),on=et>=0?1:-1,_n=Jt>=0?1:-1;_n==on&&(_n==1?Tn==1?et>=Jt:et<=Jt:Tn==1?et<=Jt:et>=Jt)&&(Dn=vn,Hn=H)}else Dn=vn,Hn=H}}if(tn||Li){let rn,vn;O.ori==0?(rn=Ft,vn=Tt):(rn=Tt,vn=Ft);let Tn,Jt,on,_n,Zn,Zt,qt=!0,mi=kt.bbox;if(mi!=null){qt=!1;let Pt=mi(r,H);on=Pt.left,_n=Pt.top,Tn=Pt.width,Jt=Pt.height}else on=rn,_n=vn,Tn=Jt=kt.size(r,H);if(Zt=kt.fill(r,H),Zn=kt.stroke(r,H),Li)H==Hn&&Dn<=In.prox&&(A=on,F=_n,ie=Tn,ke=Jt,$e=qt,fe=Zt,X=Zn);else{let Pt=nn[H];Pt!=null&&(Mi[H]=on,Fi[H]=_n,rm(Pt,Tn,Jt,qt),nm(Pt,Zt,Zn),Pn(Pt,sn(on),sn(_n),le,L))}}}}if(Li){let H=In.prox,Ae=di==null?Dn<=H:Dn>H||Hn!=di;if(tn||Ae){let Qe=nn[0];Qe!=null&&(Mi[0]=A,Fi[0]=F,rm(Qe,ie,ke,$e),nm(Qe,fe,X),Pn(Qe,sn(A),sn(F),le,L))}}}if(Re.show&&Vn)if(u!=null){let[Z,N]=it.scales,[R,A]=it.match,[F,ie]=u.cursor.sync.scales,ke=u.cursor.drag;if(ft=ke._x,mt=ke._y,ft||mt){let{left:$e,top:fe,width:X,height:H}=u.select,Ae=u.scales[F].ori,Qe=u.posToVal,ht,Te,et,Ft,Tt,rn=Z!=null&&R(Z,F),vn=N!=null&&A(N,ie);rn&&ft?(Ae==0?(ht=$e,Te=X):(ht=fe,Te=H),et=z[Z],Ft=V(Qe(ht,F),et,y,0),Tt=V(Qe(ht+Te,F),et,y,0),zr(yn(Ft,Tt),lt(Tt-Ft))):zr(0,y),vn&&mt?(Ae==1?(ht=$e,Te=X):(ht=fe,Te=H),et=z[N],Ft=ne(Qe(ht,ie),et,x,0),Tt=ne(Qe(ht+Te,ie),et,x,0),Tr(yn(Ft,Tt),lt(Tt-Ft))):Tr(0,x)}else Qs()}else{let Z=lt(qd-Fd),N=lt(Vd-Jd);if(O.ori==1){let ie=Z;Z=N,N=ie}ft=vt.x&&Z>=vt.dist,mt=vt.y&&N>=vt.dist;let R=vt.uni;R!=null?ft&&mt&&(ft=Z>=R,mt=N>=R,!ft&&!mt&&(N>Z?mt=!0:ft=!0)):vt.x&&vt.y&&(ft||mt)&&(ft=mt=!0);let A,F;ft&&(O.ori==0?(A=Bi,F=De):(A=Gi,F=Ve),zr(yn(A,F),lt(F-A)),mt||Tr(0,x)),mt&&(O.ori==1?(A=Bi,F=De):(A=Gi,F=Ve),Tr(yn(A,F),lt(F-A)),ft||zr(0,y)),!ft&&!mt&&(zr(0,0),Tr(0,0))}if(vt._x=ft,vt._y=mt,u==null){if(p){if(of!=null){let[Z,N]=it.scales;it.values[0]=Z!=null?zn(O.ori==0?De:Ve,Z):null,it.values[1]=N!=null?zn(O.ori==1?De:Ve,N):null}Pr(ol,r,De,Ve,le,L,g)}if(Ci){let Z=p&&it.setSeries,N=In.prox;di==null?Dn<=N&&xn(Hn,Yi,!0,Z):Dn>N?xn(null,Yi,!0,Z):Hn!=di&&xn(Hn,Yi,!0,Z)}}tn&&(de.idx=g,Ks()),d!==!1&&pt("setCursor")}let Bn=null;Object.defineProperty(r,"rect",{get(){return Bn==null&&Zr(!1),Bn}});function Zr(u=!1){u?Bn=null:(Bn=S.getBoundingClientRect(),pt("syncRect",Bn))}function Bd(u,d,p,g,_,y,x){W._lock||Vn&&u!=null&&u.movementX==0&&u.movementY==0||(Ys(u,d,p,g,_,y,x,!1,u!=null),u!=null?fi(null,!0,!0):fi(d,!0,!1))}function Ys(u,d,p,g,_,y,x,Z,N){if(Bn==null&&Zr(!1),ui(u),u!=null)p=u.clientX-Bn.left,g=u.clientY-Bn.top;else{if(p<0||g<0){De=-10,Ve=-10;return}let[R,A]=it.scales,F=d.cursor.sync,[ie,ke]=F.values,[$e,fe]=F.scales,[X,H]=it.match,Ae=d.axes[0].side%2==1,Qe=O.ori==0?le:L,ht=O.ori==1?le:L,Te=Ae?y:_,et=Ae?_:y,Ft=Ae?g:p,Tt=Ae?p:g;if($e!=null?p=X(R,$e)?s(ie,z[R],Qe,0):-10:p=Qe*(Ft/Te),fe!=null?g=H(A,fe)?s(ke,z[A],ht,0):-10:g=ht*(Tt/et),O.ori==1){let rn=p;p=g,g=rn}}N&&(d==null||d.cursor.event.type==ol)&&((p<=1||p>=le-1)&&(p=vi(p,le)),(g<=1||g>=L-1)&&(g=vi(g,L))),Z?(Fd=p,Jd=g,[Bi,Gi]=W.move(r,p,g)):(De=p,Ve=g)}const Xs={width:0,height:0,left:0,top:0};function Qs(){Vo(Xs,!1)}let Gd,Kd,Yd,Xd;function Qd(u,d,p,g,_,y,x){Vn=!0,ft=mt=vt._x=vt._y=!1,Ys(u,d,p,g,_,y,x,!0,!1),u!=null&&(zt(al,zl,ef,!1),Pr(Gf,r,Bi,Gi,le,L,null));let{left:Z,top:N,width:R,height:A}=Re;Gd=Z,Kd=N,Yd=R,Xd=A}function ef(u,d,p,g,_,y,x){Vn=vt._x=vt._y=!1,Ys(u,d,p,g,_,y,x,!1,!0);let{left:Z,top:N,width:R,height:A}=Re,F=R>0||A>0,ie=Gd!=Z||Kd!=N||Yd!=R||Xd!=A;if(F&&ie&&Vo(Re),vt.setScale&&F&&ie){let ke=Z,$e=R,fe=N,X=A;if(O.ori==1&&(ke=N,$e=A,fe=Z,X=R),ft&&Wn(C,zn(ke,C),zn(ke+$e,C)),mt)for(let H in z){let Ae=z[H];H!=C&&Ae.from==null&&Ae.min!=Ee&&Wn(H,zn(fe+X,H),zn(fe,H))}Qs()}else W.lock&&(W._lock=!W._lock,fi(d,!0,u!=null));u!=null&&(Di(al,zl),Pr(al,r,De,Ve,le,L,null))}function Ly(u,d,p,g,_,y,x){if(W._lock)return;ui(u);let Z=Vn;if(Vn){let N=!0,R=!0,A=10,F,ie;O.ori==0?(F=ft,ie=mt):(F=mt,ie=ft),F&&ie&&(N=De<=A||De>=le-A,R=Ve<=A||Ve>=L-A),F&&N&&(De=De<Bi?0:le),ie&&R&&(Ve=Ve<Gi?0:L),fi(null,!0,!0),Vn=!1}De=-10,Ve=-10,ze.fill(null),fi(null,!0,!0),Z&&(Vn=Z)}function tf(u,d,p,g,_,y,x){W._lock||(ui(u),js(),Qs(),u!=null&&Pr(Xf,r,De,Ve,le,L,null))}function nf(){T.forEach(xZ),Os(r.width,r.height,!0)}wi(wa,ir,nf);const Xi={};Xi.mousedown=Qd,Xi.mousemove=Bd,Xi.mouseup=ef,Xi.dblclick=tf,Xi.setSeries=(u,d,p,g)=>{let _=it.match[2];p=_(r,d,p),p!=-1&&xn(p,g,!0,!1)},xt&&(zt(Gf,S,Qd),zt(ol,S,Bd),zt(Kf,S,u=>{ui(u),Zr(!1)}),zt(Yf,S,Ly),zt(Xf,S,tf),jl.add(r),r.syncRect=Zr);const Wo=r.hooks=e.hooks||{};function pt(u,d,p){Ws?xr.push([u,d,p]):u in Wo&&Wo[u].forEach(g=>{g.call(null,r,d,p)})}(e.plugins||[]).forEach(u=>{for(let d in u.hooks)Wo[d]=(Wo[d]||[]).concat(u.hooks[d])});const rf=(u,d,p)=>p,it=rt({key:null,setSeries:!1,filters:{pub:um,sub:um},scales:[C,k[1]?k[1].scale:null],match:[cm,cm,rf],values:[null,null]},W.sync);it.match.length==2&&it.match.push(rf),W.sync=it;const of=it.key,el=q$(of);function Pr(u,d,p,g,_,y,x){it.filters.pub(u,d,p,g,_,y,x)&&el.pub(u,d,p,g,_,y,x)}el.sub(r);function My(u,d,p,g,_,y,x){it.filters.sub(u,d,p,g,_,y,x)&&Xi[u](null,d,p,g,_,y,x)}r.pub=My;function Fy(){el.unsub(r),jl.delete(r),ri.clear(),Pl(wa,ir,nf),c.remove(),Se?.remove(),pt("destroy")}r.destroy=Fy;function tl(){pt("init",e,t),Od(t||e.data,!1),K[C]?Hs(C,K[C]):js(),Co=Re.show&&(Re.width>0||Re.height>0),li=tn=!0,Os(e.width,e.height)}return k.forEach(Ud),T.forEach($y),i?i instanceof HTMLElement?(i.appendChild(c),tl()):i(r,tl):tl(),r}St.assign=rt;St.fmtNum=bd;St.rangeNum=Sa;St.rangeLog=xs;St.rangeAsinh=vd;St.orient=Ni;St.pxRatio=we;St.join=v6;St.fmtDate=yd,St.tzDate=T6;St.sync=q$;{St.addGap=cZ,St.clipGaps=Zs;let e=St.paths={points:K$};e.linear=X$,e.stepped=mZ,e.bars=pZ,e.spline=gZ}function Rl(e,t=0){return e<=0?[]:Array.from({length:e},(i,r)=>t+r+1)}function zZ(e,t){return e<=0&&t<=0?[]:[...Rl(e,0),...Rl(t,e)]}function ny(e,t){return t==="failed"||t==="cancelled"||e.kernel_complete===!0?!0:t==="completed"}function dl(e){return`${e} (panel)`}function TZ(e){if(String(e.training_progress_phase??e.progress_phase??"").toLowerCase()==="floop"||String(e.job_type??"").toLowerCase().includes("floop"))return!0;const r=e.run_config;return!!(r&&typeof r=="object"&&String(r.model??"").toLowerCase().includes("floop"))}function ZZ(e){const t=e.training_curve_source_batches,i=[],r=t&&typeof t.ordering=="number"?t.ordering:null,n=t&&typeof t.convergence=="number"?t.convergence:null,o=Array.isArray(e.ordering_errors)?e.ordering_errors.length:0,a=Array.isArray(e.convergence_errors)?e.convergence_errors.length:0;r!=null&&r>o&&i.push(`${o} of ${r.toLocaleString()} ordering batch samples`),n!=null&&n>a&&i.push(`${a} of ${n.toLocaleString()} convergence batch samples`);const s=e.te_evaluation,l=s?.te_panel_size,c=s?.te_panel_n_train;if(typeof l=="number"&&typeof c=="number"&&c>0){const $=s?.te_panel_stratified===!0?", stratified by mesh":"";i.push(`TE curve uses a fixed panel of ${l.toLocaleString()} of ${c.toLocaleString()} training rows${$}`)}const m=e.panel_topographic_error,f=e.map_topographic_error;return m!=null&&f!=null&&Number.isFinite(Number(m))&&Number.isFinite(Number(f))&&i.push(`Panel TE ${Number(m).toFixed(4)} · Map TE ${Number(f).toFixed(4)} (full training set)`),i.length===0?null:`${r!=null||n!=null?"Displaying uniformly subsampled QE+TE curves (≤1000 points/phase, joint indices): ":""}${i.join("; ")}.`.replace(/: ;/,":")}function fl(e,t){if(t<=0)return[];if(e.length===t)return e;if(e.length===0)return Array(t).fill(null);if(e.length>=t)return e.slice(0,t);const i=t-e.length;return[...Array(i).fill(null),...e]}function iy(e){return e==="completed"||e==="failed"||e==="cancelled"}function Td(e){const t=String(e.status??"");return t!=="completed"?iy(t):(e.finalize_job_id!=null&&String(e.finalize_job_id)!==""?String(e.finalize_job_id):null)?String(e.finalize_status??"")==="completed":!0}function Al(e){const t=String(e.status??"");if(!iy(t))return!0;if(t!=="completed"||e.finalize_failed===!0)return!1;const i=e.finalize_job_id!=null&&String(e.finalize_job_id)!==""?String(e.finalize_job_id):null,r=e.finalize_status!=null&&String(e.finalize_status)!==""?String(e.finalize_status):null;return i!=null&&r!=="completed"}const En=new ET({name:"Training Monitor",version:"1.0.0"},{},{autoResize:!0});let Um=!1,Nm=!1;const PZ=document.getElementById("progress-fill"),EZ=document.getElementById("progress-pct"),Cl=document.getElementById("elapsed"),UZ=document.getElementById("phase"),oa=document.getElementById("eta"),Ll=document.getElementById("job-id"),Dt=document.getElementById("status-badge"),NZ=document.getElementById("params"),Wt=document.getElementById("advisory-banner");document.getElementById("curve");const Do=document.getElementById("chart-target"),Si=document.getElementById("curve-fallback"),ry=document.getElementById("chart-legend"),OZ=document.getElementById("metric-scalars"),to=document.getElementById("cancel-btn"),hi=document.getElementById("open-results-btn"),nr=document.getElementById("copy-monitor-url"),Om=document.getElementById("open-standalone"),ml=document.getElementById("hitgrid-container"),aa=document.getElementById("hitgrid-canvas"),jZ=document.getElementById("hitgrid-meta"),Ml=5e3,pl=2e3;let tt="",hl=null,Jr=null,qr=null,gl=!1,jm=!1,Kn=null;function Zd(e){return`/viz/results-explorer?mode=standalone&job_id=${encodeURIComponent(e)}`}function oy(e){const t=String(e.id??e.job_id??tt);if(!t||!hi)return;hi.style.display="inline-flex";const i=String(e.status??"")==="completed"&&Td(e);hi.href=Zd(t),i?(hi.classList.remove("nav-btn-disabled"),hi.title="Browse figures for this job"):(hi.classList.add("nav-btn-disabled"),hi.title="Available when training and finalize (if any) complete")}async function DZ(e){if(jm||String(e.status??"")!=="completed"||!Td(e))return;jm=!0,oy(e);const t=String(e.id??e.job_id??tt);if(!t)return;const i=Zd(t);if(kr){window.open(i,"_blank","noopener,noreferrer");return}try{await En.callServerTool({name:"results_explorer",arguments:{job_id:t}})}catch{}}function Cr(e){if(e<60)return`${e}s`;const t=Math.floor(e/60),i=e%60;return t<60?`${t}m ${i}s`:`${Math.floor(t/60)}h ${t%60}m`}function RZ(e){if(Jr)return;hl=e;const t=()=>{if(hl==null)return;const i=Math.max(0,Math.round((Date.now()-hl)/1e3));Cl.textContent=Cr(i)};t(),Jr=setInterval(t,1e3)}function Vr(){Jr&&(clearInterval(Jr),Jr=null)}function AZ(e,t,i){const r=ny(e,t);r&&Vr();const n=e.training_elapsed_sec!=null?Number(e.training_elapsed_sec):e.kernel_elapsed_sec!=null?Number(e.kernel_elapsed_sec):null,o=e.wall_elapsed_sec!=null?Number(e.wall_elapsed_sec):null;if(n!=null&&n>=0)Cl.textContent=r?`Training ${Cr(Math.round(n))}`:Cr(Math.round(n));else if(!r&&e.started_at&&typeof e.started_at=="string"){const s=Date.parse(e.started_at);Number.isNaN(s)||RZ(s)}else e.training_duration_seconds&&(Cl.textContent=`${e.training_duration_seconds}s`);if(r){if(i&&o!=null&&o>=0){const s=n!=null?Math.round(n):null;oa.textContent=s==null||Math.abs(Math.round(o)-s)>=2?`Wall ${Cr(Math.round(o))}`:""}else oa.textContent="";return}const a=e.training_eta_sec!=null?Number(e.training_eta_sec):null;a!=null&&a>0?oa.textContent=`ETA ~${Cr(Math.round(a))}`:oa.textContent=""}function ay(){const e=Do.clientWidth;if(e>0)return Math.max(e,200);const t=En.getHostContext?.()?.containerDimensions,i=t?.width??t?.maxWidth;return Math.max(typeof i=="number"?i:360,200)}function Nn(e){return Array.isArray(e)?e.map(t=>Number(t)).filter(t=>Number.isFinite(t)):[]}function Fl(e){return Nn(e.ordering_errors).length>0||Nn(e.convergence_errors).length>0||Nn(e.ordering_topographic_errors).length>0||Nn(e.convergence_topographic_errors).length>0}function sa(e){return Array(e).fill(null)}function sy(){Kn&&(Kn.destroy(),Kn=null),Do.innerHTML=""}function CZ(e,t){ry.innerHTML=e.map((i,r)=>`<span class="legend-item"><span class="legend-swatch" style="background:${t[r]}"></span>${i}</span>`).join("")}function Dm(e,t,i="Batch sample"){if(sy(),e.length<2)return;Si.style.display="none";const r=getComputedStyle(document.documentElement).getPropertyValue("--accent").trim()||"#4f46e5",n=getComputedStyle(document.documentElement).getPropertyValue("--coral").trim()||"#f97316",o=getComputedStyle(document.documentElement).getPropertyValue("--text2").trim()||"#666",a=getComputedStyle(document.documentElement).getPropertyValue("--border").trim()||"#e0e0e0",s=t.map(f=>({...f,stroke:f.stroke==="accent"?r:f.stroke==="coral"?n:f.stroke})),l={width:ay(),height:160,cursor:{show:!0},select:{show:!1,left:0,top:0,width:0,height:0},scales:{x:{time:!1},y:{auto:!0},y2:{auto:!0}},axes:[{stroke:o,grid:{stroke:a,width:1},font:"11px -apple-system, BlinkMacSystemFont, sans-serif",label:i,labelFont:"11px -apple-system, BlinkMacSystemFont, sans-serif",size:32},{scale:"y",stroke:o,grid:{stroke:a,width:1},font:"11px -apple-system, BlinkMacSystemFont, sans-serif",label:"QE",labelFont:"11px -apple-system, BlinkMacSystemFont, sans-serif",size:48},{scale:"y2",side:1,stroke:o,grid:{show:!1},font:"11px -apple-system, BlinkMacSystemFont, sans-serif",label:"TE",labelFont:"11px -apple-system, BlinkMacSystemFont, sans-serif",size:48}],series:[{},...s.map(f=>({label:f.label,scale:f.scale,stroke:f.stroke,width:f.scale==="y"?2:1.5,dash:f.dash,spanGaps:!1}))]},c=[e,...s.map(f=>f.values)];Kn=new St(l,c,Do),CZ(s.map(f=>f.label),s.map(f=>f.stroke));const m=ZZ(ly);m&&ry.insertAdjacentHTML("beforeend",`<div class="curve-source-note" style="margin-top:6px;font-size:11px;color:var(--text2);">${m}</div>`)}let ly={};function LZ(e,t){ny(e,t);const i=e;ly=i;const r=Nn(i.ordering_errors),n=Nn(i.convergence_errors),o=Nn(i.ordering_topographic_errors),a=Nn(i.convergence_topographic_errors);if(r.length===0&&n.length===0&&o.length===0&&a.length===0)return!1;const s=TZ(i),l=r.length,c=n.length,m=s?Math.max(l,o.length,1):Math.max(l+c,1),f=s?Rl(m):zZ(l,c);if(f.length<2)return!1;if(s){const J=r,k=fl(o,m),T=[{label:"QE (FLooP steps)",values:J,scale:"y",stroke:"accent"}];return o.length>0&&T.push({label:dl("TE (FLooP steps)"),values:k,scale:"y2",stroke:"accent",dash:[4,4]}),Dm(f,T,"FLooP step"),!0}const h=[...r,...sa(c)],$=[...sa(l),...n],S=[...fl(o,l),...sa(c)],U=[...sa(l),...fl(a,c)],D=[];return l>0&&D.push({label:"QE ordering",values:h,scale:"y",stroke:"accent"}),c>0&&D.push({label:"QE convergence",values:$,scale:"y",stroke:"coral"}),o.length>0&&D.push({label:dl("TE ordering"),values:S,scale:"y2",stroke:"accent",dash:[4,4]}),a.length>0&&D.push({label:dl("TE convergence"),values:U,scale:"y2",stroke:"coral",dash:[4,4]}),Dm(f,D,"Batch sample"),!0}function MZ(e){const t=Nn(e.hit_grid),i=Nn(e.hit_grid_dims);if(t.length===0||i.length<2){ml.style.display="none";return}const r=Math.max(1,Math.round(i[0])),n=Math.max(1,Math.round(i[1]));if(r*n!==t.length){ml.style.display="none";return}ml.style.display="block",aa.width=r,aa.height=n,aa.style.height=`${Math.min(220,n*8)}px`;const o=aa.getContext("2d");if(!o)return;const a=t.reduce((U,D)=>D>U?D:U,0)||1,s=getComputedStyle(document.documentElement).getPropertyValue("--accent").trim()||"#4f46e5",l=/^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(s.replace("#","")),c=l?parseInt(l[1],16):79,m=l?parseInt(l[2],16):70,f=l?parseInt(l[3],16):229,h=o.createImageData(r,n);let $=0;for(let U=0;U<t.length;U++){const D=t[U]/a;t[U]>0&&$++;const J=U%r,T=(Math.floor(U/r)*r+J)*4;h.data[T]=Math.round(245-D*(245-c)),h.data[T+1]=Math.round(245-D*(245-m)),h.data[T+2]=Math.round(245-D*(245-f)),h.data[T+3]=255}o.putImageData(h,0,0);const S=t.reduce((U,D)=>U+D,0);jZ.textContent=`${r}×${n} nodes · ${$} occupied (${($/t.length*100).toFixed(0)}%) · ${S} sampled hits · max ${a}`}function FZ(e){const t=e.quantization_error??e.qe,i=e.panel_topographic_error??e.epoch_topographic_error??e.topographic_error,r=e.map_topographic_error,n=[];t!=null&&Number.isFinite(Number(t))&&n.push(`QE ${Number(t).toFixed(4)}`),i!=null&&Number.isFinite(Number(i))&&n.push(`Panel TE ${Number(i).toFixed(4)}`),r!=null&&Number.isFinite(Number(r))&&n.push(`Map TE ${Number(r).toFixed(4)}`),OZ.textContent=n.length>0?n.join(" · "):""}function uy(e){return e==="completed"||e==="failed"||e==="cancelled"}function cy(e){const t=e.standaloneUrl;if(t)return t;if(kr&&tt)return`${window.location.origin}/viz/training-monitor?mode=standalone&job_id=${encodeURIComponent(tt)}`}function JZ(e){const t=e.relatedUrls;if(t?.resultsExplorer)return t.resultsExplorer;if(kr&&tt)return`${window.location.origin}/viz/results-explorer?mode=standalone&job_id=${encodeURIComponent(tt)}`}function qZ(e){const t=cy(e);t&&(nr.style.display="inline-block",nr.dataset.url=t,Om.href=t,Om.style.display="inline-block")}function VZ(e,t,i){if(i||t!=="completed")return;const r=JZ(e);r&&(Wt.style.display="block",Wt.innerHTML=`Training complete. <a href="${r}" target="_blank" rel="noreferrer">Open results explorer</a> to browse figures.`)}nr.addEventListener("click",async()=>{const e=nr.dataset.url||cy({standaloneUrl:void 0});if(e)try{await navigator.clipboard.writeText(e),nr.textContent="Copied!",setTimeout(()=>{nr.textContent="Copy monitor URL"},1500)}catch{window.prompt("Copy monitor URL:",e)}});function WZ(e,t){if(kr||Um||Nm||!Td(e))return;const i=String(e.id??e.job_id??tt);if(!i)return;Um=!0;const r=[`Training job ${i} ${t}.`],n=e.quantization_error??e.qe,o=e.map_topographic_error??e.topographic_error,a=e.panel_topographic_error??e.epoch_topographic_error;if(n!=null&&Number.isFinite(Number(n))&&r.push(`Final QE: ${Number(n).toFixed(4)}`),a!=null&&Number.isFinite(Number(a))&&r.push(`Final panel TE: ${Number(a).toFixed(4)}`),o!=null&&Number.isFinite(Number(o))&&r.push(`Final map TE: ${Number(o).toFixed(4)}`),e.finalize_failed===!0)r.push(`Finalize failed${e.finalize_failure_stage?` [${String(e.finalize_failure_stage)}]`:""}: ${String(e.finalize_error??"results not uploaded")}. Results may be unavailable until finalize is retried.`);else if(t==="completed"){r.push(`Use results(get, job_id="${i}") for metrics, then results_explorer(job_id="${i}") to browse figures.`);const s=Zd(i);r.push(`Results explorer: ${s}`)}DZ(e),En.updateModelContext({content:[{type:"text",text:r.join(`
|
|
142
|
+
`)==-1?[qt]:qt.split(/\n/gm);for(let Pt=0;Pt<mi.length;Pt++){let af=mi[Pt];Te?(f.save(),f.translate(_,y+Pt*Jt),f.rotate(Te),f.fillText(af,0,0),f.restore()):f.fillText(af,_,y+Pt*Jt)}}}X.show&&Vs(on,X.filter(r,$e,u,R,fe),g,p,et,Qe,Ue(X.width*we,3),X.stroke(r,u),X.dash,X.cap);let Zn=d.grid;Zn.show&&Vs(on,Zn.filter(r,$e,u,R,fe),g,g==0?2:1,g==0?en:Mt,g==0?hn:Ot,Ue(Zn.width*we,3),Zn.stroke(r,u),Zn.dash,Zn.cap),H.show&&Vs([et],[1],g==0?1:0,g==0?1:2,g==1?en:Mt,g==1?hn:Ot,Ue(H.width*we,3),H.stroke(r,u),H.dash,H.cap)}pt("drawAxes")}function qi(u){k.forEach((d,p)=>{p>0&&(d._paths=null,u&&(n==1?(d.min=null,d.max=null):d.facets.forEach(g=>{g.min=null,g.max=null})))})}let qo=!1,Ws=!1,xr=[];function Zy(){Ws=!1;for(let u=0;u<xr.length;u++)pt(...xr[u]);xr.length=0}function Vi(){qo||(_6(Md),qo=!0)}function Py(u,d=!1){qo=!0,Ws=d,u(r),Md(),d&&xr.length>0&&queueMicrotask(Zy)}r.batch=Py;function Md(){if(Ns&&(yy(),Ns=!1),Ai&&(py(),Ai=!1),Ao){if(Ce($,er,je),Ce($,Nr,at),Ce($,Dr,le),Ce($,Rr,L),Ce(S,er,je),Ce(S,Nr,at),Ce(S,Dr,le),Ce(S,Rr,L),Ce(h,Dr,jn),Ce(h,Rr,oi),m.width=st(jn*we),m.height=st(oi*we),T.forEach(({_el:u,_show:d,_size:p,_pos:g,side:_})=>{if(u!=null)if(d){let y=_===3||_===0?p:0,x=_%2==1;Ce(u,x?"left":"top",g-y),Ce(u,x?"width":"height",p),Ce(u,x?"top":"left",x?at:je),Ce(u,x?"height":"width",x?L:le),Zl(u,bi)}else Vt(u,bi)}),Mo=Ji=Ds=As=Cs=Ls=Ms=Fs=Rs=null,jt=1,Zr(!0),je!=Ri||at!=ai||le!=Sn||L!=si){qi(!1);let u=le/Sn,d=L/si;if(xt&&!li&&W.left>=0){W.left*=u,W.top*=d,Wi&&Pn(Wi,st(W.left),0,le,L),Hi&&Pn(Hi,0,st(W.top),le,L);for(let p=0;p<nn.length;p++){let g=nn[p];g!=null&&(Mi[p]*=u,Fi[p]*=d,Pn(g,sn(Mi[p]),sn(Fi[p]),le,L))}}if(Re.show&&!Co&&Re.left>=0&&Re.width>0){Re.left*=u,Re.width*=u,Re.top*=d,Re.height*=d;for(let p in Xs)Ce(Ki,p,Re[p])}Ri=je,ai=at,Sn=le,si=L}pt("setSize"),Ao=!1}jn>0&&oi>0&&(f.clearRect(0,0,m.width,m.height),pt("drawClear"),te.forEach(u=>u()),pt("draw")),Re.show&&Co&&(Vo(Re),Co=!1),xt&&li&&(fi(null,!0,!1),li=!1),de.show&&de.live&&tn&&(Ks(),tn=!1),l||(l=!0,r.status=1,pt("ready")),Ir=!1,qo=!1}r.redraw=(u,d)=>{Ai=d||!1,u!==!1?Wn(C,O.min,O.max):Vi()};function Hs(u,d){let p=z[u];if(p.from==null){if(gt==0){let g=p.range(r,d.min,d.max,u);d.min=g[0],d.max=g[1]}if(d.min>d.max){let g=d.min;d.min=d.max,d.max=g}if(gt>1&&d.min!=null&&d.max!=null&&d.max-d.min<1e-16)return;u==C&&p.distr==2&>>0&&(d.min=$n(d.min,t[0]),d.max=$n(d.max,t[0]),d.min==d.max&&d.max++),K[u]=d,Ns=!0,Vi()}}r.setScale=Hs;let Bs,Gs,Wi,Hi,Fd,Jd,Bi,Gi,qd,Vd,De,Ve,Vn=!1;const vt=W.drag;let ft=vt.x,mt=vt.y;xt&&(W.x&&(Bs=an(FT,S)),W.y&&(Gs=an(JT,S)),O.ori==0?(Wi=Bs,Hi=Gs):(Wi=Gs,Hi=Bs),De=W.left,Ve=W.top);const Re=r.select=rt({show:!0,over:!0,left:0,width:0,top:0,height:0},e.select),Ki=Re.show?an(MT,Re.over?S:$):null;function Vo(u,d){if(Re.show){for(let p in u)Re[p]=u[p],p in Xs&&Ce(Ki,p,u[p]);d!==!1&&pt("setSelect")}}r.setSelect=Vo;function Ey(u){if(k[u].show)ot&&Zl(Be[u],bi);else if(ot&&Vt(Be[u],bi),xt){let p=Li?nn[0]:nn[u];p!=null&&Pn(p,-10,-10,le,L)}}function Wn(u,d,p){Hs(u,{min:d,max:p})}function xn(u,d,p,g){d.focus!=null&&Dy(u),d.show!=null&&k.forEach((_,y)=>{y>0&&(u==y||u==null)&&(_.show=d.show,Ey(y),n==2?(Wn(_.facets[0].scale,null,null),Wn(_.facets[1].scale,null,null)):Wn(_.scale,null,null),Vi())}),p!==!1&&pt("setSeries",u,d),g&&Pr("setSeries",r,u,d)}r.setSeries=xn;function Uy(u,d){rt(ee[u],d)}function Ny(u,d){u.fill=he(u.fill||null),u.dir=ye(u.dir,-1),d=d??ee.length,ee.splice(d,0,u)}function Oy(u){u==null?ee.length=0:ee.splice(u,1)}r.addBand=Ny,r.setBand=Uy,r.delBand=Oy;function jy(u,d){k[u].alpha=d,xt&&nn[u]!=null&&(nn[u].style.opacity=d),ot&&Be[u]&&(Be[u].style.opacity=d)}let Dn,Hn,di;const Yi={focus:!0};function Dy(u){if(u!=di){let d=u==null,p=In.alpha!=1;k.forEach((g,_)=>{if(n==1||_>0){let y=d||_==0||_==u;g._focus=d?null:y,p&&jy(_,y?1:In.alpha)}}),di=u,p&&Vi()}}ot&&Ci&&zt(Yf,Se,u=>{W._lock||(ui(u),di!=null&&xn(null,Yi,!0,it.setSeries))});function zn(u,d,p){let g=z[d];p&&(u=u/we-(g.ori==1?at:je));let _=le;g.ori==1&&(_=L,u=_-u),g.dir==-1&&(u=_-u);let y=g._min,x=g._max,Z=u/_,N=y+(x-y)*Z,R=g.distr;return R==3?fr(10,N):R==4?a6(N,g.asinh):R==100?g.bwd(N):N}function Ry(u,d){let p=zn(u,C,d);return $n(p,t[0],ct,dt)}r.valToIdx=u=>$n(u,t[0]),r.posToIdx=Ry,r.posToVal=zn,r.valToPos=(u,d,p)=>z[d].ori==0?o(u,z[d],p?Ot:le,p?Mt:0):a(u,z[d],p?hn:L,p?en:0),r.setCursor=(u,d,p)=>{De=u.left,Ve=u.top,fi(null,d,p)};function Wd(u,d){Ce(Ki,er,Re.left=u),Ce(Ki,Dr,Re.width=d)}function Hd(u,d){Ce(Ki,Nr,Re.top=u),Ce(Ki,Rr,Re.height=d)}let zr=O.ori==0?Wd:Hd,Tr=O.ori==1?Wd:Hd;function Ay(){if(ot&&de.live)for(let u=n==2?1:0;u<k.length;u++){if(u==0&&Qt)continue;let d=de.values[u],p=0;for(let g in d)wn[u][p++].firstChild.nodeValue=d[g]}}function Ks(u,d){if(u!=null&&(u.idxs?u.idxs.forEach((p,g)=>{ze[g]=p}):c6(u.idx)||ze.fill(u.idx),de.idx=ze[0]),ot&&de.live){for(let p=0;p<k.length;p++)(p>0||n==1&&!Qt)&&Cy(p,ze[p]);Ay()}tn=!1,d!==!1&&pt("setLegend")}r.setLegend=Ks;function Cy(u,d){let p=k[u],g=u==0&&ue==2?gn:t[u],_;Qt?_=p.values(r,u,d)??Jn:(_=p.value(r,d==null?null:g[d],u,d),_=_==null?Jn:{_}),de.values[u]=_}function fi(u,d,p){qd=De,Vd=Ve,[De,Ve]=W.move(r,De,Ve),W.left=De,W.top=Ve,xt&&(Wi&&Pn(Wi,st(De),0,le,L),Hi&&Pn(Hi,0,st(Ve),le,L));let g,_=ct>dt;Dn=Ee,Hn=null;let y=O.ori==0?le:L,x=O.ori==1?le:L;if(De<0||gt==0||_){g=W.idx=null;for(let Z=0;Z<k.length;Z++){let N=nn[Z];N!=null&&Pn(N,-10,-10,le,L)}Ci&&xn(null,Yi,!0,u==null&&it.setSeries),de.live&&(ze.fill(g),tn=!0)}else{let Z,N,R;n==1&&(Z=O.ori==0?De:Ve,N=zn(Z,C),g=W.idx=$n(N,t[0],ct,dt),R=V(t[0][g],O,y,0));let A=-10,F=-10,ie=0,ke=0,$e=!0,fe="",X="";for(let H=n==2?1:0;H<k.length;H++){let Ae=k[H],Qe=ze[H],ht=Qe==null?null:n==1?t[H][Qe]:t[H][1][Qe],Te=W.dataIdx(r,H,g,N),et=Te==null?null:n==1?t[H][Te]:t[H][1][Te];if(tn=tn||et!=ht||Te!=Qe,ze[H]=Te,H>0&&Ae.show){let Ft=Te==null?-10:Te==g?R:V(n==1?t[0][Te]:t[H][0][Te],O,y,0),Tt=et==null?-10:ne(et,n==1?z[Ae.scale]:z[Ae.facets[1].scale],x,0);if(Ci&&et!=null){let rn=O.ori==1?De:Ve,vn=lt(In.dist(r,H,Te,Tt,rn));if(vn<Dn){let Tn=In.bias;if(Tn!=0){let Jt=zn(rn,Ae.scale),on=et>=0?1:-1,_n=Jt>=0?1:-1;_n==on&&(_n==1?Tn==1?et>=Jt:et<=Jt:Tn==1?et<=Jt:et>=Jt)&&(Dn=vn,Hn=H)}else Dn=vn,Hn=H}}if(tn||Li){let rn,vn;O.ori==0?(rn=Ft,vn=Tt):(rn=Tt,vn=Ft);let Tn,Jt,on,_n,Zn,Zt,qt=!0,mi=kt.bbox;if(mi!=null){qt=!1;let Pt=mi(r,H);on=Pt.left,_n=Pt.top,Tn=Pt.width,Jt=Pt.height}else on=rn,_n=vn,Tn=Jt=kt.size(r,H);if(Zt=kt.fill(r,H),Zn=kt.stroke(r,H),Li)H==Hn&&Dn<=In.prox&&(A=on,F=_n,ie=Tn,ke=Jt,$e=qt,fe=Zt,X=Zn);else{let Pt=nn[H];Pt!=null&&(Mi[H]=on,Fi[H]=_n,rm(Pt,Tn,Jt,qt),nm(Pt,Zt,Zn),Pn(Pt,sn(on),sn(_n),le,L))}}}}if(Li){let H=In.prox,Ae=di==null?Dn<=H:Dn>H||Hn!=di;if(tn||Ae){let Qe=nn[0];Qe!=null&&(Mi[0]=A,Fi[0]=F,rm(Qe,ie,ke,$e),nm(Qe,fe,X),Pn(Qe,sn(A),sn(F),le,L))}}}if(Re.show&&Vn)if(u!=null){let[Z,N]=it.scales,[R,A]=it.match,[F,ie]=u.cursor.sync.scales,ke=u.cursor.drag;if(ft=ke._x,mt=ke._y,ft||mt){let{left:$e,top:fe,width:X,height:H}=u.select,Ae=u.scales[F].ori,Qe=u.posToVal,ht,Te,et,Ft,Tt,rn=Z!=null&&R(Z,F),vn=N!=null&&A(N,ie);rn&&ft?(Ae==0?(ht=$e,Te=X):(ht=fe,Te=H),et=z[Z],Ft=V(Qe(ht,F),et,y,0),Tt=V(Qe(ht+Te,F),et,y,0),zr(yn(Ft,Tt),lt(Tt-Ft))):zr(0,y),vn&&mt?(Ae==1?(ht=$e,Te=X):(ht=fe,Te=H),et=z[N],Ft=ne(Qe(ht,ie),et,x,0),Tt=ne(Qe(ht+Te,ie),et,x,0),Tr(yn(Ft,Tt),lt(Tt-Ft))):Tr(0,x)}else Qs()}else{let Z=lt(qd-Fd),N=lt(Vd-Jd);if(O.ori==1){let ie=Z;Z=N,N=ie}ft=vt.x&&Z>=vt.dist,mt=vt.y&&N>=vt.dist;let R=vt.uni;R!=null?ft&&mt&&(ft=Z>=R,mt=N>=R,!ft&&!mt&&(N>Z?mt=!0:ft=!0)):vt.x&&vt.y&&(ft||mt)&&(ft=mt=!0);let A,F;ft&&(O.ori==0?(A=Bi,F=De):(A=Gi,F=Ve),zr(yn(A,F),lt(F-A)),mt||Tr(0,x)),mt&&(O.ori==1?(A=Bi,F=De):(A=Gi,F=Ve),Tr(yn(A,F),lt(F-A)),ft||zr(0,y)),!ft&&!mt&&(zr(0,0),Tr(0,0))}if(vt._x=ft,vt._y=mt,u==null){if(p){if(of!=null){let[Z,N]=it.scales;it.values[0]=Z!=null?zn(O.ori==0?De:Ve,Z):null,it.values[1]=N!=null?zn(O.ori==1?De:Ve,N):null}Pr(ol,r,De,Ve,le,L,g)}if(Ci){let Z=p&&it.setSeries,N=In.prox;di==null?Dn<=N&&xn(Hn,Yi,!0,Z):Dn>N?xn(null,Yi,!0,Z):Hn!=di&&xn(Hn,Yi,!0,Z)}}tn&&(de.idx=g,Ks()),d!==!1&&pt("setCursor")}let Bn=null;Object.defineProperty(r,"rect",{get(){return Bn==null&&Zr(!1),Bn}});function Zr(u=!1){u?Bn=null:(Bn=S.getBoundingClientRect(),pt("syncRect",Bn))}function Bd(u,d,p,g,_,y,x){W._lock||Vn&&u!=null&&u.movementX==0&&u.movementY==0||(Ys(u,d,p,g,_,y,x,!1,u!=null),u!=null?fi(null,!0,!0):fi(d,!0,!1))}function Ys(u,d,p,g,_,y,x,Z,N){if(Bn==null&&Zr(!1),ui(u),u!=null)p=u.clientX-Bn.left,g=u.clientY-Bn.top;else{if(p<0||g<0){De=-10,Ve=-10;return}let[R,A]=it.scales,F=d.cursor.sync,[ie,ke]=F.values,[$e,fe]=F.scales,[X,H]=it.match,Ae=d.axes[0].side%2==1,Qe=O.ori==0?le:L,ht=O.ori==1?le:L,Te=Ae?y:_,et=Ae?_:y,Ft=Ae?g:p,Tt=Ae?p:g;if($e!=null?p=X(R,$e)?s(ie,z[R],Qe,0):-10:p=Qe*(Ft/Te),fe!=null?g=H(A,fe)?s(ke,z[A],ht,0):-10:g=ht*(Tt/et),O.ori==1){let rn=p;p=g,g=rn}}N&&(d==null||d.cursor.event.type==ol)&&((p<=1||p>=le-1)&&(p=vi(p,le)),(g<=1||g>=L-1)&&(g=vi(g,L))),Z?(Fd=p,Jd=g,[Bi,Gi]=W.move(r,p,g)):(De=p,Ve=g)}const Xs={width:0,height:0,left:0,top:0};function Qs(){Vo(Xs,!1)}let Gd,Kd,Yd,Xd;function Qd(u,d,p,g,_,y,x){Vn=!0,ft=mt=vt._x=vt._y=!1,Ys(u,d,p,g,_,y,x,!0,!1),u!=null&&(zt(al,zl,ef,!1),Pr(Gf,r,Bi,Gi,le,L,null));let{left:Z,top:N,width:R,height:A}=Re;Gd=Z,Kd=N,Yd=R,Xd=A}function ef(u,d,p,g,_,y,x){Vn=vt._x=vt._y=!1,Ys(u,d,p,g,_,y,x,!1,!0);let{left:Z,top:N,width:R,height:A}=Re,F=R>0||A>0,ie=Gd!=Z||Kd!=N||Yd!=R||Xd!=A;if(F&&ie&&Vo(Re),vt.setScale&&F&&ie){let ke=Z,$e=R,fe=N,X=A;if(O.ori==1&&(ke=N,$e=A,fe=Z,X=R),ft&&Wn(C,zn(ke,C),zn(ke+$e,C)),mt)for(let H in z){let Ae=z[H];H!=C&&Ae.from==null&&Ae.min!=Ee&&Wn(H,zn(fe+X,H),zn(fe,H))}Qs()}else W.lock&&(W._lock=!W._lock,fi(d,!0,u!=null));u!=null&&(Di(al,zl),Pr(al,r,De,Ve,le,L,null))}function Ly(u,d,p,g,_,y,x){if(W._lock)return;ui(u);let Z=Vn;if(Vn){let N=!0,R=!0,A=10,F,ie;O.ori==0?(F=ft,ie=mt):(F=mt,ie=ft),F&&ie&&(N=De<=A||De>=le-A,R=Ve<=A||Ve>=L-A),F&&N&&(De=De<Bi?0:le),ie&&R&&(Ve=Ve<Gi?0:L),fi(null,!0,!0),Vn=!1}De=-10,Ve=-10,ze.fill(null),fi(null,!0,!0),Z&&(Vn=Z)}function tf(u,d,p,g,_,y,x){W._lock||(ui(u),js(),Qs(),u!=null&&Pr(Xf,r,De,Ve,le,L,null))}function nf(){T.forEach(xZ),Os(r.width,r.height,!0)}wi(wa,ir,nf);const Xi={};Xi.mousedown=Qd,Xi.mousemove=Bd,Xi.mouseup=ef,Xi.dblclick=tf,Xi.setSeries=(u,d,p,g)=>{let _=it.match[2];p=_(r,d,p),p!=-1&&xn(p,g,!0,!1)},xt&&(zt(Gf,S,Qd),zt(ol,S,Bd),zt(Kf,S,u=>{ui(u),Zr(!1)}),zt(Yf,S,Ly),zt(Xf,S,tf),jl.add(r),r.syncRect=Zr);const Wo=r.hooks=e.hooks||{};function pt(u,d,p){Ws?xr.push([u,d,p]):u in Wo&&Wo[u].forEach(g=>{g.call(null,r,d,p)})}(e.plugins||[]).forEach(u=>{for(let d in u.hooks)Wo[d]=(Wo[d]||[]).concat(u.hooks[d])});const rf=(u,d,p)=>p,it=rt({key:null,setSeries:!1,filters:{pub:um,sub:um},scales:[C,k[1]?k[1].scale:null],match:[cm,cm,rf],values:[null,null]},W.sync);it.match.length==2&&it.match.push(rf),W.sync=it;const of=it.key,el=q$(of);function Pr(u,d,p,g,_,y,x){it.filters.pub(u,d,p,g,_,y,x)&&el.pub(u,d,p,g,_,y,x)}el.sub(r);function My(u,d,p,g,_,y,x){it.filters.sub(u,d,p,g,_,y,x)&&Xi[u](null,d,p,g,_,y,x)}r.pub=My;function Fy(){el.unsub(r),jl.delete(r),ri.clear(),Pl(wa,ir,nf),c.remove(),Se?.remove(),pt("destroy")}r.destroy=Fy;function tl(){pt("init",e,t),Od(t||e.data,!1),K[C]?Hs(C,K[C]):js(),Co=Re.show&&(Re.width>0||Re.height>0),li=tn=!0,Os(e.width,e.height)}return k.forEach(Ud),T.forEach($y),i?i instanceof HTMLElement?(i.appendChild(c),tl()):i(r,tl):tl(),r}St.assign=rt;St.fmtNum=bd;St.rangeNum=Sa;St.rangeLog=xs;St.rangeAsinh=vd;St.orient=Ni;St.pxRatio=we;St.join=v6;St.fmtDate=yd,St.tzDate=T6;St.sync=q$;{St.addGap=cZ,St.clipGaps=Zs;let e=St.paths={points:K$};e.linear=X$,e.stepped=mZ,e.bars=pZ,e.spline=gZ}function Rl(e,t=0){return e<=0?[]:Array.from({length:e},(i,r)=>t+r+1)}function zZ(e,t){return e<=0&&t<=0?[]:[...Rl(e,0),...Rl(t,e)]}function ny(e,t){return t==="failed"||t==="cancelled"||e.kernel_complete===!0?!0:t==="completed"}function dl(e){return`${e} (panel)`}function TZ(e){const t=String(e.training_progress_phase??e.progress_phase??"").toLowerCase();if((t==="missing"?"":t)==="floop"||String(e.job_type??"").toLowerCase().includes("floop"))return!0;const n=e.run_config;return!!(n&&typeof n=="object"&&String(n.model??"").toLowerCase().includes("floop"))}function ZZ(e){const t=e.training_curve_source_batches,i=[],r=t&&typeof t.ordering=="number"?t.ordering:null,n=t&&typeof t.convergence=="number"?t.convergence:null,o=Array.isArray(e.ordering_errors)?e.ordering_errors.length:0,a=Array.isArray(e.convergence_errors)?e.convergence_errors.length:0;r!=null&&r>o&&i.push(`${o} of ${r.toLocaleString()} ordering batch samples`),n!=null&&n>a&&i.push(`${a} of ${n.toLocaleString()} convergence batch samples`);const s=e.te_evaluation,l=s?.te_panel_size,c=s?.te_panel_n_train;if(typeof l=="number"&&typeof c=="number"&&c>0){const $=s?.te_panel_stratified===!0?", stratified by mesh":"";i.push(`TE curve uses a fixed panel of ${l.toLocaleString()} of ${c.toLocaleString()} training rows${$}`)}const m=e.panel_topographic_error,f=e.map_topographic_error;return m!=null&&f!=null&&Number.isFinite(Number(m))&&Number.isFinite(Number(f))&&i.push(`Panel TE ${Number(m).toFixed(4)} · Map TE ${Number(f).toFixed(4)} (full training set)`),i.length===0?null:`${r!=null||n!=null?"Displaying uniformly subsampled QE+TE curves (≤1000 points/phase, joint indices): ":""}${i.join("; ")}.`.replace(/: ;/,":")}function fl(e,t){if(t<=0)return[];if(e.length===t)return e;if(e.length===0)return Array(t).fill(null);if(e.length>=t)return e.slice(0,t);const i=t-e.length;return[...Array(i).fill(null),...e]}function iy(e){return e==="completed"||e==="failed"||e==="cancelled"}function Td(e){const t=String(e.status??"");return t!=="completed"?iy(t):(e.finalize_job_id!=null&&String(e.finalize_job_id)!==""?String(e.finalize_job_id):null)?String(e.finalize_status??"")==="completed":!0}function Al(e){const t=String(e.status??"");if(!iy(t))return!0;if(t!=="completed"||e.finalize_failed===!0)return!1;const i=e.finalize_job_id!=null&&String(e.finalize_job_id)!==""?String(e.finalize_job_id):null,r=e.finalize_status!=null&&String(e.finalize_status)!==""?String(e.finalize_status):null;return i!=null&&r!=="completed"}const En=new ET({name:"Training Monitor",version:"1.0.0"},{},{autoResize:!0});let Um=!1,Nm=!1;const PZ=document.getElementById("progress-fill"),EZ=document.getElementById("progress-pct"),Cl=document.getElementById("elapsed"),UZ=document.getElementById("phase"),oa=document.getElementById("eta"),Ll=document.getElementById("job-id"),Dt=document.getElementById("status-badge"),NZ=document.getElementById("params"),Wt=document.getElementById("advisory-banner");document.getElementById("curve");const Do=document.getElementById("chart-target"),Si=document.getElementById("curve-fallback"),ry=document.getElementById("chart-legend"),OZ=document.getElementById("metric-scalars"),to=document.getElementById("cancel-btn"),hi=document.getElementById("open-results-btn"),nr=document.getElementById("copy-monitor-url"),Om=document.getElementById("open-standalone"),ml=document.getElementById("hitgrid-container"),aa=document.getElementById("hitgrid-canvas"),jZ=document.getElementById("hitgrid-meta"),Ml=5e3,pl=2e3;let tt="",hl=null,Jr=null,qr=null,gl=!1,jm=!1,Kn=null;function Zd(e){return`/viz/results-explorer?mode=standalone&job_id=${encodeURIComponent(e)}`}function oy(e){const t=String(e.id??e.job_id??tt);if(!t||!hi)return;hi.style.display="inline-flex";const i=String(e.status??"")==="completed"&&Td(e);hi.href=Zd(t),i?(hi.classList.remove("nav-btn-disabled"),hi.title="Browse figures for this job"):(hi.classList.add("nav-btn-disabled"),hi.title="Available when training and finalize (if any) complete")}async function DZ(e){if(jm||String(e.status??"")!=="completed"||!Td(e))return;jm=!0,oy(e);const t=String(e.id??e.job_id??tt);if(!t)return;const i=Zd(t);if(kr){window.open(i,"_blank","noopener,noreferrer");return}try{await En.callServerTool({name:"results_explorer",arguments:{job_id:t}})}catch{}}function Cr(e){if(e<60)return`${e}s`;const t=Math.floor(e/60),i=e%60;return t<60?`${t}m ${i}s`:`${Math.floor(t/60)}h ${t%60}m`}function RZ(e){if(Jr)return;hl=e;const t=()=>{if(hl==null)return;const i=Math.max(0,Math.round((Date.now()-hl)/1e3));Cl.textContent=Cr(i)};t(),Jr=setInterval(t,1e3)}function Vr(){Jr&&(clearInterval(Jr),Jr=null)}function AZ(e,t,i){const r=ny(e,t);r&&Vr();const n=e.training_elapsed_sec!=null?Number(e.training_elapsed_sec):e.kernel_elapsed_sec!=null?Number(e.kernel_elapsed_sec):null,o=e.wall_elapsed_sec!=null?Number(e.wall_elapsed_sec):null;if(n!=null&&n>=0)Cl.textContent=r?`Training ${Cr(Math.round(n))}`:Cr(Math.round(n));else if(!r&&e.started_at&&typeof e.started_at=="string"){const s=Date.parse(e.started_at);Number.isNaN(s)||RZ(s)}else e.training_duration_seconds&&(Cl.textContent=`${e.training_duration_seconds}s`);if(r){if(i&&o!=null&&o>=0){const s=n!=null?Math.round(n):null;oa.textContent=s==null||Math.abs(Math.round(o)-s)>=2?`Wall ${Cr(Math.round(o))}`:""}else oa.textContent="";return}const a=e.training_eta_sec!=null?Number(e.training_eta_sec):null;a!=null&&a>0?oa.textContent=`ETA ~${Cr(Math.round(a))}`:oa.textContent=""}function ay(){const e=Do.clientWidth;if(e>0)return Math.max(e,200);const t=En.getHostContext?.()?.containerDimensions,i=t?.width??t?.maxWidth;return Math.max(typeof i=="number"?i:360,200)}function Nn(e){return Array.isArray(e)?e.map(t=>Number(t)).filter(t=>Number.isFinite(t)):[]}function Fl(e){return Nn(e.ordering_errors).length>0||Nn(e.convergence_errors).length>0||Nn(e.ordering_topographic_errors).length>0||Nn(e.convergence_topographic_errors).length>0}function sa(e){return Array(e).fill(null)}function sy(){Kn&&(Kn.destroy(),Kn=null),Do.innerHTML=""}function CZ(e,t){ry.innerHTML=e.map((i,r)=>`<span class="legend-item"><span class="legend-swatch" style="background:${t[r]}"></span>${i}</span>`).join("")}function Dm(e,t,i="Batch sample"){if(sy(),e.length<2)return;Si.style.display="none";const r=getComputedStyle(document.documentElement).getPropertyValue("--accent").trim()||"#4f46e5",n=getComputedStyle(document.documentElement).getPropertyValue("--coral").trim()||"#f97316",o=getComputedStyle(document.documentElement).getPropertyValue("--text2").trim()||"#666",a=getComputedStyle(document.documentElement).getPropertyValue("--border").trim()||"#e0e0e0",s=t.map(f=>({...f,stroke:f.stroke==="accent"?r:f.stroke==="coral"?n:f.stroke})),l={width:ay(),height:160,cursor:{show:!0},select:{show:!1,left:0,top:0,width:0,height:0},scales:{x:{time:!1},y:{auto:!0},y2:{auto:!0}},axes:[{stroke:o,grid:{stroke:a,width:1},font:"11px -apple-system, BlinkMacSystemFont, sans-serif",label:i,labelFont:"11px -apple-system, BlinkMacSystemFont, sans-serif",size:32},{scale:"y",stroke:o,grid:{stroke:a,width:1},font:"11px -apple-system, BlinkMacSystemFont, sans-serif",label:"QE",labelFont:"11px -apple-system, BlinkMacSystemFont, sans-serif",size:48},{scale:"y2",side:1,stroke:o,grid:{show:!1},font:"11px -apple-system, BlinkMacSystemFont, sans-serif",label:"TE",labelFont:"11px -apple-system, BlinkMacSystemFont, sans-serif",size:48}],series:[{},...s.map(f=>({label:f.label,scale:f.scale,stroke:f.stroke,width:f.scale==="y"?2:1.5,dash:f.dash,spanGaps:!1}))]},c=[e,...s.map(f=>f.values)];Kn=new St(l,c,Do),CZ(s.map(f=>f.label),s.map(f=>f.stroke));const m=ZZ(ly);m&&ry.insertAdjacentHTML("beforeend",`<div class="curve-source-note" style="margin-top:6px;font-size:11px;color:var(--text2);">${m}</div>`)}let ly={};function LZ(e,t){ny(e,t);const i=e;ly=i;const r=Nn(i.ordering_errors),n=Nn(i.convergence_errors),o=Nn(i.ordering_topographic_errors),a=Nn(i.convergence_topographic_errors);if(r.length===0&&n.length===0&&o.length===0&&a.length===0)return!1;const s=TZ(i),l=r.length,c=n.length,m=s?Math.max(l,o.length,1):Math.max(l+c,1),f=s?Rl(m):zZ(l,c);if(f.length<2)return!1;if(s){const J=r,k=fl(o,m),T=[{label:"QE (FLooP steps)",values:J,scale:"y",stroke:"accent"}];return o.length>0&&T.push({label:dl("TE (FLooP steps)"),values:k,scale:"y2",stroke:"accent",dash:[4,4]}),Dm(f,T,"FLooP step"),!0}const h=[...r,...sa(c)],$=[...sa(l),...n],S=[...fl(o,l),...sa(c)],U=[...sa(l),...fl(a,c)],D=[];return l>0&&D.push({label:"QE ordering",values:h,scale:"y",stroke:"accent"}),c>0&&D.push({label:"QE convergence",values:$,scale:"y",stroke:"coral"}),o.length>0&&D.push({label:dl("TE ordering"),values:S,scale:"y2",stroke:"accent",dash:[4,4]}),a.length>0&&D.push({label:dl("TE convergence"),values:U,scale:"y2",stroke:"coral",dash:[4,4]}),Dm(f,D,"Batch sample"),!0}function MZ(e){const t=Nn(e.hit_grid),i=Nn(e.hit_grid_dims);if(t.length===0||i.length<2){ml.style.display="none";return}const r=Math.max(1,Math.round(i[0])),n=Math.max(1,Math.round(i[1]));if(r*n!==t.length){ml.style.display="none";return}ml.style.display="block",aa.width=r,aa.height=n,aa.style.height=`${Math.min(220,n*8)}px`;const o=aa.getContext("2d");if(!o)return;const a=t.reduce((U,D)=>D>U?D:U,0)||1,s=getComputedStyle(document.documentElement).getPropertyValue("--accent").trim()||"#4f46e5",l=/^#?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(s.replace("#","")),c=l?parseInt(l[1],16):79,m=l?parseInt(l[2],16):70,f=l?parseInt(l[3],16):229,h=o.createImageData(r,n);let $=0;for(let U=0;U<t.length;U++){const D=t[U]/a;t[U]>0&&$++;const J=U%r,T=(Math.floor(U/r)*r+J)*4;h.data[T]=Math.round(245-D*(245-c)),h.data[T+1]=Math.round(245-D*(245-m)),h.data[T+2]=Math.round(245-D*(245-f)),h.data[T+3]=255}o.putImageData(h,0,0);const S=t.reduce((U,D)=>U+D,0);jZ.textContent=`${r}×${n} nodes · ${$} occupied (${($/t.length*100).toFixed(0)}%) · ${S} sampled hits · max ${a}`}function FZ(e){const t=e.quantization_error??e.qe,i=e.panel_topographic_error??e.epoch_topographic_error??e.topographic_error,r=e.map_topographic_error,n=[];t!=null&&Number.isFinite(Number(t))&&n.push(`QE ${Number(t).toFixed(4)}`),i!=null&&Number.isFinite(Number(i))&&n.push(`Panel TE ${Number(i).toFixed(4)}`),r!=null&&Number.isFinite(Number(r))&&n.push(`Map TE ${Number(r).toFixed(4)}`),OZ.textContent=n.length>0?n.join(" · "):""}function uy(e){return e==="completed"||e==="failed"||e==="cancelled"}function cy(e){const t=e.standaloneUrl;if(t)return t;if(kr&&tt)return`${window.location.origin}/viz/training-monitor?mode=standalone&job_id=${encodeURIComponent(tt)}`}function JZ(e){const t=e.relatedUrls;if(t?.resultsExplorer)return t.resultsExplorer;if(kr&&tt)return`${window.location.origin}/viz/results-explorer?mode=standalone&job_id=${encodeURIComponent(tt)}`}function qZ(e){const t=cy(e);t&&(nr.style.display="inline-block",nr.dataset.url=t,Om.href=t,Om.style.display="inline-block")}function VZ(e,t,i){if(i||t!=="completed")return;const r=JZ(e);r&&(Wt.style.display="block",Wt.innerHTML=`Training complete. <a href="${r}" target="_blank" rel="noreferrer">Open results explorer</a> to browse figures.`)}nr.addEventListener("click",async()=>{const e=nr.dataset.url||cy({standaloneUrl:void 0});if(e)try{await navigator.clipboard.writeText(e),nr.textContent="Copied!",setTimeout(()=>{nr.textContent="Copy monitor URL"},1500)}catch{window.prompt("Copy monitor URL:",e)}});function WZ(e,t){if(kr||Um||Nm||!Td(e))return;const i=String(e.id??e.job_id??tt);if(!i)return;Um=!0;const r=[`Training job ${i} ${t}.`],n=e.quantization_error??e.qe,o=e.map_topographic_error??e.topographic_error,a=e.panel_topographic_error??e.epoch_topographic_error;if(n!=null&&Number.isFinite(Number(n))&&r.push(`Final QE: ${Number(n).toFixed(4)}`),a!=null&&Number.isFinite(Number(a))&&r.push(`Final panel TE: ${Number(a).toFixed(4)}`),o!=null&&Number.isFinite(Number(o))&&r.push(`Final map TE: ${Number(o).toFixed(4)}`),e.finalize_failed===!0)r.push(`Finalize failed${e.finalize_failure_stage?` [${String(e.finalize_failure_stage)}]`:""}: ${String(e.finalize_error??"results not uploaded")}. Results may be unavailable until finalize is retried.`);else if(t==="completed"){r.push(`Use results(get, job_id="${i}") for metrics, then results_explorer(job_id="${i}") to browse figures.`);const s=Zd(i);r.push(`Results explorer: ${s}`)}DZ(e),En.updateModelContext({content:[{type:"text",text:r.join(`
|
|
143
143
|
`)}]}).catch(()=>{Nm=!0})}async function HZ(e){const t=String(e.status??"");if(!uy(t)||Fl(e)||gl)return e;const i=String(e.id??e.job_id??tt);if(!i)return e;gl=!0;try{if(kr){const o=await fetch(`/api/results/${i}/training-log`);if(!o.ok)return e;const a=await o.json();return{...e,...a}}const n=(await En.callServerTool({name:"training_monitor",arguments:{job_id:i,fetch_training_log:!0}})).structuredContent;if(n&&typeof n=="object")return{...e,...n}}catch{gl=!1}return e}function xa(){qr&&(clearTimeout(qr),qr=null)}function Jl(e){xa(),qr=setTimeout(()=>{qr=null,BZ()},e)}async function BZ(){if(tt)try{const e=await En.callServerTool({name:"training_monitor",arguments:{job_id:tt}});await dy(e)}catch{Jl(Ml)}}function dy(e){let t;const i=e.content?.find(o=>o.type==="image");i&&(t=`data:${i.mimeType};base64,${i.data}`);const r=e.structuredContent;if(r&&typeof r=="object"&&r.type==="training-monitor"){if(ql(r,t),String(r.status??"running"),Al(r)){const o=typeof r.refresh_interval_ms=="number"?r.refresh_interval_ms:Ml;Jl(o)}else xa();return}const n=e.content?.find(o=>o.type==="text");if(n?.type==="text"&&n.text)try{const o=JSON.parse(n.text);ql(o,t),Al(o)?Jl(Ml):xa()}catch{t&&(Si.style.display="none",Do.innerHTML=`<img src="${t}" alt="Learning Curve" style="max-width:100%;border-radius:4px;" />`)}}function Rm(e){const t=[];if(e.grid){const i=e.grid;t.push(["Grid",`${i[0]}×${i[1]}`])}if(e.model&&t.push(["Model",String(e.model)]),e.epochs){const i=e.epochs;t.push(["Epochs",Array.isArray(i)?`${i[0]}+${i[1]}`:String(i)])}e.n_features&&t.push(["Features",String(e.n_features)]),e.n_samples&&t.push(["Samples",String(e.n_samples)]),e.dataset_rows&&t.push(["Dataset rows",Number(e.dataset_rows).toLocaleString()]),e.dataset_name&&t.push(["Dataset",String(e.dataset_name)]),e.backend&&t.push(["Backend",String(e.backend)]),e.attempt!=null&&Number(e.attempt)>1&&t.push(["Attempt",String(e.attempt)]),NZ.innerHTML=t.map(([i,r])=>`<div class="param"><span class="param-label">${i}: </span><span class="param-value">${r}</span></div>`).join("")}async function ql(e,t){let i=e;uy(String(e.status??""))&&!Fl(e)&&(i=await HZ(e)),i.id&&(tt=i.id),i.job_id&&(tt=i.job_id),Ll.textContent=tt;let r=i.status??"running";const n=i.finalize_failed===!0,o=i.finalize_job_id!=null&&String(i.finalize_job_id)!==""?String(i.finalize_job_id):null,a=i.finalize_status!=null&&String(i.finalize_status)!==""?String(i.finalize_status):null;r==="completed"&&n?(Dt.textContent="training done · finalize failed",Dt.className="status-badge status-failed",to.style.display="none",Vr(),Wt.style.display="block",Wt.textContent=`Finalize job ${o??"unknown"} failed`+(i.finalize_failure_stage?` [${String(i.finalize_failure_stage)}]`:"")+`: ${String(i.finalize_error??"results not uploaded")}. Poll jobs(status) or retry finalize before results(get).`):r==="completed"&&o&&a!=="completed"?(Dt.textContent="training done · finalizing",Dt.className="status-badge status-warning",Vr(),Wt.style.display="block",Wt.textContent=`Finalize job ${o} (${a??"running"}). Wait for finalize before results(get).`):(Dt.textContent=r,Dt.className=`status-badge status-${r==="completed"?"completed":r==="failed"||r==="cancelled"?"failed":"running"}`,(r==="completed"||r==="failed"||r==="cancelled")&&(to.style.display="none",Vr()));const s=r==="completed"&&n||r==="completed"&&o!=null&&a!=="completed",l=(i.progress??0)*100;PZ.style.width=`${l}%`,EZ.textContent=`${l.toFixed(1)}%`;const c=i.progress_phase!=null&&String(i.progress_phase)!==""?String(i.progress_phase):r==="running"?"preprocessing (pending)":"—";UZ.textContent=c,AZ(i,r,s);const m=i.dataset_rows!=null?Number(i.dataset_rows):null,f=r==="running"&&l<5&&(c.startsWith("download")||c.startsWith("preprocessing")||c.includes("pending"));!s&&f&&m!=null&&m>1e6?(Wt.style.display="block",Wt.textContent=`Loading/normalizing ${m.toLocaleString()} rows — progress may stay near 0% until ingest completes.`):!s&&f&&l<1?(Wt.style.display="block",Wt.textContent="Early preprocessing (download/parse/normalize) — epoch counter starts after ingest completes."):!s&&r==="completed"?VZ(i,r,s):s||(Wt.style.display="none"),Rm(i),i.summary&&Rm(i.summary),oy(i),qZ(i),FZ(i),MZ(i),WZ(i,r),Fl(i)?LZ(i,r):t?(sy(),Si.style.display="none",Do.innerHTML=`<img src="${t}" alt="Learning Curve" style="max-width:100%;border-radius:4px;" />`):Kn||(Si.style.display="block",Si.textContent="Waiting for training data...")}function Am(){const e=new Date().toISOString().replace("T"," ").slice(0,19);Dt.textContent=`cancelled at ${e} UTC`,Dt.className="status-badge status-failed",to.style.display="none",Vr(),xa()}const fy=new URLSearchParams(window.location.search),kr=fy.get("mode")==="standalone",vl=fy.get("job_id");if(kr&&vl){tt=vl,Ll.textContent=vl,to.addEventListener("click",async()=>{if(tt)try{(await fetch(`/api/jobs/${tt}/cancel`,{method:"POST"})).ok?Am():Dt.textContent="cancel failed"}catch{Dt.textContent="cancel failed"}});const e=5;let t=0;const i=o=>{Wt.style.display="block",Wt.textContent=o},r="The local viz port is assigned per MCP session and changes when the proxy restarts. Re-run training_monitor for a fresh URL, or set BARIVIA_VIZ_PORT in your MCP env for a persistent port.",n=async()=>{try{const o=await fetch(`/api/jobs/${tt}`);if(!o.ok){if(t++,o.status===404){i(`Job ${tt} was not found via the local viz server (HTTP 404). It may have been deleted, or this link points at a stale session. ${r}`);return}if(t>=e){i(`Could not load job status (HTTP ${o.status}) after ${t} attempts. ${r}`);return}setTimeout(n,pl);return}t=0;const a=await o.json();await ql(a),Al(a)&&setTimeout(n,pl)}catch{if(t++,t>=e){i(`Cannot reach the local viz server (network error). The MCP proxy may have stopped or restarted. ${r}`);return}setTimeout(n,pl)}};n()}else to.addEventListener("click",async()=>{if(tt)try{await En.callServerTool({name:"jobs",arguments:{action:"cancel",job_id:tt}}),Am(),En.updateModelContext({content:[{type:"text",text:`User cancelled job ${tt}`}]})}catch{Dt.textContent="cancel failed"}}),En.ontoolinput=e=>{const t=e?.arguments??{},i=t.job_id!=null?String(t.job_id):"";i&&(tt=i,Ll.textContent=i),Dt.textContent||(Dt.textContent="loading",Dt.className="status-badge status-running"),Kn||(Si.style.display="block",Si.textContent="Loading training data...")},En.ontoolresult=e=>{dy(e)},En.connect();window.addEventListener("resize",()=>{Kn&&Kn.setSize({width:ay(),height:160})});</script>
|
|
144
144
|
<style rel="stylesheet" crossorigin>.uplot,.uplot *,.uplot *:before,.uplot *:after{box-sizing:border-box}.uplot{font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";line-height:1.5;width:min-content}.u-title{text-align:center;font-size:18px;font-weight:700}.u-wrap{position:relative;-webkit-user-select:none;user-select:none}.u-over,.u-under{position:absolute}.u-under{overflow:hidden}.uplot canvas{display:block;position:relative;width:100%;height:100%}.u-axis{position:absolute}.u-legend{font-size:14px;margin:auto;text-align:center}.u-inline{display:block}.u-inline *{display:inline-block}.u-inline tr{margin-right:16px}.u-legend th{font-weight:600}.u-legend th>*{vertical-align:middle;display:inline-block}.u-legend .u-marker{width:1em;height:1em;margin-right:4px;background-clip:padding-box!important}.u-inline.u-live th:after{content:":";vertical-align:middle}.u-inline:not(.u-live) .u-value{display:none}.u-series>*{padding:4px}.u-series th{cursor:pointer}.u-legend .u-off>*{opacity:.3}.u-select{background:#00000012;position:absolute;pointer-events:none}.u-cursor-x,.u-cursor-y{position:absolute;left:0;top:0;pointer-events:none;will-change:transform}.u-hz .u-cursor-x,.u-vt .u-cursor-y{height:100%;border-right:1px dashed #607D8B}.u-hz .u-cursor-y,.u-vt .u-cursor-x{width:100%;border-bottom:1px dashed #607D8B}.u-cursor-pt{position:absolute;top:0;left:0;border-radius:50%;border:0 solid;pointer-events:none;will-change:transform;background-clip:padding-box!important}.u-axis.u-off,.u-select.u-off,.u-cursor-x.u-off,.u-cursor-y.u-off,.u-cursor-pt.u-off{display:none}</style>
|
|
145
145
|
</head>
|