@dbx-tools/appkit-mastra 0.1.41 → 0.1.48
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 +122 -461
- package/dist/index.d.ts +2 -3
- package/dist/index.js +2 -3
- package/dist/src/agents.d.ts +3 -3
- package/dist/src/agents.js +1 -1
- package/dist/src/chart.d.ts +7 -1
- package/dist/src/chart.js +3 -5
- package/dist/src/config.d.ts +18 -13
- package/dist/src/genie.d.ts +0 -12
- package/dist/src/genie.js +2 -16
- package/dist/src/history.d.ts +6 -6
- package/dist/src/history.js +6 -6
- package/dist/src/model.d.ts +24 -131
- package/dist/src/model.js +39 -268
- package/dist/src/plugin.d.ts +13 -12
- package/dist/src/plugin.js +38 -34
- package/dist/src/server.d.ts +13 -20
- package/dist/src/server.js +15 -22
- package/dist/src/serving.d.ts +14 -98
- package/dist/src/serving.js +18 -163
- package/dist/src/writer.d.ts +2 -2
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/index.ts +2 -14
- package/package.json +6 -11
- package/src/agents.ts +3 -3
- package/src/chart.ts +3 -5
- package/src/config.ts +18 -13
- package/src/genie.ts +4 -22
- package/src/history.ts +6 -6
- package/src/model.ts +57 -291
- package/src/plugin.ts +45 -42
- package/src/server.ts +15 -22
- package/src/serving.ts +18 -220
- package/src/writer.ts +2 -2
- package/dist/src/intercept.d.ts +0 -48
- package/dist/src/intercept.js +0 -167
- package/dist/src/tools/email.d.ts +0 -74
- package/dist/src/tools/email.js +0 -121
- package/src/intercept.ts +0 -206
- package/src/tools/email.ts +0 -158
package/dist/src/model.js
CHANGED
|
@@ -7,214 +7,24 @@
|
|
|
7
7
|
* fresh bearer header, then point Mastra's OpenAI-compatible provider
|
|
8
8
|
* at `/serving-endpoints` on that host.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* names like `"claude sonnet"` resolve to the real endpoint name.
|
|
23
|
-
* Fuzzy matching is best-effort: when the workspace client throws
|
|
24
|
-
* (network blip, expired token at cache-fill time) we fall back to
|
|
25
|
-
* the input verbatim and let Databricks return the canonical error.
|
|
10
|
+
* This module only adds the Mastra-specific glue. The actual model
|
|
11
|
+
* selection - listing the workspace catalogue and resolving an
|
|
12
|
+
* explicit name / class / fallback chain to a real endpoint id - lives
|
|
13
|
+
* in `@dbx-tools/model` ({@link selectModel}) so non-Mastra consumers
|
|
14
|
+
* (e.g. a job that just needs a model name) can reuse it. Here we
|
|
15
|
+
* assemble the explicit ask from Mastra's request context (the
|
|
16
|
+
* per-request override under {@link MASTRA_MODEL_OVERRIDE_KEY}, the
|
|
17
|
+
* agent / plugin `modelId`, or `DATABRICKS_SERVING_ENDPOINT_NAME`),
|
|
18
|
+
* pass the plugin's fuzzy / class / fallback knobs through, and wrap
|
|
19
|
+
* the resolved id in the OpenAI-compatible provider config Mastra
|
|
20
|
+
* expects. Catalogue fetches fail loud: network / auth errors
|
|
21
|
+
* propagate so callers see the real SDK message.
|
|
26
22
|
*/
|
|
23
|
+
import { parseModelClass, selectModel } from "@dbx-tools/model";
|
|
27
24
|
import { commonUtils, logUtils, netUtils, stringUtils } from "@dbx-tools/shared";
|
|
28
25
|
import { MASTRA_USER_KEY } from "./config.js";
|
|
29
|
-
import {
|
|
30
|
-
|
|
31
|
-
* Capability tiers for Databricks Foundation Model API endpoints.
|
|
32
|
-
*
|
|
33
|
-
* - {@link ModelTier.Thinking}: deepest reasoning / "thinking" models
|
|
34
|
-
* (Claude Opus, GPT-5.5 Pro, Gemini Pro, Llama 4 Maverick, etc).
|
|
35
|
-
* Highest cost and latency; reserve for hard multi-step reasoning.
|
|
36
|
-
* - {@link ModelTier.Balanced}: cost/latency sweet spot for general
|
|
37
|
-
* agent work (Claude Sonnet, GPT-5.x, Gemini Flash, Llama 3.3 70B).
|
|
38
|
-
* The right default for most agents.
|
|
39
|
-
* - {@link ModelTier.Fast}: cheap and quick; classification, routing,
|
|
40
|
-
* tool-arg extraction, simple summarisation (Claude Haiku, GPT-5
|
|
41
|
-
* mini/nano, Gemini Flash Lite, GPT-OSS 20B, Llama 3.1 8B).
|
|
42
|
-
*
|
|
43
|
-
* String enum so the value is the slug we use in cache keys, logs,
|
|
44
|
-
* and as the value users see in serialized configs.
|
|
45
|
-
*/
|
|
46
|
-
export var ModelTier;
|
|
47
|
-
(function (ModelTier) {
|
|
48
|
-
ModelTier["Thinking"] = "thinking";
|
|
49
|
-
ModelTier["Balanced"] = "balanced";
|
|
50
|
-
ModelTier["Fast"] = "fast";
|
|
51
|
-
})(ModelTier || (ModelTier = {}));
|
|
52
|
-
/**
|
|
53
|
-
* Catalogue of Databricks-hosted Foundation Model API endpoints,
|
|
54
|
-
* grouped by capability {@link ModelTier} and then by provider. Each
|
|
55
|
-
* inner array is priority-ordered (most powerful first within the
|
|
56
|
-
* same provider+tier).
|
|
57
|
-
*
|
|
58
|
-
* Provider buckets:
|
|
59
|
-
*
|
|
60
|
-
* - `claude`: Anthropic Claude family (closed; flagship reasoning).
|
|
61
|
-
* - `gpt`: OpenAI GPT-5 family (closed; "ChatGPT" on Databricks FMAPI).
|
|
62
|
-
* - `gemini`: Google Gemini family (closed; multimodal + web-search).
|
|
63
|
-
* - `openSource`: open-weights models (widest regional / SKU availability).
|
|
64
|
-
*
|
|
65
|
-
* The list is curated by hand; refresh from the Databricks "supported
|
|
66
|
-
* foundation models" doc when new endpoints land.
|
|
67
|
-
*/
|
|
68
|
-
export const MODEL_CATALOG = {
|
|
69
|
-
[ModelTier.Thinking]: {
|
|
70
|
-
claude: [
|
|
71
|
-
"databricks-claude-opus-4-8",
|
|
72
|
-
"databricks-claude-opus-4-7",
|
|
73
|
-
"databricks-claude-opus-4-6",
|
|
74
|
-
"databricks-claude-opus-4-5",
|
|
75
|
-
"databricks-claude-opus-4-1",
|
|
76
|
-
],
|
|
77
|
-
gpt: ["databricks-gpt-5-5-pro"],
|
|
78
|
-
gemini: [
|
|
79
|
-
"databricks-gemini-3-1-pro",
|
|
80
|
-
"databricks-gemini-3-pro",
|
|
81
|
-
"databricks-gemini-2-5-pro",
|
|
82
|
-
],
|
|
83
|
-
openSource: [
|
|
84
|
-
"databricks-llama-4-maverick",
|
|
85
|
-
"databricks-gpt-oss-120b",
|
|
86
|
-
"databricks-meta-llama-3-1-405b-instruct",
|
|
87
|
-
],
|
|
88
|
-
},
|
|
89
|
-
[ModelTier.Balanced]: {
|
|
90
|
-
claude: [
|
|
91
|
-
"databricks-claude-sonnet-4-6",
|
|
92
|
-
"databricks-claude-sonnet-4-5",
|
|
93
|
-
"databricks-claude-sonnet-4",
|
|
94
|
-
],
|
|
95
|
-
gpt: [
|
|
96
|
-
"databricks-gpt-5-5",
|
|
97
|
-
"databricks-gpt-5-4",
|
|
98
|
-
"databricks-gpt-5-2",
|
|
99
|
-
"databricks-gpt-5-1",
|
|
100
|
-
"databricks-gpt-5",
|
|
101
|
-
],
|
|
102
|
-
gemini: [
|
|
103
|
-
"databricks-gemini-3-5-flash",
|
|
104
|
-
"databricks-gemini-3-flash",
|
|
105
|
-
"databricks-gemini-2-5-flash",
|
|
106
|
-
],
|
|
107
|
-
openSource: [
|
|
108
|
-
"databricks-meta-llama-3-3-70b-instruct",
|
|
109
|
-
"databricks-qwen3-next-80b-a3b-instruct",
|
|
110
|
-
"databricks-qwen35-122b-a10b",
|
|
111
|
-
],
|
|
112
|
-
},
|
|
113
|
-
[ModelTier.Fast]: {
|
|
114
|
-
claude: ["databricks-claude-haiku-4-5"],
|
|
115
|
-
gpt: [
|
|
116
|
-
"databricks-gpt-5-4-mini",
|
|
117
|
-
"databricks-gpt-5-4-nano",
|
|
118
|
-
"databricks-gpt-5-mini",
|
|
119
|
-
"databricks-gpt-5-nano",
|
|
120
|
-
],
|
|
121
|
-
gemini: ["databricks-gemini-3-1-flash-lite"],
|
|
122
|
-
openSource: [
|
|
123
|
-
"databricks-gpt-oss-20b",
|
|
124
|
-
"databricks-gemma-3-12b",
|
|
125
|
-
"databricks-meta-llama-3-1-8b-instruct",
|
|
126
|
-
],
|
|
127
|
-
},
|
|
128
|
-
};
|
|
129
|
-
/**
|
|
130
|
-
* Round-robin zip: take one from each input list in order, skipping
|
|
131
|
-
* lists that have already been exhausted. Used to interleave provider
|
|
132
|
-
* buckets within a tier so the resolver alternates between vendors
|
|
133
|
-
* instead of draining one before trying the next.
|
|
134
|
-
*
|
|
135
|
-
* Example: `interleave(["a1","a2","a3"], ["b1","b2"])` ->
|
|
136
|
-
* `["a1","b1","a2","b2","a3"]`.
|
|
137
|
-
*/
|
|
138
|
-
function interleave(...lists) {
|
|
139
|
-
const out = [];
|
|
140
|
-
const max = Math.max(0, ...lists.map((l) => l.length));
|
|
141
|
-
for (let i = 0; i < max; i++) {
|
|
142
|
-
for (const list of lists) {
|
|
143
|
-
if (i < list.length)
|
|
144
|
-
out.push(list[i]);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
return out;
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Priority-ordered model ids for a single capability {@link ModelTier},
|
|
151
|
-
* interleaved across providers so a workspace missing the top Claude
|
|
152
|
-
* still lands on a flagship GPT / Gemini on the next probe.
|
|
153
|
-
*
|
|
154
|
-
* Provider order within the interleave: Claude, GPT, Gemini, then the
|
|
155
|
-
* open-weights tail appended verbatim as the universal floor (widest
|
|
156
|
-
* regional availability).
|
|
157
|
-
*
|
|
158
|
-
* @example
|
|
159
|
-
* ```ts
|
|
160
|
-
* mastra({
|
|
161
|
-
* defaultModelFallbacks: modelsForTier(ModelTier.Fast),
|
|
162
|
-
* });
|
|
163
|
-
* ```
|
|
164
|
-
*/
|
|
165
|
-
export function modelsForTier(tier) {
|
|
166
|
-
const bucket = MODEL_CATALOG[tier];
|
|
167
|
-
return [
|
|
168
|
-
...interleave(bucket.claude, bucket.gpt, bucket.gemini),
|
|
169
|
-
...bucket.openSource,
|
|
170
|
-
];
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* Top model id at the given {@link ModelTier}. Sync; the agent-step
|
|
174
|
-
* resolver fuzzy-matches it against the workspace catalogue at call
|
|
175
|
-
* time, so this works even when the literal top pick isn't deployed.
|
|
176
|
-
*
|
|
177
|
-
* Use when wiring a tier-appropriate model into an agent definition:
|
|
178
|
-
*
|
|
179
|
-
* @example
|
|
180
|
-
* ```ts
|
|
181
|
-
* const classifier = createAgent({
|
|
182
|
-
* instructions: "Classify this email",
|
|
183
|
-
* model: modelForTier(ModelTier.Fast), // cheap, quick
|
|
184
|
-
* });
|
|
185
|
-
*
|
|
186
|
-
* const planner = createAgent({
|
|
187
|
-
* instructions: "Plan a multi-step migration",
|
|
188
|
-
* model: modelForTier(ModelTier.Thinking), // deep reasoning
|
|
189
|
-
* });
|
|
190
|
-
* ```
|
|
191
|
-
*/
|
|
192
|
-
export function modelForTier(tier) {
|
|
193
|
-
return modelsForTier(tier)[0];
|
|
194
|
-
}
|
|
195
|
-
/**
|
|
196
|
-
* Last-resort model ids used when neither `config.defaultModel`,
|
|
197
|
-
* per-agent `model`, nor `DATABRICKS_SERVING_ENDPOINT_NAME` is set.
|
|
198
|
-
*
|
|
199
|
-
* Walked in order at resolve time: the first id whose endpoint is
|
|
200
|
-
* actually present in the workspace's `/serving-endpoints` listing
|
|
201
|
-
* wins. Workspaces vary - not every region / SKU has every model,
|
|
202
|
-
* and the list of Foundation Model APIs evolves quickly - so the
|
|
203
|
-
* resolver degrades all the way from "best thinking model" down to
|
|
204
|
-
* "smallest commodity Llama" before giving up.
|
|
205
|
-
*
|
|
206
|
-
* Built by chaining the per-tier interleaves (Thinking -> Balanced
|
|
207
|
-
* -> Fast); within each tier the providers are round-robin-zipped
|
|
208
|
-
* (Claude, GPT, Gemini, then open-weights tail). Override the entire
|
|
209
|
-
* list via `MastraPluginConfig.defaultModelFallbacks` (e.g. to pin a
|
|
210
|
-
* regulated workspace to a specific approved subset, or to bias the
|
|
211
|
-
* priority toward a particular tier).
|
|
212
|
-
*/
|
|
213
|
-
export const FALLBACK_MODEL_IDS = [
|
|
214
|
-
...modelsForTier(ModelTier.Thinking),
|
|
215
|
-
...modelsForTier(ModelTier.Balanced),
|
|
216
|
-
...modelsForTier(ModelTier.Fast),
|
|
217
|
-
];
|
|
26
|
+
import { MASTRA_MODEL_OVERRIDE_KEY, resolveServingConfig } from "./serving.js";
|
|
27
|
+
export { classifyEndpoints, FALLBACK_MODEL_IDS, ModelClass, modelForClass, modelsForClass, } from "@dbx-tools/model";
|
|
218
28
|
/**
|
|
219
29
|
* Resolve a `MastraModelConfig` for the current agent step. Runs
|
|
220
30
|
* while `agent.stream` is inside the `asUser(req)` scope so tokens
|
|
@@ -232,76 +42,37 @@ export async function buildModel(config, requestContext, overrides = {}) {
|
|
|
232
42
|
// URL we hand it. Drop the trailing slash so the resulting URL stays
|
|
233
43
|
// well-formed (`/serving-endpoints/chat/completions`).
|
|
234
44
|
const url = new URL("/serving-endpoints", host).toString().replace(/\/$/, "");
|
|
235
|
-
const modelId = await pickModelId(config, requestContext, overrides, user, host);
|
|
236
|
-
return {
|
|
237
|
-
providerId: config.providerId ?? "databricks",
|
|
238
|
-
modelId,
|
|
239
|
-
url,
|
|
240
|
-
headers: Object.fromEntries(headers.entries()),
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
/**
|
|
244
|
-
* Walk the resolution ladder and pick a modelId.
|
|
245
|
-
*
|
|
246
|
-
* 1. **Explicit ask** (per-request override, agent `model` string,
|
|
247
|
-
* `config.defaultModel` string, or `DATABRICKS_SERVING_ENDPOINT_NAME`):
|
|
248
|
-
* when fuzzy matching is on, snap the input to the closest live
|
|
249
|
-
* endpoint so loose names like `"claude sonnet"` resolve. When it's
|
|
250
|
-
* off (or no endpoint matches within threshold), the input is used
|
|
251
|
-
* verbatim and Databricks surfaces the canonical 404.
|
|
252
|
-
*
|
|
253
|
-
* 2. **No explicit ask**: walk
|
|
254
|
-
* {@link MastraPluginConfig.defaultModelFallbacks} (or
|
|
255
|
-
* {@link FALLBACK_MODEL_IDS} when unset) and return the first id
|
|
256
|
-
* whose endpoint is actually present in the workspace listing. A
|
|
257
|
-
* workspace without Claude Opus still gets a sensible default by
|
|
258
|
-
* skipping ahead to whichever Sonnet / GPT-5 / Llama variant is
|
|
259
|
-
* wired up.
|
|
260
|
-
*
|
|
261
|
-
* Catalogue fetches fail loud: network / auth errors propagate to the
|
|
262
|
-
* caller so they see the real SDK message instead of a silent fallback
|
|
263
|
-
* to the top of the priority list.
|
|
264
|
-
*/
|
|
265
|
-
async function pickModelId(config, requestContext, overrides, user, host) {
|
|
266
45
|
const log = logUtils.logger(config);
|
|
267
|
-
const serving = resolveServingConfig(config
|
|
46
|
+
const serving = resolveServingConfig(config);
|
|
268
47
|
const override = serving.allowOverride
|
|
269
48
|
? requestContext.get(MASTRA_MODEL_OVERRIDE_KEY)
|
|
270
49
|
: undefined;
|
|
271
|
-
|
|
272
|
-
//
|
|
273
|
-
//
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
const
|
|
50
|
+
// The override / agent default / env value can be either a concrete
|
|
51
|
+
// endpoint name or a model class slug ("chat-thinking" /
|
|
52
|
+
// "chat-balanced" / "chat-fast"). A class slug becomes a class intent
|
|
53
|
+
// (let the live catalogue pick the best model in that band); anything
|
|
54
|
+
// else is an explicit name fuzzy-matched against the catalogue. An
|
|
55
|
+
// internal `overrides.modelClass` (e.g. the chart planner) is the
|
|
56
|
+
// floor when nothing was requested.
|
|
57
|
+
const requested = override ?? overrides.modelId ?? process.env.DATABRICKS_SERVING_ENDPOINT_NAME;
|
|
58
|
+
const requestedClass = requested !== undefined ? parseModelClass(requested) : null;
|
|
59
|
+
const explicit = requestedClass === null ? requested : undefined;
|
|
60
|
+
const modelClass = requestedClass ?? overrides.modelClass;
|
|
61
|
+
const { modelId, source } = await selectModel(user.executionContext.client, host, {
|
|
62
|
+
...(explicit !== undefined ? { explicit } : {}),
|
|
63
|
+
fuzzy: serving.fuzzy,
|
|
64
|
+
threshold: serving.threshold,
|
|
65
|
+
...(modelClass !== undefined ? { modelClass } : {}),
|
|
66
|
+
fallbacks: serving.fallbacks,
|
|
279
67
|
ttlMs: serving.ttlMs,
|
|
280
68
|
});
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
:
|
|
284
|
-
log.debug("model selected", {
|
|
69
|
+
log.debug("model selected", { modelId, source, requested });
|
|
70
|
+
return {
|
|
71
|
+
providerId: config.providerId ?? "databricks",
|
|
285
72
|
modelId,
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
return modelId;
|
|
290
|
-
}
|
|
291
|
-
/**
|
|
292
|
-
* Find the first id in `fallbacks` whose endpoint is present in
|
|
293
|
-
* `endpoints`. Returns the top fallback when the workspace has none
|
|
294
|
-
* of them so callers always get a string; an offline workspace will
|
|
295
|
-
* then receive a clean 404 from Databricks instead of a malformed
|
|
296
|
-
* config.
|
|
297
|
-
*/
|
|
298
|
-
function pickFirstAvailable(fallbacks, endpoints) {
|
|
299
|
-
const present = new Set(endpoints.map((e) => e.name));
|
|
300
|
-
for (const candidate of fallbacks) {
|
|
301
|
-
if (present.has(candidate))
|
|
302
|
-
return candidate;
|
|
303
|
-
}
|
|
304
|
-
return fallbacks[0] ?? FALLBACK_MODEL_IDS[0];
|
|
73
|
+
url,
|
|
74
|
+
headers: Object.fromEntries(headers.entries()),
|
|
75
|
+
};
|
|
305
76
|
}
|
|
306
77
|
/** Path prefix that identifies a Databricks Model Serving REST call. */
|
|
307
78
|
const SERVING_ENDPOINTS_PATH_PREFIX = "/serving-endpoints/";
|
package/dist/src/plugin.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AppKit plugin that builds one or more Mastra `Agent` instances and
|
|
3
|
-
* mounts the `@mastra/express` server
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* mounts the `@mastra/express` server. Clients drive the conversation
|
|
4
|
+
* over the standard Mastra agent stream (`@mastra/client-js`'s
|
|
5
|
+
* `getAgent(id).stream()`), so there's no bespoke chat transport to
|
|
6
|
+
* keep in sync.
|
|
7
7
|
*
|
|
8
8
|
* - Agents: registered through `config.agents` at plugin creation
|
|
9
9
|
* ({@link MastraAgentDefinition}). Each entry's `tools` field accepts
|
|
@@ -21,17 +21,18 @@
|
|
|
21
21
|
* `schemaName: "mastra_<agentId>"`; the vector store is a single
|
|
22
22
|
* shared singleton across every agent.
|
|
23
23
|
* - Server: the Express subapp wiring lives in `./server.js`.
|
|
24
|
-
* - HTTP: AppKit mounts this plugin under `/api/mastra`.
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* - HTTP: AppKit mounts this plugin under `/api/mastra`. Alongside the
|
|
25
|
+
* Mastra agent routes, the plugin registers `/route/history`
|
|
26
|
+
* (load + clear thread history), `/models`, `/suggestions`, and the
|
|
27
|
+
* generic `/embed/:type/:id` resolver for inline chart / data
|
|
28
|
+
* markers.
|
|
28
29
|
*/
|
|
29
30
|
import { Plugin, type IAppRouter, type ResourceRequirement } from "@databricks/appkit";
|
|
30
31
|
import type { Agent } from "@mastra/core/agent";
|
|
31
32
|
import { Mastra } from "@mastra/core/mastra";
|
|
33
|
+
import { type ServingEndpointSummary } from "@dbx-tools/model";
|
|
32
34
|
import type { MastraPluginConfig } from "./config.js";
|
|
33
35
|
import { MastraServer } from "./server.js";
|
|
34
|
-
import { type ServingEndpointSummary } from "./serving.js";
|
|
35
36
|
/**
|
|
36
37
|
* AppKit plugin (registered name: `mastra`) that hosts Mastra agents
|
|
37
38
|
* with optional Lakebase-backed memory and AI SDK chat routes under
|
|
@@ -97,9 +98,9 @@ export declare class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
97
98
|
*/
|
|
98
99
|
get: (id: string) => Agent | null;
|
|
99
100
|
/**
|
|
100
|
-
* The agent
|
|
101
|
-
*
|
|
102
|
-
*
|
|
101
|
+
* The agent the client converses with when it doesn't name one.
|
|
102
|
+
* Resolves to `config.defaultAgent`, the first registered id, or
|
|
103
|
+
* the built-in `default` fallback.
|
|
103
104
|
*/
|
|
104
105
|
getDefault: () => Agent | null;
|
|
105
106
|
/** Underlying Mastra instance for advanced use (custom routes etc.). */
|
package/dist/src/plugin.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AppKit plugin that builds one or more Mastra `Agent` instances and
|
|
3
|
-
* mounts the `@mastra/express` server
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* mounts the `@mastra/express` server. Clients drive the conversation
|
|
4
|
+
* over the standard Mastra agent stream (`@mastra/client-js`'s
|
|
5
|
+
* `getAgent(id).stream()`), so there's no bespoke chat transport to
|
|
6
|
+
* keep in sync.
|
|
7
7
|
*
|
|
8
8
|
* - Agents: registered through `config.agents` at plugin creation
|
|
9
9
|
* ({@link MastraAgentDefinition}). Each entry's `tools` field accepts
|
|
@@ -21,16 +21,18 @@
|
|
|
21
21
|
* `schemaName: "mastra_<agentId>"`; the vector store is a single
|
|
22
22
|
* shared singleton across every agent.
|
|
23
23
|
* - Server: the Express subapp wiring lives in `./server.js`.
|
|
24
|
-
* - HTTP: AppKit mounts this plugin under `/api/mastra`.
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* - HTTP: AppKit mounts this plugin under `/api/mastra`. Alongside the
|
|
25
|
+
* Mastra agent routes, the plugin registers `/route/history`
|
|
26
|
+
* (load + clear thread history), `/models`, `/suggestions`, and the
|
|
27
|
+
* generic `/embed/:type/:id` resolver for inline chart / data
|
|
28
|
+
* markers.
|
|
28
29
|
*/
|
|
29
30
|
import { genie, getExecutionContext, lakebase, Plugin, toPlugin, } from "@databricks/appkit";
|
|
30
31
|
import { appkitUtils, commonUtils, logUtils } from "@dbx-tools/shared";
|
|
31
|
-
import { chatRoute } from "@mastra/ai-sdk";
|
|
32
32
|
import { Mastra } from "@mastra/core/mastra";
|
|
33
33
|
import express from "express";
|
|
34
|
+
import { MASTRA_ROUTES, } from "@dbx-tools/appkit-mastra-shared";
|
|
35
|
+
import { clearServingEndpointsCache, listServingEndpoints, } from "@dbx-tools/model";
|
|
34
36
|
import { buildAgents, FALLBACK_AGENT_ID } from "./agents.js";
|
|
35
37
|
import { fetchChart } from "./chart.js";
|
|
36
38
|
import { collectSpaceSuggestions, resolveGenieSpaces } from "./genie.js";
|
|
@@ -38,7 +40,7 @@ import { historyRoute } from "./history.js";
|
|
|
38
40
|
import { createMemoryBuilder, createServicePrincipalPool, needsLakebase, } from "./memory.js";
|
|
39
41
|
import { buildObservability } from "./observability.js";
|
|
40
42
|
import { attachRoutePatchMiddleware, MastraServer } from "./server.js";
|
|
41
|
-
import {
|
|
43
|
+
import { resolveServingConfig } from "./serving.js";
|
|
42
44
|
import { fetchStatementData, isStatementNotFoundError, STATEMENT_ROW_CAP, } from "./statement.js";
|
|
43
45
|
const GENIE_MANIFEST = appkitUtils.data(genie).plugin.manifest;
|
|
44
46
|
const LAKEBASE_MANIFEST = appkitUtils.data(lakebase).plugin.manifest;
|
|
@@ -160,9 +162,9 @@ export class MastraPlugin extends Plugin {
|
|
|
160
162
|
*/
|
|
161
163
|
get: (id) => this.built?.agents[id] ?? null,
|
|
162
164
|
/**
|
|
163
|
-
* The agent
|
|
164
|
-
*
|
|
165
|
-
*
|
|
165
|
+
* The agent the client converses with when it doesn't name one.
|
|
166
|
+
* Resolves to `config.defaultAgent`, the first registered id, or
|
|
167
|
+
* the built-in `default` fallback.
|
|
166
168
|
*/
|
|
167
169
|
getDefault: () => (this.built && this.built.agents[this.built.defaultAgentId]) ?? null,
|
|
168
170
|
/** Underlying Mastra instance for advanced use (custom routes etc.). */
|
|
@@ -188,22 +190,16 @@ export class MastraPlugin extends Plugin {
|
|
|
188
190
|
}
|
|
189
191
|
clientConfig() {
|
|
190
192
|
// AppKit mounts every plugin at `/api/<plugin.name>`. `this.name`
|
|
191
|
-
// honors `config.name` overrides, so
|
|
192
|
-
//
|
|
193
|
+
// honors `config.name` overrides, so publishing `basePath` is
|
|
194
|
+
// enough for the client to stay correct under a custom mount id -
|
|
195
|
+
// the per-route segments are fixed (`MASTRA_ROUTES`) and the
|
|
196
|
+
// client (`MastraPluginClient`) derives every endpoint from
|
|
197
|
+
// `basePath`.
|
|
193
198
|
// Return widens to `Record<string, unknown>` to satisfy the
|
|
194
199
|
// base-class signature; consumers read it through the typed
|
|
195
200
|
// `MastraClientConfig` shape via `usePluginClientConfig<...>(...)`.
|
|
196
|
-
const basePath = `/api/${this.name}`;
|
|
197
201
|
const config = {
|
|
198
|
-
basePath
|
|
199
|
-
chatPath: `${basePath}/route/chat`,
|
|
200
|
-
chatPathTemplate: `${basePath}/route/chat/:agentId`,
|
|
201
|
-
modelsPath: `${basePath}/models`,
|
|
202
|
-
historyPath: `${basePath}/route/history`,
|
|
203
|
-
historyPathTemplate: `${basePath}/route/history/:agentId`,
|
|
204
|
-
embedPathTemplate: `${basePath}/embed/:type/:id`,
|
|
205
|
-
suggestionsPath: `${basePath}/suggestions`,
|
|
206
|
-
suggestionsPathTemplate: `${basePath}/suggestions/:agentId`,
|
|
202
|
+
basePath: `/api/${this.name}`,
|
|
207
203
|
defaultAgent: this.built?.defaultAgentId ?? FALLBACK_AGENT_ID,
|
|
208
204
|
agents: Object.keys(this.built?.agents ?? {}),
|
|
209
205
|
};
|
|
@@ -215,7 +211,7 @@ export class MastraPlugin extends Plugin {
|
|
|
215
211
|
// be registered before the catch-all that forwards everything to
|
|
216
212
|
// the Mastra subapp. Errors propagate to Express's default error
|
|
217
213
|
// handler via `next(err)` so callers see the real SDK message.
|
|
218
|
-
router.get(
|
|
214
|
+
router.get(MASTRA_ROUTES.models, (req, res, next) => {
|
|
219
215
|
this.userScopedSelf(req)
|
|
220
216
|
.listModels()
|
|
221
217
|
.then((endpoints) => res.json({ endpoints }))
|
|
@@ -267,7 +263,7 @@ export class MastraPlugin extends Plugin {
|
|
|
267
263
|
});
|
|
268
264
|
},
|
|
269
265
|
};
|
|
270
|
-
router.get(
|
|
266
|
+
router.get(`${MASTRA_ROUTES.embed}/:type/:id`, (req, res, next) => {
|
|
271
267
|
const type = req.params["type"] ?? "";
|
|
272
268
|
const id = req.params["id"];
|
|
273
269
|
const resolve = embedResolvers[type];
|
|
@@ -322,8 +318,8 @@ export class MastraPlugin extends Plugin {
|
|
|
322
318
|
res.json({ questions: [] });
|
|
323
319
|
});
|
|
324
320
|
};
|
|
325
|
-
router.get(
|
|
326
|
-
router.get(
|
|
321
|
+
router.get(MASTRA_ROUTES.suggestions, handleSuggestions);
|
|
322
|
+
router.get(`${MASTRA_ROUTES.suggestions}/:agentId`, handleSuggestions);
|
|
327
323
|
router.use((req, res, next) => {
|
|
328
324
|
if (!this.mastraApp)
|
|
329
325
|
return res.status(503).end();
|
|
@@ -495,20 +491,28 @@ export class MastraPlugin extends Plugin {
|
|
|
495
491
|
mastra: this.mastra,
|
|
496
492
|
prefix: "",
|
|
497
493
|
customApiRoutes: [
|
|
498
|
-
chatRoute({ path: "/route/chat", agent: this.built.defaultAgentId }),
|
|
499
|
-
chatRoute({ path: "/route/chat/:agentId" }),
|
|
500
494
|
// `historyRoute` registers both GET (load) and DELETE
|
|
501
495
|
// (clear) on the same path, so it returns an array we
|
|
502
496
|
// splice in.
|
|
503
|
-
...historyRoute({
|
|
504
|
-
|
|
497
|
+
...historyRoute({
|
|
498
|
+
path: MASTRA_ROUTES.history,
|
|
499
|
+
agent: this.built.defaultAgentId,
|
|
500
|
+
}),
|
|
501
|
+
// Assert the `:agentId` template type: the per-package build's
|
|
502
|
+
// NodeNext resolution widens the imported `MASTRA_ROUTES.history`
|
|
503
|
+
// to `string` (the source/bundler typecheck keeps it a literal),
|
|
504
|
+
// which would otherwise drop this out of the dynamic-agent
|
|
505
|
+
// overload and demand a fixed `agent`.
|
|
506
|
+
...historyRoute({
|
|
507
|
+
path: `${MASTRA_ROUTES.history}/:agentId`,
|
|
508
|
+
}),
|
|
505
509
|
],
|
|
506
510
|
});
|
|
507
511
|
await this.mastraServer.init();
|
|
508
512
|
this.log.debug("build:done", {
|
|
509
513
|
agents: Object.keys(this.built.agents),
|
|
510
514
|
defaultAgent: this.built.defaultAgentId,
|
|
511
|
-
routes: ["/route/
|
|
515
|
+
routes: ["/route/history", "/models", "/suggestions", "/embed/:type/:id"],
|
|
512
516
|
instanceStorage: instanceStorage !== undefined,
|
|
513
517
|
observability: observability !== undefined ? "mlflow" : "off",
|
|
514
518
|
});
|
package/dist/src/server.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Express-layer plumbing for the Mastra plugin: a `MastraServer` that
|
|
3
3
|
* stamps the per-request `RequestContext`, and a route-patch middleware
|
|
4
|
-
* that lets
|
|
5
|
-
* point.
|
|
4
|
+
* that lets the plugin's custom API routes (e.g. `historyRoute`) work
|
|
5
|
+
* behind an Express mount point.
|
|
6
6
|
*/
|
|
7
7
|
import { type RequestContext } from "@mastra/core/request-context";
|
|
8
8
|
import { MastraServer as MastraServerExpress } from "@mastra/express";
|
|
@@ -35,24 +35,17 @@ export declare class MastraServer extends MastraServerExpress {
|
|
|
35
35
|
configureRequestContextModelOverride(req: express.Request, requestContext: RequestContext): void;
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
|
-
* Patches around `@mastra/express`'s custom-route dispatcher so
|
|
39
|
-
*
|
|
40
|
-
* mounted under a parent
|
|
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`).
|
|
41
42
|
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
* until we overwrite `originalUrl` for `/route` and `/route/*` to
|
|
50
|
-
* the mount-relative path.
|
|
51
|
-
*
|
|
52
|
-
* 2. `memory.resource` must be the authenticated user, not whatever the
|
|
53
|
-
* client posts. The custom-route forwarder re-serializes `req.body`
|
|
54
|
-
* into the Request body it hands Hono, so mutating the parsed body
|
|
55
|
-
* here would propagate into `handleChatStream`'s params (kept for
|
|
56
|
-
* future use; `express.json()` runs first so `req.body` is parsed).
|
|
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.
|
|
57
50
|
*/
|
|
58
51
|
export declare function attachRoutePatchMiddleware(app: express.Express): void;
|
package/dist/src/server.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Express-layer plumbing for the Mastra plugin: a `MastraServer` that
|
|
3
3
|
* stamps the per-request `RequestContext`, and a route-patch middleware
|
|
4
|
-
* that lets
|
|
5
|
-
* point.
|
|
4
|
+
* that lets the plugin's custom API routes (e.g. `historyRoute`) work
|
|
5
|
+
* behind an Express mount point.
|
|
6
6
|
*/
|
|
7
7
|
import { getExecutionContext } from "@databricks/appkit";
|
|
8
8
|
import { httpUtils, logUtils, stringUtils } from "@dbx-tools/shared";
|
|
@@ -128,30 +128,23 @@ export class MastraServer extends MastraServerExpress {
|
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
/**
|
|
131
|
-
* Patches around `@mastra/express`'s custom-route dispatcher so
|
|
132
|
-
*
|
|
133
|
-
* mounted under a parent
|
|
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`).
|
|
134
135
|
*
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
* until we overwrite `originalUrl` for `/route` and `/route/*` to
|
|
143
|
-
* the mount-relative path.
|
|
144
|
-
*
|
|
145
|
-
* 2. `memory.resource` must be the authenticated user, not whatever the
|
|
146
|
-
* client posts. The custom-route forwarder re-serializes `req.body`
|
|
147
|
-
* into the Request body it hands Hono, so mutating the parsed body
|
|
148
|
-
* here would propagate into `handleChatStream`'s params (kept for
|
|
149
|
-
* future use; `express.json()` runs first so `req.body` is parsed).
|
|
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.
|
|
150
143
|
*/
|
|
151
144
|
export function attachRoutePatchMiddleware(app) {
|
|
152
145
|
app.use((req, _res, next) => {
|
|
153
|
-
const
|
|
154
|
-
if (!
|
|
146
|
+
const isCustomRoute = req.path === "/route" || req.path.startsWith("/route/");
|
|
147
|
+
if (!isCustomRoute)
|
|
155
148
|
return next();
|
|
156
149
|
req.originalUrl = req.path;
|
|
157
150
|
next();
|