@mastra/otel-exporter 1.3.16-alpha.1 → 1.4.0-alpha.0

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.
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Feature detection for paired @mastra/core and @mastra/observability versions.
3
+ *
4
+ * Both are resolved synchronously so span classification is stable from the
5
+ * very first span: `OtelBridge.createSpan()` picks the OTEL SpanKind at
6
+ * `startSpan()` time, where it can no longer change, so detection must not
7
+ * depend on a pending dynamic import.
8
+ *
9
+ * `@mastra/observability` is a hard dependency, but older versions do not
10
+ * export `observabilityFeatures`. The namespace import reads it as `undefined`
11
+ * in that case instead of failing to link, which is the graceful fallback
12
+ * documented in `observability/mastra/src/features.ts`.
13
+ */
14
+ /**
15
+ * Returns true when both packages report the `model-inference-span` feature,
16
+ * meaning MODEL_INFERENCE spans are emitted by the tracker. Drives which span
17
+ * is exported as the GenAI `chat` call (new: MODEL_INFERENCE; legacy:
18
+ * MODEL_GENERATION).
19
+ */
20
+ export declare function isModelInferenceEnabled(): boolean;
21
+ /**
22
+ * @internal Test-only override. Allows tests to simulate a paired older or
23
+ * newer `@mastra/observability` without juggling module mocks.
24
+ */
25
+ export declare function __setObservabilityFeaturesForTest(features: ReadonlySet<string> | undefined): void;
26
+ //# sourceMappingURL=features.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features.d.ts","sourceRoot":"","sources":["../src/features.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAWH;;;;;GAKG;AACH,wBAAgB,uBAAuB,IAAI,OAAO,CAEjD;AAED;;;GAGG;AACH,wBAAgB,iCAAiC,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,SAAS,GAAG,IAAI,CAEjG"}
@@ -8,6 +8,7 @@
8
8
  * @see https://opentelemetry.io/docs/specs/semconv/gen-ai/non-normative/examples-llm-calls/
9
9
  * @see https://opentelemetry.io/docs/specs/semconv/registry/attributes/gen-ai/
10
10
  */
11
+ import { SpanType } from '@mastra/core/observability';
11
12
  import type { AnyExportedSpan, UsageStats } from '@mastra/core/observability';
12
13
  import type { Attributes } from '@opentelemetry/api';
13
14
  import { ATTR_GEN_AI_USAGE_INPUT_TOKENS, ATTR_GEN_AI_USAGE_OUTPUT_TOKENS, ATTR_GEN_AI_USAGE_CACHE_READ_INPUT_TOKENS, ATTR_GEN_AI_USAGE_CACHE_CREATION_INPUT_TOKENS } from '@opentelemetry/semantic-conventions/incubating';
