@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/package.json
CHANGED
|
@@ -9,17 +9,19 @@
|
|
|
9
9
|
}
|
|
10
10
|
},
|
|
11
11
|
"name": "@dbx-tools/appkit-mastra",
|
|
12
|
-
"version": "0.1.
|
|
12
|
+
"version": "0.1.13",
|
|
13
13
|
"type": "module",
|
|
14
14
|
"module": "index.ts",
|
|
15
15
|
"dependencies": {
|
|
16
|
-
"@dbx-tools/appkit-mastra-shared": "0.1.
|
|
17
|
-
"@dbx-tools/appkit-shared": "0.1.
|
|
16
|
+
"@dbx-tools/appkit-mastra-shared": "0.1.13",
|
|
17
|
+
"@dbx-tools/appkit-shared": "0.1.13",
|
|
18
18
|
"@mastra/ai-sdk": "^1.3",
|
|
19
19
|
"@mastra/core": "^1.32",
|
|
20
20
|
"@mastra/express": "^1.3",
|
|
21
21
|
"@mastra/fastembed": "^1.0",
|
|
22
22
|
"@mastra/memory": "^1.17",
|
|
23
|
+
"@mastra/observability": "^0.0.0-graph-crash-v3-20260528202217",
|
|
24
|
+
"@mastra/otel-exporter": "^0.0.0-graph-crash-v3-20260528202217",
|
|
23
25
|
"@mastra/pg": "^1.10",
|
|
24
26
|
"fuse.js": "^7.0.0",
|
|
25
27
|
"zod": "^4.3.6"
|
package/src/memory.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.
|
|
@@ -105,6 +113,34 @@ export class MemoryBuilder {
|
|
|
105
113
|
* vector store enabled - Mastra accepts a missing `memory` field
|
|
106
114
|
* and treats the agent as stateless.
|
|
107
115
|
*/
|
|
116
|
+
/**
|
|
117
|
+
* Build the Mastra-instance-level storage used for workflow
|
|
118
|
+
* snapshots. Returns `undefined` when plugin-level `storage` is
|
|
119
|
+
* disabled, in which case `agent.resumeStream()` (and therefore
|
|
120
|
+
* the `requireApproval` flow) will not be available.
|
|
121
|
+
*
|
|
122
|
+
* The store lives in a dedicated `mastra_instance` schema so it
|
|
123
|
+
* never collides with per-agent `mastra_<agentId>` namespaces.
|
|
124
|
+
* Workflow snapshots are not per-agent state; they belong to the
|
|
125
|
+
* `Mastra` instance that owns the workflow execution.
|
|
126
|
+
*/
|
|
127
|
+
instanceStorage(): PostgresStore | undefined {
|
|
128
|
+
const setting = this.config.storage;
|
|
129
|
+
if (!setting) return undefined;
|
|
130
|
+
if (typeof setting === "object") {
|
|
131
|
+
return new PostgresStore(
|
|
132
|
+
withId(setting, "mastra-store__instance") as ConstructorParameters<
|
|
133
|
+
typeof PostgresStore
|
|
134
|
+
>[0],
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
return new PostgresStore({
|
|
138
|
+
id: "mastra-store__instance",
|
|
139
|
+
schemaName: "mastra_instance",
|
|
140
|
+
pool: this.requirePool() as Pool,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
108
144
|
forAgent(agentId: string, def: MastraAgentDefinition): Memory | undefined {
|
|
109
145
|
const storageSetting = def.storage ?? this.config.storage;
|
|
110
146
|
const memorySetting = def.memory ?? this.config.memory;
|
|
@@ -0,0 +1,92 @@
|
|
|
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
|
+
|
|
21
|
+
import type { pluginUtils } from "@dbx-tools/appkit-shared";
|
|
22
|
+
import { Observability } from "@mastra/observability";
|
|
23
|
+
import { OtelExporter } from "@mastra/otel-exporter";
|
|
24
|
+
|
|
25
|
+
/** Plugin name the phoenix plugin registers under (matches `phoenix()`). */
|
|
26
|
+
const PHOENIX_PLUGIN_NAME = "phoenix";
|
|
27
|
+
|
|
28
|
+
/** Structural shape of the bits of `phoenix().exports()` we touch. */
|
|
29
|
+
interface PhoenixExportsLike {
|
|
30
|
+
collectorEndpoint?(): string | undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Structural shape of an AppKit plugin instance with `exports()`. */
|
|
34
|
+
interface PluginWithExports {
|
|
35
|
+
exports?(): unknown;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* If the sibling `phoenix` plugin is registered AND has booted with a
|
|
40
|
+
* usable collector URL, return a Mastra `Observability` configured to
|
|
41
|
+
* stream traces + logs there. Otherwise return `undefined` so the
|
|
42
|
+
* caller can omit the field on the `new Mastra({...})` constructor.
|
|
43
|
+
*
|
|
44
|
+
* The exporter uses `provider.custom` with `http/protobuf`, which is
|
|
45
|
+
* what Phoenix's `/v1/traces` endpoint speaks natively. Switching
|
|
46
|
+
* Phoenix to gRPC would be a one-line `protocol: "grpc"` change and
|
|
47
|
+
* a different exported URL.
|
|
48
|
+
*/
|
|
49
|
+
export function buildPhoenixObservability(
|
|
50
|
+
context: pluginUtils.PluginContextLike | undefined,
|
|
51
|
+
serviceName: string,
|
|
52
|
+
): Observability | undefined {
|
|
53
|
+
const endpoint = readPhoenixEndpoint(context);
|
|
54
|
+
if (!endpoint) return undefined;
|
|
55
|
+
|
|
56
|
+
return new Observability({
|
|
57
|
+
configs: {
|
|
58
|
+
phoenix: {
|
|
59
|
+
serviceName,
|
|
60
|
+
exporters: [
|
|
61
|
+
new OtelExporter({
|
|
62
|
+
provider: {
|
|
63
|
+
custom: {
|
|
64
|
+
endpoint,
|
|
65
|
+
protocol: "http/protobuf",
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
}),
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
},
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Pull the OTLP collector URL out of the registered `phoenix` plugin.
|
|
77
|
+
* Tolerant of the plugin being absent (returns `undefined`) and of a
|
|
78
|
+
* future shape change in its exports (anything that's not a string
|
|
79
|
+
* is ignored). The lookup is keyed off the registered plugin *name*
|
|
80
|
+
* so this file does not depend on `@dbx-tools/appkit-phoenix`.
|
|
81
|
+
*/
|
|
82
|
+
function readPhoenixEndpoint(
|
|
83
|
+
context: pluginUtils.PluginContextLike | undefined,
|
|
84
|
+
): string | undefined {
|
|
85
|
+
if (!context) return undefined;
|
|
86
|
+
const plugin = context.getPlugins().get(PHOENIX_PLUGIN_NAME) as
|
|
87
|
+
| PluginWithExports
|
|
88
|
+
| undefined;
|
|
89
|
+
const exports_ = plugin?.exports?.() as PhoenixExportsLike | undefined;
|
|
90
|
+
const url = exports_?.collectorEndpoint?.();
|
|
91
|
+
return typeof url === "string" ? url : undefined;
|
|
92
|
+
}
|
package/src/plugin.ts
CHANGED
|
@@ -48,6 +48,7 @@ import type { MastraClientConfig } from "@dbx-tools/appkit-mastra-shared";
|
|
|
48
48
|
import type { MastraPluginConfig } from "./config.js";
|
|
49
49
|
import { historyRoute } from "./history.js";
|
|
50
50
|
import { createMemoryBuilder, needsLakebase } from "./memory.js";
|
|
51
|
+
import { buildPhoenixObservability } from "./observability.js";
|
|
51
52
|
import { attachRoutePatchMiddleware, MastraServer } from "./server.js";
|
|
52
53
|
import {
|
|
53
54
|
clearServingEndpointsCache,
|
|
@@ -271,7 +272,26 @@ export class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
271
272
|
// dev server. Since we're hosting Mastra inside our own Express
|
|
272
273
|
// subapp via `@mastra/express`, custom routes must be passed to
|
|
273
274
|
// the `MastraServer` constructor directly.
|
|
274
|
-
|
|
275
|
+
//
|
|
276
|
+
// `storage` here is *Mastra-instance-level* and persists workflow
|
|
277
|
+
// snapshots (where suspended `requireApproval` tool calls live).
|
|
278
|
+
// It's separate from each agent's `Memory.storage`, which only
|
|
279
|
+
// covers thread / message history. Without it,
|
|
280
|
+
// `agent.resumeStream()` errors with "could not find a suspended
|
|
281
|
+
// run" and the approval UI hangs after the user clicks Approve.
|
|
282
|
+
const instanceStorage = memoryBuilder?.instanceStorage();
|
|
283
|
+
// Auto-wire OTLP trace export to the sibling `phoenix` plugin if
|
|
284
|
+
// it's registered. Returns undefined when phoenix isn't around so
|
|
285
|
+
// the field stays off the constructor and Mastra keeps its noop
|
|
286
|
+
// observability default. The serviceName is the plugin's bound
|
|
287
|
+
// name so multiple mastra instances in one process stay
|
|
288
|
+
// distinguishable in Phoenix.
|
|
289
|
+
const observability = buildPhoenixObservability(this.context, this.name);
|
|
290
|
+
this.mastra = new Mastra({
|
|
291
|
+
agents: this.built.agents,
|
|
292
|
+
...(instanceStorage ? { storage: instanceStorage } : {}),
|
|
293
|
+
...(observability ? { observability } : {}),
|
|
294
|
+
});
|
|
275
295
|
this.mastraApp = express();
|
|
276
296
|
attachRoutePatchMiddleware(this.mastraApp);
|
|
277
297
|
this.mastraServer = new MastraServer(this.config, {
|
|
@@ -290,6 +310,8 @@ export class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
290
310
|
agents: Object.keys(this.built.agents),
|
|
291
311
|
defaultAgent: this.built.defaultAgentId,
|
|
292
312
|
routes: ["/route/chat", "/route/history", "/models"],
|
|
313
|
+
instanceStorage: instanceStorage !== undefined,
|
|
314
|
+
observability: observability !== undefined ? "phoenix" : "off",
|
|
293
315
|
});
|
|
294
316
|
}
|
|
295
317
|
}
|