@dbx-tools/appkit-mastra 0.1.40 → 0.1.41
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/dist/src/memory.d.ts +34 -25
- package/dist/src/memory.js +48 -35
- package/dist/src/plugin.d.ts +15 -0
- package/dist/src/plugin.js +48 -8
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +6 -5
- package/src/memory.ts +48 -45
- package/src/plugin.ts +54 -8
package/dist/src/memory.d.ts
CHANGED
|
@@ -26,45 +26,55 @@
|
|
|
26
26
|
* (auto-defaulted to `true` in `plugin.ts` when the `lakebase` plugin
|
|
27
27
|
* is registered); per-agent settings cascade on top of that.
|
|
28
28
|
*/
|
|
29
|
-
import { lakebase } from "@databricks/appkit";
|
|
30
|
-
import { appkitUtils } from "@dbx-tools/shared";
|
|
31
29
|
import { Memory } from "@mastra/memory";
|
|
32
30
|
import { PostgresStore } from "@mastra/pg";
|
|
31
|
+
import { Pool, type PoolConfig } from "pg";
|
|
33
32
|
import type { MastraAgentDefinition } from "./agents.js";
|
|
34
33
|
import type { MastraPluginConfig } from "./config.js";
|
|
35
|
-
/** Pool handle returned by the AppKit `lakebase` plugin `exports().pool`. */
|
|
36
|
-
export type LakebasePool = ReturnType<InstanceType<ReturnType<typeof lakebase>["plugin"]>["exports"]>["pool"];
|
|
37
34
|
/**
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
35
|
+
* Build a dedicated **service-principal** Lakebase pool for Mastra
|
|
36
|
+
* memory from the lakebase plugin's resolved SP pg config.
|
|
37
|
+
*
|
|
38
|
+
* The plugin's `exports().pool` is a `RoutingPool` that switches to
|
|
39
|
+
* the per-user (OBO) pool whenever a query runs inside an `asUser`
|
|
40
|
+
* scope - exactly the context the mastra plugin establishes around
|
|
41
|
+
* every chat turn. Memory (threads / messages + semantic recall) must
|
|
42
|
+
* instead always act as the app service principal: it owns the
|
|
43
|
+
* auto-created `mastra_*` schemas (a per-user role usually can't
|
|
44
|
+
* `CREATE SCHEMA`) and is shared across users, so it cannot inherit a
|
|
45
|
+
* request's OBO identity.
|
|
46
|
+
*
|
|
47
|
+
* `pgConfig` must be the plugin's `exports().getPgConfig()` evaluated
|
|
48
|
+
* **outside** any `asUser` scope (i.e. during setup), so it carries
|
|
49
|
+
* the SP connection target, OAuth token-refresh `password` callback,
|
|
50
|
+
* and any `lakebase({ pool })` tuning overrides - all of which this
|
|
51
|
+
* pool inherits. See the call site in `plugin.ts`.
|
|
42
52
|
*/
|
|
43
|
-
export declare function
|
|
53
|
+
export declare function createServicePrincipalPool(pgConfig: PoolConfig): Promise<Pool>;
|
|
44
54
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
55
|
+
* True when any plugin-level or per-agent setting could need the
|
|
56
|
+
* Lakebase pool. Used by `plugin.ts` to gate creation of the
|
|
57
|
+
* service-principal pool and the {@link MemoryBuilder} that consumes
|
|
58
|
+
* it; when false neither is built.
|
|
49
59
|
*/
|
|
50
|
-
export declare function
|
|
60
|
+
export declare function needsLakebase(config: MastraPluginConfig): boolean;
|
|
51
61
|
/**
|
|
52
|
-
* Construct a per-agent {@link Memory} factory
|
|
53
|
-
*
|
|
54
|
-
*
|
|
62
|
+
* Construct a per-agent {@link Memory} factory bound to the supplied
|
|
63
|
+
* service-principal pool (see {@link createServicePrincipalPool}).
|
|
64
|
+
* Caches the shared `PgVector` singleton (built on first need) so each
|
|
65
|
+
* agent build is O(1) after the first.
|
|
55
66
|
*/
|
|
56
|
-
export declare function createMemoryBuilder(config: MastraPluginConfig,
|
|
67
|
+
export declare function createMemoryBuilder(config: MastraPluginConfig, servicePrincipalPool: Pool): MemoryBuilder;
|
|
57
68
|
/**
|
|
58
|
-
* Builds one `Memory` per agent
|
|
59
|
-
*
|
|
60
|
-
* registering N agents stays cheap.
|
|
69
|
+
* Builds one `Memory` per agent against a shared service-principal
|
|
70
|
+
* Lakebase pool. Per-instance state keeps the shared `PgVector` alive
|
|
71
|
+
* across calls so registering N agents stays cheap.
|
|
61
72
|
*/
|
|
62
73
|
export declare class MemoryBuilder {
|
|
63
74
|
private readonly config;
|
|
64
|
-
private readonly
|
|
75
|
+
private readonly servicePrincipalPool;
|
|
65
76
|
private sharedVector;
|
|
66
|
-
|
|
67
|
-
constructor(config: MastraPluginConfig, context: appkitUtils.PluginContextLike | undefined);
|
|
77
|
+
constructor(config: MastraPluginConfig, servicePrincipalPool: Pool);
|
|
68
78
|
/**
|
|
69
79
|
* Build a `Memory` for `agentId` after the plugin/agent cascade.
|
|
70
80
|
* Returns `undefined` when the agent has neither storage nor a
|
|
@@ -96,5 +106,4 @@ export declare class MemoryBuilder {
|
|
|
96
106
|
*/
|
|
97
107
|
private buildVector;
|
|
98
108
|
private getSharedVector;
|
|
99
|
-
private requirePool;
|
|
100
109
|
}
|
package/dist/src/memory.js
CHANGED
|
@@ -26,18 +26,46 @@
|
|
|
26
26
|
* (auto-defaulted to `true` in `plugin.ts` when the `lakebase` plugin
|
|
27
27
|
* is registered); per-agent settings cascade on top of that.
|
|
28
28
|
*/
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
29
|
+
import { getUsernameWithApiLookup } from "@databricks/appkit";
|
|
30
|
+
import { logUtils } from "@dbx-tools/shared";
|
|
31
31
|
import { fastembed } from "@mastra/fastembed";
|
|
32
32
|
import { Memory } from "@mastra/memory";
|
|
33
33
|
import { PgVector, PostgresStore } from "@mastra/pg";
|
|
34
34
|
import { randomUUID } from "node:crypto";
|
|
35
|
+
import { Pool } from "pg";
|
|
35
36
|
const log = logUtils.logger("mastra/memory");
|
|
37
|
+
/**
|
|
38
|
+
* Build a dedicated **service-principal** Lakebase pool for Mastra
|
|
39
|
+
* memory from the lakebase plugin's resolved SP pg config.
|
|
40
|
+
*
|
|
41
|
+
* The plugin's `exports().pool` is a `RoutingPool` that switches to
|
|
42
|
+
* the per-user (OBO) pool whenever a query runs inside an `asUser`
|
|
43
|
+
* scope - exactly the context the mastra plugin establishes around
|
|
44
|
+
* every chat turn. Memory (threads / messages + semantic recall) must
|
|
45
|
+
* instead always act as the app service principal: it owns the
|
|
46
|
+
* auto-created `mastra_*` schemas (a per-user role usually can't
|
|
47
|
+
* `CREATE SCHEMA`) and is shared across users, so it cannot inherit a
|
|
48
|
+
* request's OBO identity.
|
|
49
|
+
*
|
|
50
|
+
* `pgConfig` must be the plugin's `exports().getPgConfig()` evaluated
|
|
51
|
+
* **outside** any `asUser` scope (i.e. during setup), so it carries
|
|
52
|
+
* the SP connection target, OAuth token-refresh `password` callback,
|
|
53
|
+
* and any `lakebase({ pool })` tuning overrides - all of which this
|
|
54
|
+
* pool inherits. See the call site in `plugin.ts`.
|
|
55
|
+
*/
|
|
56
|
+
export async function createServicePrincipalPool(pgConfig) {
|
|
57
|
+
// `getPgConfig()` resolves the SP username synchronously from
|
|
58
|
+
// `PGUSER` / `DATABRICKS_CLIENT_ID`; fall back to the async API
|
|
59
|
+
// lookup (e.g. local dev authenticating via PAT) so the pool always
|
|
60
|
+
// has an identity to connect with.
|
|
61
|
+
const user = pgConfig.user ?? (await getUsernameWithApiLookup());
|
|
62
|
+
return new Pool({ ...pgConfig, user });
|
|
63
|
+
}
|
|
36
64
|
/**
|
|
37
65
|
* True when any plugin-level or per-agent setting could need the
|
|
38
|
-
* Lakebase pool. Used by `plugin.ts` to gate
|
|
39
|
-
*
|
|
40
|
-
*
|
|
66
|
+
* Lakebase pool. Used by `plugin.ts` to gate creation of the
|
|
67
|
+
* service-principal pool and the {@link MemoryBuilder} that consumes
|
|
68
|
+
* it; when false neither is built.
|
|
41
69
|
*/
|
|
42
70
|
export function needsLakebase(config) {
|
|
43
71
|
if (settingNeedsSharedPool(config.storage))
|
|
@@ -48,35 +76,26 @@ export function needsLakebase(config) {
|
|
|
48
76
|
return defs.some((d) => settingNeedsSharedPool(d.storage) || settingNeedsSharedPool(d.memory));
|
|
49
77
|
}
|
|
50
78
|
/**
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
79
|
+
* Construct a per-agent {@link Memory} factory bound to the supplied
|
|
80
|
+
* service-principal pool (see {@link createServicePrincipalPool}).
|
|
81
|
+
* Caches the shared `PgVector` singleton (built on first need) so each
|
|
82
|
+
* agent build is O(1) after the first.
|
|
55
83
|
*/
|
|
56
|
-
export function
|
|
57
|
-
return
|
|
84
|
+
export function createMemoryBuilder(config, servicePrincipalPool) {
|
|
85
|
+
return new MemoryBuilder(config, servicePrincipalPool);
|
|
58
86
|
}
|
|
59
87
|
/**
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*/
|
|
64
|
-
export function createMemoryBuilder(config, context) {
|
|
65
|
-
return new MemoryBuilder(config, context);
|
|
66
|
-
}
|
|
67
|
-
/**
|
|
68
|
-
* Builds one `Memory` per agent. Per-instance state keeps the shared
|
|
69
|
-
* `PgVector` and the resolved Lakebase pool alive across calls so
|
|
70
|
-
* registering N agents stays cheap.
|
|
88
|
+
* Builds one `Memory` per agent against a shared service-principal
|
|
89
|
+
* Lakebase pool. Per-instance state keeps the shared `PgVector` alive
|
|
90
|
+
* across calls so registering N agents stays cheap.
|
|
71
91
|
*/
|
|
72
92
|
export class MemoryBuilder {
|
|
73
93
|
config;
|
|
74
|
-
|
|
94
|
+
servicePrincipalPool;
|
|
75
95
|
sharedVector;
|
|
76
|
-
|
|
77
|
-
constructor(config, context) {
|
|
96
|
+
constructor(config, servicePrincipalPool) {
|
|
78
97
|
this.config = config;
|
|
79
|
-
this.
|
|
98
|
+
this.servicePrincipalPool = servicePrincipalPool;
|
|
80
99
|
}
|
|
81
100
|
/**
|
|
82
101
|
* Build a `Memory` for `agentId` after the plugin/agent cascade.
|
|
@@ -105,7 +124,7 @@ export class MemoryBuilder {
|
|
|
105
124
|
return new PostgresStore({
|
|
106
125
|
id: "mastra-store__instance",
|
|
107
126
|
schemaName: "mastra_instance",
|
|
108
|
-
pool: this.
|
|
127
|
+
pool: this.servicePrincipalPool,
|
|
109
128
|
});
|
|
110
129
|
}
|
|
111
130
|
forAgent(agentId, def) {
|
|
@@ -143,7 +162,7 @@ export class MemoryBuilder {
|
|
|
143
162
|
return new PostgresStore({
|
|
144
163
|
id: `mastra-store__${agentId}`,
|
|
145
164
|
schemaName: `mastra_${agentId}`,
|
|
146
|
-
pool: this.
|
|
165
|
+
pool: this.servicePrincipalPool,
|
|
147
166
|
});
|
|
148
167
|
}
|
|
149
168
|
// Cast: `withId` guarantees `id` is set, but the distributive
|
|
@@ -169,16 +188,10 @@ export class MemoryBuilder {
|
|
|
169
188
|
}
|
|
170
189
|
getSharedVector() {
|
|
171
190
|
if (!this.sharedVector) {
|
|
172
|
-
this.sharedVector = buildSharedPgVector(this.
|
|
191
|
+
this.sharedVector = buildSharedPgVector(this.servicePrincipalPool);
|
|
173
192
|
}
|
|
174
193
|
return this.sharedVector;
|
|
175
194
|
}
|
|
176
|
-
requirePool() {
|
|
177
|
-
if (!this.pool) {
|
|
178
|
-
this.pool = resolveLakebasePool(this.context, this.config);
|
|
179
|
-
}
|
|
180
|
-
return this.pool;
|
|
181
|
-
}
|
|
182
195
|
}
|
|
183
196
|
/**
|
|
184
197
|
* Build the shared `PgVector` that backs the default
|
package/dist/src/plugin.d.ts
CHANGED
|
@@ -59,6 +59,14 @@ export declare class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
59
59
|
private mastra;
|
|
60
60
|
private mastraApp;
|
|
61
61
|
private mastraServer;
|
|
62
|
+
/**
|
|
63
|
+
* Dedicated service-principal Lakebase pool backing Mastra memory /
|
|
64
|
+
* storage. Built once in {@link buildAgentAndServer} (outside any
|
|
65
|
+
* `asUser` scope, so it never inherits a request's OBO identity) and
|
|
66
|
+
* drained in {@link abortActiveOperations}. `null` until setup runs
|
|
67
|
+
* or when Lakebase isn't needed.
|
|
68
|
+
*/
|
|
69
|
+
private servicePrincipalPool;
|
|
62
70
|
setup(): Promise<void>;
|
|
63
71
|
/**
|
|
64
72
|
* When the `lakebase` plugin is registered, auto-enable `storage`
|
|
@@ -68,6 +76,13 @@ export declare class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
68
76
|
* already in the registry by the time this fires.
|
|
69
77
|
*/
|
|
70
78
|
private applyLakebaseAutoDefaults;
|
|
79
|
+
/**
|
|
80
|
+
* Drain the memory service-principal pool on shutdown. AppKit calls
|
|
81
|
+
* this during teardown; the lakebase plugin closes its own SP / OBO
|
|
82
|
+
* pools the same way. Fire-and-forget so shutdown isn't blocked on a
|
|
83
|
+
* slow drain, and clear the handle so a re-`setup()` rebuilds it.
|
|
84
|
+
*/
|
|
85
|
+
abortActiveOperations(): void;
|
|
71
86
|
exports(): {
|
|
72
87
|
/**
|
|
73
88
|
* Ids of every registered agent in registration order. Matches
|
package/dist/src/plugin.js
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
* AI SDK transport URL is `/api/mastra/route/chat/<agentId>`.
|
|
28
28
|
*/
|
|
29
29
|
import { genie, getExecutionContext, lakebase, Plugin, toPlugin, } from "@databricks/appkit";
|
|
30
|
-
import { appkitUtils, logUtils } from "@dbx-tools/shared";
|
|
30
|
+
import { appkitUtils, commonUtils, logUtils } from "@dbx-tools/shared";
|
|
31
31
|
import { chatRoute } from "@mastra/ai-sdk";
|
|
32
32
|
import { Mastra } from "@mastra/core/mastra";
|
|
33
33
|
import express from "express";
|
|
@@ -35,7 +35,7 @@ import { buildAgents, FALLBACK_AGENT_ID } from "./agents.js";
|
|
|
35
35
|
import { fetchChart } from "./chart.js";
|
|
36
36
|
import { collectSpaceSuggestions, resolveGenieSpaces } from "./genie.js";
|
|
37
37
|
import { historyRoute } from "./history.js";
|
|
38
|
-
import { createMemoryBuilder, needsLakebase } from "./memory.js";
|
|
38
|
+
import { createMemoryBuilder, createServicePrincipalPool, needsLakebase, } from "./memory.js";
|
|
39
39
|
import { buildObservability } from "./observability.js";
|
|
40
40
|
import { attachRoutePatchMiddleware, MastraServer } from "./server.js";
|
|
41
41
|
import { clearServingEndpointsCache, listServingEndpoints, resolveServingConfig, } from "./serving.js";
|
|
@@ -93,6 +93,14 @@ export class MastraPlugin extends Plugin {
|
|
|
93
93
|
mastra = null;
|
|
94
94
|
mastraApp = null;
|
|
95
95
|
mastraServer = null;
|
|
96
|
+
/**
|
|
97
|
+
* Dedicated service-principal Lakebase pool backing Mastra memory /
|
|
98
|
+
* storage. Built once in {@link buildAgentAndServer} (outside any
|
|
99
|
+
* `asUser` scope, so it never inherits a request's OBO identity) and
|
|
100
|
+
* drained in {@link abortActiveOperations}. `null` until setup runs
|
|
101
|
+
* or when Lakebase isn't needed.
|
|
102
|
+
*/
|
|
103
|
+
servicePrincipalPool = null;
|
|
96
104
|
async setup() {
|
|
97
105
|
// Wait until sibling plugins (e.g. `lakebase`) finish `setup()` so
|
|
98
106
|
// the lakebase pool is valid when storage/memory are enabled.
|
|
@@ -118,6 +126,25 @@ export class MastraPlugin extends Plugin {
|
|
|
118
126
|
if (this.config.memory === undefined)
|
|
119
127
|
this.config.memory = true;
|
|
120
128
|
}
|
|
129
|
+
/**
|
|
130
|
+
* Drain the memory service-principal pool on shutdown. AppKit calls
|
|
131
|
+
* this during teardown; the lakebase plugin closes its own SP / OBO
|
|
132
|
+
* pools the same way. Fire-and-forget so shutdown isn't blocked on a
|
|
133
|
+
* slow drain, and clear the handle so a re-`setup()` rebuilds it.
|
|
134
|
+
*/
|
|
135
|
+
abortActiveOperations() {
|
|
136
|
+
super.abortActiveOperations();
|
|
137
|
+
if (this.servicePrincipalPool) {
|
|
138
|
+
this.log.info("closing memory SP pool");
|
|
139
|
+
const pool = this.servicePrincipalPool;
|
|
140
|
+
this.servicePrincipalPool = null;
|
|
141
|
+
pool.end().catch((err) => {
|
|
142
|
+
this.log.error("error closing memory SP pool", {
|
|
143
|
+
error: commonUtils.errorMessage(err),
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
121
148
|
exports() {
|
|
122
149
|
return {
|
|
123
150
|
/**
|
|
@@ -402,12 +429,25 @@ export class MastraPlugin extends Plugin {
|
|
|
402
429
|
return listServingEndpoints(client, host, { ttlMs: serving.ttlMs });
|
|
403
430
|
}
|
|
404
431
|
async buildAgentAndServer() {
|
|
405
|
-
// Per-agent memory factory.
|
|
406
|
-
//
|
|
407
|
-
//
|
|
408
|
-
//
|
|
409
|
-
|
|
410
|
-
|
|
432
|
+
// Per-agent memory factory. When any storage / memory setting needs
|
|
433
|
+
// Postgres, stand up a dedicated service-principal pool first so
|
|
434
|
+
// memory acts as the app SP (owner of the `mastra_*` schemas),
|
|
435
|
+
// never the per-request OBO identity the chat turn runs under.
|
|
436
|
+
// `getPgConfig()` is read here, outside any `asUser` scope, so it
|
|
437
|
+
// returns the SP connection target + token refresh plus any
|
|
438
|
+
// `lakebase({ pool })` overrides; `require` turns a missing
|
|
439
|
+
// sibling into a clear wiring error. The builder caches the shared
|
|
440
|
+
// `PgVector` singleton so registering N agents stays cheap. See
|
|
441
|
+
// `./memory.js`.
|
|
442
|
+
if (needsLakebase(this.config)) {
|
|
443
|
+
const spPgConfig = appkitUtils
|
|
444
|
+
.require(this.context, lakebase, this.config)
|
|
445
|
+
.exports()
|
|
446
|
+
.getPgConfig();
|
|
447
|
+
this.servicePrincipalPool = await createServicePrincipalPool(spPgConfig);
|
|
448
|
+
}
|
|
449
|
+
const memoryBuilder = this.servicePrincipalPool
|
|
450
|
+
? createMemoryBuilder(this.config, this.servicePrincipalPool)
|
|
411
451
|
: undefined;
|
|
412
452
|
this.log.debug("build:start", {
|
|
413
453
|
lakebase: memoryBuilder !== undefined,
|