@@ -30,13 +31,30 @@ export interface OtelUsageMetrics {
30
31
  * Formats UsageStats to OTel GenAI semantic convention attributes.
31
32
  */
32
33
  export declare function formatUsageMetrics(usage?: UsageStats): OtelUsageMetrics;
34
+ /**
35
+ * Whether this span is exported as the GenAI inference (`chat`) span.
36
+ *
37
+ * Exactly one span per model call carries `gen_ai.request.model`, the messages
38
+ * and `gen_ai.usage.*`, because OTel backends (Langfuse, Phoenix, ...) sum usage
39
+ * across nested spans. With paired packages that emit MODEL_INFERENCE, that is
40
+ * the call; MODEL_GENERATION is then the parent loop and MODEL_STEP one turn of
41
+ * it. Older pairings only emit MODEL_GENERATION, which keeps the `chat` role.
42
+ */
43
+ export declare function isModelCallSpan(type: SpanType): boolean;
44
+ export interface GenAISemanticsOptions {
45
+ /**
46
+ * Override {@link isModelCallSpan} for this span. Exporters that flatten the
47
+ * generation loop into a single `chat` span pass `true` for MODEL_GENERATION.
48
+ */
49
+ modelCall?: boolean;
50
+ }
33
51
  /**
34
52
  * Get an OTEL-compliant span name based on span type and attributes
35
53
  */
36
- export declare function getSpanName(span: AnyExportedSpan): string;
54
+ export declare function getSpanName(span: AnyExportedSpan, options?: GenAISemanticsOptions): string;
37
55
  /**
38
56
  * Gets OpenTelemetry attributes from Mastra Span
39
57
  * Following OTEL Semantic Conventions for GenAI
40
58
  */
41
- export declare function getAttributes(span: AnyExportedSpan): Attributes;
59
+ export declare function getAttributes(span: AnyExportedSpan, options?: GenAISemanticsOptions): Attributes;
42
60
  //# sourceMappingURL=gen-ai-semantics.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"gen-ai-semantics.d.ts","sourceRoot":"","sources":["../src/gen-ai-semantics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,KAAK,EAEV,eAAe,EAKf,UAAU,EASX,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAgBL,8BAA8B,EAC9B,+BAA+B,EAC/B,yCAAyC,EACzC,6CAA6C,EAY9C,MAAM,gDAAgD,CAAC;AAGxD;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,CAAC,8BAA8B,CAAC,CAAC,EAAE,MAAM,CAAC;IAC1C,CAAC,+BAA+B,CAAC,CAAC,EAAE,MAAM,CAAC;IAC3C,CAAC,yCAAyC,CAAC,CAAC,EAAE,MAAM,CAAC;IACrD,CAAC,6CAA6C,CAAC,CAAC,EAAE,MAAM,CAAC;IACzD,6CAA6C,CAAC,EAAE,MAAM,CAAC;IACvD,6CAA6C,CAAC,EAAE,MAAM,CAAC;IACvD,+BAA+B,CAAC,EAAE,MAAM,CAAC;IACzC,iCAAiC,CAAC,EAAE,MAAM,CAAC;IAC3C,kCAAkC,CAAC,EAAE,MAAM,CAAC;CAC7C;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,gBAAgB,CA2CvE;AA8ED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,MAAM,CAUzD;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,eAAe,GAAG,UAAU,CAyT/D"}
1
+ {"version":3,"file":"gen-ai-semantics.d.ts","sourceRoot":"","sources":["../src/gen-ai-semantics.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AACtD,OAAO,KAAK,EAEV,eAAe,EAOf,UAAU,EASX,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAgBL,8BAA8B,EAC9B,+BAA+B,EAC/B,yCAAyC,EACzC,6CAA6C,EAY9C,MAAM,gDAAgD,CAAC;AAIxD;;;GAGG;AACH,MAAM,WAAW,gBAAgB;IAC/B,CAAC,8BAA8B,CAAC,CAAC,EAAE,MAAM,CAAC;IAC1C,CAAC,+BAA+B,CAAC,CAAC,EAAE,MAAM,CAAC;IAC3C,CAAC,yCAAyC,CAAC,CAAC,EAAE,MAAM,CAAC;IACrD,CAAC,6CAA6C,CAAC,CAAC,EAAE,MAAM,CAAC;IACzD,6CAA6C,CAAC,EAAE,MAAM,CAAC;IACvD,6CAA6C,CAAC,EAAE,MAAM,CAAC;IACvD,+BAA+B,CAAC,EAAE,MAAM,CAAC;IACzC,iCAAiC,CAAC,EAAE,MAAM,CAAC;IAC3C,kCAAkC,CAAC,EAAE,MAAM,CAAC;CAC7C;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,CAAC,EAAE,UAAU,GAAG,gBAAgB,CA2CvE;AAoBD;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,CAEvD;AAED,MAAM,WAAW,qBAAqB;IACpC;;;OAGG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAmED;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,MAAM,CAU1F;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,eAAe,EAAE,OAAO,CAAC,EAAE,qBAAqB,GAAG,UAAU,CAoUhG"}
package/dist/index.cjs CHANGED
@@ -1,6 +1,29 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ //#region \0rolldown/runtime.js
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
+ get: ((k) => from[k]).bind(null, key),
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
+ value: mod,
21
+ enumerable: true
22
+ }) : target, mod));
23
+ //#endregion
2
24
  let _mastra_core_observability = require("@mastra/core/observability");
3
25
  let _mastra_observability = require("@mastra/observability");
