@mastra/langfuse 1.3.0-alpha.1 → 1.3.0-alpha.3

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/CHANGELOG.md CHANGED
@@ -1,5 +1,24 @@
1
1
  # @mastra/langfuse
2
2
 
3
+ ## 1.3.0-alpha.3
4
+
5
+ ### Patch Changes
6
+
7
+ - Scope Langfuse traces to the agent or workflow that started them. See [Scoping evaluators per agent](https://mastra.ai/docs/observability/tracing/exporters/langfuse#scoping-evaluators-per-agent) for more info. ([#16393](https://github.com/mastra-ai/mastra/pull/16393))
8
+
9
+ - Updated dependencies [[`6a569eb`](https://github.com/mastra-ai/mastra/commit/6a569eb89b006ba4714eeb92019c652ffa30e4e3), [`5688881`](https://github.com/mastra-ai/mastra/commit/5688881669c7ed157f31ac77f6fc5f8d95ceea32)]:
10
+ - @mastra/otel-exporter@1.1.0-alpha.3
11
+ - @mastra/core@1.33.0-alpha.9
12
+
13
+ ## 1.3.0-alpha.2
14
+
15
+ ### Patch Changes
16
+
17
+ - Updated dependencies [[`7c275a8`](https://github.com/mastra-ai/mastra/commit/7c275a810595e1a6c41ccc39720531ab65734700), [`890b24c`](https://github.com/mastra-ai/mastra/commit/890b24cc7d32ed6aa4dfe253e54dc6bf4099f690), [`0f48ebf`](https://github.com/mastra-ai/mastra/commit/0f48ebfc7ac7897b2092a189f45751924cf56d1c), [`f180e49`](https://github.com/mastra-ai/mastra/commit/f180e4990e71b04c9a475b523584071712f0048f), [`9260e01`](https://github.com/mastra-ai/mastra/commit/9260e015276fb1b500f7878ee452b47476bf1583), [`2f6c54e`](https://github.com/mastra-ai/mastra/commit/2f6c54e17c041cac1def54baaa6b771647836414), [`e06a159`](https://github.com/mastra-ai/mastra/commit/e06a1598ca07a6c3778aefc2a2d288363c6294ff), [`db34bc6`](https://github.com/mastra-ai/mastra/commit/db34bc6fb36cf125bda0c46be4d3fdc774b70cc4), [`33767a0`](https://github.com/mastra-ai/mastra/commit/33767a0e3762beeb33dab03b1608b6d5f405fc94)]:
18
+ - @mastra/core@1.33.0-alpha.8
19
+ - @mastra/observability@1.12.0-alpha.2
20
+ - @mastra/otel-exporter@1.0.23-alpha.2
21
+
3
22
  ## 1.3.0-alpha.1
4
23
 
5
24
  ### Minor Changes
package/dist/index.cjs CHANGED
@@ -74,7 +74,7 @@ var LangfuseExporter = class extends observability.BaseExporter {
74
74
  }
75
75
  try {
76
76
  const otelSpan = await this.#spanConverter.convertSpan(span);
77
- mapMastraToLangfuseAttributes(otelSpan.attributes, this.#environment, this.#release);
77
+ mapMastraToLangfuseAttributes(otelSpan.attributes, span, this.#environment, this.#release);
78
78
  this.#processor.onEnd(otelSpan);
79
79
  } catch (error) {
80
80
  this.logger.error(`${LOG_PREFIX} Failed to export span ${span.id}:`, error);
@@ -157,7 +157,7 @@ var LangfuseExporter = class extends observability.BaseExporter {
157
157
  await Promise.all([this.#processor?.shutdown(), this.#client?.shutdown()]);
158
158
  }
159
159
  };
160
- function mapMastraToLangfuseAttributes(attributes, environment, release) {
160
+ function mapMastraToLangfuseAttributes(attributes, span, environment, release) {
161
161
  if (environment) {
162
162
  attributes["langfuse.environment"] = environment;
163
163
  }
@@ -206,6 +206,29 @@ function mapMastraToLangfuseAttributes(attributes, environment, release) {
206
206
  attributes["langfuse.trace.version"] = attributes["mastra.metadata.version"];
207
207
  delete attributes["mastra.metadata.version"];
208
208
  }
209
+ if (span.isRootSpan) {
210
+ if (span.type === observability$1.SpanType.AGENT_RUN) {
211
+ if (!attributes["langfuse.trace.name"] && (span.entityName || span.entityId)) {
212
+ attributes["langfuse.trace.name"] = span.entityName ?? span.entityId;
213
+ }
214
+ if (span.entityId) {
215
+ attributes["langfuse.trace.metadata.agentId"] = span.entityId;
216
+ }
217
+ if (span.entityName) {
218
+ attributes["langfuse.trace.metadata.agentName"] = span.entityName;
219
+ }
220
+ } else if (span.type === observability$1.SpanType.WORKFLOW_RUN) {
221
+ if (!attributes["langfuse.trace.name"] && (span.entityName || span.entityId)) {
222
+ attributes["langfuse.trace.name"] = span.entityName ?? span.entityId;
223
+ }
224
+ if (span.entityId) {
225
+ attributes["langfuse.trace.metadata.workflowId"] = span.entityId;
226
+ }
227
+ if (span.entityName) {
228
+ attributes["langfuse.trace.metadata.workflowName"] = span.entityName;
229
+ }
230
+ }
231
+ }
209
232
  if (attributes["gen_ai.agent.id"]) {
210
233
  attributes["langfuse.observation.metadata.agentId"] = attributes["gen_ai.agent.id"];
211
234
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tracing.ts","../src/helpers.ts"],"names":["BaseExporter","LangfuseSpanProcessor","LangfuseClient","SpanConverter","TracingEventType"],"mappings":";;;;;;;;;AAiBA,IAAM,UAAA,GAAa,oBAAA;AAEZ,IAAM,yBAAA,GAA4B;AAqBlC,IAAM,gBAAA,GAAN,cAA+BA,0BAAA,CAAa;AAAA,EACjD,IAAA,GAAO,UAAA;AAAA,EACP,UAAA;AAAA,EACA,OAAA;AAAA,EACA,cAAA;AAAA,EACA,SAAA;AAAA,EACA,YAAA;AAAA,EACA,QAAA;AAAA,EAEA,WAAA,CAAY,MAAA,GAAiC,EAAC,EAAG;AAC/C,IAAA,KAAA,CAAM,MAAM,CAAA;AAEZ,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,SAAA,IAAa,OAAA,CAAQ,GAAA,CAAI,mBAAA;AAClD,IAAA,MAAM,SAAA,GAAY,MAAA,CAAO,SAAA,IAAa,OAAA,CAAQ,GAAA,CAAI,mBAAA;AAClD,IAAA,MAAM,UAAU,oBAAA,CAAqB,MAAA,CAAO,WAAW,OAAA,CAAQ,GAAA,CAAI,qBAAqB,yBAAyB,CAAA;AACjH,IAAA,IAAA,CAAK,SAAA,GAAY,OAAO,QAAA,IAAY,KAAA;AAEpC,IAAA,IAAI,CAAC,SAAA,IAAa,CAAC,SAAA,EAAW;AAC5B,MAAA,MAAM,kBAAkB,MAAA,CAAO,SAAA,GAC3B,gBACA,OAAA,CAAQ,GAAA,CAAI,sBACV,UAAA,GACA,SAAA;AACN,MAAA,MAAM,kBAAkB,MAAA,CAAO,SAAA,GAC3B,gBACA,OAAA,CAAQ,GAAA,CAAI,sBACV,UAAA,GACA,SAAA;AACN,MAAA,IAAA,CAAK,WAAA;AAAA,QACH,CAAA,EAAG,UAAU,CAAA,0CAAA,EAA6C,eAAe,gBAAgB,eAAe,CAAA,gGAAA;AAAA,OAE1G;AACA,MAAA;AAAA,IACF;AAEA,IAAA,IAAA,CAAK,UAAA,GAAa,IAAIC,0BAAA,CAAsB;AAAA,MAC1C,SAAA;AAAA,MACA,SAAA;AAAA,MACA,OAAA;AAAA,MACA,aAAa,MAAA,CAAO,WAAA;AAAA,MACpB,SAAS,MAAA,CAAO,OAAA;AAAA,MAChB,UAAA,EAAY,IAAA,CAAK,SAAA,GAAY,WAAA,GAAc,SAAA;AAAA,MAC3C,SAAS,MAAA,CAAO,OAAA;AAAA,MAChB,eAAe,MAAA,CAAO,aAAA;AAAA;AAAA;AAAA,MAGtB,kBAAkB,MAAM;AAAA,KACzB,CAAA;AAED,IAAA,IAAA,CAAK,OAAA,GAAU,IAAIC,qBAAA,CAAe;AAAA,MAChC,SAAA;AAAA,MACA,SAAA;AAAA,MACA;AAAA,KACD,CAAA;AAED,IAAA,IAAA,CAAK,YAAA,GAAe,MAAA,CAAO,WAAA,IAAe,OAAA,CAAQ,GAAA,CAAI,4BAAA;AACtD,IAAA,IAAA,CAAK,QAAA,GAAW,MAAA,CAAO,OAAA,IAAW,OAAA,CAAQ,GAAA,CAAI,gBAAA;AAAA,EAChD;AAAA,EAEA,KAAK,OAAA,EAA8B;AACjC,IAAA,IAAA,CAAK,cAAA,GAAiB,IAAIC,0BAAA,CAAc;AAAA,MACtC,WAAA,EAAa,kBAAA;AAAA,MACb,WAAA,EAAa,QAAQ,MAAA,EAAQ,WAAA;AAAA,MAC7B,MAAA,EAAQ;AAAA,KACT,CAAA;AAAA,EACH;AAAA,EAEA,MAAgB,oBAAoB,KAAA,EAAoC;AACtE,IAAA,IAAI,KAAA,CAAM,IAAA,KAASC,gCAAA,CAAiB,UAAA,EAAY;AAChD,IAAA,IAAI,CAAC,KAAK,UAAA,EAAY;AAEtB,IAAA,MAAM,IAAA,CAAK,UAAA,CAAW,KAAA,CAAM,YAAY,CAAA;AAAA,EAC1C;AAAA,EAEA,MAAc,WAAW,IAAA,EAAsC;AAC7D,IAAA,IAAI,CAAC,KAAK,cAAA,EAAgB;AAExB,MAAA,IAAA,CAAK,cAAA,GAAiB,IAAID,0BAAA,CAAc;AAAA,QACtC,WAAA,EAAa,kBAAA;AAAA,QACb,WAAA,EAAa,gBAAA;AAAA,QACb,MAAA,EAAQ;AAAA,OACT,CAAA;AAAA,IACH;AAEA,IAAA,IAAI;AACF,MAAA,MAAM,QAAA,GAAW,MAAM,IAAA,CAAK,cAAA,CAAe,YAAY,IAAI,CAAA;AAM3D,MAAA,6BAAA,CAA8B,QAAA,CAAS,UAAA,EAAY,IAAA,CAAK,YAAA,EAAc,KAAK,QAAQ,CAAA;AAEnF,MAAA,IAAA,CAAK,UAAA,CAAY,MAAM,QAAQ,CAAA;AAAA,IACjC,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,MAAA,CAAO,MAAM,CAAA,EAAG,UAAU,0BAA0B,IAAA,CAAK,EAAE,KAAK,KAAK,CAAA;AAAA,IAC5E;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMA,IAAI,MAAA,GAAqC;AACvC,IAAA,OAAO,IAAA,CAAK,OAAA;AAAA,EACd;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,YAAY,IAAA,EAQX;AACP,IAAA,IAAI,CAAC,KAAK,OAAA,EAAS;AAEnB,IAAA,MAAM,EAAE,IAAI,OAAA,EAAS,MAAA,EAAQ,MAAM,KAAA,EAAO,OAAA,EAAS,UAAS,GAAI,IAAA;AAChE,IAAA,IAAI;AACF,MAAA,IAAA,CAAK,OAAA,CAAQ,MAAM,MAAA,CAAO;AAAA,QACxB,EAAA;AAAA,QACA,OAAA;AAAA,QACA,GAAI,MAAA,GAAS,EAAE,aAAA,EAAe,MAAA,KAAW,EAAC;AAAA,QAC1C,IAAA;AAAA,QACA,KAAA;AAAA,QACA,GAAI,OAAA,GAAU,EAAE,OAAA,KAAY,EAAC;AAAA,QAC7B,GAAI,QAAA,GAAW,EAAE,QAAA,KAAa,EAAC;AAAA,QAC/B,QAAA,EAAU;AAAA,OACX,CAAA;AAAA,IACH,SAAS,KAAA,EAAO;AACd,MAAA,IAAA,CAAK,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,UAAU,CAAA,uBAAA,CAAA,EAA2B;AAAA,QACxD,KAAA;AAAA,QACA,OAAA;AAAA,QACA,MAAA;AAAA,QACA;AAAA,OACD,CAAA;AAAA,IACH;AAAA,EACF;AAAA,EAEA,MAAM,aAAa,KAAA,EAAkC;AACnD,IAAA,MAAM,EAAE,OAAM,GAAI,KAAA;AAClB,IAAA,IAAI,CAAC,MAAM,OAAA,EAAS;AACpB,IAAA,IAAA,CAAK,WAAA,CAAY;AAAA,MACf,IAAI,KAAA,CAAM,OAAA;AAAA,MACV,SAAS,KAAA,CAAM,OAAA;AAAA,MACf,QAAQ,KAAA,CAAM,MAAA;AAAA,MACd,IAAA,EAAM,KAAA,CAAM,UAAA,IAAc,KAAA,CAAM,QAAA;AAAA,MAChC,OAAO,KAAA,CAAM,KAAA;AAAA,MACb,SAAS,KAAA,CAAM,MAAA;AAAA,MACf,UAAU,KAAA,CAAM;AAAA,KACjB,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,eAAA,CAAgB;AAAA,IACpB,OAAA;AAAA,IACA,MAAA;AAAA,IACA,KAAA;AAAA,IACA,MAAA;AAAA,IACA,UAAA;AAAA,IACA;AAAA,GACF,EAOkB;AAChB,IAAA,IAAA,CAAK,WAAA,CAAY;AAAA,MACf,IAAI,CAAA,EAAG,OAAO,IAAI,MAAA,IAAU,EAAE,IAAI,UAAU,CAAA,CAAA;AAAA,MAC5C,OAAA;AAAA,MACA,MAAA;AAAA,MACA,IAAA,EAAM,UAAA;AAAA,MACN,KAAA,EAAO,KAAA;AAAA,MACP,OAAA,EAAS,MAAA;AAAA,MACT;AAAA,KACD,CAAA;AAAA,EACH;AAAA,EAEA,MAAM,KAAA,GAAuB;AAC3B,IAAA,MAAM,OAAA,CAAQ,GAAA,CAAI,CAAC,IAAA,CAAK,UAAA,EAAY,UAAA,EAAW,EAAG,IAAA,CAAK,OAAA,EAAS,KAAA,EAAO,CAAC,CAAA;AAAA,EAC1E;AAAA,EAEA,MAAM,QAAA,GAA0B;AAC9B,IAAA,MAAM,OAAA,CAAQ,GAAA,CAAI,CAAC,IAAA,CAAK,UAAA,EAAY,QAAA,EAAS,EAAG,IAAA,CAAK,OAAA,EAAS,QAAA,EAAU,CAAC,CAAA;AAAA,EAC3E;AACF;AAYA,SAAS,6BAAA,CAA8B,UAAA,EAAiC,WAAA,EAAsB,OAAA,EAAwB;AAEpH,EAAA,IAAI,WAAA,EAAa;AACf,IAAA,UAAA,CAAW,sBAAsB,CAAA,GAAI,WAAA;AAAA,EACvC;AACA,EAAA,IAAI,OAAA,EAAS;AACX,IAAA,UAAA,CAAW,kBAAkB,CAAA,GAAI,OAAA;AAAA,EACnC;AAGA,EAAA,MAAM,gBAAA,GAAmB,WAAW,0BAA0B,CAAA;AAC9D,EAAA,IAAI,gBAAA,EAAkB;AACpB,IAAA,IAAI;AACF,MAAA,MAAM,SAAS,OAAO,gBAAA,KAAqB,WAAW,IAAA,CAAK,KAAA,CAAM,gBAAgB,CAAA,GAAI,gBAAA;AACrF,MAAA,IAAI,QAAQ,MAAA,EAAQ;AAClB,QAAA,IAAI,MAAA,CAAO,MAAA,CAAO,IAAA,KAAS,MAAA,EAAW;AACpC,UAAA,UAAA,CAAW,kCAAkC,CAAA,GAAI,MAAA,CAAO,MAAA,CAAO,IAAA;AAAA,QACjE;AACA,QAAA,IAAI,MAAA,CAAO,MAAA,CAAO,OAAA,KAAY,MAAA,EAAW;AACvC,UAAA,UAAA,CAAW,qCAAqC,CAAA,GAAI,MAAA,CAAO,MAAA,CAAO,OAAA;AAAA,QACpE;AAAA,MACF;AAAA,IACF,CAAA,CAAA,MAAQ;AAAA,IAER;AACA,IAAA,OAAO,WAAW,0BAA0B,CAAA;AAAA,EAC9C;AAGA,EAAA,IAAI,UAAA,CAAW,8BAA8B,CAAA,EAAG;AAC9C,IAAA,UAAA,CAAW,4CAA4C,CAAA,GAAI,UAAA,CAAW,8BAA8B,CAAA;AACpG,IAAA,OAAO,WAAW,8BAA8B,CAAA;AAAA,EAClD;AAGA,EAAA,IAAI,UAAA,CAAW,wBAAwB,CAAA,EAAG;AACxC,IAAA,UAAA,CAAW,SAAS,CAAA,GAAI,UAAA,CAAW,wBAAwB,CAAA;AAC3D,IAAA,OAAO,WAAW,wBAAwB,CAAA;AAAA,EAC5C;AAGA,EAAA,MAAM,SAAA,GAAY,UAAA,CAAW,2BAA2B,CAAA,IAAK,WAAW,0BAA0B,CAAA;AAClG,EAAA,IAAI,SAAA,EAAW;AACb,IAAA,UAAA,CAAW,YAAY,CAAA,GAAI,SAAA;AAC3B,IAAA,OAAO,WAAW,2BAA2B,CAAA;AAC7C,IAAA,OAAO,WAAW,0BAA0B,CAAA;AAAA,EAC9C;AAGA,EAAA,IAAI,UAAA,CAAW,aAAa,CAAA,EAAG;AAC7B,IAAA,UAAA,CAAW,qBAAqB,CAAA,GAAI,UAAA,CAAW,aAAa,CAAA;AAC5D,IAAA,OAAO,WAAW,aAAa,CAAA;AAAA,EACjC;AAGA,EAAA,IAAI,UAAA,CAAW,2BAA2B,CAAA,EAAG;AAC3C,IAAA,UAAA,CAAW,qBAAqB,CAAA,GAAI,UAAA,CAAW,2BAA2B,CAAA;AAC1E,IAAA,OAAO,WAAW,2BAA2B,CAAA;AAAA,EAC/C;AAGA,EAAA,IAAI,UAAA,CAAW,yBAAyB,CAAA,EAAG;AACzC,IAAA,UAAA,CAAW,wBAAwB,CAAA,GAAI,UAAA,CAAW,yBAAyB,CAAA;AAC3E,IAAA,OAAO,WAAW,yBAAyB,CAAA;AAAA,EAC7C;AAKA,EAAA,IAAI,UAAA,CAAW,iBAAiB,CAAA,EAAG;AACjC,IAAA,UAAA,CAAW,uCAAuC,CAAA,GAAI,UAAA,CAAW,iBAAiB,CAAA;AAAA,EACpF;AACA,EAAA,IAAI,UAAA,CAAW,mBAAmB,CAAA,EAAG;AACnC,IAAA,UAAA,CAAW,yCAAyC,CAAA,GAAI,UAAA,CAAW,mBAAmB,CAAA;AAAA,EACxF;AACA,EAAA,IAAI,UAAA,CAAW,kBAAkB,CAAA,EAAG;AAClC,IAAA,UAAA,CAAW,wCAAwC,CAAA,GAAI,UAAA,CAAW,kBAAkB,CAAA;AAAA,EACtF;AACA,EAAA,IAAI,UAAA,CAAW,uBAAuB,CAAA,EAAG;AACvC,IAAA,UAAA,CAAW,6CAA6C,CAAA,GAAI,UAAA,CAAW,uBAAuB,CAAA;AAAA,EAChG;AAKA,EAAA,IAAI,CAAC,UAAA,CAAW,uBAAuB,KAAK,CAAC,UAAA,CAAW,4BAA4B,CAAA,EAAG;AACrF,IAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,UAAU,CAAA,EAAG;AACzC,MAAA,IAAI,IAAI,UAAA,CAAW,SAAS,KAAK,GAAA,CAAI,QAAA,CAAS,QAAQ,CAAA,EAAG;AACvD,QAAA,UAAA,CAAW,4BAA4B,CAAA,GAAI,UAAA,CAAW,GAAG,CAAA;AACzD,QAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACA,EAAA,IAAI,CAAC,UAAA,CAAW,wBAAwB,KAAK,CAAC,UAAA,CAAW,yBAAyB,CAAA,EAAG;AACnF,IAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,IAAA,CAAK,UAAU,CAAA,EAAG;AACzC,MAAA,IAAI,IAAI,UAAA,CAAW,SAAS,KAAK,GAAA,CAAI,QAAA,CAAS,SAAS,CAAA,EAAG;AACxD,QAAA,UAAA,CAAW,6BAA6B,CAAA,GAAI,UAAA,CAAW,GAAG,CAAA;AAC1D,QAAA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAOA,SAAS,qBAAqB,CAAA,EAAmB;AAC/C,EAAA,IAAI,MAAM,CAAA,CAAE,MAAA;AACZ,EAAA,OAAO,MAAM,CAAA,IAAK,CAAA,CAAE,WAAW,GAAA,GAAM,CAAC,MAAM,EAAA,EAAc;AACxD,IAAA,GAAA,EAAA;AAAA,EACF;AACA,EAAA,OAAO,QAAQ,CAAA,CAAE,MAAA,GAAS,IAAI,CAAA,CAAE,KAAA,CAAM,GAAG,GAAG,CAAA;AAC9C;;;AClSO,SAAS,mBAAmB,MAAA,EAAoD;AACrF,EAAA,OAAO,CAAA,IAAA,MAAS;AAAA,IACd,GAAG,IAAA;AAAA,IACH,QAAA,EAAU;AAAA,MACR,GAAG,IAAA,CAAK,QAAA;AAAA,MACR,QAAA,EAAU;AAAA,QACR,GAAI,KAAK,QAAA,EAAU,QAAA;AAAA,QACnB,MAAA,EAAQ;AAAA,UACN,GAAI,MAAA,CAAO,IAAA,KAAS,UAAa,EAAE,IAAA,EAAM,OAAO,IAAA,EAAK;AAAA,UACrD,GAAI,MAAA,CAAO,OAAA,KAAY,UAAa,EAAE,OAAA,EAAS,OAAO,OAAA,EAAQ;AAAA,UAC9D,GAAI,MAAA,CAAO,EAAA,KAAO,UAAa,EAAE,EAAA,EAAI,OAAO,EAAA;AAAG;AACjD;AACF;AACF,GACF,CAAA;AACF","file":"index.cjs","sourcesContent":["/**\n * Langfuse Exporter for Mastra Observability\n *\n * Sends observability data to Langfuse using the official @langfuse/otel span processor\n * and @langfuse/client for non-tracing features (scoring, prompt management, evaluations).\n *\n * @see https://langfuse.com/docs/observability/sdk/typescript/overview\n */\n\nimport { LangfuseClient } from '@langfuse/client';\nimport { LangfuseSpanProcessor } from '@langfuse/otel';\nimport type { TracingEvent, AnyExportedSpan, InitExporterOptions, ScoreEvent } from '@mastra/core/observability';\nimport { TracingEventType } from '@mastra/core/observability';\nimport { BaseExporter } from '@mastra/observability';\nimport type { BaseExporterConfig } from '@mastra/observability';\nimport { SpanConverter } from '@mastra/otel-exporter';\n\nconst LOG_PREFIX = '[LangfuseExporter]';\n\nexport const LANGFUSE_DEFAULT_BASE_URL = 'https://cloud.langfuse.com';\n\nexport interface LangfuseExporterConfig extends BaseExporterConfig {\n /** Langfuse public key */\n publicKey?: string;\n /** Langfuse secret key */\n secretKey?: string;\n /** Langfuse host URL (defaults to https://cloud.langfuse.com) */\n baseUrl?: string;\n /** Enable realtime mode - flushes after each event for immediate visibility */\n realtime?: boolean;\n /** Maximum number of spans per OTEL export batch */\n flushAt?: number;\n /** Maximum time in seconds before pending spans are exported */\n flushInterval?: number;\n /** Langfuse environment tag for traces */\n environment?: string;\n /** Langfuse release tag for traces */\n release?: string;\n}\n\nexport class LangfuseExporter extends BaseExporter {\n name = 'langfuse';\n #processor: LangfuseSpanProcessor | undefined;\n #client: LangfuseClient | undefined;\n #spanConverter: SpanConverter | undefined;\n #realtime: boolean;\n #environment: string | undefined;\n #release: string | undefined;\n\n constructor(config: LangfuseExporterConfig = {}) {\n super(config);\n\n const publicKey = config.publicKey ?? process.env.LANGFUSE_PUBLIC_KEY;\n const secretKey = config.secretKey ?? process.env.LANGFUSE_SECRET_KEY;\n const baseUrl = stripTrailingSlashes(config.baseUrl ?? process.env.LANGFUSE_BASE_URL ?? LANGFUSE_DEFAULT_BASE_URL);\n this.#realtime = config.realtime ?? false;\n\n if (!publicKey || !secretKey) {\n const publicKeySource = config.publicKey\n ? 'from config'\n : process.env.LANGFUSE_PUBLIC_KEY\n ? 'from env'\n : 'missing';\n const secretKeySource = config.secretKey\n ? 'from config'\n : process.env.LANGFUSE_SECRET_KEY\n ? 'from env'\n : 'missing';\n this.setDisabled(\n `${LOG_PREFIX} Missing required credentials (publicKey: ${publicKeySource}, secretKey: ${secretKeySource}). ` +\n `Set LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY environment variables or pass them in config.`,\n );\n return;\n }\n\n this.#processor = new LangfuseSpanProcessor({\n publicKey,\n secretKey,\n baseUrl,\n environment: config.environment,\n release: config.release,\n exportMode: this.#realtime ? 'immediate' : 'batched',\n flushAt: config.flushAt,\n flushInterval: config.flushInterval,\n // Export all spans — the default filter only passes spans with gen_ai.* attributes\n // or known LLM instrumentation scopes, but Mastra spans use mastra.* attributes.\n shouldExportSpan: () => true,\n });\n\n this.#client = new LangfuseClient({\n publicKey,\n secretKey,\n baseUrl,\n });\n\n this.#environment = config.environment ?? process.env.LANGFUSE_TRACING_ENVIRONMENT;\n this.#release = config.release ?? process.env.LANGFUSE_RELEASE;\n }\n\n init(options: InitExporterOptions) {\n this.#spanConverter = new SpanConverter({\n packageName: '@mastra/langfuse',\n serviceName: options.config?.serviceName,\n format: 'GenAI_v1_38_0',\n });\n }\n\n protected async _exportTracingEvent(event: TracingEvent): Promise<void> {\n if (event.type !== TracingEventType.SPAN_ENDED) return;\n if (!this.#processor) return;\n\n await this.exportSpan(event.exportedSpan);\n }\n\n private async exportSpan(span: AnyExportedSpan): Promise<void> {\n if (!this.#spanConverter) {\n // Fallback if init() was not called (e.g., standalone usage without Mastra)\n this.#spanConverter = new SpanConverter({\n packageName: '@mastra/langfuse',\n serviceName: 'mastra-service',\n format: 'GenAI_v1_38_0',\n });\n }\n\n try {\n const otelSpan = await this.#spanConverter.convertSpan(span);\n\n // Map mastra.* attributes to langfuse.* namespace so that Langfuse's OTLP\n // endpoint reads them correctly. SpanConverter produces mastra.* attributes,\n // but Langfuse only reads langfuse.* attributes for prompt linking, TTFT, etc.\n // @see https://langfuse.com/integrations/native/opentelemetry#property-mapping\n mapMastraToLangfuseAttributes(otelSpan.attributes, this.#environment, this.#release);\n\n this.#processor!.onEnd(otelSpan);\n } catch (error) {\n this.logger.error(`${LOG_PREFIX} Failed to export span ${span.id}:`, error);\n }\n }\n\n /**\n * The LangfuseClient instance for advanced Langfuse features.\n * Use this for prompt management, evaluations, datasets, and direct API access.\n */\n get client(): LangfuseClient | undefined {\n return this.#client;\n }\n\n /**\n * Submit a score to Langfuse. Used by both the new `onScoreEvent` path and the\n * deprecated `addScoreToTrace` wrapper.\n */\n private submitScore(args: {\n id: string;\n traceId: string;\n spanId?: string;\n name: string;\n value: number;\n comment?: string;\n metadata?: Record<string, unknown>;\n }): void {\n if (!this.#client) return;\n\n const { id, traceId, spanId, name, value, comment, metadata } = args;\n try {\n this.#client.score.create({\n id,\n traceId,\n ...(spanId ? { observationId: spanId } : {}),\n name,\n value,\n ...(comment ? { comment } : {}),\n ...(metadata ? { metadata } : {}),\n dataType: 'NUMERIC' as const,\n });\n } catch (error) {\n this.logger.error(`${LOG_PREFIX} Error submitting score`, {\n error,\n traceId,\n spanId,\n name,\n });\n }\n }\n\n async onScoreEvent(event: ScoreEvent): Promise<void> {\n const { score } = event;\n if (!score.traceId) return;\n this.submitScore({\n id: score.scoreId,\n traceId: score.traceId,\n spanId: score.spanId,\n name: score.scorerName ?? score.scorerId,\n value: score.score,\n comment: score.reason,\n metadata: score.metadata,\n });\n }\n\n /**\n * @deprecated Use the observability score event pipeline (`mastra.observability.addScore`)\n * instead. This method is preserved for backwards compatibility and forwards to the same\n * underlying client call as `onScoreEvent`.\n */\n async addScoreToTrace({\n traceId,\n spanId,\n score,\n reason,\n scorerName,\n metadata,\n }: {\n traceId: string;\n spanId?: string;\n score: number;\n reason?: string;\n scorerName: string;\n metadata?: Record<string, any>;\n }): Promise<void> {\n this.submitScore({\n id: `${traceId}-${spanId || ''}-${scorerName}`,\n traceId,\n spanId,\n name: scorerName,\n value: score,\n comment: reason,\n metadata,\n });\n }\n\n async flush(): Promise<void> {\n await Promise.all([this.#processor?.forceFlush(), this.#client?.flush()]);\n }\n\n async shutdown(): Promise<void> {\n await Promise.all([this.#processor?.shutdown(), this.#client?.shutdown()]);\n }\n}\n\n/**\n * Maps Mastra-specific OTel attributes to the langfuse.* namespace that\n * Langfuse's OTLP endpoint reads for prompt linking, TTFT, and other features.\n *\n * SpanConverter produces attributes like mastra.metadata.*, mastra.completion_start_time, etc.\n * Langfuse's OTLP server only reads langfuse.observation.prompt.name, langfuse.observation.completion_start_time, etc.\n *\n * This function mutates the attributes object in place.\n * @see https://langfuse.com/integrations/native/opentelemetry#property-mapping\n */\nfunction mapMastraToLangfuseAttributes(attributes: Record<string, any>, environment?: string, release?: string): void {\n // Environment and release: set directly since onStart() is not called\n if (environment) {\n attributes['langfuse.environment'] = environment;\n }\n if (release) {\n attributes['langfuse.release'] = release;\n }\n\n // Prompt linking: mastra.metadata.langfuse → langfuse.observation.prompt.name / version\n const langfuseMetadata = attributes['mastra.metadata.langfuse'];\n if (langfuseMetadata) {\n try {\n const parsed = typeof langfuseMetadata === 'string' ? JSON.parse(langfuseMetadata) : langfuseMetadata;\n if (parsed?.prompt) {\n if (parsed.prompt.name !== undefined) {\n attributes['langfuse.observation.prompt.name'] = parsed.prompt.name;\n }\n if (parsed.prompt.version !== undefined) {\n attributes['langfuse.observation.prompt.version'] = parsed.prompt.version;\n }\n }\n } catch {\n // best effort — invalid JSON is silently ignored\n }\n delete attributes['mastra.metadata.langfuse'];\n }\n\n // TTFT: mastra.completion_start_time → langfuse.observation.completion_start_time\n if (attributes['mastra.completion_start_time']) {\n attributes['langfuse.observation.completion_start_time'] = attributes['mastra.completion_start_time'];\n delete attributes['mastra.completion_start_time'];\n }\n\n // User ID: mastra.metadata.userId → user.id\n if (attributes['mastra.metadata.userId']) {\n attributes['user.id'] = attributes['mastra.metadata.userId'];\n delete attributes['mastra.metadata.userId'];\n }\n\n // Session ID: mastra.metadata.sessionId or threadId → session.id\n const sessionId = attributes['mastra.metadata.sessionId'] ?? attributes['mastra.metadata.threadId'];\n if (sessionId) {\n attributes['session.id'] = sessionId;\n delete attributes['mastra.metadata.sessionId'];\n delete attributes['mastra.metadata.threadId'];\n }\n\n // Tags: mastra.tags → langfuse.trace.tags\n if (attributes['mastra.tags']) {\n attributes['langfuse.trace.tags'] = attributes['mastra.tags'];\n delete attributes['mastra.tags'];\n }\n\n // Trace name: mastra.metadata.traceName → langfuse.trace.name\n if (attributes['mastra.metadata.traceName']) {\n attributes['langfuse.trace.name'] = attributes['mastra.metadata.traceName'];\n delete attributes['mastra.metadata.traceName'];\n }\n\n // Trace version: mastra.metadata.version → langfuse.trace.version\n if (attributes['mastra.metadata.version']) {\n attributes['langfuse.trace.version'] = attributes['mastra.metadata.version'];\n delete attributes['mastra.metadata.version'];\n }\n\n // Observation metadata: map semantic attributes to langfuse.observation.metadata.*\n // so they become top-level filterable keys on each observation in Langfuse.\n // @see https://langfuse.com/integrations/native/opentelemetry#how-metadata-mapping-works\n if (attributes['gen_ai.agent.id']) {\n attributes['langfuse.observation.metadata.agentId'] = attributes['gen_ai.agent.id'];\n }\n if (attributes['gen_ai.agent.name']) {\n attributes['langfuse.observation.metadata.agentName'] = attributes['gen_ai.agent.name'];\n }\n if (attributes['mastra.span.type']) {\n attributes['langfuse.observation.metadata.spanType'] = attributes['mastra.span.type'];\n }\n if (attributes['gen_ai.operation.name']) {\n attributes['langfuse.observation.metadata.operationName'] = attributes['gen_ai.operation.name'];\n }\n\n // Input/Output: mastra.*.input/output → langfuse.observation.input/output\n // For gen_ai spans, Langfuse reads gen_ai.input.messages natively.\n // For non-gen_ai spans, we map the first mastra.*.input/output we find.\n if (!attributes['gen_ai.input.messages'] && !attributes['gen_ai.tool.call.arguments']) {\n for (const key of Object.keys(attributes)) {\n if (key.startsWith('mastra.') && key.endsWith('.input')) {\n attributes['langfuse.observation.input'] = attributes[key];\n break;\n }\n }\n }\n if (!attributes['gen_ai.output.messages'] && !attributes['gen_ai.tool.call.result']) {\n for (const key of Object.keys(attributes)) {\n if (key.startsWith('mastra.') && key.endsWith('.output')) {\n attributes['langfuse.observation.output'] = attributes[key];\n break;\n }\n }\n }\n}\n\n/**\n * Remove trailing \"/\" characters procedurally. Avoids polynomial\n * backtracking that a greedy regex like `/\\/+$/` can exhibit when the\n * input is attacker-controlled.\n */\nfunction stripTrailingSlashes(s: string): string {\n let end = s.length;\n while (end > 0 && s.charCodeAt(end - 1) === 47 /* \"/\" */) {\n end--;\n }\n return end === s.length ? s : s.slice(0, end);\n}\n","/**\n * Langfuse Tracing Options Helpers\n *\n * These helpers integrate with the `buildTracingOptions` pattern from\n * `@mastra/observability` to add Langfuse-specific tracing features.\n *\n * @example\n * ```typescript\n * import { buildTracingOptions } from '@mastra/observability';\n * import { withLangfusePrompt } from '@mastra/langfuse';\n *\n * const agent = new Agent({\n * defaultGenerateOptions: {\n * tracingOptions: buildTracingOptions(\n * withLangfusePrompt({ name: 'my-prompt', version: 1 }),\n * ),\n * },\n * });\n * ```\n */\n\nimport type { TracingOptionsUpdater } from '@mastra/observability';\n\n/**\n * Langfuse prompt input - accepts either a Langfuse SDK prompt object\n * or manual fields.\n */\nexport interface LangfusePromptInput {\n /** Prompt name */\n name?: string;\n /** Prompt version */\n version?: number;\n /** @deprecated Langfuse v5 only supports linking by name + version. This field is ignored. */\n id?: string;\n}\n\n/**\n * Adds Langfuse prompt metadata to the tracing options\n * to enable Langfuse Prompt Tracing.\n *\n * The metadata is added under `metadata.langfuse.prompt` and includes:\n * - `name` - Prompt name (required for Langfuse v5)\n * - `version` - Prompt version (required for Langfuse v5)\n *\n * All fields are deeply merged with any existing metadata.\n *\n * @param prompt - Prompt fields for linking (`name` and `version` required)\n * @returns A TracingOptionsUpdater function for use with `buildTracingOptions`\n *\n * @example\n * ```typescript\n * import { buildTracingOptions } from '@mastra/observability';\n * import { withLangfusePrompt } from '@mastra/langfuse';\n *\n * // Link a generation to a Langfuse prompt by name and version\n * const tracingOptions = buildTracingOptions(\n * withLangfusePrompt({ name: 'customer-support', version: 1 }),\n * );\n *\n * // Or directly in agent config\n * const agent = new Agent({\n * name: 'support-agent',\n * instructions: 'You are a helpful assistant',\n * model: openai('gpt-4o'),\n * defaultGenerateOptions: {\n * tracingOptions: buildTracingOptions(\n * withLangfusePrompt({ name: 'my-prompt', version: 1 }),\n * ),\n * },\n * });\n * ```\n */\nexport function withLangfusePrompt(prompt: LangfusePromptInput): TracingOptionsUpdater {\n return opts => ({\n ...opts,\n metadata: {\n ...opts.metadata,\n langfuse: {\n ...(opts.metadata?.langfuse as Record<string, unknown>),\n prompt: {\n ...(prompt.name !== undefined && { name: prompt.name }),\n ...(prompt.version !== undefined && { version: prompt.version }),\n ...(prompt.id !== undefined && { id: prompt.id }),\n },\n },\n },\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/tracing.ts","../src/helpers.ts"],"names":["BaseExporter","LangfuseSpanProcessor","LangfuseClient","SpanConverter","TracingEventType","SpanType"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Langfuse Exporter for Mastra Observability\n *\n * Sends observability data to Langfuse using the official @langfuse/otel span processor\n * and @langfuse/client for non-tracing features (scoring, prompt management, evaluations).\n *\n * @see https://langfuse.com/docs/observability/sdk/typescript/overview\n */\n\nimport { LangfuseClient } from '@langfuse/client';\nimport { LangfuseSpanProcessor } from '@langfuse/otel';\nimport type { TracingEvent, AnyExportedSpan, InitExporterOptions, ScoreEvent } from '@mastra/core/observability';\nimport { SpanType, TracingEventType } from '@mastra/core/observability';\nimport { BaseExporter } from '@mastra/observability';\nimport type { BaseExporterConfig } from '@mastra/observability';\nimport { SpanConverter } from '@mastra/otel-exporter';\n\nconst LOG_PREFIX = '[LangfuseExporter]';\n\nexport const LANGFUSE_DEFAULT_BASE_URL = 'https://cloud.langfuse.com';\n\nexport interface LangfuseExporterConfig extends BaseExporterConfig {\n /** Langfuse public key */\n publicKey?: string;\n /** Langfuse secret key */\n secretKey?: string;\n /** Langfuse host URL (defaults to https://cloud.langfuse.com) */\n baseUrl?: string;\n /** Enable realtime mode - flushes after each event for immediate visibility */\n realtime?: boolean;\n /** Maximum number of spans per OTEL export batch */\n flushAt?: number;\n /** Maximum time in seconds before pending spans are exported */\n flushInterval?: number;\n /** Langfuse environment tag for traces */\n environment?: string;\n /** Langfuse release tag for traces */\n release?: string;\n}\n\nexport class LangfuseExporter extends BaseExporter {\n name = 'langfuse';\n #processor: LangfuseSpanProcessor | undefined;\n #client: LangfuseClient | undefined;\n #spanConverter: SpanConverter | undefined;\n #realtime: boolean;\n #environment: string | undefined;\n #release: string | undefined;\n\n constructor(config: LangfuseExporterConfig = {}) {\n super(config);\n\n const publicKey = config.publicKey ?? process.env.LANGFUSE_PUBLIC_KEY;\n const secretKey = config.secretKey ?? process.env.LANGFUSE_SECRET_KEY;\n const baseUrl = stripTrailingSlashes(config.baseUrl ?? process.env.LANGFUSE_BASE_URL ?? LANGFUSE_DEFAULT_BASE_URL);\n this.#realtime = config.realtime ?? false;\n\n if (!publicKey || !secretKey) {\n const publicKeySource = config.publicKey\n ? 'from config'\n : process.env.LANGFUSE_PUBLIC_KEY\n ? 'from env'\n : 'missing';\n const secretKeySource = config.secretKey\n ? 'from config'\n : process.env.LANGFUSE_SECRET_KEY\n ? 'from env'\n : 'missing';\n this.setDisabled(\n `${LOG_PREFIX} Missing required credentials (publicKey: ${publicKeySource}, secretKey: ${secretKeySource}). ` +\n `Set LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY environment variables or pass them in config.`,\n );\n return;\n }\n\n this.#processor = new LangfuseSpanProcessor({\n publicKey,\n secretKey,\n baseUrl,\n environment: config.environment,\n release: config.release,\n exportMode: this.#realtime ? 'immediate' : 'batched',\n flushAt: config.flushAt,\n flushInterval: config.flushInterval,\n // Export all spans — the default filter only passes spans with gen_ai.* attributes\n // or known LLM instrumentation scopes, but Mastra spans use mastra.* attributes.\n shouldExportSpan: () => true,\n });\n\n this.#client = new LangfuseClient({\n publicKey,\n secretKey,\n baseUrl,\n });\n\n this.#environment = config.environment ?? process.env.LANGFUSE_TRACING_ENVIRONMENT;\n this.#release = config.release ?? process.env.LANGFUSE_RELEASE;\n }\n\n init(options: InitExporterOptions) {\n this.#spanConverter = new SpanConverter({\n packageName: '@mastra/langfuse',\n serviceName: options.config?.serviceName,\n format: 'GenAI_v1_38_0',\n });\n }\n\n protected async _exportTracingEvent(event: TracingEvent): Promise<void> {\n if (event.type !== TracingEventType.SPAN_ENDED) return;\n if (!this.#processor) return;\n\n await this.exportSpan(event.exportedSpan);\n }\n\n private async exportSpan(span: AnyExportedSpan): Promise<void> {\n if (!this.#spanConverter) {\n // Fallback if init() was not called (e.g., standalone usage without Mastra)\n this.#spanConverter = new SpanConverter({\n packageName: '@mastra/langfuse',\n serviceName: 'mastra-service',\n format: 'GenAI_v1_38_0',\n });\n }\n\n try {\n const otelSpan = await this.#spanConverter.convertSpan(span);\n\n // Map mastra.* attributes to langfuse.* namespace so that Langfuse's OTLP\n // endpoint reads them correctly. SpanConverter produces mastra.* attributes,\n // but Langfuse only reads langfuse.* attributes for prompt linking, TTFT, etc.\n // @see https://langfuse.com/integrations/native/opentelemetry#property-mapping\n mapMastraToLangfuseAttributes(otelSpan.attributes, span, this.#environment, this.#release);\n\n this.#processor!.onEnd(otelSpan);\n } catch (error) {\n this.logger.error(`${LOG_PREFIX} Failed to export span ${span.id}:`, error);\n }\n }\n\n /**\n * The LangfuseClient instance for advanced Langfuse features.\n * Use this for prompt management, evaluations, datasets, and direct API access.\n */\n get client(): LangfuseClient | undefined {\n return this.#client;\n }\n\n /**\n * Submit a score to Langfuse. Used by both the new `onScoreEvent` path and the\n * deprecated `addScoreToTrace` wrapper.\n */\n private submitScore(args: {\n id: string;\n traceId: string;\n spanId?: string;\n name: string;\n value: number;\n comment?: string;\n metadata?: Record<string, unknown>;\n }): void {\n if (!this.#client) return;\n\n const { id, traceId, spanId, name, value, comment, metadata } = args;\n try {\n this.#client.score.create({\n id,\n traceId,\n ...(spanId ? { observationId: spanId } : {}),\n name,\n value,\n ...(comment ? { comment } : {}),\n ...(metadata ? { metadata } : {}),\n dataType: 'NUMERIC' as const,\n });\n } catch (error) {\n this.logger.error(`${LOG_PREFIX} Error submitting score`, {\n error,\n traceId,\n spanId,\n name,\n });\n }\n }\n\n async onScoreEvent(event: ScoreEvent): Promise<void> {\n const { score } = event;\n if (!score.traceId) return;\n this.submitScore({\n id: score.scoreId,\n traceId: score.traceId,\n spanId: score.spanId,\n name: score.scorerName ?? score.scorerId,\n value: score.score,\n comment: score.reason,\n metadata: score.metadata,\n });\n }\n\n /**\n * @deprecated Use the observability score event pipeline (`mastra.observability.addScore`)\n * instead. This method is preserved for backwards compatibility and forwards to the same\n * underlying client call as `onScoreEvent`.\n */\n async addScoreToTrace({\n traceId,\n spanId,\n score,\n reason,\n scorerName,\n metadata,\n }: {\n traceId: string;\n spanId?: string;\n score: number;\n reason?: string;\n scorerName: string;\n metadata?: Record<string, any>;\n }): Promise<void> {\n this.submitScore({\n id: `${traceId}-${spanId || ''}-${scorerName}`,\n traceId,\n spanId,\n name: scorerName,\n value: score,\n comment: reason,\n metadata,\n });\n }\n\n async flush(): Promise<void> {\n await Promise.all([this.#processor?.forceFlush(), this.#client?.flush()]);\n }\n\n async shutdown(): Promise<void> {\n await Promise.all([this.#processor?.shutdown(), this.#client?.shutdown()]);\n }\n}\n\n/**\n * Maps Mastra-specific OTel attributes to the langfuse.* namespace that\n * Langfuse's OTLP endpoint reads for prompt linking, TTFT, and other features.\n *\n * SpanConverter produces attributes like mastra.metadata.*, mastra.completion_start_time, etc.\n * Langfuse's OTLP server only reads langfuse.observation.prompt.name, langfuse.observation.completion_start_time, etc.\n *\n * This function mutates the attributes object in place.\n * @see https://langfuse.com/integrations/native/opentelemetry#property-mapping\n */\nfunction mapMastraToLangfuseAttributes(\n attributes: Record<string, any>,\n span: AnyExportedSpan,\n environment?: string,\n release?: string,\n): void {\n // Environment and release: set directly since onStart() is not called\n if (environment) {\n attributes['langfuse.environment'] = environment;\n }\n if (release) {\n attributes['langfuse.release'] = release;\n }\n\n // Prompt linking: mastra.metadata.langfuse → langfuse.observation.prompt.name / version\n const langfuseMetadata = attributes['mastra.metadata.langfuse'];\n if (langfuseMetadata) {\n try {\n const parsed = typeof langfuseMetadata === 'string' ? JSON.parse(langfuseMetadata) : langfuseMetadata;\n if (parsed?.prompt) {\n if (parsed.prompt.name !== undefined) {\n attributes['langfuse.observation.prompt.name'] = parsed.prompt.name;\n }\n if (parsed.prompt.version !== undefined) {\n attributes['langfuse.observation.prompt.version'] = parsed.prompt.version;\n }\n }\n } catch {\n // best effort — invalid JSON is silently ignored\n }\n delete attributes['mastra.metadata.langfuse'];\n }\n\n // TTFT: mastra.completion_start_time → langfuse.observation.completion_start_time\n if (attributes['mastra.completion_start_time']) {\n attributes['langfuse.observation.completion_start_time'] = attributes['mastra.completion_start_time'];\n delete attributes['mastra.completion_start_time'];\n }\n\n // User ID: mastra.metadata.userId → user.id\n if (attributes['mastra.metadata.userId']) {\n attributes['user.id'] = attributes['mastra.metadata.userId'];\n delete attributes['mastra.metadata.userId'];\n }\n\n // Session ID: mastra.metadata.sessionId or threadId → session.id\n const sessionId = attributes['mastra.metadata.sessionId'] ?? attributes['mastra.metadata.threadId'];\n if (sessionId) {\n attributes['session.id'] = sessionId;\n delete attributes['mastra.metadata.sessionId'];\n delete attributes['mastra.metadata.threadId'];\n }\n\n // Tags: mastra.tags → langfuse.trace.tags\n if (attributes['mastra.tags']) {\n attributes['langfuse.trace.tags'] = attributes['mastra.tags'];\n delete attributes['mastra.tags'];\n }\n\n // Trace name: mastra.metadata.traceName → langfuse.trace.name\n if (attributes['mastra.metadata.traceName']) {\n attributes['langfuse.trace.name'] = attributes['mastra.metadata.traceName'];\n delete attributes['mastra.metadata.traceName'];\n }\n\n // Trace version: mastra.metadata.version → langfuse.trace.version\n if (attributes['mastra.metadata.version']) {\n attributes['langfuse.trace.version'] = attributes['mastra.metadata.version'];\n delete attributes['mastra.metadata.version'];\n }\n\n // Root-span trace identity: scope each Langfuse trace to the entity that\n // started it (agent or workflow). This makes the Langfuse trace name match\n // the agent/workflow id and exposes the same identity as trace metadata, so\n // users can scope Langfuse evaluators per agent via trace name or metadata\n // filters. User-provided traceName (set via mastra.metadata.traceName) takes\n // precedence and is preserved.\n if (span.isRootSpan) {\n if (span.type === SpanType.AGENT_RUN) {\n if (!attributes['langfuse.trace.name'] && (span.entityName || span.entityId)) {\n attributes['langfuse.trace.name'] = span.entityName ?? span.entityId;\n }\n if (span.entityId) {\n attributes['langfuse.trace.metadata.agentId'] = span.entityId;\n }\n if (span.entityName) {\n attributes['langfuse.trace.metadata.agentName'] = span.entityName;\n }\n } else if (span.type === SpanType.WORKFLOW_RUN) {\n if (!attributes['langfuse.trace.name'] && (span.entityName || span.entityId)) {\n attributes['langfuse.trace.name'] = span.entityName ?? span.entityId;\n }\n if (span.entityId) {\n attributes['langfuse.trace.metadata.workflowId'] = span.entityId;\n }\n if (span.entityName) {\n attributes['langfuse.trace.metadata.workflowName'] = span.entityName;\n }\n }\n }\n\n // Observation metadata: map semantic attributes to langfuse.observation.metadata.*\n // so they become top-level filterable keys on each observation in Langfuse.\n // @see https://langfuse.com/integrations/native/opentelemetry#how-metadata-mapping-works\n if (attributes['gen_ai.agent.id']) {\n attributes['langfuse.observation.metadata.agentId'] = attributes['gen_ai.agent.id'];\n }\n if (attributes['gen_ai.agent.name']) {\n attributes['langfuse.observation.metadata.agentName'] = attributes['gen_ai.agent.name'];\n }\n if (attributes['mastra.span.type']) {\n attributes['langfuse.observation.metadata.spanType'] = attributes['mastra.span.type'];\n }\n if (attributes['gen_ai.operation.name']) {\n attributes['langfuse.observation.metadata.operationName'] = attributes['gen_ai.operation.name'];\n }\n\n // Input/Output: mastra.*.input/output → langfuse.observation.input/output\n // For gen_ai spans, Langfuse reads gen_ai.input.messages natively.\n // For non-gen_ai spans, we map the first mastra.*.input/output we find.\n if (!attributes['gen_ai.input.messages'] && !attributes['gen_ai.tool.call.arguments']) {\n for (const key of Object.keys(attributes)) {\n if (key.startsWith('mastra.') && key.endsWith('.input')) {\n attributes['langfuse.observation.input'] = attributes[key];\n break;\n }\n }\n }\n if (!attributes['gen_ai.output.messages'] && !attributes['gen_ai.tool.call.result']) {\n for (const key of Object.keys(attributes)) {\n if (key.startsWith('mastra.') && key.endsWith('.output')) {\n attributes['langfuse.observation.output'] = attributes[key];\n break;\n }\n }\n }\n}\n\n/**\n * Remove trailing \"/\" characters procedurally. Avoids polynomial\n * backtracking that a greedy regex like `/\\/+$/` can exhibit when the\n * input is attacker-controlled.\n */\nfunction stripTrailingSlashes(s: string): string {\n let end = s.length;\n while (end > 0 && s.charCodeAt(end - 1) === 47 /* \"/\" */) {\n end--;\n }\n return end === s.length ? s : s.slice(0, end);\n}\n","/**\n * Langfuse Tracing Options Helpers\n *\n * These helpers integrate with the `buildTracingOptions` pattern from\n * `@mastra/observability` to add Langfuse-specific tracing features.\n *\n * @example\n * ```typescript\n * import { buildTracingOptions } from '@mastra/observability';\n * import { withLangfusePrompt } from '@mastra/langfuse';\n *\n * const agent = new Agent({\n * defaultGenerateOptions: {\n * tracingOptions: buildTracingOptions(\n * withLangfusePrompt({ name: 'my-prompt', version: 1 }),\n * ),\n * },\n * });\n * ```\n */\n\nimport type { TracingOptionsUpdater } from '@mastra/observability';\n\n/**\n * Langfuse prompt input - accepts either a Langfuse SDK prompt object\n * or manual fields.\n */\nexport interface LangfusePromptInput {\n /** Prompt name */\n name?: string;\n /** Prompt version */\n version?: number;\n /** @deprecated Langfuse v5 only supports linking by name + version. This field is ignored. */\n id?: string;\n}\n\n/**\n * Adds Langfuse prompt metadata to the tracing options\n * to enable Langfuse Prompt Tracing.\n *\n * The metadata is added under `metadata.langfuse.prompt` and includes:\n * - `name` - Prompt name (required for Langfuse v5)\n * - `version` - Prompt version (required for Langfuse v5)\n *\n * All fields are deeply merged with any existing metadata.\n *\n * @param prompt - Prompt fields for linking (`name` and `version` required)\n * @returns A TracingOptionsUpdater function for use with `buildTracingOptions`\n *\n * @example\n * ```typescript\n * import { buildTracingOptions } from '@mastra/observability';\n * import { withLangfusePrompt } from '@mastra/langfuse';\n *\n * // Link a generation to a Langfuse prompt by name and version\n * const tracingOptions = buildTracingOptions(\n * withLangfusePrompt({ name: 'customer-support', version: 1 }),\n * );\n *\n * // Or directly in agent config\n * const agent = new Agent({\n * name: 'support-agent',\n * instructions: 'You are a helpful assistant',\n * model: openai('gpt-4o'),\n * defaultGenerateOptions: {\n * tracingOptions: buildTracingOptions(\n * withLangfusePrompt({ name: 'my-prompt', version: 1 }),\n * ),\n * },\n * });\n * ```\n */\nexport function withLangfusePrompt(prompt: LangfusePromptInput): TracingOptionsUpdater {\n return opts => ({\n ...opts,\n metadata: {\n ...opts.metadata,\n langfuse: {\n ...(opts.metadata?.langfuse as Record<string, unknown>),\n prompt: {\n ...(prompt.name !== undefined && { name: prompt.name }),\n ...(prompt.version !== undefined && { version: prompt.version }),\n ...(prompt.id !== undefined && { id: prompt.id }),\n },\n },\n },\n });\n}\n"]}
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { LangfuseClient } from '@langfuse/client';
2
2
  import { LangfuseSpanProcessor } from '@langfuse/otel';
3
- import { TracingEventType } from '@mastra/core/observability';
3
+ import { TracingEventType, SpanType } from '@mastra/core/observability';
4
4
  import { BaseExporter } from '@mastra/observability';
5
5
  import { SpanConverter } from '@mastra/otel-exporter';
6
6
 
@@ -72,7 +72,7 @@ var LangfuseExporter = class extends BaseExporter {
72
72
  }
73
73
  try {
74
74
  const otelSpan = await this.#spanConverter.convertSpan(span);
75
- mapMastraToLangfuseAttributes(otelSpan.attributes, this.#environment, this.#release);
75
+ mapMastraToLangfuseAttributes(otelSpan.attributes, span, this.#environment, this.#release);
76
76
  this.#processor.onEnd(otelSpan);
77
77
  } catch (error) {
78
78
  this.logger.error(`${LOG_PREFIX} Failed to export span ${span.id}:`, error);
@@ -155,7 +155,7 @@ var LangfuseExporter = class extends BaseExporter {
155
155
  await Promise.all([this.#processor?.shutdown(), this.#client?.shutdown()]);
156
156
  }
157
157
  };
158
- function mapMastraToLangfuseAttributes(attributes, environment, release) {
158
+ function mapMastraToLangfuseAttributes(attributes, span, environment, release) {
159
159
  if (environment) {
160
160
  attributes["langfuse.environment"] = environment;
161
161
  }
@@ -204,6 +204,29 @@ function mapMastraToLangfuseAttributes(attributes, environment, release) {
204
204
  attributes["langfuse.trace.version"] = attributes["mastra.metadata.version"];
205
205
  delete attributes["mastra.metadata.version"];
206
206
  }
207
+ if (span.isRootSpan) {
208
+ if (span.type === SpanType.AGENT_RUN) {
209
+ if (!attributes["langfuse.trace.name"] && (span.entityName || span.entityId)) {
210
+ attributes["langfuse.trace.name"] = span.entityName ?? span.entityId;
211
+ }
212
+ if (span.entityId) {
213
+ attributes["langfuse.trace.metadata.agentId"] = span.entityId;
214
+ }
215
+ if (span.entityName) {
216
+ attributes["langfuse.trace.metadata.agentName"] = span.entityName;
217
+ }
218
+ } else if (span.type === SpanType.WORKFLOW_RUN) {
219
+ if (!attributes["langfuse.trace.name"] && (span.entityName || span.entityId)) {
220
+ attributes["langfuse.trace.name"] = span.entityName ?? span.entityId;
221
+ }
222
+ if (span.entityId) {
223
+ attributes["langfuse.trace.metadata.workflowId"] = span.entityId;
224
+ }
225
+ if (span.entityName) {
226
+ attributes["langfuse.trace.metadata.workflowName"] = span.entityName;
227
+ }
228
+ }
229
+ }
207
230
  if (attributes["gen_ai.agent.id"]) {
208
231
  attributes["langfuse.observation.metadata.agentId"] = attributes["gen_ai.agent.id"];
209
232
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tracing.ts","../src/helpers.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Langfuse Exporter for Mastra Observability\n *\n * Sends observability data to Langfuse using the official @langfuse/otel span processor\n * and @langfuse/client for non-tracing features (scoring, prompt management, evaluations).\n *\n * @see https://langfuse.com/docs/observability/sdk/typescript/overview\n */\n\nimport { LangfuseClient } from '@langfuse/client';\nimport { LangfuseSpanProcessor } from '@langfuse/otel';\nimport type { TracingEvent, AnyExportedSpan, InitExporterOptions, ScoreEvent } from '@mastra/core/observability';\nimport { TracingEventType } from '@mastra/core/observability';\nimport { BaseExporter } from '@mastra/observability';\nimport type { BaseExporterConfig } from '@mastra/observability';\nimport { SpanConverter } from '@mastra/otel-exporter';\n\nconst LOG_PREFIX = '[LangfuseExporter]';\n\nexport const LANGFUSE_DEFAULT_BASE_URL = 'https://cloud.langfuse.com';\n\nexport interface LangfuseExporterConfig extends BaseExporterConfig {\n /** Langfuse public key */\n publicKey?: string;\n /** Langfuse secret key */\n secretKey?: string;\n /** Langfuse host URL (defaults to https://cloud.langfuse.com) */\n baseUrl?: string;\n /** Enable realtime mode - flushes after each event for immediate visibility */\n realtime?: boolean;\n /** Maximum number of spans per OTEL export batch */\n flushAt?: number;\n /** Maximum time in seconds before pending spans are exported */\n flushInterval?: number;\n /** Langfuse environment tag for traces */\n environment?: string;\n /** Langfuse release tag for traces */\n release?: string;\n}\n\nexport class LangfuseExporter extends BaseExporter {\n name = 'langfuse';\n #processor: LangfuseSpanProcessor | undefined;\n #client: LangfuseClient | undefined;\n #spanConverter: SpanConverter | undefined;\n #realtime: boolean;\n #environment: string | undefined;\n #release: string | undefined;\n\n constructor(config: LangfuseExporterConfig = {}) {\n super(config);\n\n const publicKey = config.publicKey ?? process.env.LANGFUSE_PUBLIC_KEY;\n const secretKey = config.secretKey ?? process.env.LANGFUSE_SECRET_KEY;\n const baseUrl = stripTrailingSlashes(config.baseUrl ?? process.env.LANGFUSE_BASE_URL ?? LANGFUSE_DEFAULT_BASE_URL);\n this.#realtime = config.realtime ?? false;\n\n if (!publicKey || !secretKey) {\n const publicKeySource = config.publicKey\n ? 'from config'\n : process.env.LANGFUSE_PUBLIC_KEY\n ? 'from env'\n : 'missing';\n const secretKeySource = config.secretKey\n ? 'from config'\n : process.env.LANGFUSE_SECRET_KEY\n ? 'from env'\n : 'missing';\n this.setDisabled(\n `${LOG_PREFIX} Missing required credentials (publicKey: ${publicKeySource}, secretKey: ${secretKeySource}). ` +\n `Set LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY environment variables or pass them in config.`,\n );\n return;\n }\n\n this.#processor = new LangfuseSpanProcessor({\n publicKey,\n secretKey,\n baseUrl,\n environment: config.environment,\n release: config.release,\n exportMode: this.#realtime ? 'immediate' : 'batched',\n flushAt: config.flushAt,\n flushInterval: config.flushInterval,\n // Export all spans — the default filter only passes spans with gen_ai.* attributes\n // or known LLM instrumentation scopes, but Mastra spans use mastra.* attributes.\n shouldExportSpan: () => true,\n });\n\n this.#client = new LangfuseClient({\n publicKey,\n secretKey,\n baseUrl,\n });\n\n this.#environment = config.environment ?? process.env.LANGFUSE_TRACING_ENVIRONMENT;\n this.#release = config.release ?? process.env.LANGFUSE_RELEASE;\n }\n\n init(options: InitExporterOptions) {\n this.#spanConverter = new SpanConverter({\n packageName: '@mastra/langfuse',\n serviceName: options.config?.serviceName,\n format: 'GenAI_v1_38_0',\n });\n }\n\n protected async _exportTracingEvent(event: TracingEvent): Promise<void> {\n if (event.type !== TracingEventType.SPAN_ENDED) return;\n if (!this.#processor) return;\n\n await this.exportSpan(event.exportedSpan);\n }\n\n private async exportSpan(span: AnyExportedSpan): Promise<void> {\n if (!this.#spanConverter) {\n // Fallback if init() was not called (e.g., standalone usage without Mastra)\n this.#spanConverter = new SpanConverter({\n packageName: '@mastra/langfuse',\n serviceName: 'mastra-service',\n format: 'GenAI_v1_38_0',\n });\n }\n\n try {\n const otelSpan = await this.#spanConverter.convertSpan(span);\n\n // Map mastra.* attributes to langfuse.* namespace so that Langfuse's OTLP\n // endpoint reads them correctly. SpanConverter produces mastra.* attributes,\n // but Langfuse only reads langfuse.* attributes for prompt linking, TTFT, etc.\n // @see https://langfuse.com/integrations/native/opentelemetry#property-mapping\n mapMastraToLangfuseAttributes(otelSpan.attributes, this.#environment, this.#release);\n\n this.#processor!.onEnd(otelSpan);\n } catch (error) {\n this.logger.error(`${LOG_PREFIX} Failed to export span ${span.id}:`, error);\n }\n }\n\n /**\n * The LangfuseClient instance for advanced Langfuse features.\n * Use this for prompt management, evaluations, datasets, and direct API access.\n */\n get client(): LangfuseClient | undefined {\n return this.#client;\n }\n\n /**\n * Submit a score to Langfuse. Used by both the new `onScoreEvent` path and the\n * deprecated `addScoreToTrace` wrapper.\n */\n private submitScore(args: {\n id: string;\n traceId: string;\n spanId?: string;\n name: string;\n value: number;\n comment?: string;\n metadata?: Record<string, unknown>;\n }): void {\n if (!this.#client) return;\n\n const { id, traceId, spanId, name, value, comment, metadata } = args;\n try {\n this.#client.score.create({\n id,\n traceId,\n ...(spanId ? { observationId: spanId } : {}),\n name,\n value,\n ...(comment ? { comment } : {}),\n ...(metadata ? { metadata } : {}),\n dataType: 'NUMERIC' as const,\n });\n } catch (error) {\n this.logger.error(`${LOG_PREFIX} Error submitting score`, {\n error,\n traceId,\n spanId,\n name,\n });\n }\n }\n\n async onScoreEvent(event: ScoreEvent): Promise<void> {\n const { score } = event;\n if (!score.traceId) return;\n this.submitScore({\n id: score.scoreId,\n traceId: score.traceId,\n spanId: score.spanId,\n name: score.scorerName ?? score.scorerId,\n value: score.score,\n comment: score.reason,\n metadata: score.metadata,\n });\n }\n\n /**\n * @deprecated Use the observability score event pipeline (`mastra.observability.addScore`)\n * instead. This method is preserved for backwards compatibility and forwards to the same\n * underlying client call as `onScoreEvent`.\n */\n async addScoreToTrace({\n traceId,\n spanId,\n score,\n reason,\n scorerName,\n metadata,\n }: {\n traceId: string;\n spanId?: string;\n score: number;\n reason?: string;\n scorerName: string;\n metadata?: Record<string, any>;\n }): Promise<void> {\n this.submitScore({\n id: `${traceId}-${spanId || ''}-${scorerName}`,\n traceId,\n spanId,\n name: scorerName,\n value: score,\n comment: reason,\n metadata,\n });\n }\n\n async flush(): Promise<void> {\n await Promise.all([this.#processor?.forceFlush(), this.#client?.flush()]);\n }\n\n async shutdown(): Promise<void> {\n await Promise.all([this.#processor?.shutdown(), this.#client?.shutdown()]);\n }\n}\n\n/**\n * Maps Mastra-specific OTel attributes to the langfuse.* namespace that\n * Langfuse's OTLP endpoint reads for prompt linking, TTFT, and other features.\n *\n * SpanConverter produces attributes like mastra.metadata.*, mastra.completion_start_time, etc.\n * Langfuse's OTLP server only reads langfuse.observation.prompt.name, langfuse.observation.completion_start_time, etc.\n *\n * This function mutates the attributes object in place.\n * @see https://langfuse.com/integrations/native/opentelemetry#property-mapping\n */\nfunction mapMastraToLangfuseAttributes(attributes: Record<string, any>, environment?: string, release?: string): void {\n // Environment and release: set directly since onStart() is not called\n if (environment) {\n attributes['langfuse.environment'] = environment;\n }\n if (release) {\n attributes['langfuse.release'] = release;\n }\n\n // Prompt linking: mastra.metadata.langfuse → langfuse.observation.prompt.name / version\n const langfuseMetadata = attributes['mastra.metadata.langfuse'];\n if (langfuseMetadata) {\n try {\n const parsed = typeof langfuseMetadata === 'string' ? JSON.parse(langfuseMetadata) : langfuseMetadata;\n if (parsed?.prompt) {\n if (parsed.prompt.name !== undefined) {\n attributes['langfuse.observation.prompt.name'] = parsed.prompt.name;\n }\n if (parsed.prompt.version !== undefined) {\n attributes['langfuse.observation.prompt.version'] = parsed.prompt.version;\n }\n }\n } catch {\n // best effort — invalid JSON is silently ignored\n }\n delete attributes['mastra.metadata.langfuse'];\n }\n\n // TTFT: mastra.completion_start_time → langfuse.observation.completion_start_time\n if (attributes['mastra.completion_start_time']) {\n attributes['langfuse.observation.completion_start_time'] = attributes['mastra.completion_start_time'];\n delete attributes['mastra.completion_start_time'];\n }\n\n // User ID: mastra.metadata.userId → user.id\n if (attributes['mastra.metadata.userId']) {\n attributes['user.id'] = attributes['mastra.metadata.userId'];\n delete attributes['mastra.metadata.userId'];\n }\n\n // Session ID: mastra.metadata.sessionId or threadId → session.id\n const sessionId = attributes['mastra.metadata.sessionId'] ?? attributes['mastra.metadata.threadId'];\n if (sessionId) {\n attributes['session.id'] = sessionId;\n delete attributes['mastra.metadata.sessionId'];\n delete attributes['mastra.metadata.threadId'];\n }\n\n // Tags: mastra.tags → langfuse.trace.tags\n if (attributes['mastra.tags']) {\n attributes['langfuse.trace.tags'] = attributes['mastra.tags'];\n delete attributes['mastra.tags'];\n }\n\n // Trace name: mastra.metadata.traceName → langfuse.trace.name\n if (attributes['mastra.metadata.traceName']) {\n attributes['langfuse.trace.name'] = attributes['mastra.metadata.traceName'];\n delete attributes['mastra.metadata.traceName'];\n }\n\n // Trace version: mastra.metadata.version → langfuse.trace.version\n if (attributes['mastra.metadata.version']) {\n attributes['langfuse.trace.version'] = attributes['mastra.metadata.version'];\n delete attributes['mastra.metadata.version'];\n }\n\n // Observation metadata: map semantic attributes to langfuse.observation.metadata.*\n // so they become top-level filterable keys on each observation in Langfuse.\n // @see https://langfuse.com/integrations/native/opentelemetry#how-metadata-mapping-works\n if (attributes['gen_ai.agent.id']) {\n attributes['langfuse.observation.metadata.agentId'] = attributes['gen_ai.agent.id'];\n }\n if (attributes['gen_ai.agent.name']) {\n attributes['langfuse.observation.metadata.agentName'] = attributes['gen_ai.agent.name'];\n }\n if (attributes['mastra.span.type']) {\n attributes['langfuse.observation.metadata.spanType'] = attributes['mastra.span.type'];\n }\n if (attributes['gen_ai.operation.name']) {\n attributes['langfuse.observation.metadata.operationName'] = attributes['gen_ai.operation.name'];\n }\n\n // Input/Output: mastra.*.input/output → langfuse.observation.input/output\n // For gen_ai spans, Langfuse reads gen_ai.input.messages natively.\n // For non-gen_ai spans, we map the first mastra.*.input/output we find.\n if (!attributes['gen_ai.input.messages'] && !attributes['gen_ai.tool.call.arguments']) {\n for (const key of Object.keys(attributes)) {\n if (key.startsWith('mastra.') && key.endsWith('.input')) {\n attributes['langfuse.observation.input'] = attributes[key];\n break;\n }\n }\n }\n if (!attributes['gen_ai.output.messages'] && !attributes['gen_ai.tool.call.result']) {\n for (const key of Object.keys(attributes)) {\n if (key.startsWith('mastra.') && key.endsWith('.output')) {\n attributes['langfuse.observation.output'] = attributes[key];\n break;\n }\n }\n }\n}\n\n/**\n * Remove trailing \"/\" characters procedurally. Avoids polynomial\n * backtracking that a greedy regex like `/\\/+$/` can exhibit when the\n * input is attacker-controlled.\n */\nfunction stripTrailingSlashes(s: string): string {\n let end = s.length;\n while (end > 0 && s.charCodeAt(end - 1) === 47 /* \"/\" */) {\n end--;\n }\n return end === s.length ? s : s.slice(0, end);\n}\n","/**\n * Langfuse Tracing Options Helpers\n *\n * These helpers integrate with the `buildTracingOptions` pattern from\n * `@mastra/observability` to add Langfuse-specific tracing features.\n *\n * @example\n * ```typescript\n * import { buildTracingOptions } from '@mastra/observability';\n * import { withLangfusePrompt } from '@mastra/langfuse';\n *\n * const agent = new Agent({\n * defaultGenerateOptions: {\n * tracingOptions: buildTracingOptions(\n * withLangfusePrompt({ name: 'my-prompt', version: 1 }),\n * ),\n * },\n * });\n * ```\n */\n\nimport type { TracingOptionsUpdater } from '@mastra/observability';\n\n/**\n * Langfuse prompt input - accepts either a Langfuse SDK prompt object\n * or manual fields.\n */\nexport interface LangfusePromptInput {\n /** Prompt name */\n name?: string;\n /** Prompt version */\n version?: number;\n /** @deprecated Langfuse v5 only supports linking by name + version. This field is ignored. */\n id?: string;\n}\n\n/**\n * Adds Langfuse prompt metadata to the tracing options\n * to enable Langfuse Prompt Tracing.\n *\n * The metadata is added under `metadata.langfuse.prompt` and includes:\n * - `name` - Prompt name (required for Langfuse v5)\n * - `version` - Prompt version (required for Langfuse v5)\n *\n * All fields are deeply merged with any existing metadata.\n *\n * @param prompt - Prompt fields for linking (`name` and `version` required)\n * @returns A TracingOptionsUpdater function for use with `buildTracingOptions`\n *\n * @example\n * ```typescript\n * import { buildTracingOptions } from '@mastra/observability';\n * import { withLangfusePrompt } from '@mastra/langfuse';\n *\n * // Link a generation to a Langfuse prompt by name and version\n * const tracingOptions = buildTracingOptions(\n * withLangfusePrompt({ name: 'customer-support', version: 1 }),\n * );\n *\n * // Or directly in agent config\n * const agent = new Agent({\n * name: 'support-agent',\n * instructions: 'You are a helpful assistant',\n * model: openai('gpt-4o'),\n * defaultGenerateOptions: {\n * tracingOptions: buildTracingOptions(\n * withLangfusePrompt({ name: 'my-prompt', version: 1 }),\n * ),\n * },\n * });\n * ```\n */\nexport function withLangfusePrompt(prompt: LangfusePromptInput): TracingOptionsUpdater {\n return opts => ({\n ...opts,\n metadata: {\n ...opts.metadata,\n langfuse: {\n ...(opts.metadata?.langfuse as Record<string, unknown>),\n prompt: {\n ...(prompt.name !== undefined && { name: prompt.name }),\n ...(prompt.version !== undefined && { version: prompt.version }),\n ...(prompt.id !== undefined && { id: prompt.id }),\n },\n },\n },\n });\n}\n"]}
1
+ {"version":3,"sources":["../src/tracing.ts","../src/helpers.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Langfuse Exporter for Mastra Observability\n *\n * Sends observability data to Langfuse using the official @langfuse/otel span processor\n * and @langfuse/client for non-tracing features (scoring, prompt management, evaluations).\n *\n * @see https://langfuse.com/docs/observability/sdk/typescript/overview\n */\n\nimport { LangfuseClient } from '@langfuse/client';\nimport { LangfuseSpanProcessor } from '@langfuse/otel';\nimport type { TracingEvent, AnyExportedSpan, InitExporterOptions, ScoreEvent } from '@mastra/core/observability';\nimport { SpanType, TracingEventType } from '@mastra/core/observability';\nimport { BaseExporter } from '@mastra/observability';\nimport type { BaseExporterConfig } from '@mastra/observability';\nimport { SpanConverter } from '@mastra/otel-exporter';\n\nconst LOG_PREFIX = '[LangfuseExporter]';\n\nexport const LANGFUSE_DEFAULT_BASE_URL = 'https://cloud.langfuse.com';\n\nexport interface LangfuseExporterConfig extends BaseExporterConfig {\n /** Langfuse public key */\n publicKey?: string;\n /** Langfuse secret key */\n secretKey?: string;\n /** Langfuse host URL (defaults to https://cloud.langfuse.com) */\n baseUrl?: string;\n /** Enable realtime mode - flushes after each event for immediate visibility */\n realtime?: boolean;\n /** Maximum number of spans per OTEL export batch */\n flushAt?: number;\n /** Maximum time in seconds before pending spans are exported */\n flushInterval?: number;\n /** Langfuse environment tag for traces */\n environment?: string;\n /** Langfuse release tag for traces */\n release?: string;\n}\n\nexport class LangfuseExporter extends BaseExporter {\n name = 'langfuse';\n #processor: LangfuseSpanProcessor | undefined;\n #client: LangfuseClient | undefined;\n #spanConverter: SpanConverter | undefined;\n #realtime: boolean;\n #environment: string | undefined;\n #release: string | undefined;\n\n constructor(config: LangfuseExporterConfig = {}) {\n super(config);\n\n const publicKey = config.publicKey ?? process.env.LANGFUSE_PUBLIC_KEY;\n const secretKey = config.secretKey ?? process.env.LANGFUSE_SECRET_KEY;\n const baseUrl = stripTrailingSlashes(config.baseUrl ?? process.env.LANGFUSE_BASE_URL ?? LANGFUSE_DEFAULT_BASE_URL);\n this.#realtime = config.realtime ?? false;\n\n if (!publicKey || !secretKey) {\n const publicKeySource = config.publicKey\n ? 'from config'\n : process.env.LANGFUSE_PUBLIC_KEY\n ? 'from env'\n : 'missing';\n const secretKeySource = config.secretKey\n ? 'from config'\n : process.env.LANGFUSE_SECRET_KEY\n ? 'from env'\n : 'missing';\n this.setDisabled(\n `${LOG_PREFIX} Missing required credentials (publicKey: ${publicKeySource}, secretKey: ${secretKeySource}). ` +\n `Set LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY environment variables or pass them in config.`,\n );\n return;\n }\n\n this.#processor = new LangfuseSpanProcessor({\n publicKey,\n secretKey,\n baseUrl,\n environment: config.environment,\n release: config.release,\n exportMode: this.#realtime ? 'immediate' : 'batched',\n flushAt: config.flushAt,\n flushInterval: config.flushInterval,\n // Export all spans — the default filter only passes spans with gen_ai.* attributes\n // or known LLM instrumentation scopes, but Mastra spans use mastra.* attributes.\n shouldExportSpan: () => true,\n });\n\n this.#client = new LangfuseClient({\n publicKey,\n secretKey,\n baseUrl,\n });\n\n this.#environment = config.environment ?? process.env.LANGFUSE_TRACING_ENVIRONMENT;\n this.#release = config.release ?? process.env.LANGFUSE_RELEASE;\n }\n\n init(options: InitExporterOptions) {\n this.#spanConverter = new SpanConverter({\n packageName: '@mastra/langfuse',\n serviceName: options.config?.serviceName,\n format: 'GenAI_v1_38_0',\n });\n }\n\n protected async _exportTracingEvent(event: TracingEvent): Promise<void> {\n if (event.type !== TracingEventType.SPAN_ENDED) return;\n if (!this.#processor) return;\n\n await this.exportSpan(event.exportedSpan);\n }\n\n private async exportSpan(span: AnyExportedSpan): Promise<void> {\n if (!this.#spanConverter) {\n // Fallback if init() was not called (e.g., standalone usage without Mastra)\n this.#spanConverter = new SpanConverter({\n packageName: '@mastra/langfuse',\n serviceName: 'mastra-service',\n format: 'GenAI_v1_38_0',\n });\n }\n\n try {\n const otelSpan = await this.#spanConverter.convertSpan(span);\n\n // Map mastra.* attributes to langfuse.* namespace so that Langfuse's OTLP\n // endpoint reads them correctly. SpanConverter produces mastra.* attributes,\n // but Langfuse only reads langfuse.* attributes for prompt linking, TTFT, etc.\n // @see https://langfuse.com/integrations/native/opentelemetry#property-mapping\n mapMastraToLangfuseAttributes(otelSpan.attributes, span, this.#environment, this.#release);\n\n this.#processor!.onEnd(otelSpan);\n } catch (error) {\n this.logger.error(`${LOG_PREFIX} Failed to export span ${span.id}:`, error);\n }\n }\n\n /**\n * The LangfuseClient instance for advanced Langfuse features.\n * Use this for prompt management, evaluations, datasets, and direct API access.\n */\n get client(): LangfuseClient | undefined {\n return this.#client;\n }\n\n /**\n * Submit a score to Langfuse. Used by both the new `onScoreEvent` path and the\n * deprecated `addScoreToTrace` wrapper.\n */\n private submitScore(args: {\n id: string;\n traceId: string;\n spanId?: string;\n name: string;\n value: number;\n comment?: string;\n metadata?: Record<string, unknown>;\n }): void {\n if (!this.#client) return;\n\n const { id, traceId, spanId, name, value, comment, metadata } = args;\n try {\n this.#client.score.create({\n id,\n traceId,\n ...(spanId ? { observationId: spanId } : {}),\n name,\n value,\n ...(comment ? { comment } : {}),\n ...(metadata ? { metadata } : {}),\n dataType: 'NUMERIC' as const,\n });\n } catch (error) {\n this.logger.error(`${LOG_PREFIX} Error submitting score`, {\n error,\n traceId,\n spanId,\n name,\n });\n }\n }\n\n async onScoreEvent(event: ScoreEvent): Promise<void> {\n const { score } = event;\n if (!score.traceId) return;\n this.submitScore({\n id: score.scoreId,\n traceId: score.traceId,\n spanId: score.spanId,\n name: score.scorerName ?? score.scorerId,\n value: score.score,\n comment: score.reason,\n metadata: score.metadata,\n });\n }\n\n /**\n * @deprecated Use the observability score event pipeline (`mastra.observability.addScore`)\n * instead. This method is preserved for backwards compatibility and forwards to the same\n * underlying client call as `onScoreEvent`.\n */\n async addScoreToTrace({\n traceId,\n spanId,\n score,\n reason,\n scorerName,\n metadata,\n }: {\n traceId: string;\n spanId?: string;\n score: number;\n reason?: string;\n scorerName: string;\n metadata?: Record<string, any>;\n }): Promise<void> {\n this.submitScore({\n id: `${traceId}-${spanId || ''}-${scorerName}`,\n traceId,\n spanId,\n name: scorerName,\n value: score,\n comment: reason,\n metadata,\n });\n }\n\n async flush(): Promise<void> {\n await Promise.all([this.#processor?.forceFlush(), this.#client?.flush()]);\n }\n\n async shutdown(): Promise<void> {\n await Promise.all([this.#processor?.shutdown(), this.#client?.shutdown()]);\n }\n}\n\n/**\n * Maps Mastra-specific OTel attributes to the langfuse.* namespace that\n * Langfuse's OTLP endpoint reads for prompt linking, TTFT, and other features.\n *\n * SpanConverter produces attributes like mastra.metadata.*, mastra.completion_start_time, etc.\n * Langfuse's OTLP server only reads langfuse.observation.prompt.name, langfuse.observation.completion_start_time, etc.\n *\n * This function mutates the attributes object in place.\n * @see https://langfuse.com/integrations/native/opentelemetry#property-mapping\n */\nfunction mapMastraToLangfuseAttributes(\n attributes: Record<string, any>,\n span: AnyExportedSpan,\n environment?: string,\n release?: string,\n): void {\n // Environment and release: set directly since onStart() is not called\n if (environment) {\n attributes['langfuse.environment'] = environment;\n }\n if (release) {\n attributes['langfuse.release'] = release;\n }\n\n // Prompt linking: mastra.metadata.langfuse → langfuse.observation.prompt.name / version\n const langfuseMetadata = attributes['mastra.metadata.langfuse'];\n if (langfuseMetadata) {\n try {\n const parsed = typeof langfuseMetadata === 'string' ? JSON.parse(langfuseMetadata) : langfuseMetadata;\n if (parsed?.prompt) {\n if (parsed.prompt.name !== undefined) {\n attributes['langfuse.observation.prompt.name'] = parsed.prompt.name;\n }\n if (parsed.prompt.version !== undefined) {\n attributes['langfuse.observation.prompt.version'] = parsed.prompt.version;\n }\n }\n } catch {\n // best effort — invalid JSON is silently ignored\n }\n delete attributes['mastra.metadata.langfuse'];\n }\n\n // TTFT: mastra.completion_start_time → langfuse.observation.completion_start_time\n if (attributes['mastra.completion_start_time']) {\n attributes['langfuse.observation.completion_start_time'] = attributes['mastra.completion_start_time'];\n delete attributes['mastra.completion_start_time'];\n }\n\n // User ID: mastra.metadata.userId → user.id\n if (attributes['mastra.metadata.userId']) {\n attributes['user.id'] = attributes['mastra.metadata.userId'];\n delete attributes['mastra.metadata.userId'];\n }\n\n // Session ID: mastra.metadata.sessionId or threadId → session.id\n const sessionId = attributes['mastra.metadata.sessionId'] ?? attributes['mastra.metadata.threadId'];\n if (sessionId) {\n attributes['session.id'] = sessionId;\n delete attributes['mastra.metadata.sessionId'];\n delete attributes['mastra.metadata.threadId'];\n }\n\n // Tags: mastra.tags → langfuse.trace.tags\n if (attributes['mastra.tags']) {\n attributes['langfuse.trace.tags'] = attributes['mastra.tags'];\n delete attributes['mastra.tags'];\n }\n\n // Trace name: mastra.metadata.traceName → langfuse.trace.name\n if (attributes['mastra.metadata.traceName']) {\n attributes['langfuse.trace.name'] = attributes['mastra.metadata.traceName'];\n delete attributes['mastra.metadata.traceName'];\n }\n\n // Trace version: mastra.metadata.version → langfuse.trace.version\n if (attributes['mastra.metadata.version']) {\n attributes['langfuse.trace.version'] = attributes['mastra.metadata.version'];\n delete attributes['mastra.metadata.version'];\n }\n\n // Root-span trace identity: scope each Langfuse trace to the entity that\n // started it (agent or workflow). This makes the Langfuse trace name match\n // the agent/workflow id and exposes the same identity as trace metadata, so\n // users can scope Langfuse evaluators per agent via trace name or metadata\n // filters. User-provided traceName (set via mastra.metadata.traceName) takes\n // precedence and is preserved.\n if (span.isRootSpan) {\n if (span.type === SpanType.AGENT_RUN) {\n if (!attributes['langfuse.trace.name'] && (span.entityName || span.entityId)) {\n attributes['langfuse.trace.name'] = span.entityName ?? span.entityId;\n }\n if (span.entityId) {\n attributes['langfuse.trace.metadata.agentId'] = span.entityId;\n }\n if (span.entityName) {\n attributes['langfuse.trace.metadata.agentName'] = span.entityName;\n }\n } else if (span.type === SpanType.WORKFLOW_RUN) {\n if (!attributes['langfuse.trace.name'] && (span.entityName || span.entityId)) {\n attributes['langfuse.trace.name'] = span.entityName ?? span.entityId;\n }\n if (span.entityId) {\n attributes['langfuse.trace.metadata.workflowId'] = span.entityId;\n }\n if (span.entityName) {\n attributes['langfuse.trace.metadata.workflowName'] = span.entityName;\n }\n }\n }\n\n // Observation metadata: map semantic attributes to langfuse.observation.metadata.*\n // so they become top-level filterable keys on each observation in Langfuse.\n // @see https://langfuse.com/integrations/native/opentelemetry#how-metadata-mapping-works\n if (attributes['gen_ai.agent.id']) {\n attributes['langfuse.observation.metadata.agentId'] = attributes['gen_ai.agent.id'];\n }\n if (attributes['gen_ai.agent.name']) {\n attributes['langfuse.observation.metadata.agentName'] = attributes['gen_ai.agent.name'];\n }\n if (attributes['mastra.span.type']) {\n attributes['langfuse.observation.metadata.spanType'] = attributes['mastra.span.type'];\n }\n if (attributes['gen_ai.operation.name']) {\n attributes['langfuse.observation.metadata.operationName'] = attributes['gen_ai.operation.name'];\n }\n\n // Input/Output: mastra.*.input/output → langfuse.observation.input/output\n // For gen_ai spans, Langfuse reads gen_ai.input.messages natively.\n // For non-gen_ai spans, we map the first mastra.*.input/output we find.\n if (!attributes['gen_ai.input.messages'] && !attributes['gen_ai.tool.call.arguments']) {\n for (const key of Object.keys(attributes)) {\n if (key.startsWith('mastra.') && key.endsWith('.input')) {\n attributes['langfuse.observation.input'] = attributes[key];\n break;\n }\n }\n }\n if (!attributes['gen_ai.output.messages'] && !attributes['gen_ai.tool.call.result']) {\n for (const key of Object.keys(attributes)) {\n if (key.startsWith('mastra.') && key.endsWith('.output')) {\n attributes['langfuse.observation.output'] = attributes[key];\n break;\n }\n }\n }\n}\n\n/**\n * Remove trailing \"/\" characters procedurally. Avoids polynomial\n * backtracking that a greedy regex like `/\\/+$/` can exhibit when the\n * input is attacker-controlled.\n */\nfunction stripTrailingSlashes(s: string): string {\n let end = s.length;\n while (end > 0 && s.charCodeAt(end - 1) === 47 /* \"/\" */) {\n end--;\n }\n return end === s.length ? s : s.slice(0, end);\n}\n","/**\n * Langfuse Tracing Options Helpers\n *\n * These helpers integrate with the `buildTracingOptions` pattern from\n * `@mastra/observability` to add Langfuse-specific tracing features.\n *\n * @example\n * ```typescript\n * import { buildTracingOptions } from '@mastra/observability';\n * import { withLangfusePrompt } from '@mastra/langfuse';\n *\n * const agent = new Agent({\n * defaultGenerateOptions: {\n * tracingOptions: buildTracingOptions(\n * withLangfusePrompt({ name: 'my-prompt', version: 1 }),\n * ),\n * },\n * });\n * ```\n */\n\nimport type { TracingOptionsUpdater } from '@mastra/observability';\n\n/**\n * Langfuse prompt input - accepts either a Langfuse SDK prompt object\n * or manual fields.\n */\nexport interface LangfusePromptInput {\n /** Prompt name */\n name?: string;\n /** Prompt version */\n version?: number;\n /** @deprecated Langfuse v5 only supports linking by name + version. This field is ignored. */\n id?: string;\n}\n\n/**\n * Adds Langfuse prompt metadata to the tracing options\n * to enable Langfuse Prompt Tracing.\n *\n * The metadata is added under `metadata.langfuse.prompt` and includes:\n * - `name` - Prompt name (required for Langfuse v5)\n * - `version` - Prompt version (required for Langfuse v5)\n *\n * All fields are deeply merged with any existing metadata.\n *\n * @param prompt - Prompt fields for linking (`name` and `version` required)\n * @returns A TracingOptionsUpdater function for use with `buildTracingOptions`\n *\n * @example\n * ```typescript\n * import { buildTracingOptions } from '@mastra/observability';\n * import { withLangfusePrompt } from '@mastra/langfuse';\n *\n * // Link a generation to a Langfuse prompt by name and version\n * const tracingOptions = buildTracingOptions(\n * withLangfusePrompt({ name: 'customer-support', version: 1 }),\n * );\n *\n * // Or directly in agent config\n * const agent = new Agent({\n * name: 'support-agent',\n * instructions: 'You are a helpful assistant',\n * model: openai('gpt-4o'),\n * defaultGenerateOptions: {\n * tracingOptions: buildTracingOptions(\n * withLangfusePrompt({ name: 'my-prompt', version: 1 }),\n * ),\n * },\n * });\n * ```\n */\nexport function withLangfusePrompt(prompt: LangfusePromptInput): TracingOptionsUpdater {\n return opts => ({\n ...opts,\n metadata: {\n ...opts.metadata,\n langfuse: {\n ...(opts.metadata?.langfuse as Record<string, unknown>),\n prompt: {\n ...(prompt.name !== undefined && { name: prompt.name }),\n ...(prompt.version !== undefined && { version: prompt.version }),\n ...(prompt.id !== undefined && { id: prompt.id }),\n },\n },\n },\n });\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/langfuse",
3
- "version": "1.3.0-alpha.1",
3
+ "version": "1.3.0-alpha.3",
4
4
  "description": "Langfuse observability provider for Mastra - uses official Langfuse v5 SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -26,8 +26,8 @@
26
26
  "dependencies": {
27
27
  "@langfuse/client": "^5.0.2",
28
28
  "@langfuse/otel": "^5.0.2",
29
- "@mastra/observability": "1.12.0-alpha.1",
30
- "@mastra/otel-exporter": "1.0.23-alpha.1"
29
+ "@mastra/observability": "1.12.0-alpha.2",
30
+ "@mastra/otel-exporter": "1.1.0-alpha.3"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@opentelemetry/api": "^1.9.1",
@@ -40,8 +40,8 @@
40
40
  "typescript": "^6.0.3",
41
41
  "vitest": "4.1.5",
42
42
  "@internal/lint": "0.0.92",
43
- "@internal/types-builder": "0.0.67",
44
- "@mastra/core": "1.33.0-alpha.7"
43
+ "@mastra/core": "1.33.0-alpha.9",
44
+ "@internal/types-builder": "0.0.67"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@mastra/core": ">=1.16.0-0 <2.0.0-0",