@mastra/otel-exporter 0.0.0-netlify-no-bundle-20251127120354 → 0.0.0-partial-response-backport-20251204204441

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 { SpanType, TracingEventType } from '@mastra/core/observability';
2
- import { BaseExporter } from '@mastra/observability';
3
- import { SpanStatusCode, TraceFlags, SpanKind, diag, DiagConsoleLogger, DiagLogLevel } from '@opentelemetry/api';
1
+ import { BaseExporter, AITracingEventType, AISpanType } from '@mastra/core/ai-tracing';
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,118 +303,121 @@ 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
- function getSpanKind(type, isRootSpan) {
316
- if (isRootSpan) {
317
- if (type === SpanType.AGENT_RUN || type === SpanType.WORKFLOW_RUN) {
318
- return SpanKind.SERVER;
319
- }
320
- }
321
- return SPAN_KIND_MAPPING[type] || SpanKind.INTERNAL;
322
- }
323
314
  var SpanConverter = class {
324
315
  resource;
325
316
  instrumentationLibrary;
326
317
  constructor(resource) {
327
318
  this.resource = resource;
328
319
  this.instrumentationLibrary = {
329
- name: "@mastra/otel-exporter",
320
+ name: "@mastra/otel",
330
321
  version: "1.0.0"
331
322
  };
332
323
  }
333
324
  /**
334
- * Convert a Mastra Span to an OpenTelemetry ReadableSpan
325
+ * Convert a Mastra AI span to an OpenTelemetry ReadableSpan
335
326
  * This preserves Mastra's trace and span IDs
336
327
  */
337
- convertSpan(span) {
338
- const spanKind = getSpanKind(span.type, span.isRootSpan);
339
- const attributes = this.buildAttributes(span);
340
- const spanName = this.buildSpanName(span);
341
- 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 };
342
333
  return new MastraReadableSpan(
343
334
  otelSpan,
344
335
  attributes,
345
336
  spanKind,
346
- span.parentSpanId,
337
+ aiSpan.parentSpanId,
347
338
  // Use the parentSpanId from the Mastra span directly
348
339
  this.resource,
349
340
  this.instrumentationLibrary
350
341
  );
351
342
  }
343
+ /**
344
+ * Get the appropriate SpanKind based on span type and context
345
+ */
346
+ getSpanKind(aiSpan) {
347
+ if (aiSpan.isRootSpan) {
348
+ if (aiSpan.type === AISpanType.AGENT_RUN || aiSpan.type === AISpanType.WORKFLOW_RUN) {
349
+ return SpanKind.SERVER;
350
+ }
351
+ }
352
+ return SPAN_KIND_MAPPING[aiSpan.type] || SpanKind.INTERNAL;
353
+ }
352
354
  /**
353
355
  * Build OTEL-compliant span name based on span type and attributes
354
356
  */
