@dbx-tools/appkit-mastra 0.1.12 → 0.1.13
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 +13 -6
- package/dist/src/memory.d.ts +21 -0
- package/dist/src/memory.js +32 -0
- package/dist/src/observability.d.ts +33 -0
- package/dist/src/observability.js +71 -0
- package/dist/src/plugin.js +23 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +5 -3
- package/src/memory.ts +36 -0
- package/src/observability.ts +92 -0
- package/src/plugin.ts +23 -1
package/README.md
CHANGED
|
@@ -582,10 +582,10 @@ the moment the `lakebase` plugin is registered. Bare `mastra()` next to
|
|
|
582
582
|
zero extra config required.
|
|
583
583
|
|
|
584
584
|
|
|
585
|
-
| Knob | Default when `lakebase()` is registered | What it backs
|
|
586
|
-
| --------- | ----------------------------------------------------------------------------------------------------------------- |
|
|
587
|
-
| `storage` | **Per-agent** `PostgresStore` namespaced by `schemaName: "mastra_<agentId>"` so threads + messages stay isolated
|
|
588
|
-
| `memory` | **Shared singleton** `PgVector` across every agent (cross-agent semantic recall on one index). | RAG-style recall over past messages via FastEmbed vectors.
|
|
585
|
+
| Knob | Default when `lakebase()` is registered | What it backs |
|
|
586
|
+
| --------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
587
|
+
| `storage` | **Per-agent** `PostgresStore` namespaced by `schemaName: "mastra_<agentId>"` so threads + messages stay isolated, plus a Mastra-instance-level `PostgresStore` in schema `mastra_instance`. | Mastra threads, messages, working memory; Mastra-instance-level workflow snapshots (`requireApproval` / `agent.resumeStream()`). |
|
|
588
|
+
| `memory` | **Shared singleton** `PgVector` across every agent (cross-agent semantic recall on one index). | RAG-style recall over past messages via FastEmbed vectors. |
|
|
589
589
|
|
|
590
590
|
|
|
591
591
|
Override either at the plugin level, the agent level, or both. The agent
|
|
@@ -635,8 +635,15 @@ mastra({
|
|
|
635
635
|
Notes:
|
|
636
636
|
|
|
637
637
|
- `PostgresStore` runs `CREATE SCHEMA IF NOT EXISTS` on `init()`, so
|
|
638
|
-
per-agent schemas
|
|
639
|
-
|
|
638
|
+
per-agent schemas (and the shared `mastra_instance` schema) spring
|
|
639
|
+
into existence the first time the agent saves anything. No bundle /
|
|
640
|
+
migration step required.
|
|
641
|
+
- The `mastra_instance` schema exists so that Mastra-instance-level
|
|
642
|
+
artifacts (workflow snapshots used by `agent.resumeStream()`,
|
|
643
|
+
`requireApproval` flows, etc.) live in their own namespace and never
|
|
644
|
+
collide with per-agent thread / message tables. Disable the instance
|
|
645
|
+
store by setting `storage: false` at plugin level - approval-gated
|
|
646
|
+
tool calls will then error on resume.
|
|
640
647
|
- Disabling `lakebase()` from your plugin list while leaving `storage` /
|
|
641
648
|
`memory` truthy fails fast at setup with a clear "lakebase plugin not
|
|
642
649
|
registered" error.
|
package/dist/src/memory.d.ts
CHANGED
|
@@ -14,6 +14,14 @@
|
|
|
14
14
|
* index is almost always what users want; opt into per-agent recall
|
|
15
15
|
* by passing a {@link MastraMemoryConfigOverride} on the agent.
|
|
16
16
|
*
|
|
17
|
+
* Additionally, {@link MemoryBuilder.instanceStorage} returns a
|
|
18
|
+
* **Mastra-instance-level** `PostgresStore` (schema `mastra_instance`)
|
|
19
|
+
* used for workflow snapshots - the persistence layer
|
|
20
|
+
* `agent.resumeStream()` reads from when waking a suspended
|
|
21
|
+
* `requireApproval` tool call. Per-agent stores are not enough for
|
|
22
|
+
* this: workflow runs are scoped to the Mastra instance, not an
|
|
23
|
+
* individual agent's `Memory`.
|
|
24
|
+
*
|
|
17
25
|
* Plugin-level `config.storage` / `config.memory` act as the baseline
|
|
18
26
|
* (auto-defaulted to `true` in `plugin.ts` when the `lakebase` plugin
|
|
19
27
|
* is registered); per-agent settings cascade on top of that.
|
|
@@ -21,6 +29,7 @@
|
|
|
21
29
|
import { lakebase } from "@databricks/appkit";
|
|
22
30
|
import { pluginUtils } from "@dbx-tools/appkit-shared";
|
|
23
31
|
import { Memory } from "@mastra/memory";
|
|
32
|
+
import { PostgresStore } from "@mastra/pg";
|
|
24
33
|
import type { MastraAgentDefinition } from "./agents.js";
|
|
25
34
|
import type { MastraPluginConfig } from "./config.js";
|
|
26
35
|
/** Pool handle returned by the AppKit `lakebase` plugin `exports().pool`. */
|
|
@@ -62,6 +71,18 @@ export declare class MemoryBuilder {
|
|
|
62
71
|
* vector store enabled - Mastra accepts a missing `memory` field
|
|
63
72
|
* and treats the agent as stateless.
|
|
64
73
|
*/
|
|
74
|
+
/**
|
|
75
|
+
* Build the Mastra-instance-level storage used for workflow
|
|
76
|
+
* snapshots. Returns `undefined` when plugin-level `storage` is
|
|
77
|
+
* disabled, in which case `agent.resumeStream()` (and therefore
|
|
78
|
+
* the `requireApproval` flow) will not be available.
|
|
79
|
+
*
|
|
80
|
+
* The store lives in a dedicated `mastra_instance` schema so it
|
|
81
|
+
* never collides with per-agent `mastra_<agentId>` namespaces.
|
|
82
|
+
* Workflow snapshots are not per-agent state; they belong to the
|
|
83
|
+
* `Mastra` instance that owns the workflow execution.
|
|
84
|
+
*/
|
|
85
|
+
instanceStorage(): PostgresStore | undefined;
|
|
65
86
|
forAgent(agentId: string, def: MastraAgentDefinition): Memory | undefined;
|
|
66
87
|
private buildStorage;
|
|
67
88
|
/**
|
package/dist/src/memory.js
CHANGED
|
@@ -14,6 +14,14 @@
|
|
|
14
14
|
* index is almost always what users want; opt into per-agent recall
|
|
15
15
|
* by passing a {@link MastraMemoryConfigOverride} on the agent.
|
|
16
16
|
*
|
|
17
|
+
* Additionally, {@link MemoryBuilder.instanceStorage} returns a
|
|
18
|
+
* **Mastra-instance-level** `PostgresStore` (schema `mastra_instance`)
|
|
19
|
+
* used for workflow snapshots - the persistence layer
|
|
20
|
+
* `agent.resumeStream()` reads from when waking a suspended
|
|
21
|
+
* `requireApproval` tool call. Per-agent stores are not enough for
|
|
22
|
+
* this: workflow runs are scoped to the Mastra instance, not an
|
|
23
|
+
* individual agent's `Memory`.
|
|
24
|
+
*
|
|
17
25
|
* Plugin-level `config.storage` / `config.memory` act as the baseline
|
|
18
26
|
* (auto-defaulted to `true` in `plugin.ts` when the `lakebase` plugin
|
|
19
27
|
* is registered); per-agent settings cascade on top of that.
|
|
@@ -76,6 +84,30 @@ export class MemoryBuilder {
|
|
|
76
84
|
* vector store enabled - Mastra accepts a missing `memory` field
|
|
77
85
|
* and treats the agent as stateless.
|
|
78
86
|
*/
|
|
87
|
+
/**
|
|
88
|
+
* Build the Mastra-instance-level storage used for workflow
|
|
89
|
+
* snapshots. Returns `undefined` when plugin-level `storage` is
|
|
90
|
+
* disabled, in which case `agent.resumeStream()` (and therefore
|
|
91
|
+
* the `requireApproval` flow) will not be available.
|
|
92
|
+
*
|
|
93
|
+
* The store lives in a dedicated `mastra_instance` schema so it
|
|
94
|
+
* never collides with per-agent `mastra_<agentId>` namespaces.
|
|
95
|
+
* Workflow snapshots are not per-agent state; they belong to the
|
|
96
|
+
* `Mastra` instance that owns the workflow execution.
|
|
97
|
+
*/
|
|
98
|
+
instanceStorage() {
|
|
99
|
+
const setting = this.config.storage;
|
|
100
|
+
if (!setting)
|
|
101
|
+
return undefined;
|
|
102
|
+
if (typeof setting === "object") {
|
|
103
|
+
return new PostgresStore(withId(setting, "mastra-store__instance"));
|
|
104
|
+
}
|
|
105
|
+
return new PostgresStore({
|
|
106
|
+
id: "mastra-store__instance",
|
|
107
|
+
schemaName: "mastra_instance",
|
|
108
|
+
pool: this.requirePool(),
|
|
109
|
+
});
|
|
110
|
+
}
|
|
79
111
|
forAgent(agentId, def) {
|
|
80
112
|
const storageSetting = def.storage ?? this.config.storage;
|
|
81
113
|
const memorySetting = def.memory ?? this.config.memory;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mastra observability wiring for the `@dbx-tools/appkit-phoenix`
|
|
3
|
+
* sibling plugin.
|
|
4
|
+
*
|
|
5
|
+
* Mastra's `Observability` registry accepts any
|
|
6
|
+
* `@mastra/observability` `BaseExporter`. We use `OtelExporter` from
|
|
7
|
+
* `@mastra/otel-exporter` (Mastra's first-party OTLP shim) with the
|
|
8
|
+
* `custom` provider pointed at Phoenix's local collector URL. No
|
|
9
|
+
* Arize-specific wrapper is needed - Phoenix is a vanilla
|
|
10
|
+
* OpenInference-compatible OTLP/HTTP receiver.
|
|
11
|
+
*
|
|
12
|
+
* Discovery is structural so this module doesn't depend on
|
|
13
|
+
* `@dbx-tools/appkit-phoenix` at compile time: we look up the
|
|
14
|
+
* registered plugin by its registered name (`"phoenix"`) and read its
|
|
15
|
+
* `exports().collectorEndpoint()` if it is shaped like the phoenix
|
|
16
|
+
* plugin. The phoenix package is therefore an *optional* sibling -
|
|
17
|
+
* apps that don't install it just get an undefined observability
|
|
18
|
+
* config and Mastra runs without OTLP export.
|
|
19
|
+
*/
|
|
20
|
+
import type { pluginUtils } from "@dbx-tools/appkit-shared";
|
|
21
|
+
import { Observability } from "@mastra/observability";
|
|
22
|
+
/**
|
|
23
|
+
* If the sibling `phoenix` plugin is registered AND has booted with a
|
|
24
|
+
* usable collector URL, return a Mastra `Observability` configured to
|
|
25
|
+
* stream traces + logs there. Otherwise return `undefined` so the
|
|
26
|
+
* caller can omit the field on the `new Mastra({...})` constructor.
|
|
27
|
+
*
|
|
28
|
+
* The exporter uses `provider.custom` with `http/protobuf`, which is
|
|
29
|
+
* what Phoenix's `/v1/traces` endpoint speaks natively. Switching
|
|
30
|
+
* Phoenix to gRPC would be a one-line `protocol: "grpc"` change and
|
|
31
|
+
* a different exported URL.
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildPhoenixObservability(context: pluginUtils.PluginContextLike | undefined, serviceName: string): Observability | undefined;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mastra observability wiring for the `@dbx-tools/appkit-phoenix`
|
|
3
|
+
* sibling plugin.
|
|
4
|
+
*
|
|
5
|
+
* Mastra's `Observability` registry accepts any
|
|
6
|
+
* `@mastra/observability` `BaseExporter`. We use `OtelExporter` from
|
|
7
|
+
* `@mastra/otel-exporter` (Mastra's first-party OTLP shim) with the
|
|
8
|
+
* `custom` provider pointed at Phoenix's local collector URL. No
|
|
9
|
+
* Arize-specific wrapper is needed - Phoenix is a vanilla
|
|
10
|
+
* OpenInference-compatible OTLP/HTTP receiver.
|
|
11
|
+
*
|
|
12
|
+
* Discovery is structural so this module doesn't depend on
|
|
13
|
+
* `@dbx-tools/appkit-phoenix` at compile time: we look up the
|
|
14
|
+
* registered plugin by its registered name (`"phoenix"`) and read its
|
|
15
|
+
* `exports().collectorEndpoint()` if it is shaped like the phoenix
|
|
16
|
+
* plugin. The phoenix package is therefore an *optional* sibling -
|
|
17
|
+
* apps that don't install it just get an undefined observability
|
|
18
|
+
* config and Mastra runs without OTLP export.
|
|
19
|
+
*/
|
|
20
|
+
import { Observability } from "@mastra/observability";
|
|
21
|
+
import { OtelExporter } from "@mastra/otel-exporter";
|
|
22
|
+
/** Plugin name the phoenix plugin registers under (matches `phoenix()`). */
|
|
23
|
+
const PHOENIX_PLUGIN_NAME = "phoenix";
|
|
24
|
+
/**
|
|
25
|
+
* If the sibling `phoenix` plugin is registered AND has booted with a
|
|
26
|
+
* usable collector URL, return a Mastra `Observability` configured to
|
|
27
|
+
* stream traces + logs there. Otherwise return `undefined` so the
|
|
28
|
+
* caller can omit the field on the `new Mastra({...})` constructor.
|
|
29
|
+
*
|
|
30
|
+
* The exporter uses `provider.custom` with `http/protobuf`, which is
|
|
31
|
+
* what Phoenix's `/v1/traces` endpoint speaks natively. Switching
|
|
32
|
+
* Phoenix to gRPC would be a one-line `protocol: "grpc"` change and
|
|
33
|
+
* a different exported URL.
|
|
34
|
+
*/
|
|
35
|
+
export function buildPhoenixObservability(context, serviceName) {
|
|
36
|
+
const endpoint = readPhoenixEndpoint(context);
|
|
37
|
+
if (!endpoint)
|
|
38
|
+
return undefined;
|
|
39
|
+
return new Observability({
|
|
40
|
+
configs: {
|
|
41
|
+
phoenix: {
|
|
42
|
+
serviceName,
|
|
43
|
+
exporters: [
|
|
44
|
+
new OtelExporter({
|
|
45
|
+
provider: {
|
|
46
|
+
custom: {
|
|
47
|
+
endpoint,
|
|
48
|
+
protocol: "http/protobuf",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
}),
|
|
52
|
+
],
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Pull the OTLP collector URL out of the registered `phoenix` plugin.
|
|
59
|
+
* Tolerant of the plugin being absent (returns `undefined`) and of a
|
|
60
|
+
* future shape change in its exports (anything that's not a string
|
|
61
|
+
* is ignored). The lookup is keyed off the registered plugin *name*
|
|
62
|
+
* so this file does not depend on `@dbx-tools/appkit-phoenix`.
|
|
63
|
+
*/
|
|
64
|
+
function readPhoenixEndpoint(context) {
|
|
65
|
+
if (!context)
|
|
66
|
+
return undefined;
|
|
67
|
+
const plugin = context.getPlugins().get(PHOENIX_PLUGIN_NAME);
|
|
68
|
+
const exports_ = plugin?.exports?.();
|
|
69
|
+
const url = exports_?.collectorEndpoint?.();
|
|
70
|
+
return typeof url === "string" ? url : undefined;
|
|
71
|
+
}
|
package/dist/src/plugin.js
CHANGED
|
@@ -34,6 +34,7 @@ import express from "express";
|
|
|
34
34
|
import { buildAgents, FALLBACK_AGENT_ID } from "./agents.js";
|
|
35
35
|
import { historyRoute } from "./history.js";
|
|
36
36
|
import { createMemoryBuilder, needsLakebase } from "./memory.js";
|
|
37
|
+
import { buildPhoenixObservability } from "./observability.js";
|
|
37
38
|
import { attachRoutePatchMiddleware, MastraServer } from "./server.js";
|
|
38
39
|
import { clearServingEndpointsCache, listServingEndpoints, resolveServingConfig, } from "./serving.js";
|
|
39
40
|
const GENIE_MANIFEST = pluginUtils.data(genie).plugin.manifest;
|
|
@@ -236,7 +237,26 @@ export class MastraPlugin extends Plugin {
|
|
|
236
237
|
// dev server. Since we're hosting Mastra inside our own Express
|
|
237
238
|
// subapp via `@mastra/express`, custom routes must be passed to
|
|
238
239
|
// the `MastraServer` constructor directly.
|
|
239
|
-
|
|
240
|
+
//
|
|
241
|
+
// `storage` here is *Mastra-instance-level* and persists workflow
|
|
242
|
+
// snapshots (where suspended `requireApproval` tool calls live).
|
|
243
|
+
// It's separate from each agent's `Memory.storage`, which only
|
|
244
|
+
// covers thread / message history. Without it,
|
|
245
|
+
// `agent.resumeStream()` errors with "could not find a suspended
|
|
246
|
+
// run" and the approval UI hangs after the user clicks Approve.
|
|
247
|
+
const instanceStorage = memoryBuilder?.instanceStorage();
|
|
248
|
+
// Auto-wire OTLP trace export to the sibling `phoenix` plugin if
|
|
249
|
+
// it's registered. Returns undefined when phoenix isn't around so
|
|
250
|
+
// the field stays off the constructor and Mastra keeps its noop
|
|
251
|
+
// observability default. The serviceName is the plugin's bound
|
|
252
|
+
// name so multiple mastra instances in one process stay
|
|
253
|
+
// distinguishable in Phoenix.
|
|
254
|
+
const observability = buildPhoenixObservability(this.context, this.name);
|
|
255
|
+
this.mastra = new Mastra({
|
|
256
|
+
agents: this.built.agents,
|
|
257
|
+
...(instanceStorage ? { storage: instanceStorage } : {}),
|
|
258
|
+
...(observability ? { observability } : {}),
|
|
259
|
+
});
|
|
240
260
|
this.mastraApp = express();
|
|
241
261
|
attachRoutePatchMiddleware(this.mastraApp);
|
|
242
262
|
this.mastraServer = new MastraServer(this.config, {
|
|
@@ -255,6 +275,8 @@ export class MastraPlugin extends Plugin {
|
|
|
255
275
|
agents: Object.keys(this.built.agents),
|
|
256
276
|
defaultAgent: this.built.defaultAgentId,
|
|
257
277
|
routes: ["/route/chat", "/route/history", "/models"],
|
|
278
|
+
instanceStorage: instanceStorage !== undefined,
|
|
279
|
+
observability: observability !== undefined ? "phoenix" : "off",
|
|
258
280
|
});
|
|
259
281
|
}
|
|
260
282
|
}
|