@mastra/otel-exporter 0.0.0-ai-sdk-workflow-route-20251010135341 → 0.0.0-allow-to-pass-a-mastra-url-instance-20251105224938

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,24 +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,
300
- // Tool calls can be CLIENT (external) or INTERNAL based on context
301
- [AISpanType.TOOL_CALL]: SpanKind.INTERNAL,
302
- [AISpanType.MCP_TOOL_CALL]: SpanKind.CLIENT,
306
+ // Model operations are CLIENT spans (calling external AI services)
307
+ [SpanType.MODEL_GENERATION]: SpanKind.CLIENT,
308
+ [SpanType.MODEL_CHUNK]: SpanKind.CLIENT,
309
+ // MCP tool calls are CLIENT (external service calls)
310
+ [SpanType.MCP_TOOL_CALL]: SpanKind.CLIENT,
303
311
  // Root spans for agent/workflow are SERVER (entry points)
304
- [AISpanType.AGENT_RUN]: SpanKind.SERVER,
305
- [AISpanType.WORKFLOW_RUN]: SpanKind.SERVER,
306
- // Internal workflow operations
307
- [AISpanType.WORKFLOW_STEP]: SpanKind.INTERNAL,
308
- [AISpanType.WORKFLOW_LOOP]: SpanKind.INTERNAL,
309
- [AISpanType.WORKFLOW_PARALLEL]: SpanKind.INTERNAL,
310
- [AISpanType.WORKFLOW_CONDITIONAL]: SpanKind.INTERNAL,
311
- [AISpanType.WORKFLOW_CONDITIONAL_EVAL]: SpanKind.INTERNAL,
312
- [AISpanType.WORKFLOW_SLEEP]: SpanKind.INTERNAL,
313
- [AISpanType.WORKFLOW_WAIT_EVENT]: SpanKind.INTERNAL,
314
- [AISpanType.GENERIC]: SpanKind.INTERNAL
312
+ [SpanType.AGENT_RUN]: SpanKind.SERVER,
313
+ [SpanType.WORKFLOW_RUN]: SpanKind.SERVER
315
314
  };
316
315
  var SpanConverter = class {
317
316
  resource;
@@ -324,19 +323,19 @@ var SpanConverter = class {
324
323
  };
325
324
  }
326
325
  /**
327
- * Convert a Mastra AI span to an OpenTelemetry ReadableSpan
326
+ * Convert a Mastra Span to an OpenTelemetry ReadableSpan
328
327
  * This preserves Mastra's trace and span IDs
329
328
  */
