@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
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Databricks Statement Execution helpers for the Mastra plugin.
|
|
3
|
+
*
|
|
4
|
+
* Wraps `client.statementExecution.getStatement` with the shape
|
|
5
|
+
* + size + error handling the plugin's tools and the
|
|
6
|
+
* `/embed/data/:id` route both need:
|
|
7
|
+
*
|
|
8
|
+
* - {@link fetchStatementData}: low-level fetch that returns the
|
|
9
|
+
* raw `{columns, rows, rowCount}` shape used by the
|
|
10
|
+
* `get_statement` tool's output, the `prepare_chart` tool's
|
|
11
|
+
* dataset resolver, and the route's response body. Coerces
|
|
12
|
+
* numeric strings to numbers so downstream charts /
|
|
13
|
+
* aggregations don't have to.
|
|
14
|
+
* - {@link STATEMENT_ROW_CAP}: hard cap callers (notably the
|
|
15
|
+
* `/embed/data/:id` route) clamp `limit` to so a
|
|
16
|
+
* runaway result set can't hose a response.
|
|
17
|
+
* - {@link isStatementNotFoundError}: structural detector that
|
|
18
|
+
* normalizes the SDK's two error classes plus the loose
|
|
19
|
+
* `does not exist` / `not found` message shapes into a single
|
|
20
|
+
* boolean - lets the route map upstream 404s to a clean
|
|
21
|
+
* HTTP 404 without coupling to SDK error-class identity.
|
|
22
|
+
*
|
|
23
|
+
* Not Genie-specific: a Databricks `statement_id` is workspace
|
|
24
|
+
* scoped and lives in the Statement Execution API regardless of
|
|
25
|
+
* which producer (Genie, a tool, a notebook, etc.) submitted the
|
|
26
|
+
* query. Co-located here so consumers can fetch / cap / handle
|
|
27
|
+
* 404s without reaching into the Genie tool module.
|
|
28
|
+
*/
|
|
29
|
+
import { ApiError, HttpError, WorkspaceClient } from "@databricks/sdk-experimental";
|
|
30
|
+
import { apiUtils } from "@dbx-tools/shared";
|
|
31
|
+
/**
|
|
32
|
+
* Hard server-side cap on rows returned by the
|
|
33
|
+
* `/embed/data/:id` route. Sized to keep responses small
|
|
34
|
+
* enough for inline tables to render snappily; the route surfaces
|
|
35
|
+
* a `truncated` flag whenever the upstream `rowCount` exceeds
|
|
36
|
+
* this so end users know they're seeing a sample.
|
|
37
|
+
*/
|
|
38
|
+
export const STATEMENT_ROW_CAP = 500;
|
|
39
|
+
/**
|
|
40
|
+
* Best-effort numeric coercion for the Statement Execution API's
|
|
41
|
+
* all-strings cells. Leaves non-numeric strings (and explicit
|
|
42
|
+
* `null`s) intact; everything else flows through `Number`.
|
|
43
|
+
*/
|
|
44
|
+
function coerceCell(cell) {
|
|
45
|
+
if (cell === null)
|
|
46
|
+
return null;
|
|
47
|
+
if (/^-?\d+(\.\d+)?$/.test(cell)) {
|
|
48
|
+
const n = Number(cell);
|
|
49
|
+
if (Number.isFinite(n))
|
|
50
|
+
return n;
|
|
51
|
+
}
|
|
52
|
+
return cell;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Fetch a single statement's rows via the Statement Execution API
|
|
56
|
+
* and reshape into the shared {@link GenieDatasetData} shape
|
|
57
|
+
* (column array + row records).
|
|
58
|
+
*
|
|
59
|
+
* Optional `limit` slices the returned `rows` client-side so the
|
|
60
|
+
* agent can scan a small sample without paging the full result
|
|
61
|
+
* set into context. `rowCount` always reflects the upstream total
|
|
62
|
+
* so callers know when the slice truncated.
|
|
63
|
+
*
|
|
64
|
+
* Exported because every consumer in the plugin (the
|
|
65
|
+
* `get_statement` tool, the `prepare_chart` dataset resolver, and
|
|
66
|
+
* the `/embed/data/:id` route) needs the exact same
|
|
67
|
+
* fetch + coercion pipeline so LLM-side `get_statement` output
|
|
68
|
+
* and UI-side `[data:<id>]` rendering stay shape-identical for
|
|
69
|
+
* the same `statement_id`.
|
|
70
|
+
*/
|
|
71
|
+
export async function fetchStatementData(client, statementId, options) {
|
|
72
|
+
const ctx = options?.signal ? apiUtils.toContext(options.signal) : undefined;
|
|
73
|
+
const r = await client.statementExecution.getStatement({ statement_id: statementId }, ctx);
|
|
74
|
+
const columns = (r.manifest?.schema?.columns ?? []).map((c) => c.name ?? "");
|
|
75
|
+
const dataArray = (r.result?.data_array ?? []);
|
|
76
|
+
const sliced = options?.limit !== undefined && options.limit >= 0
|
|
77
|
+
? dataArray.slice(0, options.limit)
|
|
78
|
+
: dataArray;
|
|
79
|
+
const rows = sliced.map((row) => {
|
|
80
|
+
const obj = {};
|
|
81
|
+
columns.forEach((col, i) => {
|
|
82
|
+
obj[col] = coerceCell(row[i] ?? null);
|
|
83
|
+
});
|
|
84
|
+
return obj;
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
columns,
|
|
88
|
+
rows,
|
|
89
|
+
rowCount: r.manifest?.total_row_count ?? dataArray.length,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* True when `err` looks like the Databricks SDK's "statement not
|
|
94
|
+
* found" error. Matches the typed {@link ApiError} 404 /
|
|
95
|
+
* `RESOURCE_DOES_NOT_EXIST` shape first, then falls back to the
|
|
96
|
+
* lower-level {@link HttpError} 404, then to a loose `does not
|
|
97
|
+
* exist` / `not found` message sniff for SDK shapes we haven't
|
|
98
|
+
* catalogued.
|
|
99
|
+
*
|
|
100
|
+
* Pulled into its own helper so callers (notably the
|
|
101
|
+
* `/embed/data/:id` route) stay decoupled from SDK
|
|
102
|
+
* error-class identity, and the conversion logic stays testable
|
|
103
|
+
* in isolation.
|
|
104
|
+
*/
|
|
105
|
+
export function isStatementNotFoundError(err) {
|
|
106
|
+
if (err instanceof ApiError) {
|
|
107
|
+
if (err.statusCode === 404)
|
|
108
|
+
return true;
|
|
109
|
+
if (err.errorCode === "RESOURCE_DOES_NOT_EXIST")
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
if (err instanceof HttpError && err.code === 404)
|
|
113
|
+
return true;
|
|
114
|
+
if (err instanceof Error && /does not exist|not found/i.test(err.message)) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
return false;
|
|
118
|
+
}
|
package/dist/src/tools/email.js
CHANGED
|
@@ -30,40 +30,40 @@ import { createTool } from "@mastra/core/tools";
|
|
|
30
30
|
import { z } from "zod";
|
|
31
31
|
const log = logUtils.logger("mastra/tool/send-email");
|
|
32
32
|
const emailInputSchema = z.object({
|
|
33
|
-
to: z.string().describe(stringUtils.toDescription
|
|
33
|
+
to: z.string().describe(stringUtils.toDescription(`
|
|
34
34
|
Single recipient email address (e.g. "alice@example.com"). For
|
|
35
35
|
multiple recipients, comma-separate them yourself.
|
|
36
|
-
`),
|
|
37
|
-
subject: z.string().describe(stringUtils.toDescription
|
|
36
|
+
`)),
|
|
37
|
+
subject: z.string().describe(stringUtils.toDescription(`
|
|
38
38
|
Subject line.
|
|
39
|
-
`),
|
|
40
|
-
body: z.string().describe(stringUtils.toDescription
|
|
41
|
-
Email body. Plain text or markdown; the renderer downstream
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
`),
|
|
39
|
+
`)),
|
|
40
|
+
body: z.string().describe(stringUtils.toDescription(`
|
|
41
|
+
Email body. Plain text or markdown; the renderer downstream decides
|
|
42
|
+
which to honour. Be specific - the recipient may not have any
|
|
43
|
+
context the model has from prior chat turns.
|
|
44
|
+
`)),
|
|
45
45
|
cc: z
|
|
46
46
|
.array(z.string())
|
|
47
47
|
.optional()
|
|
48
|
-
.describe(stringUtils.toDescription
|
|
48
|
+
.describe(stringUtils.toDescription(`
|
|
49
49
|
Optional CC recipients.
|
|
50
|
-
`),
|
|
50
|
+
`)),
|
|
51
51
|
bcc: z
|
|
52
52
|
.array(z.string())
|
|
53
53
|
.optional()
|
|
54
|
-
.describe(stringUtils.toDescription
|
|
54
|
+
.describe(stringUtils.toDescription(`
|
|
55
55
|
Optional BCC recipients.
|
|
56
|
-
`),
|
|
56
|
+
`)),
|
|
57
57
|
});
|
|
58
58
|
const emailOutputSchema = z.object({
|
|
59
|
-
sent: z.boolean().describe(stringUtils.toDescription
|
|
59
|
+
sent: z.boolean().describe(stringUtils.toDescription(`
|
|
60
60
|
True when the email was dispatched. The current implementation
|
|
61
|
-
always returns true after console-logging the would-be email;
|
|
62
|
-
|
|
63
|
-
`),
|
|
64
|
-
recipient: z.string().describe(stringUtils.toDescription
|
|
61
|
+
always returns true after console-logging the would-be email; swap
|
|
62
|
+
in a real provider to make this meaningful.
|
|
63
|
+
`)),
|
|
64
|
+
recipient: z.string().describe(stringUtils.toDescription(`
|
|
65
65
|
Echo of the \`to\` field for confirmation.
|
|
66
|
-
`),
|
|
66
|
+
`)),
|
|
67
67
|
});
|
|
68
68
|
/**
|
|
69
69
|
* Build the `send_email` tool. Approval-gated by default; the
|
|
@@ -88,15 +88,14 @@ const emailOutputSchema = z.object({
|
|
|
88
88
|
export function buildEmailTool(opts = {}) {
|
|
89
89
|
return createTool({
|
|
90
90
|
id: opts.id ?? "send_email",
|
|
91
|
-
description: stringUtils.toDescription
|
|
92
|
-
Send an email on the user's behalf. Pass a recipient
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
`,
|
|
91
|
+
description: stringUtils.toDescription(`
|
|
92
|
+
Send an email on the user's behalf. Pass a recipient address,
|
|
93
|
+
subject, and body; the user will be prompted to approve the send
|
|
94
|
+
before it goes out (the tool is approval-gated). Use this when
|
|
95
|
+
the user explicitly asks to send / forward / share something via
|
|
96
|
+
email - never autonomously. Keep subjects short and bodies
|
|
97
|
+
focused; the recipient may not have any of the chat context.
|
|
98
|
+
`),
|
|
100
99
|
inputSchema: emailInputSchema,
|
|
101
100
|
outputSchema: emailOutputSchema,
|
|
102
101
|
requireApproval: true,
|