@dbx-tools/appkit-mastra 0.9.65 → 0.9.68
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 +29 -3
- package/exports.ts +7 -0
- package/index.ts +8 -4
- package/lib/exports.d.ts +1 -0
- package/lib/exports.js +2 -0
- package/lib/index.d.ts +8 -4
- package/lib/index.js +7 -3
- package/lib/src/genie.js +3 -2
- package/lib/src/mlflow.d.ts +68 -5
- package/lib/src/mlflow.js +310 -8
- package/lib/src/model.js +4 -1
- package/lib/src/observability.d.ts +2 -1
- package/lib/src/observability.js +18 -6
- package/lib/src/plugin.js +10 -2
- package/lib/src/server.js +12 -1
- package/lib/src/telemetry.d.ts +17 -16
- package/lib/src/telemetry.js +177 -23
- package/lib/src/trace-attributes.d.ts +26 -0
- package/lib/src/trace-attributes.js +27 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +30 -16
- package/src/genie.ts +2 -1
- package/src/mlflow.ts +356 -7
- package/src/model.ts +3 -0
- package/src/observability.ts +22 -5
- package/src/plugin.ts +8 -0
- package/src/server.ts +13 -0
- package/src/telemetry.ts +238 -23
- package/src/trace-attributes.ts +38 -0
package/src/telemetry.ts
CHANGED
|
@@ -20,13 +20,48 @@
|
|
|
20
20
|
|
|
21
21
|
import { StringDecoder } from "node:string_decoder";
|
|
22
22
|
|
|
23
|
+
import { appkit } from "@dbx-tools/appkit";
|
|
24
|
+
import { getRequestTags } from "@dbx-tools/appkit/request-tags";
|
|
23
25
|
import { json, log, object, stringUtils } from "@dbx-tools/shared-core";
|
|
24
26
|
import { feedback, thread } from "@dbx-tools/shared-mastra";
|
|
25
|
-
import { context, SpanKind, trace, type Span } from "@opentelemetry/api";
|
|
27
|
+
import { context, createContextKey, SpanKind, trace, type Span } from "@opentelemetry/api";
|
|
26
28
|
import { getRPCMetadata, RPCType } from "@opentelemetry/core";
|
|
27
29
|
import type express from "express";
|
|
28
30
|
|
|
29
31
|
import { USER_EMAIL_HEADER, USER_ID_HEADER } from "./identity.ts";
|
|
32
|
+
import {
|
|
33
|
+
directMlflowTracingActive,
|
|
34
|
+
persistAppMlflowTraceInfo,
|
|
35
|
+
updateActiveMlflowTrace,
|
|
36
|
+
updateActiveMlflowTraceTags,
|
|
37
|
+
} from "./mlflow.ts";
|
|
38
|
+
import {
|
|
39
|
+
MLFLOW_AGENT_TAG,
|
|
40
|
+
MLFLOW_AGENT_TAG_ATTR,
|
|
41
|
+
MLFLOW_GENIE_TAG,
|
|
42
|
+
MLFLOW_GENIE_TAG_ATTR,
|
|
43
|
+
MLFLOW_MODEL_TAG,
|
|
44
|
+
MLFLOW_LOCAL_TAG,
|
|
45
|
+
MLFLOW_OBO_AUTH_TAG,
|
|
46
|
+
MLFLOW_SP_AUTH_TAG,
|
|
47
|
+
MLFLOW_TRACE_TAG_PREFIX,
|
|
48
|
+
} from "./trace-attributes.ts";
|
|
49
|
+
|
|
50
|
+
export {
|
|
51
|
+
MLFLOW_AGENT_TAG,
|
|
52
|
+
MLFLOW_AGENT_TAG_ATTR,
|
|
53
|
+
MLFLOW_GENIE_TAG,
|
|
54
|
+
MLFLOW_GENIE_TAG_ATTR,
|
|
55
|
+
MLFLOW_MODEL_TAG,
|
|
56
|
+
MLFLOW_MODEL_TAG_ATTR,
|
|
57
|
+
MLFLOW_LOCAL_TAG,
|
|
58
|
+
MLFLOW_LOCAL_TAG_ATTR,
|
|
59
|
+
MLFLOW_OBO_AUTH_TAG,
|
|
60
|
+
MLFLOW_OBO_AUTH_TAG_ATTR,
|
|
61
|
+
MLFLOW_SP_AUTH_TAG,
|
|
62
|
+
MLFLOW_SP_AUTH_TAG_ATTR,
|
|
63
|
+
MLFLOW_TRACE_TAG_PREFIX,
|
|
64
|
+
} from "./trace-attributes.ts";
|
|
30
65
|
|
|
31
66
|
const logger = log.logger("mastra/telemetry");
|
|
32
67
|
|
|
@@ -74,24 +109,19 @@ export const GEN_AI_OPERATION_NAME_ATTR = "gen_ai.operation.name";
|
|
|
74
109
|
export const MLFLOW_USER_ATTR = "user.id";
|
|
75
110
|
/** Root-span session id that enables the MLflow Sessions tab. */
|
|
76
111
|
export const MLFLOW_SESSION_ATTR = "session.id";
|
|
77
|
-
/**
|
|
78
|
-
* Prefix Databricks/MLflow copies onto the unified-view `tags` map. The
|
|
79
|
-
* suffix after this prefix is the tag name shown in the experiment UI.
|
|
80
|
-
*/
|
|
81
|
-
export const MLFLOW_TRACE_TAG_PREFIX = "mlflow.trace.tag.";
|
|
82
|
-
/** Experiment-UI tag marking a turn that called Genie. */
|
|
83
|
-
export const MLFLOW_GENIE_TAG = "genie";
|
|
84
112
|
/** Root-span type for a Mastra agent turn. */
|
|
85
113
|
export const MLFLOW_SPAN_TYPE_AGENT = "AGENT";
|
|
86
114
|
/** Span type for an `ask_genie` tool call. */
|
|
87
115
|
export const MLFLOW_SPAN_TYPE_GENIE = "GENIE";
|
|
88
|
-
/** Attribute key for the `genie` trace tag. */
|
|
89
|
-
export const MLFLOW_GENIE_TAG_ATTR = `${MLFLOW_TRACE_TAG_PREFIX}${MLFLOW_GENIE_TAG}`;
|
|
90
116
|
|
|
91
117
|
/** Root-span metadata known before a chat request is dispatched. */
|
|
92
118
|
export interface ChatTurnTelemetryOptions {
|
|
93
119
|
readonly identity?:
|
|
94
120
|
"obo" | "service-principal" | ((request: express.Request) => "obo" | "service-principal");
|
|
121
|
+
/** Initial trace tags derived from the inbound request. */
|
|
122
|
+
readonly tags?:
|
|
123
|
+
| Readonly<Record<string, ChatTraceTagValue | undefined>>
|
|
124
|
+
| ((request: express.Request) => Readonly<Record<string, ChatTraceTagValue | undefined>>);
|
|
95
125
|
}
|
|
96
126
|
|
|
97
127
|
interface TraceTarget {
|
|
@@ -99,16 +129,20 @@ interface TraceTarget {
|
|
|
99
129
|
owned: boolean;
|
|
100
130
|
}
|
|
101
131
|
|
|
132
|
+
interface ChatTraceState {
|
|
133
|
+
readonly models: Set<string>;
|
|
134
|
+
readonly tags: Map<string, string>;
|
|
135
|
+
user?: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/** Values accepted by request-scoped trace-tag injection helpers. */
|
|
139
|
+
export type ChatTraceTagValue = string | number | boolean;
|
|
140
|
+
|
|
141
|
+
const CHAT_TRACE_STATE_KEY = createContextKey("@dbx-tools/appkit-mastra/chat-trace-state");
|
|
142
|
+
|
|
102
143
|
/** OpenTelemetry's sentinel for "no valid trace" - 32 zero hex chars. */
|
|
103
144
|
const INVALID_TRACE_ID = "0".repeat(32);
|
|
104
145
|
|
|
105
|
-
/**
|
|
106
|
-
* Publish the turn's MLflow trace id before the handler writes the body.
|
|
107
|
-
*
|
|
108
|
-
* The chat UI only shows thumbs when this header is present. Stamping it here
|
|
109
|
-
* (on the same span the middleware just resolved) avoids relying on async
|
|
110
|
-
* Express continuation still sitting inside `context.with()`.
|
|
111
|
-
*/
|
|
112
146
|
/**
|
|
113
147
|
* Mark the active span as a Genie call so the experiment UI types it `GENIE`
|
|
114
148
|
* and tags the trace `genie=true`. Safe no-op when no recording span is active.
|
|
@@ -135,14 +169,89 @@ function requestQuery(req: express.Request, name: string): string | undefined {
|
|
|
135
169
|
return stringUtils.trimToNull(typeof value === "string" ? value : undefined) ?? undefined;
|
|
136
170
|
}
|
|
137
171
|
|
|
138
|
-
function stampMlflowActor(span: Span, req: express.Request):
|
|
172
|
+
function stampMlflowActor(span: Span, req: express.Request): string | undefined {
|
|
139
173
|
const user = requestHeader(req, USER_EMAIL_HEADER) ?? requestHeader(req, USER_ID_HEADER);
|
|
140
174
|
if (user) span.setAttribute(MLFLOW_USER_ATTR, user);
|
|
141
175
|
const session =
|
|
142
176
|
requestHeader(req, thread.THREAD_ID_HEADER) ?? requestQuery(req, thread.THREAD_ID_QUERY);
|
|
143
177
|
if (session) span.setAttribute(MLFLOW_SESSION_ATTR, session);
|
|
178
|
+
return user;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function traceId(span: Span | undefined = trace.getActiveSpan()): string | undefined {
|
|
182
|
+
const value = span?.spanContext().traceId;
|
|
183
|
+
return value && value !== INVALID_TRACE_ID ? value : undefined;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function activeChatTraceState(): ChatTraceState | undefined {
|
|
187
|
+
return context.active().getValue(CHAT_TRACE_STATE_KEY) as ChatTraceState | undefined;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function setChatTraceTag(
|
|
191
|
+
state: ChatTraceState,
|
|
192
|
+
name: string,
|
|
193
|
+
value: ChatTraceTagValue,
|
|
194
|
+
): void {
|
|
195
|
+
const tag = stringUtils.trimToNull(name);
|
|
196
|
+
if (!tag) return;
|
|
197
|
+
state.tags.set(tag, String(value));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/** Inject one MLflow trace tag into the active chat request. */
|
|
201
|
+
export function recordActiveTraceTag(
|
|
202
|
+
name: string,
|
|
203
|
+
value: ChatTraceTagValue = true,
|
|
204
|
+
): void {
|
|
205
|
+
const state = activeChatTraceState();
|
|
206
|
+
if (state) setChatTraceTag(state, name, value);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/** Inject multiple MLflow trace tags into the active chat request. */
|
|
210
|
+
export function recordActiveTraceTags(
|
|
211
|
+
tags: Readonly<Record<string, ChatTraceTagValue | undefined>>,
|
|
212
|
+
): void {
|
|
213
|
+
const state = activeChatTraceState();
|
|
214
|
+
if (!state) return;
|
|
215
|
+
for (const [name, value] of Object.entries(tags)) {
|
|
216
|
+
if (value !== undefined) setChatTraceTag(state, name, value);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** Record a selected model on the active chat trace, preserving first-use order. */
|
|
221
|
+
export function recordActiveTraceModel(modelId: string): void {
|
|
222
|
+
const value = stringUtils.trimToNull(modelId);
|
|
223
|
+
if (!value) return;
|
|
224
|
+
activeChatTraceState()?.models.add(value);
|
|
144
225
|
}
|
|
145
226
|
|
|
227
|
+
/** Record the best resolved user identifier on the active chat trace. */
|
|
228
|
+
export function recordActiveTraceUser(user: string | undefined): void {
|
|
229
|
+
const value = stringUtils.trimToNull(user);
|
|
230
|
+
if (!value) return;
|
|
231
|
+
const state = activeChatTraceState();
|
|
232
|
+
if (state) state.user = value;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** Record one Databricks authentication mode used during the active chat trace. */
|
|
236
|
+
export function recordActiveTraceAuth(auth: "obo" | "service-principal"): void {
|
|
237
|
+
recordActiveTraceTag(auth === "obo" ? MLFLOW_OBO_AUTH_TAG : MLFLOW_SP_AUTH_TAG);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** Serialize model tags as a scalar or an ordered JSON array for multiple values. */
|
|
241
|
+
export function modelTraceTag(models: Iterable<string>): string | undefined {
|
|
242
|
+
const values: string[] = [];
|
|
243
|
+
const seen = new Set<string>();
|
|
244
|
+
for (const model of models) {
|
|
245
|
+
const value = stringUtils.trimToNull(model);
|
|
246
|
+
if (!value || seen.has(value)) continue;
|
|
247
|
+
seen.add(value);
|
|
248
|
+
values.push(value);
|
|
249
|
+
}
|
|
250
|
+
if (values.length === 0) return undefined;
|
|
251
|
+
return values.length === 1 ? values[0] : JSON.stringify(values);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** Publish the turn's MLflow trace id before the handler writes the body. */
|
|
146
255
|
function stampMlflowTraceId(res: express.Response, span: Span): void {
|
|
147
256
|
if (res.headersSent) return;
|
|
148
257
|
const traceId = span.spanContext().traceId;
|
|
@@ -315,6 +424,28 @@ class AssistantResponseCollector {
|
|
|
315
424
|
*/
|
|
316
425
|
function resolveTraceTarget(): TraceTarget | undefined {
|
|
317
426
|
const activeContext = context.active();
|
|
427
|
+
if (directMlflowTracingActive()) {
|
|
428
|
+
const span = trace
|
|
429
|
+
.getTracer(CHAT_TURN_TRACER)
|
|
430
|
+
.startSpan(
|
|
431
|
+
"mastra.chat_turn",
|
|
432
|
+
{
|
|
433
|
+
kind: SpanKind.SERVER,
|
|
434
|
+
attributes: {
|
|
435
|
+
[MLFLOW_AGENT_TAG_ATTR]: "true",
|
|
436
|
+
[MLFLOW_SPAN_TYPE_ATTR]: MLFLOW_SPAN_TYPE_AGENT,
|
|
437
|
+
[GEN_AI_OPERATION_NAME_ATTR]: "invoke_agent",
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
trace.deleteSpan(activeContext),
|
|
441
|
+
);
|
|
442
|
+
if (!span.isRecording()) {
|
|
443
|
+
span.end();
|
|
444
|
+
return undefined;
|
|
445
|
+
}
|
|
446
|
+
return { span, owned: true };
|
|
447
|
+
}
|
|
448
|
+
|
|
318
449
|
const rpc = getRPCMetadata(activeContext);
|
|
319
450
|
if (rpc?.type === RPCType.HTTP && rpc.span.isRecording()) {
|
|
320
451
|
return { span: rpc.span, owned: false };
|
|
@@ -325,7 +456,18 @@ function resolveTraceTarget(): TraceTarget | undefined {
|
|
|
325
456
|
|
|
326
457
|
const span = trace
|
|
327
458
|
.getTracer(CHAT_TURN_TRACER)
|
|
328
|
-
.startSpan(
|
|
459
|
+
.startSpan(
|
|
460
|
+
"mastra.chat_turn",
|
|
461
|
+
{
|
|
462
|
+
kind: SpanKind.SERVER,
|
|
463
|
+
attributes: {
|
|
464
|
+
[MLFLOW_AGENT_TAG_ATTR]: "true",
|
|
465
|
+
[MLFLOW_SPAN_TYPE_ATTR]: MLFLOW_SPAN_TYPE_AGENT,
|
|
466
|
+
[GEN_AI_OPERATION_NAME_ATTR]: "invoke_agent",
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
activeContext,
|
|
470
|
+
);
|
|
329
471
|
if (!span.isRecording()) {
|
|
330
472
|
span.end();
|
|
331
473
|
return undefined;
|
|
@@ -358,36 +500,69 @@ export function chatTurnTelemetryMiddleware(
|
|
|
358
500
|
next();
|
|
359
501
|
return;
|
|
360
502
|
}
|
|
503
|
+
const targetTraceId = traceId(target.span);
|
|
504
|
+
if (!targetTraceId) {
|
|
505
|
+
if (target.owned) target.span.end();
|
|
506
|
+
next();
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
const traceState: ChatTraceState = { models: new Set(), tags: new Map() };
|
|
510
|
+
const traceRequestTime = Date.now();
|
|
511
|
+
setChatTraceTag(traceState, MLFLOW_AGENT_TAG, true);
|
|
512
|
+
if (!appkit.isDatabricksAppEnv()) {
|
|
513
|
+
setChatTraceTag(traceState, MLFLOW_LOCAL_TAG, true);
|
|
514
|
+
}
|
|
515
|
+
for (const [name, value] of Object.entries(getRequestTags(req))) {
|
|
516
|
+
setChatTraceTag(traceState, name, value);
|
|
517
|
+
}
|
|
518
|
+
const initialTags = typeof options.tags === "function" ? options.tags(req) : options.tags;
|
|
519
|
+
if (initialTags) {
|
|
520
|
+
for (const [name, value] of Object.entries(initialTags)) {
|
|
521
|
+
if (value !== undefined) setChatTraceTag(traceState, name, value);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
361
524
|
stampMlflowTraceId(res, target.span);
|
|
362
|
-
stampMlflowActor(target.span, req);
|
|
525
|
+
traceState.user = stampMlflowActor(target.span, req);
|
|
363
526
|
target.span.setAttribute(MLFLOW_SPAN_TYPE_ATTR, MLFLOW_SPAN_TYPE_AGENT);
|
|
364
527
|
target.span.setAttribute(GEN_AI_OPERATION_NAME_ATTR, "invoke_agent");
|
|
528
|
+
target.span.setAttribute(MLFLOW_AGENT_TAG_ATTR, "true");
|
|
365
529
|
const identity =
|
|
366
530
|
typeof options.identity === "function" ? options.identity(req) : options.identity;
|
|
367
531
|
if (identity) {
|
|
368
532
|
target.span.setAttribute(CHAT_IDENTITY_ATTR, identity);
|
|
533
|
+
setChatTraceTag(
|
|
534
|
+
traceState,
|
|
535
|
+
identity === "obo" ? MLFLOW_OBO_AUTH_TAG : MLFLOW_SP_AUTH_TAG,
|
|
536
|
+
true,
|
|
537
|
+
);
|
|
369
538
|
}
|
|
370
539
|
target.span.setAttribute(CHAT_GENIE_USED_ATTR, false);
|
|
371
540
|
|
|
372
541
|
const messages = (req.body as { messages?: unknown } | undefined)?.messages;
|
|
542
|
+
let requestPreview: string | undefined;
|
|
373
543
|
if (messages !== undefined) {
|
|
374
544
|
const fullInput = serialized(messages);
|
|
545
|
+
requestPreview = textOnlyChatInput(messages) ?? fullInput;
|
|
375
546
|
target.span.setAttribute(CHAT_MESSAGES_ATTR, fullInput);
|
|
376
547
|
target.span.setAttribute(
|
|
377
548
|
MLFLOW_SPAN_INPUTS_ATTR,
|
|
378
|
-
|
|
549
|
+
requestPreview.slice(0, TRACE_IO_LIMIT),
|
|
379
550
|
);
|
|
380
551
|
}
|
|
381
552
|
|
|
382
553
|
const collector = new AssistantResponseCollector();
|
|
554
|
+
let responsePreview: string | undefined;
|
|
383
555
|
let outputRecorded = false;
|
|
384
556
|
let fullOutputRecorded = false;
|
|
557
|
+
let genieRecorded = false;
|
|
385
558
|
let ownedSpanEnded = false;
|
|
559
|
+
let traceFinalized = false;
|
|
386
560
|
const passThroughWrite = res.write.bind(res) as (...args: unknown[]) => boolean;
|
|
387
561
|
const passThroughEnd = res.end.bind(res) as (...args: unknown[]) => unknown;
|
|
388
562
|
const passThroughJson = res.json.bind(res) as (body?: unknown) => express.Response;
|
|
389
563
|
const recordOutput = (answer: string): void => {
|
|
390
564
|
if (outputRecorded || !answer) return;
|
|
565
|
+
responsePreview = answer;
|
|
391
566
|
target.span.setAttribute(MLFLOW_SPAN_OUTPUTS_ATTR, answer.slice(0, TRACE_IO_LIMIT));
|
|
392
567
|
outputRecorded = true;
|
|
393
568
|
};
|
|
@@ -397,9 +572,41 @@ export function chatTurnTelemetryMiddleware(
|
|
|
397
572
|
fullOutputRecorded = true;
|
|
398
573
|
};
|
|
399
574
|
const recordGenie = (used: boolean): void => {
|
|
400
|
-
if (!used) return;
|
|
575
|
+
if (!used || genieRecorded) return;
|
|
576
|
+
genieRecorded = true;
|
|
401
577
|
target.span.setAttribute(CHAT_GENIE_USED_ATTR, true);
|
|
402
578
|
target.span.setAttribute(MLFLOW_GENIE_TAG_ATTR, "true");
|
|
579
|
+
setChatTraceTag(traceState, MLFLOW_GENIE_TAG, true);
|
|
580
|
+
context.with(trace.setSpan(context.active(), target.span), () => {
|
|
581
|
+
updateActiveMlflowTraceTags({ [MLFLOW_GENIE_TAG]: "true" });
|
|
582
|
+
});
|
|
583
|
+
};
|
|
584
|
+
const finalizeTrace = (): void => {
|
|
585
|
+
if (traceFinalized) return;
|
|
586
|
+
traceFinalized = true;
|
|
587
|
+
const model = modelTraceTag(traceState.models);
|
|
588
|
+
if (model) setChatTraceTag(traceState, MLFLOW_MODEL_TAG, model);
|
|
589
|
+
const tags = Object.fromEntries(traceState.tags);
|
|
590
|
+
for (const [name, value] of traceState.tags) {
|
|
591
|
+
target.span.setAttribute(`${MLFLOW_TRACE_TAG_PREFIX}${name}`, value);
|
|
592
|
+
}
|
|
593
|
+
if (traceState.user) target.span.setAttribute(MLFLOW_USER_ATTR, traceState.user);
|
|
594
|
+
context.with(trace.setSpan(context.active(), target.span), () => {
|
|
595
|
+
updateActiveMlflowTrace({
|
|
596
|
+
...(Object.keys(tags).length > 0 ? { tags } : {}),
|
|
597
|
+
...(traceState.user ? { user: traceState.user } : {}),
|
|
598
|
+
});
|
|
599
|
+
});
|
|
600
|
+
persistAppMlflowTraceInfo({
|
|
601
|
+
traceId: targetTraceId,
|
|
602
|
+
requestTime: traceRequestTime,
|
|
603
|
+
executionDuration: Date.now() - traceRequestTime,
|
|
604
|
+
error: res.statusCode >= 500,
|
|
605
|
+
tags,
|
|
606
|
+
...(traceState.user ? { user: traceState.user } : {}),
|
|
607
|
+
...(requestPreview ? { requestPreview } : {}),
|
|
608
|
+
...(responsePreview ? { responsePreview } : {}),
|
|
609
|
+
});
|
|
403
610
|
};
|
|
404
611
|
const endOwnedSpan = (): void => {
|
|
405
612
|
if (!target.owned || ownedSpanEnded) return;
|
|
@@ -424,6 +631,7 @@ export function chatTurnTelemetryMiddleware(
|
|
|
424
631
|
recordOutput(collector.finish());
|
|
425
632
|
recordFullOutput(collector.rawBody());
|
|
426
633
|
recordGenie(collector.usedGenie());
|
|
634
|
+
finalizeTrace();
|
|
427
635
|
try {
|
|
428
636
|
return passThroughEnd(chunk, ...rest);
|
|
429
637
|
} finally {
|
|
@@ -435,10 +643,17 @@ export function chatTurnTelemetryMiddleware(
|
|
|
435
643
|
recordOutput(collector.finish());
|
|
436
644
|
recordFullOutput(collector.rawBody());
|
|
437
645
|
recordGenie(collector.usedGenie());
|
|
646
|
+
finalizeTrace();
|
|
438
647
|
endOwnedSpan();
|
|
439
648
|
});
|
|
440
649
|
|
|
441
|
-
|
|
650
|
+
const chatContext = trace
|
|
651
|
+
.setSpan(context.active(), target.span)
|
|
652
|
+
.setValue(CHAT_TRACE_STATE_KEY, traceState);
|
|
653
|
+
context.with(chatContext, () => {
|
|
654
|
+
updateActiveMlflowTraceTags({ [MLFLOW_AGENT_TAG]: "true" });
|
|
655
|
+
next();
|
|
656
|
+
});
|
|
442
657
|
}
|
|
443
658
|
|
|
444
659
|
/**
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/** Prefix Databricks/MLflow copies onto the unified-view trace tags map. */
|
|
2
|
+
export const MLFLOW_TRACE_TAG_PREFIX = "mlflow.traceTag.";
|
|
3
|
+
|
|
4
|
+
/** Experiment-UI tag marking every Mastra chat turn as an agent trace. */
|
|
5
|
+
export const MLFLOW_AGENT_TAG = "agent";
|
|
6
|
+
|
|
7
|
+
/** Experiment-UI tag marking a turn that called Genie. */
|
|
8
|
+
export const MLFLOW_GENIE_TAG = "genie";
|
|
9
|
+
|
|
10
|
+
/** Experiment-UI tag recording the model or ordered models used by a turn. */
|
|
11
|
+
export const MLFLOW_MODEL_TAG = "model";
|
|
12
|
+
|
|
13
|
+
/** Experiment-UI tag marking a turn that invoked OBO authentication. */
|
|
14
|
+
export const MLFLOW_OBO_AUTH_TAG = "obo_auth";
|
|
15
|
+
|
|
16
|
+
/** Experiment-UI tag marking a turn that invoked service-principal authentication. */
|
|
17
|
+
export const MLFLOW_SP_AUTH_TAG = "sp_auth";
|
|
18
|
+
|
|
19
|
+
/** Experiment-UI tag marking a turn emitted outside Databricks Apps. */
|
|
20
|
+
export const MLFLOW_LOCAL_TAG = "local";
|
|
21
|
+
|
|
22
|
+
/** Attribute key for the `agent` trace tag. */
|
|
23
|
+
export const MLFLOW_AGENT_TAG_ATTR = `${MLFLOW_TRACE_TAG_PREFIX}${MLFLOW_AGENT_TAG}`;
|
|
24
|
+
|
|
25
|
+
/** Attribute key for the `genie` trace tag. */
|
|
26
|
+
export const MLFLOW_GENIE_TAG_ATTR = `${MLFLOW_TRACE_TAG_PREFIX}${MLFLOW_GENIE_TAG}`;
|
|
27
|
+
|
|
28
|
+
/** Attribute key for the `model` trace tag. */
|
|
29
|
+
export const MLFLOW_MODEL_TAG_ATTR = `${MLFLOW_TRACE_TAG_PREFIX}${MLFLOW_MODEL_TAG}`;
|
|
30
|
+
|
|
31
|
+
/** Attribute key for the `obo_auth` trace tag. */
|
|
32
|
+
export const MLFLOW_OBO_AUTH_TAG_ATTR = `${MLFLOW_TRACE_TAG_PREFIX}${MLFLOW_OBO_AUTH_TAG}`;
|
|
33
|
+
|
|
34
|
+
/** Attribute key for the `sp_auth` trace tag. */
|
|
35
|
+
export const MLFLOW_SP_AUTH_TAG_ATTR = `${MLFLOW_TRACE_TAG_PREFIX}${MLFLOW_SP_AUTH_TAG}`;
|
|
36
|
+
|
|
37
|
+
/** Attribute key for the `local` trace tag. */
|
|
38
|
+
export const MLFLOW_LOCAL_TAG_ATTR = `${MLFLOW_TRACE_TAG_PREFIX}${MLFLOW_LOCAL_TAG}`;
|