330
- convertSpan(aiSpan) {
331
- const spanKind = this.getSpanKind(aiSpan);
332
- const attributes = this.buildAttributes(aiSpan);
333
- const spanName = this.buildSpanName(aiSpan);
334
- 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 };
335
334
  return new MastraReadableSpan(
336
335
  otelSpan,
337
336
  attributes,
338
337
  spanKind,
339
- aiSpan.parentSpanId,
338
+ Span.parentSpanId,
340
339
  // Use the parentSpanId from the Mastra span directly
341
340
  this.resource,
342
341
  this.instrumentationLibrary
@@ -345,139 +344,139 @@ var SpanConverter = class {
345
344
  /**
346
345
  * Get the appropriate SpanKind based on span type and context
347
346
  */
348
- getSpanKind(aiSpan) {
349
- if (aiSpan.isRootSpan) {
350
- 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) {
351
350
  return SpanKind.SERVER;
352
351
  }
353
352
  }
354
- return SPAN_KIND_MAPPING[aiSpan.type] || SpanKind.INTERNAL;
353
+ return SPAN_KIND_MAPPING[Span.type] || SpanKind.INTERNAL;
355
354
  }
356
355
  /**
357
356
  * Build OTEL-compliant span name based on span type and attributes
358
357
  */
359
- buildSpanName(aiSpan) {
360
- switch (aiSpan.type) {
361
- case AISpanType.LLM_GENERATION: {
362
- const attrs = aiSpan.attributes;
358
+ buildSpanName(Span) {
359
+ switch (Span.type) {
360
+ case SpanType.MODEL_GENERATION: {
361
+ const attrs = Span.attributes;
363
362
  const operation = attrs?.resultType === "tool_selection" ? "tool_selection" : "chat";
364
363
  const model = attrs?.model || "unknown";
365
364
  return `${operation} ${model}`;
366
365
  }
367
- case AISpanType.TOOL_CALL:
368
- case AISpanType.MCP_TOOL_CALL: {
369
- const toolAttrs = aiSpan.attributes;
366
+ case SpanType.TOOL_CALL:
367
+ case SpanType.MCP_TOOL_CALL: {
368
+ const toolAttrs = Span.attributes;
370
369
  const toolName = toolAttrs?.toolId || "unknown";
371
370
  return `tool.execute ${toolName}`;
372
371
  }
373
- case AISpanType.AGENT_RUN: {
374
- const agentAttrs = aiSpan.attributes;
372
+ case SpanType.AGENT_RUN: {
373
+ const agentAttrs = Span.attributes;
375
374
  const agentId = agentAttrs?.agentId || "unknown";
376
375
  return `agent.${agentId}`;
377
376
  }
378
- case AISpanType.WORKFLOW_RUN: {
379
- const workflowAttrs = aiSpan.attributes;
377
+ case SpanType.WORKFLOW_RUN: {
378
+ const workflowAttrs = Span.attributes;
380
379
  const workflowId = workflowAttrs?.workflowId || "unknown";
381
380
  return `workflow.${workflowId}`;
382
381
  }
383
- case AISpanType.WORKFLOW_STEP:
384
- return aiSpan.name;
382
+ case SpanType.WORKFLOW_STEP:
383
+ return Span.name;
385
384
  default:
386
- return aiSpan.name;
385
+ return Span.name;
387
386
  }
388
387
  }
389
388
  /**
390
- * Build OpenTelemetry attributes from Mastra AI span
389
+ * Build OpenTelemetry attributes from Mastra Span
391
390
  * Following OTEL Semantic Conventions for GenAI
392
391
  */
393
- buildAttributes(aiSpan) {
392
+ buildAttributes(Span) {
394
393
  const attributes = {};
395
- attributes["gen_ai.operation.name"] = this.getOperationName(aiSpan);
396
- attributes["span.kind"] = this.getSpanKindString(aiSpan);
397
- attributes["mastra.span.type"] = aiSpan.type;
398
- attributes["mastra.trace_id"] = aiSpan.traceId;
399
- attributes["mastra.span_id"] = aiSpan.id;
400
- if (aiSpan.parentSpanId) {
401
- 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;
402
401
  }
403
- if (aiSpan.input !== void 0) {
404
- 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);
405
404
  attributes["input"] = inputStr;
406
- if (aiSpan.type === AISpanType.LLM_GENERATION) {
405
+ if (Span.type === SpanType.MODEL_GENERATION) {
407
406
  attributes["gen_ai.prompt"] = inputStr;
408
- } 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) {
409
408
  attributes["gen_ai.tool.input"] = inputStr;
410
409
  }
411
410
  }
412
- if (aiSpan.output !== void 0) {
413
- 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);
414
413
  attributes["output"] = outputStr;
415
- if (aiSpan.type === AISpanType.LLM_GENERATION) {
414
+ if (Span.type === SpanType.MODEL_GENERATION) {
416
415
  attributes["gen_ai.completion"] = outputStr;
417
- } 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) {
418
417
  attributes["gen_ai.tool.output"] = outputStr;
419
418
  }
420
419
  }
421
- if (aiSpan.type === AISpanType.LLM_GENERATION && aiSpan.attributes) {
422
- const llmAttrs = aiSpan.attributes;
423
- if (llmAttrs.model) {
424
- 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;
425
424
  }
426
- if (llmAttrs.provider) {
427
- attributes["gen_ai.system"] = llmAttrs.provider;
425
+ if (modelAttrs.provider) {
426
+ attributes["gen_ai.system"] = modelAttrs.provider;
428
427
  }
429
- if (llmAttrs.usage) {
430
- const inputTokens = llmAttrs.usage.inputTokens ?? llmAttrs.usage.promptTokens;
431
- 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;
432
431
  if (inputTokens !== void 0) {
433
432
  attributes["gen_ai.usage.input_tokens"] = inputTokens;
434
433
  }
435
434
  if (outputTokens !== void 0) {
436
435
  attributes["gen_ai.usage.output_tokens"] = outputTokens;
437
436
  }
438
- if (llmAttrs.usage.totalTokens !== void 0) {
439
- 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;
440
439
  }
441
- if (llmAttrs.usage.reasoningTokens !== void 0) {
442
- 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;
443
442
  }
444
- if (llmAttrs.usage.cachedInputTokens !== void 0) {
445
- 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;
446
445
  }
447
446
  }
448
- if (llmAttrs.parameters) {
449
- if (llmAttrs.parameters.temperature !== void 0) {
450
- 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;
451
450
  }
452
- if (llmAttrs.parameters.maxOutputTokens !== void 0) {
453
- 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;
454
453
  }
455
- if (llmAttrs.parameters.topP !== void 0) {
456
- 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;
457
456
  }
458
- if (llmAttrs.parameters.topK !== void 0) {
459
- 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;
460
459
  }
461
- if (llmAttrs.parameters.presencePenalty !== void 0) {
462
- 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;
463
462
  }
464
- if (llmAttrs.parameters.frequencyPenalty !== void 0) {
465
- 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;
466
465
  }
467
- if (llmAttrs.parameters.stopSequences) {
468
- 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);
469
468
  }
470
469
  }
471
- if (llmAttrs.finishReason) {
472
- attributes["gen_ai.response.finish_reasons"] = llmAttrs.finishReason;
470
+ if (modelAttrs.finishReason) {
471
+ attributes["gen_ai.response.finish_reasons"] = modelAttrs.finishReason;
473
472
  }
474
473
  }
475
- if ((aiSpan.type === AISpanType.TOOL_CALL || aiSpan.type === AISpanType.MCP_TOOL_CALL) && aiSpan.attributes) {
476
- const toolAttrs = aiSpan.attributes;
474
+ if ((Span.type === SpanType.TOOL_CALL || Span.type === SpanType.MCP_TOOL_CALL) && Span.attributes) {
475
+ const toolAttrs = Span.attributes;
477
476
  if (toolAttrs.toolId) {
478
477
  attributes["gen_ai.tool.name"] = toolAttrs.toolId;
479
478
  }
480
- if (aiSpan.type === AISpanType.MCP_TOOL_CALL) {
479
+ if (Span.type === SpanType.MCP_TOOL_CALL) {
481
480
  const mcpAttrs = toolAttrs;
482
481
  if (mcpAttrs.mcpServer) {
483
482
  attributes["mcp.server"] = mcpAttrs.mcpServer;
@@ -494,10 +493,11 @@ var SpanConverter = class {
494
493
  attributes["gen_ai.tool.success"] = toolAttrs.success;
495
494
  }
496
495
  }
497
- if (aiSpan.type === AISpanType.AGENT_RUN && aiSpan.attributes) {
498
- const agentAttrs = aiSpan.attributes;
496
+ if (Span.type === SpanType.AGENT_RUN && Span.attributes) {
497
+ const agentAttrs = Span.attributes;
499
498
  if (agentAttrs.agentId) {
500
499
  attributes["agent.id"] = agentAttrs.agentId;
500
+ attributes["gen_ai.agent.id"] = agentAttrs.agentId;
501
501
  }
502
502
  if (agentAttrs.maxSteps) {
503
503
  attributes["agent.max_steps"] = agentAttrs.maxSteps;
@@ -506,8 +506,8 @@ var SpanConverter = class {
506
506
  attributes["agent.available_tools"] = JSON.stringify(agentAttrs.availableTools);
507
507
  }
508
508
  }
509
- if (aiSpan.type === AISpanType.WORKFLOW_RUN && aiSpan.attributes) {
510
- const workflowAttrs = aiSpan.attributes;
509
+ if (Span.type === SpanType.WORKFLOW_RUN && Span.attributes) {
510
+ const workflowAttrs = Span.attributes;
511
511
  if (workflowAttrs.workflowId) {
512
512
  attributes["workflow.id"] = workflowAttrs.workflowId;
513
513
  }
@@ -515,19 +515,19 @@ var SpanConverter = class {
515
515
  attributes["workflow.status"] = workflowAttrs.status;
516
516
  }
517
517
  }
518
- if (aiSpan.errorInfo) {
518
+ if (Span.errorInfo) {
519
519
  attributes["error"] = true;
520
- attributes["error.type"] = aiSpan.errorInfo.id || "unknown";
521
- attributes["error.message"] = aiSpan.errorInfo.message;
522
- if (aiSpan.errorInfo.domain) {
523
- 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;
524
524
  }
525
- if (aiSpan.errorInfo.category) {
526
- attributes["error.category"] = aiSpan.errorInfo.category;
525
+ if (Span.errorInfo.category) {
526
+ attributes["error.category"] = Span.errorInfo.category;
527
527
  }
528
528
  }
529
- if (aiSpan.metadata) {
530
- Object.entries(aiSpan.metadata).forEach(([key, value]) => {
529
+ if (Span.metadata) {
530
+ Object.entries(Span.metadata).forEach(([key, value]) => {
531
531
  if (!attributes[key]) {
532
532
  if (value === null || value === void 0) {
533
533
  return;
@@ -540,12 +540,12 @@ var SpanConverter = class {
540
540
  }
541
541
  });
542
542
  }
543
- if (aiSpan.startTime) {
544
- attributes["mastra.start_time"] = aiSpan.startTime.toISOString();
543
+ if (Span.startTime) {
544
+ attributes["mastra.start_time"] = Span.startTime.toISOString();
545
545
  }
546
- if (aiSpan.endTime) {
547
- attributes["mastra.end_time"] = aiSpan.endTime.toISOString();
548
- 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();
549
549
  attributes["mastra.duration_ms"] = duration;
550
550
  }
551
551
  return attributes;
@@ -553,28 +553,28 @@ var SpanConverter = class {
553
553
  /**
554
554
  * Get the operation name based on span type for gen_ai.operation.name
555
555
  */
556
- getOperationName(aiSpan) {
557
- switch (aiSpan.type) {
558
- case AISpanType.LLM_GENERATION: {
559
- const attrs = aiSpan.attributes;
556
+ getOperationName(Span) {
557
+ switch (Span.type) {
558
+ case SpanType.MODEL_GENERATION: {
559
+ const attrs = Span.attributes;
560
560
  return attrs?.resultType === "tool_selection" ? "tool_selection" : "chat";
561
561
  }
562
- case AISpanType.TOOL_CALL:
563
- case AISpanType.MCP_TOOL_CALL:
562
+ case SpanType.TOOL_CALL:
563
+ case SpanType.MCP_TOOL_CALL:
564
564
  return "tool.execute";
565
- case AISpanType.AGENT_RUN:
565
+ case SpanType.AGENT_RUN:
566
566
  return "agent.run";
567
- case AISpanType.WORKFLOW_RUN:
567
+ case SpanType.WORKFLOW_RUN:
568
568
  return "workflow.run";
569
569
  default:
570
- return aiSpan.type.replace(/_/g, ".");
570
+ return Span.type.replace(/_/g, ".");
571
571
  }
572
572
  }
573
573
  /**
574
574
  * Get span kind as string for attribute
575
575
  */
576
- getSpanKindString(aiSpan) {
577
- const kind = this.getSpanKind(aiSpan);
576
+ getSpanKindString(Span) {
577
+ const kind = this.getSpanKind(Span);
578
578
  switch (kind) {
579
579
  case SpanKind.SERVER:
580
580
  return "server";
@@ -592,21 +592,19 @@ var SpanConverter = class {
592
592
  }
593
593
  };
594
594
 
595
- // src/ai-tracing.ts
596
- var OtelExporter = class {
595
+ // src/tracing.ts
596
+ var OtelExporter = class extends BaseExporter {
597
597
  config;
598
598
  tracingConfig;
599
599
  spanConverter;
600
600
  processor;
601
601
  exporter;
602
602
  isSetup = false;
603
- isDisabled = false;
604
- logger;
605
603
  name = "opentelemetry";
606
604
  constructor(config) {
605
+ super(config);
607
606
  this.config = config;
608
607
  this.spanConverter = new SpanConverter();
609
- this.logger = new ConsoleLogger({ level: config.logLevel ?? "warn" });
610
608
  if (config.logLevel === "debug") {
611
609
  diag.setLogger(new DiagConsoleLogger(), DiagLogLevel.DEBUG);
612
610
  }
@@ -614,11 +612,11 @@ var OtelExporter = class {
614
612
  /**
615
613
  * Initialize with tracing configuration
616
614
  */
617
- init(config) {
618
- this.tracingConfig = config;
615
+ init(options) {
616
+ this.tracingConfig = options.config;
619
617
  }
620
618
  async setupExporter() {
621
- if (this.isSetup) return;
619
+ if (this.isSetup || this.exporter) return;
622
620
  if (!this.config.provider) {
623
621
  this.logger.error(
624
622
  '[OtelExporter] Provider configuration is required. Use the "custom" provider for generic endpoints.'
@@ -633,6 +631,10 @@ var OtelExporter = class {
633
631
  this.isSetup = true;
634
632
  return;
635
633
  }
634
+ if (this.config.exporter) {
635
+ this.exporter = this.config.exporter;
636
+ return;
637
+ }
636
638
  const endpoint = resolved.endpoint;
637
639
  const headers = resolved.headers;
638
640
  const protocol = resolved.protocol;
@@ -686,7 +688,10 @@ var OtelExporter = class {
686
688
  this.isSetup = true;
687
689
  return;
688
690
  }
689
- const resource = resourceFromAttributes({
691
+ }
692
+ async setupProcessor() {
693
+ if (this.processor || this.isSetup) return;
694
+ let resource = resourceFromAttributes({
690
695
  [ATTR_SERVICE_NAME]: this.tracingConfig?.serviceName || "mastra-service",
691
696
  [ATTR_SERVICE_VERSION]: "1.0.0",
692
697
  // Add telemetry SDK information
@@ -694,6 +699,12 @@ var OtelExporter = class {
694
699
  [ATTR_TELEMETRY_SDK_VERSION]: "1.0.0",
695
700
  [ATTR_TELEMETRY_SDK_LANGUAGE]: "nodejs"
696
701
  });
702
+ if (this.config.resourceAttributes) {
703
+ resource = resource.merge(
704
+ // Duplicate attributes from config will override defaults above
705
+ resourceFromAttributes(this.config.resourceAttributes)
706
+ );
707
+ }
697
708
  this.spanConverter = new SpanConverter(resource);
698
709
  this.processor = new BatchSpanProcessor(this.exporter, {
699
710
  maxExportBatchSize: this.config.batchSize || 512,
@@ -708,13 +719,15 @@ var OtelExporter = class {
708
719
  this.logger.debug(
709
720
  `[OtelExporter] Using BatchSpanProcessor (batch size: ${this.config.batchSize || 512}, delay: 5s)`
710
721
  );
722
+ }
723
+ async setup() {
724
+ if (this.isSetup) return;
725
+ await this.setupExporter();
726
+ await this.setupProcessor();
711
727
  this.isSetup = true;
712
728
  }
713
- async exportEvent(event) {
714
- if (this.isDisabled) {
715
- return;
716
- }
717
- if (event.type !== AITracingEventType.SPAN_ENDED) {
729
+ async _exportTracingEvent(event) {
730
+ if (event.type !== TracingEventType.SPAN_ENDED) {
718
731
  return;
719
732
  }
720
733
  const span = event.exportedSpan;
@@ -722,7 +735,7 @@ var OtelExporter = class {
722
735
  }
723
736
  async exportSpan(span) {
724
737
  if (!this.isSetup) {
725
- await this.setupExporter();
738
+ await this.setup();
726
739
  }
727
740
  if (this.isDisabled || !this.processor) {
728
741
  return;