@dbx-tools/appkit-mastra 0.9.131 → 0.9.136
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 +138 -104
- package/index.ts +3 -1
- package/lib/index.d.ts +3 -1
- package/lib/index.js +3 -2
- package/lib/src/config.d.ts +12 -17
- package/lib/src/config.js +5 -4
- package/lib/src/feedback-mode.d.ts +15 -0
- package/lib/src/feedback-mode.js +23 -0
- package/lib/src/genie-chat.d.ts +20 -13
- package/lib/src/genie-chat.js +202 -28
- package/lib/src/memory.js +4 -2
- package/lib/src/mlflow.d.ts +1 -14
- package/lib/src/mlflow.js +1 -25
- package/lib/src/model.d.ts +5 -3
- package/lib/src/model.js +7 -4
- package/lib/src/observability.js +6 -7
- package/lib/src/plugin.d.ts +16 -9
- package/lib/src/plugin.js +92 -28
- package/lib/src/server.d.ts +2 -3
- package/lib/src/server.js +5 -7
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +24 -16
- package/src/config.ts +16 -20
- package/src/feedback-mode.ts +35 -0
- package/src/genie-chat.ts +231 -31
- package/src/memory.ts +3 -1
- package/src/mlflow.ts +1 -27
- package/src/model.ts +12 -5
- package/src/observability.ts +5 -6
- package/src/plugin.ts +104 -30
- package/src/server.ts +4 -6
package/src/plugin.ts
CHANGED
|
@@ -57,6 +57,7 @@ import {
|
|
|
57
57
|
type ResourceRequirement,
|
|
58
58
|
} from "@databricks/appkit";
|
|
59
59
|
import { lakebaseResolver, pluginRegistry, telemetry as appkitTelemetry } from "@dbx-tools/appkit";
|
|
60
|
+
import { sendGenieMessageFeedback } from "@dbx-tools/genie/feedback";
|
|
60
61
|
import { modelCatalog } from "@dbx-tools/model";
|
|
61
62
|
import { asyncUtils, errorUtils, log, object, stringUtils } from "@dbx-tools/shared-core";
|
|
62
63
|
import {
|
|
@@ -66,13 +67,14 @@ import {
|
|
|
66
67
|
type Chart,
|
|
67
68
|
type GenieChatRequest,
|
|
68
69
|
type MastraClientConfig,
|
|
69
|
-
type MastraFeedbackRequest,
|
|
70
70
|
type StatementData,
|
|
71
71
|
} from "@dbx-tools/shared-mastra";
|
|
72
72
|
import { display, type ServingEndpointSummary } from "@dbx-tools/shared-model";
|
|
73
|
+
import { workflowRoute } from "@mastra/ai-sdk";
|
|
73
74
|
import type { Agent } from "@mastra/core/agent";
|
|
74
75
|
import { Mastra } from "@mastra/core/mastra";
|
|
75
|
-
import
|
|
76
|
+
import { RequestContext } from "@mastra/core/request-context";
|
|
77
|
+
import type { AnyWorkflow } from "@mastra/core/workflows";
|
|
76
78
|
import { pipeUIMessageStreamToResponse } from "ai";
|
|
77
79
|
import express from "express";
|
|
78
80
|
import type { Pool } from "pg";
|
|
@@ -100,6 +102,7 @@ import {
|
|
|
100
102
|
modelCatalogueDefaults,
|
|
101
103
|
statementDataDefaults,
|
|
102
104
|
} from "./defaults.ts";
|
|
105
|
+
import { resolveFeedbackMode } from "./feedback-mode.ts";
|
|
103
106
|
import {
|
|
104
107
|
deleteGenieThread,
|
|
105
108
|
deleteGenieThreadMessage,
|
|
@@ -120,9 +123,8 @@ import { createMemoryBuilder, needsLakebase } from "./memory.ts";
|
|
|
120
123
|
import {
|
|
121
124
|
flushDirectMlflowTracing,
|
|
122
125
|
logFeedback,
|
|
126
|
+
mlflowEnabled,
|
|
123
127
|
mlflowExperimentManagerUrl,
|
|
124
|
-
resolveFeedbackEnabled,
|
|
125
|
-
validateFeedbackConfig,
|
|
126
128
|
} from "./mlflow.ts";
|
|
127
129
|
import { resolveDefaultModelId } from "./model.ts";
|
|
128
130
|
import { buildObservability, configureOtelPropagation } from "./observability.ts";
|
|
@@ -130,9 +132,11 @@ import { provisionRemoteSkills } from "./remote-skills.ts";
|
|
|
130
132
|
import {
|
|
131
133
|
attachRoutePatchMiddleware,
|
|
132
134
|
attachStaleResumeRecovery,
|
|
135
|
+
clearTrustedRequestContext,
|
|
133
136
|
createRequestContext,
|
|
134
137
|
isMastraRequestAllowed,
|
|
135
138
|
MastraServer,
|
|
139
|
+
stampRequestContextUser,
|
|
136
140
|
} from "./server.ts";
|
|
137
141
|
import { resolveServingConfig } from "./serving.ts";
|
|
138
142
|
import { fetchStatementEmbedData, STATEMENT_ROW_CAP } from "./statement.ts";
|
|
@@ -244,12 +248,18 @@ export class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
244
248
|
private mastraApp: express.Express | null = null;
|
|
245
249
|
private mastraServer: MastraServer | null = null;
|
|
246
250
|
private readonly outputProcessorFactories: MastraOutputProcessorFactory[] = [];
|
|
251
|
+
/** Code-defined workflows registered before the Mastra instance is built. */
|
|
252
|
+
private readonly workflows: Record<string, AnyWorkflow> = {
|
|
253
|
+
...(this.config.workflows ?? {}),
|
|
254
|
+
};
|
|
247
255
|
/**
|
|
248
256
|
* The optional MCP server exposing this plugin's agents / tools, or
|
|
249
257
|
* `null` when `config.mcp` is disabled (the default). Built in
|
|
250
258
|
* {@link buildAgentAndServer} and registered on the Mastra instance.
|
|
251
259
|
*/
|
|
252
260
|
private mcp: ResolvedMcp | null = null;
|
|
261
|
+
/** Resolved feedback destination published to server and browser clients. */
|
|
262
|
+
private resolvedFeedbackMode: feedback.FeedbackMode = "none";
|
|
253
263
|
/**
|
|
254
264
|
* Resolved {@link MastraIdentityMode}: the identity the chat path's Databricks
|
|
255
265
|
* calls run as. Read once at setup so an invalid `genieIdentity` fails the app
|
|
@@ -260,7 +270,12 @@ export class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
260
270
|
// Resolve the identity mode up front so an invalid `genieIdentity` fails the
|
|
261
271
|
// app boot with a clear message rather than 500-ing the first chat request.
|
|
262
272
|
this.identityMode = resolveIdentityMode(this.config.genieIdentity);
|
|
263
|
-
|
|
273
|
+
const spaces = resolveGenieSpaces(this.config, this.context);
|
|
274
|
+
this.resolvedFeedbackMode = resolveFeedbackMode(this.config.feedbackMode, {
|
|
275
|
+
genie: this.config.genieAgentMode !== false && Object.keys(spaces).length > 0,
|
|
276
|
+
mlflow: mlflowEnabled(),
|
|
277
|
+
});
|
|
278
|
+
this.config.feedbackMode = this.resolvedFeedbackMode;
|
|
264
279
|
if (this.config.observability !== false) {
|
|
265
280
|
await registerAppMlflowTraceInfoProcessor();
|
|
266
281
|
}
|
|
@@ -358,6 +373,18 @@ export class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
358
373
|
}
|
|
359
374
|
this.outputProcessorFactories.push(factory);
|
|
360
375
|
},
|
|
376
|
+
/** Register a workflow before the hosted Mastra instance is built. */
|
|
377
|
+
registerWorkflow: (key: string, workflow: AnyWorkflow): void => {
|
|
378
|
+
if (this.mastra) {
|
|
379
|
+
throw new Error("Mastra workflows must be registered before the instance is built");
|
|
380
|
+
}
|
|
381
|
+
if (this.workflows[key]) {
|
|
382
|
+
throw new Error(`Mastra workflow key "${key}" is already registered`);
|
|
383
|
+
}
|
|
384
|
+
this.workflows[key] = workflow;
|
|
385
|
+
},
|
|
386
|
+
/** Registered workflow keys in insertion order. */
|
|
387
|
+
listWorkflows: (): string[] => Object.keys(this.workflows),
|
|
361
388
|
/** Underlying Mastra instance for advanced use (custom routes etc.). */
|
|
362
389
|
getMastra: () => this.mastra,
|
|
363
390
|
/**
|
|
@@ -452,7 +479,7 @@ export class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
452
479
|
basePath: this.basePath,
|
|
453
480
|
defaultAgent: this.built?.defaultAgentId ?? FALLBACK_AGENT_ID,
|
|
454
481
|
agents: Object.keys(this.built?.agents ?? {}),
|
|
455
|
-
|
|
482
|
+
feedbackMode: this.feedbackMode(),
|
|
456
483
|
backgroundTurns: this.config.backgroundTurns !== false,
|
|
457
484
|
// In `service-principal` mode the chat works for any caller who can open
|
|
458
485
|
// the app, so a UI that hides chat behind an OBO-token probe should show
|
|
@@ -462,13 +489,9 @@ export class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
462
489
|
return config as unknown as Record<string, unknown>;
|
|
463
490
|
}
|
|
464
491
|
|
|
465
|
-
/**
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
* feedback route share the same gate as the server's trace-id header.
|
|
469
|
-
*/
|
|
470
|
-
private feedbackEnabled(): boolean {
|
|
471
|
-
return resolveFeedbackEnabled(this.config.feedback);
|
|
492
|
+
/** Resolve the only feedback destination exposed for this plugin mode. */
|
|
493
|
+
private feedbackMode(): feedback.FeedbackMode {
|
|
494
|
+
return this.resolvedFeedbackMode;
|
|
472
495
|
}
|
|
473
496
|
|
|
474
497
|
override injectRoutes(router: IAppRouter): void {
|
|
@@ -897,12 +920,29 @@ export class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
897
920
|
): Promise<void> {
|
|
898
921
|
const context = getExecutionContext();
|
|
899
922
|
const client = context.client;
|
|
923
|
+
const requestContext = new RequestContext<unknown>(
|
|
924
|
+
Object.entries(request.requestContext ?? {}),
|
|
925
|
+
);
|
|
926
|
+
clearTrustedRequestContext(requestContext);
|
|
927
|
+
const applicationContext = requestContext.toJSON();
|
|
928
|
+
await stampRequestContextUser(requestContext);
|
|
929
|
+
const memory = await this.built?.agents[this.built.defaultAgentId]?.getMemory({
|
|
930
|
+
requestContext,
|
|
931
|
+
});
|
|
900
932
|
await pipeUIMessageStreamToResponse({
|
|
901
933
|
response: res,
|
|
902
934
|
stream: genieChatStream({
|
|
903
935
|
client,
|
|
904
936
|
space,
|
|
905
|
-
request
|
|
937
|
+
request: {
|
|
938
|
+
question: request.question,
|
|
939
|
+
...(request.conversationId ? { conversationId: request.conversationId } : {}),
|
|
940
|
+
...(Object.keys(applicationContext).length > 0
|
|
941
|
+
? { requestContext: applicationContext }
|
|
942
|
+
: {}),
|
|
943
|
+
},
|
|
944
|
+
...(memory ? { memory } : {}),
|
|
945
|
+
requestContext,
|
|
906
946
|
signal,
|
|
907
947
|
}),
|
|
908
948
|
});
|
|
@@ -924,24 +964,15 @@ export class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
924
964
|
return this.execute(() => write(client), genieConversationWriteDefaults);
|
|
925
965
|
}
|
|
926
966
|
|
|
927
|
-
/** Register
|
|
967
|
+
/** Register the single feedback destination selected by plugin mode. */
|
|
928
968
|
private registerFeedbackRoutes(router: IAppRouter): void {
|
|
929
|
-
// `POST /route/feedback` logs a thumbs / comment assessment against
|
|
930
|
-
// a turn's MLflow trace (the `traceId` the client captured from the
|
|
931
|
-
// stream response's trace-id header). Registered on the AppKit
|
|
932
|
-
// router (like `/models`) rather than the Mastra subapp so it runs
|
|
933
|
-
// under the same OBO scope - the feedback is attributed to the
|
|
934
|
-
// signed-in user. Returns 404 when feedback is disabled so the
|
|
935
|
-
// client treats the capability as absent; 400 on a malformed body.
|
|
936
|
-
// A recorded assessment yields `{ ok: true }`; a soft failure (most
|
|
937
|
-
// often the trace hasn't finished exporting to MLflow yet) yields
|
|
938
|
-
// `{ ok: false }` without a 5xx so the UI can prompt a retry.
|
|
939
969
|
this.route(router, {
|
|
940
970
|
name: "feedback",
|
|
941
971
|
method: "post",
|
|
942
972
|
path: routes.MASTRA_ROUTES.feedback,
|
|
943
973
|
handler: async (req, res) => {
|
|
944
|
-
|
|
974
|
+
const mode = this.feedbackMode();
|
|
975
|
+
if (mode === "none") {
|
|
945
976
|
res.status(404).json({ ok: false });
|
|
946
977
|
return;
|
|
947
978
|
}
|
|
@@ -957,6 +988,19 @@ export class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
957
988
|
});
|
|
958
989
|
return;
|
|
959
990
|
}
|
|
991
|
+
if (parsed.data.mode !== mode) {
|
|
992
|
+
res.status(400).json({ ok: false, error: INVALID_FEEDBACK_MESSAGE });
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
if (parsed.data.mode === "genie") {
|
|
996
|
+
const result = await this.scopedSelf(req).sendGenieFeedback(parsed.data);
|
|
997
|
+
if (!result.ok) {
|
|
998
|
+
this.sendFailure(res, result, "genie-feedback", "Feedback could not be recorded");
|
|
999
|
+
return;
|
|
1000
|
+
}
|
|
1001
|
+
res.json({ ok: true });
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
960
1004
|
const result = await this.scopedSelf(req).logFeedback(parsed.data);
|
|
961
1005
|
if (!result.ok) {
|
|
962
1006
|
this.sendFailure(res, result, "feedback", "Feedback could not be recorded");
|
|
@@ -978,7 +1022,7 @@ export class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
978
1022
|
method: "get",
|
|
979
1023
|
path: routes.MASTRA_ROUTES.mlflowExperiment,
|
|
980
1024
|
handler: async (req, res) => {
|
|
981
|
-
if (
|
|
1025
|
+
if (this.feedbackMode() !== "mlflow") {
|
|
982
1026
|
res.json({ url: null });
|
|
983
1027
|
return;
|
|
984
1028
|
}
|
|
@@ -1120,7 +1164,7 @@ export class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
1120
1164
|
* (see {@link logFeedback} in `./mlflow.js`).
|
|
1121
1165
|
*/
|
|
1122
1166
|
private async logFeedback(
|
|
1123
|
-
feedback:
|
|
1167
|
+
feedback: feedback.MlflowFeedbackRequest,
|
|
1124
1168
|
): Promise<ExecutionResult<string | undefined>> {
|
|
1125
1169
|
const ctx = getExecutionContext();
|
|
1126
1170
|
const sourceId =
|
|
@@ -1139,6 +1183,28 @@ export class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
1139
1183
|
);
|
|
1140
1184
|
}
|
|
1141
1185
|
|
|
1186
|
+
/** Send Agent Mode rating and comment directly to the originating Genie message. */
|
|
1187
|
+
private sendGenieFeedback(input: feedback.GenieFeedbackRequest): Promise<ExecutionResult<void>> {
|
|
1188
|
+
const client = getExecutionContext().client;
|
|
1189
|
+
return this.execute(
|
|
1190
|
+
(signal) =>
|
|
1191
|
+
sendGenieMessageFeedback(
|
|
1192
|
+
{
|
|
1193
|
+
space_id: input.spaceId,
|
|
1194
|
+
conversation_id: input.conversationId,
|
|
1195
|
+
message_id: input.messageId,
|
|
1196
|
+
rating: input.rating,
|
|
1197
|
+
...(input.comment ? { comment: input.comment } : {}),
|
|
1198
|
+
},
|
|
1199
|
+
{
|
|
1200
|
+
workspaceClient: client,
|
|
1201
|
+
...(signal ? { context: signal } : {}),
|
|
1202
|
+
},
|
|
1203
|
+
),
|
|
1204
|
+
feedbackWriteDefaults,
|
|
1205
|
+
);
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1142
1208
|
/**
|
|
1143
1209
|
* Resolve an experiment link under the active request identity. The helper
|
|
1144
1210
|
* returns no URL unless the viewer has effective `CAN_MANAGE` permission.
|
|
@@ -1353,6 +1419,7 @@ export class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
1353
1419
|
});
|
|
1354
1420
|
this.mastra = new Mastra({
|
|
1355
1421
|
agents: this.built.agents,
|
|
1422
|
+
...(Object.keys(this.workflows).length > 0 ? { workflows: this.workflows } : {}),
|
|
1356
1423
|
...(instanceStorage ? { storage: instanceStorage } : {}),
|
|
1357
1424
|
...(observability ? { observability } : {}),
|
|
1358
1425
|
...(this.mcp ? { mcpServers: { [this.mcp.serverId]: this.mcp.server } } : {}),
|
|
@@ -1372,18 +1439,25 @@ export class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
1372
1439
|
app: this.mastraApp,
|
|
1373
1440
|
mastra: this.mastra,
|
|
1374
1441
|
prefix: "",
|
|
1375
|
-
customApiRoutes:
|
|
1442
|
+
customApiRoutes: [
|
|
1443
|
+
...agentChatRoutes(),
|
|
1444
|
+
workflowRoute({
|
|
1445
|
+
path: "/route/workflows/:workflowId/stream",
|
|
1446
|
+
version: "v7",
|
|
1447
|
+
}),
|
|
1448
|
+
],
|
|
1376
1449
|
});
|
|
1377
1450
|
await this.mastraServer.init();
|
|
1378
1451
|
attachStaleResumeRecovery(this.mastraApp);
|
|
1379
1452
|
this.logger.info("ready", {
|
|
1380
1453
|
agents: Object.keys(this.built.agents),
|
|
1454
|
+
workflows: Object.keys(this.workflows),
|
|
1381
1455
|
defaultAgent: this.built.defaultAgentId,
|
|
1382
1456
|
backgroundTurns: this.config.backgroundTurns !== false,
|
|
1383
1457
|
apiAccess: this.config.apiAccess ?? "scoped",
|
|
1384
1458
|
mcp: this.mcp ? `${this.basePath}${this.mcp.httpPath}` : "off",
|
|
1385
1459
|
lakebase: memoryPool !== undefined,
|
|
1386
|
-
feedback: this.
|
|
1460
|
+
feedback: this.feedbackMode(),
|
|
1387
1461
|
observability: observability !== undefined ? "mlflow" : "off",
|
|
1388
1462
|
});
|
|
1389
1463
|
}
|
package/src/server.ts
CHANGED
|
@@ -30,7 +30,6 @@ import {
|
|
|
30
30
|
type User,
|
|
31
31
|
} from "./config.ts";
|
|
32
32
|
import { requestUserEmail, requestUserId } from "./identity.ts";
|
|
33
|
-
import { resolveFeedbackEnabled } from "./mlflow.ts";
|
|
34
33
|
import { MASTRA_ORIGIN_KEY } from "./request-context.ts";
|
|
35
34
|
import {
|
|
36
35
|
extractModelOverride,
|
|
@@ -202,10 +201,9 @@ export async function createRequestContext(
|
|
|
202
201
|
export class MastraServer extends ExpressMastraServer {
|
|
203
202
|
private log: log.Logger;
|
|
204
203
|
/**
|
|
205
|
-
* Whether to stamp the MLflow trace-id header on responses.
|
|
206
|
-
* plugin's feedback gate via {@link resolveFeedbackEnabled}.
|
|
204
|
+
* Whether to stamp the MLflow trace-id header on responses.
|
|
207
205
|
*/
|
|
208
|
-
private
|
|
206
|
+
private mlflowFeedback: boolean;
|
|
209
207
|
|
|
210
208
|
constructor(
|
|
211
209
|
private config: MastraPluginConfig,
|
|
@@ -213,7 +211,7 @@ export class MastraServer extends ExpressMastraServer {
|
|
|
213
211
|
) {
|
|
214
212
|
super(...args);
|
|
215
213
|
this.log = log.logger(config);
|
|
216
|
-
this.
|
|
214
|
+
this.mlflowFeedback = config.feedbackMode === "mlflow";
|
|
217
215
|
}
|
|
218
216
|
|
|
219
217
|
override registerAuthMiddleware(): void {
|
|
@@ -324,7 +322,7 @@ export class MastraServer extends ExpressMastraServer {
|
|
|
324
322
|
* degrading gracefully rather than emitting a bogus trace id.
|
|
325
323
|
*/
|
|
326
324
|
configureMlflowTraceId(res: express.Response) {
|
|
327
|
-
if (!this.
|
|
325
|
+
if (!this.mlflowFeedback || res.headersSent) return;
|
|
328
326
|
if (res.getHeader(feedback.MLFLOW_TRACE_ID_HEADER)) return;
|
|
329
327
|
const traceId = trace.getActiveSpan()?.spanContext().traceId;
|
|
330
328
|
if (!traceId || traceId === INVALID_TRACE_ID) return;
|