@dbx-tools/appkit-mastra 0.1.26 → 0.1.28
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 +12 -10
- package/dist/index.d.ts +4 -3
- package/dist/index.js +4 -3
- package/dist/src/agents.js +1 -1
- package/dist/src/chart.d.ts +4 -13
- package/dist/src/chart.js +5 -16
- package/dist/src/config.d.ts +1 -1
- package/dist/src/genie.d.ts +13 -35
- package/dist/src/genie.js +31 -38
- package/dist/src/intercept.d.ts +48 -0
- package/dist/src/intercept.js +167 -0
- package/dist/src/plugin.d.ts +2 -2
- package/dist/src/plugin.js +105 -89
- package/dist/src/serving.d.ts +10 -17
- package/dist/src/serving.js +10 -17
- package/dist/src/statement.d.ts +14 -21
- package/dist/src/statement.js +14 -21
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/index.ts +4 -3
- package/package.json +5 -5
- package/src/agents.ts +1 -1
- package/src/chart.ts +7 -18
- package/src/config.ts +1 -1
- package/src/genie.ts +31 -38
- package/src/intercept.ts +206 -0
- package/src/plugin.ts +121 -90
- package/src/serving.ts +10 -17
- package/src/statement.ts +14 -21
package/dist/src/plugin.js
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* AI SDK transport URL is `/api/mastra/route/chat/<agentId>`.
|
|
28
28
|
*/
|
|
29
29
|
import { genie, getExecutionContext, lakebase, Plugin, toPlugin, } from "@databricks/appkit";
|
|
30
|
-
import { appkitUtils, logUtils } from "@dbx-tools/shared";
|
|
30
|
+
import { appkitUtils, commonUtils, logUtils } from "@dbx-tools/shared";
|
|
31
31
|
import { chatRoute } from "@mastra/ai-sdk";
|
|
32
32
|
import { Mastra } from "@mastra/core/mastra";
|
|
33
33
|
import express from "express";
|
|
@@ -37,6 +37,7 @@ import { historyRoute } from "./history.js";
|
|
|
37
37
|
import { createMemoryBuilder, needsLakebase } from "./memory.js";
|
|
38
38
|
import { buildObservability } from "./observability.js";
|
|
39
39
|
import { attachRoutePatchMiddleware, MastraServer } from "./server.js";
|
|
40
|
+
import { installStreamEventInterceptor, } from "./intercept.js";
|
|
40
41
|
import { clearServingEndpointsCache, listServingEndpoints, resolveServingConfig, } from "./serving.js";
|
|
41
42
|
import { fetchStatementData, isStatementNotFoundError, STATEMENT_ROW_CAP, } from "./statement.js";
|
|
42
43
|
const GENIE_MANIFEST = appkitUtils.data(genie).plugin.manifest;
|
|
@@ -173,8 +174,7 @@ export class MastraPlugin extends Plugin {
|
|
|
173
174
|
modelsPath: `${basePath}/models`,
|
|
174
175
|
historyPath: `${basePath}/route/history`,
|
|
175
176
|
historyPathTemplate: `${basePath}/route/history/:agentId`,
|
|
176
|
-
|
|
177
|
-
statementsPathTemplate: `${basePath}/statements/:statementId`,
|
|
177
|
+
embedPathTemplate: `${basePath}/embed/:type/:id`,
|
|
178
178
|
defaultAgent: this.built?.defaultAgentId ?? FALLBACK_AGENT_ID,
|
|
179
179
|
agents: Object.keys(this.built?.agents ?? {}),
|
|
180
180
|
};
|
|
@@ -192,112 +192,95 @@ export class MastraPlugin extends Plugin {
|
|
|
192
192
|
.then((endpoints) => res.json({ endpoints }))
|
|
193
193
|
.catch(next);
|
|
194
194
|
});
|
|
195
|
-
// `GET /
|
|
196
|
-
//
|
|
197
|
-
//
|
|
198
|
-
//
|
|
199
|
-
//
|
|
200
|
-
//
|
|
201
|
-
//
|
|
202
|
-
// set) or the server-side budget elapses (then returns the
|
|
203
|
-
// last seen still-processing entry so the client can poll
|
|
204
|
-
// again).
|
|
195
|
+
// `GET /embed/:type/:id` is the single resolver for every embed
|
|
196
|
+
// marker the agent emits in prose (`[chart:<id>]`,
|
|
197
|
+
// `[data:<id>]`, ...). `:type` selects a resolver from the
|
|
198
|
+
// registry below; `:id` is that resolver's lookup key. The
|
|
199
|
+
// grammar (see `marker.ts`) is type-agnostic on purpose - new
|
|
200
|
+
// embed kinds are added by registering a resolver here, with no
|
|
201
|
+
// client or grammar change.
|
|
205
202
|
//
|
|
206
203
|
// Status codes:
|
|
207
|
-
// - 200 with JSON body when the
|
|
208
|
-
// - 404 when
|
|
204
|
+
// - 200 with the resolver's JSON body when the id resolves.
|
|
205
|
+
// - 404 when `:type` isn't registered (unsupported embed
|
|
206
|
+
// type) OR a registered resolver can't find `:id` (unknown
|
|
207
|
+
// / expired - e.g. a chart past its 1h TTL or a fabricated
|
|
208
|
+
// id the model never minted).
|
|
209
|
+
// - 400 when `:id` is empty.
|
|
209
210
|
//
|
|
210
|
-
//
|
|
211
|
-
// - `
|
|
212
|
-
//
|
|
211
|
+
// Per-type query knobs and behavior:
|
|
212
|
+
// - `chart`: long-polls the chart cache until the entry
|
|
213
|
+
// settles (`result` / `error`) or the budget elapses (then
|
|
214
|
+
// returns the still-processing entry to poll again).
|
|
215
|
+
// `?timeoutMs=<n>` (default 60s, capped 5min) tunes it.
|
|
216
|
+
// - `data`: one OBO-scoped Statement Execution fetch.
|
|
217
|
+
// `?limit=<n>` caps rows (clamped to STATEMENT_ROW_CAP).
|
|
213
218
|
//
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
219
|
+
// Built once (this handler is registered once) and keyed by the
|
|
220
|
+
// raw `:type` token. Each resolver gets the request (for query
|
|
221
|
+
// parsing + OBO scoping) and an `AbortSignal` bridged off the
|
|
222
|
+
// connection `close` event so a long-poll unblocks the instant
|
|
223
|
+
// the client disconnects. `undefined` from a resolver maps to a
|
|
224
|
+
// clean 404; thrown errors bubble through `next(err)`.
|
|
225
|
+
const embedResolvers = {
|
|
226
|
+
chart: (req, id, signal) => {
|
|
227
|
+
const timeoutMs = parseTimeoutMs(req.query["timeoutMs"]);
|
|
228
|
+
return fetchChart(id, {
|
|
229
|
+
...(timeoutMs !== undefined ? { timeoutMs } : {}),
|
|
230
|
+
signal,
|
|
231
|
+
});
|
|
232
|
+
},
|
|
233
|
+
data: (req, id, signal) => {
|
|
234
|
+
const limit = parseStatementLimit(req.query["limit"]);
|
|
235
|
+
return this.userScopedSelf(req).fetchStatement(id, {
|
|
236
|
+
...(limit !== undefined ? { limit } : {}),
|
|
237
|
+
signal,
|
|
238
|
+
});
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
router.get("/embed/:type/:id", (req, res, next) => {
|
|
242
|
+
const type = req.params["type"] ?? "";
|
|
243
|
+
const id = req.params["id"];
|
|
244
|
+
const resolve = embedResolvers[type];
|
|
245
|
+
if (!resolve) {
|
|
246
|
+
res.status(404).json({ error: `unsupported embed type: ${type}` });
|
|
222
247
|
return;
|
|
223
248
|
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
// `close` event onto an `AbortController` so a closed
|
|
227
|
-
// connection unblocks the long-poll immediately and frees
|
|
228
|
-
// the request thread. The `close` listener also fires on
|
|
229
|
-
// a normal completion, but at that point we no longer
|
|
230
|
-
// care about the abort and the listener is GC'd with the
|
|
231
|
-
// request.
|
|
232
|
-
const controller = new AbortController();
|
|
233
|
-
req.on("close", () => controller.abort());
|
|
234
|
-
fetchChart(chartId, {
|
|
235
|
-
...(timeoutMs !== undefined ? { timeoutMs } : {}),
|
|
236
|
-
signal: controller.signal,
|
|
237
|
-
})
|
|
238
|
-
.then((entry) => {
|
|
239
|
-
if (!entry) {
|
|
240
|
-
res.status(404).json({ error: "chart not found" });
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
res.json(entry);
|
|
244
|
-
})
|
|
245
|
-
.catch(next);
|
|
246
|
-
});
|
|
247
|
-
// `GET /statements/:statementId` resolves a Genie / Statement
|
|
248
|
-
// Execution result for inline rendering as a table.
|
|
249
|
-
//
|
|
250
|
-
// The agent embeds `[data:<statement_id>]` markers in prose
|
|
251
|
-
// (see `GENIE_INSTRUCTIONS`) whenever it wants the host UI to
|
|
252
|
-
// display a result set without the model re-spelling every
|
|
253
|
-
// row in markdown. This route is what those markers resolve
|
|
254
|
-
// against - one OBO-scoped fetch returns the columns + rows
|
|
255
|
-
// so the client renders an AppKit Table inline.
|
|
256
|
-
//
|
|
257
|
-
// Status codes:
|
|
258
|
-
// - 200 with `StatementData` body when the statement is found.
|
|
259
|
-
// - 404 when the workspace can't find / read the statement.
|
|
260
|
-
//
|
|
261
|
-
// Query params:
|
|
262
|
-
// - `limit` (optional): row cap. Clamped to
|
|
263
|
-
// {@link STATEMENT_ROW_CAP} so a runaway result set
|
|
264
|
-
// can't hose the response.
|
|
265
|
-
//
|
|
266
|
-
// OBO-scoped via {@link userScopedSelf} so the caller's
|
|
267
|
-
// workspace permissions apply; SDK errors propagate through
|
|
268
|
-
// `next(err)` to Express's default error handler.
|
|
269
|
-
router.get("/statements/:statementId", (req, res, next) => {
|
|
270
|
-
const statementId = req.params["statementId"];
|
|
271
|
-
if (!statementId) {
|
|
272
|
-
res.status(400).json({ error: "statementId is required" });
|
|
249
|
+
if (!id) {
|
|
250
|
+
res.status(400).json({ error: "id is required" });
|
|
273
251
|
return;
|
|
274
252
|
}
|
|
275
|
-
|
|
253
|
+
// Express's `req` predates `AbortSignal`; bridge the `close`
|
|
254
|
+
// event onto an `AbortController` so a closed connection
|
|
255
|
+
// unblocks any long-poll immediately and frees the request
|
|
256
|
+
// thread. The listener is GC'd with the request on normal
|
|
257
|
+
// completion.
|
|
276
258
|
const controller = new AbortController();
|
|
277
259
|
req.on("close", () => controller.abort());
|
|
278
|
-
|
|
279
|
-
.
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
})
|
|
283
|
-
.then((data) => {
|
|
284
|
-
if (!data) {
|
|
285
|
-
res.status(404).json({ error: "statement not found" });
|
|
260
|
+
resolve(req, id, controller.signal)
|
|
261
|
+
.then((entry) => {
|
|
262
|
+
if (entry === undefined) {
|
|
263
|
+
res.status(404).json({ error: `${type} not found` });
|
|
286
264
|
return;
|
|
287
265
|
}
|
|
288
|
-
res.json(
|
|
266
|
+
res.json(entry);
|
|
289
267
|
})
|
|
290
268
|
.catch(next);
|
|
291
269
|
});
|
|
292
270
|
router.use("", (req, res, next) => {
|
|
293
271
|
if (!this.mastraApp)
|
|
294
272
|
return res.status(503).end();
|
|
273
|
+
// Run each Mastra `/stream` SSE frame through the interceptor
|
|
274
|
+
// (keep / rewrite / drop). Only engages on a
|
|
275
|
+
// `200 text/event-stream` body, so the JSON routes above and any
|
|
276
|
+
// error response are untouched.
|
|
277
|
+
installStreamEventInterceptor(res, interceptStreamFrame);
|
|
295
278
|
return this.userScopedSelf(req).mastraApp(req, res, next);
|
|
296
279
|
});
|
|
297
280
|
}
|
|
298
281
|
/**
|
|
299
|
-
* Implementation backing the
|
|
300
|
-
* Runs inside the AppKit user-context proxy so
|
|
282
|
+
* Implementation backing the `data` embed resolver
|
|
283
|
+
* (`GET /embed/data/:id`). Runs inside the AppKit user-context proxy so
|
|
301
284
|
* `getExecutionContext()` returns the OBO-scoped workspace
|
|
302
285
|
* client, then reuses the same `fetchStatementData` pipeline
|
|
303
286
|
* the `get_statement` tool runs so the LLM and the UI see the
|
|
@@ -427,7 +410,7 @@ export class MastraPlugin extends Plugin {
|
|
|
427
410
|
}
|
|
428
411
|
/**
|
|
429
412
|
* Parse the optional `?timeoutMs=<n>` query parameter from a
|
|
430
|
-
* `GET /
|
|
413
|
+
* `GET /embed/chart/:id` request. Accepts a positive integer up
|
|
431
414
|
* to 5 minutes (clamped) and rejects everything else as
|
|
432
415
|
* `undefined` so {@link fetchChart} falls back to its default.
|
|
433
416
|
* Express produces `string | string[] | undefined`; we normalize
|
|
@@ -444,7 +427,7 @@ function parseTimeoutMs(raw) {
|
|
|
444
427
|
}
|
|
445
428
|
/**
|
|
446
429
|
* Parse the optional `?limit=<n>` query parameter from a
|
|
447
|
-
* `GET /
|
|
430
|
+
* `GET /embed/data/:id` request. Accepts a non-negative
|
|
448
431
|
* integer and lets the route clamp to `STATEMENT_ROW_CAP`;
|
|
449
432
|
* rejects anything else as `undefined` so the route falls back
|
|
450
433
|
* to the server-side cap.
|
|
@@ -458,4 +441,37 @@ function parseStatementLimit(raw) {
|
|
|
458
441
|
return undefined;
|
|
459
442
|
return Math.floor(n);
|
|
460
443
|
}
|
|
444
|
+
/**
|
|
445
|
+
* {@link StreamFrameInterceptor} for Mastra `/stream` responses.
|
|
446
|
+
*
|
|
447
|
+
* Removes large, redundant payloads from terminal `step-finish`,
|
|
448
|
+
* `finish`, and `tool-result` frames before they reach the browser.
|
|
449
|
+
* These frames often repeat information already delivered incrementally
|
|
450
|
+
* via streamed text and tool events, including full tool outputs,
|
|
451
|
+
* accumulated responses, message history, SQL results, chart data, and
|
|
452
|
+
* other large result payloads.
|
|
453
|
+
*
|
|
454
|
+
* The interceptor deletes heavyweight payload properties
|
|
455
|
+
* (`output`, `messages`, `response`, and `result`) while preserving the
|
|
456
|
+
* event envelope and lifecycle metadata required by the client.
|
|
457
|
+
*
|
|
458
|
+
* Deletion is key based, so streams that do not contain these
|
|
459
|
+
* fields are passed through unchanged.
|
|
460
|
+
*/
|
|
461
|
+
const interceptStreamFrame = (chunk) => {
|
|
462
|
+
if (!commonUtils.isRecord(chunk))
|
|
463
|
+
return true;
|
|
464
|
+
if (!["step-finish", "finish", "tool-result"].find((type) => type === chunk.type))
|
|
465
|
+
return true;
|
|
466
|
+
const payload = chunk.payload;
|
|
467
|
+
if (!commonUtils.isRecord(payload))
|
|
468
|
+
return true;
|
|
469
|
+
const trimmedPayload = commonUtils.deleteKeys(payload, [
|
|
470
|
+
"output",
|
|
471
|
+
"messages",
|
|
472
|
+
"response",
|
|
473
|
+
"result",
|
|
474
|
+
]);
|
|
475
|
+
return trimmedPayload ? { replace: chunk } : true;
|
|
476
|
+
};
|
|
461
477
|
export const mastra = toPlugin(MastraPlugin);
|
package/dist/src/serving.d.ts
CHANGED
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Dynamic model resolution against Databricks Model Serving.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* 3. **Per-request override** - {@link extractModelOverride} pulls a
|
|
15
|
-
* model name from the `X-Mastra-Model` header, `?model=` query
|
|
16
|
-
* string, or `model` body field so the same agent can be exercised
|
|
17
|
-
* against different endpoints without redeploying.
|
|
18
|
-
*
|
|
19
|
-
* `model.ts` glues these together inside the per-step model resolver;
|
|
20
|
-
* `plugin.ts` exposes the cached list at `GET /models`.
|
|
4
|
+
* Turns loose, human-typed model names into real serving-endpoint ids
|
|
5
|
+
* so the same agent can target different endpoints without code
|
|
6
|
+
* changes. The workspace's `/serving-endpoints` list is fetched once
|
|
7
|
+
* per host and cached with a TTL, with concurrent callers sharing one
|
|
8
|
+
* in-flight promise (the coalescing pattern of Python's
|
|
9
|
+
* `cachetools-async`). Names are matched through `fuse.js` extended
|
|
10
|
+
* search so tokens like `"claude sonnet"` snap to
|
|
11
|
+
* `databricks-claude-sonnet-4-6`, and a per-request override can be
|
|
12
|
+
* pulled from the request header, query string, or body so a model
|
|
13
|
+
* can be swapped per call without redeploying.
|
|
21
14
|
*/
|
|
22
15
|
import { type getExecutionContext } from "@databricks/appkit";
|
|
23
16
|
import type { ServingEndpointSummary } from "@dbx-tools/appkit-mastra-shared";
|
package/dist/src/serving.js
CHANGED
|
@@ -1,23 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Dynamic model resolution against Databricks Model Serving.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* 3. **Per-request override** - {@link extractModelOverride} pulls a
|
|
15
|
-
* model name from the `X-Mastra-Model` header, `?model=` query
|
|
16
|
-
* string, or `model` body field so the same agent can be exercised
|
|
17
|
-
* against different endpoints without redeploying.
|
|
18
|
-
*
|
|
19
|
-
* `model.ts` glues these together inside the per-step model resolver;
|
|
20
|
-
* `plugin.ts` exposes the cached list at `GET /models`.
|
|
4
|
+
* Turns loose, human-typed model names into real serving-endpoint ids
|
|
5
|
+
* so the same agent can target different endpoints without code
|
|
6
|
+
* changes. The workspace's `/serving-endpoints` list is fetched once
|
|
7
|
+
* per host and cached with a TTL, with concurrent callers sharing one
|
|
8
|
+
* in-flight promise (the coalescing pattern of Python's
|
|
9
|
+
* `cachetools-async`). Names are matched through `fuse.js` extended
|
|
10
|
+
* search so tokens like `"claude sonnet"` snap to
|
|
11
|
+
* `databricks-claude-sonnet-4-6`, and a per-request override can be
|
|
12
|
+
* pulled from the request header, query string, or body so a model
|
|
13
|
+
* can be swapped per call without redeploying.
|
|
21
14
|
*/
|
|
22
15
|
import { CacheManager } from "@databricks/appkit";
|
|
23
16
|
import { logUtils, stringUtils } from "@dbx-tools/shared";
|
package/dist/src/statement.d.ts
CHANGED
|
@@ -1,24 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Databricks Statement Execution helpers for the Mastra plugin.
|
|
3
3
|
*
|
|
4
|
-
* Wraps `client.statementExecution.getStatement` with the shape
|
|
5
|
-
*
|
|
6
|
-
* `/
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* `/statements/:statementId` route) clamp `limit` to so a
|
|
16
|
-
* runaway result set can't hose a response.
|
|
17
|
-
* - {@link isStatementNotFoundError}: structural detector that
|
|
18
|
-
* normalizes the SDK's two error classes plus the loose
|
|
19
|
-
* `does not exist` / `not found` message shapes into a single
|
|
20
|
-
* boolean - lets the route map upstream 404s to a clean
|
|
21
|
-
* HTTP 404 without coupling to SDK error-class identity.
|
|
4
|
+
* Wraps `client.statementExecution.getStatement` with the shape,
|
|
5
|
+
* size, and error handling the plugin's tools and the
|
|
6
|
+
* `/embed/data/:id` route both need: a low-level fetch that returns a
|
|
7
|
+
* raw `{columns, rows, rowCount}` shape and coerces numeric strings
|
|
8
|
+
* to numbers so downstream charts and aggregations don't have to, a
|
|
9
|
+
* hard row cap callers clamp `limit` to so a runaway result set can't
|
|
10
|
+
* hose a response, and a structural not-found detector that
|
|
11
|
+
* normalizes the SDK's error classes and loose `does not exist` /
|
|
12
|
+
* `not found` message shapes into one boolean so the route can map
|
|
13
|
+
* upstream 404s to a clean HTTP 404 without coupling to SDK
|
|
14
|
+
* error-class identity.
|
|
22
15
|
*
|
|
23
16
|
* Not Genie-specific: a Databricks `statement_id` is workspace
|
|
24
17
|
* scoped and lives in the Statement Execution API regardless of
|
|
@@ -30,7 +23,7 @@ import { WorkspaceClient } from "@databricks/sdk-experimental";
|
|
|
30
23
|
import type { GenieDatasetData } from "@dbx-tools/appkit-mastra-shared";
|
|
31
24
|
/**
|
|
32
25
|
* Hard server-side cap on rows returned by the
|
|
33
|
-
* `/
|
|
26
|
+
* `/embed/data/:id` route. Sized to keep responses small
|
|
34
27
|
* enough for inline tables to render snappily; the route surfaces
|
|
35
28
|
* a `truncated` flag whenever the upstream `rowCount` exceeds
|
|
36
29
|
* this so end users know they're seeing a sample.
|
|
@@ -48,7 +41,7 @@ export declare const STATEMENT_ROW_CAP = 500;
|
|
|
48
41
|
*
|
|
49
42
|
* Exported because every consumer in the plugin (the
|
|
50
43
|
* `get_statement` tool, the `prepare_chart` dataset resolver, and
|
|
51
|
-
* the `/
|
|
44
|
+
* the `/embed/data/:id` route) needs the exact same
|
|
52
45
|
* fetch + coercion pipeline so LLM-side `get_statement` output
|
|
53
46
|
* and UI-side `[data:<id>]` rendering stay shape-identical for
|
|
54
47
|
* the same `statement_id`.
|
|
@@ -66,7 +59,7 @@ export declare function fetchStatementData(client: WorkspaceClient, statementId:
|
|
|
66
59
|
* catalogued.
|
|
67
60
|
*
|
|
68
61
|
* Pulled into its own helper so callers (notably the
|
|
69
|
-
* `/
|
|
62
|
+
* `/embed/data/:id` route) stay decoupled from SDK
|
|
70
63
|
* error-class identity, and the conversion logic stays testable
|
|
71
64
|
* in isolation.
|
|
72
65
|
*/
|
package/dist/src/statement.js
CHANGED
|
@@ -1,24 +1,17 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Databricks Statement Execution helpers for the Mastra plugin.
|
|
3
3
|
*
|
|
4
|
-
* Wraps `client.statementExecution.getStatement` with the shape
|
|
5
|
-
*
|
|
6
|
-
* `/
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* `/statements/:statementId` route) clamp `limit` to so a
|
|
16
|
-
* runaway result set can't hose a response.
|
|
17
|
-
* - {@link isStatementNotFoundError}: structural detector that
|
|
18
|
-
* normalizes the SDK's two error classes plus the loose
|
|
19
|
-
* `does not exist` / `not found` message shapes into a single
|
|
20
|
-
* boolean - lets the route map upstream 404s to a clean
|
|
21
|
-
* HTTP 404 without coupling to SDK error-class identity.
|
|
4
|
+
* Wraps `client.statementExecution.getStatement` with the shape,
|
|
5
|
+
* size, and error handling the plugin's tools and the
|
|
6
|
+
* `/embed/data/:id` route both need: a low-level fetch that returns a
|
|
7
|
+
* raw `{columns, rows, rowCount}` shape and coerces numeric strings
|
|
8
|
+
* to numbers so downstream charts and aggregations don't have to, a
|
|
9
|
+
* hard row cap callers clamp `limit` to so a runaway result set can't
|
|
10
|
+
* hose a response, and a structural not-found detector that
|
|
11
|
+
* normalizes the SDK's error classes and loose `does not exist` /
|
|
12
|
+
* `not found` message shapes into one boolean so the route can map
|
|
13
|
+
* upstream 404s to a clean HTTP 404 without coupling to SDK
|
|
14
|
+
* error-class identity.
|
|
22
15
|
*
|
|
23
16
|
* Not Genie-specific: a Databricks `statement_id` is workspace
|
|
24
17
|
* scoped and lives in the Statement Execution API regardless of
|
|
@@ -30,7 +23,7 @@ import { ApiError, HttpError, WorkspaceClient } from "@databricks/sdk-experiment
|
|
|
30
23
|
import { apiUtils } from "@dbx-tools/shared";
|
|
31
24
|
/**
|
|
32
25
|
* Hard server-side cap on rows returned by the
|
|
33
|
-
* `/
|
|
26
|
+
* `/embed/data/:id` route. Sized to keep responses small
|
|
34
27
|
* enough for inline tables to render snappily; the route surfaces
|
|
35
28
|
* a `truncated` flag whenever the upstream `rowCount` exceeds
|
|
36
29
|
* this so end users know they're seeing a sample.
|
|
@@ -63,7 +56,7 @@ function coerceCell(cell) {
|
|
|
63
56
|
*
|
|
64
57
|
* Exported because every consumer in the plugin (the
|
|
65
58
|
* `get_statement` tool, the `prepare_chart` dataset resolver, and
|
|
66
|
-
* the `/
|
|
59
|
+
* the `/embed/data/:id` route) needs the exact same
|
|
67
60
|
* fetch + coercion pipeline so LLM-side `get_statement` output
|
|
68
61
|
* and UI-side `[data:<id>]` rendering stay shape-identical for
|
|
69
62
|
* the same `statement_id`.
|
|
@@ -98,7 +91,7 @@ export async function fetchStatementData(client, statementId, options) {
|
|
|
98
91
|
* catalogued.
|
|
99
92
|
*
|
|
100
93
|
* Pulled into its own helper so callers (notably the
|
|
101
|
-
* `/
|
|
94
|
+
* `/embed/data/:id` route) stay decoupled from SDK
|
|
102
95
|
* error-class identity, and the conversion logic stays testable
|
|
103
96
|
* in isolation.
|
|
104
97
|
*/
|