@dbx-tools/appkit-mastra 0.1.4 → 0.1.12
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 +145 -10
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/src/agents.d.ts +1 -1
- package/dist/src/agents.js +43 -19
- package/dist/src/chart.d.ts +170 -0
- package/dist/src/chart.js +491 -0
- package/dist/src/config.d.ts +13 -0
- package/dist/src/genie.d.ts +36 -14
- package/dist/src/genie.js +434 -75
- package/dist/src/history.d.ts +67 -0
- package/dist/src/history.js +172 -0
- package/dist/src/memory.js +15 -2
- package/dist/src/model.js +18 -14
- package/dist/src/plugin.d.ts +11 -1
- package/dist/src/plugin.js +28 -2
- package/dist/src/processors/strip-stale-charts.d.ts +29 -0
- package/dist/src/processors/strip-stale-charts.js +96 -0
- package/dist/src/server.d.ts +4 -0
- package/dist/src/server.js +59 -45
- package/dist/src/serving.js +19 -2
- package/dist/src/tools/email.d.ts +74 -0
- package/dist/src/tools/email.js +122 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/index.ts +2 -0
- package/package.json +21 -25
- package/src/agents.ts +46 -21
- package/src/chart.ts +593 -0
- package/src/config.ts +13 -0
- package/src/genie.ts +499 -102
- package/src/history.ts +210 -0
- package/src/memory.ts +19 -2
- package/src/model.ts +18 -13
- package/src/plugin.ts +30 -2
- package/src/processors/strip-stale-charts.ts +105 -0
- package/src/server.ts +76 -51
- package/src/serving.ts +21 -2
- package/src/tools/email.ts +147 -0
package/src/serving.ts
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
import { CacheManager, type getExecutionContext } from "@databricks/appkit";
|
|
24
|
-
import { stringUtils } from "@dbx-tools/appkit-shared";
|
|
24
|
+
import { logUtils, stringUtils } from "@dbx-tools/appkit-shared";
|
|
25
25
|
import Fuse from "fuse.js";
|
|
26
26
|
|
|
27
27
|
import type { ServingEndpointSummary } from "@dbx-tools/appkit-mastra-shared";
|
|
@@ -29,6 +29,8 @@ import type { MastraPluginConfig } from "./config.js";
|
|
|
29
29
|
|
|
30
30
|
export type { ServingEndpointSummary };
|
|
31
31
|
|
|
32
|
+
const log = logUtils.logger("mastra/serving");
|
|
33
|
+
|
|
32
34
|
/**
|
|
33
35
|
* Structural type for the Databricks workspace client. Derived from
|
|
34
36
|
* AppKit's `ExecutionContext` so this module doesn't take a direct
|
|
@@ -111,6 +113,7 @@ export async function listServingEndpoints(
|
|
|
111
113
|
async function fetchEndpoints(
|
|
112
114
|
client: WorkspaceClientLike,
|
|
113
115
|
): Promise<ServingEndpointSummary[]> {
|
|
116
|
+
const startedAt = Date.now();
|
|
114
117
|
const out: ServingEndpointSummary[] = [];
|
|
115
118
|
for await (const ep of client.servingEndpoints.list()) {
|
|
116
119
|
if (!ep.name) continue;
|
|
@@ -121,6 +124,7 @@ async function fetchEndpoints(
|
|
|
121
124
|
...(ep.description !== undefined ? { description: ep.description } : {}),
|
|
122
125
|
});
|
|
123
126
|
}
|
|
127
|
+
log.debug("listed", { count: out.length, elapsedMs: Date.now() - startedAt });
|
|
124
128
|
return out;
|
|
125
129
|
}
|
|
126
130
|
|
|
@@ -185,10 +189,12 @@ export function resolveModelId(
|
|
|
185
189
|
opts: ResolveModelOptions = {},
|
|
186
190
|
): ResolvedModel {
|
|
187
191
|
if (endpoints.length === 0) {
|
|
192
|
+
log.debug("resolve:no-endpoints", { input });
|
|
188
193
|
return { modelId: input, matched: false };
|
|
189
194
|
}
|
|
190
195
|
for (const ep of endpoints) {
|
|
191
196
|
if (ep.name === input) {
|
|
197
|
+
log.debug("resolve:exact", { input });
|
|
192
198
|
return { modelId: ep.name, matched: true, score: 0 };
|
|
193
199
|
}
|
|
194
200
|
}
|
|
@@ -208,12 +214,25 @@ export function resolveModelId(
|
|
|
208
214
|
const query = Array.from(
|
|
209
215
|
stringUtils.tokenizeWithOptions({ lowerCase: true, camelCase: false }, input),
|
|
210
216
|
).join(" ");
|
|
211
|
-
if (!query)
|
|
217
|
+
if (!query) {
|
|
218
|
+
log.debug("resolve:empty-tokens", { input });
|
|
219
|
+
return { modelId: input, matched: false };
|
|
220
|
+
}
|
|
212
221
|
const results = fuse.search(query);
|
|
213
222
|
const best = results[0];
|
|
214
223
|
if (best?.item.name && (best.score ?? 0) <= threshold) {
|
|
224
|
+
log.debug("resolve:fuzzy-match", {
|
|
225
|
+
input,
|
|
226
|
+
modelId: best.item.name,
|
|
227
|
+
score: best.score,
|
|
228
|
+
});
|
|
215
229
|
return { modelId: best.item.name, matched: true, score: best.score };
|
|
216
230
|
}
|
|
231
|
+
log.debug("resolve:no-match", {
|
|
232
|
+
input,
|
|
233
|
+
bestScore: best?.score,
|
|
234
|
+
threshold,
|
|
235
|
+
});
|
|
217
236
|
return { modelId: input, matched: false };
|
|
218
237
|
}
|
|
219
238
|
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mastra tool: `send_email`. Gated behind {@link requireApproval}
|
|
3
|
+
* so the model can call it freely but execution is paused until a
|
|
4
|
+
* human approves via the chat UI.
|
|
5
|
+
*
|
|
6
|
+
* The execute body is a stub - it logs the would-be email to the
|
|
7
|
+
* server console (via `logUtils.logger`) and returns success. Swap
|
|
8
|
+
* in a real SMTP / SES / Resend / Workspace Mail call later by
|
|
9
|
+
* editing the `execute` body; the tool surface and approval gate
|
|
10
|
+
* stay the same.
|
|
11
|
+
*
|
|
12
|
+
* Approval flow (Mastra + AI SDK V5):
|
|
13
|
+
*
|
|
14
|
+
* 1. Model calls the tool with `{ to, subject, body, ... }`.
|
|
15
|
+
* 2. Mastra evaluates `requireApproval` (here always `true`),
|
|
16
|
+
* pauses the agent loop, and emits a `tool-call-approval`
|
|
17
|
+
* chunk on the response stream.
|
|
18
|
+
* 3. The chat client renders an approve/deny prompt against the
|
|
19
|
+
* `state: 'approval-requested'` tool part. On approve, it sends
|
|
20
|
+
* a `MastraToolApproval` response back; on deny, the tool call
|
|
21
|
+
* is rejected and the model sees an error.
|
|
22
|
+
* 4. On approve, this `execute` runs and logs the email.
|
|
23
|
+
*
|
|
24
|
+
* The tool is intentionally NOT auto-installed on every agent -
|
|
25
|
+
* email is domain-specific, not infrastructure. Spread it into the
|
|
26
|
+
* specific agents that should be able to draft emails.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { logUtils, stringUtils } from "@dbx-tools/appkit-shared";
|
|
30
|
+
import { createTool } from "@mastra/core/tools";
|
|
31
|
+
import { z } from "zod";
|
|
32
|
+
|
|
33
|
+
const log = logUtils.logger("mastra/tool/send-email");
|
|
34
|
+
|
|
35
|
+
const emailInputSchema = z.object({
|
|
36
|
+
to: z.string().describe(stringUtils.toDescription`
|
|
37
|
+
Single recipient email address (e.g. "alice@example.com"). For
|
|
38
|
+
multiple recipients, comma-separate them yourself.
|
|
39
|
+
`),
|
|
40
|
+
subject: z.string().describe(stringUtils.toDescription`
|
|
41
|
+
Subject line.
|
|
42
|
+
`),
|
|
43
|
+
body: z.string().describe(stringUtils.toDescription`
|
|
44
|
+
Email body. Plain text or markdown; the renderer downstream
|
|
45
|
+
decides which to honour. Be specific - the recipient may not
|
|
46
|
+
have any context the model has from prior chat turns.
|
|
47
|
+
`),
|
|
48
|
+
cc: z
|
|
49
|
+
.array(z.string())
|
|
50
|
+
.optional()
|
|
51
|
+
.describe(stringUtils.toDescription`
|
|
52
|
+
Optional CC recipients.
|
|
53
|
+
`),
|
|
54
|
+
bcc: z
|
|
55
|
+
.array(z.string())
|
|
56
|
+
.optional()
|
|
57
|
+
.describe(stringUtils.toDescription`
|
|
58
|
+
Optional BCC recipients.
|
|
59
|
+
`),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
const emailOutputSchema = z.object({
|
|
63
|
+
sent: z.boolean().describe(stringUtils.toDescription`
|
|
64
|
+
True when the email was dispatched. The current implementation
|
|
65
|
+
always returns true after console-logging the would-be email;
|
|
66
|
+
swap in a real provider to make this meaningful.
|
|
67
|
+
`),
|
|
68
|
+
recipient: z.string().describe(stringUtils.toDescription`
|
|
69
|
+
Echo of the \`to\` field for confirmation.
|
|
70
|
+
`),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
/** Options accepted by {@link buildEmailTool}. */
|
|
74
|
+
export interface BuildEmailToolOptions {
|
|
75
|
+
/**
|
|
76
|
+
* Override the tool id. Defaults to `"send_email"`. Useful if a
|
|
77
|
+
* caller wants `send_internal_email` / `send_external_email`
|
|
78
|
+
* variants.
|
|
79
|
+
*/
|
|
80
|
+
id?: string;
|
|
81
|
+
/**
|
|
82
|
+
* Replace the default execute body with a real provider call.
|
|
83
|
+
* Receives the validated input and must return `{sent, recipient}`.
|
|
84
|
+
* The console-log default is meant for demos / dev; production
|
|
85
|
+
* deployments should wire SMTP / SES / Resend / Workspace Mail
|
|
86
|
+
* here.
|
|
87
|
+
*/
|
|
88
|
+
send?: (input: z.infer<typeof emailInputSchema>) => Promise<void> | void;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Build the `send_email` tool. Approval-gated by default; the
|
|
93
|
+
* execute body either calls the supplied {@link send} hook or
|
|
94
|
+
* logs the email to the server console as a demo stub.
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```ts
|
|
98
|
+
* import { buildEmailTool, createAgent, mastra } from "@dbx-tools/appkit-mastra";
|
|
99
|
+
*
|
|
100
|
+
* const support = createAgent({
|
|
101
|
+
* instructions: "...",
|
|
102
|
+
* tools(plugins) {
|
|
103
|
+
* return {
|
|
104
|
+
* ...(plugins.genie?.toolkit() ?? {}),
|
|
105
|
+
* send_email: buildEmailTool(),
|
|
106
|
+
* };
|
|
107
|
+
* },
|
|
108
|
+
* });
|
|
109
|
+
* ```
|
|
110
|
+
*/
|
|
111
|
+
export function buildEmailTool(opts: BuildEmailToolOptions = {}) {
|
|
112
|
+
return createTool({
|
|
113
|
+
id: opts.id ?? "send_email",
|
|
114
|
+
description: stringUtils.toDescription`
|
|
115
|
+
Send an email on the user's behalf. Pass a recipient
|
|
116
|
+
address, subject, and body; the user will be prompted to
|
|
117
|
+
approve the send before it goes out (the tool is
|
|
118
|
+
approval-gated). Use this when the user explicitly asks
|
|
119
|
+
to send / forward / share something via email - never
|
|
120
|
+
autonomously. Keep subjects short and bodies focused; the
|
|
121
|
+
recipient may not have any of the chat context.
|
|
122
|
+
`,
|
|
123
|
+
inputSchema: emailInputSchema,
|
|
124
|
+
outputSchema: emailOutputSchema,
|
|
125
|
+
requireApproval: true,
|
|
126
|
+
execute: async (input) => {
|
|
127
|
+
const { to, subject, body, cc, bcc } = input as z.infer<
|
|
128
|
+
typeof emailInputSchema
|
|
129
|
+
>;
|
|
130
|
+
// Default behaviour: dump the email to the server console so
|
|
131
|
+
// demos can see the gate fire end-to-end without a real
|
|
132
|
+
// provider. Replace by passing `opts.send`.
|
|
133
|
+
log.info("send", {
|
|
134
|
+
to,
|
|
135
|
+
...(cc && cc.length > 0 ? { cc } : {}),
|
|
136
|
+
...(bcc && bcc.length > 0 ? { bcc } : {}),
|
|
137
|
+
subject,
|
|
138
|
+
bodyLength: body.length,
|
|
139
|
+
body,
|
|
140
|
+
});
|
|
141
|
+
if (opts.send) {
|
|
142
|
+
await opts.send(input as z.infer<typeof emailInputSchema>);
|
|
143
|
+
}
|
|
144
|
+
return { sent: true, recipient: to };
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
}
|