langsmith 0.3.43 → 0.3.44

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.
@@ -692,6 +692,7 @@ async function _evaluate(target, fields) {
692
692
  // Start consuming the results.
693
693
  const results = new ExperimentResults(manager);
694
694
  await results.processData(manager);
695
+ await client.awaitPendingTraceBatches();
695
696
  return results;
696
697
  }
697
698
  async function _forward(fn, example, experimentName, metadata, client, includeAttachments) {
@@ -687,6 +687,7 @@ async function _evaluate(target, fields) {
687
687
  // Start consuming the results.
688
688
  const results = new ExperimentResults(manager);
689
689
  await results.processData(manager);
690
+ await client.awaitPendingTraceBatches();
690
691
  return results;
691
692
  }
692
693
  async function _forward(fn, example, experimentName, metadata, client, includeAttachments) {
@@ -217,5 +217,6 @@ async function evaluateComparative(experiments, options) {
217
217
  return tracedEvaluators.map((evaluator) => caller.call(evaluateAndSubmitFeedback, runs, exampleMap[exampleId], evaluator));
218
218
  });
219
219
  const results = await Promise.all(promises);
220
+ await client.awaitPendingTraceBatches();
220
221
  return { experimentName, results };
221
222
  }
@@ -211,5 +211,6 @@ export async function evaluateComparative(experiments, options) {
211
211
  return tracedEvaluators.map((evaluator) => caller.call(evaluateAndSubmitFeedback, runs, exampleMap[exampleId], evaluator));
212
212
  });
213
213
  const results = await Promise.all(promises);
214
+ await client.awaitPendingTraceBatches();
214
215
  return { experimentName, results };
215
216
  }
