@dbx-tools/appkit-mastra 0.1.58 → 0.1.67
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 +41 -1
- package/dist/index.d.ts +1229 -17
- package/dist/index.js +3348 -19
- package/package.json +17 -28
- package/src/agents.ts +7 -1
- package/src/config.ts +64 -0
- package/src/genie.ts +9 -21
- package/{index.ts → src/index.ts} +7 -9
- package/src/mcp.ts +89 -0
- package/src/model.ts +9 -3
- package/src/plugin.ts +51 -8
- package/src/serving.ts +5 -9
- package/src/statement.ts +8 -36
- package/dist/src/agents.d.ts +0 -316
- package/dist/src/agents.js +0 -470
- package/dist/src/chart.d.ts +0 -153
- package/dist/src/chart.js +0 -570
- package/dist/src/config.d.ts +0 -295
- package/dist/src/config.js +0 -55
- package/dist/src/genie.d.ts +0 -161
- package/dist/src/genie.js +0 -973
- package/dist/src/history.d.ts +0 -95
- package/dist/src/history.js +0 -278
- package/dist/src/memory.d.ts +0 -109
- package/dist/src/memory.js +0 -253
- package/dist/src/model.d.ts +0 -52
- package/dist/src/model.js +0 -198
- package/dist/src/observability.d.ts +0 -64
- package/dist/src/observability.js +0 -83
- package/dist/src/plugin.d.ts +0 -177
- package/dist/src/plugin.js +0 -554
- package/dist/src/processor.d.ts +0 -8
- package/dist/src/processor.js +0 -40
- package/dist/src/processors/strip-stale-charts.d.ts +0 -32
- package/dist/src/processors/strip-stale-charts.js +0 -98
- package/dist/src/server.d.ts +0 -51
- package/dist/src/server.js +0 -152
- package/dist/src/serving.d.ts +0 -65
- package/dist/src/serving.js +0 -79
- package/dist/src/statement.d.ts +0 -66
- package/dist/src/statement.js +0 -111
- package/dist/src/writer.d.ts +0 -23
- package/dist/src/writer.js +0 -37
- package/dist/tsconfig.build.tsbuildinfo +0 -1
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mastra input processor that strips `chartId` fields from every
|
|
3
|
-
* tool-invocation result in prior assistant messages before they
|
|
4
|
-
* reach the model.
|
|
5
|
-
*
|
|
6
|
-
* Why: chartIds are turn-scoped from the model's point of view -
|
|
7
|
-
* each `prepare_chart` / `render_data` call mints a fresh id and
|
|
8
|
-
* the host UI binds it to that turn's reply. Mastra Memory
|
|
9
|
-
* replays prior tool results into the prompt; if old chartIds
|
|
10
|
-
* leak through, the model is tempted to copy them verbatim into
|
|
11
|
-
* the new turn's `[chart:<id>]` markers and the host UI ends up
|
|
12
|
-
* rendering an unrelated chart from the chart cache (or
|
|
13
|
-
* a 404 once the 1h TTL elapsed). This processor removes the
|
|
14
|
-
* temptation by deleting `chartId` keys from every assistant
|
|
15
|
-
* message's tool results before the prompt is built. The current
|
|
16
|
-
* turn's tool results don't exist yet at `processInput` time, so
|
|
17
|
-
* they pass through unmodified.
|
|
18
|
-
*
|
|
19
|
-
* The strip is recursive - any nested `chartId` field is removed,
|
|
20
|
-
* regardless of which tool produced the result. This covers
|
|
21
|
-
* `prepare_chart` / `render_data` top-level chartIds and any
|
|
22
|
-
* legacy `datasets[].chartId` payloads uniformly without coupling
|
|
23
|
-
* to specific tool ids.
|
|
24
|
-
*/
|
|
25
|
-
import { logUtils } from "@dbx-tools/shared";
|
|
26
|
-
const log = logUtils.logger("mastra/processor/strip-stale-charts");
|
|
27
|
-
/**
|
|
28
|
-
* Recursively clone `value`, omitting any property whose key is
|
|
29
|
-
* `chartId`. Arrays are mapped element-wise; primitives are
|
|
30
|
-
* returned as-is. The result is structurally identical to the
|
|
31
|
-
* input minus chartIds, so downstream message-shape consumers
|
|
32
|
-
* keep working.
|
|
33
|
-
*/
|
|
34
|
-
function stripChartIds(value) {
|
|
35
|
-
if (Array.isArray(value)) {
|
|
36
|
-
return value.map(stripChartIds);
|
|
37
|
-
}
|
|
38
|
-
if (value && typeof value === "object") {
|
|
39
|
-
const obj = value;
|
|
40
|
-
const out = {};
|
|
41
|
-
for (const [key, val] of Object.entries(obj)) {
|
|
42
|
-
if (key === "chartId")
|
|
43
|
-
continue;
|
|
44
|
-
out[key] = stripChartIds(val);
|
|
45
|
-
}
|
|
46
|
-
return out;
|
|
47
|
-
}
|
|
48
|
-
return value;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* Input processor that scrubs `chartId` from every tool-invocation
|
|
52
|
-
* result in the message list. Wired onto every agent by default
|
|
53
|
-
* via {@link buildAgents}; opt out with
|
|
54
|
-
* `MastraPluginConfig.stripStaleCharts: false`.
|
|
55
|
-
*/
|
|
56
|
-
export const stripStaleChartsProcessor = {
|
|
57
|
-
id: "strip-stale-charts",
|
|
58
|
-
description: "Removes chartId fields from prior tool-invocation results so the model can't reuse turn-scoped ids from memory.",
|
|
59
|
-
processInput(args) {
|
|
60
|
-
let stripped = 0;
|
|
61
|
-
for (const message of args.messages) {
|
|
62
|
-
if (message.role !== "assistant")
|
|
63
|
-
continue;
|
|
64
|
-
const parts = message.content?.parts;
|
|
65
|
-
if (!Array.isArray(parts))
|
|
66
|
-
continue;
|
|
67
|
-
for (const part of parts) {
|
|
68
|
-
// Tool-invocation parts hold the persisted tool result.
|
|
69
|
-
// We don't scrub the input args (`rawInput` / `args`) because
|
|
70
|
-
// the chartId there is the model's outgoing claim, not
|
|
71
|
-
// anything it could re-reference; only `result` carries
|
|
72
|
-
// ids that subsequent turns might copy.
|
|
73
|
-
if (part.type !== "tool-invocation") {
|
|
74
|
-
continue;
|
|
75
|
-
}
|
|
76
|
-
const inv = part.toolInvocation;
|
|
77
|
-
if (!inv || inv.result === undefined)
|
|
78
|
-
continue;
|
|
79
|
-
const before = inv.result;
|
|
80
|
-
const after = stripChartIds(before);
|
|
81
|
-
// Cheap structural check via JSON length - the actual
|
|
82
|
-
// strip writes a fresh object only when chartId keys
|
|
83
|
-
// existed, so different stringification length is a
|
|
84
|
-
// reliable signal that something was removed.
|
|
85
|
-
if (typeof before === "object" &&
|
|
86
|
-
before !== null &&
|
|
87
|
-
JSON.stringify(before).length !== JSON.stringify(after).length) {
|
|
88
|
-
inv.result = after;
|
|
89
|
-
stripped += 1;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (stripped > 0) {
|
|
94
|
-
log.debug("stripped", { results: stripped });
|
|
95
|
-
}
|
|
96
|
-
return args.messages;
|
|
97
|
-
},
|
|
98
|
-
};
|
package/dist/src/server.d.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Express-layer plumbing for the Mastra plugin: a `MastraServer` that
|
|
3
|
-
* stamps the per-request `RequestContext`, and a route-patch middleware
|
|
4
|
-
* that lets the plugin's custom API routes (e.g. `historyRoute`) work
|
|
5
|
-
* behind an Express mount point.
|
|
6
|
-
*/
|
|
7
|
-
import { type RequestContext } from "@mastra/core/request-context";
|
|
8
|
-
import { MastraServer as MastraServerExpress } from "@mastra/express";
|
|
9
|
-
import type express from "express";
|
|
10
|
-
import { type MastraPluginConfig } from "./config.js";
|
|
11
|
-
/**
|
|
12
|
-
* `@mastra/express` subclass that stamps `RequestContext` with the
|
|
13
|
-
* AppKit user, resource id, and a thread id backed by an HTTP-only
|
|
14
|
-
* session cookie (`appkit_<plugin-name>_session_id`).
|
|
15
|
-
*/
|
|
16
|
-
export declare class MastraServer extends MastraServerExpress {
|
|
17
|
-
private config;
|
|
18
|
-
private log;
|
|
19
|
-
constructor(config: MastraPluginConfig, ...args: ConstructorParameters<typeof MastraServerExpress>);
|
|
20
|
-
registerAuthMiddleware(): void;
|
|
21
|
-
configureRequestContextUser(requestContext: RequestContext): void;
|
|
22
|
-
/**
|
|
23
|
-
* Stamp a per-request id and echo it on the response so an upstream
|
|
24
|
-
* proxy / curl client / browser-side log line can pair its view of
|
|
25
|
-
* the request with the matching trace span. Reuses `X-Request-Id`
|
|
26
|
-
* when the upstream already supplies one so multi-hop traces stay
|
|
27
|
-
* joined; otherwise mints a UUIDv4.
|
|
28
|
-
*
|
|
29
|
-
* The id is surfaced as `mastra__requestId` span metadata via
|
|
30
|
-
* {@link TRACE_REQUEST_CONTEXT_KEYS} and as the `X-Request-Id`
|
|
31
|
-
* response header so dev tools can copy it from either side.
|
|
32
|
-
*/
|
|
33
|
-
configureRequestContextRequestId(req: express.Request, res: express.Response, requestContext: RequestContext): void;
|
|
34
|
-
configureRequestContextThreadId(req: express.Request, res: express.Response, requestContext: RequestContext): void;
|
|
35
|
-
configureRequestContextModelOverride(req: express.Request, requestContext: RequestContext): void;
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Patches around `@mastra/express`'s custom-route dispatcher so the
|
|
39
|
-
* plugin's custom API routes (e.g. `historyRoute`) work when
|
|
40
|
-
* `MastraServer` is hosted on an Express subapp mounted under a parent
|
|
41
|
-
* path (e.g. `/api/mastra`).
|
|
42
|
-
*
|
|
43
|
-
* The adapter's `registerCustomApiRoutes` matches against `req.path`
|
|
44
|
-
* (mount-relative, correct) but dispatches to its internal Hono
|
|
45
|
-
* mini-app using `req.originalUrl`, which still contains the parent
|
|
46
|
-
* mount prefix. The Hono app registers the literal route paths
|
|
47
|
-
* (for example `/route/history`), so the absolute URL never matches
|
|
48
|
-
* until we overwrite `originalUrl` for `/route` and `/route/*` to the
|
|
49
|
-
* mount-relative path.
|
|
50
|
-
*/
|
|
51
|
-
export declare function attachRoutePatchMiddleware(app: express.Express): void;
|
package/dist/src/server.js
DELETED
|
@@ -1,152 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Express-layer plumbing for the Mastra plugin: a `MastraServer` that
|
|
3
|
-
* stamps the per-request `RequestContext`, and a route-patch middleware
|
|
4
|
-
* that lets the plugin's custom API routes (e.g. `historyRoute`) work
|
|
5
|
-
* behind an Express mount point.
|
|
6
|
-
*/
|
|
7
|
-
import { getExecutionContext } from "@databricks/appkit";
|
|
8
|
-
import { httpUtils, logUtils, stringUtils } from "@dbx-tools/shared";
|
|
9
|
-
import { MASTRA_RESOURCE_ID_KEY, MASTRA_THREAD_ID_KEY, } from "@mastra/core/request-context";
|
|
10
|
-
import { MastraServer as MastraServerExpress } from "@mastra/express";
|
|
11
|
-
import { randomUUID } from "node:crypto";
|
|
12
|
-
import { MASTRA_REQUEST_ID_KEY, MASTRA_USER_EMAIL_KEY, MASTRA_USER_KEY, MASTRA_USER_NAME_KEY, } from "./config.js";
|
|
13
|
-
import { extractModelOverride, MASTRA_MODEL_OVERRIDE_KEY, resolveServingConfig, } from "./serving.js";
|
|
14
|
-
/**
|
|
15
|
-
* `@mastra/express` subclass that stamps `RequestContext` with the
|
|
16
|
-
* AppKit user, resource id, and a thread id backed by an HTTP-only
|
|
17
|
-
* session cookie (`appkit_<plugin-name>_session_id`).
|
|
18
|
-
*/
|
|
19
|
-
export class MastraServer extends MastraServerExpress {
|
|
20
|
-
config;
|
|
21
|
-
log;
|
|
22
|
-
constructor(config, ...args) {
|
|
23
|
-
super(...args);
|
|
24
|
-
this.config = config;
|
|
25
|
-
this.log = logUtils.logger(config);
|
|
26
|
-
}
|
|
27
|
-
registerAuthMiddleware() {
|
|
28
|
-
super.registerAuthMiddleware();
|
|
29
|
-
this.app.use((req, res, next) => {
|
|
30
|
-
const requestContext = res.locals.requestContext;
|
|
31
|
-
this.configureRequestContextUser(requestContext);
|
|
32
|
-
this.configureRequestContextThreadId(req, res, requestContext);
|
|
33
|
-
this.configureRequestContextModelOverride(req, requestContext);
|
|
34
|
-
this.configureRequestContextRequestId(req, res, requestContext);
|
|
35
|
-
this.log.debug("auth:middleware", {
|
|
36
|
-
method: req.method,
|
|
37
|
-
path: req.path,
|
|
38
|
-
requestId: requestContext.get(MASTRA_REQUEST_ID_KEY),
|
|
39
|
-
threadId: requestContext.get(MASTRA_THREAD_ID_KEY),
|
|
40
|
-
resourceId: requestContext.get(MASTRA_RESOURCE_ID_KEY),
|
|
41
|
-
userName: requestContext.get(MASTRA_USER_NAME_KEY),
|
|
42
|
-
userEmail: requestContext.get(MASTRA_USER_EMAIL_KEY),
|
|
43
|
-
modelOverride: requestContext.get(
|
|
44
|
-
// imported below; logged so a misrouted request shows
|
|
45
|
-
// up alongside its model selection in `LOG_LEVEL=debug`.
|
|
46
|
-
"mastra__model_override"),
|
|
47
|
-
});
|
|
48
|
-
next();
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
configureRequestContextUser(requestContext) {
|
|
52
|
-
if ([MASTRA_USER_KEY, MASTRA_RESOURCE_ID_KEY].every((key) => requestContext.get(key)))
|
|
53
|
-
return;
|
|
54
|
-
const executionContext = getExecutionContext();
|
|
55
|
-
const user = {
|
|
56
|
-
id: "userId" in executionContext
|
|
57
|
-
? executionContext.userId
|
|
58
|
-
: executionContext.serviceUserId,
|
|
59
|
-
executionContext,
|
|
60
|
-
};
|
|
61
|
-
requestContext.set(MASTRA_USER_KEY, user);
|
|
62
|
-
requestContext.set(MASTRA_RESOURCE_ID_KEY, user.id);
|
|
63
|
-
// AppKit's `UserContext` surfaces display name / email only on
|
|
64
|
-
// OBO requests. Service-context calls (background tasks, server
|
|
65
|
-
// start-up) leave these undefined and we skip the stamp so
|
|
66
|
-
// downstream trace metadata stays absent rather than empty.
|
|
67
|
-
if ("isUserContext" in executionContext) {
|
|
68
|
-
if (executionContext.userName) {
|
|
69
|
-
requestContext.set(MASTRA_USER_NAME_KEY, executionContext.userName);
|
|
70
|
-
}
|
|
71
|
-
if (executionContext.userEmail) {
|
|
72
|
-
requestContext.set(MASTRA_USER_EMAIL_KEY, executionContext.userEmail);
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Stamp a per-request id and echo it on the response so an upstream
|
|
78
|
-
* proxy / curl client / browser-side log line can pair its view of
|
|
79
|
-
* the request with the matching trace span. Reuses `X-Request-Id`
|
|
80
|
-
* when the upstream already supplies one so multi-hop traces stay
|
|
81
|
-
* joined; otherwise mints a UUIDv4.
|
|
82
|
-
*
|
|
83
|
-
* The id is surfaced as `mastra__requestId` span metadata via
|
|
84
|
-
* {@link TRACE_REQUEST_CONTEXT_KEYS} and as the `X-Request-Id`
|
|
85
|
-
* response header so dev tools can copy it from either side.
|
|
86
|
-
*/
|
|
87
|
-
configureRequestContextRequestId(req, res, requestContext) {
|
|
88
|
-
if (requestContext.get(MASTRA_REQUEST_ID_KEY))
|
|
89
|
-
return;
|
|
90
|
-
const headerValue = req.headers["x-request-id"];
|
|
91
|
-
const upstream = Array.isArray(headerValue) ? headerValue[0] : headerValue;
|
|
92
|
-
const requestId = upstream?.trim() || randomUUID();
|
|
93
|
-
requestContext.set(MASTRA_REQUEST_ID_KEY, requestId);
|
|
94
|
-
res.setHeader("X-Request-Id", requestId);
|
|
95
|
-
}
|
|
96
|
-
configureRequestContextThreadId(req, res, requestContext) {
|
|
97
|
-
if (requestContext.get(MASTRA_THREAD_ID_KEY))
|
|
98
|
-
return;
|
|
99
|
-
const cookies = httpUtils.parseCookies(req.headers.cookie);
|
|
100
|
-
const cookieName = stringUtils.toIdentifierWithOptions({ delimiter: "_", distinct: true }, "appkit", this.config.name, "sessionId");
|
|
101
|
-
let sessionId = cookies[cookieName];
|
|
102
|
-
if (!sessionId) {
|
|
103
|
-
sessionId = randomUUID();
|
|
104
|
-
res.cookie(cookieName, sessionId, {
|
|
105
|
-
httpOnly: true,
|
|
106
|
-
sameSite: "lax",
|
|
107
|
-
secure: req.secure,
|
|
108
|
-
path: "/",
|
|
109
|
-
});
|
|
110
|
-
}
|
|
111
|
-
requestContext.set(MASTRA_THREAD_ID_KEY, sessionId);
|
|
112
|
-
}
|
|
113
|
-
configureRequestContextModelOverride(req, requestContext) {
|
|
114
|
-
// Per-request model override: only honored when the plugin
|
|
115
|
-
// opts in (default). Sources, in priority order, are
|
|
116
|
-
// `X-Mastra-Model` header, `?model=` query, and `model` /
|
|
117
|
-
// `modelId` body field; see `serving.ts`.
|
|
118
|
-
const serving = resolveServingConfig(this.config);
|
|
119
|
-
if (serving.allowOverride) {
|
|
120
|
-
const override = extractModelOverride({
|
|
121
|
-
headers: req.headers,
|
|
122
|
-
query: req.query,
|
|
123
|
-
body: req.body,
|
|
124
|
-
});
|
|
125
|
-
if (override)
|
|
126
|
-
requestContext.set(MASTRA_MODEL_OVERRIDE_KEY, override);
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
/**
|
|
131
|
-
* Patches around `@mastra/express`'s custom-route dispatcher so the
|
|
132
|
-
* plugin's custom API routes (e.g. `historyRoute`) work when
|
|
133
|
-
* `MastraServer` is hosted on an Express subapp mounted under a parent
|
|
134
|
-
* path (e.g. `/api/mastra`).
|
|
135
|
-
*
|
|
136
|
-
* The adapter's `registerCustomApiRoutes` matches against `req.path`
|
|
137
|
-
* (mount-relative, correct) but dispatches to its internal Hono
|
|
138
|
-
* mini-app using `req.originalUrl`, which still contains the parent
|
|
139
|
-
* mount prefix. The Hono app registers the literal route paths
|
|
140
|
-
* (for example `/route/history`), so the absolute URL never matches
|
|
141
|
-
* until we overwrite `originalUrl` for `/route` and `/route/*` to the
|
|
142
|
-
* mount-relative path.
|
|
143
|
-
*/
|
|
144
|
-
export function attachRoutePatchMiddleware(app) {
|
|
145
|
-
app.use((req, _res, next) => {
|
|
146
|
-
const isCustomRoute = req.path === "/route" || req.path.startsWith("/route/");
|
|
147
|
-
if (!isCustomRoute)
|
|
148
|
-
return next();
|
|
149
|
-
req.originalUrl = req.path;
|
|
150
|
-
next();
|
|
151
|
-
});
|
|
152
|
-
}
|
package/dist/src/serving.d.ts
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mastra-specific glue over the generic `@dbx-tools/model` toolkit.
|
|
3
|
-
*
|
|
4
|
-
* The live `/serving-endpoints` catalogue access, fuzzy name
|
|
5
|
-
* resolution, and class/fallback selection all live in
|
|
6
|
-
* `@dbx-tools/model`; this module only adds what is specific to the
|
|
7
|
-
* Mastra plugin: pulling a per-request model override off an HTTP
|
|
8
|
-
* request (header / query / body) and projecting the plugin config
|
|
9
|
-
* onto the knobs `buildModel` and the `/models` route share.
|
|
10
|
-
*/
|
|
11
|
-
import type { MastraPluginConfig } from "./config.js";
|
|
12
|
-
/**
|
|
13
|
-
* `RequestContext` key under which {@link MastraServer} stores the
|
|
14
|
-
* per-request model override (header / query / body). `model.ts`
|
|
15
|
-
* reads it before falling back to the agent / plugin default.
|
|
16
|
-
*/
|
|
17
|
-
export declare const MASTRA_MODEL_OVERRIDE_KEY = "mastra__model_override";
|
|
18
|
-
/** HTTP header inspected for a per-request model override. */
|
|
19
|
-
export declare const MODEL_OVERRIDE_HEADER = "x-mastra-model";
|
|
20
|
-
/** Query string parameter inspected for a per-request model override. */
|
|
21
|
-
export declare const MODEL_OVERRIDE_QUERY = "model";
|
|
22
|
-
/** Body fields (in priority order) inspected for a per-request model override. */
|
|
23
|
-
export declare const MODEL_OVERRIDE_BODY_FIELDS: readonly ["model", "modelId"];
|
|
24
|
-
/**
|
|
25
|
-
* Minimal Express-ish request shape used by {@link extractModelOverride}.
|
|
26
|
-
* Keeps this module independent of `express` so the helper can be
|
|
27
|
-
* reused from non-Express adapters.
|
|
28
|
-
*/
|
|
29
|
-
export interface ModelOverrideRequest {
|
|
30
|
-
headers?: Record<string, string | string[] | undefined>;
|
|
31
|
-
query?: Record<string, unknown> | undefined;
|
|
32
|
-
body?: unknown;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* Pull a model override out of a single HTTP request, checking
|
|
36
|
-
* sources in priority order:
|
|
37
|
-
*
|
|
38
|
-
* 1. `X-Mastra-Model` header
|
|
39
|
-
* 2. `?model=` query string parameter
|
|
40
|
-
* 3. Body field (`model` or `modelId`, in that order)
|
|
41
|
-
*
|
|
42
|
-
* Returns `null` when nothing is set, so callers can wrap with
|
|
43
|
-
* `if (override) ...` without juggling empty strings. Body inspection
|
|
44
|
-
* is lenient - any plain object with one of the configured keys
|
|
45
|
-
* counts, mirroring how AI SDK chat clients pass arbitrary metadata
|
|
46
|
-
* alongside `messages`.
|
|
47
|
-
*/
|
|
48
|
-
export declare function extractModelOverride(req: ModelOverrideRequest): string | null;
|
|
49
|
-
/**
|
|
50
|
-
* Read the fuzzy-resolution config knobs off the plugin config with
|
|
51
|
-
* `@dbx-tools/model` defaults applied. Kept here so `buildModel` and
|
|
52
|
-
* the `/models` route agree on what "enabled" means.
|
|
53
|
-
*
|
|
54
|
-
* `fallbacks` is the priority-ordered list `resolveModel` walks first
|
|
55
|
-
* when nothing explicit is set; defaults to an empty list so the
|
|
56
|
-
* generic resolver falls through to the live catalogue and its own
|
|
57
|
-
* `FALLBACK_MODEL_IDS` floor.
|
|
58
|
-
*/
|
|
59
|
-
export declare function resolveServingConfig(config: MastraPluginConfig): {
|
|
60
|
-
ttlMs: number;
|
|
61
|
-
threshold: number;
|
|
62
|
-
fuzzy: boolean;
|
|
63
|
-
allowOverride: boolean;
|
|
64
|
-
fallbacks: readonly string[];
|
|
65
|
-
};
|
package/dist/src/serving.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mastra-specific glue over the generic `@dbx-tools/model` toolkit.
|
|
3
|
-
*
|
|
4
|
-
* The live `/serving-endpoints` catalogue access, fuzzy name
|
|
5
|
-
* resolution, and class/fallback selection all live in
|
|
6
|
-
* `@dbx-tools/model`; this module only adds what is specific to the
|
|
7
|
-
* Mastra plugin: pulling a per-request model override off an HTTP
|
|
8
|
-
* request (header / query / body) and projecting the plugin config
|
|
9
|
-
* onto the knobs `buildModel` and the `/models` route share.
|
|
10
|
-
*/
|
|
11
|
-
import { DEFAULT_FUZZY_THRESHOLD, DEFAULT_MODEL_CACHE_TTL_MS } from "@dbx-tools/model";
|
|
12
|
-
import { stringUtils } from "@dbx-tools/shared";
|
|
13
|
-
/**
|
|
14
|
-
* `RequestContext` key under which {@link MastraServer} stores the
|
|
15
|
-
* per-request model override (header / query / body). `model.ts`
|
|
16
|
-
* reads it before falling back to the agent / plugin default.
|
|
17
|
-
*/
|
|
18
|
-
export const MASTRA_MODEL_OVERRIDE_KEY = "mastra__model_override";
|
|
19
|
-
/** HTTP header inspected for a per-request model override. */
|
|
20
|
-
export const MODEL_OVERRIDE_HEADER = "x-mastra-model";
|
|
21
|
-
/** Query string parameter inspected for a per-request model override. */
|
|
22
|
-
export const MODEL_OVERRIDE_QUERY = "model";
|
|
23
|
-
/** Body fields (in priority order) inspected for a per-request model override. */
|
|
24
|
-
export const MODEL_OVERRIDE_BODY_FIELDS = ["model", "modelId"];
|
|
25
|
-
/**
|
|
26
|
-
* Pull a model override out of a single HTTP request, checking
|
|
27
|
-
* sources in priority order:
|
|
28
|
-
*
|
|
29
|
-
* 1. `X-Mastra-Model` header
|
|
30
|
-
* 2. `?model=` query string parameter
|
|
31
|
-
* 3. Body field (`model` or `modelId`, in that order)
|
|
32
|
-
*
|
|
33
|
-
* Returns `null` when nothing is set, so callers can wrap with
|
|
34
|
-
* `if (override) ...` without juggling empty strings. Body inspection
|
|
35
|
-
* is lenient - any plain object with one of the configured keys
|
|
36
|
-
* counts, mirroring how AI SDK chat clients pass arbitrary metadata
|
|
37
|
-
* alongside `messages`.
|
|
38
|
-
*/
|
|
39
|
-
export function extractModelOverride(req) {
|
|
40
|
-
const headers = req.headers;
|
|
41
|
-
if (headers) {
|
|
42
|
-
const headerVal = stringUtils.firstNonEmpty(headers[MODEL_OVERRIDE_HEADER] ?? headers[MODEL_OVERRIDE_HEADER.toLowerCase()]);
|
|
43
|
-
if (headerVal)
|
|
44
|
-
return headerVal;
|
|
45
|
-
}
|
|
46
|
-
if (req.query) {
|
|
47
|
-
const queryVal = stringUtils.firstNonEmpty(req.query[MODEL_OVERRIDE_QUERY]);
|
|
48
|
-
if (queryVal)
|
|
49
|
-
return queryVal;
|
|
50
|
-
}
|
|
51
|
-
if (req.body && typeof req.body === "object") {
|
|
52
|
-
const record = req.body;
|
|
53
|
-
for (const field of MODEL_OVERRIDE_BODY_FIELDS) {
|
|
54
|
-
const bodyVal = stringUtils.firstNonEmpty(record[field]);
|
|
55
|
-
if (bodyVal)
|
|
56
|
-
return bodyVal;
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
/**
|
|
62
|
-
* Read the fuzzy-resolution config knobs off the plugin config with
|
|
63
|
-
* `@dbx-tools/model` defaults applied. Kept here so `buildModel` and
|
|
64
|
-
* the `/models` route agree on what "enabled" means.
|
|
65
|
-
*
|
|
66
|
-
* `fallbacks` is the priority-ordered list `resolveModel` walks first
|
|
67
|
-
* when nothing explicit is set; defaults to an empty list so the
|
|
68
|
-
* generic resolver falls through to the live catalogue and its own
|
|
69
|
-
* `FALLBACK_MODEL_IDS` floor.
|
|
70
|
-
*/
|
|
71
|
-
export function resolveServingConfig(config) {
|
|
72
|
-
return {
|
|
73
|
-
ttlMs: config.modelCacheTtlMs ?? DEFAULT_MODEL_CACHE_TTL_MS,
|
|
74
|
-
threshold: config.modelFuzzyThreshold ?? DEFAULT_FUZZY_THRESHOLD,
|
|
75
|
-
fuzzy: config.modelFuzzyMatch !== false,
|
|
76
|
-
allowOverride: config.modelOverride !== false,
|
|
77
|
-
fallbacks: config.defaultModelFallbacks ?? [],
|
|
78
|
-
};
|
|
79
|
-
}
|
package/dist/src/statement.d.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Databricks Statement Execution helpers for the Mastra plugin.
|
|
3
|
-
*
|
|
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.
|
|
15
|
-
*
|
|
16
|
-
* Not Genie-specific: a Databricks `statement_id` is workspace
|
|
17
|
-
* scoped and lives in the Statement Execution API regardless of
|
|
18
|
-
* which producer (Genie, a tool, a notebook, etc.) submitted the
|
|
19
|
-
* query. Co-located here so consumers can fetch / cap / handle
|
|
20
|
-
* 404s without reaching into the Genie tool module.
|
|
21
|
-
*/
|
|
22
|
-
import { WorkspaceClient } from "@databricks/sdk-experimental";
|
|
23
|
-
import type { GenieDatasetData } from "@dbx-tools/appkit-mastra-shared";
|
|
24
|
-
/**
|
|
25
|
-
* Hard server-side cap on rows returned by the
|
|
26
|
-
* `/embed/data/:id` route. Sized to keep responses small
|
|
27
|
-
* enough for inline tables to render snappily; the route surfaces
|
|
28
|
-
* a `truncated` flag whenever the upstream `rowCount` exceeds
|
|
29
|
-
* this so end users know they're seeing a sample.
|
|
30
|
-
*/
|
|
31
|
-
export declare const STATEMENT_ROW_CAP = 500;
|
|
32
|
-
/**
|
|
33
|
-
* Fetch a single statement's rows via the Statement Execution API
|
|
34
|
-
* and reshape into the shared {@link GenieDatasetData} shape
|
|
35
|
-
* (column array + row records).
|
|
36
|
-
*
|
|
37
|
-
* Optional `limit` slices the returned `rows` client-side so the
|
|
38
|
-
* agent can scan a small sample without paging the full result
|
|
39
|
-
* set into context. `rowCount` always reflects the upstream total
|
|
40
|
-
* so callers know when the slice truncated.
|
|
41
|
-
*
|
|
42
|
-
* Exported because every consumer in the plugin (the
|
|
43
|
-
* `get_statement` tool, the `prepare_chart` dataset resolver, and
|
|
44
|
-
* the `/embed/data/:id` route) needs the exact same
|
|
45
|
-
* fetch + coercion pipeline so LLM-side `get_statement` output
|
|
46
|
-
* and UI-side `[data:<id>]` rendering stay shape-identical for
|
|
47
|
-
* the same `statement_id`.
|
|
48
|
-
*/
|
|
49
|
-
export declare function fetchStatementData(client: WorkspaceClient, statementId: string, options?: {
|
|
50
|
-
limit?: number;
|
|
51
|
-
signal?: AbortSignal;
|
|
52
|
-
}): Promise<GenieDatasetData>;
|
|
53
|
-
/**
|
|
54
|
-
* True when `err` looks like the Databricks SDK's "statement not
|
|
55
|
-
* found" error. Matches the typed {@link ApiError} 404 /
|
|
56
|
-
* `RESOURCE_DOES_NOT_EXIST` shape first, then falls back to the
|
|
57
|
-
* lower-level {@link HttpError} 404, then to a loose `does not
|
|
58
|
-
* exist` / `not found` message sniff for SDK shapes we haven't
|
|
59
|
-
* catalogued.
|
|
60
|
-
*
|
|
61
|
-
* Pulled into its own helper so callers (notably the
|
|
62
|
-
* `/embed/data/:id` route) stay decoupled from SDK
|
|
63
|
-
* error-class identity, and the conversion logic stays testable
|
|
64
|
-
* in isolation.
|
|
65
|
-
*/
|
|
66
|
-
export declare function isStatementNotFoundError(err: unknown): boolean;
|
package/dist/src/statement.js
DELETED
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Databricks Statement Execution helpers for the Mastra plugin.
|
|
3
|
-
*
|
|
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.
|
|
15
|
-
*
|
|
16
|
-
* Not Genie-specific: a Databricks `statement_id` is workspace
|
|
17
|
-
* scoped and lives in the Statement Execution API regardless of
|
|
18
|
-
* which producer (Genie, a tool, a notebook, etc.) submitted the
|
|
19
|
-
* query. Co-located here so consumers can fetch / cap / handle
|
|
20
|
-
* 404s without reaching into the Genie tool module.
|
|
21
|
-
*/
|
|
22
|
-
import { ApiError, HttpError, WorkspaceClient } from "@databricks/sdk-experimental";
|
|
23
|
-
import { apiUtils } from "@dbx-tools/shared";
|
|
24
|
-
/**
|
|
25
|
-
* Hard server-side cap on rows returned by the
|
|
26
|
-
* `/embed/data/:id` route. Sized to keep responses small
|
|
27
|
-
* enough for inline tables to render snappily; the route surfaces
|
|
28
|
-
* a `truncated` flag whenever the upstream `rowCount` exceeds
|
|
29
|
-
* this so end users know they're seeing a sample.
|
|
30
|
-
*/
|
|
31
|
-
export const STATEMENT_ROW_CAP = 500;
|
|
32
|
-
/**
|
|
33
|
-
* Best-effort numeric coercion for the Statement Execution API's
|
|
34
|
-
* all-strings cells. Leaves non-numeric strings (and explicit
|
|
35
|
-
* `null`s) intact; everything else flows through `Number`.
|
|
36
|
-
*/
|
|
37
|
-
function coerceCell(cell) {
|
|
38
|
-
if (cell === null)
|
|
39
|
-
return null;
|
|
40
|
-
if (/^-?\d+(\.\d+)?$/.test(cell)) {
|
|
41
|
-
const n = Number(cell);
|
|
42
|
-
if (Number.isFinite(n))
|
|
43
|
-
return n;
|
|
44
|
-
}
|
|
45
|
-
return cell;
|
|
46
|
-
}
|
|
47
|
-
/**
|
|
48
|
-
* Fetch a single statement's rows via the Statement Execution API
|
|
49
|
-
* and reshape into the shared {@link GenieDatasetData} shape
|
|
50
|
-
* (column array + row records).
|
|
51
|
-
*
|
|
52
|
-
* Optional `limit` slices the returned `rows` client-side so the
|
|
53
|
-
* agent can scan a small sample without paging the full result
|
|
54
|
-
* set into context. `rowCount` always reflects the upstream total
|
|
55
|
-
* so callers know when the slice truncated.
|
|
56
|
-
*
|
|
57
|
-
* Exported because every consumer in the plugin (the
|
|
58
|
-
* `get_statement` tool, the `prepare_chart` dataset resolver, and
|
|
59
|
-
* the `/embed/data/:id` route) needs the exact same
|
|
60
|
-
* fetch + coercion pipeline so LLM-side `get_statement` output
|
|
61
|
-
* and UI-side `[data:<id>]` rendering stay shape-identical for
|
|
62
|
-
* the same `statement_id`.
|
|
63
|
-
*/
|
|
64
|
-
export async function fetchStatementData(client, statementId, options) {
|
|
65
|
-
const ctx = options?.signal ? apiUtils.toContext(options.signal) : undefined;
|
|
66
|
-
const r = await client.statementExecution.getStatement({ statement_id: statementId }, ctx);
|
|
67
|
-
const columns = (r.manifest?.schema?.columns ?? []).map((c) => c.name ?? "");
|
|
68
|
-
const dataArray = (r.result?.data_array ?? []);
|
|
69
|
-
const sliced = options?.limit !== undefined && options.limit >= 0
|
|
70
|
-
? dataArray.slice(0, options.limit)
|
|
71
|
-
: dataArray;
|
|
72
|
-
const rows = sliced.map((row) => {
|
|
73
|
-
const obj = {};
|
|
74
|
-
columns.forEach((col, i) => {
|
|
75
|
-
obj[col] = coerceCell(row[i] ?? null);
|
|
76
|
-
});
|
|
77
|
-
return obj;
|
|
78
|
-
});
|
|
79
|
-
return {
|
|
80
|
-
columns,
|
|
81
|
-
rows,
|
|
82
|
-
rowCount: r.manifest?.total_row_count ?? dataArray.length,
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* True when `err` looks like the Databricks SDK's "statement not
|
|
87
|
-
* found" error. Matches the typed {@link ApiError} 404 /
|
|
88
|
-
* `RESOURCE_DOES_NOT_EXIST` shape first, then falls back to the
|
|
89
|
-
* lower-level {@link HttpError} 404, then to a loose `does not
|
|
90
|
-
* exist` / `not found` message sniff for SDK shapes we haven't
|
|
91
|
-
* catalogued.
|
|
92
|
-
*
|
|
93
|
-
* Pulled into its own helper so callers (notably the
|
|
94
|
-
* `/embed/data/:id` route) stay decoupled from SDK
|
|
95
|
-
* error-class identity, and the conversion logic stays testable
|
|
96
|
-
* in isolation.
|
|
97
|
-
*/
|
|
98
|
-
export function isStatementNotFoundError(err) {
|
|
99
|
-
if (err instanceof ApiError) {
|
|
100
|
-
if (err.statusCode === 404)
|
|
101
|
-
return true;
|
|
102
|
-
if (err.errorCode === "RESOURCE_DOES_NOT_EXIST")
|
|
103
|
-
return true;
|
|
104
|
-
}
|
|
105
|
-
if (err instanceof HttpError && err.code === 404)
|
|
106
|
-
return true;
|
|
107
|
-
if (err instanceof Error && /does not exist|not found/i.test(err.message)) {
|
|
108
|
-
return true;
|
|
109
|
-
}
|
|
110
|
-
return false;
|
|
111
|
-
}
|
package/dist/src/writer.d.ts
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared helper for publishing events through Mastra's
|
|
3
|
-
* `ctx.writer`. Centralizes the "the downstream stream may already
|
|
4
|
-
* be closed, don't take the whole tool down" pattern that the
|
|
5
|
-
* Genie agent and chart tool both need.
|
|
6
|
-
*
|
|
7
|
-
* Failures are logged at `warn` (a persistently-closed writer is
|
|
8
|
-
* the most likely culprit when events go missing client-side) but
|
|
9
|
-
* swallowed so a cancelled request or a client that navigated
|
|
10
|
-
* away can't crash a tool mid-flight.
|
|
11
|
-
*/
|
|
12
|
-
import type { MastraWriter } from "@dbx-tools/appkit-mastra-shared";
|
|
13
|
-
import { type logUtils } from "@dbx-tools/shared";
|
|
14
|
-
/**
|
|
15
|
-
* Best-effort `writer.write`. No-op when `writer` is undefined;
|
|
16
|
-
* caught errors are logged via `log.warn("writer:error", ...)`
|
|
17
|
-
* along with any caller-supplied `context` fields (e.g. a
|
|
18
|
-
* `chartId` or `messageId`) so the warning is greppable per
|
|
19
|
-
* resource.
|
|
20
|
-
*
|
|
21
|
-
* Returns when the write resolves or rejects; never throws.
|
|
22
|
-
*/
|
|
23
|
-
export declare function safeWrite(log: logUtils.Logger, writer: MastraWriter | undefined, chunk: unknown, context?: Record<string, unknown>): Promise<void>;
|