@dbx-tools/appkit-mastra 0.1.40 → 0.1.42
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 +59 -58
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- 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 +14 -11
- 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/memory.d.ts +34 -25
- package/dist/src/memory.js +48 -35
- package/dist/src/model.d.ts +24 -131
- package/dist/src/model.js +39 -268
- package/dist/src/plugin.d.ts +28 -12
- package/dist/src/plugin.js +86 -42
- 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/tools/email.d.ts +10 -1
- package/dist/src/writer.d.ts +2 -2
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/index.ts +2 -13
- package/package.json +7 -6
- package/src/agents.ts +3 -3
- package/src/chart.ts +3 -5
- package/src/config.ts +14 -11
- package/src/genie.ts +4 -22
- package/src/history.ts +6 -6
- package/src/memory.ts +48 -45
- package/src/model.ts +57 -291
- package/src/plugin.ts +99 -50
- 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/src/intercept.ts +0 -206
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
|
|
@@ -59,6 +60,14 @@ export declare class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
59
60
|
private mastra;
|
|
60
61
|
private mastraApp;
|
|
61
62
|
private mastraServer;
|
|
63
|
+
/**
|
|
64
|
+
* Dedicated service-principal Lakebase pool backing Mastra memory /
|
|
65
|
+
* storage. Built once in {@link buildAgentAndServer} (outside any
|
|
66
|
+
* `asUser` scope, so it never inherits a request's OBO identity) and
|
|
67
|
+
* drained in {@link abortActiveOperations}. `null` until setup runs
|
|
68
|
+
* or when Lakebase isn't needed.
|
|
69
|
+
*/
|
|
70
|
+
private servicePrincipalPool;
|
|
62
71
|
setup(): Promise<void>;
|
|
63
72
|
/**
|
|
64
73
|
* When the `lakebase` plugin is registered, auto-enable `storage`
|
|
@@ -68,6 +77,13 @@ export declare class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
68
77
|
* already in the registry by the time this fires.
|
|
69
78
|
*/
|
|
70
79
|
private applyLakebaseAutoDefaults;
|
|
80
|
+
/**
|
|
81
|
+
* Drain the memory service-principal pool on shutdown. AppKit calls
|
|
82
|
+
* this during teardown; the lakebase plugin closes its own SP / OBO
|
|
83
|
+
* pools the same way. Fire-and-forget so shutdown isn't blocked on a
|
|
84
|
+
* slow drain, and clear the handle so a re-`setup()` rebuilds it.
|
|
85
|
+
*/
|
|
86
|
+
abortActiveOperations(): void;
|
|
71
87
|
exports(): {
|
|
72
88
|
/**
|
|
73
89
|
* Ids of every registered agent in registration order. Matches
|
|
@@ -82,9 +98,9 @@ export declare class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
82
98
|
*/
|
|
83
99
|
get: (id: string) => Agent | null;
|
|
84
100
|
/**
|
|
85
|
-
* The agent
|
|
86
|
-
*
|
|
87
|
-
*
|
|
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.
|
|
88
104
|
*/
|
|
89
105
|
getDefault: () => Agent | null;
|
|
90
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,24 +21,26 @@
|
|
|
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
|
-
import { appkitUtils, logUtils } from "@dbx-tools/shared";
|
|
31
|
-
import { chatRoute } from "@mastra/ai-sdk";
|
|
31
|
+
import { appkitUtils, commonUtils, logUtils } from "@dbx-tools/shared";
|
|
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";
|
|
37
39
|
import { historyRoute } from "./history.js";
|
|
38
|
-
import { createMemoryBuilder, needsLakebase } from "./memory.js";
|
|
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;
|
|
@@ -93,6 +95,14 @@ export class MastraPlugin extends Plugin {
|
|
|
93
95
|
mastra = null;
|
|
94
96
|
mastraApp = null;
|
|
95
97
|
mastraServer = null;
|
|
98
|
+
/**
|
|
99
|
+
* Dedicated service-principal Lakebase pool backing Mastra memory /
|
|
100
|
+
* storage. Built once in {@link buildAgentAndServer} (outside any
|
|
101
|
+
* `asUser` scope, so it never inherits a request's OBO identity) and
|
|
102
|
+
* drained in {@link abortActiveOperations}. `null` until setup runs
|
|
103
|
+
* or when Lakebase isn't needed.
|
|
104
|
+
*/
|
|
105
|
+
servicePrincipalPool = null;
|
|
96
106
|
async setup() {
|
|
97
107
|
// Wait until sibling plugins (e.g. `lakebase`) finish `setup()` so
|
|
98
108
|
// the lakebase pool is valid when storage/memory are enabled.
|
|
@@ -118,6 +128,25 @@ export class MastraPlugin extends Plugin {
|
|
|
118
128
|
if (this.config.memory === undefined)
|
|
119
129
|
this.config.memory = true;
|
|
120
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Drain the memory service-principal pool on shutdown. AppKit calls
|
|
133
|
+
* this during teardown; the lakebase plugin closes its own SP / OBO
|
|
134
|
+
* pools the same way. Fire-and-forget so shutdown isn't blocked on a
|
|
135
|
+
* slow drain, and clear the handle so a re-`setup()` rebuilds it.
|
|
136
|
+
*/
|
|
137
|
+
abortActiveOperations() {
|
|
138
|
+
super.abortActiveOperations();
|
|
139
|
+
if (this.servicePrincipalPool) {
|
|
140
|
+
this.log.info("closing memory SP pool");
|
|
141
|
+
const pool = this.servicePrincipalPool;
|
|
142
|
+
this.servicePrincipalPool = null;
|
|
143
|
+
pool.end().catch((err) => {
|
|
144
|
+
this.log.error("error closing memory SP pool", {
|
|
145
|
+
error: commonUtils.errorMessage(err),
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
}
|
|
121
150
|
exports() {
|
|
122
151
|
return {
|
|
123
152
|
/**
|
|
@@ -133,9 +162,9 @@ export class MastraPlugin extends Plugin {
|
|
|
133
162
|
*/
|
|
134
163
|
get: (id) => this.built?.agents[id] ?? null,
|
|
135
164
|
/**
|
|
136
|
-
* The agent
|
|
137
|
-
*
|
|
138
|
-
*
|
|
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.
|
|
139
168
|
*/
|
|
140
169
|
getDefault: () => (this.built && this.built.agents[this.built.defaultAgentId]) ?? null,
|
|
141
170
|
/** Underlying Mastra instance for advanced use (custom routes etc.). */
|
|
@@ -161,22 +190,16 @@ export class MastraPlugin extends Plugin {
|
|
|
161
190
|
}
|
|
162
191
|
clientConfig() {
|
|
163
192
|
// AppKit mounts every plugin at `/api/<plugin.name>`. `this.name`
|
|
164
|
-
// honors `config.name` overrides, so
|
|
165
|
-
//
|
|
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`.
|
|
166
198
|
// Return widens to `Record<string, unknown>` to satisfy the
|
|
167
199
|
// base-class signature; consumers read it through the typed
|
|
168
200
|
// `MastraClientConfig` shape via `usePluginClientConfig<...>(...)`.
|
|
169
|
-
const basePath = `/api/${this.name}`;
|
|
170
201
|
const config = {
|
|
171
|
-
basePath
|
|
172
|
-
chatPath: `${basePath}/route/chat`,
|
|
173
|
-
chatPathTemplate: `${basePath}/route/chat/:agentId`,
|
|
174
|
-
modelsPath: `${basePath}/models`,
|
|
175
|
-
historyPath: `${basePath}/route/history`,
|
|
176
|
-
historyPathTemplate: `${basePath}/route/history/:agentId`,
|
|
177
|
-
embedPathTemplate: `${basePath}/embed/:type/:id`,
|
|
178
|
-
suggestionsPath: `${basePath}/suggestions`,
|
|
179
|
-
suggestionsPathTemplate: `${basePath}/suggestions/:agentId`,
|
|
202
|
+
basePath: `/api/${this.name}`,
|
|
180
203
|
defaultAgent: this.built?.defaultAgentId ?? FALLBACK_AGENT_ID,
|
|
181
204
|
agents: Object.keys(this.built?.agents ?? {}),
|
|
182
205
|
};
|
|
@@ -188,7 +211,7 @@ export class MastraPlugin extends Plugin {
|
|
|
188
211
|
// be registered before the catch-all that forwards everything to
|
|
189
212
|
// the Mastra subapp. Errors propagate to Express's default error
|
|
190
213
|
// handler via `next(err)` so callers see the real SDK message.
|
|
191
|
-
router.get(
|
|
214
|
+
router.get(MASTRA_ROUTES.models, (req, res, next) => {
|
|
192
215
|
this.userScopedSelf(req)
|
|
193
216
|
.listModels()
|
|
194
217
|
.then((endpoints) => res.json({ endpoints }))
|
|
@@ -240,7 +263,7 @@ export class MastraPlugin extends Plugin {
|
|
|
240
263
|
});
|
|
241
264
|
},
|
|
242
265
|
};
|
|
243
|
-
router.get(
|
|
266
|
+
router.get(`${MASTRA_ROUTES.embed}/:type/:id`, (req, res, next) => {
|
|
244
267
|
const type = req.params["type"] ?? "";
|
|
245
268
|
const id = req.params["id"];
|
|
246
269
|
const resolve = embedResolvers[type];
|
|
@@ -295,8 +318,8 @@ export class MastraPlugin extends Plugin {
|
|
|
295
318
|
res.json({ questions: [] });
|
|
296
319
|
});
|
|
297
320
|
};
|
|
298
|
-
router.get(
|
|
299
|
-
router.get(
|
|
321
|
+
router.get(MASTRA_ROUTES.suggestions, handleSuggestions);
|
|
322
|
+
router.get(`${MASTRA_ROUTES.suggestions}/:agentId`, handleSuggestions);
|
|
300
323
|
router.use((req, res, next) => {
|
|
301
324
|
if (!this.mastraApp)
|
|
302
325
|
return res.status(503).end();
|
|
@@ -402,12 +425,25 @@ export class MastraPlugin extends Plugin {
|
|
|
402
425
|
return listServingEndpoints(client, host, { ttlMs: serving.ttlMs });
|
|
403
426
|
}
|
|
404
427
|
async buildAgentAndServer() {
|
|
405
|
-
// Per-agent memory factory.
|
|
406
|
-
//
|
|
407
|
-
//
|
|
408
|
-
//
|
|
409
|
-
|
|
410
|
-
|
|
428
|
+
// Per-agent memory factory. When any storage / memory setting needs
|
|
429
|
+
// Postgres, stand up a dedicated service-principal pool first so
|
|
430
|
+
// memory acts as the app SP (owner of the `mastra_*` schemas),
|
|
431
|
+
// never the per-request OBO identity the chat turn runs under.
|
|
432
|
+
// `getPgConfig()` is read here, outside any `asUser` scope, so it
|
|
433
|
+
// returns the SP connection target + token refresh plus any
|
|
434
|
+
// `lakebase({ pool })` overrides; `require` turns a missing
|
|
435
|
+
// sibling into a clear wiring error. The builder caches the shared
|
|
436
|
+
// `PgVector` singleton so registering N agents stays cheap. See
|
|
437
|
+
// `./memory.js`.
|
|
438
|
+
if (needsLakebase(this.config)) {
|
|
439
|
+
const spPgConfig = appkitUtils
|
|
440
|
+
.require(this.context, lakebase, this.config)
|
|
441
|
+
.exports()
|
|
442
|
+
.getPgConfig();
|
|
443
|
+
this.servicePrincipalPool = await createServicePrincipalPool(spPgConfig);
|
|
444
|
+
}
|
|
445
|
+
const memoryBuilder = this.servicePrincipalPool
|
|
446
|
+
? createMemoryBuilder(this.config, this.servicePrincipalPool)
|
|
411
447
|
: undefined;
|
|
412
448
|
this.log.debug("build:start", {
|
|
413
449
|
lakebase: memoryBuilder !== undefined,
|
|
@@ -455,20 +491,28 @@ export class MastraPlugin extends Plugin {
|
|
|
455
491
|
mastra: this.mastra,
|
|
456
492
|
prefix: "",
|
|
457
493
|
customApiRoutes: [
|
|
458
|
-
chatRoute({ path: "/route/chat", agent: this.built.defaultAgentId }),
|
|
459
|
-
chatRoute({ path: "/route/chat/:agentId" }),
|
|
460
494
|
// `historyRoute` registers both GET (load) and DELETE
|
|
461
495
|
// (clear) on the same path, so it returns an array we
|
|
462
496
|
// splice in.
|
|
463
|
-
...historyRoute({
|
|
464
|
-
|
|
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
|
+
}),
|
|
465
509
|
],
|
|
466
510
|
});
|
|
467
511
|
await this.mastraServer.init();
|
|
468
512
|
this.log.debug("build:done", {
|
|
469
513
|
agents: Object.keys(this.built.agents),
|
|
470
514
|
defaultAgent: this.built.defaultAgentId,
|
|
471
|
-
routes: ["/route/
|
|
515
|
+
routes: ["/route/history", "/models", "/suggestions", "/embed/:type/:id"],
|
|
472
516
|
instanceStorage: instanceStorage !== undefined,
|
|
473
517
|
observability: observability !== undefined ? "mlflow" : "off",
|
|
474
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();
|
package/dist/src/serving.d.ts
CHANGED
|
@@ -1,28 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Mastra-specific glue over the generic `@dbx-tools/model` toolkit.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* `
|
|
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.
|
|
4
|
+
* The live `/serving-endpoints` catalogue access, fuzzy name
|
|
5
|
+
* resolution, and tier/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.
|
|
14
10
|
*/
|
|
15
|
-
import { type getExecutionContext } from "@databricks/appkit";
|
|
16
|
-
import type { ServingEndpointSummary } from "@dbx-tools/appkit-mastra-shared";
|
|
17
11
|
import type { MastraPluginConfig } from "./config.js";
|
|
18
|
-
export type { ServingEndpointSummary };
|
|
19
|
-
/**
|
|
20
|
-
* Structural type for the Databricks workspace client. Derived from
|
|
21
|
-
* AppKit's `ExecutionContext` so this module doesn't take a direct
|
|
22
|
-
* dependency on `@databricks/sdk-experimental`; the dep flows in
|
|
23
|
-
* transitively through `@databricks/appkit`.
|
|
24
|
-
*/
|
|
25
|
-
type WorkspaceClientLike = ReturnType<typeof getExecutionContext>["client"];
|
|
26
12
|
/**
|
|
27
13
|
* `RequestContext` key under which {@link MastraServer} stores the
|
|
28
14
|
* per-request model override (header / query / body). `model.ts`
|
|
@@ -35,76 +21,6 @@ export declare const MODEL_OVERRIDE_HEADER = "x-mastra-model";
|
|
|
35
21
|
export declare const MODEL_OVERRIDE_QUERY = "model";
|
|
36
22
|
/** Body fields (in priority order) inspected for a per-request model override. */
|
|
37
23
|
export declare const MODEL_OVERRIDE_BODY_FIELDS: readonly ["model", "modelId"];
|
|
38
|
-
/**
|
|
39
|
-
* List Model Serving endpoints for the workspace owning `client`,
|
|
40
|
-
* routed through AppKit's `CacheManager`. The manager gives us
|
|
41
|
-
* everything `cachetools.TTLCache` provides plus what
|
|
42
|
-
* `cachetools-async` adds on top: per-entry TTL, in-flight request
|
|
43
|
-
* coalescing (concurrent callers share one fetch via the manager's
|
|
44
|
-
* internal `inFlightRequests` map), bounded size, telemetry spans
|
|
45
|
-
* (`cache.getOrExecute`), and optional Lakebase persistence so the
|
|
46
|
-
* catalogue survives restarts when the lakebase plugin is wired up.
|
|
47
|
-
*
|
|
48
|
-
* Returns plain {@link ServingEndpointSummary} objects (a stable
|
|
49
|
-
* subset of the SDK type) so cache hits never expose stale SDK
|
|
50
|
-
* internals. Errors from `CacheManager` or the SDK fetch propagate
|
|
51
|
-
* to the caller - we don't swallow them so users see the real
|
|
52
|
-
* auth / network issue.
|
|
53
|
-
*
|
|
54
|
-
* @param host - Workspace host used as the cache key. Pass the value
|
|
55
|
-
* resolved from `client.config.getHost()` so multi-host apps share
|
|
56
|
-
* one entry per workspace.
|
|
57
|
-
* @param opts.ttlMs - Override the default TTL just for this call.
|
|
58
|
-
* Forwarded to `CacheManager` as seconds.
|
|
59
|
-
*/
|
|
60
|
-
export declare function listServingEndpoints(client: WorkspaceClientLike, host: string, opts?: {
|
|
61
|
-
ttlMs?: number;
|
|
62
|
-
}): Promise<ServingEndpointSummary[]>;
|
|
63
|
-
/**
|
|
64
|
-
* Force-evict cached endpoint listings via AppKit's `CacheManager`.
|
|
65
|
-
* With a `host` deletes that one workspace's entry; without one
|
|
66
|
-
* clears every cache entry on the manager (since `CacheManager`
|
|
67
|
-
* doesn't expose a namespace-scoped clear, this is the brute-force
|
|
68
|
-
* path - fine for tests, avoid in steady-state code).
|
|
69
|
-
*/
|
|
70
|
-
export declare function clearServingEndpointsCache(host?: string): Promise<void>;
|
|
71
|
-
/**
|
|
72
|
-
* Result of fuzzy-resolving a user-supplied model name against the
|
|
73
|
-
* live endpoint list. `score` is Fuse.js's distance (`0` is exact,
|
|
74
|
-
* `1` is no match); `matched` is `false` when the score exceeds the
|
|
75
|
-
* configured threshold so callers can fall back to the original
|
|
76
|
-
* input (Databricks will then return a clean 404).
|
|
77
|
-
*/
|
|
78
|
-
export interface ResolvedModel {
|
|
79
|
-
modelId: string;
|
|
80
|
-
matched: boolean;
|
|
81
|
-
score?: number;
|
|
82
|
-
}
|
|
83
|
-
/** Options accepted by {@link resolveModelId}. */
|
|
84
|
-
export interface ResolveModelOptions {
|
|
85
|
-
/** Fuse.js threshold (0 = exact, 1 = anything). Default `0.4`. */
|
|
86
|
-
threshold?: number;
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Snap a user-supplied model name to the closest configured serving
|
|
90
|
-
* endpoint:
|
|
91
|
-
*
|
|
92
|
-
* 1. Exact name match wins immediately (no fuzzy needed).
|
|
93
|
-
* 2. Otherwise the input is tokenized (dashes / underscores / spaces
|
|
94
|
-
* become separators) and fed through Fuse.js extended search,
|
|
95
|
-
* which AND-s each token with fuzzy matching enabled. This is the
|
|
96
|
-
* "tokenized fuzzy match" the user reaches for when they type
|
|
97
|
-
* `"claude sonnet"` instead of the full endpoint name.
|
|
98
|
-
* 3. If the best Fuse score is above `threshold`, return the input
|
|
99
|
-
* unchanged and let the upstream call surface the 404. This keeps
|
|
100
|
-
* deliberate model ids (e.g. brand new endpoints) from being
|
|
101
|
-
* silently rewritten to a similar-looking neighbour.
|
|
102
|
-
*
|
|
103
|
-
* Pass an empty endpoint list to short-circuit fuzzy matching - the
|
|
104
|
-
* input is returned verbatim. This is what {@link buildModel} does
|
|
105
|
-
* when the workspace client can't be reached at resolve time.
|
|
106
|
-
*/
|
|
107
|
-
export declare function resolveModelId(input: string, endpoints: readonly ServingEndpointSummary[], opts?: ResolveModelOptions): ResolvedModel;
|
|
108
24
|
/**
|
|
109
25
|
* Minimal Express-ish request shape used by {@link extractModelOverride}.
|
|
110
26
|
* Keeps this module independent of `express` so the helper can be
|
|
@@ -132,15 +48,15 @@ export interface ModelOverrideRequest {
|
|
|
132
48
|
export declare function extractModelOverride(req: ModelOverrideRequest): string | null;
|
|
133
49
|
/**
|
|
134
50
|
* Read the fuzzy-resolution config knobs off the plugin config with
|
|
135
|
-
* defaults applied. Kept here so `buildModel` and
|
|
136
|
-
* agree on what "enabled" means.
|
|
51
|
+
* `@dbx-tools/model` defaults applied. Kept here so `buildModel` and
|
|
52
|
+
* the `/models` route agree on what "enabled" means.
|
|
137
53
|
*
|
|
138
|
-
* `fallbacks` is the priority-ordered list `
|
|
139
|
-
* nothing explicit is set; defaults
|
|
140
|
-
*
|
|
141
|
-
*
|
|
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.
|
|
142
58
|
*/
|
|
143
|
-
export declare function resolveServingConfig(config: MastraPluginConfig
|
|
59
|
+
export declare function resolveServingConfig(config: MastraPluginConfig): {
|
|
144
60
|
ttlMs: number;
|
|
145
61
|
threshold: number;
|
|
146
62
|
fuzzy: boolean;
|