@@ -67,7 +67,7 @@ class DynamicRunEvaluator {
67
67
  * @returns A promise that extracts to the evaluation result.
68
68
  */
69
69
  async evaluateRun(run, example, options) {
70
- const sourceRunId = (0, uuid_1.v4)();
70
+ let sourceRunId = (0, uuid_1.v4)();
71
71
  const metadata = {
72
72
  targetRunId: run.id,
73
73
  };
@@ -80,7 +80,11 @@ class DynamicRunEvaluator {
80
80
  const wrappedTraceableFunc = (0, traceable_js_1.traceable)(this.func, {
81
81
  project_name: "evaluators",
82
82
  name: "evaluator",
83
- id: sourceRunId,
83
+ on_end: (runTree) => {
84
+ // If tracing with OTEL, setting run id manually does not work.
85
+ // Instead get it at the end of the run.
86
+ sourceRunId = runTree.id;
87
+ },
84
88
  ...options,
85
89
  });
86
90
  const result = await wrappedTraceableFunc(
@@ -63,7 +63,7 @@ export class DynamicRunEvaluator {
63
63
  * @returns A promise that extracts to the evaluation result.
64
64
  */
65
65
  async evaluateRun(run, example, options) {
66
- const sourceRunId = uuidv4();
66
+ let sourceRunId = uuidv4();
67
67
  const metadata = {
68
68
  targetRunId: run.id,
69
69
  };
@@ -76,7 +76,11 @@ export class DynamicRunEvaluator {
76
76
  const wrappedTraceableFunc = traceable(this.func, {
77
77
  project_name: "evaluators",
78
78
  name: "evaluator",
79
- id: sourceRunId,
79
+ on_end: (runTree) => {
80
+ // If tracing with OTEL, setting run id manually does not work.
81
+ // Instead get it at the end of the run.
82
+ sourceRunId = runTree.id;
83
+ },
80
84
  ...options,
81
85
  });
82
86
  const result = await wrappedTraceableFunc(
@@ -1,35 +1,23 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getOtelTraceIdFromUuid = getOtelTraceIdFromUuid;
4
- exports.getOtelSpanIdFromUuid = getOtelSpanIdFromUuid;
5
- exports.createOtelSpanContextFromRun = createOtelSpanContextFromRun;
3
+ exports.getUuidFromOtelTraceId = getUuidFromOtelTraceId;
4
+ exports.getUuidFromOtelSpanId = getUuidFromOtelSpanId;
6
5
  /**
7
- * Get OpenTelemetry trace ID as hex string from UUID.
8
- * @param uuidStr - The UUID string to convert
9
- * @returns Hex string representation of the trace ID
6
+ * Get UUID string from OpenTelemetry trace ID hex string.
7
+ * @param traceId - The hex string trace ID to convert
8
+ * @returns UUID string representation
10
9
  */
11
- function getOtelTraceIdFromUuid(uuidStr) {
12
- // Use full UUID hex (like Python's uuid_val.hex)
13
- return uuidStr.replace(/-/g, "");
10
+ function getUuidFromOtelTraceId(traceId) {
11
+ // Insert hyphens to convert back to UUID format
12
+ return `${traceId.substring(0, 8)}-${traceId.substring(8, 12)}-${traceId.substring(12, 16)}-${traceId.substring(16, 20)}-${traceId.substring(20, 32)}`;
14
13
  }
15
14
  /**
16
- * Get OpenTelemetry span ID as hex string from UUID.
17
- * @param uuidStr - The UUID string to convert
18
- * @returns Hex string representation of the span ID
15
+ * Get UUID string from OpenTelemetry span ID hex string.
16
+ * @param spanId - The hex string span ID to convert (8 bytes/16 hex chars)
17
+ * @returns UUID string representation with zero padding at the front
19
18
  */
20
- function getOtelSpanIdFromUuid(uuidStr) {
21
- // Convert UUID string to bytes equivalent (first 8 bytes for span ID)
22
- // Like Python's uuid_val.bytes[:8].hex()
23
- const cleanUuid = uuidStr.replace(/-/g, "");
24
- return cleanUuid.substring(0, 16); // First 8 bytes (16 hex chars)
25
- }
26
- function createOtelSpanContextFromRun(run) {
27
- const traceId = getOtelTraceIdFromUuid(run.trace_id ?? run.id);
28
- const spanId = getOtelSpanIdFromUuid(run.id);
29
- return {
30
- traceId,
31
- spanId,
32
- isRemote: false,
33
- traceFlags: 1, // SAMPLED
34
- };
19
+ function getUuidFromOtelSpanId(spanId) {
20
+ // Pad with zeros at the front, then format as UUID
21
+ const paddedHex = spanId.padStart(16, "0");
22
+ return `00000000-0000-0000-${paddedHex.substring(0, 4)}-${paddedHex.substring(4, 16)}`;
35
23
  }
@@ -1,17 +1,12 @@
1
- import type { OTELSpanContext } from "./types.js";
2
1
  /**
3
- * Get OpenTelemetry trace ID as hex string from UUID.
4
- * @param uuidStr - The UUID string to convert
5
- * @returns Hex string representation of the trace ID
2
+ * Get UUID string from OpenTelemetry trace ID hex string.
3
+ * @param traceId - The hex string trace ID to convert
4
+ * @returns UUID string representation
6
5
  */
7
- export declare function getOtelTraceIdFromUuid(uuidStr: string): string;
6
+ export declare function getUuidFromOtelTraceId(traceId: string): string;
8
7
  /**
9
- * Get OpenTelemetry span ID as hex string from UUID.
10
- * @param uuidStr - The UUID string to convert
11
- * @returns Hex string representation of the span ID
8
+ * Get UUID string from OpenTelemetry span ID hex string.
9
+ * @param spanId - The hex string span ID to convert (8 bytes/16 hex chars)
10
+ * @returns UUID string representation with zero padding at the front
12
11
  */
13
- export declare function getOtelSpanIdFromUuid(uuidStr: string): string;
14
- export declare function createOtelSpanContextFromRun(run: {
15
- trace_id?: string;
16
- id: string;
17
- }): OTELSpanContext;
12
+ export declare function getUuidFromOtelSpanId(spanId: string): string;
@@ -1,30 +1,19 @@
1
1
  /**
2
- * Get OpenTelemetry trace ID as hex string from UUID.
3
- * @param uuidStr - The UUID string to convert
4
- * @returns Hex string representation of the trace ID
2
+ * Get UUID string from OpenTelemetry trace ID hex string.
3
+ * @param traceId - The hex string trace ID to convert
4
+ * @returns UUID string representation
5
5
  */
6
- export function getOtelTraceIdFromUuid(uuidStr) {
7
- // Use full UUID hex (like Python's uuid_val.hex)
8
- return uuidStr.replace(/-/g, "");
6
+ export function getUuidFromOtelTraceId(traceId) {
7
+ // Insert hyphens to convert back to UUID format
8
+ return `${traceId.substring(0, 8)}-${traceId.substring(8, 12)}-${traceId.substring(12, 16)}-${traceId.substring(16, 20)}-${traceId.substring(20, 32)}`;
9
9
  }
10
10
  /**
11
- * Get OpenTelemetry span ID as hex string from UUID.
12
- * @param uuidStr - The UUID string to convert
13
- * @returns Hex string representation of the span ID
11
+ * Get UUID string from OpenTelemetry span ID hex string.
12
+ * @param spanId - The hex string span ID to convert (8 bytes/16 hex chars)
13
+ * @returns UUID string representation with zero padding at the front
14
14
  */
15
- export function getOtelSpanIdFromUuid(uuidStr) {
16
- // Convert UUID string to bytes equivalent (first 8 bytes for span ID)
17
- // Like Python's uuid_val.bytes[:8].hex()
18
- const cleanUuid = uuidStr.replace(/-/g, "");
19
- return cleanUuid.substring(0, 16); // First 8 bytes (16 hex chars)
20
- }
21
- export function createOtelSpanContextFromRun(run) {
22
- const traceId = getOtelTraceIdFromUuid(run.trace_id ?? run.id);
23
- const spanId = getOtelSpanIdFromUuid(run.id);
24
- return {
25
- traceId,
26
- spanId,
27
- isRemote: false,
28
- traceFlags: 1, // SAMPLED
29
- };
15
+ export function getUuidFromOtelSpanId(spanId) {
16
+ // Pad with zeros at the front, then format as UUID
17
+ const paddedHex = spanId.padStart(16, "0");
18
+ return `00000000-0000-0000-${paddedHex.substring(0, 4)}-${paddedHex.substring(4, 16)}`;
30
19
  }
package/dist/index.cjs CHANGED
@@ -10,4 +10,4 @@ Object.defineProperty(exports, "overrideFetchImplementation", { enumerable: true
10
10
  var project_js_1 = require("./utils/project.cjs");
11
11
  Object.defineProperty(exports, "getDefaultProjectName", { enumerable: true, get: function () { return project_js_1.getDefaultProjectName; } });
12
12
  // Update using yarn bump-version
13
- exports.__version__ = "0.3.43";
13
+ exports.__version__ = "0.3.44";
package/dist/index.d.ts CHANGED
@@ -3,4 +3,4 @@ export type { Dataset, Example, TracerSession, Run, Feedback, RetrieverOutput, }
3
3
  export { RunTree, type RunTreeConfig } from "./run_trees.js";
4
4
  export { overrideFetchImplementation } from "./singletons/fetch.js";
5
5
  export { getDefaultProjectName } from "./utils/project.js";
6
- export declare const __version__ = "0.3.43";
6
+ export declare const __version__ = "0.3.44";
package/dist/index.js CHANGED
@@ -3,4 +3,4 @@ export { RunTree } from "./run_trees.js";
3
3
  export { overrideFetchImplementation } from "./singletons/fetch.js";
4
4
  export { getDefaultProjectName } from "./utils/project.js";
5
5
  // Update using yarn bump-version
6
- export const __version__ = "0.3.43";
6
+ export const __version__ = "0.3.44";
@@ -778,7 +778,7 @@ function _ensureWriteReplicas(replicas) {
778
778
  if (Array.isArray(replica)) {
779
779
  return {
780
780
  projectName: replica[0],
781
- update: replica[1],
781
+ updates: replica[1],
782
782
  };
783
783
  }
784
784
  return replica;
package/dist/run_trees.js CHANGED
@@ -738,7 +738,7 @@ function _ensureWriteReplicas(replicas) {
738
738
  if (Array.isArray(replica)) {
739
739
  return {
740
740
  projectName: replica[0],
741
- update: replica[1],
741
+ updates: replica[1],
742
742
  };
743
743
  }
744
744
  return replica;
@@ -24,9 +24,8 @@ function maybeCreateOtelContext(runTree, tracer
24
24
  return;
25
25
  }
26
26
  const otel_trace = (0, otel_js_1.getOTELTrace)();
27
- const otel_context = (0, otel_js_1.getOTELContext)();
28
27
  try {
29
- const spanContext = (0, utils_js_1.createOtelSpanContextFromRun)(runTree);
28
+ const activeTraceId = otel_trace.getActiveSpan()?.spanContext()?.traceId;
30
29
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
30
  return (fn) => {
32
31
  const resolvedTracer = tracer ?? otel_trace.getTracer("langsmith", index_js_1.__version__);
@@ -35,10 +34,29 @@ function maybeCreateOtelContext(runTree, tracer
35
34
  attributes[constants_js_2.LANGSMITH_REFERENCE_EXAMPLE_ID] =
36
35
  runTree.reference_example_id;
37
36
  }
37
+ const forceOTELRoot = runTree.extra?.ls_otel_root === true;
38
38
  return resolvedTracer.startActiveSpan(runTree.name, {
39
39
  attributes,
40
+ root: forceOTELRoot,
40
41
  }, () => {
41
- otel_trace.setSpanContext(otel_context.active(), spanContext);
42
+ if (activeTraceId === undefined || forceOTELRoot) {
43
+ const otelSpanId = otel_trace
44
+ .getActiveSpan()
45
+ ?.spanContext()?.spanId;
46
+ if (otelSpanId) {
47
+ const langsmithTraceId = (0, utils_js_1.getUuidFromOtelSpanId)(otelSpanId);
48
+ // Must refetch from our primary async local storage
49
+ const currentRunTree = (0, traceable_js_1.getCurrentRunTree)();
50
+ if (currentRunTree) {
51
+ // This is only for root runs to ensure that trace id
52
+ // and the root run id are returned correctly.
53
+ // This is important for things like leaving feedback on
54
+ // target function runs during evaluation.
55
+ currentRunTree.id = langsmithTraceId;
56
+ currentRunTree.trace_id = langsmithTraceId;
57
+ }
58
+ }
59
+ }
42
60
  return fn();
43
61
  });
44
62
  };
package/dist/traceable.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
2
  import { RunTree, isRunTree, isRunnableConfigLike, } from "./run_trees.js";
3
3
  import { isTracingEnabled } from "./env.js";
4
- import { ROOT, AsyncLocalStorageProviderSingleton, } from "./singletons/traceable.js";
4
+ import { ROOT, AsyncLocalStorageProviderSingleton, getCurrentRunTree, } from "./singletons/traceable.js";
5
5
  import { _LC_CONTEXT_VARIABLES_KEY } from "./singletons/constants.js";
6
6
  import { isKVMap, isReadableStream, isAsyncIterable, isIteratorLike, isThenable, isGenerator, isPromiseMethod, } from "./utils/asserts.js";
7
7
  import { getEnvironmentVariable } from "./utils/env.js";
8
8
  import { __version__ } from "./index.js";
9
9
  import { getOTELTrace, getOTELContext } from "./singletons/otel.js";
10
- import { createOtelSpanContextFromRun } from "./experimental/otel/utils.js";
10
+ import { getUuidFromOtelSpanId } from "./experimental/otel/utils.js";
11
11
  import { LANGSMITH_REFERENCE_EXAMPLE_ID } from "./experimental/otel/constants.js";
12
12
  AsyncLocalStorageProviderSingleton.initializeGlobalInstance(new AsyncLocalStorage());
13
13
  /**
@@ -20,9 +20,8 @@ function maybeCreateOtelContext(runTree, tracer
20
20
  return;
21
21
  }
22
22
  const otel_trace = getOTELTrace();
23
- const otel_context = getOTELContext();
24
23
  try {
25
- const spanContext = createOtelSpanContextFromRun(runTree);
24
+ const activeTraceId = otel_trace.getActiveSpan()?.spanContext()?.traceId;
26
25
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
27
26
  return (fn) => {
28
27
  const resolvedTracer = tracer ?? otel_trace.getTracer("langsmith", __version__);
@@ -31,10 +30,29 @@ function maybeCreateOtelContext(runTree, tracer
31
30
  attributes[LANGSMITH_REFERENCE_EXAMPLE_ID] =
32
31
  runTree.reference_example_id;
33
32
  }
33
+ const forceOTELRoot = runTree.extra?.ls_otel_root === true;
34
34
  return resolvedTracer.startActiveSpan(runTree.name, {
35
35
  attributes,
36
+ root: forceOTELRoot,
36
37
  }, () => {
37
- otel_trace.setSpanContext(otel_context.active(), spanContext);
38
+ if (activeTraceId === undefined || forceOTELRoot) {
39
+ const otelSpanId = otel_trace
40
+ .getActiveSpan()
41
+ ?.spanContext()?.spanId;
42
+ if (otelSpanId) {
43
+ const langsmithTraceId = getUuidFromOtelSpanId(otelSpanId);
44
+ // Must refetch from our primary async local storage
45
+ const currentRunTree = getCurrentRunTree();
46
+ if (currentRunTree) {
47
+ // This is only for root runs to ensure that trace id
48
+ // and the root run id are returned correctly.
49
+ // This is important for things like leaving feedback on
50
+ // target function runs during evaluation.
51
+ currentRunTree.id = langsmithTraceId;
52
+ currentRunTree.trace_id = langsmithTraceId;
53
+ }
54
+ }
55
+ }
38
56
  return fn();
39
57
  });
40
58
  };
@@ -22,7 +22,7 @@ function wrapEvaluator(evaluator) {
22
22
  `See this page for more information: https://docs.smith.langchain.com/evaluation/how_to_guides/vitest_jest`,
23
23
  ].join("\n"));
24
24
  }
25
- const evalRunId = config?.runId ?? config?.id ?? (0, uuid_1.v4)();
25
+ let evalRunId = config?.runId ?? config?.id ?? (0, uuid_1.v4)();
26
26
  let evalResult;
27
27
  if ((0, globals_js_1.trackingEnabled)(context)) {
28
28
  const wrappedEvaluator = (0, traceable_js_1.traceable)(async (_runTree, params) => {
@@ -30,12 +30,21 @@ function wrapEvaluator(evaluator) {
30
30
  }, {
31
31
  id: evalRunId,
32
32
  trace_id: evalRunId,
33
+ on_end: (runTree) => {
34
+ // If tracing with OTEL, setting run id manually does not work.
35
+ // Instead get it at the end of the run.
36
+ evalRunId = runTree.id;
37
+ },
33
38
  reference_example_id: context.currentExample.id,
34
39
  client: context.client,
35
40
  tracingEnabled: true,
36
41
  name: evaluator.name ?? "<evaluator>",
37
42
  project_name: "evaluators",
38
43
  ...config,
44
+ extra: {
45
+ ...config?.extra,
46
+ ls_otel_root: true,
47
+ },
39
48
  });
40
49
  evalResult = await wrappedEvaluator(traceable_js_1.ROOT, input);
41
50
  }
@@ -18,7 +18,7 @@ export function wrapEvaluator(evaluator) {
18
18
  `See this page for more information: https://docs.smith.langchain.com/evaluation/how_to_guides/vitest_jest`,
19
19
  ].join("\n"));
20
20
  }
21
- const evalRunId = config?.runId ?? config?.id ?? v4();
21
+ let evalRunId = config?.runId ?? config?.id ?? v4();
22
22
  let evalResult;
23
23
  if (trackingEnabled(context)) {
24
24
  const wrappedEvaluator = traceable(async (_runTree, params) => {
@@ -26,12 +26,21 @@ export function wrapEvaluator(evaluator) {
26
26
  }, {
27
27
  id: evalRunId,
28
28
  trace_id: evalRunId,
29
+ on_end: (runTree) => {
30
+ // If tracing with OTEL, setting run id manually does not work.
31
+ // Instead get it at the end of the run.
32
+ evalRunId = runTree.id;
33
+ },
29
34
  reference_example_id: context.currentExample.id,
30
35
  client: context.client,
31
36
  tracingEnabled: true,
32
37
  name: evaluator.name ?? "<evaluator>",
33
38
  project_name: "evaluators",
34
39
  ...config,
40
+ extra: {
41
+ ...config?.extra,
42
+ ls_otel_root: true,
43
+ },
35
44
  });
36
45
  evalResult = await wrappedEvaluator(ROOT, input);
37
46
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langsmith",
3
- "version": "0.3.43",
3
+ "version": "0.3.44",
4
4
  "description": "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform.",
5
5
  "packageManager": "yarn@1.22.19",
6
6
  "files": [