26
+ _mastra_observability = __toESM(_mastra_observability, 1);
4
27
  let _opentelemetry_api = require("@opentelemetry/api");
5
28
  let _opentelemetry_core = require("@opentelemetry/core");
6
29
  let _opentelemetry_resources = require("@opentelemetry/resources");
@@ -11,6 +34,7 @@ let _opentelemetry_api_logs = require("@opentelemetry/api-logs");
11
34
  let fs = require("fs");
12
35
  let url = require("url");
13
36
  let _opentelemetry_semantic_conventions_incubating = require("@opentelemetry/semantic-conventions/incubating");
37
+ let _mastra_core_features = require("@mastra/core/features");
14
38
  //#region src/loadExporter.ts
15
39
  const EXPORTER_SPECS = {
16
40
  traces: {
@@ -267,6 +291,39 @@ function resolveCustomConfig(config) {
267
291
  };
268
292
  }
269
293
  //#endregion
294
+ //#region src/features.ts
295
+ /**
296
+ * Feature detection for paired @mastra/core and @mastra/observability versions.
297
+ *
298
+ * Both are resolved synchronously so span classification is stable from the
299
+ * very first span: `OtelBridge.createSpan()` picks the OTEL SpanKind at
300
+ * `startSpan()` time, where it can no longer change, so detection must not
301
+ * depend on a pending dynamic import.
302
+ *
303
+ * `@mastra/observability` is a hard dependency, but older versions do not
304
+ * export `observabilityFeatures`. The namespace import reads it as `undefined`
305
+ * in that case instead of failing to link, which is the graceful fallback
306
+ * documented in `observability/mastra/src/features.ts`.
307
+ */
308
+ const FEATURE = "model-inference-span";
309
+ let observabilityFeatures = _mastra_observability.observabilityFeatures;
310
+ /**
311
+ * Returns true when both packages report the `model-inference-span` feature,
312
+ * meaning MODEL_INFERENCE spans are emitted by the tracker. Drives which span
313
+ * is exported as the GenAI `chat` call (new: MODEL_INFERENCE; legacy:
314
+ * MODEL_GENERATION).
315
+ */
316
+ function isModelInferenceEnabled() {
317
+ return observabilityFeatures?.has(FEATURE) === true && _mastra_core_features.coreFeatures.has(FEATURE);
318
+ }
319
+ /**
320
+ * @internal Test-only override. Allows tests to simulate a paired older or
321
+ * newer `@mastra/observability` without juggling module mocks.
322
+ */
323
+ function __setObservabilityFeaturesForTest(features) {
324
+ observabilityFeatures = features;
325
+ }
326
+ //#endregion
270
327
  //#region src/gen-ai-messages.ts
271
328
  const isMastraMessagePart = (p) => {
272
329
  return typeof p === "object" && p != null && "type" in p && (p.type === "text" || p.type === "tool-call" || p.type === "tool-result") && (p.type === "text" && "text" in p || p.type === "tool-call" && "toolCallId" in p && "toolName" in p && "input" in p || p.type === "tool-result" && "toolCallId" in p && "toolName" in p && "output" in p);
@@ -377,11 +434,24 @@ function addModelRequestAttributes(attributes, attrs) {
377
434
  Object.assign(attributes, formatUsageMetrics(attrs.usage));
378
435
  }
379
436
  /**
437
+ * Whether this span is exported as the GenAI inference (`chat`) span.
438
+ *
439
+ * Exactly one span per model call carries `gen_ai.request.model`, the messages
440
+ * and `gen_ai.usage.*`, because OTel backends (Langfuse, Phoenix, ...) sum usage
441
+ * across nested spans. With paired packages that emit MODEL_INFERENCE, that is
442
+ * the call; MODEL_GENERATION is then the parent loop and MODEL_STEP one turn of
443
+ * it. Older pairings only emit MODEL_GENERATION, which keeps the `chat` role.
444
+ */
445
+ function isModelCallSpan(type) {
446
+ return type === (isModelInferenceEnabled() ? _mastra_core_observability.SpanType.MODEL_INFERENCE : _mastra_core_observability.SpanType.MODEL_GENERATION);
447
+ }
448
+ /**
380
449
  * Get the operation name based on span type for gen_ai.operation.name
381
450
  */
382
- function getOperationName(span) {
451
+ function getOperationName(span, modelCall = isModelCallSpan(span.type)) {
452
+ if (modelCall) return "chat";
383
453
  switch (span.type) {
384
- case _mastra_core_observability.SpanType.MODEL_GENERATION: return "chat";
454
+ case _mastra_core_observability.SpanType.MODEL_STEP: return "agent_step";
385
455
  case _mastra_core_observability.SpanType.RAG_EMBEDDING: return "embeddings";
386
456
  case _mastra_core_observability.SpanType.TOOL_CALL:
387
457
  case _mastra_core_observability.SpanType.MCP_TOOL_CALL:
@@ -399,7 +469,8 @@ function sanitizeSpanName(name) {
399
469
  }
400
470
  function getSpanIdentifier(span) {
401
471
  switch (span.type) {
402
- case _mastra_core_observability.SpanType.MODEL_GENERATION: return span.attributes?.model;
472
+ case _mastra_core_observability.SpanType.MODEL_GENERATION:
473
+ case _mastra_core_observability.SpanType.MODEL_INFERENCE: return span.attributes?.model;
403
474
  case _mastra_core_observability.SpanType.RAG_EMBEDDING: return span.attributes?.model;
404
475
  case _mastra_core_observability.SpanType.WORKFLOW_STEP: return span.entityId;
405
476
  case _mastra_core_observability.SpanType.WORKFLOW_CONDITIONAL:
@@ -414,33 +485,34 @@ function getSpanIdentifier(span) {
414
485
  /**
415
486
  * Get an OTEL-compliant span name based on span type and attributes
416
487
  */
417
- function getSpanName(span) {
488
+ function getSpanName(span, options) {
418
489
  const identifier = getSpanIdentifier(span);
419
- if (identifier) return `${getOperationName(span)} ${identifier}`;
490
+ if (identifier) return `${getOperationName(span, options?.modelCall)} ${identifier}`;
420
491
  return sanitizeSpanName(span.name);
421
492
  }
422
493
  /**
423
494
  * Gets OpenTelemetry attributes from Mastra Span
424
495
  * Following OTEL Semantic Conventions for GenAI
425
496
  */
426
- function getAttributes(span) {
497
+ function getAttributes(span, options) {
427
498
  const attributes = {};
428
499
  const spanType = span.type.toLowerCase();
429
- attributes[_opentelemetry_semantic_conventions_incubating.ATTR_GEN_AI_OPERATION_NAME] = getOperationName(span);
500
+ const modelCall = options?.modelCall ?? isModelCallSpan(span.type);
501
+ attributes[_opentelemetry_semantic_conventions_incubating.ATTR_GEN_AI_OPERATION_NAME] = getOperationName(span, modelCall);
430
502
  attributes["mastra.span.type"] = span.type;
431
503
  if (span.input !== void 0) {
432
504
  const inputStr = typeof span.input === "string" ? span.input : JSON.stringify(span.input);
433
- if (span.type === _mastra_core_observability.SpanType.MODEL_GENERATION) attributes[_opentelemetry_semantic_conventions_incubating.ATTR_GEN_AI_INPUT_MESSAGES] = convertMastraMessagesToGenAIMessages(inputStr);
505
+ if (modelCall) attributes[_opentelemetry_semantic_conventions_incubating.ATTR_GEN_AI_INPUT_MESSAGES] = convertMastraMessagesToGenAIMessages(inputStr);
434
506
  else if (span.type === _mastra_core_observability.SpanType.TOOL_CALL || span.type === _mastra_core_observability.SpanType.MCP_TOOL_CALL || span.type === _mastra_core_observability.SpanType.PROVIDER_TOOL_CALL) attributes["gen_ai.tool.call.arguments"] = inputStr;
435
507
  else attributes[`mastra.${spanType}.input`] = inputStr;
436
508
  }
437
509
  if (span.output !== void 0) {
438
510
  const outputStr = typeof span.output === "string" ? span.output : JSON.stringify(span.output);
439
- if (span.type === _mastra_core_observability.SpanType.MODEL_GENERATION) attributes[_opentelemetry_semantic_conventions_incubating.ATTR_GEN_AI_OUTPUT_MESSAGES] = convertMastraMessagesToGenAIMessages(outputStr);
511
+ if (modelCall) attributes[_opentelemetry_semantic_conventions_incubating.ATTR_GEN_AI_OUTPUT_MESSAGES] = convertMastraMessagesToGenAIMessages(outputStr);
440
512
  else if (span.type === _mastra_core_observability.SpanType.TOOL_CALL || span.type === _mastra_core_observability.SpanType.MCP_TOOL_CALL || span.type === _mastra_core_observability.SpanType.PROVIDER_TOOL_CALL) attributes["gen_ai.tool.call.result"] = outputStr;
441
513
  else attributes[`mastra.${spanType}.output`] = outputStr;
442
514
  }
443
- if (span.type === _mastra_core_observability.SpanType.MODEL_GENERATION && span.attributes) {
515
+ if (modelCall && span.attributes) {
444
516
  const modelAttrs = span.attributes;
445
517
  addModelRequestAttributes(attributes, modelAttrs);
446
518
  if (span.entityId) attributes[_opentelemetry_semantic_conventions_incubating.ATTR_GEN_AI_AGENT_ID] = span.entityId;
@@ -462,6 +534,11 @@ function getAttributes(span) {
462
534
  if (modelAttrs.serverAddress) attributes[_opentelemetry_semantic_conventions_incubating.ATTR_SERVER_ADDRESS] = modelAttrs.serverAddress;
463
535
  if (modelAttrs.serverPort !== void 0) attributes[_opentelemetry_semantic_conventions_incubating.ATTR_SERVER_PORT] = modelAttrs.serverPort;
464
536
  }
537
+ if (span.type === _mastra_core_observability.SpanType.MODEL_STEP && span.attributes) {
538
+ const stepAttrs = span.attributes;
539
+ if (stepAttrs.stepIndex !== void 0) attributes[`mastra.${spanType}.step_index`] = stepAttrs.stepIndex;
540
+ if (stepAttrs.isContinued !== void 0) attributes[`mastra.${spanType}.is_continued`] = stepAttrs.isContinued;
541
+ }
465
542
  if (span.type === _mastra_core_observability.SpanType.RAG_EMBEDDING && span.attributes) {
466
543
  const embeddingAttrs = span.attributes;
467
544
  addModelRequestAttributes(attributes, embeddingAttrs);
@@ -720,8 +797,8 @@ async function getPackageVersion(pkgName) {
720
797
  * @returns The appropriate OTEL SpanKind
721
798
  */
722
799
  function getSpanKind(type) {
800
+ if (isModelCallSpan(type)) return _opentelemetry_api.SpanKind.CLIENT;
723
801
  switch (type) {
724
- case _mastra_core_observability.SpanType.MODEL_GENERATION:
725
802
  case _mastra_core_observability.SpanType.RAG_EMBEDDING:
726
803
  case _mastra_core_observability.SpanType.MCP_TOOL_CALL: return _opentelemetry_api.SpanKind.CLIENT;
727
804
  default: return _opentelemetry_api.SpanKind.INTERNAL;
@@ -1107,11 +1184,13 @@ var OtelExporter = class extends _mastra_observability.BaseExporter {
1107
1184
  //#endregion
1108
1185
  exports.OtelExporter = OtelExporter;
1109
1186
  exports.SpanConverter = SpanConverter;
1187
+ exports.__setObservabilityFeaturesForTest = __setObservabilityFeaturesForTest;
1110
1188
  exports.buildLogAttributes = buildLogAttributes;
1111
1189
  exports.convertLog = convertLog;
1112
1190
  exports.getAttributes = getAttributes;
1113
1191
  exports.getSpanKind = getSpanKind;
1114
1192
  exports.getSpanName = getSpanName;
1193
+ exports.isModelCallSpan = isModelCallSpan;
1115
1194
  exports.mapSeverity = mapSeverity;
1116
1195
 
1117
1196
  //# sourceMappingURL=index.cjs.map