@mastra/otel-exporter 0.0.0-fix-issue-10434-concurrent-write-corruption-20251124213939 → 0.0.0-fix-backport-setserver-20251201151948

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/dist/index.js CHANGED
@@ -1,11 +1,10 @@
1
- import { TracingEventType, SpanType } from '@mastra/core/observability';
2
- import { BaseExporter } from '@mastra/observability';
1
+ import { BaseExporter, AITracingEventType, AISpanType } from '@mastra/core/ai-tracing';
3
2
  import { diag, DiagConsoleLogger, DiagLogLevel, SpanKind, SpanStatusCode, TraceFlags } from '@opentelemetry/api';
4
3
  import { resourceFromAttributes } from '@opentelemetry/resources';
5
4
  import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
6
5
  import { ATTR_TELEMETRY_SDK_LANGUAGE, ATTR_TELEMETRY_SDK_VERSION, ATTR_TELEMETRY_SDK_NAME, ATTR_SERVICE_VERSION, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
7
6
 
8
- // src/tracing.ts
7
+ // src/ai-tracing.ts
9
8
 
10
9
  // src/loadExporter.ts
11
10
  var OTLPHttpExporter;
@@ -223,45 +222,45 @@ var MastraReadableSpan = class {
223
222
  droppedAttributesCount = 0;
224
223
  droppedEventsCount = 0;
225
224
  droppedLinksCount = 0;
226
- constructor(span, attributes, kind, parentSpanId, resource, instrumentationLibrary) {
227
- this.name = span.name;
225
+ constructor(aiSpan, attributes, kind, parentSpanId, resource, instrumentationLibrary) {
226
+ this.name = aiSpan.name;
228
227
  this.kind = kind;
229
228
  this.attributes = attributes;
230
229
  this.parentSpanId = parentSpanId;
231
230
  this.links = [];
232
231
  this.events = [];
233
- this.startTime = this.dateToHrTime(span.startTime);
234
- this.endTime = span.endTime ? this.dateToHrTime(span.endTime) : this.startTime;
235
- this.ended = !!span.endTime;
236
- if (span.endTime) {
237
- const durationMs = span.endTime.getTime() - span.startTime.getTime();
232
+ this.startTime = this.dateToHrTime(aiSpan.startTime);
233
+ this.endTime = aiSpan.endTime ? this.dateToHrTime(aiSpan.endTime) : this.startTime;
234
+ this.ended = !!aiSpan.endTime;
235
+ if (aiSpan.endTime) {
236
+ const durationMs = aiSpan.endTime.getTime() - aiSpan.startTime.getTime();
238
237
  this.duration = [Math.floor(durationMs / 1e3), durationMs % 1e3 * 1e6];
239
238
  } else {
240
239
  this.duration = [0, 0];
241
240
  }
242
- if (span.errorInfo) {
241
+ if (aiSpan.errorInfo) {
243
242
  this.status = {
244
243
  code: SpanStatusCode.ERROR,
245
- message: span.errorInfo.message
244
+ message: aiSpan.errorInfo.message
246
245
  };
247
246
  this.events.push({
248
247
  name: "exception",
249
248
  attributes: {
250
- "exception.message": span.errorInfo.message,
249
+ "exception.message": aiSpan.errorInfo.message,
251
250
  "exception.type": "Error",
252
- ...span.errorInfo.details?.stack && {
253
- "exception.stacktrace": span.errorInfo.details.stack
251
+ ...aiSpan.errorInfo.details?.stack && {
252
+ "exception.stacktrace": aiSpan.errorInfo.details.stack
254
253
  }
255
254
  },
256
255
  time: this.startTime,
257
256
  droppedAttributesCount: 0
258
257
  });
259
- } else if (span.endTime) {
258
+ } else if (aiSpan.endTime) {
260
259
  this.status = { code: SpanStatusCode.OK };
261
260
  } else {
262
261
  this.status = { code: SpanStatusCode.UNSET };
263
262
  }
264
- if (span.isEvent) {
263
+ if (aiSpan.isEvent) {
265
264
  this.events.push({
266
265
  name: "instant_event",
267
266
  attributes: {},
@@ -270,14 +269,14 @@ var MastraReadableSpan = class {
270
269
  });
271
270
  }
272
271
  this.spanContext = () => ({
273
- traceId: span.traceId,
274
- spanId: span.id,
272
+ traceId: aiSpan.traceId,
273
+ spanId: aiSpan.id,
275
274
  traceFlags: TraceFlags.SAMPLED,
276
275
  isRemote: false
277
276
  });
278
277
  if (parentSpanId) {
279
278
  this.parentSpanContext = {
280
- traceId: span.traceId,
279
+ traceId: aiSpan.traceId,
281
280
  spanId: parentSpanId,
282
281
  traceFlags: TraceFlags.SAMPLED,
283
282
  isRemote: false
@@ -304,13 +303,13 @@ var MastraReadableSpan = class {
304
303
  // src/span-converter.ts
305
304
  var SPAN_KIND_MAPPING = {
306
305
  // Model operations are CLIENT spans (calling external AI services)
307
- [SpanType.MODEL_GENERATION]: SpanKind.CLIENT,
308
- [SpanType.MODEL_CHUNK]: SpanKind.CLIENT,
306
+ [AISpanType.MODEL_GENERATION]: SpanKind.CLIENT,
307
+ [AISpanType.MODEL_CHUNK]: SpanKind.CLIENT,
309
308
  // MCP tool calls are CLIENT (external service calls)
310
- [SpanType.MCP_TOOL_CALL]: SpanKind.CLIENT,
309
+ [AISpanType.MCP_TOOL_CALL]: SpanKind.CLIENT,
311
310
  // Root spans for agent/workflow are SERVER (entry points)
312
- [SpanType.AGENT_RUN]: SpanKind.SERVER,
313
- [SpanType.WORKFLOW_RUN]: SpanKind.SERVER
311
+ [AISpanType.AGENT_RUN]: SpanKind.SERVER,
312
+ [AISpanType.WORKFLOW_RUN]: SpanKind.SERVER
314
313
  };
315
314
  var SpanConverter = class {
316
315
  resource;
@@ -323,19 +322,19 @@ var SpanConverter = class {
323
322
  };
324
323
  }
325
324
  /**
326
- * Convert a Mastra Span to an OpenTelemetry ReadableSpan
325
+ * Convert a Mastra AI span to an OpenTelemetry ReadableSpan
327
326
  * This preserves Mastra's trace and span IDs
328
327
  */
329
- convertSpan(Span) {
330
- const spanKind = this.getSpanKind(Span);
331
- const attributes = this.buildAttributes(Span);
332
- const spanName = this.buildSpanName(Span);
333
- const otelSpan = { ...Span, name: spanName };
328
+ convertSpan(aiSpan) {
329
+ const spanKind = this.getSpanKind(aiSpan);
330
+ const attributes = this.buildAttributes(aiSpan);
331
+ const spanName = this.buildSpanName(aiSpan);
332
+ const otelSpan = { ...aiSpan, name: spanName };
334
333
  return new MastraReadableSpan(
335
334
  otelSpan,
336
335
  attributes,
337
336
  spanKind,
338
- Span.parentSpanId,
337
+ aiSpan.parentSpanId,
339
338
  // Use the parentSpanId from the Mastra span directly
340
339
  this.resource,
341
340
  this.instrumentationLibrary
@@ -344,81 +343,81 @@ var SpanConverter = class {
344
343
  /**
345
344
  * Get the appropriate SpanKind based on span type and context
346
345
  */
347
- getSpanKind(Span) {
348
- if (Span.isRootSpan) {
349
- if (Span.type === SpanType.AGENT_RUN || Span.type === SpanType.WORKFLOW_RUN) {
346
+ getSpanKind(aiSpan) {
347
+ if (aiSpan.isRootSpan) {
348
+ if (aiSpan.type === AISpanType.AGENT_RUN || aiSpan.type === AISpanType.WORKFLOW_RUN) {
350
349
  return SpanKind.SERVER;
351
350
  }
352
351
  }
353
- return SPAN_KIND_MAPPING[Span.type] || SpanKind.INTERNAL;
352
+ return SPAN_KIND_MAPPING[aiSpan.type] || SpanKind.INTERNAL;
354
353
  }
355
354
  /**
356
355
  * Build OTEL-compliant span name based on span type and attributes
357
356
  */
358
- buildSpanName(Span) {
359
- switch (Span.type) {
360
- case SpanType.MODEL_GENERATION: {
361
- const attrs = Span.attributes;
357
+ buildSpanName(aiSpan) {
358
+ switch (aiSpan.type) {
359
+ case AISpanType.MODEL_GENERATION: {
360
+ const attrs = aiSpan.attributes;
362
361
  const operation = attrs?.resultType === "tool_selection" ? "tool_selection" : "chat";
363
362
  const model = attrs?.model || "unknown";
364
363
  return `${operation} ${model}`;
365
364
  }
366
- case SpanType.TOOL_CALL:
367
- case SpanType.MCP_TOOL_CALL: {
368
- const toolAttrs = Span.attributes;
365
+ case AISpanType.TOOL_CALL:
366
+ case AISpanType.MCP_TOOL_CALL: {
367
+ const toolAttrs = aiSpan.attributes;
369
368
  const toolName = toolAttrs?.toolId || "unknown";
370
369
  return `tool.execute ${toolName}`;
371
370
  }
372
- case SpanType.AGENT_RUN: {
373
- const agentAttrs = Span.attributes;
371
+ case AISpanType.AGENT_RUN: {
372
+ const agentAttrs = aiSpan.attributes;
374
373
  const agentId = agentAttrs?.agentId || "unknown";
375
374
  return `agent.${agentId}`;
376
375
  }
377
- case SpanType.WORKFLOW_RUN: {
378
- const workflowAttrs = Span.attributes;
376
+ case AISpanType.WORKFLOW_RUN: {
377
+ const workflowAttrs = aiSpan.attributes;
379
378
  const workflowId = workflowAttrs?.workflowId || "unknown";
380
379
  return `workflow.${workflowId}`;
381
380
  }
382
- case SpanType.WORKFLOW_STEP:
383
- return Span.name;
381
+ case AISpanType.WORKFLOW_STEP:
382
+ return aiSpan.name;
384
383
  default:
385
- return Span.name;
384
+ return aiSpan.name;
386
385
  }
387
386
  }
388
387
  /**
389
- * Build OpenTelemetry attributes from Mastra Span
388
+ * Build OpenTelemetry attributes from Mastra AI span
390
389
  * Following OTEL Semantic Conventions for GenAI
391
390
  */
392
- buildAttributes(Span) {
391
+ buildAttributes(aiSpan) {
393
392
  const attributes = {};
394
- attributes["gen_ai.operation.name"] = this.getOperationName(Span);
395
- attributes["span.kind"] = this.getSpanKindString(Span);
396
- attributes["mastra.span.type"] = Span.type;
397
- attributes["mastra.trace_id"] = Span.traceId;
398
- attributes["mastra.span_id"] = Span.id;
399
- if (Span.parentSpanId) {
400
- attributes["mastra.parent_span_id"] = Span.parentSpanId;
393
+ attributes["gen_ai.operation.name"] = this.getOperationName(aiSpan);
394
+ attributes["span.kind"] = this.getSpanKindString(aiSpan);
395
+ attributes["mastra.span.type"] = aiSpan.type;
396
+ attributes["mastra.trace_id"] = aiSpan.traceId;
397
+ attributes["mastra.span_id"] = aiSpan.id;
398
+ if (aiSpan.parentSpanId) {
399
+ attributes["mastra.parent_span_id"] = aiSpan.parentSpanId;
401
400
  }
402
- if (Span.input !== void 0) {
403
- const inputStr = typeof Span.input === "string" ? Span.input : JSON.stringify(Span.input);
401
+ if (aiSpan.input !== void 0) {
402
+ const inputStr = typeof aiSpan.input === "string" ? aiSpan.input : JSON.stringify(aiSpan.input);
404
403
  attributes["input"] = inputStr;
405
- if (Span.type === SpanType.MODEL_GENERATION) {
404
+ if (aiSpan.type === AISpanType.MODEL_GENERATION) {
406
405
  attributes["gen_ai.prompt"] = inputStr;
407
- } else if (Span.type === SpanType.TOOL_CALL || Span.type === SpanType.MCP_TOOL_CALL) {
406
+ } else if (aiSpan.type === AISpanType.TOOL_CALL || aiSpan.type === AISpanType.MCP_TOOL_CALL) {
408
407
  attributes["gen_ai.tool.input"] = inputStr;
409
408
  }
410
409
  }
411
- if (Span.output !== void 0) {
412
- const outputStr = typeof Span.output === "string" ? Span.output : JSON.stringify(Span.output);
410
+ if (aiSpan.output !== void 0) {
411
+ const outputStr = typeof aiSpan.output === "string" ? aiSpan.output : JSON.stringify(aiSpan.output);
413
412
  attributes["output"] = outputStr;
414
- if (Span.type === SpanType.MODEL_GENERATION) {
413
+ if (aiSpan.type === AISpanType.MODEL_GENERATION) {
415
414
  attributes["gen_ai.completion"] = outputStr;
416
- } else if (Span.type === SpanType.TOOL_CALL || Span.type === SpanType.MCP_TOOL_CALL) {
415
+ } else if (aiSpan.type === AISpanType.TOOL_CALL || aiSpan.type === AISpanType.MCP_TOOL_CALL) {
417
416
  attributes["gen_ai.tool.output"] = outputStr;
418
417
  }
419
418
  }
420
- if (Span.type === SpanType.MODEL_GENERATION && Span.attributes) {
421
- const modelAttrs = Span.attributes;
419
+ if (aiSpan.type === AISpanType.MODEL_GENERATION && aiSpan.attributes) {
420
+ const modelAttrs = aiSpan.attributes;
422
421
  if (modelAttrs.model) {
423
422
  attributes["gen_ai.request.model"] = modelAttrs.model;
424
423
  }
@@ -471,12 +470,12 @@ var SpanConverter = class {
471
470
  attributes["gen_ai.response.finish_reasons"] = modelAttrs.finishReason;
472
471
  }
473
472
  }
474
- if ((Span.type === SpanType.TOOL_CALL || Span.type === SpanType.MCP_TOOL_CALL) && Span.attributes) {
475
- const toolAttrs = Span.attributes;
473
+ if ((aiSpan.type === AISpanType.TOOL_CALL || aiSpan.type === AISpanType.MCP_TOOL_CALL) && aiSpan.attributes) {
474
+ const toolAttrs = aiSpan.attributes;
476
475
  if (toolAttrs.toolId) {
477
476
  attributes["gen_ai.tool.name"] = toolAttrs.toolId;
478
477
  }
479
- if (Span.type === SpanType.MCP_TOOL_CALL) {
478
+ if (aiSpan.type === AISpanType.MCP_TOOL_CALL) {
480
479
  const mcpAttrs = toolAttrs;
481
480
  if (mcpAttrs.mcpServer) {
482
481
  attributes["mcp.server"] = mcpAttrs.mcpServer;
@@ -493,8 +492,8 @@ var SpanConverter = class {
493
492
  attributes["gen_ai.tool.success"] = toolAttrs.success;
494
493
  }
495
494
  }
496
- if (Span.type === SpanType.AGENT_RUN && Span.attributes) {
497
- const agentAttrs = Span.attributes;
495
+ if (aiSpan.type === AISpanType.AGENT_RUN && aiSpan.attributes) {
496
+ const agentAttrs = aiSpan.attributes;
498
497
  if (agentAttrs.agentId) {
499
498
  attributes["agent.id"] = agentAttrs.agentId;
500
499
  attributes["gen_ai.agent.id"] = agentAttrs.agentId;
@@ -506,8 +505,8 @@ var SpanConverter = class {
506
505
  attributes["agent.available_tools"] = JSON.stringify(agentAttrs.availableTools);
507
506
  }
508
507
  }
509
- if (Span.type === SpanType.WORKFLOW_RUN && Span.attributes) {
510
- const workflowAttrs = Span.attributes;
508
+ if (aiSpan.type === AISpanType.WORKFLOW_RUN && aiSpan.attributes) {
509
+ const workflowAttrs = aiSpan.attributes;
511
510
  if (workflowAttrs.workflowId) {
512
511
  attributes["workflow.id"] = workflowAttrs.workflowId;
513
512
  }
@@ -515,19 +514,19 @@ var SpanConverter = class {
515
514
  attributes["workflow.status"] = workflowAttrs.status;
516
515
  }
517
516
  }
518
- if (Span.errorInfo) {
517
+ if (aiSpan.errorInfo) {
519
518
  attributes["error"] = true;
520
- attributes["error.type"] = Span.errorInfo.id || "unknown";
521
- attributes["error.message"] = Span.errorInfo.message;
522
- if (Span.errorInfo.domain) {
523
- attributes["error.domain"] = Span.errorInfo.domain;
519
+ attributes["error.type"] = aiSpan.errorInfo.id || "unknown";
520
+ attributes["error.message"] = aiSpan.errorInfo.message;
521
+ if (aiSpan.errorInfo.domain) {
522
+ attributes["error.domain"] = aiSpan.errorInfo.domain;
524
523
  }
525
- if (Span.errorInfo.category) {
526
- attributes["error.category"] = Span.errorInfo.category;
524
+ if (aiSpan.errorInfo.category) {
525
+ attributes["error.category"] = aiSpan.errorInfo.category;
527
526
  }
528
527
  }
529
- if (Span.metadata) {
530
- Object.entries(Span.metadata).forEach(([key, value]) => {
528
+ if (aiSpan.metadata) {
529
+ Object.entries(aiSpan.metadata).forEach(([key, value]) => {
531
530
  if (!attributes[key]) {
532
531
  if (value === null || value === void 0) {
533
532
  return;
@@ -540,12 +539,12 @@ var SpanConverter = class {
540
539
  }
541
540
  });
542
541
  }
543
- if (Span.startTime) {
544
- attributes["mastra.start_time"] = Span.startTime.toISOString();
542
+ if (aiSpan.startTime) {
543
+ attributes["mastra.start_time"] = aiSpan.startTime.toISOString();
545
544
  }
546
- if (Span.endTime) {
547
- attributes["mastra.end_time"] = Span.endTime.toISOString();
548
- const duration = Span.endTime.getTime() - Span.startTime.getTime();
545
+ if (aiSpan.endTime) {
546
+ attributes["mastra.end_time"] = aiSpan.endTime.toISOString();
547
+ const duration = aiSpan.endTime.getTime() - aiSpan.startTime.getTime();
549
548
  attributes["mastra.duration_ms"] = duration;
550
549
  }
551
550
  return attributes;
@@ -553,28 +552,28 @@ var SpanConverter = class {
553
552
  /**
554
553
  * Get the operation name based on span type for gen_ai.operation.name
555
554
  */
556
- getOperationName(Span) {
557
- switch (Span.type) {
558
- case SpanType.MODEL_GENERATION: {
559
- const attrs = Span.attributes;
555
+ getOperationName(aiSpan) {
556
+ switch (aiSpan.type) {
557
+ case AISpanType.MODEL_GENERATION: {
558
+ const attrs = aiSpan.attributes;
560
559
  return attrs?.resultType === "tool_selection" ? "tool_selection" : "chat";
561
560
  }
562
- case SpanType.TOOL_CALL:
563
- case SpanType.MCP_TOOL_CALL:
561
+ case AISpanType.TOOL_CALL:
562
+ case AISpanType.MCP_TOOL_CALL:
564
563
  return "tool.execute";
565
- case SpanType.AGENT_RUN:
564
+ case AISpanType.AGENT_RUN:
566
565
  return "agent.run";
567
- case SpanType.WORKFLOW_RUN:
566
+ case AISpanType.WORKFLOW_RUN:
568
567
  return "workflow.run";
569
568
  default:
570
- return Span.type.replace(/_/g, ".");
569
+ return aiSpan.type.replace(/_/g, ".");
571
570
  }
572
571
  }
573
572
  /**
574
573
  * Get span kind as string for attribute
575
574
  */
576
- getSpanKindString(Span) {
577
- const kind = this.getSpanKind(Span);
575
+ getSpanKindString(aiSpan) {
576
+ const kind = this.getSpanKind(aiSpan);
578
577
  switch (kind) {
579
578
  case SpanKind.SERVER:
580
579
  return "server";
@@ -592,7 +591,7 @@ var SpanConverter = class {
592
591
  }
593
592
  };
594
593
 
595
- // src/tracing.ts
594
+ // src/ai-tracing.ts
596
595
  var OtelExporter = class extends BaseExporter {
597
596
  config;
598
597
  tracingConfig;
@@ -612,8 +611,8 @@ var OtelExporter = class extends BaseExporter {
612
611
  /**
613
612
  * Initialize with tracing configuration
614
613
  */
615
- init(options) {
616
- this.tracingConfig = options.config;
614
+ init(config) {
615
+ this.tracingConfig = config;
617
616
  }
618
617
  async setupExporter() {
619
618
  if (this.isSetup || this.exporter) return;
@@ -726,8 +725,8 @@ var OtelExporter = class extends BaseExporter {
726
725
  await this.setupProcessor();
727
726
  this.isSetup = true;
728
727
  }
729
- async _exportTracingEvent(event) {
730
- if (event.type !== TracingEventType.SPAN_ENDED) {
728
+ async _exportEvent(event) {
729
+ if (event.type !== AITracingEventType.SPAN_ENDED) {
731
730
  return;
732
731
  }
733
732
  const span = event.exportedSpan;