355
- buildSpanName(Span) {
356
- switch (Span.type) {
357
- case SpanType.MODEL_GENERATION: {
358
- const attrs = Span.attributes;
357
+ buildSpanName(aiSpan) {
358
+ switch (aiSpan.type) {
359
+ case AISpanType.MODEL_GENERATION: {
360
+ const attrs = aiSpan.attributes;
359
361
  const operation = attrs?.resultType === "tool_selection" ? "tool_selection" : "chat";
360
362
  const model = attrs?.model || "unknown";
361
363
  return `${operation} ${model}`;
362
364
  }
363
- case SpanType.TOOL_CALL:
364
- case SpanType.MCP_TOOL_CALL: {
365
- const toolAttrs = Span.attributes;
365
+ case AISpanType.TOOL_CALL:
366
+ case AISpanType.MCP_TOOL_CALL: {
367
+ const toolAttrs = aiSpan.attributes;
366
368
  const toolName = toolAttrs?.toolId || "unknown";
367
369
  return `tool.execute ${toolName}`;
368
370
  }
369
- case SpanType.AGENT_RUN: {
370
- const agentAttrs = Span.attributes;
371
+ case AISpanType.AGENT_RUN: {
372
+ const agentAttrs = aiSpan.attributes;
371
373
  const agentId = agentAttrs?.agentId || "unknown";
372
374
  return `agent.${agentId}`;
373
375
  }
374
- case SpanType.WORKFLOW_RUN: {
375
- const workflowAttrs = Span.attributes;
376
+ case AISpanType.WORKFLOW_RUN: {
377
+ const workflowAttrs = aiSpan.attributes;
376
378
  const workflowId = workflowAttrs?.workflowId || "unknown";
377
379
  return `workflow.${workflowId}`;
378
380
  }
379
- case SpanType.WORKFLOW_STEP:
380
- return Span.name;
381
+ case AISpanType.WORKFLOW_STEP:
382
+ return aiSpan.name;
381
383
  default:
382
- return Span.name;
384
+ return aiSpan.name;
383
385
  }
384
386
  }
385
387
  /**
386
- * Build OpenTelemetry attributes from Mastra Span
388
+ * Build OpenTelemetry attributes from Mastra AI span
387
389
  * Following OTEL Semantic Conventions for GenAI
388
390
  */
389
- buildAttributes(Span) {
391
+ buildAttributes(aiSpan) {
390
392
  const attributes = {};
391
- attributes["gen_ai.operation.name"] = this.getOperationName(Span);
392
- attributes["span.kind"] = this.getSpanKindString(Span);
393
- attributes["mastra.span.type"] = Span.type;
394
- attributes["mastra.trace_id"] = Span.traceId;
395
- attributes["mastra.span_id"] = Span.id;
396
- if (Span.parentSpanId) {
397
- 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;
398
400
  }
399
- if (Span.input !== void 0) {
400
- 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);
401
403
  attributes["input"] = inputStr;
402
- if (Span.type === SpanType.MODEL_GENERATION) {
404
+ if (aiSpan.type === AISpanType.MODEL_GENERATION) {
403
405
  attributes["gen_ai.prompt"] = inputStr;
404
- } 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) {
405
407
  attributes["gen_ai.tool.input"] = inputStr;
406
408
  }
407
409
  }
408
- if (Span.output !== void 0) {
409
- 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);
410
412
  attributes["output"] = outputStr;
411
- if (Span.type === SpanType.MODEL_GENERATION) {
413
+ if (aiSpan.type === AISpanType.MODEL_GENERATION) {
412
414
  attributes["gen_ai.completion"] = outputStr;
413
- } 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) {
414
416
  attributes["gen_ai.tool.output"] = outputStr;
415
417
  }
416
418
  }
417
- if (Span.type === SpanType.MODEL_GENERATION && Span.attributes) {
418
- const modelAttrs = Span.attributes;
419
+ if (aiSpan.type === AISpanType.MODEL_GENERATION && aiSpan.attributes) {
420
+ const modelAttrs = aiSpan.attributes;
419
421
  if (modelAttrs.model) {
420
422
  attributes["gen_ai.request.model"] = modelAttrs.model;
421
423
  }
@@ -468,12 +470,12 @@ var SpanConverter = class {
468
470
  attributes["gen_ai.response.finish_reasons"] = modelAttrs.finishReason;
469
471
  }
470
472
  }
471
- if ((Span.type === SpanType.TOOL_CALL || Span.type === SpanType.MCP_TOOL_CALL) && Span.attributes) {
472
- const toolAttrs = Span.attributes;
473
+ if ((aiSpan.type === AISpanType.TOOL_CALL || aiSpan.type === AISpanType.MCP_TOOL_CALL) && aiSpan.attributes) {
474
+ const toolAttrs = aiSpan.attributes;
473
475
  if (toolAttrs.toolId) {
474
476
  attributes["gen_ai.tool.name"] = toolAttrs.toolId;
475
477
  }
476
- if (Span.type === SpanType.MCP_TOOL_CALL) {
478
+ if (aiSpan.type === AISpanType.MCP_TOOL_CALL) {
477
479
  const mcpAttrs = toolAttrs;
478
480
  if (mcpAttrs.mcpServer) {
479
481
  attributes["mcp.server"] = mcpAttrs.mcpServer;
@@ -490,8 +492,8 @@ var SpanConverter = class {
490
492
  attributes["gen_ai.tool.success"] = toolAttrs.success;
491
493
  }
492
494
  }
493
- if (Span.type === SpanType.AGENT_RUN && Span.attributes) {
494
- const agentAttrs = Span.attributes;
495
+ if (aiSpan.type === AISpanType.AGENT_RUN && aiSpan.attributes) {
496
+ const agentAttrs = aiSpan.attributes;
495
497
  if (agentAttrs.agentId) {
496
498
  attributes["agent.id"] = agentAttrs.agentId;
497
499
  attributes["gen_ai.agent.id"] = agentAttrs.agentId;
@@ -503,8 +505,8 @@ var SpanConverter = class {
503
505
  attributes["agent.available_tools"] = JSON.stringify(agentAttrs.availableTools);
504
506
  }
505
507
  }
506
- if (Span.type === SpanType.WORKFLOW_RUN && Span.attributes) {
507
- const workflowAttrs = Span.attributes;
508
+ if (aiSpan.type === AISpanType.WORKFLOW_RUN && aiSpan.attributes) {
509
+ const workflowAttrs = aiSpan.attributes;
508
510
  if (workflowAttrs.workflowId) {
509
511
  attributes["workflow.id"] = workflowAttrs.workflowId;
510
512
  }
@@ -512,19 +514,19 @@ var SpanConverter = class {
512
514
  attributes["workflow.status"] = workflowAttrs.status;
513
515
  }
514
516
  }
515
- if (Span.errorInfo) {
517
+ if (aiSpan.errorInfo) {
516
518
  attributes["error"] = true;
517
- attributes["error.type"] = Span.errorInfo.id || "unknown";
518
- attributes["error.message"] = Span.errorInfo.message;
519
- if (Span.errorInfo.domain) {
520
- 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;
521
523
  }
522
- if (Span.errorInfo.category) {
523
- attributes["error.category"] = Span.errorInfo.category;
524
+ if (aiSpan.errorInfo.category) {
525
+ attributes["error.category"] = aiSpan.errorInfo.category;
524
526
  }
525
527
  }
526
- if (Span.metadata) {
527
- Object.entries(Span.metadata).forEach(([key, value]) => {
528
+ if (aiSpan.metadata) {
529
+ Object.entries(aiSpan.metadata).forEach(([key, value]) => {
528
530
  if (!attributes[key]) {
529
531
  if (value === null || value === void 0) {
530
532
  return;
@@ -537,12 +539,12 @@ var SpanConverter = class {
537
539
  }
538
540
  });
539
541
  }
540
- if (Span.startTime) {
541
- attributes["mastra.start_time"] = Span.startTime.toISOString();
542
+ if (aiSpan.startTime) {
543
+ attributes["mastra.start_time"] = aiSpan.startTime.toISOString();
542
544
  }
543
- if (Span.endTime) {
544
- attributes["mastra.end_time"] = Span.endTime.toISOString();
545
- 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();
546
548
  attributes["mastra.duration_ms"] = duration;
547
549
  }
548
550
  return attributes;
@@ -550,28 +552,28 @@ var SpanConverter = class {
550
552
  /**
551
553
  * Get the operation name based on span type for gen_ai.operation.name
552
554
  */
553
- getOperationName(Span) {
554
- switch (Span.type) {
555
- case SpanType.MODEL_GENERATION: {
556
- const attrs = Span.attributes;
555
+ getOperationName(aiSpan) {
556
+ switch (aiSpan.type) {
557
+ case AISpanType.MODEL_GENERATION: {
558
+ const attrs = aiSpan.attributes;
557
559
  return attrs?.resultType === "tool_selection" ? "tool_selection" : "chat";
558
560
  }
559
- case SpanType.TOOL_CALL:
560
- case SpanType.MCP_TOOL_CALL:
561
+ case AISpanType.TOOL_CALL:
562
+ case AISpanType.MCP_TOOL_CALL:
561
563
  return "tool.execute";
562
- case SpanType.AGENT_RUN:
564
+ case AISpanType.AGENT_RUN:
563
565
  return "agent.run";
564
- case SpanType.WORKFLOW_RUN:
566
+ case AISpanType.WORKFLOW_RUN:
565
567
  return "workflow.run";
566
568
  default:
567
- return Span.type.replace(/_/g, ".");
569
+ return aiSpan.type.replace(/_/g, ".");
568
570
  }
569
571
  }
570
572
  /**
571
573
  * Get span kind as string for attribute
572
574
  */
573
- getSpanKindString(Span) {
574
- const kind = getSpanKind(Span.type, Span.isRootSpan);
575
+ getSpanKindString(aiSpan) {
576
+ const kind = this.getSpanKind(aiSpan);
575
577
  switch (kind) {
576
578
  case SpanKind.SERVER:
577
579
  return "server";
@@ -589,7 +591,7 @@ var SpanConverter = class {
589
591
  }
590
592
  };
591
593
 
592
- // src/tracing.ts
594
+ // src/ai-tracing.ts
593
595
  var OtelExporter = class extends BaseExporter {
594
596
  config;
595
597
  tracingConfig;
@@ -609,8 +611,8 @@ var OtelExporter = class extends BaseExporter {
609
611
  /**
610
612
  * Initialize with tracing configuration
611
613
  */
612
- init(options) {
613
- this.tracingConfig = options.config;
614
+ init(config) {
615
+ this.tracingConfig = config;
614
616
  }
615
617
  async setupExporter() {
616
618
  if (this.isSetup || this.exporter) return;
@@ -723,8 +725,8 @@ var OtelExporter = class extends BaseExporter {
723
725
  await this.setupProcessor();
724
726
  this.isSetup = true;
725
727
  }
726
- async _exportTracingEvent(event) {
727
- if (event.type !== TracingEventType.SPAN_ENDED) {
728
+ async _exportEvent(event) {
729
+ if (event.type !== AITracingEventType.SPAN_ENDED) {
728
730
  return;
729
731
  }
730
732
  const span = event.exportedSpan;
@@ -757,6 +759,6 @@ var OtelExporter = class extends BaseExporter {
757
759
  }
758
760
  };
759
761
 
760
- export { MastraReadableSpan, OtelExporter, SpanConverter, getSpanKind };
762
+ export { OtelExporter };
761
763
  //# sourceMappingURL=index.js.map
762
764
  //# sourceMappingURL=index.js.map