@dbx-tools/appkit-mastra 0.9.153 → 0.9.157
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 +4 -3
- package/index.ts +32 -24
- package/lib/index.d.ts +32 -24
- package/lib/index.js +32 -23
- package/lib/src/agents.d.ts +5 -6
- package/lib/src/agents.js +12 -15
- package/lib/src/chart.js +33 -33
- package/lib/src/chat.js +10 -13
- package/lib/src/config.d.ts +12 -13
- package/lib/src/config.js +4 -4
- package/lib/src/evaluation.js +7 -9
- package/lib/src/feedback-mode.d.ts +1 -1
- package/lib/src/feedback-mode.js +1 -1
- package/lib/src/filesystems.js +6 -6
- package/lib/src/follow-up-suggestions.js +10 -10
- package/lib/src/genie-chat.js +27 -29
- package/lib/src/genie.d.ts +2 -2
- package/lib/src/genie.js +29 -29
- package/lib/src/identity.d.ts +3 -30
- package/lib/src/identity.js +2 -27
- package/lib/src/memory.d.ts +1 -1
- package/lib/src/memory.js +24 -6
- package/lib/src/mlflow.d.ts +4 -4
- package/lib/src/mlflow.js +15 -18
- package/lib/src/model.d.ts +3 -3
- package/lib/src/model.js +6 -6
- package/lib/src/monty-sandbox.js +8 -8
- package/lib/src/observability.d.ts +6 -5
- package/lib/src/observability.js +8 -6
- package/lib/src/plugin.d.ts +1 -1
- package/lib/src/plugin.js +49 -51
- package/lib/src/remote-skills.d.ts +3 -4
- package/lib/src/remote-skills.js +46 -46
- package/lib/src/rest.d.ts +2 -2
- package/lib/src/rest.js +3 -3
- package/lib/src/server.js +17 -20
- package/lib/src/serving.js +10 -11
- package/lib/src/statement.js +7 -7
- package/lib/src/storage-schema.js +4 -4
- package/lib/src/summarize.js +5 -5
- package/lib/src/telemetry.d.ts +0 -6
- package/lib/src/telemetry.js +34 -41
- package/lib/src/validation.js +3 -4
- package/lib/src/workspaces.d.ts +2 -2
- package/lib/src/workspaces.js +34 -36
- package/lib/src/writer.d.ts +2 -2
- package/lib/src/writer.js +3 -3
- package/package.json +17 -305
- package/src/agents.ts +27 -21
- package/src/chart.ts +43 -37
- package/src/chat.ts +10 -12
- package/src/config.ts +14 -15
- package/src/evaluation.ts +6 -8
- package/src/feedback-mode.ts +1 -1
- package/src/filesystems.ts +5 -5
- package/src/follow-up-suggestions.ts +9 -9
- package/src/genie-chat.ts +32 -31
- package/src/genie.ts +29 -29
- package/src/identity.ts +3 -38
- package/src/memory.ts +24 -6
- package/src/mlflow.ts +21 -21
- package/src/model.ts +7 -7
- package/src/monty-sandbox.ts +7 -7
- package/src/observability.ts +8 -6
- package/src/plugin.ts +78 -65
- package/src/remote-skills.ts +63 -55
- package/src/rest.ts +4 -4
- package/src/server.ts +32 -20
- package/src/serving.ts +14 -11
- package/src/statement.ts +10 -6
- package/src/storage-schema.ts +3 -3
- package/src/summarize.ts +4 -4
- package/src/telemetry.ts +49 -55
- package/src/validation.ts +2 -3
- package/src/workspaces.ts +52 -42
- package/src/writer.ts +3 -3
package/src/genie-chat.ts
CHANGED
|
@@ -13,16 +13,19 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import type { WorkspaceClient } from "@databricks/appkit";
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
16
|
+
import { toContext } from "@dbx-tools/appkit";
|
|
17
|
+
import { listAgentModeConversation, genieEventChat } from "@dbx-tools/genie";
|
|
18
|
+
import { log, toError, id, isRecord } from "@dbx-tools/shared-core";
|
|
19
|
+
import { type GenieMessage, eventsFromMessage, humanizeStatus } from "@dbx-tools/shared-genie";
|
|
20
20
|
import {
|
|
21
|
-
markers,
|
|
22
|
-
wire,
|
|
23
21
|
type GenieChatRequest,
|
|
24
22
|
type GenieThread,
|
|
25
23
|
type StartedEvent,
|
|
24
|
+
formatMarker,
|
|
25
|
+
GENIE_PROGRESS_PART_TYPE,
|
|
26
|
+
type GenieWriterEvent,
|
|
27
|
+
type GenieConversationPartData,
|
|
28
|
+
GENIE_CONVERSATION_PART_TYPE,
|
|
26
29
|
} from "@dbx-tools/shared-mastra";
|
|
27
30
|
import type { MastraDBMessage } from "@mastra/core/agent";
|
|
28
31
|
import type { MastraMemory } from "@mastra/core/memory";
|
|
@@ -90,7 +93,7 @@ export function genieAnswerText(message: GenieMessage): string {
|
|
|
90
93
|
const queryId = queriesByVisualization.get(visualizationId);
|
|
91
94
|
const statementId = queryId ? statementsByQuery.get(queryId) : undefined;
|
|
92
95
|
if (statementId) {
|
|
93
|
-
sections.push(
|
|
96
|
+
sections.push(formatMarker("visualization", statementId));
|
|
94
97
|
inlineStatements.add(statementId);
|
|
95
98
|
}
|
|
96
99
|
continue;
|
|
@@ -106,8 +109,8 @@ export function genieAnswerText(message: GenieMessage): string {
|
|
|
106
109
|
if (description) sections.push(description);
|
|
107
110
|
sections.push(
|
|
108
111
|
attachment.attachment_id && visualizedQueries.has(attachment.attachment_id)
|
|
109
|
-
?
|
|
110
|
-
:
|
|
112
|
+
? formatMarker("visualization", statementId)
|
|
113
|
+
: formatMarker("data", statementId),
|
|
111
114
|
);
|
|
112
115
|
}
|
|
113
116
|
if (sections.length === 0 && message.error?.error) sections.push(message.error.error);
|
|
@@ -115,19 +118,19 @@ export function genieAnswerText(message: GenieMessage): string {
|
|
|
115
118
|
}
|
|
116
119
|
|
|
117
120
|
function visualizationAttachmentId(metadata: unknown): string | undefined {
|
|
118
|
-
if (!
|
|
121
|
+
if (!isRecord(metadata) || !isRecord(metadata.viz)) return undefined;
|
|
119
122
|
return typeof metadata.viz.attachment_id === "string" ? metadata.viz.attachment_id : undefined;
|
|
120
123
|
}
|
|
121
124
|
|
|
122
125
|
/** Whether a text attachment is Agent Mode's Markdown copy of a query result. */
|
|
123
126
|
function isQueryOutputText(metadata: unknown): boolean {
|
|
124
|
-
return
|
|
127
|
+
return isRecord(metadata) && typeof metadata.statement_id === "string";
|
|
125
128
|
}
|
|
126
129
|
|
|
127
130
|
/** Error text for a terminal Genie message that did not complete, or `undefined`. */
|
|
128
131
|
function genieFailure(message: GenieMessage): string | undefined {
|
|
129
132
|
if (message.status === "COMPLETED") return undefined;
|
|
130
|
-
return message.error?.error ?? `Genie ${
|
|
133
|
+
return message.error?.error ?? `Genie ${humanizeStatus(message.status!)}`;
|
|
131
134
|
}
|
|
132
135
|
|
|
133
136
|
/** Recover the original user question from a context-augmented Genie input. */
|
|
@@ -140,9 +143,7 @@ export function visibleGenieQuestion(content: string): string {
|
|
|
140
143
|
function messageText(message: MastraDBMessage): string | undefined {
|
|
141
144
|
const text = message.content.parts
|
|
142
145
|
.flatMap((part) =>
|
|
143
|
-
|
|
144
|
-
? [part.text]
|
|
145
|
-
: [],
|
|
146
|
+
isRecord(part) && part.type === "text" && typeof part.text === "string" ? [part.text] : [],
|
|
146
147
|
)
|
|
147
148
|
.join("\n")
|
|
148
149
|
.trim();
|
|
@@ -230,7 +231,7 @@ async function saveGenieMemory(options: {
|
|
|
230
231
|
saveThread: true,
|
|
231
232
|
});
|
|
232
233
|
}
|
|
233
|
-
const messageId = message.message_id || message.id ||
|
|
234
|
+
const messageId = message.message_id || message.id || id();
|
|
234
235
|
const createdAt = new Date();
|
|
235
236
|
await memory.saveMessages({
|
|
236
237
|
messages: [
|
|
@@ -269,9 +270,9 @@ export function genieMessageToUiMessages(
|
|
|
269
270
|
const question = visibleGenieQuestion(message.content);
|
|
270
271
|
const input = { question };
|
|
271
272
|
const failure = genieFailure(message);
|
|
272
|
-
const progress = [...
|
|
273
|
+
const progress = [...eventsFromMessage(message, undefined, space.spaceId)].map(
|
|
273
274
|
(progressEvent) => ({
|
|
274
|
-
type:
|
|
275
|
+
type: GENIE_PROGRESS_PART_TYPE,
|
|
275
276
|
data: { toolCallId, event: progressEvent },
|
|
276
277
|
}),
|
|
277
278
|
);
|
|
@@ -312,7 +313,7 @@ export async function listGenieThreads(
|
|
|
312
313
|
space: GenieChatSpace,
|
|
313
314
|
signal?: AbortSignal,
|
|
314
315
|
): Promise<GenieThread[]> {
|
|
315
|
-
const context = signal ?
|
|
316
|
+
const context = signal ? toContext(signal) : undefined;
|
|
316
317
|
const threads: GenieThread[] = [];
|
|
317
318
|
let pageToken: string | undefined;
|
|
318
319
|
do {
|
|
@@ -325,7 +326,7 @@ export async function listGenieThreads(
|
|
|
325
326
|
context,
|
|
326
327
|
);
|
|
327
328
|
for (const conversation of page.conversations ?? []) {
|
|
328
|
-
if (!
|
|
329
|
+
if (!isRecord(conversation) || conversation.agent_type !== AGENT_CONVERSATION_TYPE) {
|
|
329
330
|
continue;
|
|
330
331
|
}
|
|
331
332
|
threads.push({
|
|
@@ -351,7 +352,7 @@ export async function listGenieThreadMessages(options: {
|
|
|
351
352
|
signal?: AbortSignal;
|
|
352
353
|
}): Promise<UIMessage[]> {
|
|
353
354
|
const { space } = options;
|
|
354
|
-
const messages = await
|
|
355
|
+
const messages = await listAgentModeConversation(
|
|
355
356
|
options.client,
|
|
356
357
|
space.spaceId,
|
|
357
358
|
options.conversationId,
|
|
@@ -379,7 +380,7 @@ export function genieChatStream(options: {
|
|
|
379
380
|
signal?: AbortSignal;
|
|
380
381
|
}): ReadableStream<UIMessageChunk> {
|
|
381
382
|
const { client, space, request, memory, requestContext, signal } = options;
|
|
382
|
-
const toolCallId =
|
|
383
|
+
const toolCallId = id();
|
|
383
384
|
const toolName = askGenieToolId(space.alias);
|
|
384
385
|
return createUIMessageStream({
|
|
385
386
|
onError: () => DIRECT_GENIE_ERROR,
|
|
@@ -396,13 +397,13 @@ export function genieChatStream(options: {
|
|
|
396
397
|
} catch (error) {
|
|
397
398
|
logger.warn("memory:recall-failed", {
|
|
398
399
|
spaceId: space.spaceId,
|
|
399
|
-
error:
|
|
400
|
+
error: toError(error),
|
|
400
401
|
});
|
|
401
402
|
}
|
|
402
403
|
const question = contextualQuestion(request.question, request.requestContext, memoryContext);
|
|
403
|
-
const progress = (progressEvent:
|
|
404
|
+
const progress = (progressEvent: GenieWriterEvent) =>
|
|
404
405
|
writer.write({
|
|
405
|
-
type:
|
|
406
|
+
type: GENIE_PROGRESS_PART_TYPE,
|
|
406
407
|
data: { toolCallId, event: progressEvent },
|
|
407
408
|
});
|
|
408
409
|
writer.write({ type: "start" });
|
|
@@ -420,10 +421,10 @@ export function genieChatStream(options: {
|
|
|
420
421
|
};
|
|
421
422
|
progress(started);
|
|
422
423
|
|
|
423
|
-
let announced:
|
|
424
|
+
let announced: GenieConversationPartData | undefined;
|
|
424
425
|
let finalMessage: GenieMessage | undefined;
|
|
425
426
|
try {
|
|
426
|
-
for await (const chatEvent of
|
|
427
|
+
for await (const chatEvent of genieEventChat(space.spaceId, question, {
|
|
427
428
|
workspaceClient: client,
|
|
428
429
|
agentMode: true,
|
|
429
430
|
agentModeFallback: false,
|
|
@@ -443,7 +444,7 @@ export function genieChatStream(options: {
|
|
|
443
444
|
) {
|
|
444
445
|
announced = { conversationId, ...(messageId ? { messageId } : {}) };
|
|
445
446
|
writer.write({
|
|
446
|
-
type:
|
|
447
|
+
type: GENIE_CONVERSATION_PART_TYPE,
|
|
447
448
|
data: announced,
|
|
448
449
|
transient: true,
|
|
449
450
|
});
|
|
@@ -454,7 +455,7 @@ export function genieChatStream(options: {
|
|
|
454
455
|
} catch (error) {
|
|
455
456
|
logger.warn("turn:error", {
|
|
456
457
|
spaceId: space.spaceId,
|
|
457
|
-
error:
|
|
458
|
+
error: toError(error),
|
|
458
459
|
});
|
|
459
460
|
writer.write({
|
|
460
461
|
type: "tool-output-error",
|
|
@@ -474,7 +475,7 @@ export function genieChatStream(options: {
|
|
|
474
475
|
writer.write({ type: "finish-step" });
|
|
475
476
|
const answer = genieAnswerText(finalMessage);
|
|
476
477
|
if (answer) {
|
|
477
|
-
const textId =
|
|
478
|
+
const textId = id();
|
|
478
479
|
writer.write({ type: "start-step" });
|
|
479
480
|
writer.write({ type: "text-start", id: textId });
|
|
480
481
|
writer.write({ type: "text-delta", id: textId, delta: answer });
|
|
@@ -495,7 +496,7 @@ export function genieChatStream(options: {
|
|
|
495
496
|
} catch (error) {
|
|
496
497
|
logger.warn("memory:save-failed", {
|
|
497
498
|
spaceId: space.spaceId,
|
|
498
|
-
error:
|
|
499
|
+
error: toError(error),
|
|
499
500
|
});
|
|
500
501
|
}
|
|
501
502
|
}
|
package/src/genie.ts
CHANGED
|
@@ -44,11 +44,11 @@ import {
|
|
|
44
44
|
ValidationError,
|
|
45
45
|
} from "@databricks/appkit";
|
|
46
46
|
import type { WorkspaceClient } from "@databricks/appkit";
|
|
47
|
-
import {
|
|
48
|
-
import { createCache } from "@dbx-tools/cache
|
|
49
|
-
import {
|
|
50
|
-
import {
|
|
51
|
-
import {
|
|
47
|
+
import { type PluginContextLike, instance } from "@dbx-tools/appkit";
|
|
48
|
+
import { createCache } from "@dbx-tools/cache";
|
|
49
|
+
import { genieEventChat, getGenieSpace, genieSampleQuestions } from "@dbx-tools/genie";
|
|
50
|
+
import { log, toError, errorContext, toDescription, toIdentifier } from "@dbx-tools/shared-core";
|
|
51
|
+
import { type GenieMessage, GenieMessageSchema } from "@dbx-tools/shared-genie";
|
|
52
52
|
import type { StartedEvent } from "@dbx-tools/shared-mastra";
|
|
53
53
|
import type { RequestContext } from "@mastra/core/request-context";
|
|
54
54
|
import { MASTRA_THREAD_ID_KEY } from "@mastra/core/request-context";
|
|
@@ -249,7 +249,7 @@ async function readCachedConversationId(cacheKey: string | undefined): Promise<s
|
|
|
249
249
|
return v ?? undefined;
|
|
250
250
|
} catch (err) {
|
|
251
251
|
logger.warn("conversation-cache:read-error", {
|
|
252
|
-
error:
|
|
252
|
+
error: toError(err),
|
|
253
253
|
});
|
|
254
254
|
return undefined;
|
|
255
255
|
}
|
|
@@ -273,7 +273,7 @@ async function saveCachedConversationId(
|
|
|
273
273
|
});
|
|
274
274
|
} catch (err) {
|
|
275
275
|
logger.warn("conversation-cache:write-error", {
|
|
276
|
-
error:
|
|
276
|
+
error: toError(err),
|
|
277
277
|
});
|
|
278
278
|
}
|
|
279
279
|
}
|
|
@@ -285,7 +285,7 @@ async function evictCachedConversationId(cacheKey: string | undefined): Promise<
|
|
|
285
285
|
await CacheManager.getInstanceSync().delete(cacheKey);
|
|
286
286
|
} catch (err) {
|
|
287
287
|
logger.warn("conversation-cache:delete-error", {
|
|
288
|
-
error:
|
|
288
|
+
error: toError(err),
|
|
289
289
|
});
|
|
290
290
|
}
|
|
291
291
|
}
|
|
@@ -328,7 +328,7 @@ const prepareChartRequestSchema = chartPlannerRequestSchema
|
|
|
328
328
|
.string()
|
|
329
329
|
.min(1, "statement_id is required")
|
|
330
330
|
.describe(
|
|
331
|
-
|
|
331
|
+
toDescription(`
|
|
332
332
|
Genie \`statement_id\` to chart. Read from
|
|
333
333
|
\`message.query_result.statement_id\` or
|
|
334
334
|
\`message.attachments[*].query.statement_id\` returned by
|
|
@@ -349,7 +349,7 @@ const prepareChartRequestSchema = chartPlannerRequestSchema
|
|
|
349
349
|
*/
|
|
350
350
|
function aliasSuffix(alias: string): string {
|
|
351
351
|
if (alias === DEFAULT_GENIE_ALIAS) return "";
|
|
352
|
-
const slug =
|
|
352
|
+
const slug = toIdentifier(alias);
|
|
353
353
|
return slug ? `_${slug}` : "";
|
|
354
354
|
}
|
|
355
355
|
|
|
@@ -390,7 +390,7 @@ function buildAskGenieTool(opts: {
|
|
|
390
390
|
const hintLine = hint ? ` (${hint})` : "";
|
|
391
391
|
return createTool({
|
|
392
392
|
id: toolId,
|
|
393
|
-
description:
|
|
393
|
+
description: toDescription(`
|
|
394
394
|
Ask the Genie space "${alias}"${hintLine} ONE focused
|
|
395
395
|
natural-language sub-question and wait for the turn to
|
|
396
396
|
complete. Genie answers best when each call covers a
|
|
@@ -422,7 +422,7 @@ function buildAskGenieTool(opts: {
|
|
|
422
422
|
.string()
|
|
423
423
|
.min(1, "question is required")
|
|
424
424
|
.describe(
|
|
425
|
-
|
|
425
|
+
toDescription(`
|
|
426
426
|
ONE focused natural-language question about the data in this
|
|
427
427
|
space, covering a single metric / dimension / time window
|
|
428
428
|
(e.g. "What was Q3 revenue by region?"). Decompose a
|
|
@@ -432,7 +432,7 @@ function buildAskGenieTool(opts: {
|
|
|
432
432
|
),
|
|
433
433
|
}),
|
|
434
434
|
outputSchema: z.object({
|
|
435
|
-
message:
|
|
435
|
+
message: GenieMessageSchema,
|
|
436
436
|
}),
|
|
437
437
|
execute: async ({ question }, ctxRaw) => {
|
|
438
438
|
const ctx = ctxRaw as ToolExecuteCtx;
|
|
@@ -485,7 +485,7 @@ function buildAskGenieTool(opts: {
|
|
|
485
485
|
const runTurn = async (): Promise<GenieMessage> => {
|
|
486
486
|
const seedConversationId = conversationId;
|
|
487
487
|
let finalMessage: GenieMessage | undefined;
|
|
488
|
-
for await (const event of
|
|
488
|
+
for await (const event of genieEventChat(spaceId, question, {
|
|
489
489
|
workspaceClient: client,
|
|
490
490
|
...(agentMode !== undefined ? { agentMode } : {}),
|
|
491
491
|
...(seedConversationId ? { conversationId: seedConversationId } : {}),
|
|
@@ -525,11 +525,11 @@ function buildAskGenieTool(opts: {
|
|
|
525
525
|
// A rejected conversation id is stale or inaccessible. Reset this
|
|
526
526
|
// lane and retry once with a fresh Genie conversation.
|
|
527
527
|
const rejectedConversationId = conversationId;
|
|
528
|
-
if (rejectedConversationId &&
|
|
528
|
+
if (rejectedConversationId && errorContext(err).notAccessible) {
|
|
529
529
|
logger.warn("conversation-cache:stale, resetting", {
|
|
530
530
|
spaceId,
|
|
531
531
|
conversationId: rejectedConversationId,
|
|
532
|
-
error:
|
|
532
|
+
error: toError(err),
|
|
533
533
|
});
|
|
534
534
|
if (ownsReusableConversation) {
|
|
535
535
|
await evictCachedConversationId(cacheKey);
|
|
@@ -559,7 +559,7 @@ function buildSpaceDescriptionTool(opts: { spaceId: string; alias: string }) {
|
|
|
559
559
|
const toolId = `get_space_description${aliasSuffix(alias)}`;
|
|
560
560
|
return createTool({
|
|
561
561
|
id: toolId,
|
|
562
|
-
description:
|
|
562
|
+
description: toDescription(`
|
|
563
563
|
Return the Genie space "${alias}"'s title, description, and
|
|
564
564
|
warehouse id. Cheap (single REST call, no LLM round-trip).
|
|
565
565
|
Call this FIRST on any user turn that's going to touch
|
|
@@ -584,7 +584,7 @@ function buildSpaceDescriptionTool(opts: { spaceId: string; alias: string }) {
|
|
|
584
584
|
// description surface (title / description / warehouse id) lives
|
|
585
585
|
// on the directory-listing shape, so skip the larger
|
|
586
586
|
// `serialized_space` payload here.
|
|
587
|
-
const space = await
|
|
587
|
+
const space = await getGenieSpace(spaceId, {
|
|
588
588
|
workspaceClient: client,
|
|
589
589
|
serialized: false,
|
|
590
590
|
...(signal ? { context: signal } : {}),
|
|
@@ -604,7 +604,7 @@ function buildSpaceSerializedTool(opts: { spaceId: string; alias: string }) {
|
|
|
604
604
|
const toolId = `get_space_serialized${aliasSuffix(alias)}`;
|
|
605
605
|
return createTool({
|
|
606
606
|
id: toolId,
|
|
607
|
-
description:
|
|
607
|
+
description: toDescription(`
|
|
608
608
|
Return the full \`GenieSpace\` JSON for the "${alias}" space.
|
|
609
609
|
Use only when you need exact column / table identifiers
|
|
610
610
|
\`get_space_description\` doesn't expose. Larger payload, so
|
|
@@ -619,7 +619,7 @@ function buildSpaceSerializedTool(opts: { spaceId: string; alias: string }) {
|
|
|
619
619
|
// Central Genie space fetch with the opt-in `serialized_space`
|
|
620
620
|
// blob (catalogs, tables, sample questions, prompts) that the
|
|
621
621
|
// typed SDK `getSpace` omits - the whole point of this tool.
|
|
622
|
-
const space = await
|
|
622
|
+
const space = await getGenieSpace(spaceId, {
|
|
623
623
|
workspaceClient: client,
|
|
624
624
|
...(signal ? { context: signal } : {}),
|
|
625
625
|
});
|
|
@@ -643,7 +643,7 @@ function buildGetStatementTool() {
|
|
|
643
643
|
const toolId = "get_statement";
|
|
644
644
|
return createTool({
|
|
645
645
|
id: toolId,
|
|
646
|
-
description:
|
|
646
|
+
description: toDescription(`
|
|
647
647
|
Fetch the rows of a Genie statement by its \`statement_id\` (the
|
|
648
648
|
value at \`message.query_result.statement_id\` or
|
|
649
649
|
\`message.attachments[*].query.statement_id\` returned from
|
|
@@ -663,7 +663,7 @@ function buildGetStatementTool() {
|
|
|
663
663
|
.string()
|
|
664
664
|
.min(1, "statement_id is required")
|
|
665
665
|
.describe(
|
|
666
|
-
|
|
666
|
+
toDescription(`
|
|
667
667
|
Genie \`statement_id\` whose rows to read. Take it from
|
|
668
668
|
\`message.query_result.statement_id\` or
|
|
669
669
|
\`message.attachments[*].query.statement_id\` on an
|
|
@@ -728,7 +728,7 @@ function buildPrepareChartTool(opts: { config: MastraPluginConfig }) {
|
|
|
728
728
|
const toolId = "prepare_chart";
|
|
729
729
|
return createTool({
|
|
730
730
|
id: toolId,
|
|
731
|
-
description:
|
|
731
|
+
description: toDescription([
|
|
732
732
|
`
|
|
733
733
|
Queue a chart for the rows of a Genie statement. Mints a
|
|
734
734
|
\`chartId\` plus its complete \`marker\` synchronously and
|
|
@@ -800,7 +800,7 @@ function buildPrepareChartTool(opts: { config: MastraPluginConfig }) {
|
|
|
800
800
|
* variant that names the suffixed per-space tools
|
|
801
801
|
* (e.g. `ask_genie_sales`).
|
|
802
802
|
*/
|
|
803
|
-
export const GENIE_INSTRUCTIONS =
|
|
803
|
+
export const GENIE_INSTRUCTIONS = toDescription([
|
|
804
804
|
"Genie orchestration. For every user question that needs SQL-backed data:",
|
|
805
805
|
{
|
|
806
806
|
numbered: [
|
|
@@ -993,7 +993,7 @@ type AppKitGenieConfig = NonNullable<Parameters<typeof genie>[0]>;
|
|
|
993
993
|
*/
|
|
994
994
|
export function resolveGenieSpaces(
|
|
995
995
|
config: MastraPluginConfig,
|
|
996
|
-
context:
|
|
996
|
+
context: PluginContextLike | undefined,
|
|
997
997
|
): Record<string, GenieSpaceConfig> {
|
|
998
998
|
const merged: Record<string, GenieSpaceConfig> = {};
|
|
999
999
|
|
|
@@ -1006,7 +1006,7 @@ export function resolveGenieSpaces(
|
|
|
1006
1006
|
// Source 2: AppKit `genie()` plugin instance config. Use a
|
|
1007
1007
|
// structural cast - `Plugin.config` is `protected` in TS only,
|
|
1008
1008
|
// and the runtime layout is plain object property access.
|
|
1009
|
-
const geniePlugin =
|
|
1009
|
+
const geniePlugin = instance(context, genie);
|
|
1010
1010
|
if (geniePlugin) {
|
|
1011
1011
|
const pluginSpaces = (geniePlugin as unknown as { config?: AppKitGenieConfig }).config?.spaces;
|
|
1012
1012
|
if (pluginSpaces) {
|
|
@@ -1147,17 +1147,17 @@ export async function collectSpaceSuggestions(opts: {
|
|
|
1147
1147
|
let questions = await readSuggestionCache(spaceId);
|
|
1148
1148
|
if (!questions) {
|
|
1149
1149
|
try {
|
|
1150
|
-
const space = await
|
|
1150
|
+
const space = await getGenieSpace(spaceId, {
|
|
1151
1151
|
workspaceClient: opts.client,
|
|
1152
1152
|
...(opts.signal ? { context: opts.signal } : {}),
|
|
1153
1153
|
});
|
|
1154
|
-
questions =
|
|
1154
|
+
questions = genieSampleQuestions(space);
|
|
1155
1155
|
await writeSuggestionCache(spaceId, questions);
|
|
1156
1156
|
} catch (err) {
|
|
1157
1157
|
if (opts.signal?.aborted) return merged;
|
|
1158
1158
|
logger.warn("suggestions:fetch-error", {
|
|
1159
1159
|
spaceId,
|
|
1160
|
-
error:
|
|
1160
|
+
error: toError(err),
|
|
1161
1161
|
});
|
|
1162
1162
|
questions = [];
|
|
1163
1163
|
}
|
package/src/identity.ts
CHANGED
|
@@ -51,50 +51,15 @@
|
|
|
51
51
|
* @module
|
|
52
52
|
*/
|
|
53
53
|
|
|
54
|
-
import { identity } from "@dbx-tools/appkit";
|
|
55
|
-
|
|
56
|
-
/** Identity a chat turn's Databricks calls run as. See the module docs. */
|
|
57
|
-
export type MastraIdentityMode = identity.IdentityMode;
|
|
54
|
+
import { identity, type IdentityMode } from "@dbx-tools/appkit";
|
|
58
55
|
|
|
59
56
|
/** Environment fallback for `config.genieIdentity`. */
|
|
60
57
|
export const IDENTITY_ENV = "MASTRA_GENIE_IDENTITY";
|
|
61
58
|
|
|
62
|
-
/** Every accepted {@link MastraIdentityMode}. */
|
|
63
|
-
export const IDENTITY_MODES = identity.IDENTITY_MODES;
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* Default mode. `"user"` keeps OBO the only identity unless an app opts in, so
|
|
67
|
-
* adopting this option can never silently widen an existing app's data access.
|
|
68
|
-
*/
|
|
69
|
-
export const DEFAULT_IDENTITY_MODE = identity.DEFAULT_IDENTITY_MODE;
|
|
70
|
-
|
|
71
|
-
/** Header Databricks Apps forward the signed-in user's id on. */
|
|
72
|
-
export const USER_ID_HEADER = identity.USER_ID_HEADER;
|
|
73
|
-
|
|
74
|
-
/** Header Databricks Apps forward the signed-in user's email on. */
|
|
75
|
-
export const USER_EMAIL_HEADER = identity.USER_EMAIL_HEADER;
|
|
76
|
-
|
|
77
59
|
/**
|
|
78
60
|
* Resolve the configured mode: explicit plugin config, then {@link IDENTITY_ENV},
|
|
79
|
-
* then
|
|
61
|
+
* then AppKit's default identity mode. An unrecognized value throws.
|
|
80
62
|
*/
|
|
81
|
-
export function resolveIdentityMode(configured: string | undefined):
|
|
63
|
+
export function resolveIdentityMode(configured: string | undefined): IdentityMode {
|
|
82
64
|
return identity.resolveIdentityMode(configured, IDENTITY_ENV, "genieIdentity");
|
|
83
65
|
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* The forwarded user id on `req`, or `undefined`. Used to attribute a turn to
|
|
87
|
-
* the real caller when the Databricks client is the app SP but memory / cache /
|
|
88
|
-
* traces must still key off the user.
|
|
89
|
-
*/
|
|
90
|
-
export const requestUserId = identity.requestUserId;
|
|
91
|
-
|
|
92
|
-
/** The forwarded user email on `req`, or `undefined`. */
|
|
93
|
-
export const requestUserEmail = identity.requestUserEmail;
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Whether this request should run its Databricks calls as the app service
|
|
97
|
-
* principal rather than OBO - `"service-principal"` always, `"user"` never, and
|
|
98
|
-
* `"auto"` only when the request carries no OBO token for AppKit to use.
|
|
99
|
-
*/
|
|
100
|
-
export const useServicePrincipal = identity.useServicePrincipal;
|
package/src/memory.ts
CHANGED
|
@@ -32,8 +32,8 @@
|
|
|
32
32
|
* @module
|
|
33
33
|
*/
|
|
34
34
|
|
|
35
|
-
import {
|
|
36
|
-
import {
|
|
35
|
+
import { handleOwnershipMigrationError } from "@dbx-tools/appkit";
|
|
36
|
+
import { log, id } from "@dbx-tools/shared-core";
|
|
37
37
|
import type { AgentMemoryOption } from "@mastra/core/agent";
|
|
38
38
|
import {
|
|
39
39
|
MASTRA_RESOURCE_ID_KEY,
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
import { fastembed } from "@mastra/fastembed";
|
|
44
44
|
import { Memory } from "@mastra/memory";
|
|
45
45
|
import { PgVector, PostgresStore } from "@mastra/pg";
|
|
46
|
-
import
|
|
46
|
+
import { Pool, type PoolConfig } from "pg";
|
|
47
47
|
|
|
48
48
|
import type { AnyMastraAgentDefinition, MastraMemoryConfigOverride } from "./agents.ts";
|
|
49
49
|
import type { MastraPluginConfig } from "./config.ts";
|
|
@@ -108,7 +108,7 @@ export function withSoftStorageInit(store: PostgresStore): PostgresStore {
|
|
|
108
108
|
await originalInit();
|
|
109
109
|
} catch (err) {
|
|
110
110
|
if (
|
|
111
|
-
!
|
|
111
|
+
!handleOwnershipMigrationError(err, {
|
|
112
112
|
scope: "mastra-storage",
|
|
113
113
|
logger,
|
|
114
114
|
event: "mastra storage migration failed",
|
|
@@ -160,6 +160,24 @@ export function createMemoryBuilder(
|
|
|
160
160
|
return new MemoryBuilder(config, servicePrincipalPool);
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Create the plugin-owned Lakebase pool used by durable workflow storage.
|
|
165
|
+
*
|
|
166
|
+
* @internal
|
|
167
|
+
*
|
|
168
|
+
* AppKit shuts plugin hooks down concurrently, so borrowing the native
|
|
169
|
+
* Lakebase plugin's pool lets its shutdown race Mastra's final snapshot writes.
|
|
170
|
+
* A caller-provided pool is not closed by PostgresStore; idle connections do
|
|
171
|
+
* not keep the process alive and remain available to runs Mastra is still
|
|
172
|
+
* draining after its bounded shutdown window.
|
|
173
|
+
*/
|
|
174
|
+
export function createMastraLakebasePool(config: PoolConfig): Pool {
|
|
175
|
+
return new Pool({
|
|
176
|
+
...config,
|
|
177
|
+
allowExitOnIdle: true,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
163
181
|
/**
|
|
164
182
|
* Builds one `Memory` per agent against a shared service-principal
|
|
165
183
|
* Lakebase pool. Per-instance state keeps the shared `PgVector` alive
|
|
@@ -329,7 +347,7 @@ export class MemoryBuilder {
|
|
|
329
347
|
*/
|
|
330
348
|
function buildSharedPgVector(pool: Pool): PgVector {
|
|
331
349
|
const vector = new PgVector({
|
|
332
|
-
id: `pg${
|
|
350
|
+
id: `pg${id()}`,
|
|
333
351
|
// Keep the recall index out of `public`: on a Lakebase database the app
|
|
334
352
|
// service principal has no CREATE on `public` (PG15+ locks it down), so a
|
|
335
353
|
// default-schema PgVector fails on CREATE INDEX with "permission denied for
|
|
@@ -353,7 +371,7 @@ function buildSharedPgVector(pool: Pool): PgVector {
|
|
|
353
371
|
/** Per-agent dedicated `PgVector` (rare; opt-in via object override). */
|
|
354
372
|
function buildPgVector(setting: MastraMemoryConfigOverride): PgVector {
|
|
355
373
|
return new PgVector(
|
|
356
|
-
withId(setting, `pg-vector__${
|
|
374
|
+
withId(setting, `pg-vector__${id()}`) as ConstructorParameters<typeof PgVector>[0],
|
|
357
375
|
);
|
|
358
376
|
}
|
|
359
377
|
|