@mastra/langfuse 1.1.4-alpha.0 → 1.2.0-alpha.4

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,44 @@
1
1
  # @mastra/langfuse
2
2
 
3
+ ## 1.2.0-alpha.4
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [[`8613972`](https://github.com/mastra-ai/mastra/commit/86139723d82536d204f66a79a3a5d30af286a2e7), [`8786a61`](https://github.com/mastra-ai/mastra/commit/8786a61fa54ba265f85eeff9985ca39863d18bb6), [`8fb2405`](https://github.com/mastra-ai/mastra/commit/8fb2405138f2d208b7962ad03f121ca25bcc28c5)]:
8
+ - @mastra/observability@1.10.0-alpha.2
9
+ - @mastra/core@1.26.0-alpha.7
10
+ - @mastra/otel-exporter@1.0.17-alpha.2
11
+
12
+ ## 1.2.0-alpha.3
13
+
14
+ ### Patch Changes
15
+
16
+ - Updated dependencies [[`6315317`](https://github.com/mastra-ai/mastra/commit/63153175fe9a7b224e5be7c209bbebc01dd9b0d5), [`9d3b24b`](https://github.com/mastra-ai/mastra/commit/9d3b24b19407ae9c09586cf7766d38dc4dff4a69), [`11d8fb7`](https://github.com/mastra-ai/mastra/commit/11d8fb7056bd56e2e6680109c71c4436e06c0a81)]:
17
+ - @mastra/core@1.26.0-alpha.6
18
+ - @mastra/observability@1.9.2-alpha.1
19
+ - @mastra/otel-exporter@1.0.17-alpha.1
20
+
21
+ ## 1.2.0-alpha.2
22
+
23
+ ### Patch Changes
24
+
25
+ - Improved Langfuse trace batching for streamed runs by adding `flushAt` and `flushInterval` controls. ([#15460](https://github.com/mastra-ai/mastra/pull/15460))
26
+
27
+ ## 1.2.0-alpha.1
28
+
29
+ ### Minor Changes
30
+
31
+ - Added new attribute mappings to the Langfuse exporter so more Mastra attributes are filterable in Langfuse's UI. ([#15445](https://github.com/mastra-ai/mastra/pull/15445))
32
+
33
+ **Observation-level metadata** — `gen_ai.agent.id`, `gen_ai.agent.name`, `mastra.span.type`, and `gen_ai.operation.name` are now mapped to `langfuse.observation.metadata.*`, making them top-level filterable keys on each observation. This lets you scope Langfuse evaluators to specific agents or span types.
34
+
35
+ **Trace-level attributes** — `mastra.metadata.traceName` and `mastra.metadata.version` are now mapped to `langfuse.trace.name` and `langfuse.trace.version`, enabling custom trace names and version-based filtering.
36
+
37
+ ### Patch Changes
38
+
39
+ - Updated dependencies [[`0474c2b`](https://github.com/mastra-ai/mastra/commit/0474c2b2e7c7e1ad8691dca031284841391ff1ef), [`f607106`](https://github.com/mastra-ai/mastra/commit/f607106854c6416c4a07d4082604b9f66d047221), [`62919a6`](https://github.com/mastra-ai/mastra/commit/62919a6ee0fbf3779ad21a97b1ec6696515d5104), [`0fd90a2`](https://github.com/mastra-ai/mastra/commit/0fd90a215caf5fca8099c15a67ca03e4427747a3)]:
40
+ - @mastra/core@1.26.0-alpha.4
41
+
3
42
  ## 1.1.4-alpha.0
4
43
 
5
44
  ### Patch Changes
package/README.md CHANGED
@@ -58,6 +58,8 @@ const mastra = new Mastra({
58
58
  secretKey: 'sk-lf-...',
59
59
  baseUrl: 'https://cloud.langfuse.com', // Optional
60
60
  realtime: true, // Optional - flush after each event
61
+ flushAt: 200, // Optional - spans per OTEL batch
62
+ flushInterval: 15, // Optional - seconds between OTEL batch flushes
61
63
  }),
62
64
  ],
63
65
  },
@@ -68,20 +70,41 @@ const mastra = new Mastra({
68
70
 
69
71
  ### Configuration Options
70
72
 
71
- | Option | Type | Description |
72
- | ----------- | --------- | ---------------------------------------------------------------------------- |
73
- | `publicKey` | `string` | Langfuse public key. Defaults to `LANGFUSE_PUBLIC_KEY` env var |
74
- | `secretKey` | `string` | Langfuse secret key. Defaults to `LANGFUSE_SECRET_KEY` env var |
75
- | `baseUrl` | `string` | Langfuse host URL. Defaults to `LANGFUSE_BASE_URL` env var or Langfuse cloud |
76
- | `realtime` | `boolean` | Flush after each event for immediate visibility. Defaults to `false` |
77
- | `options` | `object` | Additional options to pass to the Langfuse client |
73
+ | Option | Type | Description |
74
+ | --------------- | --------- | ---------------------------------------------------------------------------- |
75
+ | `publicKey` | `string` | Langfuse public key. Defaults to `LANGFUSE_PUBLIC_KEY` env var |
76
+ | `secretKey` | `string` | Langfuse secret key. Defaults to `LANGFUSE_SECRET_KEY` env var |
77
+ | `baseUrl` | `string` | Langfuse host URL. Defaults to `LANGFUSE_BASE_URL` env var or Langfuse cloud |
78
+ | `realtime` | `boolean` | Flush after each event for immediate visibility. Defaults to `false` |
79
+ | `flushAt` | `number` | Maximum number of spans per OTEL export batch |
80
+ | `flushInterval` | `number` | Maximum time in seconds before pending spans are exported |
81
+ | `environment` | `string` | Langfuse tracing environment tag |
82
+ | `release` | `string` | Langfuse release tag |
83
+
84
+ ### High-Volume Streaming
85
+
86
+ For self-hosted Langfuse deployments under load, increase the OTEL batch size and flush interval to reduce request pressure:
87
+
88
+ ```typescript
89
+ new LangfuseExporter({
90
+ flushAt: 500,
91
+ flushInterval: 20,
92
+ });
93
+ ```
94
+
95
+ `flushAt` and `flushInterval` map directly to the upstream `LangfuseSpanProcessor` options, so you can cross-reference Langfuse OTEL documentation when tuning them.
96
+
97
+ To suppress high-volume `MODEL_CHUNK` spans, use the observability-level `excludeSpanTypes` option. See the [span filtering reference](https://mastra.ai/reference/observability/tracing/span-filtering) for details.
78
98
 
79
99
  ## Features
80
100
 
81
101
  ### Tracing
82
102
 
83
103
  - **Automatic span mapping**: Root spans become Langfuse traces
84
- - **Model generation support**: `MODEL_GENERATION` spans become Langfuse generations with token usage
85
- - **Type-specific metadata**: Extracts relevant metadata for each span type (agents, tools, workflows)
86
- - **Error tracking**: Automatic error status and message tracking
87
- - **Hierarchical traces**: Maintains parent-child relationships
104
+ - **Official Langfuse OTEL export**: Uses `@langfuse/otel` and `@langfuse/client`
105
+ - **Model generation support**: `MODEL_GENERATION` spans are mapped into Langfuse generations with usage data
106
+ - **Type-specific metadata**: Preserves agent, tool, workflow, and span metadata
107
+ - **Prompt linking and TTFT**: Maps Mastra tracing metadata into Langfuse OTEL attributes
108
+ - **Error tracking**: Preserves span failures and error details in exported traces
109
+ - **Hierarchical traces**: Maintains parent-child relationships across exported spans
110
+ - **Batch tuning for self-hosted deployments**: Exposes OTEL batch size and interval controls
package/dist/index.cjs CHANGED
@@ -38,6 +38,8 @@ var LangfuseExporter = class extends observability.BaseExporter {
38
38
  environment: config.environment,
39
39
  release: config.release,
40
40
  exportMode: this.#realtime ? "immediate" : "batched",
41
+ flushAt: config.flushAt,
42
+ flushInterval: config.flushInterval,
41
43
  // Export all spans — the default filter only passes spans with gen_ai.* attributes
42
44
  // or known LLM instrumentation scopes, but Mastra spans use mastra.* attributes.
43
45
  shouldExportSpan: () => true
@@ -165,6 +167,26 @@ function mapMastraToLangfuseAttributes(attributes, environment, release) {
165
167
  attributes["langfuse.trace.tags"] = attributes["mastra.tags"];
166
168
  delete attributes["mastra.tags"];
167
169
  }
170
+ if (attributes["mastra.metadata.traceName"]) {
171
+ attributes["langfuse.trace.name"] = attributes["mastra.metadata.traceName"];
172
+ delete attributes["mastra.metadata.traceName"];
173
+ }
174
+ if (attributes["mastra.metadata.version"]) {
175
+ attributes["langfuse.trace.version"] = attributes["mastra.metadata.version"];
176
+ delete attributes["mastra.metadata.version"];
177
+ }
178
+ if (attributes["gen_ai.agent.id"]) {
179
+ attributes["langfuse.observation.metadata.agentId"] = attributes["gen_ai.agent.id"];
180
+ }
181
+ if (attributes["gen_ai.agent.name"]) {
182
+ attributes["langfuse.observation.metadata.agentName"] = attributes["gen_ai.agent.name"];
183
+ }
184
+ if (attributes["mastra.span.type"]) {
185
+ attributes["langfuse.observation.metadata.spanType"] = attributes["mastra.span.type"];
186
+ }
187
+ if (attributes["gen_ai.operation.name"]) {
188
+ attributes["langfuse.observation.metadata.operationName"] = attributes["gen_ai.operation.name"];
189
+ }
168
190
  if (!attributes["gen_ai.input.messages"] && !attributes["gen_ai.tool.call.arguments"]) {
169
191
  for (const key of Object.keys(attributes)) {
170
192
  if (key.startsWith("mastra.") && key.endsWith(".input")) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/tracing.ts","../src/helpers.ts"],"names":["BaseExporter","LangfuseSpanProcessor","LangfuseClient","SpanConverter","TracingEventType"],"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 } 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 /** 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 = (config.baseUrl ?? process.env.LANGFUSE_BASE_URL ?? LANGFUSE_DEFAULT_BASE_URL).replace(/\\/+$/, '');\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 // 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 * Add a score to a trace via the Langfuse client.\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 if (!this.#client) return;\n\n try {\n this.#client.score.create({\n id: `${traceId}-${spanId || ''}-${scorerName}`,\n traceId,\n ...(spanId ? { observationId: spanId } : {}),\n name: scorerName,\n value: score,\n ...(reason ? { comment: reason } : {}),\n ...(metadata ? { metadata } : {}),\n dataType: 'NUMERIC' as const,\n });\n } catch (error) {\n this.logger.error(`${LOG_PREFIX} Error adding score to trace`, {\n error,\n traceId,\n spanId,\n scorerName,\n });\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 // 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 * 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"],"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 } 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 = (config.baseUrl ?? process.env.LANGFUSE_BASE_URL ?? LANGFUSE_DEFAULT_BASE_URL).replace(/\\/+$/, '');\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 * Add a score to a trace via the Langfuse client.\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 if (!this.#client) return;\n\n try {\n this.#client.score.create({\n id: `${traceId}-${spanId || ''}-${scorerName}`,\n traceId,\n ...(spanId ? { observationId: spanId } : {}),\n name: scorerName,\n value: score,\n ...(reason ? { comment: reason } : {}),\n ...(metadata ? { metadata } : {}),\n dataType: 'NUMERIC' as const,\n });\n } catch (error) {\n this.logger.error(`${LOG_PREFIX} Error adding score to trace`, {\n error,\n traceId,\n spanId,\n scorerName,\n });\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 * 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
@@ -36,6 +36,8 @@ var LangfuseExporter = class extends BaseExporter {
36
36
  environment: config.environment,
37
37
  release: config.release,
38
38
  exportMode: this.#realtime ? "immediate" : "batched",
39
+ flushAt: config.flushAt,
40
+ flushInterval: config.flushInterval,
39
41
  // Export all spans — the default filter only passes spans with gen_ai.* attributes
40
42
  // or known LLM instrumentation scopes, but Mastra spans use mastra.* attributes.
41
43
  shouldExportSpan: () => true
@@ -163,6 +165,26 @@ function mapMastraToLangfuseAttributes(attributes, environment, release) {
163
165
  attributes["langfuse.trace.tags"] = attributes["mastra.tags"];
164
166
  delete attributes["mastra.tags"];
165
167
  }
168
+ if (attributes["mastra.metadata.traceName"]) {
169
+ attributes["langfuse.trace.name"] = attributes["mastra.metadata.traceName"];
170
+ delete attributes["mastra.metadata.traceName"];
171
+ }
172
+ if (attributes["mastra.metadata.version"]) {
173
+ attributes["langfuse.trace.version"] = attributes["mastra.metadata.version"];
174
+ delete attributes["mastra.metadata.version"];
175
+ }
176
+ if (attributes["gen_ai.agent.id"]) {
177
+ attributes["langfuse.observation.metadata.agentId"] = attributes["gen_ai.agent.id"];
178
+ }
179
+ if (attributes["gen_ai.agent.name"]) {
180
+ attributes["langfuse.observation.metadata.agentName"] = attributes["gen_ai.agent.name"];
181
+ }
182
+ if (attributes["mastra.span.type"]) {
183
+ attributes["langfuse.observation.metadata.spanType"] = attributes["mastra.span.type"];
184
+ }
185
+ if (attributes["gen_ai.operation.name"]) {
186
+ attributes["langfuse.observation.metadata.operationName"] = attributes["gen_ai.operation.name"];
187
+ }
166
188
  if (!attributes["gen_ai.input.messages"] && !attributes["gen_ai.tool.call.arguments"]) {
167
189
  for (const key of Object.keys(attributes)) {
168
190
  if (key.startsWith("mastra.") && key.endsWith(".input")) {
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 } 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 /** 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 = (config.baseUrl ?? process.env.LANGFUSE_BASE_URL ?? LANGFUSE_DEFAULT_BASE_URL).replace(/\\/+$/, '');\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 // 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 * Add a score to a trace via the Langfuse client.\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 if (!this.#client) return;\n\n try {\n this.#client.score.create({\n id: `${traceId}-${spanId || ''}-${scorerName}`,\n traceId,\n ...(spanId ? { observationId: spanId } : {}),\n name: scorerName,\n value: score,\n ...(reason ? { comment: reason } : {}),\n ...(metadata ? { metadata } : {}),\n dataType: 'NUMERIC' as const,\n });\n } catch (error) {\n this.logger.error(`${LOG_PREFIX} Error adding score to trace`, {\n error,\n traceId,\n spanId,\n scorerName,\n });\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 // 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 * 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 } 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 = (config.baseUrl ?? process.env.LANGFUSE_BASE_URL ?? LANGFUSE_DEFAULT_BASE_URL).replace(/\\/+$/, '');\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 * Add a score to a trace via the Langfuse client.\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 if (!this.#client) return;\n\n try {\n this.#client.score.create({\n id: `${traceId}-${spanId || ''}-${scorerName}`,\n traceId,\n ...(spanId ? { observationId: spanId } : {}),\n name: scorerName,\n value: score,\n ...(reason ? { comment: reason } : {}),\n ...(metadata ? { metadata } : {}),\n dataType: 'NUMERIC' as const,\n });\n } catch (error) {\n this.logger.error(`${LOG_PREFIX} Error adding score to trace`, {\n error,\n traceId,\n spanId,\n scorerName,\n });\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 * 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/tracing.d.ts CHANGED
@@ -20,6 +20,10 @@ export interface LangfuseExporterConfig extends BaseExporterConfig {
20
20
  baseUrl?: string;
21
21
  /** Enable realtime mode - flushes after each event for immediate visibility */
22
22
  realtime?: boolean;
23
+ /** Maximum number of spans per OTEL export batch */
24
+ flushAt?: number;
25
+ /** Maximum time in seconds before pending spans are exported */
26
+ flushInterval?: number;
23
27
  /** Langfuse environment tag for traces */
24
28
  environment?: string;
25
29
  /** Langfuse release tag for traces */
@@ -1 +1 @@
1
- {"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,KAAK,EAAE,YAAY,EAAmB,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAErG,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAKhE,eAAO,MAAM,yBAAyB,+BAA+B,CAAC;AAEtE,MAAM,WAAW,sBAAuB,SAAQ,kBAAkB;IAChE,0BAA0B;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0BAA0B;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,gBAAiB,SAAQ,YAAY;;IAChD,IAAI,SAAc;gBAQN,MAAM,GAAE,sBAA2B;IAgD/C,IAAI,CAAC,OAAO,EAAE,mBAAmB;cAQjB,mBAAmB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;YAOzD,UAAU;IAyBxB;;;OAGG;IACH,IAAI,MAAM,IAAI,cAAc,GAAG,SAAS,CAEvC;IAED;;OAEG;IACG,eAAe,CAAC,EACpB,OAAO,EACP,MAAM,EACN,KAAK,EACL,MAAM,EACN,UAAU,EACV,QAAQ,GACT,EAAE;QACD,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAChC,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBX,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAGhC"}
1
+ {"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAElD,OAAO,KAAK,EAAE,YAAY,EAAmB,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAErG,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAKhE,eAAO,MAAM,yBAAyB,+BAA+B,CAAC;AAEtE,MAAM,WAAW,sBAAuB,SAAQ,kBAAkB;IAChE,0BAA0B;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,0BAA0B;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,oDAAoD;IACpD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,0CAA0C;IAC1C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,qBAAa,gBAAiB,SAAQ,YAAY;;IAChD,IAAI,SAAc;gBAQN,MAAM,GAAE,sBAA2B;IAkD/C,IAAI,CAAC,OAAO,EAAE,mBAAmB;cAQjB,mBAAmB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;YAOzD,UAAU;IAyBxB;;;OAGG;IACH,IAAI,MAAM,IAAI,cAAc,GAAG,SAAS,CAEvC;IAED;;OAEG;IACG,eAAe,CAAC,EACpB,OAAO,EACP,MAAM,EACN,KAAK,EACL,MAAM,EACN,UAAU,EACV,QAAQ,GACT,EAAE;QACD,OAAO,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;KAChC,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBX,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAGhC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/langfuse",
3
- "version": "1.1.4-alpha.0",
3
+ "version": "1.2.0-alpha.4",
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/otel-exporter": "1.0.17-alpha.0",
30
- "@mastra/observability": "1.9.2-alpha.0"
29
+ "@mastra/observability": "1.10.0-alpha.2",
30
+ "@mastra/otel-exporter": "1.0.17-alpha.2"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@opentelemetry/api": "^1.9.0",
@@ -41,7 +41,7 @@
41
41
  "vitest": "4.0.18",
42
42
  "@internal/lint": "0.0.83",
43
43
  "@internal/types-builder": "0.0.58",
44
- "@mastra/core": "1.26.0-alpha.2"
44
+ "@mastra/core": "1.26.0-alpha.8"
45
45
  },
46
46
  "peerDependencies": {
47
47
  "@mastra/core": ">=1.0.0-0 <2.0.0-0",