@mastra/otel-exporter 0.0.0-just-snapshot-20251014192224 → 0.0.0-main-test-20251105183450

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,11 @@
1
- import { AITracingEventType, AISpanType } from '@mastra/core/ai-tracing';
2
- import { ConsoleLogger } from '@mastra/core/logger';
1
+ import { TracingEventType, SpanType } from '@mastra/core/observability';
2
+ import { BaseExporter } from '@mastra/observability';
3
3
  import { diag, DiagConsoleLogger, DiagLogLevel, SpanKind, SpanStatusCode, TraceFlags } from '@opentelemetry/api';
4
4
  import { resourceFromAttributes } from '@opentelemetry/resources';
5
5
  import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base';
6
6
  import { ATTR_TELEMETRY_SDK_LANGUAGE, ATTR_TELEMETRY_SDK_VERSION, ATTR_TELEMETRY_SDK_NAME, ATTR_SERVICE_VERSION, ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
7
7
 
8
- // src/ai-tracing.ts
8
+ // src/tracing.ts
9
9
 
10
10
  // src/loadExporter.ts
11
11
  var OTLPHttpExporter;
@@ -207,6 +207,7 @@ var MastraReadableSpan = class {
207
207
  name;
208
208
  kind;
209
209
  spanContext;
210
+ parentSpanContext;
210
211
  parentSpanId;
211
212
  startTime;
212
213
  endTime;
@@ -222,45 +223,45 @@ var MastraReadableSpan = class {
222
223
  droppedAttributesCount = 0;
223
224
  droppedEventsCount = 0;
224
225
  droppedLinksCount = 0;
225
- constructor(aiSpan, attributes, kind, parentSpanId, resource, instrumentationLibrary) {
226
- this.name = aiSpan.name;
226
+ constructor(span, attributes, kind, parentSpanId, resource, instrumentationLibrary) {
227
+ this.name = span.name;
227
228
  this.kind = kind;
228
229
  this.attributes = attributes;
229
230
  this.parentSpanId = parentSpanId;
230
231
  this.links = [];
231
232
  this.events = [];
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();
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();
237
238
  this.duration = [Math.floor(durationMs / 1e3), durationMs % 1e3 * 1e6];
238
239
  } else {
239
240
  this.duration = [0, 0];
240
241
  }
241
- if (aiSpan.errorInfo) {
242
+ if (span.errorInfo) {
242
243
  this.status = {
243
244
  code: SpanStatusCode.ERROR,
244
- message: aiSpan.errorInfo.message
245
+ message: span.errorInfo.message
245
246
  };
246
247
  this.events.push({
247
248
  name: "exception",
248
249
  attributes: {
249
- "exception.message": aiSpan.errorInfo.message,
250
+ "exception.message": span.errorInfo.message,
250
251
  "exception.type": "Error",
251
- ...aiSpan.errorInfo.details?.stack && {
252
- "exception.stacktrace": aiSpan.errorInfo.details.stack
252
+ ...span.errorInfo.details?.stack && {
253
+ "exception.stacktrace": span.errorInfo.details.stack
253
254
  }
254
255
  },
255
256
  time: this.startTime,
256
257
  droppedAttributesCount: 0
257
258
  });
258
- } else if (aiSpan.endTime) {
259
+ } else if (span.endTime) {
259
260
  this.status = { code: SpanStatusCode.OK };
260
261
  } else {
261
262
  this.status = { code: SpanStatusCode.UNSET };
262
263
  }
263
- if (aiSpan.isEvent) {
264
+ if (span.isEvent) {
264
265
  this.events.push({
265
266
  name: "instant_event",
266
267
  attributes: {},
@@ -269,11 +270,19 @@ var MastraReadableSpan = class {
269
270
  });
270
271
  }
271
272
  this.spanContext = () => ({
272
- traceId: aiSpan.traceId,
273
- spanId: aiSpan.id,
273
+ traceId: span.traceId,
274
+ spanId: span.id,
274
275
  traceFlags: TraceFlags.SAMPLED,
275
276
  isRemote: false
276
277
  });
278
+ if (parentSpanId) {
279
+ this.parentSpanContext = {
280
+ traceId: span.traceId,
281
+ spanId: parentSpanId,
282
+ traceFlags: TraceFlags.SAMPLED,
283
+ isRemote: false
284
+ };
285
+ }
277
286
  this.resource = resource || {};
278
287
  this.instrumentationLibrary = instrumentationLibrary || {
279
288
  name: "@mastra/otel",
@@ -294,14 +303,14 @@ var MastraReadableSpan = class {
294
303
 
295
304
  // src/span-converter.ts
296
305
  var SPAN_KIND_MAPPING = {
297
- // LLM operations are CLIENT spans (calling external AI services)
298
- [AISpanType.LLM_GENERATION]: SpanKind.CLIENT,
299
- [AISpanType.LLM_CHUNK]: SpanKind.CLIENT,
306
+ // Model operations are CLIENT spans (calling external AI services)
307
+ [SpanType.MODEL_GENERATION]: SpanKind.CLIENT,
308
+ [SpanType.MODEL_CHUNK]: SpanKind.CLIENT,
300
309
  // MCP tool calls are CLIENT (external service calls)
301
- [AISpanType.MCP_TOOL_CALL]: SpanKind.CLIENT,
310
+ [SpanType.MCP_TOOL_CALL]: SpanKind.CLIENT,
302
311
  // Root spans for agent/workflow are SERVER (entry points)
303
- [AISpanType.AGENT_RUN]: SpanKind.SERVER,
304
- [AISpanType.WORKFLOW_RUN]: SpanKind.SERVER
312
+ [SpanType.AGENT_RUN]: SpanKind.SERVER,
313
+ [SpanType.WORKFLOW_RUN]: SpanKind.SERVER
305
314
  };
306
315
  var SpanConverter = class {
307
316
  resource;
@@ -314,19 +323,19 @@ var SpanConverter = class {
314
323
  };
315
324
  }
316
325
  /**
317
- * Convert a Mastra AI span to an OpenTelemetry ReadableSpan
326
+ * Convert a Mastra Span to an OpenTelemetry ReadableSpan
318
327
  * This preserves Mastra's trace and span IDs
319
328
  */
320
- convertSpan(aiSpan) {
321
- const spanKind = this.getSpanKind(aiSpan);
322
- const attributes = this.buildAttributes(aiSpan);
323
- const spanName = this.buildSpanName(aiSpan);
324
- const otelSpan = { ...aiSpan, name: spanName };
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 };
325
334
  return new MastraReadableSpan(
326
335
  otelSpan,
327
336
  attributes,
328
337
  spanKind,
329
- aiSpan.parentSpanId,
338
+ Span.parentSpanId,
330
339
  // Use the parentSpanId from the Mastra span directly
331
340
  this.resource,
332
341
  this.instrumentationLibrary
@@ -335,139 +344,139 @@ var SpanConverter = class {
335
344
  /**
336
345
  * Get the appropriate SpanKind based on span type and context
337
346
  */
338
- getSpanKind(aiSpan) {
339
- if (aiSpan.isRootSpan) {
340
- if (aiSpan.type === AISpanType.AGENT_RUN || aiSpan.type === AISpanType.WORKFLOW_RUN) {
347
+ getSpanKind(Span) {
348
+ if (Span.isRootSpan) {
349
+ if (Span.type === SpanType.AGENT_RUN || Span.type === SpanType.WORKFLOW_RUN) {
341
350
  return SpanKind.SERVER;
342
351
  }
343
352
  }
344
- return SPAN_KIND_MAPPING[aiSpan.type] || SpanKind.INTERNAL;
353
+ return SPAN_KIND_MAPPING[Span.type] || SpanKind.INTERNAL;
345
354
  }
346
355
  /**
347
356
  * Build OTEL-compliant span name based on span type and attributes
348
357
  */
349
- buildSpanName(aiSpan) {
350
- switch (aiSpan.type) {
351
- case AISpanType.LLM_GENERATION: {
352
- const attrs = aiSpan.attributes;
358
+ buildSpanName(Span) {
359
+ switch (Span.type) {
360
+ case SpanType.MODEL_GENERATION: {
361
+ const attrs = Span.attributes;
353
362
  const operation = attrs?.resultType === "tool_selection" ? "tool_selection" : "chat";
354
363
  const model = attrs?.model || "unknown";
355
364
  return `${operation} ${model}`;
356
365
  }
357
- case AISpanType.TOOL_CALL:
358
- case AISpanType.MCP_TOOL_CALL: {
359
- const toolAttrs = aiSpan.attributes;
366
+ case SpanType.TOOL_CALL:
367
+ case SpanType.MCP_TOOL_CALL: {
368
+ const toolAttrs = Span.attributes;
360
369
  const toolName = toolAttrs?.toolId || "unknown";
361
370
  return `tool.execute ${toolName}`;
362
371
  }
363
- case AISpanType.AGENT_RUN: {
364
- const agentAttrs = aiSpan.attributes;
372
+ case SpanType.AGENT_RUN: {
373
+ const agentAttrs = Span.attributes;
365
374
  const agentId = agentAttrs?.agentId || "unknown";
366
375
  return `agent.${agentId}`;
367
376
  }
368
- case AISpanType.WORKFLOW_RUN: {
369
- const workflowAttrs = aiSpan.attributes;
377
+ case SpanType.WORKFLOW_RUN: {
378
+ const workflowAttrs = Span.attributes;
370
379
  const workflowId = workflowAttrs?.workflowId || "unknown";
371
380
  return `workflow.${workflowId}`;
372
381
  }
373
- case AISpanType.WORKFLOW_STEP:
374
- return aiSpan.name;
382
+ case SpanType.WORKFLOW_STEP:
383
+ return Span.name;
375
384
  default:
376
- return aiSpan.name;
385
+ return Span.name;
377
386
  }
378
387
  }
379
388
  /**
380
- * Build OpenTelemetry attributes from Mastra AI span
389
+ * Build OpenTelemetry attributes from Mastra Span
381
390
  * Following OTEL Semantic Conventions for GenAI
382
391
  */
383
- buildAttributes(aiSpan) {
392
+ buildAttributes(Span) {
384
393
  const attributes = {};
385
- attributes["gen_ai.operation.name"] = this.getOperationName(aiSpan);
386
- attributes["span.kind"] = this.getSpanKindString(aiSpan);
387
- attributes["mastra.span.type"] = aiSpan.type;
388
- attributes["mastra.trace_id"] = aiSpan.traceId;
389
- attributes["mastra.span_id"] = aiSpan.id;
390
- if (aiSpan.parentSpanId) {
391
- attributes["mastra.parent_span_id"] = aiSpan.parentSpanId;
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;
392
401
  }
393
- if (aiSpan.input !== void 0) {
394
- const inputStr = typeof aiSpan.input === "string" ? aiSpan.input : JSON.stringify(aiSpan.input);
402
+ if (Span.input !== void 0) {
403
+ const inputStr = typeof Span.input === "string" ? Span.input : JSON.stringify(Span.input);
395
404
  attributes["input"] = inputStr;
396
- if (aiSpan.type === AISpanType.LLM_GENERATION) {
405
+ if (Span.type === SpanType.MODEL_GENERATION) {
397
406
  attributes["gen_ai.prompt"] = inputStr;
398
- } else if (aiSpan.type === AISpanType.TOOL_CALL || aiSpan.type === AISpanType.MCP_TOOL_CALL) {
407
+ } else if (Span.type === SpanType.TOOL_CALL || Span.type === SpanType.MCP_TOOL_CALL) {
399
408
  attributes["gen_ai.tool.input"] = inputStr;
400
409
  }
401
410
  }
402
- if (aiSpan.output !== void 0) {
403
- const outputStr = typeof aiSpan.output === "string" ? aiSpan.output : JSON.stringify(aiSpan.output);
411
+ if (Span.output !== void 0) {
412
+ const outputStr = typeof Span.output === "string" ? Span.output : JSON.stringify(Span.output);
404
413
  attributes["output"] = outputStr;
405
- if (aiSpan.type === AISpanType.LLM_GENERATION) {
414
+ if (Span.type === SpanType.MODEL_GENERATION) {
406
415
  attributes["gen_ai.completion"] = outputStr;
407
- } else if (aiSpan.type === AISpanType.TOOL_CALL || aiSpan.type === AISpanType.MCP_TOOL_CALL) {
416
+ } else if (Span.type === SpanType.TOOL_CALL || Span.type === SpanType.MCP_TOOL_CALL) {
408
417
  attributes["gen_ai.tool.output"] = outputStr;
409
418
  }
410
419
  }
411
- if (aiSpan.type === AISpanType.LLM_GENERATION && aiSpan.attributes) {
412
- const llmAttrs = aiSpan.attributes;
413
- if (llmAttrs.model) {
414
- attributes["gen_ai.request.model"] = llmAttrs.model;
420
+ if (Span.type === SpanType.MODEL_GENERATION && Span.attributes) {
421
+ const modelAttrs = Span.attributes;
422
+ if (modelAttrs.model) {
423
+ attributes["gen_ai.request.model"] = modelAttrs.model;
415
424
  }
416
- if (llmAttrs.provider) {
417
- attributes["gen_ai.system"] = llmAttrs.provider;
425
+ if (modelAttrs.provider) {
426
+ attributes["gen_ai.system"] = modelAttrs.provider;
418
427
  }
419
- if (llmAttrs.usage) {
420
- const inputTokens = llmAttrs.usage.inputTokens ?? llmAttrs.usage.promptTokens;
421
- const outputTokens = llmAttrs.usage.outputTokens ?? llmAttrs.usage.completionTokens;
428
+ if (modelAttrs.usage) {
429
+ const inputTokens = modelAttrs.usage.inputTokens ?? modelAttrs.usage.promptTokens;
430
+ const outputTokens = modelAttrs.usage.outputTokens ?? modelAttrs.usage.completionTokens;
422
431
  if (inputTokens !== void 0) {
423
432
  attributes["gen_ai.usage.input_tokens"] = inputTokens;
424
433
  }
425
434
  if (outputTokens !== void 0) {
426
435
  attributes["gen_ai.usage.output_tokens"] = outputTokens;
427
436
  }
428
- if (llmAttrs.usage.totalTokens !== void 0) {
429
- attributes["gen_ai.usage.total_tokens"] = llmAttrs.usage.totalTokens;
437
+ if (modelAttrs.usage.totalTokens !== void 0) {
438
+ attributes["gen_ai.usage.total_tokens"] = modelAttrs.usage.totalTokens;
430
439
  }
431
- if (llmAttrs.usage.reasoningTokens !== void 0) {
432
- attributes["gen_ai.usage.reasoning_tokens"] = llmAttrs.usage.reasoningTokens;
440
+ if (modelAttrs.usage.reasoningTokens !== void 0) {
441
+ attributes["gen_ai.usage.reasoning_tokens"] = modelAttrs.usage.reasoningTokens;
433
442
  }
434
- if (llmAttrs.usage.cachedInputTokens !== void 0) {
435
- attributes["gen_ai.usage.cached_input_tokens"] = llmAttrs.usage.cachedInputTokens;
443
+ if (modelAttrs.usage.cachedInputTokens !== void 0) {
444
+ attributes["gen_ai.usage.cached_input_tokens"] = modelAttrs.usage.cachedInputTokens;
436
445
  }
437
446
  }
438
- if (llmAttrs.parameters) {
439
- if (llmAttrs.parameters.temperature !== void 0) {
440
- attributes["gen_ai.request.temperature"] = llmAttrs.parameters.temperature;
447
+ if (modelAttrs.parameters) {
448
+ if (modelAttrs.parameters.temperature !== void 0) {
449
+ attributes["gen_ai.request.temperature"] = modelAttrs.parameters.temperature;
441
450
  }
442
- if (llmAttrs.parameters.maxOutputTokens !== void 0) {
443
- attributes["gen_ai.request.max_tokens"] = llmAttrs.parameters.maxOutputTokens;
451
+ if (modelAttrs.parameters.maxOutputTokens !== void 0) {
452
+ attributes["gen_ai.request.max_tokens"] = modelAttrs.parameters.maxOutputTokens;
444
453
  }
445
- if (llmAttrs.parameters.topP !== void 0) {
446
- attributes["gen_ai.request.top_p"] = llmAttrs.parameters.topP;
454
+ if (modelAttrs.parameters.topP !== void 0) {
455
+ attributes["gen_ai.request.top_p"] = modelAttrs.parameters.topP;
447
456
  }
448
- if (llmAttrs.parameters.topK !== void 0) {
449
- attributes["gen_ai.request.top_k"] = llmAttrs.parameters.topK;
457
+ if (modelAttrs.parameters.topK !== void 0) {
458
+ attributes["gen_ai.request.top_k"] = modelAttrs.parameters.topK;
450
459
  }
451
- if (llmAttrs.parameters.presencePenalty !== void 0) {
452
- attributes["gen_ai.request.presence_penalty"] = llmAttrs.parameters.presencePenalty;
460
+ if (modelAttrs.parameters.presencePenalty !== void 0) {
461
+ attributes["gen_ai.request.presence_penalty"] = modelAttrs.parameters.presencePenalty;
453
462
  }
454
- if (llmAttrs.parameters.frequencyPenalty !== void 0) {
455
- attributes["gen_ai.request.frequency_penalty"] = llmAttrs.parameters.frequencyPenalty;
463
+ if (modelAttrs.parameters.frequencyPenalty !== void 0) {
464
+ attributes["gen_ai.request.frequency_penalty"] = modelAttrs.parameters.frequencyPenalty;
456
465
  }
457
- if (llmAttrs.parameters.stopSequences) {
458
- attributes["gen_ai.request.stop_sequences"] = JSON.stringify(llmAttrs.parameters.stopSequences);
466
+ if (modelAttrs.parameters.stopSequences) {
467
+ attributes["gen_ai.request.stop_sequences"] = JSON.stringify(modelAttrs.parameters.stopSequences);
459
468
  }
460
469
  }
461
- if (llmAttrs.finishReason) {
462
- attributes["gen_ai.response.finish_reasons"] = llmAttrs.finishReason;
470
+ if (modelAttrs.finishReason) {
471
+ attributes["gen_ai.response.finish_reasons"] = modelAttrs.finishReason;
463
472
  }
464
473
  }
465
- if ((aiSpan.type === AISpanType.TOOL_CALL || aiSpan.type === AISpanType.MCP_TOOL_CALL) && aiSpan.attributes) {
466
- const toolAttrs = aiSpan.attributes;
474
+ if ((Span.type === SpanType.TOOL_CALL || Span.type === SpanType.MCP_TOOL_CALL) && Span.attributes) {
475
+ const toolAttrs = Span.attributes;
467
476
  if (toolAttrs.toolId) {
468
477
  attributes["gen_ai.tool.name"] = toolAttrs.toolId;
469
478
  }
470
- if (aiSpan.type === AISpanType.MCP_TOOL_CALL) {
479
+ if (Span.type === SpanType.MCP_TOOL_CALL) {
471
480
  const mcpAttrs = toolAttrs;
472
481
  if (mcpAttrs.mcpServer) {
473
482
  attributes["mcp.server"] = mcpAttrs.mcpServer;
@@ -484,10 +493,11 @@ var SpanConverter = class {
484
493
  attributes["gen_ai.tool.success"] = toolAttrs.success;
485
494
  }
486
495
  }
487
- if (aiSpan.type === AISpanType.AGENT_RUN && aiSpan.attributes) {
488
- const agentAttrs = aiSpan.attributes;
496
+ if (Span.type === SpanType.AGENT_RUN && Span.attributes) {
497
+ const agentAttrs = Span.attributes;
489
498
  if (agentAttrs.agentId) {
490
499
  attributes["agent.id"] = agentAttrs.agentId;
500
+ attributes["gen_ai.agent.id"] = agentAttrs.agentId;
491
501
  }
492
502
  if (agentAttrs.maxSteps) {
493
503
  attributes["agent.max_steps"] = agentAttrs.maxSteps;
@@ -496,8 +506,8 @@ var SpanConverter = class {
496
506
  attributes["agent.available_tools"] = JSON.stringify(agentAttrs.availableTools);
497
507
  }
498
508
  }
499
- if (aiSpan.type === AISpanType.WORKFLOW_RUN && aiSpan.attributes) {
500
- const workflowAttrs = aiSpan.attributes;
509
+ if (Span.type === SpanType.WORKFLOW_RUN && Span.attributes) {
510
+ const workflowAttrs = Span.attributes;
501
511
  if (workflowAttrs.workflowId) {
502
512
  attributes["workflow.id"] = workflowAttrs.workflowId;
503
513
  }
@@ -505,19 +515,19 @@ var SpanConverter = class {
505
515
  attributes["workflow.status"] = workflowAttrs.status;
506
516
  }
507
517
  }
508
- if (aiSpan.errorInfo) {
518
+ if (Span.errorInfo) {
509
519
  attributes["error"] = true;
510
- attributes["error.type"] = aiSpan.errorInfo.id || "unknown";
511
- attributes["error.message"] = aiSpan.errorInfo.message;
512
- if (aiSpan.errorInfo.domain) {
513
- attributes["error.domain"] = aiSpan.errorInfo.domain;
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;
514
524
  }
515
- if (aiSpan.errorInfo.category) {
516
- attributes["error.category"] = aiSpan.errorInfo.category;
525
+ if (Span.errorInfo.category) {
526
+ attributes["error.category"] = Span.errorInfo.category;
517
527
  }
518
528
  }
519
- if (aiSpan.metadata) {
520
- Object.entries(aiSpan.metadata).forEach(([key, value]) => {
529
+ if (Span.metadata) {
530
+ Object.entries(Span.metadata).forEach(([key, value]) => {
521
531
  if (!attributes[key]) {
522
532
  if (value === null || value === void 0) {
523
533
  return;
@@ -530,12 +540,12 @@ var SpanConverter = class {
530
540
  }
531
541
  });
532
542
  }
533
- if (aiSpan.startTime) {
534
- attributes["mastra.start_time"] = aiSpan.startTime.toISOString();
543
+ if (Span.startTime) {
544
+ attributes["mastra.start_time"] = Span.startTime.toISOString();
535
545
  }
536
- if (aiSpan.endTime) {
537
- attributes["mastra.end_time"] = aiSpan.endTime.toISOString();
538
- const duration = aiSpan.endTime.getTime() - aiSpan.startTime.getTime();
546
+ if (Span.endTime) {
547
+ attributes["mastra.end_time"] = Span.endTime.toISOString();
548
+ const duration = Span.endTime.getTime() - Span.startTime.getTime();
539
549
  attributes["mastra.duration_ms"] = duration;
540
550
  }
541
551
  return attributes;
@@ -543,28 +553,28 @@ var SpanConverter = class {
543
553
  /**
544
554
  * Get the operation name based on span type for gen_ai.operation.name
545
555
  */
546
- getOperationName(aiSpan) {
547
- switch (aiSpan.type) {
548
- case AISpanType.LLM_GENERATION: {
549
- const attrs = aiSpan.attributes;
556
+ getOperationName(Span) {
557
+ switch (Span.type) {
558
+ case SpanType.MODEL_GENERATION: {
559
+ const attrs = Span.attributes;
550
560
  return attrs?.resultType === "tool_selection" ? "tool_selection" : "chat";
551
561
  }
552
- case AISpanType.TOOL_CALL:
553
- case AISpanType.MCP_TOOL_CALL:
562
+ case SpanType.TOOL_CALL:
563
+ case SpanType.MCP_TOOL_CALL:
554
564
  return "tool.execute";
555
- case AISpanType.AGENT_RUN:
565
+ case SpanType.AGENT_RUN:
556
566
  return "agent.run";
557
- case AISpanType.WORKFLOW_RUN:
567
+ case SpanType.WORKFLOW_RUN:
558
568
  return "workflow.run";
559
569
  default:
560
- return aiSpan.type.replace(/_/g, ".");
570
+ return Span.type.replace(/_/g, ".");
561
571
  }
562
572
  }
563
573
  /**
564
574
  * Get span kind as string for attribute
565
575
  */
566
- getSpanKindString(aiSpan) {
567
- const kind = this.getSpanKind(aiSpan);
576
+ getSpanKindString(Span) {
577
+ const kind = this.getSpanKind(Span);
568
578
  switch (kind) {
569
579
  case SpanKind.SERVER:
570
580
  return "server";
@@ -582,21 +592,19 @@ var SpanConverter = class {
582
592
  }
583
593
  };
584
594
 
585
- // src/ai-tracing.ts
586
- var OtelExporter = class {
595
+ // src/tracing.ts
596
+ var OtelExporter = class extends BaseExporter {
587
597
  config;
588
598
  tracingConfig;
589
599
  spanConverter;
590
600
  processor;
591
601
  exporter;
592
602
  isSetup = false;
593
- isDisabled = false;
594
- logger;
595
603
  name = "opentelemetry";
596
604
  constructor(config) {
605
+ super(config);
597
606
  this.config = config;
598
607
  this.spanConverter = new SpanConverter();
599
- this.logger = new ConsoleLogger({ level: config.logLevel ?? "warn" });
600
608
  if (config.logLevel === "debug") {
601
609
  diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);
602
610
  }
@@ -604,11 +612,11 @@ var OtelExporter = class {
604
612
  /**
605
613
  * Initialize with tracing configuration
606
614
  */
607
- init(config) {
608
- this.tracingConfig = config;
615
+ init(options) {
616
+ this.tracingConfig = options.config;
609
617
  }
610
618
  async setupExporter() {
611
- if (this.isSetup) return;
619
+ if (this.isSetup || this.exporter) return;
612
620
  if (!this.config.provider) {
613
621
  this.logger.error(
614
622
  '[OtelExporter] Provider configuration is required. Use the "custom" provider for generic endpoints.'
@@ -623,6 +631,10 @@ var OtelExporter = class {
623
631
  this.isSetup = true;
624
632
  return;
625
633
  }
634
+ if (this.config.exporter) {
635
+ this.exporter = this.config.exporter;
636
+ return;
637
+ }
626
638
  const endpoint = resolved.endpoint;
627
639
  const headers = resolved.headers;
628
640
  const protocol = resolved.protocol;
@@ -676,6 +688,9 @@ var OtelExporter = class {
676
688
  this.isSetup = true;
677
689
  return;
678
690
  }
691
+ }
692
+ async setupProcessor() {
693
+ if (this.processor || this.isSetup) return;
679
694
  let resource = resourceFromAttributes({
680
695
  [ATTR_SERVICE_NAME]: this.tracingConfig?.serviceName || "mastra-service",
681
696
  [ATTR_SERVICE_VERSION]: "1.0.0",
@@ -704,13 +719,15 @@ var OtelExporter = class {
704
719
  this.logger.debug(
705
720
  `[OtelExporter] Using BatchSpanProcessor (batch size: ${this.config.batchSize || 512}, delay: 5s)`
706
721
  );
722
+ }
723
+ async setup() {
724
+ if (this.isSetup) return;
725
+ await this.setupExporter();
726
+ await this.setupProcessor();
707
727
  this.isSetup = true;
708
728
  }
709
- async exportEvent(event) {
710
- if (this.isDisabled) {
711
- return;
712
- }
713
- if (event.type !== AITracingEventType.SPAN_ENDED) {
729
+ async _exportTracingEvent(event) {
730
+ if (event.type !== TracingEventType.SPAN_ENDED) {
714
731
  return;
715
732
  }
716
733
  const span = event.exportedSpan;
@@ -718,7 +735,7 @@ var OtelExporter = class {
718
735
  }
719
736
  async exportSpan(span) {
720
737
  if (!this.isSetup) {
721
- await this.setupExporter();
738
+ await this.setup();
722
739
  }
723
740
  if (this.isDisabled || !this.processor) {
724
741
  return;