@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.
@@ -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 plus `@mastra/ai-sdk` `chatRoute`
4
- * handlers. The UI message stream matches what `chatRoute()` emits, so
5
- * the client can use `useChat()` from `@ai-sdk/react` without custom
6
- * parsing.
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`. `chatRoute`
25
- * is registered at `/route/chat` (bound to `config.defaultAgent` or
26
- * the first registered id) and `/route/chat/:agentId`, so the
27
- * AI SDK transport URL is `/api/mastra/route/chat/<agentId>`.
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 `chatRoute` binds to when the client doesn't name
86
- * one. Resolves to `config.defaultAgent`, the first registered
87
- * id, or the built-in `default` fallback.
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.). */
@@ -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 plus `@mastra/ai-sdk` `chatRoute`
4
- * handlers. The UI message stream matches what `chatRoute()` emits, so
5
- * the client can use `useChat()` from `@ai-sdk/react` without custom
6
- * parsing.
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`. `chatRoute`
25
- * is registered at `/route/chat` (bound to `config.defaultAgent` or
26
- * the first registered id) and `/route/chat/:agentId`, so the
27
- * AI SDK transport URL is `/api/mastra/route/chat/<agentId>`.
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 { clearServingEndpointsCache, listServingEndpoints, resolveServingConfig, } from "./serving.js";
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 `chatRoute` binds to when the client doesn't name
137
- * one. Resolves to `config.defaultAgent`, the first registered
138
- * id, or the built-in `default` fallback.
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 the published paths stay
165
- // accurate if someone remounts the plugin under a custom id.
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("/models", (req, res, next) => {
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("/embed/:type/:id", (req, res, next) => {
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("/suggestions", handleSuggestions);
299
- router.get("/suggestions/:agentId", handleSuggestions);
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. The builder resolves the Lakebase pool
406
- // lazily (on first agent that actually needs storage / vector) and
407
- // caches both the pool and the shared `PgVector` singleton so
408
- // registering N agents stays cheap. See `./memory.js`.
409
- const memoryBuilder = needsLakebase(this.config)
410
- ? createMemoryBuilder(this.config, this.context)
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({ path: "/route/history", agent: this.built.defaultAgentId }),
464
- ...historyRoute({ path: "/route/history/:agentId" }),
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/chat", "/route/history", "/models"],
515
+ routes: ["/route/history", "/models", "/suggestions", "/embed/:type/:id"],
472
516
  instanceStorage: instanceStorage !== undefined,
473
517
  observability: observability !== undefined ? "mlflow" : "off",
474
518
  });
@@ -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 `@mastra/ai-sdk` `chatRoute` work behind an Express mount
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
- * `chatRoute` works when `MastraServer` is hosted on an Express subapp
40
- * mounted under a parent path (e.g. `/api/mastra`).
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
- * Two concerns:
43
- *
44
- * 1. The adapter's `registerCustomApiRoutes` matches against `req.path`
45
- * (mount-relative, correct) but dispatches to its internal Hono
46
- * mini-app using `req.originalUrl`, which still contains the parent
47
- * mount prefix. The Hono app registers the literal `chatRoute` paths
48
- * (for example `/route/chat`), so the absolute URL never matches
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;
@@ -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 `@mastra/ai-sdk` `chatRoute` work behind an Express mount
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
- * `chatRoute` works when `MastraServer` is hosted on an Express subapp
133
- * mounted under a parent path (e.g. `/api/mastra`).
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
- * Two concerns:
136
- *
137
- * 1. The adapter's `registerCustomApiRoutes` matches against `req.path`
138
- * (mount-relative, correct) but dispatches to its internal Hono
139
- * mini-app using `req.originalUrl`, which still contains the parent
140
- * mount prefix. The Hono app registers the literal `chatRoute` paths
141
- * (for example `/route/chat`), so the absolute URL never matches
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 isChat = req.path === "/route" || req.path.startsWith("/route/");
154
- if (!isChat)
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();
@@ -1,28 +1,14 @@
1
1
  /**
2
- * Dynamic model resolution against Databricks Model Serving.
2
+ * Mastra-specific glue over the generic `@dbx-tools/model` toolkit.
3
3
  *
4
- * Turns loose, human-typed model names into real serving-endpoint ids
5
- * so the same agent can target different endpoints without code
6
- * changes. The workspace's `/serving-endpoints` list is fetched once
7
- * per host and cached with a TTL, with concurrent callers sharing one
8
- * in-flight promise (the coalescing pattern of Python's
9
- * `cachetools-async`). Names are matched through `fuse.js` extended
10
- * search so tokens like `"claude sonnet"` snap to
11
- * `databricks-claude-sonnet-4-6`, and a per-request override can be
12
- * pulled from the request header, query string, or body so a model
13
- * can be swapped per call without redeploying.
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 the `/models` route
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 `pickModelId` walks when
139
- * nothing explicit is set; defaults live in `model.ts`
140
- * (`FALLBACK_MODEL_IDS`) and are passed in by callers to avoid a
141
- * circular import between `serving.ts` and `model.ts`.
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, defaultFallbacks?: readonly string[]): {
59
+ export declare function resolveServingConfig(config: MastraPluginConfig): {
144
60
  ttlMs: number;
145
61
  threshold: number;
146
62
  fuzzy: boolean;