@mastra/observability 1.0.0-beta.2 → 1.0.0-beta.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # @mastra/observability
2
2
 
3
+ ## 1.0.0-beta.3
4
+
5
+ ### Patch Changes
6
+
7
+ - Add time-to-first-token (TTFT) support for Braintrust integration ([#10840](https://github.com/mastra-ai/mastra/pull/10840))
8
+
9
+ Adds `time_to_first_token` metric to Braintrust spans, populated from the `completionStartTime` attribute captured when the first streaming chunk arrives.
10
+
11
+ ```typescript
12
+ // time_to_first_token is now automatically sent to Braintrust
13
+ // as part of span metrics during streaming
14
+ const result = await agent.stream('Hello');
15
+ ```
16
+
17
+ - Add time-to-first-token (TTFT) support for Langfuse integration ([#10781](https://github.com/mastra-ai/mastra/pull/10781))
18
+
19
+ Adds `completionStartTime` to model generation spans, which Langfuse uses to calculate TTFT metrics. The timestamp is automatically captured when the first content chunk arrives during streaming.
20
+
21
+ ```typescript
22
+ // completionStartTime is now automatically captured and sent to Langfuse
23
+ // enabling TTFT metrics in your Langfuse dashboard
24
+ const result = await agent.stream('Hello');
25
+ ```
26
+
27
+ - Consolidated tool-output chunks from nested agents into single tool-result spans ([#10836](https://github.com/mastra-ai/mastra/pull/10836))
28
+
29
+ - link langfuse prompts and helper functions ([#10738](https://github.com/mastra-ai/mastra/pull/10738))
30
+
31
+ - Updated dependencies [[`3076c67`](https://github.com/mastra-ai/mastra/commit/3076c6778b18988ae7d5c4c5c466366974b2d63f), [`85d7ee1`](https://github.com/mastra-ai/mastra/commit/85d7ee18ff4e14d625a8a30ec6656bb49804989b), [`c6c1092`](https://github.com/mastra-ai/mastra/commit/c6c1092f8fbf76109303f69e000e96fd1960c4ce), [`81dc110`](https://github.com/mastra-ai/mastra/commit/81dc11008d147cf5bdc8996ead1aa61dbdebb6fc), [`7aedb74`](https://github.com/mastra-ai/mastra/commit/7aedb74883adf66af38e270e4068fd42e7a37036), [`8f02d80`](https://github.com/mastra-ai/mastra/commit/8f02d800777397e4b45d7f1ad041988a8b0c6630), [`d7aad50`](https://github.com/mastra-ai/mastra/commit/d7aad501ce61646b76b4b511e558ac4eea9884d0), [`ce0a73a`](https://github.com/mastra-ai/mastra/commit/ce0a73abeaa75b10ca38f9e40a255a645d50ebfb), [`a02e542`](https://github.com/mastra-ai/mastra/commit/a02e542d23179bad250b044b17ff023caa61739f), [`a372c64`](https://github.com/mastra-ai/mastra/commit/a372c640ad1fd12e8f0613cebdc682fc156b4d95), [`8846867`](https://github.com/mastra-ai/mastra/commit/8846867ffa9a3746767618e314bebac08eb77d87), [`42a42cf`](https://github.com/mastra-ai/mastra/commit/42a42cf3132b9786feecbb8c13c583dce5b0e198), [`ae08bf0`](https://github.com/mastra-ai/mastra/commit/ae08bf0ebc6a4e4da992b711c4a389c32ba84cf4), [`21735a7`](https://github.com/mastra-ai/mastra/commit/21735a7ef306963554a69a89b44f06c3bcd85141), [`1d877b8`](https://github.com/mastra-ai/mastra/commit/1d877b8d7b536a251c1a7a18db7ddcf4f68d6f8b)]:
32
+ - @mastra/core@1.0.0-beta.7
33
+
3
34
  ## 1.0.0-beta.2
4
35
 
5
36
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -955,9 +955,30 @@ var ModelSpanTracker = class {
955
955
  #accumulator = {};
956
956
  #stepIndex = 0;
957
957
  #chunkSequence = 0;
958
+ /** Tracks whether completionStartTime has been captured for this generation */
959
+ #completionStartTimeCaptured = false;
960
+ /** Tracks tool output accumulators by toolCallId for consolidating sub-agent streams */
961
+ #toolOutputAccumulators = /* @__PURE__ */ new Map();
962
+ /** Tracks toolCallIds that had streaming output (to skip redundant tool-result spans) */
963
+ #streamedToolCallIds = /* @__PURE__ */ new Set();
958
964
  constructor(modelSpan) {
959
965
  this.#modelSpan = modelSpan;
960
966
  }
967
+ /**
968
+ * Capture the completion start time (time to first token) when the first content chunk arrives.
969
+ * This is used by observability providers like Langfuse to calculate TTFT metrics.
970
+ */
971
+ #captureCompletionStartTime() {
972
+ if (this.#completionStartTimeCaptured || !this.#modelSpan) {
973
+ return;
974
+ }
975
+ this.#completionStartTimeCaptured = true;
976
+ this.#modelSpan.update({
977
+ attributes: {
978
+ completionStartTime: /* @__PURE__ */ new Date()
979
+ }
980
+ });
981
+ }
961
982
  /**
962
983
  * Get the tracing context for creating child spans.
963
984
  * Returns the current step span if active, otherwise the model span.
@@ -1182,6 +1203,77 @@ var ModelSpanTracker = class {
1182
1203
  break;
1183
1204
  }
1184
1205
  }
1206
+ /**
1207
+ * Handle tool-output chunks from sub-agents.
1208
+ * Consolidates streaming text/reasoning deltas into a single span per tool call.
1209
+ */
1210
+ #handleToolOutputChunk(chunk) {
1211
+ if (chunk.type !== "tool-output") return;
1212
+ const payload = chunk.payload;
1213
+ const { output, toolCallId, toolName } = payload;
1214
+ let acc = this.#toolOutputAccumulators.get(toolCallId);
1215
+ if (!acc) {
1216
+ if (!this.#currentStepSpan) {
1217
+ this.#startStepSpan();
1218
+ }
1219
+ acc = {
1220
+ toolName: toolName || "unknown",
1221
+ toolCallId,
1222
+ text: "",
1223
+ reasoning: "",
1224
+ sequenceNumber: this.#chunkSequence++,
1225
+ // Name the span 'tool-result' for consistency (tool-call → tool-result)
1226
+ span: this.#currentStepSpan?.createChildSpan({
1227
+ name: `chunk: 'tool-result'`,
1228
+ type: observability.SpanType.MODEL_CHUNK,
1229
+ attributes: {
1230
+ chunkType: "tool-result",
1231
+ sequenceNumber: this.#chunkSequence - 1
1232
+ }
1233
+ })
1234
+ };
1235
+ this.#toolOutputAccumulators.set(toolCallId, acc);
1236
+ }
1237
+ if (output && typeof output === "object" && "type" in output) {
1238
+ const innerType = output.type;
1239
+ switch (innerType) {
1240
+ case "text-delta":
1241
+ if (output.payload?.text) {
1242
+ acc.text += output.payload.text;
1243
+ }
1244
+ break;
1245
+ case "reasoning-delta":
1246
+ if (output.payload?.text) {
1247
+ acc.reasoning += output.payload.text;
1248
+ }
1249
+ break;
1250
+ case "finish":
1251
+ case "workflow-finish":
1252
+ this.#endToolOutputSpan(toolCallId);
1253
+ break;
1254
+ }
1255
+ }
1256
+ }
1257
+ /**
1258
+ * End a tool output span and clean up the accumulator
1259
+ */
1260
+ #endToolOutputSpan(toolCallId) {
1261
+ const acc = this.#toolOutputAccumulators.get(toolCallId);
1262
+ if (!acc) return;
1263
+ const output = {
1264
+ toolCallId: acc.toolCallId,
1265
+ toolName: acc.toolName
1266
+ };
1267
+ if (acc.text) {
1268
+ output.text = acc.text;
1269
+ }
1270
+ if (acc.reasoning) {
1271
+ output.reasoning = acc.reasoning;
1272
+ }
1273
+ acc.span?.end({ output });
1274
+ this.#toolOutputAccumulators.delete(toolCallId);
1275
+ this.#streamedToolCallIds.add(toolCallId);
1276
+ }
1185
1277
  /**
1186
1278
  * Wraps a stream with model tracing transform to track MODEL_STEP and MODEL_CHUNK spans.
1187
1279
  *
@@ -1189,9 +1281,14 @@ var ModelSpanTracker = class {
1189
1281
  * create MODEL_STEP and MODEL_CHUNK spans for each semantic unit in the stream.
1190
1282
  */
1191
1283
  wrapStream(stream) {
1284
+ let captureCompletionStartTime = false;
1192
1285
  return stream.pipeThrough(
1193
1286
  new web.TransformStream({
1194
1287
  transform: (chunk, controller) => {
1288
+ if (!captureCompletionStartTime) {
1289
+ captureCompletionStartTime = true;
1290
+ this.#captureCompletionStartTime();
1291
+ }
1195
1292
  controller.enqueue(chunk);
1196
1293
  switch (chunk.type) {
1197
1294
  case "text-start":
@@ -1225,6 +1322,19 @@ var ModelSpanTracker = class {
1225
1322
  case "start":
1226
1323
  case "finish":
1227
1324
  break;
1325
+ case "tool-output":
1326
+ this.#handleToolOutputChunk(chunk);
1327
+ break;
1328
+ case "tool-result": {
1329
+ const toolCallId = chunk.payload?.toolCallId;
1330
+ if (toolCallId && this.#streamedToolCallIds.has(toolCallId)) {
1331
+ this.#streamedToolCallIds.delete(toolCallId);
1332
+ break;
1333
+ }
1334
+ const { args, ...cleanPayload } = chunk.payload || {};
1335
+ this.#createEventSpan(chunk.type, cleanPayload);
1336
+ break;
1337
+ }
1228
1338
  // Default: auto-create event span for all other chunk types
1229
1339
  default: {
1230
1340
  let outputPayload = chunk.payload;
@@ -2324,6 +2434,11 @@ var Observability = class extends base.MastraBase {
2324
2434
  }
2325
2435
  };
2326
2436
 
2437
+ // src/tracing-options.ts
2438
+ function buildTracingOptions(...updaters) {
2439
+ return updaters.reduce((opts, updater) => updater(opts), {});
2440
+ }
2441
+
2327
2442
  exports.BaseExporter = BaseExporter;
2328
2443
  exports.BaseObservabilityInstance = BaseObservabilityInstance;
2329
2444
  exports.BaseSpan = BaseSpan;
@@ -2338,6 +2453,7 @@ exports.Observability = Observability;
2338
2453
  exports.SamplingStrategyType = SamplingStrategyType;
2339
2454
  exports.SensitiveDataFilter = SensitiveDataFilter;
2340
2455
  exports.TestExporter = TestExporter;
2456
+ exports.buildTracingOptions = buildTracingOptions;
2341
2457
  exports.deepClean = deepClean;
2342
2458
  exports.getExternalParentId = getExternalParentId;
2343
2459
  exports.observabilityConfigValueSchema = observabilityConfigValueSchema;