@dbx-tools/appkit-mastra 0.1.25 → 0.1.27
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 +115 -303
- package/dist/src/agents.d.ts +10 -0
- package/dist/src/agents.js +15 -2
- package/dist/src/chart.d.ts +131 -79
- package/dist/src/chart.js +386 -279
- package/dist/src/config.d.ts +19 -16
- package/dist/src/genie.d.ts +90 -106
- package/dist/src/genie.js +523 -642
- package/dist/src/intercept.d.ts +48 -0
- package/dist/src/intercept.js +167 -0
- package/dist/src/plugin.d.ts +12 -0
- package/dist/src/plugin.js +185 -1
- package/dist/src/processors/strip-stale-charts.d.ts +16 -13
- package/dist/src/processors/strip-stale-charts.js +16 -13
- package/dist/src/statement.d.ts +73 -0
- package/dist/src/statement.js +118 -0
- package/dist/src/tools/email.js +27 -28
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/src/agents.ts +16 -2
- package/src/chart.ts +499 -319
- package/src/config.ts +19 -16
- package/src/genie.ts +560 -768
- package/src/intercept.ts +206 -0
- package/src/plugin.ts +210 -2
- package/src/processors/strip-stale-charts.ts +16 -13
- package/src/statement.ts +127 -0
- package/src/tools/email.ts +27 -28
package/README.md
CHANGED
|
@@ -102,18 +102,18 @@ The plugin is opinionated about what "no config" should mean. Everything
|
|
|
102
102
|
below can be overridden, but the bare path works:
|
|
103
103
|
|
|
104
104
|
|
|
105
|
-
| Scenario | What you get
|
|
106
|
-
| --------------------------------------------------- |
|
|
107
|
-
| `mastra()` | One built-in `default` analyst agent, model from `/serving-endpoints`; memory + storage auto-on if the `lakebase` plugin is registered
|
|
108
|
-
| `mastra({ agents: def })` | Single-agent shorthand - `def` is registered and marked as default
|
|
109
|
-
| `mastra({ agents: [def1, def2] })` | Array shorthand - keys come from each `def.name` (or `agent_<i>`); first one is default
|
|
110
|
-
| `mastra({ agents: { x: def, y: def }})` | Record - keys are the registered ids; first key is the default
|
|
111
|
-
| No `defaultAgent` set | First registered agent wins
|
|
105
|
+
| Scenario | What you get |
|
|
106
|
+
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
107
|
+
| `mastra()` | One built-in `default` analyst agent, model from `/serving-endpoints`; memory + storage auto-on if the `lakebase` plugin is registered |
|
|
108
|
+
| `mastra({ agents: def })` | Single-agent shorthand - `def` is registered and marked as default |
|
|
109
|
+
| `mastra({ agents: [def1, def2] })` | Array shorthand - keys come from each `def.name` (or `agent_<i>`); first one is default |
|
|
110
|
+
| `mastra({ agents: { x: def, y: def }})` | Record - keys are the registered ids; first key is the default |
|
|
111
|
+
| No `defaultAgent` set | First registered agent wins |
|
|
112
112
|
| No `model` on an agent | Falls back to `config.defaultModel`, then `DATABRICKS_SERVING_ENDPOINT_NAME`, then walks `defaultModelFallbacks` (Thinking → Balanced → Fast tiers, Claude ↔ GPT ↔ Gemini interleaved within each, then open-weights) and picks the first endpoint actually present in the workspace |
|
|
113
|
-
| No `name` on a definition | Uses the registry key as `Agent.name`
|
|
114
|
-
| No `tools` on an agent | Inherits plugin-level `config.tools` ambient set (if any)
|
|
115
|
-
| No `storage` / `memory` and `lakebase()` registered | Both auto-default to `true`. Pass `false` (or a custom config object) on the plugin or an agent to opt out / override.
|
|
116
|
-
| `storage` / `memory` on an agent | Cascades from plugin: storage namespaces **per-agent** (`schemaName: "mastra_<agentId>"`), vector recall is **shared** across agents on one `PgVector` singleton.
|
|
113
|
+
| No `name` on a definition | Uses the registry key as `Agent.name` |
|
|
114
|
+
| No `tools` on an agent | Inherits plugin-level `config.tools` ambient set (if any) |
|
|
115
|
+
| No `storage` / `memory` and `lakebase()` registered | Both auto-default to `true`. Pass `false` (or a custom config object) on the plugin or an agent to opt out / override. |
|
|
116
|
+
| `storage` / `memory` on an agent | Cascades from plugin: storage namespaces **per-agent** (`schemaName: "mastra_<agentId>"`), vector recall is **shared** across agents on one `PgVector` singleton. |
|
|
117
117
|
|
|
118
118
|
|
|
119
119
|
Every field on `MastraAgentDefinition`:
|
|
@@ -190,72 +190,79 @@ that implements the standard `getAgentTools()` + `executeAgentTool()` +
|
|
|
190
190
|
`executeAgentTool`, so OBO auth (`asUser`) and telemetry spans stay
|
|
191
191
|
intact.
|
|
192
192
|
|
|
193
|
-
`plugins.genie` is special-cased: it
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
`
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
`
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
[`
|
|
193
|
+
`plugins.genie` is special-cased: it returns a flat set of
|
|
194
|
+
Mastra tools the central agent drives directly (no inner
|
|
195
|
+
orchestrator agent). Two shared, space-agnostic tools register
|
|
196
|
+
once regardless of how many spaces are wired:
|
|
197
|
+
|
|
198
|
+
- `get_statement(statement_id, limit?)` - fetch rows for a
|
|
199
|
+
Genie statement. Use only when the agent needs to read values
|
|
200
|
+
to reason about them; otherwise embed `[data:<statement_id>]`.
|
|
201
|
+
- `prepare_chart({statement_id, title?, description?})` - mint a
|
|
202
|
+
`chartId` (v4 UUID), kick off a background chart-planner task,
|
|
203
|
+
cache the resolved Echarts spec under the id for one hour.
|
|
204
|
+
Returns `{chartId}` synchronously so the agent can embed
|
|
205
|
+
`[chart:<chartId>]` in prose without blocking.
|
|
206
|
+
|
|
207
|
+
Three per-space tools register per wired alias (`ask_genie`,
|
|
208
|
+
`get_space_description`, `get_space_serialized` for the
|
|
209
|
+
`default` alias; `ask_genie_<alias>`, etc. for additional
|
|
210
|
+
aliases):
|
|
211
|
+
|
|
212
|
+
- `ask_genie({question})` - drives one `genieEventChat` turn
|
|
213
|
+
and streams wire events (status / thinking / sql) through
|
|
214
|
+
`ctx.writer`. Returns `{message: GenieMessage}`; rows are
|
|
215
|
+
NOT fetched eagerly.
|
|
216
|
+
- `get_space_description()` - cheap title / description /
|
|
217
|
+
warehouse id lookup.
|
|
218
|
+
- `get_space_serialized()` - full `GenieSpace` JSON for
|
|
219
|
+
column-level grounding when the description isn't enough.
|
|
220
|
+
|
|
221
|
+
AppKit's `genie()` plugin is honored only for its `spaces`
|
|
222
|
+
config (and the matching `app.yaml`-declared resources). The
|
|
223
|
+
tools talk to Genie directly via `@dbx-tools/genie`
|
|
224
|
+
(`genieEventChat`) and the workspace
|
|
225
|
+
`statementExecution.getStatement` API.
|
|
226
|
+
|
|
227
|
+
The orchestration prompt (decompose, ask Genie focused
|
|
228
|
+
sub-questions, place markers in prose) ships as the exported
|
|
229
|
+
`GENIE_INSTRUCTIONS` string - compose it into your agent's
|
|
230
|
+
`instructions` to get the canonical behavior:
|
|
206
231
|
|
|
207
232
|
```ts
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
title?: string;
|
|
217
|
-
description?: string;
|
|
218
|
-
dataset: {
|
|
219
|
-
data: { columns: string[]; rows: Row[]; rowCount: number };
|
|
220
|
-
chart?: { chartId: string; chartType: "bar" | "line" | "area" | "scatter" | "pie" };
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
>;
|
|
224
|
-
error?: string;
|
|
225
|
-
};
|
|
233
|
+
import { createAgent, GENIE_INSTRUCTIONS } from "@dbx-tools/appkit-mastra";
|
|
234
|
+
|
|
235
|
+
const support = createAgent({
|
|
236
|
+
instructions: `${baseInstructions}\n\n${GENIE_INSTRUCTIONS}`,
|
|
237
|
+
tools(plugins) {
|
|
238
|
+
return { ...plugins.genie?.toolkit() };
|
|
239
|
+
},
|
|
240
|
+
});
|
|
226
241
|
```
|
|
227
242
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
`chart` reference (just `chartId` + `chartType`); the resolved
|
|
233
|
-
Echarts spec rides a separate `type: "chart"` writer event so the
|
|
234
|
-
LLM never holds it in context. The host UI joins the dataset and
|
|
235
|
-
the writer event by `chartId` and renders inline at the matching
|
|
236
|
-
`[[chart:<chartId>]]` marker.
|
|
243
|
+
`GENIE_INSTRUCTIONS` references the default (`default` alias)
|
|
244
|
+
tool names. Multi-space deployments should write a custom
|
|
245
|
+
variant that names the suffixed per-space tools
|
|
246
|
+
(e.g. `ask_genie_sales`).
|
|
237
247
|
|
|
238
248
|
Genie writer event flow:
|
|
239
249
|
|
|
240
|
-
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
events (rows, SQL pill, chart specs) don't replay because the
|
|
257
|
-
resolved Echarts spec is held off-band on the per-request
|
|
258
|
-
`RequestContext`.
|
|
250
|
+
- Each `ask_genie` call emits a Mastra-only `started` event
|
|
251
|
+
before any network round-trip, then forwards every wire
|
|
252
|
+
`GenieChatEvent` (status / thinking / sql / rows / suggested
|
|
253
|
+
/ message / result / error) through `ctx.writer`.
|
|
254
|
+
- Charts ride out-of-band: `prepare_chart` mints a `chartId`
|
|
255
|
+
synchronously, the planner runs in the background and writes
|
|
256
|
+
the spec to the chart cache (1h TTL). The host UI long-polls
|
|
257
|
+
`${MastraClientConfig.embedPathTemplate}` (`/embed/chart/:id`)
|
|
258
|
+
by id and renders inline at the matching `[chart:<chartId>]`
|
|
259
|
+
marker.
|
|
260
|
+
- After a hard reload, the live `started` / `status` / `sql`
|
|
261
|
+
events are gone (they only ride the writer, not persisted
|
|
262
|
+
tool results); the central agent's text reply (with embedded
|
|
263
|
+
`[data:<statement_id>]` / `[chart:<chartId>]` markers) is
|
|
264
|
+
preserved in the message history and the host UI re-resolves
|
|
265
|
+
the markers on its own.
|
|
259
266
|
|
|
260
267
|
### `render_data` (system-default ambient tool)
|
|
261
268
|
|
|
@@ -272,73 +279,66 @@ set, an API response, a hand-built array, etc.).
|
|
|
272
279
|
|
|
273
280
|
#### How it works
|
|
274
281
|
|
|
275
|
-
`render_data` is a thin wrapper around `
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
Planner latency lands under the tool's trace span (it's
|
|
297
|
-
`await`ed directly), and the LLM-bound payload is just
|
|
298
|
-
`{ chartId }` so the model's context stays flat regardless of
|
|
299
|
-
dataset size. The Genie agent reuses `runChartPlanner` the
|
|
300
|
-
same way to embed charts in its structured summary, so the
|
|
301
|
-
backbone is shared without coupling the two paths to a
|
|
302
|
-
fire-and-forget writer contract.
|
|
282
|
+
`render_data` is a thin wrapper around `prepareChart`, the same
|
|
283
|
+
orchestrator the Genie `prepare_chart` tool uses. It mints a
|
|
284
|
+
`chartId` synchronously, caches an empty placeholder, and kicks
|
|
285
|
+
off the chart-planner in the background. The tool returns just
|
|
286
|
+
`{ chartId }` so the model can embed `[chart:<chartId>]` in
|
|
287
|
+
prose immediately without blocking on chart generation.
|
|
288
|
+
|
|
289
|
+
1. Mint a `chartId` (v4 UUID via `crypto.randomUUID()`).
|
|
290
|
+
2. Cache an empty `{ chartId }` placeholder so the first
|
|
291
|
+
`fetchChart` call always sees an entry (no spurious 404 race).
|
|
292
|
+
3. Fire-and-forget: resolve the dataset (trivial for
|
|
293
|
+
`render_data` since the rows are already in hand) and run
|
|
294
|
+
`runChartPlanner` in the background. On success the cache
|
|
295
|
+
entry settles with `{ chartId, result }` (containing the
|
|
296
|
+
`chartType` and full `EChartsOption`). On failure it settles
|
|
297
|
+
with `{ chartId, error }`.
|
|
298
|
+
4. Return `{ chartId }` to the LLM immediately.
|
|
299
|
+
|
|
300
|
+
The host UI resolves `[chart:<chartId>]` markers by hitting
|
|
301
|
+
the plugin's generic `GET /embed/chart/:id` route, which long-polls
|
|
302
|
+
until the entry settles or the server-side timeout elapses.
|
|
303
303
|
|
|
304
304
|
#### Inline placement contract
|
|
305
305
|
|
|
306
|
-
The model embeds `[
|
|
307
|
-
markdown reply at the position where the chart should appear
|
|
306
|
+
The model embeds `[chart:<chartId>]` on its own line in its
|
|
307
|
+
markdown reply at the position where the chart should appear
|
|
308
|
+
(the `<chartId>` is the v4 UUID the tool returned):
|
|
308
309
|
|
|
309
310
|
```markdown
|
|
310
311
|
## Audit Score
|
|
311
312
|
|
|
312
313
|
Audit Score is stable at ~94%, hovering between 93.5 and 95.0.
|
|
313
314
|
|
|
314
|
-
[
|
|
315
|
+
[chart:a3f9c1d2-7b4e-4c1a-9f2d-1e6b8c0a5d31]
|
|
315
316
|
|
|
316
317
|
## Service Time
|
|
317
318
|
|
|
318
319
|
Service time is the outlier at 162.5s, up from a target of 150s.
|
|
319
320
|
|
|
320
|
-
[
|
|
321
|
+
[chart:b7e2d4f1-3a9c-4e58-8d10-2f7a4b6c9e02]
|
|
321
322
|
```
|
|
322
323
|
|
|
323
324
|
The chat client splits the assistant text on these markers and
|
|
324
325
|
drops a `<ChartSlot>` in at each spot. A marker that arrives
|
|
325
|
-
before its
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
charts resolve.
|
|
326
|
+
before its chart settles in the cache shows a "Queueing chart"
|
|
327
|
+
skeleton; a chart whose marker the model forgot to place falls
|
|
328
|
+
through to the end of the reply as a fallback. All three states
|
|
329
|
+
(queueing, rendering, rendered) share the same fixed-height
|
|
330
|
+
frame so the layout doesn't jump as charts resolve.
|
|
331
331
|
|
|
332
332
|
#### Trade-offs
|
|
333
333
|
|
|
334
|
-
-
|
|
335
|
-
|
|
336
|
-
`render_data` again
|
|
337
|
-
|
|
334
|
+
- Chart entries live in the cache with a 1h TTL. After expiry
|
|
335
|
+
the `/embed/chart/:id` route returns 404. The model can call
|
|
336
|
+
`render_data` again on the next turn if the user wants the
|
|
337
|
+
chart back.
|
|
338
338
|
- The chart-planner is a separate model call per dataset (fast
|
|
339
339
|
tier, but still ~1-3s each). For an N-chart turn, latency is
|
|
340
|
-
`
|
|
341
|
-
|
|
340
|
+
`max(planners)` since the planners run concurrently in the
|
|
341
|
+
background and the tool returns immediately.
|
|
342
342
|
|
|
343
343
|
Plugins that aren't registered (or don't implement the toolkit
|
|
344
344
|
interface) resolve to `undefined` at runtime, so guard with `?.` /
|
|
@@ -551,192 +551,4 @@ Same payload from a sibling plugin or script (no HTTP round-trip):
|
|
|
551
551
|
import { appkitUtils } from "@dbx-tools/shared";
|
|
552
552
|
import { mastra } from "@dbx-tools/appkit-mastra";
|
|
553
553
|
|
|
554
|
-
const m = appkitUtils.require(this.context,
|
|
555
|
-
const endpoints = await m.asUser(req).listModels(); // user-scoped
|
|
556
|
-
m.clearModelsCache(); // force the next call to re-fetch
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
### Per-request model override
|
|
560
|
-
|
|
561
|
-
Any in-flight request can pick a different backing endpoint without
|
|
562
|
-
redeploying. Sources, checked in priority order:
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
| Source | Example |
|
|
566
|
-
| ------------------------- | ------------------------------------------------ |
|
|
567
|
-
| `X-Mastra-Model` header | `curl -H 'X-Mastra-Model: claude-haiku' ...` |
|
|
568
|
-
| `?model=` query parameter | `POST /api/mastra/route/chat?model=llama-70b` |
|
|
569
|
-
| Body `model` or `modelId` | `{ "messages": [...], "model": "claude-haiku" }` |
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
The override flows through the same fuzzy matcher as static ids, so
|
|
573
|
-
`X-Mastra-Model: claude sonnet` still snaps to
|
|
574
|
-
`databricks-claude-sonnet-4-6`. Set `modelOverride: false` on the
|
|
575
|
-
plugin config to disable the override path entirely (e.g. for a
|
|
576
|
-
multi-tenant deployment where untrusted clients shouldn't pick the
|
|
577
|
-
endpoint).
|
|
578
|
-
|
|
579
|
-
## Memory + storage
|
|
580
|
-
|
|
581
|
-
Memory and storage are split into two independent knobs and both auto-on
|
|
582
|
-
the moment the `lakebase` plugin is registered. Bare `mastra()` next to
|
|
583
|
-
`lakebase()` already gets you per-agent threads + shared semantic recall;
|
|
584
|
-
zero extra config required.
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
| Knob | Default when `lakebase()` is registered | What it backs |
|
|
588
|
-
| --------- | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
589
|
-
| `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()`). |
|
|
590
|
-
| `memory` | **Shared singleton** `PgVector` across every agent (cross-agent semantic recall on one index). | RAG-style recall over past messages via FastEmbed vectors. |
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
Override either at the plugin level, the agent level, or both. The agent
|
|
594
|
-
value wins when set; otherwise the plugin value cascades.
|
|
595
|
-
|
|
596
|
-
```ts
|
|
597
|
-
mastra({
|
|
598
|
-
// Plugin defaults. Either field becomes the cascading baseline.
|
|
599
|
-
// Omit entirely to inherit "auto-on when lakebase is present".
|
|
600
|
-
storage: true, // (default behavior when lakebase is registered)
|
|
601
|
-
memory: true, // (default behavior when lakebase is registered)
|
|
602
|
-
|
|
603
|
-
agents: {
|
|
604
|
-
analyst: createAgent({
|
|
605
|
-
instructions: "...",
|
|
606
|
-
// No overrides: inherits the auto-on defaults above.
|
|
607
|
-
// - threads stored under schema "mastra_analyst"
|
|
608
|
-
// - recalls from the shared vector index
|
|
609
|
-
}),
|
|
610
|
-
|
|
611
|
-
router: createAgent({
|
|
612
|
-
instructions: "Stateless routing agent.",
|
|
613
|
-
// Opt out of both for a fully stateless agent.
|
|
614
|
-
storage: false,
|
|
615
|
-
memory: false,
|
|
616
|
-
}),
|
|
617
|
-
|
|
618
|
-
legal: createAgent({
|
|
619
|
-
instructions: "Compliance-bounded assistant.",
|
|
620
|
-
// Private vector index so legal's recall doesn't bleed into
|
|
621
|
-
// analyst's. Threads still get their own per-agent schema.
|
|
622
|
-
memory: { connectionString: process.env.LEGAL_PG_URL!, /* ... */ },
|
|
623
|
-
}),
|
|
624
|
-
|
|
625
|
-
archive: createAgent({
|
|
626
|
-
instructions: "Read-only archive viewer.",
|
|
627
|
-
// Pin to a specific schema (e.g. shared with another service).
|
|
628
|
-
storage: {
|
|
629
|
-
schemaName: "shared_history",
|
|
630
|
-
pool: archivePool,
|
|
631
|
-
},
|
|
632
|
-
}),
|
|
633
|
-
},
|
|
634
|
-
});
|
|
635
|
-
```
|
|
636
|
-
|
|
637
|
-
Notes:
|
|
638
|
-
|
|
639
|
-
- `PostgresStore` runs `CREATE SCHEMA IF NOT EXISTS` on `init()`, so
|
|
640
|
-
per-agent schemas (and the shared `mastra_instance` schema) spring
|
|
641
|
-
into existence the first time the agent saves anything. No bundle /
|
|
642
|
-
migration step required.
|
|
643
|
-
- The `mastra_instance` schema exists so that Mastra-instance-level
|
|
644
|
-
artifacts (workflow snapshots used by `agent.resumeStream()`,
|
|
645
|
-
`requireApproval` flows, etc.) live in their own namespace and never
|
|
646
|
-
collide with per-agent thread / message tables. Disable the instance
|
|
647
|
-
store by setting `storage: false` at plugin level - approval-gated
|
|
648
|
-
tool calls will then error on resume.
|
|
649
|
-
- Disabling `lakebase()` from your plugin list while leaving `storage` /
|
|
650
|
-
`memory` truthy fails fast at setup with a clear "lakebase plugin not
|
|
651
|
-
registered" error.
|
|
652
|
-
- The `lakebase` plugin is declared as a **required** resource only when
|
|
653
|
-
`storage` / `memory` is explicitly truthy at registration time. Auto-on
|
|
654
|
-
defaults activate inside `setup:complete`, after lakebase is already
|
|
655
|
-
proven to be present.
|
|
656
|
-
|
|
657
|
-
## Runtime exports
|
|
658
|
-
|
|
659
|
-
Other plugins / route handlers can introspect the registry via the
|
|
660
|
-
`exports()` surface, modeled on AppKit's:
|
|
661
|
-
|
|
662
|
-
```ts
|
|
663
|
-
import { appkitUtils } from "@dbx-tools/shared";
|
|
664
|
-
import { mastra } from "@dbx-tools/appkit-mastra";
|
|
665
|
-
|
|
666
|
-
const m = appkitUtils.require(this.context, mastra).exports();
|
|
667
|
-
m.list(); // ["analyst", "helper"]
|
|
668
|
-
m.get("analyst"); // Agent | null
|
|
669
|
-
m.getDefault(); // Agent | null
|
|
670
|
-
m.getMastra(); // underlying Mastra instance (advanced)
|
|
671
|
-
m.listModels(); // Promise<ServingEndpointSummary[]> - cached + OBO when wrapped with asUser(req)
|
|
672
|
-
m.clearModelsCache(); // force the next listModels() to re-fetch
|
|
673
|
-
```
|
|
674
|
-
|
|
675
|
-
## Client wiring
|
|
676
|
-
|
|
677
|
-
`clientConfig()` publishes the mount paths, default agent id, and the
|
|
678
|
-
full registry to `usePluginClientConfig("mastra")` so the React client
|
|
679
|
-
never has to hardcode `/api/mastra` or rely on `DEFAULT_AGENT_ID`
|
|
680
|
-
constants. A tiny URL helper (`chatUrl`) and the `MastraClientConfig`
|
|
681
|
-
type ship from the standalone `@dbx-tools/appkit-mastra-shared`
|
|
682
|
-
package; that package is pure (no `pg` / `fastembed` / Mastra
|
|
683
|
-
dependencies) so it imports cleanly into Vite / Webpack / esbuild
|
|
684
|
-
builds.
|
|
685
|
-
|
|
686
|
-
```tsx
|
|
687
|
-
import { usePluginClientConfig } from "@databricks/appkit-ui/react";
|
|
688
|
-
import { chatUrl, type MastraClientConfig } from "@dbx-tools/appkit-mastra-shared";
|
|
689
|
-
import { useChat } from "@ai-sdk/react";
|
|
690
|
-
import { DefaultChatTransport } from "ai";
|
|
691
|
-
import { useMemo, useState } from "react";
|
|
692
|
-
|
|
693
|
-
function Chat() {
|
|
694
|
-
const config = usePluginClientConfig<MastraClientConfig>("mastra");
|
|
695
|
-
const [selected, setSelected] = useState<string>();
|
|
696
|
-
const api = chatUrl(config, selected); // defaults to config.defaultAgent
|
|
697
|
-
|
|
698
|
-
const transport = useMemo(() => new DefaultChatTransport({ api }), [api]);
|
|
699
|
-
const { messages, sendMessage } = useChat({ transport });
|
|
700
|
-
|
|
701
|
-
return (
|
|
702
|
-
<>
|
|
703
|
-
<select onChange={(e) => setSelected(e.target.value)}>
|
|
704
|
-
{config.agents.map((id) => (
|
|
705
|
-
<option key={id} value={id}>
|
|
706
|
-
{id}
|
|
707
|
-
</option>
|
|
708
|
-
))}
|
|
709
|
-
</select>
|
|
710
|
-
{/* render messages, etc. */}
|
|
711
|
-
</>
|
|
712
|
-
);
|
|
713
|
-
}
|
|
714
|
-
```
|
|
715
|
-
|
|
716
|
-
`MastraClientConfig` fields (all derived from the server-side plugin
|
|
717
|
-
mount, so a custom `mastra({ name: "myMastra" })` rewrites every path):
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
| Field | Example | Description |
|
|
721
|
-
| --------------------- | -------------------------------------- | -------------------------------------------------------------------- |
|
|
722
|
-
| `basePath` | `"/api/mastra"` | Plugin mount path. |
|
|
723
|
-
| `chatPath` | `"/api/mastra/route/chat"` | Default-agent chat URL. Use `chatUrl(config)` to get it. |
|
|
724
|
-
| `chatPathTemplate` | `"/api/mastra/route/chat/:agentId"` | OpenAPI-style template for tools / docs. |
|
|
725
|
-
| `modelsPath` | `"/api/mastra/models"` | `GET` cached endpoint catalogue. |
|
|
726
|
-
| `historyPath` | `"/api/mastra/route/history"` | Default-agent thread history. Use `historyUrl(config, opts)`. |
|
|
727
|
-
| `historyPathTemplate` | `"/api/mastra/route/history/:agentId"` | OpenAPI-style template for tools / docs. |
|
|
728
|
-
| `defaultAgent` | `"analyst"` | Agent id `chatRoute` binds to when none is supplied. |
|
|
729
|
-
| `agents` | `["analyst", "helper"]` | Every registered agent id in order. |
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
`chatUrl(config, agentId?)` returns `config.chatPath` for the default
|
|
733
|
-
agent (the registered `chatRoute` mount that omits `:agentId`), and
|
|
734
|
-
`${config.chatPath}/${encodeURIComponent(agentId)}` otherwise. The
|
|
735
|
-
sibling `historyUrl(config, { agentId?, page?, perPage? })` mirrors
|
|
736
|
-
that pattern for the `/history` endpoint and appends pagination
|
|
737
|
-
query parameters when provided. Both are pure functions: no React,
|
|
738
|
-
no hooks, safe in service workers and SSR.
|
|
739
|
-
|
|
740
|
-
## License
|
|
741
|
-
|
|
742
|
-
Apache-2.0
|
|
554
|
+
const m = appkitUtils.require(this.context, ma
|
package/dist/src/agents.d.ts
CHANGED
|
@@ -274,6 +274,16 @@ export interface BuiltAgents {
|
|
|
274
274
|
}
|
|
275
275
|
/** Fallback agent id used when `config.agents` is omitted entirely. */
|
|
276
276
|
export declare const FALLBACK_AGENT_ID = "default";
|
|
277
|
+
/**
|
|
278
|
+
* Default per-turn step ceiling applied to every registered agent
|
|
279
|
+
* when {@link MastraPluginConfig.agentMaxSteps} is unset. Sized to
|
|
280
|
+
* fit a decomposed Genie turn (grounding + several `ask_genie`
|
|
281
|
+
* calls + `prepare_chart` per dataset + the final-text reply) with
|
|
282
|
+
* headroom for the model to chain a couple of follow-ups before
|
|
283
|
+
* answering - well above Mastra's own `agent.generate` default of
|
|
284
|
+
* 5, which would cut multi-step orchestration off mid-loop.
|
|
285
|
+
*/
|
|
286
|
+
export declare const DEFAULT_AGENT_MAX_STEPS = 25;
|
|
277
287
|
/**
|
|
278
288
|
* Style guardrails appended to every agent's `instructions` to curb
|
|
279
289
|
* common LLM-isms (em dashes, emojis, sycophantic openers, excessive
|
package/dist/src/agents.js
CHANGED
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
import { appkitUtils, logUtils, stringUtils } from "@dbx-tools/shared";
|
|
16
16
|
import { Agent } from "@mastra/core/agent";
|
|
17
17
|
import { createTool } from "@mastra/core/tools";
|
|
18
|
-
import { buildRenderDataTool } from "./chart.js";
|
|
19
18
|
import { buildGenieToolkitProvider, resolveGenieSpaces } from "./genie.js";
|
|
20
19
|
import { buildModel, FALLBACK_MODEL_IDS } from "./model.js";
|
|
21
20
|
import { stripStaleChartsProcessor } from "./processors/strip-stale-charts.js";
|
|
21
|
+
import { buildRenderDataTool } from "./chart.js";
|
|
22
22
|
/** Re-export of Mastra's native `createTool` for full-feature access. */
|
|
23
23
|
export { createTool } from "@mastra/core/tools";
|
|
24
24
|
/**
|
|
@@ -90,6 +90,16 @@ export function createAgent(def) {
|
|
|
90
90
|
}
|
|
91
91
|
/** Fallback agent id used when `config.agents` is omitted entirely. */
|
|
92
92
|
export const FALLBACK_AGENT_ID = "default";
|
|
93
|
+
/**
|
|
94
|
+
* Default per-turn step ceiling applied to every registered agent
|
|
95
|
+
* when {@link MastraPluginConfig.agentMaxSteps} is unset. Sized to
|
|
96
|
+
* fit a decomposed Genie turn (grounding + several `ask_genie`
|
|
97
|
+
* calls + `prepare_chart` per dataset + the final-text reply) with
|
|
98
|
+
* headroom for the model to chain a couple of follow-ups before
|
|
99
|
+
* answering - well above Mastra's own `agent.generate` default of
|
|
100
|
+
* 5, which would cut multi-step orchestration off mid-loop.
|
|
101
|
+
*/
|
|
102
|
+
export const DEFAULT_AGENT_MAX_STEPS = 25;
|
|
93
103
|
const FALLBACK_AGENT_INSTRUCTIONS = `You are a data analyst. The user will ask questions about
|
|
94
104
|
business metrics and may share personal preferences you should remember across turns.
|
|
95
105
|
|
|
@@ -178,7 +188,7 @@ export async function buildAgents(opts) {
|
|
|
178
188
|
const style = resolveStyleInstructions(config);
|
|
179
189
|
// Default-on protection against the model copying turn-scoped
|
|
180
190
|
// chartIds from prior assistant tool results into the new
|
|
181
|
-
// turn's `[
|
|
191
|
+
// turn's `[chart:<id>]` markers. Opt out per-plugin via
|
|
182
192
|
// `config.stripStaleCharts: false`.
|
|
183
193
|
const inputProcessors = config.stripStaleCharts === false ? [] : [stripStaleChartsProcessor];
|
|
184
194
|
const agents = {};
|
|
@@ -191,6 +201,9 @@ export async function buildAgents(opts) {
|
|
|
191
201
|
...(def.description !== undefined ? { description: def.description } : {}),
|
|
192
202
|
instructions: composeInstructions(def.instructions, style),
|
|
193
203
|
model: resolveModel(config, def.model),
|
|
204
|
+
defaultOptions: {
|
|
205
|
+
maxSteps: config.agentMaxSteps ?? DEFAULT_AGENT_MAX_STEPS,
|
|
206
|
+
},
|
|
194
207
|
tools,
|
|
195
208
|
...(memory ? { memory } : {}),
|
|
196
209
|
...(inputProcessors.length > 0 ? { inputProcessors } : {}),
|