@langfuse/core 4.3.0 → 4.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -59,6 +59,24 @@ function base64Decode(input) {
59
59
  const bytes = base64ToBytes(input);
60
60
  return new TextDecoder().decode(bytes);
61
61
  }
62
+ async function createExperimentId() {
63
+ const randomBytes = new Uint8Array(8);
64
+ crypto.getRandomValues(randomBytes);
65
+ return Array.from(randomBytes).map((b) => b.toString(16).padStart(2, "0")).join("");
66
+ }
67
+ async function createExperimentItemId(input) {
68
+ const serialized = serializeValue(input);
69
+ const data = new TextEncoder().encode(serialized);
70
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
71
+ const hashArray = Array.from(new Uint8Array(hashBuffer));
72
+ const hashHex = hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
73
+ return hashHex.slice(0, 16);
74
+ }
75
+ function serializeValue(value) {
76
+ if (value === void 0 || value === null) return void 0;
77
+ if (typeof value === "string") return value;
78
+ return JSON.stringify(value);
79
+ }
62
80
 
63
81
  // src/logger/index.ts
64
82
  var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
@@ -235,7 +253,7 @@ var resetGlobalLogger = () => {
235
253
  // package.json
236
254
  var package_default = {
237
255
  name: "@langfuse/core",
238
- version: "4.3.0",
256
+ version: "4.4.0",
239
257
  description: "Core functions and utilities for Langfuse packages",
240
258
  type: "module",
241
259
  sideEffects: false,
@@ -262,6 +280,9 @@ var package_default = {
262
280
  files: [
263
281
  "dist"
264
282
  ],
283
+ peerDependencies: {
284
+ "@opentelemetry/api": "^1.9.0"
285
+ },
265
286
  devDependencies: {
266
287
  "@types/node": "^24.1.0"
267
288
  }
@@ -271,6 +292,7 @@ var package_default = {
271
292
  var LANGFUSE_TRACER_NAME = "langfuse-sdk";
272
293
  var LANGFUSE_SDK_VERSION = package_default.version;
273
294
  var LANGFUSE_SDK_NAME = "javascript";
295
+ var LANGFUSE_SDK_EXPERIMENT_ENVIRONMENT = "sdk-experiment";
274
296
  var LangfuseOtelSpanAttributes = /* @__PURE__ */ ((LangfuseOtelSpanAttributes2) => {
275
297
  LangfuseOtelSpanAttributes2["TRACE_NAME"] = "langfuse.trace.name";
276
298
  LangfuseOtelSpanAttributes2["TRACE_USER_ID"] = "user.id";
@@ -297,6 +319,15 @@ var LangfuseOtelSpanAttributes = /* @__PURE__ */ ((LangfuseOtelSpanAttributes2)
297
319
  LangfuseOtelSpanAttributes2["RELEASE"] = "langfuse.release";
298
320
  LangfuseOtelSpanAttributes2["VERSION"] = "langfuse.version";
299
321
  LangfuseOtelSpanAttributes2["AS_ROOT"] = "langfuse.internal.as_root";
322
+ LangfuseOtelSpanAttributes2["EXPERIMENT_ID"] = "langfuse.experiment.id";
323
+ LangfuseOtelSpanAttributes2["EXPERIMENT_NAME"] = "langfuse.experiment.name";
324
+ LangfuseOtelSpanAttributes2["EXPERIMENT_DESCRIPTION"] = "langfuse.experiment.description";
325
+ LangfuseOtelSpanAttributes2["EXPERIMENT_METADATA"] = "langfuse.experiment.metadata";
326
+ LangfuseOtelSpanAttributes2["EXPERIMENT_DATASET_ID"] = "langfuse.experiment.dataset.id";
327
+ LangfuseOtelSpanAttributes2["EXPERIMENT_ITEM_ID"] = "langfuse.experiment.item.id";
328
+ LangfuseOtelSpanAttributes2["EXPERIMENT_ITEM_EXPECTED_OUTPUT"] = "langfuse.experiment.item.expected_output";
329
+ LangfuseOtelSpanAttributes2["EXPERIMENT_ITEM_METADATA"] = "langfuse.experiment.item.metadata";
330
+ LangfuseOtelSpanAttributes2["EXPERIMENT_ITEM_ROOT_OBSERVATION_ID"] = "langfuse.experiment.item.root_observation_id";
300
331
  LangfuseOtelSpanAttributes2["TRACE_COMPAT_USER_ID"] = "langfuse.user.id";
301
332
  LangfuseOtelSpanAttributes2["TRACE_COMPAT_SESSION_ID"] = "langfuse.session.id";
302
333
  return LangfuseOtelSpanAttributes2;
@@ -11352,6 +11383,374 @@ var LangfuseMedia = class {
11352
11383
  return this.base64DataUri;
11353
11384
  }
11354
11385
  };
11386
+
11387
+ // src/propagation.ts
11388
+ import {
11389
+ context as otelContextApi,
11390
+ trace as otelTraceApi,
11391
+ propagation,
11392
+ createContextKey
11393
+ } from "@opentelemetry/api";
11394
+ var experimentKeys = [
11395
+ "experimentId",
11396
+ "experimentName",
11397
+ "experimentMetadata",
11398
+ "experimentDatasetId",
11399
+ "experimentItemId",
11400
+ "experimentItemMetadata",
11401
+ "experimentItemRootObservationId"
11402
+ ];
11403
+ var LangfuseOtelContextKeys = {
11404
+ userId: createContextKey("langfuse_user_id"),
11405
+ sessionId: createContextKey("langfuse_session_id"),
11406
+ metadata: createContextKey("langfuse_metadata"),
11407
+ version: createContextKey("langfuse_version"),
11408
+ tags: createContextKey("langfuse_tags"),
11409
+ // Experiments
11410
+ experimentId: createContextKey("langfuse_experiment_id"),
11411
+ experimentName: createContextKey("langfuse_experiment_name"),
11412
+ experimentMetadata: createContextKey("langfuse_experiment_metadata"),
11413
+ experimentDatasetId: createContextKey("langfuse_experiment_dataset_id"),
11414
+ experimentItemId: createContextKey("langfuse_experiment_item_id"),
11415
+ experimentItemMetadata: createContextKey("langfuse_experiment_item_metadata"),
11416
+ experimentItemRootObservationId: createContextKey(
11417
+ "langfuse_experiment_item_root_observation_id"
11418
+ )
11419
+ };
11420
+ var LANGFUSE_BAGGAGE_PREFIX = "langfuse_";
11421
+ var LANGFUSE_BAGGAGE_TAGS_SEPARATOR = ",";
11422
+ function propagateAttributes(params, fn) {
11423
+ var _a2;
11424
+ let context = otelContextApi.active();
11425
+ const span = otelTraceApi.getActiveSpan();
11426
+ const asBaggage = (_a2 = params.asBaggage) != null ? _a2 : false;
11427
+ const { userId, sessionId, metadata, version, tags, _internalExperiment } = params;
11428
+ if (userId) {
11429
+ if (isValidPropagatedString({ value: userId, attributeName: "userId" })) {
11430
+ context = setPropagatedAttribute({
11431
+ key: "userId",
11432
+ value: userId,
11433
+ context,
11434
+ span,
11435
+ asBaggage
11436
+ });
11437
+ }
11438
+ }
11439
+ if (sessionId) {
11440
+ if (isValidPropagatedString({
11441
+ value: sessionId,
11442
+ attributeName: "sessionId"
11443
+ })) {
11444
+ context = setPropagatedAttribute({
11445
+ key: "sessionId",
11446
+ value: sessionId,
11447
+ context,
11448
+ span,
11449
+ asBaggage
11450
+ });
11451
+ }
11452
+ }
11453
+ if (version) {
11454
+ if (isValidPropagatedString({
11455
+ value: version,
11456
+ attributeName: "version"
11457
+ })) {
11458
+ context = setPropagatedAttribute({
11459
+ key: "version",
11460
+ value: version,
11461
+ context,
11462
+ span,
11463
+ asBaggage
11464
+ });
11465
+ }
11466
+ }
11467
+ if (tags && tags.length > 0) {
11468
+ const validTags = tags.filter(
11469
+ (tag) => isValidPropagatedString({
11470
+ value: tag,
11471
+ attributeName: "tag"
11472
+ })
11473
+ );
11474
+ if (validTags.length > 0) {
11475
+ context = setPropagatedAttribute({
11476
+ key: "tags",
11477
+ value: validTags,
11478
+ context,
11479
+ span,
11480
+ asBaggage
11481
+ });
11482
+ }
11483
+ }
11484
+ if (metadata) {
11485
+ const validatedMetadata = {};
11486
+ for (const [key, value] of Object.entries(metadata)) {
11487
+ if (isValidPropagatedString({
11488
+ value,
11489
+ attributeName: `metadata.${key}`
11490
+ })) {
11491
+ validatedMetadata[key] = value;
11492
+ }
11493
+ }
11494
+ if (Object.keys(validatedMetadata).length > 0) {
11495
+ context = setPropagatedAttribute({
11496
+ key: "metadata",
11497
+ value: validatedMetadata,
11498
+ context,
11499
+ span,
11500
+ asBaggage
11501
+ });
11502
+ }
11503
+ }
11504
+ if (_internalExperiment) {
11505
+ for (const [key, value] of Object.entries(_internalExperiment)) {
11506
+ if (value !== void 0) {
11507
+ context = setPropagatedAttribute({
11508
+ key,
11509
+ value,
11510
+ context,
11511
+ span,
11512
+ asBaggage
11513
+ });
11514
+ }
11515
+ }
11516
+ }
11517
+ return otelContextApi.with(context, fn);
11518
+ }
11519
+ function getPropagatedAttributesFromContext(context) {
11520
+ const propagatedAttributes = {};
11521
+ const baggage = propagation.getBaggage(context);
11522
+ if (baggage) {
11523
+ baggage.getAllEntries().forEach(([baggageKey, baggageEntry]) => {
11524
+ if (baggageKey.startsWith(LANGFUSE_BAGGAGE_PREFIX)) {
11525
+ const spanKey = getSpanKeyFromBaggageKey(baggageKey);
11526
+ if (spanKey) {
11527
+ const isMergedTags = baggageKey == getBaggageKeyForPropagatedKey("tags");
11528
+ propagatedAttributes[spanKey] = isMergedTags ? baggageEntry.value.split(LANGFUSE_BAGGAGE_TAGS_SEPARATOR) : baggageEntry.value;
11529
+ }
11530
+ }
11531
+ });
11532
+ }
11533
+ const userId = context.getValue(LangfuseOtelContextKeys["userId"]);
11534
+ if (userId && typeof userId === "string") {
11535
+ const spanKey = getSpanKeyForPropagatedKey("userId");
11536
+ propagatedAttributes[spanKey] = userId;
11537
+ }
11538
+ const sessionId = context.getValue(LangfuseOtelContextKeys["sessionId"]);
11539
+ if (sessionId && typeof sessionId === "string") {
11540
+ const spanKey = getSpanKeyForPropagatedKey("sessionId");
11541
+ propagatedAttributes[spanKey] = sessionId;
11542
+ }
11543
+ const version = context.getValue(LangfuseOtelContextKeys["version"]);
11544
+ if (version && typeof version === "string") {
11545
+ const spanKey = getSpanKeyForPropagatedKey("version");
11546
+ propagatedAttributes[spanKey] = version;
11547
+ }
11548
+ const tags = context.getValue(LangfuseOtelContextKeys["tags"]);
11549
+ if (tags && Array.isArray(tags)) {
11550
+ const spanKey = getSpanKeyForPropagatedKey("tags");
11551
+ propagatedAttributes[spanKey] = tags;
11552
+ }
11553
+ const metadata = context.getValue(LangfuseOtelContextKeys["metadata"]);
11554
+ if (metadata && typeof metadata === "object" && metadata !== null) {
11555
+ for (const [k, v] of Object.entries(metadata)) {
11556
+ const spanKey = `${"langfuse.trace.metadata" /* TRACE_METADATA */}.${k}`;
11557
+ propagatedAttributes[spanKey] = String(v);
11558
+ }
11559
+ }
11560
+ for (const key of experimentKeys) {
11561
+ const contextKey = LangfuseOtelContextKeys[key];
11562
+ const value = context.getValue(contextKey);
11563
+ if (value && typeof value === "string") {
11564
+ const spanKey = getSpanKeyForPropagatedKey(key);
11565
+ propagatedAttributes[spanKey] = value;
11566
+ }
11567
+ }
11568
+ if (propagatedAttributes[getSpanKeyForPropagatedKey("experimentItemRootObservationId")]) {
11569
+ propagatedAttributes["langfuse.environment" /* ENVIRONMENT */] = LANGFUSE_SDK_EXPERIMENT_ENVIRONMENT;
11570
+ }
11571
+ return propagatedAttributes;
11572
+ }
11573
+ function setPropagatedAttribute(params) {
11574
+ const { key, value, span, asBaggage } = params;
11575
+ let context = params.context;
11576
+ let mergedMetadata = key === "metadata" ? getContextMergedMetadata(context, value) : {};
11577
+ let mergedTags = key === "tags" ? getContextMergedTags(context, value) : [];
11578
+ const contextKey = getContextKeyForPropagatedKey(key);
11579
+ if (key === "metadata") {
11580
+ context = context.setValue(contextKey, mergedMetadata);
11581
+ } else if (key === "tags") {
11582
+ context = context.setValue(contextKey, mergedTags);
11583
+ } else {
11584
+ context = context.setValue(contextKey, value);
11585
+ }
11586
+ if (span && span.isRecording()) {
11587
+ if (key === "metadata") {
11588
+ for (const [k, v] of Object.entries(mergedMetadata)) {
11589
+ span.setAttribute(
11590
+ `${"langfuse.trace.metadata" /* TRACE_METADATA */}.${k}`,
11591
+ v
11592
+ );
11593
+ }
11594
+ } else if (key === "tags") {
11595
+ const spanKey = getSpanKeyForPropagatedKey(key);
11596
+ span.setAttribute(spanKey, mergedTags);
11597
+ } else {
11598
+ const spanKey = getSpanKeyForPropagatedKey(key);
11599
+ span.setAttribute(spanKey, value);
11600
+ }
11601
+ }
11602
+ if (asBaggage) {
11603
+ const baggageKey = getBaggageKeyForPropagatedKey(key);
11604
+ let baggage = propagation.getBaggage(context) || propagation.createBaggage();
11605
+ if (key === "metadata") {
11606
+ for (const [k, v] of Object.entries(mergedMetadata)) {
11607
+ baggage = baggage.setEntry(`${baggageKey}_${k}`, { value: v });
11608
+ }
11609
+ } else if (key === "tags") {
11610
+ baggage = baggage.setEntry(baggageKey, {
11611
+ value: mergedTags.join(LANGFUSE_BAGGAGE_TAGS_SEPARATOR)
11612
+ });
11613
+ } else {
11614
+ baggage = baggage.setEntry(baggageKey, { value });
11615
+ }
11616
+ context = propagation.setBaggage(context, baggage);
11617
+ }
11618
+ return context;
11619
+ }
11620
+ function getContextMergedTags(context, newTags) {
11621
+ const existingTags = context.getValue(LangfuseOtelContextKeys["tags"]);
11622
+ if (existingTags && Array.isArray(existingTags)) {
11623
+ return [.../* @__PURE__ */ new Set([...existingTags, ...newTags])];
11624
+ } else {
11625
+ return newTags;
11626
+ }
11627
+ }
11628
+ function getContextMergedMetadata(context, newMetadata) {
11629
+ const existingMetadata = context.getValue(
11630
+ LangfuseOtelContextKeys["metadata"]
11631
+ );
11632
+ if (existingMetadata && typeof existingMetadata === "object" && existingMetadata !== null && !Array.isArray(existingMetadata)) {
11633
+ return { ...existingMetadata, ...newMetadata };
11634
+ } else {
11635
+ return newMetadata;
11636
+ }
11637
+ }
11638
+ function isValidPropagatedString(params) {
11639
+ const logger = getGlobalLogger();
11640
+ const { value, attributeName } = params;
11641
+ if (typeof value !== "string") {
11642
+ logger.warn(
11643
+ `Propagated attribute '${attributeName}' must be a string. Dropping value.`
11644
+ );
11645
+ return false;
11646
+ }
11647
+ if (value.length > 200) {
11648
+ logger.warn(
11649
+ `Propagated attribute '${attributeName}' value is over 200 characters (${value.length} chars). Dropping value.`
11650
+ );
11651
+ return false;
11652
+ }
11653
+ return true;
11654
+ }
11655
+ function getContextKeyForPropagatedKey(key) {
11656
+ return LangfuseOtelContextKeys[key];
11657
+ }
11658
+ function getSpanKeyForPropagatedKey(key) {
11659
+ switch (key) {
11660
+ case "userId":
11661
+ return "user.id" /* TRACE_USER_ID */;
11662
+ case "sessionId":
11663
+ return "session.id" /* TRACE_SESSION_ID */;
11664
+ case "version":
11665
+ return "langfuse.version" /* VERSION */;
11666
+ case "metadata":
11667
+ return "langfuse.trace.metadata" /* TRACE_METADATA */;
11668
+ case "tags":
11669
+ return "langfuse.trace.tags" /* TRACE_TAGS */;
11670
+ case "experimentId":
11671
+ return "langfuse.experiment.id" /* EXPERIMENT_ID */;
11672
+ case "experimentName":
11673
+ return "langfuse.experiment.name" /* EXPERIMENT_NAME */;
11674
+ case "experimentMetadata":
11675
+ return "langfuse.experiment.metadata" /* EXPERIMENT_METADATA */;
11676
+ case "experimentDatasetId":
11677
+ return "langfuse.experiment.dataset.id" /* EXPERIMENT_DATASET_ID */;
11678
+ case "experimentItemId":
11679
+ return "langfuse.experiment.item.id" /* EXPERIMENT_ITEM_ID */;
11680
+ case "experimentItemMetadata":
11681
+ return "langfuse.experiment.item.metadata" /* EXPERIMENT_ITEM_METADATA */;
11682
+ case "experimentItemRootObservationId":
11683
+ return "langfuse.experiment.item.root_observation_id" /* EXPERIMENT_ITEM_ROOT_OBSERVATION_ID */;
11684
+ default: {
11685
+ const fallback = key;
11686
+ throw Error("Unhandled propagated key", fallback);
11687
+ }
11688
+ }
11689
+ }
11690
+ function getBaggageKeyForPropagatedKey(key) {
11691
+ switch (key) {
11692
+ case "userId":
11693
+ return `${LANGFUSE_BAGGAGE_PREFIX}user_id`;
11694
+ case "sessionId":
11695
+ return `${LANGFUSE_BAGGAGE_PREFIX}session_id`;
11696
+ case "version":
11697
+ return `${LANGFUSE_BAGGAGE_PREFIX}version`;
11698
+ case "metadata":
11699
+ return `${LANGFUSE_BAGGAGE_PREFIX}metadata`;
11700
+ case "tags":
11701
+ return `${LANGFUSE_BAGGAGE_PREFIX}tags`;
11702
+ case "experimentId":
11703
+ return `${LANGFUSE_BAGGAGE_PREFIX}experiment_id`;
11704
+ case "experimentName":
11705
+ return `${LANGFUSE_BAGGAGE_PREFIX}experiment_name`;
11706
+ case "experimentMetadata":
11707
+ return `${LANGFUSE_BAGGAGE_PREFIX}experiment_metadata`;
11708
+ case "experimentDatasetId":
11709
+ return `${LANGFUSE_BAGGAGE_PREFIX}experiment_dataset_id`;
11710
+ case "experimentItemId":
11711
+ return `${LANGFUSE_BAGGAGE_PREFIX}experiment_item_id`;
11712
+ case "experimentItemMetadata":
11713
+ return `${LANGFUSE_BAGGAGE_PREFIX}experiment_item_metadata`;
11714
+ case "experimentItemRootObservationId":
11715
+ return `${LANGFUSE_BAGGAGE_PREFIX}experiment_item_root_observation_id`;
11716
+ default: {
11717
+ const fallback = key;
11718
+ throw Error("Unhandled propagated key", fallback);
11719
+ }
11720
+ }
11721
+ }
11722
+ function getSpanKeyFromBaggageKey(baggageKey) {
11723
+ if (!baggageKey.startsWith(LANGFUSE_BAGGAGE_PREFIX)) return;
11724
+ const suffix = baggageKey.slice(LANGFUSE_BAGGAGE_PREFIX.length);
11725
+ if (suffix.startsWith("metadata_")) {
11726
+ const metadataKey = suffix.slice("metadata_".length);
11727
+ return `${"langfuse.trace.metadata" /* TRACE_METADATA */}.${metadataKey}`;
11728
+ }
11729
+ switch (suffix) {
11730
+ case "user_id":
11731
+ return getSpanKeyForPropagatedKey("userId");
11732
+ case "session_id":
11733
+ return getSpanKeyForPropagatedKey("sessionId");
11734
+ case "version":
11735
+ return getSpanKeyForPropagatedKey("version");
11736
+ case "tags":
11737
+ return getSpanKeyForPropagatedKey("tags");
11738
+ case "experiment_id":
11739
+ return getSpanKeyForPropagatedKey("experimentId");
11740
+ case "experiment_name":
11741
+ return getSpanKeyForPropagatedKey("experimentName");
11742
+ case "experiment_metadata":
11743
+ return getSpanKeyForPropagatedKey("experimentMetadata");
11744
+ case "experiment_dataset_id":
11745
+ return getSpanKeyForPropagatedKey("experimentDatasetId");
11746
+ case "experiment_item_id":
11747
+ return getSpanKeyForPropagatedKey("experimentItemId");
11748
+ case "experiment_item_metadata":
11749
+ return getSpanKeyForPropagatedKey("experimentItemMetadata");
11750
+ case "experiment_item_root_observation_id":
11751
+ return getSpanKeyForPropagatedKey("experimentItemRootObservationId");
11752
+ }
11753
+ }
11355
11754
  export {
11356
11755
  AccessDeniedError,
11357
11756
  AnnotationQueueObjectType,
@@ -11363,6 +11762,7 @@ export {
11363
11762
  CommentObjectType,
11364
11763
  DatasetStatus,
11365
11764
  Error2 as Error,
11765
+ LANGFUSE_SDK_EXPERIMENT_ENVIRONMENT,
11366
11766
  LANGFUSE_SDK_NAME,
11367
11767
  LANGFUSE_SDK_VERSION,
11368
11768
  LANGFUSE_TRACER_NAME,
@@ -11370,6 +11770,7 @@ export {
11370
11770
  LangfuseAPIError,
11371
11771
  LangfuseAPITimeoutError,
11372
11772
  LangfuseMedia,
11773
+ LangfuseOtelContextKeys,
11373
11774
  LangfuseOtelSpanAttributes,
11374
11775
  LlmAdapter,
11375
11776
  LogLevel,
@@ -11395,6 +11796,8 @@ export {
11395
11796
  comments_exports as comments,
11396
11797
  commons_exports as commons,
11397
11798
  configureGlobalLogger,
11799
+ createExperimentId,
11800
+ createExperimentItemId,
11398
11801
  createLogger,
11399
11802
  datasetItems_exports as datasetItems,
11400
11803
  datasetRunItems_exports as datasetRunItems,
@@ -11402,6 +11805,7 @@ export {
11402
11805
  generateUUID,
11403
11806
  getEnv,
11404
11807
  getGlobalLogger,
11808
+ getPropagatedAttributesFromContext,
11405
11809
  health_exports as health,
11406
11810
  ingestion_exports as ingestion,
11407
11811
  llmConnections_exports as llmConnections,
@@ -11415,12 +11819,14 @@ export {
11415
11819
  projects_exports as projects,
11416
11820
  promptVersion_exports as promptVersion,
11417
11821
  prompts_exports as prompts,
11822
+ propagateAttributes,
11418
11823
  resetGlobalLogger,
11419
11824
  safeSetTimeout,
11420
11825
  scim_exports as scim,
11421
11826
  score_exports as score,
11422
11827
  scoreConfigs_exports as scoreConfigs,
11423
11828
  scoreV2_exports as scoreV2,
11829
+ serializeValue,
11424
11830
  sessions_exports as sessions,
11425
11831
  trace_exports as trace,
11426
11832
  utils_exports as utils