@dbx-tools/appkit-mastra 0.9.143 → 0.9.145
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 +17 -6
- package/index.ts +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js +2 -2
- package/lib/src/chat.js +4 -1
- package/lib/src/config.d.ts +3 -2
- package/lib/src/config.js +1 -1
- package/lib/src/follow-up-suggestions.js +32 -11
- package/lib/src/identity.d.ts +8 -7
- package/lib/src/identity.js +9 -8
- package/lib/src/mlflow.d.ts +5 -0
- package/lib/src/mlflow.js +49 -3
- package/lib/src/plugin.d.ts +2 -2
- package/lib/src/plugin.js +8 -3
- package/lib/src/server.d.ts +7 -8
- package/lib/src/server.js +12 -11
- package/lib/src/telemetry.js +5 -4
- package/package.json +16 -16
- package/src/chat.ts +3 -0
- package/src/config.ts +3 -2
- package/src/follow-up-suggestions.ts +29 -10
- package/src/identity.ts +8 -7
- package/src/mlflow.ts +48 -3
- package/src/plugin.ts +7 -2
- package/src/server.ts +11 -10
- package/src/telemetry.ts +4 -2
package/src/mlflow.ts
CHANGED
|
@@ -49,6 +49,7 @@ let directMlflowInitStarted = false;
|
|
|
49
49
|
let directMlflowProvider: NodeTracerProvider | undefined;
|
|
50
50
|
let directMlflowProcessor: SpanProcessor | undefined;
|
|
51
51
|
let appMlflowTraceInfoInitStarted = false;
|
|
52
|
+
let appMlflowTraceInfoRequestInitStarted = false;
|
|
52
53
|
let appMlflowTraceInfoClient: WorkspaceClient | undefined;
|
|
53
54
|
let appMlflowTracePrefix: string | undefined;
|
|
54
55
|
const pendingAppMlflowTraceInfo = new Set<Promise<void>>();
|
|
@@ -209,7 +210,18 @@ async function resolveUcTracePrefix(client: WorkspaceClient): Promise<string | u
|
|
|
209
210
|
if (loadedUcPrefix) return loadedUcPrefix.prefix;
|
|
210
211
|
const experiment = await fetchExperiment(client);
|
|
211
212
|
if (!experiment) return undefined;
|
|
212
|
-
const
|
|
213
|
+
const location = experiment.trace_location?.uc_trace_location;
|
|
214
|
+
const prefix =
|
|
215
|
+
ucTracePrefixFromExperimentTags(experiment.tags) ??
|
|
216
|
+
(location
|
|
217
|
+
? [
|
|
218
|
+
location.catalog,
|
|
219
|
+
location.schema,
|
|
220
|
+
location.effective_table_prefix ?? location.table_prefix,
|
|
221
|
+
]
|
|
222
|
+
.filter(Boolean)
|
|
223
|
+
.join(".")
|
|
224
|
+
: undefined);
|
|
213
225
|
loadedUcPrefix = { prefix };
|
|
214
226
|
return prefix;
|
|
215
227
|
}
|
|
@@ -229,6 +241,23 @@ const MlflowExperimentResponseSchema = z
|
|
|
229
241
|
.object({
|
|
230
242
|
experiment_id: z.string().min(1).describe("Workspace MLflow experiment identifier."),
|
|
231
243
|
tags: z.unknown().optional().describe("Experiment tags, including the UC trace location."),
|
|
244
|
+
trace_location: z
|
|
245
|
+
.object({
|
|
246
|
+
uc_trace_location: z
|
|
247
|
+
.object({
|
|
248
|
+
catalog: z.string().describe("Unity Catalog catalog name."),
|
|
249
|
+
schema: z.string().describe("Unity Catalog schema name."),
|
|
250
|
+
table_prefix: z.string().describe("Configured trace table prefix."),
|
|
251
|
+
effective_table_prefix: z
|
|
252
|
+
.string()
|
|
253
|
+
.optional()
|
|
254
|
+
.describe("Effective trace table prefix selected by Databricks."),
|
|
255
|
+
})
|
|
256
|
+
.optional()
|
|
257
|
+
.describe("Unity Catalog trace location."),
|
|
258
|
+
})
|
|
259
|
+
.optional()
|
|
260
|
+
.describe("Experiment trace storage location."),
|
|
232
261
|
})
|
|
233
262
|
.passthrough()
|
|
234
263
|
.describe("Configured MLflow experiment."),
|
|
@@ -354,13 +383,13 @@ export async function initializeAppMlflowTraceInfo(client?: WorkspaceClient): Pr
|
|
|
354
383
|
appMlflowTraceInfoInitStarted = true;
|
|
355
384
|
try {
|
|
356
385
|
const resolvedClient = client ?? (createWorkspaceClient() as WorkspaceClient);
|
|
386
|
+
appMlflowTraceInfoClient ??= resolvedClient;
|
|
357
387
|
const prefix = await resolveUcTracePrefix(resolvedClient);
|
|
358
388
|
if (!prefix || prefix.split(".").length !== 3) {
|
|
359
389
|
throw new ConfigurationError(
|
|
360
390
|
"Databricks Apps MLflow trace-info promotion requires a UC-linked experiment or MLFLOW_UC_TRACE_PREFIX.",
|
|
361
391
|
);
|
|
362
392
|
}
|
|
363
|
-
appMlflowTraceInfoClient = resolvedClient;
|
|
364
393
|
appMlflowTracePrefix = prefix;
|
|
365
394
|
logger.info("Databricks Apps MLflow trace-info promotion enabled", { prefix });
|
|
366
395
|
return true;
|
|
@@ -368,10 +397,25 @@ export async function initializeAppMlflowTraceInfo(client?: WorkspaceClient): Pr
|
|
|
368
397
|
logger.warn("Databricks Apps MLflow trace-info promotion disabled", {
|
|
369
398
|
error: errorUtils.toError(err),
|
|
370
399
|
});
|
|
400
|
+
// Startup uses the service principal. Permit one later request-scoped
|
|
401
|
+
// client to resolve experiment metadata when that principal cannot read it.
|
|
402
|
+
if (client === undefined) appMlflowTraceInfoInitStarted = false;
|
|
371
403
|
return false;
|
|
372
404
|
}
|
|
373
405
|
}
|
|
374
406
|
|
|
407
|
+
/**
|
|
408
|
+
* Retry Apps trace-location discovery once with a request-scoped workspace
|
|
409
|
+
* client after service-principal startup discovery failed.
|
|
410
|
+
*/
|
|
411
|
+
export async function initializeRequestMlflowTraceInfo(client: WorkspaceClient): Promise<boolean> {
|
|
412
|
+
if (appMlflowTraceInfoClient && appMlflowTracePrefix) return true;
|
|
413
|
+
if (appMlflowTraceInfoRequestInitStarted || !appMlflowTraceInfoConfigured()) return false;
|
|
414
|
+
appMlflowTraceInfoRequestInitStarted = true;
|
|
415
|
+
appMlflowTraceInfoInitStarted = false;
|
|
416
|
+
return initializeAppMlflowTraceInfo(client);
|
|
417
|
+
}
|
|
418
|
+
|
|
375
419
|
/** Trace-level values persisted beside spans already exported by the Apps OTLP sidecar. */
|
|
376
420
|
export interface AppMlflowTraceInfo {
|
|
377
421
|
traceId: string;
|
|
@@ -434,6 +478,7 @@ export function persistAppMlflowTraceInfo(params: AppMlflowTraceInfo): void {
|
|
|
434
478
|
/** Reset Apps trace-info promotion state for tests. */
|
|
435
479
|
export function resetAppMlflowTraceInfo(): void {
|
|
436
480
|
appMlflowTraceInfoInitStarted = false;
|
|
481
|
+
appMlflowTraceInfoRequestInitStarted = false;
|
|
437
482
|
appMlflowTraceInfoClient = undefined;
|
|
438
483
|
appMlflowTracePrefix = undefined;
|
|
439
484
|
pendingAppMlflowTraceInfo.clear();
|
|
@@ -644,7 +689,7 @@ async function resolveMlflowExperimentManagerUrl(
|
|
|
644
689
|
|
|
645
690
|
async function fetchExperiment(
|
|
646
691
|
client: WorkspaceClient,
|
|
647
|
-
): Promise<
|
|
692
|
+
): Promise<z.infer<typeof MlflowExperimentResponseSchema>["experiment"] | undefined> {
|
|
648
693
|
const experiment = configuredExperiment();
|
|
649
694
|
const path = experiment.id
|
|
650
695
|
? `/api/2.0/mlflow/experiments/get?experiment_id=${encodeURIComponent(experiment.id)}`
|
package/src/plugin.ts
CHANGED
|
@@ -278,6 +278,11 @@ export class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
278
278
|
});
|
|
279
279
|
this.config.feedbackMode = this.resolvedFeedbackMode;
|
|
280
280
|
if (this.config.observability !== false) {
|
|
281
|
+
await appkitTelemetry.configureNativeHttpTelemetry({
|
|
282
|
+
// A low-level request without an active semantic span becomes a noisy
|
|
283
|
+
// standalone trace. Requests inside an agent/tool span remain visible.
|
|
284
|
+
outgoingFilter: appkitTelemetry.excludeUnparentedOutgoingHttpTelemetry,
|
|
285
|
+
});
|
|
281
286
|
await registerAppMlflowTraceInfoProcessor();
|
|
282
287
|
}
|
|
283
288
|
// Wait until sibling plugins (e.g. `lakebase`) finish `setup()` so
|
|
@@ -386,8 +391,8 @@ export class MastraPlugin extends Plugin<MastraPluginConfig> {
|
|
|
386
391
|
getMastra: () => this.mastra,
|
|
387
392
|
/**
|
|
388
393
|
* Build the `RequestContext` an agent turn driven from OUTSIDE this
|
|
389
|
-
* plugin's routes needs
|
|
390
|
-
*
|
|
394
|
+
* plugin's routes needs, such as another plugin's endpoint or a scheduled
|
|
395
|
+
* job.
|
|
391
396
|
*
|
|
392
397
|
* Mastra's user-scoped tools - `ask_genie` most visibly - read the AppKit
|
|
393
398
|
* user off the request context, which only the HTTP middleware stamps.
|
package/src/server.ts
CHANGED
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
FOLLOW_UP_USER_TEXT_KEY,
|
|
36
36
|
} from "./follow-up-suggestions.ts";
|
|
37
37
|
import { requestUserEmail, requestUserId } from "./identity.ts";
|
|
38
|
+
import { initializeRequestMlflowTraceInfo } from "./mlflow.ts";
|
|
38
39
|
import { MASTRA_ORIGIN_KEY } from "./request-context.ts";
|
|
39
40
|
import {
|
|
40
41
|
extractModelOverride,
|
|
@@ -118,12 +119,11 @@ export interface AttributedIdentity {
|
|
|
118
119
|
* `requestContext`.
|
|
119
120
|
*
|
|
120
121
|
* Split out of {@link MastraServer} because the HTTP middleware is not the only
|
|
121
|
-
* caller that needs it: an out-of-band turn
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
* request.
|
|
122
|
+
* caller that needs it: an out-of-band turn from another plugin route or a
|
|
123
|
+
* scheduled job drives `agent.generate` directly, and without this stamp every
|
|
124
|
+
* user-scoped tool - `ask_genie` above all - fails with "invoke the tool from an
|
|
125
|
+
* agent turn served by the mastra plugin". Those callers build a context with
|
|
126
|
+
* {@link createRequestContext} instead of a request.
|
|
127
127
|
*
|
|
128
128
|
* The Databricks credential always comes from the ambient execution context;
|
|
129
129
|
* `attributed` only changes WHO the turn is attributed to when that context is
|
|
@@ -138,6 +138,7 @@ export async function stampRequestContextUser(
|
|
|
138
138
|
): Promise<void> {
|
|
139
139
|
if ([MASTRA_USER_KEY, MASTRA_RESOURCE_ID_KEY].every((key) => requestContext.get(key))) return;
|
|
140
140
|
const executionContext = getExecutionContext();
|
|
141
|
+
await initializeRequestMlflowTraceInfo(executionContext.client);
|
|
141
142
|
const isUserContext = "isUserContext" in executionContext;
|
|
142
143
|
// The Databricks CREDENTIAL is always the ambient execution context's client
|
|
143
144
|
// (OBO in a user context, the service principal otherwise). WHO the turn is
|
|
@@ -185,8 +186,8 @@ export async function stampRequestContextUser(
|
|
|
185
186
|
* a request id so the turn's spans join up in traces. The result is passed as
|
|
186
187
|
* `requestContext` to `agent.generate` / `agent.stream`.
|
|
187
188
|
*
|
|
188
|
-
* This
|
|
189
|
-
*
|
|
189
|
+
* This gives an out-of-band turn the same data and tool capability as a chat
|
|
190
|
+
* turn.
|
|
190
191
|
*/
|
|
191
192
|
export async function createRequestContext(
|
|
192
193
|
options: { threadId?: string; resourceId?: string; requestId?: string } = {},
|
|
@@ -194,8 +195,8 @@ export async function createRequestContext(
|
|
|
194
195
|
const requestContext = new RequestContext();
|
|
195
196
|
await stampRequestContextUser(requestContext);
|
|
196
197
|
if (options.threadId) requestContext.set(MASTRA_THREAD_ID_KEY, options.threadId);
|
|
197
|
-
// Stamped AFTER the user so an explicit resource id
|
|
198
|
-
//
|
|
198
|
+
// Stamped AFTER the user so an explicit resource id wins over the ambient
|
|
199
|
+
// identity `stampRequestContextUser` defaulted to.
|
|
199
200
|
if (options.resourceId) requestContext.set(MASTRA_RESOURCE_ID_KEY, options.resourceId);
|
|
200
201
|
requestContext.set(MASTRA_REQUEST_ID_KEY, options.requestId ?? hash.id());
|
|
201
202
|
return requestContext;
|
package/src/telemetry.ts
CHANGED
|
@@ -42,6 +42,7 @@ import type express from "express";
|
|
|
42
42
|
import { MASTRA_AUTH_MODE_KEY, MASTRA_USER_EMAIL_KEY, MASTRA_USER_NAME_KEY } from "./config.ts";
|
|
43
43
|
import { USER_EMAIL_HEADER, USER_ID_HEADER } from "./identity.ts";
|
|
44
44
|
import {
|
|
45
|
+
appMlflowTraceInfoConfigured,
|
|
45
46
|
directMlflowTracingActive,
|
|
46
47
|
initializeAppMlflowTraceInfo,
|
|
47
48
|
persistAppMlflowTraceInfo,
|
|
@@ -118,7 +119,7 @@ export const GEN_AI_OPERATION_NAME_ATTR = "gen_ai.operation.name";
|
|
|
118
119
|
* Root-span user id the MLflow experiment UI's User column reads from OTel
|
|
119
120
|
* traces. Prefer the forwarded email when present so the column is human.
|
|
120
121
|
*/
|
|
121
|
-
export const MLFLOW_USER_ATTR =
|
|
122
|
+
export const MLFLOW_USER_ATTR = appkitTelemetry.USER_TRACE_ATTRIBUTE;
|
|
122
123
|
/** Root-span session id that enables the MLflow Sessions tab. */
|
|
123
124
|
export const MLFLOW_SESSION_ATTR = "session.id";
|
|
124
125
|
/** Root-span type for a Mastra agent turn. */
|
|
@@ -507,7 +508,8 @@ let appMlflowTraceInfoProcessorRegistered = false;
|
|
|
507
508
|
/** Register trace enrichment before AppKit constructs its native provider. */
|
|
508
509
|
export async function registerAppMlflowTraceInfoProcessor(): Promise<boolean> {
|
|
509
510
|
if (appMlflowTraceInfoProcessorRegistered) return true;
|
|
510
|
-
if (!(
|
|
511
|
+
if (!appMlflowTraceInfoConfigured()) return false;
|
|
512
|
+
await initializeAppMlflowTraceInfo();
|
|
511
513
|
await appkitTelemetry.registerNativeSpanProcessor(new AppMlflowTraceInfoSpanProcessor());
|
|
512
514
|
appMlflowTraceInfoProcessorRegistered = true;
|
|
513
515
|
return true;
|