@langfuse/otel 4.0.0-beta.0 → 4.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -2,26 +2,140 @@ import { ReadableSpan, SpanExporter, BatchSpanProcessor, Span } from '@opentelem
2
2
  import { MediaContentType } from '@langfuse/core';
3
3
  export { MediaContentType } from '@langfuse/core';
4
4
 
5
+ /**
6
+ * Function type for masking sensitive data in spans before export.
7
+ *
8
+ * @param params - Object containing the data to be masked
9
+ * @param params.data - The data that should be masked
10
+ * @returns The masked data (can be of any type)
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const maskFunction: MaskFunction = ({ data }) => {
15
+ * if (typeof data === 'string') {
16
+ * return data.replace(/password=\w+/g, 'password=***');
17
+ * }
18
+ * return data;
19
+ * };
20
+ * ```
21
+ *
22
+ * @public
23
+ */
5
24
  type MaskFunction = (params: {
6
25
  data: any;
7
26
  }) => any;
27
+ /**
28
+ * Function type for determining whether a span should be exported to Langfuse.
29
+ *
30
+ * @param params - Object containing the span to evaluate
31
+ * @param params.otelSpan - The OpenTelemetry span to evaluate
32
+ * @returns `true` if the span should be exported, `false` otherwise
33
+ *
34
+ * @example
35
+ * ```typescript
36
+ * const shouldExportSpan: ShouldExportSpan = ({ otelSpan }) => {
37
+ * // Only export spans that took longer than 100ms
38
+ * return otelSpan.duration[0] * 1000 + otelSpan.duration[1] / 1000000 > 100;
39
+ * };
40
+ * ```
41
+ *
42
+ * @public
43
+ */
8
44
  type ShouldExportSpan = (params: {
9
45
  otelSpan: ReadableSpan;
10
46
  }) => boolean;
47
+ /**
48
+ * Configuration parameters for the LangfuseSpanProcessor.
49
+ *
50
+ * @public
51
+ */
11
52
  interface LangfuseSpanProcessorParams {
53
+ /**
54
+ * Custom OpenTelemetry span exporter. If not provided, a default OTLP exporter will be used.
55
+ */
12
56
  exporter?: SpanExporter;
57
+ /**
58
+ * Langfuse public API key. Can also be set via LANGFUSE_PUBLIC_KEY environment variable.
59
+ */
13
60
  publicKey?: string;
61
+ /**
62
+ * Langfuse secret API key. Can also be set via LANGFUSE_SECRET_KEY environment variable.
63
+ */
14
64
  secretKey?: string;
65
+ /**
66
+ * Langfuse instance base URL. Can also be set via LANGFUSE_BASE_URL environment variable.
67
+ * @defaultValue "https://cloud.langfuse.com"
68
+ */
15
69
  baseUrl?: string;
70
+ /**
71
+ * Number of spans to batch before flushing. Can also be set via LANGFUSE_FLUSH_AT environment variable.
72
+ */
16
73
  flushAt?: number;
74
+ /**
75
+ * Flush interval in seconds. Can also be set via LANGFUSE_FLUSH_INTERVAL environment variable.
76
+ */
17
77
  flushInterval?: number;
78
+ /**
79
+ * Function to mask sensitive data in spans before export.
80
+ */
18
81
  mask?: MaskFunction;
82
+ /**
83
+ * Function to determine whether a span should be exported to Langfuse.
84
+ */
19
85
  shouldExportSpan?: ShouldExportSpan;
86
+ /**
87
+ * Environment identifier for the traces. Can also be set via LANGFUSE_TRACING_ENVIRONMENT environment variable.
88
+ */
20
89
  environment?: string;
90
+ /**
91
+ * Release identifier for the traces. Can also be set via LANGFUSE_RELEASE environment variable.
92
+ */
21
93
  release?: string;
94
+ /**
95
+ * Request timeout in seconds. Can also be set via LANGFUSE_TIMEOUT environment variable.
96
+ * @defaultValue 5
97
+ */
22
98
  timeout?: number;
99
+ /**
100
+ * Additional HTTP headers to include with requests.
101
+ */
23
102
  additionalHeaders?: Record<string, string>;
24
103
  }
104
+ /**
105
+ * OpenTelemetry span processor for sending spans to Langfuse.
106
+ *
107
+ * This processor extends the standard BatchSpanProcessor to provide:
108
+ * - Automatic batching and flushing of spans to Langfuse
109
+ * - Media content extraction and upload from base64 data URIs
110
+ * - Data masking capabilities for sensitive information
111
+ * - Conditional span export based on custom logic
112
+ * - Environment and release tagging
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * import { NodeSDK } from '@opentelemetry/sdk-node';
117
+ * import { LangfuseSpanProcessor } from '@langfuse/otel';
118
+ *
119
+ * const sdk = new NodeSDK({
120
+ * spanProcessors: [
121
+ * new LangfuseSpanProcessor({
122
+ * publicKey: 'pk_...',
123
+ * secretKey: 'sk_...',
124
+ * baseUrl: 'https://cloud.langfuse.com',
125
+ * environment: 'production',
126
+ * mask: ({ data }) => {
127
+ * // Mask sensitive data
128
+ * return data.replace(/api_key=\w+/g, 'api_key=***');
129
+ * }
130
+ * })
131
+ * ]
132
+ * });
133
+ *
134
+ * sdk.start();
135
+ * ```
136
+ *
137
+ * @public
138
+ */
25
139
  declare class LangfuseSpanProcessor extends BatchSpanProcessor {
26
140
  private pendingMediaUploads;
27
141
  private publicKey?;
@@ -31,12 +145,74 @@ declare class LangfuseSpanProcessor extends BatchSpanProcessor {
31
145
  private mask?;
32
146
  private shouldExportSpan?;
33
147
  private apiClient;
148
+ /**
149
+ * Creates a new LangfuseSpanProcessor instance.
150
+ *
151
+ * @param params - Configuration parameters for the processor
152
+ *
153
+ * @example
154
+ * ```typescript
155
+ * const processor = new LangfuseSpanProcessor({
156
+ * publicKey: 'pk_...',
157
+ * secretKey: 'sk_...',
158
+ * environment: 'staging',
159
+ * flushAt: 10,
160
+ * flushInterval: 2,
161
+ * mask: ({ data }) => {
162
+ * // Custom masking logic
163
+ * return typeof data === 'string'
164
+ * ? data.replace(/secret_\w+/g, 'secret_***')
165
+ * : data;
166
+ * },
167
+ * shouldExportSpan: ({ otelSpan }) => {
168
+ * // Only export spans from specific services
169
+ * return otelSpan.name.startsWith('my-service');
170
+ * }
171
+ * });
172
+ * ```
173
+ */
34
174
  constructor(params?: LangfuseSpanProcessorParams);
35
175
  private get logger();
176
+ /**
177
+ * Called when a span is started. Adds environment and release attributes to the span.
178
+ *
179
+ * @param span - The span that was started
180
+ * @param parentContext - The parent context
181
+ *
182
+ * @override
183
+ */
36
184
  onStart(span: Span, parentContext: any): void;
185
+ /**
186
+ * Called when a span ends. Processes the span for export to Langfuse.
187
+ *
188
+ * This method:
189
+ * 1. Checks if the span should be exported using the shouldExportSpan function
190
+ * 2. Applies data masking to sensitive attributes
191
+ * 3. Handles media content extraction and upload
192
+ * 4. Logs span details in debug mode
193
+ * 5. Passes the span to the parent processor for export
194
+ *
195
+ * @param span - The span that ended
196
+ *
197
+ * @override
198
+ */
37
199
  onEnd(span: ReadableSpan): void;
38
200
  private flush;
201
+ /**
202
+ * Forces an immediate flush of all pending spans and media uploads.
203
+ *
204
+ * @returns Promise that resolves when all pending operations are complete
205
+ *
206
+ * @override
207
+ */
39
208
  forceFlush(): Promise<void>;
209
+ /**
210
+ * Gracefully shuts down the processor, ensuring all pending operations are completed.
211
+ *
212
+ * @returns Promise that resolves when shutdown is complete
213
+ *
214
+ * @override
215
+ */
40
216
  shutdown(): Promise<void>;
41
217
  private handleMediaInPlace;
42
218
  private applyMaskInPlace;
@@ -45,31 +221,151 @@ declare class LangfuseSpanProcessor extends BatchSpanProcessor {
45
221
  private uploadMediaWithBackoff;
46
222
  }
47
223
 
224
+ /**
225
+ * Parameters for creating a LangfuseMedia instance.
226
+ *
227
+ * Supports two input formats:
228
+ * - Base64 data URI (e.g., "data:image/png;base64,...")
229
+ * - Raw bytes with explicit content type
230
+ *
231
+ * @public
232
+ */
48
233
  type LangfuseMediaParams = {
234
+ /** Indicates the media is provided as a base64 data URI */
49
235
  source: "base64_data_uri";
236
+ /** The complete base64 data URI string */
50
237
  base64DataUri: string;
51
238
  } | {
239
+ /** Indicates the media is provided as raw bytes */
52
240
  source: "bytes";
241
+ /** The raw content bytes */
53
242
  contentBytes: Buffer;
243
+ /** The MIME type of the content */
54
244
  contentType: MediaContentType;
55
245
  };
56
246
  /**
57
247
  * A class for wrapping media objects for upload to Langfuse.
58
248
  *
59
249
  * This class handles the preparation and formatting of media content for Langfuse,
60
- * supporting both base64 data URIs and raw content bytes.
250
+ * supporting both base64 data URIs and raw content bytes. It automatically:
251
+ * - Parses base64 data URIs to extract content type and bytes
252
+ * - Generates SHA-256 hashes for content integrity
253
+ * - Creates unique media IDs based on content hash
254
+ * - Formats media references for embedding in traces
255
+ *
256
+ * @example
257
+ * ```typescript
258
+ * // From base64 data URI
259
+ * const media1 = new LangfuseMedia({
260
+ * source: "base64_data_uri",
261
+ * base64DataUri: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg=="
262
+ * });
263
+ *
264
+ * // From raw bytes
265
+ * const media2 = new LangfuseMedia({
266
+ * source: "bytes",
267
+ * contentBytes: Buffer.from("Hello World"),
268
+ * contentType: "text/plain"
269
+ * });
270
+ *
271
+ * console.log(media1.id); // Unique media ID
272
+ * console.log(media1.tag); // Media reference tag
273
+ * ```
274
+ *
275
+ * @public
61
276
  */
62
277
  declare class LangfuseMedia {
63
278
  _contentBytes?: Buffer;
64
279
  _contentType?: MediaContentType;
65
280
  _source?: string;
281
+ /**
282
+ * Creates a new LangfuseMedia instance.
283
+ *
284
+ * @param params - Media parameters specifying the source and content
285
+ *
286
+ * @example
287
+ * ```typescript
288
+ * // Create from base64 data URI
289
+ * const media = new LangfuseMedia({
290
+ * source: "base64_data_uri",
291
+ * base64DataUri: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ..."
292
+ * });
293
+ * ```
294
+ */
66
295
  constructor(params: LangfuseMediaParams);
296
+ /**
297
+ * Parses a base64 data URI to extract content bytes and type.
298
+ *
299
+ * @param data - The base64 data URI string
300
+ * @returns Tuple of [contentBytes, contentType] or [undefined, undefined] on error
301
+ * @private
302
+ */
67
303
  private parseBase64DataUri;
304
+ /**
305
+ * Gets a unique identifier for this media based on its content hash.
306
+ *
307
+ * The ID is derived from the first 22 characters of the URL-safe base64-encoded
308
+ * SHA-256 hash of the content.
309
+ *
310
+ * @returns The unique media ID, or null if hash generation failed
311
+ *
312
+ * @example
313
+ * ```typescript
314
+ * const media = new LangfuseMedia({...});
315
+ * console.log(media.id); // "A1B2C3D4E5F6G7H8I9J0K1"
316
+ * ```
317
+ */
68
318
  get id(): string | null;
319
+ /**
320
+ * Gets the length of the media content in bytes.
321
+ *
322
+ * @returns The content length in bytes, or undefined if no content is available
323
+ */
69
324
  get contentLength(): number | undefined;
325
+ /**
326
+ * Gets the SHA-256 hash of the media content.
327
+ *
328
+ * The hash is used for content integrity verification and generating unique media IDs.
329
+ * Returns undefined if crypto is not available or hash generation fails.
330
+ *
331
+ * @returns The base64-encoded SHA-256 hash, or undefined if unavailable
332
+ */
70
333
  get contentSha256Hash(): string | undefined;
334
+ /**
335
+ * Gets the media reference tag for embedding in trace data.
336
+ *
337
+ * The tag format is: `@@@langfuseMedia:type=<contentType>|id=<mediaId>|source=<source>@@@`
338
+ * This tag can be embedded in trace attributes and will be replaced with actual
339
+ * media content when the trace is viewed in Langfuse.
340
+ *
341
+ * @returns The media reference tag, or null if required data is missing
342
+ *
343
+ * @example
344
+ * ```typescript
345
+ * const media = new LangfuseMedia({...});
346
+ * console.log(media.tag);
347
+ * // "@@@langfuseMedia:type=image/png|id=A1B2C3D4E5F6G7H8I9J0K1|source=base64_data_uri@@@"
348
+ * ```
349
+ */
71
350
  get tag(): string | null;
351
+ /**
352
+ * Gets the media content as a base64 data URI.
353
+ *
354
+ * @returns The complete data URI string, or null if no content is available
355
+ *
356
+ * @example
357
+ * ```typescript
358
+ * const media = new LangfuseMedia({...});
359
+ * console.log(media.base64DataUri);
360
+ * // "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB..."
361
+ * ```
362
+ */
72
363
  get base64DataUri(): string | null;
364
+ /**
365
+ * Serializes the media to JSON (returns the base64 data URI).
366
+ *
367
+ * @returns The base64 data URI, or null if no content is available
368
+ */
73
369
  toJSON(): string | null;
74
370
  }
75
371
 
package/dist/index.mjs CHANGED
@@ -12,7 +12,8 @@ import {
12
12
  LangfuseAPIClient,
13
13
  LANGFUSE_SDK_VERSION,
14
14
  LangfuseOtelSpanAttributes,
15
- getEnv
15
+ getEnv,
16
+ uint8ArrayToBase64 as uint8ArrayToBase642
16
17
  } from "@langfuse/core";
17
18
  import { hrTimeToMilliseconds } from "@opentelemetry/core";
18
19
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
@@ -60,6 +61,20 @@ function getSha256HashFromBytes(data) {
60
61
  // src/media.ts
61
62
  import { getGlobalLogger as getGlobalLogger2 } from "@langfuse/core";
62
63
  var LangfuseMedia = class {
64
+ /**
65
+ * Creates a new LangfuseMedia instance.
66
+ *
67
+ * @param params - Media parameters specifying the source and content
68
+ *
69
+ * @example
70
+ * ```typescript
71
+ * // Create from base64 data URI
72
+ * const media = new LangfuseMedia({
73
+ * source: "base64_data_uri",
74
+ * base64DataUri: "data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQ..."
75
+ * });
76
+ * ```
77
+ */
63
78
  constructor(params) {
64
79
  const { source } = params;
65
80
  this._source = source;
@@ -74,6 +89,13 @@ var LangfuseMedia = class {
74
89
  this._contentType = params.contentType;
75
90
  }
76
91
  }
92
+ /**
93
+ * Parses a base64 data URI to extract content bytes and type.
94
+ *
95
+ * @param data - The base64 data URI string
96
+ * @returns Tuple of [contentBytes, contentType] or [undefined, undefined] on error
97
+ * @private
98
+ */
77
99
  parseBase64DataUri(data) {
78
100
  try {
79
101
  if (!data || typeof data !== "string") {
@@ -103,15 +125,42 @@ var LangfuseMedia = class {
103
125
  return [void 0, void 0];
104
126
  }
105
127
  }
128
+ /**
129
+ * Gets a unique identifier for this media based on its content hash.
130
+ *
131
+ * The ID is derived from the first 22 characters of the URL-safe base64-encoded
132
+ * SHA-256 hash of the content.
133
+ *
134
+ * @returns The unique media ID, or null if hash generation failed
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * const media = new LangfuseMedia({...});
139
+ * console.log(media.id); // "A1B2C3D4E5F6G7H8I9J0K1"
140
+ * ```
141
+ */
106
142
  get id() {
107
143
  if (!this.contentSha256Hash) return null;
108
144
  const urlSafeContentHash = this.contentSha256Hash.replaceAll("+", "-").replaceAll("/", "_");
109
145
  return urlSafeContentHash.slice(0, 22);
110
146
  }
147
+ /**
148
+ * Gets the length of the media content in bytes.
149
+ *
150
+ * @returns The content length in bytes, or undefined if no content is available
151
+ */
111
152
  get contentLength() {
112
153
  var _a2;
113
154
  return (_a2 = this._contentBytes) == null ? void 0 : _a2.length;
114
155
  }
156
+ /**
157
+ * Gets the SHA-256 hash of the media content.
158
+ *
159
+ * The hash is used for content integrity verification and generating unique media IDs.
160
+ * Returns undefined if crypto is not available or hash generation fails.
161
+ *
162
+ * @returns The base64-encoded SHA-256 hash, or undefined if unavailable
163
+ */
115
164
  get contentSha256Hash() {
116
165
  if (!this._contentBytes || !isCryptoAvailable) {
117
166
  return void 0;
@@ -126,14 +175,47 @@ var LangfuseMedia = class {
126
175
  return void 0;
127
176
  }
128
177
  }
178
+ /**
179
+ * Gets the media reference tag for embedding in trace data.
180
+ *
181
+ * The tag format is: `@@@langfuseMedia:type=<contentType>|id=<mediaId>|source=<source>@@@`
182
+ * This tag can be embedded in trace attributes and will be replaced with actual
183
+ * media content when the trace is viewed in Langfuse.
184
+ *
185
+ * @returns The media reference tag, or null if required data is missing
186
+ *
187
+ * @example
188
+ * ```typescript
189
+ * const media = new LangfuseMedia({...});
190
+ * console.log(media.tag);
191
+ * // "@@@langfuseMedia:type=image/png|id=A1B2C3D4E5F6G7H8I9J0K1|source=base64_data_uri@@@"
192
+ * ```
193
+ */
129
194
  get tag() {
130
195
  if (!this._contentType || !this._source || !this.id) return null;
131
196
  return `@@@langfuseMedia:type=${this._contentType}|id=${this.id}|source=${this._source}@@@`;
132
197
  }
198
+ /**
199
+ * Gets the media content as a base64 data URI.
200
+ *
201
+ * @returns The complete data URI string, or null if no content is available
202
+ *
203
+ * @example
204
+ * ```typescript
205
+ * const media = new LangfuseMedia({...});
206
+ * console.log(media.base64DataUri);
207
+ * // "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAAB..."
208
+ * ```
209
+ */
133
210
  get base64DataUri() {
134
211
  if (!this._contentBytes) return null;
135
212
  return `data:${this._contentType};base64,${Buffer.from(this._contentBytes).toString("base64")}`;
136
213
  }
214
+ /**
215
+ * Serializes the media to JSON (returns the base64 data URI).
216
+ *
217
+ * @returns The base64 data URI, or null if no content is available
218
+ */
137
219
  toJSON() {
138
220
  return this.base64DataUri;
139
221
  }
@@ -141,6 +223,32 @@ var LangfuseMedia = class {
141
223
 
142
224
  // src/span-processor.ts
143
225
  var LangfuseSpanProcessor = class extends BatchSpanProcessor {
226
+ /**
227
+ * Creates a new LangfuseSpanProcessor instance.
228
+ *
229
+ * @param params - Configuration parameters for the processor
230
+ *
231
+ * @example
232
+ * ```typescript
233
+ * const processor = new LangfuseSpanProcessor({
234
+ * publicKey: 'pk_...',
235
+ * secretKey: 'sk_...',
236
+ * environment: 'staging',
237
+ * flushAt: 10,
238
+ * flushInterval: 2,
239
+ * mask: ({ data }) => {
240
+ * // Custom masking logic
241
+ * return typeof data === 'string'
242
+ * ? data.replace(/secret_\w+/g, 'secret_***')
243
+ * : data;
244
+ * },
245
+ * shouldExportSpan: ({ otelSpan }) => {
246
+ * // Only export spans from specific services
247
+ * return otelSpan.name.startsWith('my-service');
248
+ * }
249
+ * });
250
+ * ```
251
+ */
144
252
  constructor(params) {
145
253
  var _a2, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
146
254
  const logger = getGlobalLogger3();
@@ -162,8 +270,8 @@ var LangfuseSpanProcessor = class extends BatchSpanProcessor {
162
270
  }
163
271
  const flushAt = (_f = params == null ? void 0 : params.flushAt) != null ? _f : getEnv("LANGFUSE_FLUSH_AT");
164
272
  const flushIntervalSeconds = (_g = params == null ? void 0 : params.flushInterval) != null ? _g : getEnv("LANGFUSE_FLUSH_INTERVAL");
165
- const authHeaderValue = Buffer.from(`${publicKey}:${secretKey}`).toString(
166
- "base64"
273
+ const authHeaderValue = uint8ArrayToBase642(
274
+ new TextEncoder().encode(`${publicKey}:${secretKey}`)
167
275
  );
168
276
  const timeoutSeconds = (_i = params == null ? void 0 : params.timeout) != null ? _i : Number((_h = getEnv("LANGFUSE_TIMEOUT")) != null ? _h : 5);
169
277
  const exporter = (_j = params == null ? void 0 : params.exporter) != null ? _j : new OTLPTraceExporter({
@@ -217,6 +325,14 @@ var LangfuseSpanProcessor = class extends BatchSpanProcessor {
217
325
  get logger() {
218
326
  return getGlobalLogger3();
219
327
  }
328
+ /**
329
+ * Called when a span is started. Adds environment and release attributes to the span.
330
+ *
331
+ * @param span - The span that was started
332
+ * @param parentContext - The parent context
333
+ *
334
+ * @override
335
+ */
220
336
  onStart(span, parentContext) {
221
337
  span.setAttributes({
222
338
  [LangfuseOtelSpanAttributes.ENVIRONMENT]: this.environment,
@@ -224,6 +340,20 @@ var LangfuseSpanProcessor = class extends BatchSpanProcessor {
224
340
  });
225
341
  return super.onStart(span, parentContext);
226
342
  }
343
+ /**
344
+ * Called when a span ends. Processes the span for export to Langfuse.
345
+ *
346
+ * This method:
347
+ * 1. Checks if the span should be exported using the shouldExportSpan function
348
+ * 2. Applies data masking to sensitive attributes
349
+ * 3. Handles media content extraction and upload
350
+ * 4. Logs span details in debug mode
351
+ * 5. Passes the span to the parent processor for export
352
+ *
353
+ * @param span - The span that ended
354
+ *
355
+ * @override
356
+ */
227
357
  onEnd(span) {
228
358
  var _a2, _b;
229
359
  if (this.shouldExportSpan) {
@@ -269,10 +399,24 @@ ${JSON.stringify(
269
399
  );
270
400
  });
271
401
  }
402
+ /**
403
+ * Forces an immediate flush of all pending spans and media uploads.
404
+ *
405
+ * @returns Promise that resolves when all pending operations are complete
406
+ *
407
+ * @override
408
+ */
272
409
  async forceFlush() {
273
410
  await this.flush();
274
411
  return super.forceFlush();
275
412
  }
413
+ /**
414
+ * Gracefully shuts down the processor, ensuring all pending operations are completed.
415
+ *
416
+ * @returns Promise that resolves when shutdown is complete
417
+ *
418
+ * @override
419
+ */
276
420
  async shutdown() {
277
421
  await this.flush();
278
422
  return super.shutdown();