langsmith 0.5.23 → 0.5.24

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.
Files changed (88) hide show
  1. package/dist/client.cjs +99 -10
  2. package/dist/client.d.ts +38 -0
  3. package/dist/client.js +100 -11
  4. package/dist/evaluation/_runner.cjs +3 -3
  5. package/dist/evaluation/_runner.js +1 -1
  6. package/dist/evaluation/evaluate_comparative.cjs +10 -10
  7. package/dist/evaluation/evaluate_comparative.js +1 -1
  8. package/dist/evaluation/evaluator.cjs +2 -2
  9. package/dist/evaluation/evaluator.js +1 -1
  10. package/dist/index.cjs +1 -1
  11. package/dist/index.d.ts +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/run_trees.cjs +8 -7
  14. package/dist/run_trees.d.ts +7 -0
  15. package/dist/run_trees.js +7 -6
  16. package/dist/singletons/otel.cjs +3 -2
  17. package/dist/singletons/otel.js +4 -3
  18. package/dist/traceable.cjs +1 -2
  19. package/dist/traceable.js +1 -2
  20. package/dist/utils/_uuid.cjs +2 -2
  21. package/dist/utils/_uuid.js +1 -1
  22. package/dist/utils/env.cjs +33 -0
  23. package/dist/utils/env.d.ts +9 -0
  24. package/dist/utils/env.js +32 -0
  25. package/dist/utils/fast-safe-stringify/index.cjs +10 -35
  26. package/dist/utils/fast-safe-stringify/index.d.ts +14 -1
  27. package/dist/utils/fast-safe-stringify/index.js +10 -35
  28. package/dist/utils/jestlike/index.cjs +5 -5
  29. package/dist/utils/jestlike/index.js +1 -1
  30. package/dist/utils/jestlike/vendor/evaluatedBy.cjs +3 -3
  31. package/dist/utils/jestlike/vendor/evaluatedBy.js +1 -1
  32. package/dist/utils/serialize_worker.cjs +389 -0
  33. package/dist/utils/serialize_worker.d.ts +67 -0
  34. package/dist/utils/serialize_worker.js +383 -0
  35. package/dist/utils/uuid/src/index.cjs +24 -0
  36. package/dist/utils/uuid/src/index.d.ts +10 -0
  37. package/dist/utils/uuid/src/index.js +9 -0
  38. package/dist/utils/uuid/src/max.cjs +3 -0
  39. package/dist/utils/uuid/src/max.d.ts +2 -0
  40. package/dist/utils/uuid/src/max.js +1 -0
  41. package/dist/utils/uuid/src/nil.cjs +3 -0
  42. package/dist/utils/uuid/src/nil.d.ts +2 -0
  43. package/dist/utils/uuid/src/nil.js +1 -0
  44. package/dist/utils/uuid/src/parse.cjs +23 -0
  45. package/dist/utils/uuid/src/parse.d.ts +3 -0
  46. package/dist/utils/uuid/src/parse.js +18 -0
  47. package/dist/utils/uuid/src/regex.cjs +3 -0
  48. package/dist/utils/uuid/src/regex.d.ts +2 -0
  49. package/dist/utils/uuid/src/regex.js +1 -0
  50. package/dist/utils/uuid/src/rng.cjs +10 -0
  51. package/dist/utils/uuid/src/rng.d.ts +1 -0
  52. package/dist/utils/uuid/src/rng.js +7 -0
  53. package/dist/utils/uuid/src/sha1.cjs +75 -0
  54. package/dist/utils/uuid/src/sha1.d.ts +2 -0
  55. package/dist/utils/uuid/src/sha1.js +73 -0
  56. package/dist/utils/uuid/src/stringify.cjs +55 -0
  57. package/dist/utils/uuid/src/stringify.d.ts +3 -0
  58. package/dist/utils/uuid/src/stringify.js +49 -0
  59. package/dist/utils/uuid/src/types.cjs +2 -0
  60. package/dist/utils/uuid/src/types.d.ts +22 -0
  61. package/dist/utils/uuid/src/types.js +1 -0
  62. package/dist/utils/uuid/src/v35.cjs +52 -0
  63. package/dist/utils/uuid/src/v35.d.ts +7 -0
  64. package/dist/utils/uuid/src/v35.js +44 -0
  65. package/dist/utils/uuid/src/v4.cjs +40 -0
  66. package/dist/utils/uuid/src/v4.d.ts +4 -0
  67. package/dist/utils/uuid/src/v4.js +35 -0
  68. package/dist/utils/uuid/src/v5.cjs +50 -0
  69. package/dist/utils/uuid/src/v5.d.ts +9 -0
  70. package/dist/utils/uuid/src/v5.js +9 -0
  71. package/dist/utils/uuid/src/v7.cjs +88 -0
  72. package/dist/utils/uuid/src/v7.d.ts +9 -0
  73. package/dist/utils/uuid/src/v7.js +82 -0
  74. package/dist/utils/uuid/src/validate.cjs +10 -0
  75. package/dist/utils/uuid/src/validate.d.ts +2 -0
  76. package/dist/utils/uuid/src/validate.js +5 -0
  77. package/dist/utils/uuid/src/version.cjs +13 -0
  78. package/dist/utils/uuid/src/version.d.ts +2 -0
  79. package/dist/utils/uuid/src/version.js +8 -0
  80. package/dist/utils/worker_threads.browser.cjs +16 -0
  81. package/dist/utils/worker_threads.browser.d.ts +14 -0
  82. package/dist/utils/worker_threads.browser.js +13 -0
  83. package/dist/utils/worker_threads.cjs +16 -0
  84. package/dist/utils/worker_threads.d.ts +13 -0
  85. package/dist/utils/worker_threads.js +13 -0
  86. package/dist/uuid.cjs +2 -2
  87. package/dist/uuid.js +1 -1
  88. package/package.json +4 -4
@@ -14,7 +14,7 @@ const project_js_1 = require("./utils/project.cjs");
14
14
  const env_js_3 = require("./utils/env.cjs");
15
15
  const warn_js_1 = require("./utils/warn.cjs");
16
16
  const _uuid_js_1 = require("./utils/_uuid.cjs");
17
- const uuid_1 = require("uuid");
17
+ const index_js_1 = require("./utils/uuid/src/index.cjs");
18
18
  const TIMESTAMP_LENGTH = 36;
19
19
  // DNS namespace for UUID v5 (same as Python's uuid.NAMESPACE_DNS)
20
20
  const UUID_NAMESPACE_DNS = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
@@ -26,7 +26,7 @@ function getReplicaKey(replica) {
26
26
  const keyData = sortedKeys
27
27
  .map((key) => `${key}:${replica[key] ?? ""}`)
28
28
  .join("|");
29
- return (0, uuid_1.v5)(keyData, UUID_NAMESPACE_DNS);
29
+ return (0, index_js_1.v5)(keyData, UUID_NAMESPACE_DNS);
30
30
  }
31
31
  function stripNonAlphanumeric(input) {
32
32
  return input.replace(/[-:.]/g, "");
@@ -691,8 +691,7 @@ class RunTree {
691
691
  try {
692
692
  const runtimeEnv = (0, env_js_2.getRuntimeEnvironment)();
693
693
  if (this.replicas && this.replicas.length > 0) {
694
- for (const { projectName, apiKey, apiUrl, workspaceId, reroot } of this
695
- .replicas) {
694
+ for (const { projectName, apiKey, apiUrl, workspaceId, reroot, client: replicaClient, } of this.replicas) {
696
695
  const runCreate = this._remapForProject({
697
696
  projectName: projectName ?? this.project_name,
698
697
  runtimeEnv,
@@ -703,7 +702,8 @@ class RunTree {
703
702
  apiKey,
704
703
  workspaceId,
705
704
  });
706
- await this.client.createRun(runCreate, {
705
+ const targetClient = replicaClient ?? this.client;
706
+ await targetClient.createRun(runCreate, {
707
707
  apiKey,
708
708
  apiUrl,
709
709
  workspaceId,
@@ -728,7 +728,7 @@ class RunTree {
728
728
  }
729
729
  async patchRun(options) {
730
730
  if (this.replicas && this.replicas.length > 0) {
731
- for (const { projectName, apiKey, apiUrl, workspaceId, updates, reroot, } of this.replicas) {
731
+ for (const { projectName, apiKey, apiUrl, workspaceId, updates, reroot, client: replicaClient, } of this.replicas) {
732
732
  const runData = this._remapForProject({
733
733
  projectName: projectName ?? this.project_name,
734
734
  runtimeEnv: undefined,
@@ -764,7 +764,8 @@ class RunTree {
764
764
  if (!options?.excludeInputs) {
765
765
  updatePayload.inputs = runData.inputs;
766
766
  }
767
- await this.client.updateRun(runData.id, updatePayload, {
767
+ const targetClient = replicaClient ?? this.client;
768
+ await targetClient.updateRun(runData.id, updatePayload, {
768
769
  apiKey,
769
770
  apiUrl,
770
771
  workspaceId,
@@ -63,6 +63,13 @@ type WriteReplica = {
63
63
  updates?: KVMap | undefined;
64
64
  fromEnv?: boolean;
65
65
  reroot?: boolean;
66
+ /**
67
+ * Optional dedicated Client for this replica.
68
+ * When set, the replica's runs are dispatched through this client
69
+ * (and its tracing mode) instead of the RunTree's default client.
70
+ * Not serialized in distributed-tracing baggage headers.
71
+ */
72
+ client?: Client;
66
73
  };
67
74
  type Replica = ProjectReplica | WriteReplica;
68
75
  export declare class RunTree implements BaseRun {
package/dist/run_trees.js CHANGED
@@ -8,7 +8,7 @@ import { getDefaultProjectName } from "./utils/project.js";
8
8
  import { getLangSmithEnvironmentVariable } from "./utils/env.js";
9
9
  import { warnOnce } from "./utils/warn.js";
10
10
  import { uuid7FromTime, nonCryptographicUuid7Deterministic, } from "./utils/_uuid.js";
11
- import { v5 as uuidv5 } from "uuid";
11
+ import { v5 as uuidv5 } from "./utils/uuid/src/index.js";
12
12
  const TIMESTAMP_LENGTH = 36;
13
13
  // DNS namespace for UUID v5 (same as Python's uuid.NAMESPACE_DNS)
14
14
  const UUID_NAMESPACE_DNS = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
@@ -685,8 +685,7 @@ export class RunTree {
685
685
  try {
686
686
  const runtimeEnv = getRuntimeEnvironment();
687
687
  if (this.replicas && this.replicas.length > 0) {
688
- for (const { projectName, apiKey, apiUrl, workspaceId, reroot } of this
689
- .replicas) {
688
+ for (const { projectName, apiKey, apiUrl, workspaceId, reroot, client: replicaClient, } of this.replicas) {
690
689
  const runCreate = this._remapForProject({
691
690
  projectName: projectName ?? this.project_name,
692
691
  runtimeEnv,
@@ -697,7 +696,8 @@ export class RunTree {
697
696
  apiKey,
698
697
  workspaceId,
699
698
  });
700
- await this.client.createRun(runCreate, {
699
+ const targetClient = replicaClient ?? this.client;
700
+ await targetClient.createRun(runCreate, {
701
701
  apiKey,
702
702
  apiUrl,
703
703
  workspaceId,
@@ -722,7 +722,7 @@ export class RunTree {
722
722
  }
723
723
  async patchRun(options) {
724
724
  if (this.replicas && this.replicas.length > 0) {
725
- for (const { projectName, apiKey, apiUrl, workspaceId, updates, reroot, } of this.replicas) {
725
+ for (const { projectName, apiKey, apiUrl, workspaceId, updates, reroot, client: replicaClient, } of this.replicas) {
726
726
  const runData = this._remapForProject({
727
727
  projectName: projectName ?? this.project_name,
728
728
  runtimeEnv: undefined,
@@ -758,7 +758,8 @@ export class RunTree {
758
758
  if (!options?.excludeInputs) {
759
759
  updatePayload.inputs = runData.inputs;
760
760
  }
761
- await this.client.updateRun(runData.id, updatePayload, {
761
+ const targetClient = replicaClient ?? this.client;
762
+ await targetClient.updateRun(runData.id, updatePayload, {
762
763
  apiKey,
763
764
  apiUrl,
764
765
  workspaceId,
@@ -18,8 +18,9 @@ class MockTracer {
18
18
  });
19
19
  }
20
20
  startActiveSpan(_name, ...args) {
21
- if (!this.hasWarned && (0, env_js_1.getOtelEnabled)()) {
22
- console.warn("You have enabled OTEL export via the `OTEL_ENABLED` or `LANGSMITH_OTEL_ENABLED` environment variable, but have not initialized the required OTEL instances. " +
21
+ if (!this.hasWarned && (0, env_js_1.resolveTracingMode)() === "otel") {
22
+ console.warn("OTel tracing mode is active (via LANGSMITH_TRACING_MODE, OTEL_ENABLED, or " +
23
+ "LANGSMITH_OTEL_ENABLED), but the required OTEL instances have not been initialized. " +
23
24
  'Please add:\n```\nimport { initializeOTEL } from "langsmith/experimental/otel/setup";\ninitializeOTEL();\n```\nat the beginning of your code.');
24
25
  this.hasWarned = true;
25
26
  }
@@ -1,5 +1,5 @@
1
1
  // Should not import any OTEL packages to avoid pulling in optional deps.
2
- import { getOtelEnabled } from "../utils/env.js";
2
+ import { resolveTracingMode } from "../utils/env.js";
3
3
  class MockTracer {
4
4
  constructor() {
5
5
  Object.defineProperty(this, "hasWarned", {
@@ -10,8 +10,9 @@ class MockTracer {
10
10
  });
11
11
  }
12
12
  startActiveSpan(_name, ...args) {
13
- if (!this.hasWarned && getOtelEnabled()) {
14
- console.warn("You have enabled OTEL export via the `OTEL_ENABLED` or `LANGSMITH_OTEL_ENABLED` environment variable, but have not initialized the required OTEL instances. " +
13
+ if (!this.hasWarned && resolveTracingMode() === "otel") {
14
+ console.warn("OTel tracing mode is active (via LANGSMITH_TRACING_MODE, OTEL_ENABLED, or " +
15
+ "LANGSMITH_OTEL_ENABLED), but the required OTEL instances have not been initialized. " +
15
16
  'Please add:\n```\nimport { initializeOTEL } from "langsmith/experimental/otel/setup";\ninitializeOTEL();\n```\nat the beginning of your code.');
16
17
  this.hasWarned = true;
17
18
  }
@@ -8,7 +8,6 @@ const env_js_1 = require("./env.cjs");
8
8
  const traceable_js_1 = require("./singletons/traceable.cjs");
9
9
  const constants_js_1 = require("./singletons/constants.cjs");
10
10
  const asserts_js_1 = require("./utils/asserts.cjs");
11
- const env_js_2 = require("./utils/env.cjs");
12
11
  const index_js_1 = require("./index.cjs");
13
12
  const otel_js_1 = require("./singletons/otel.cjs");
14
13
  const utils_js_1 = require("./experimental/otel/utils.cjs");
@@ -20,7 +19,7 @@ traceable_js_1.AsyncLocalStorageProviderSingleton.initializeGlobalInstance(new n
20
19
  function maybeCreateOtelContext(runTree, projectName, tracer
21
20
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
21
  ) {
23
- if (!runTree || !(0, env_js_2.getOtelEnabled)()) {
22
+ if (!runTree || runTree.client.tracingMode !== "otel") {
24
23
  return;
25
24
  }
26
25
  const otel_trace = (0, otel_js_1.getOTELTrace)();
package/dist/traceable.js CHANGED
@@ -4,7 +4,6 @@ import { isTracingEnabled } from "./env.js";
4
4
  import { ROOT, AsyncLocalStorageProviderSingleton, getCurrentRunTree, } from "./singletons/traceable.js";
5
5
  import { _LC_CHILD_RUN_END_PROMISES_KEY, _LC_CONTEXT_VARIABLES_KEY, } from "./singletons/constants.js";
6
6
  import { isKVMap, isReadableStream, isAsyncIterable, isIteratorLike, isThenable, isGenerator, isPromiseMethod, } from "./utils/asserts.js";
7
- import { getOtelEnabled } from "./utils/env.js";
8
7
  import { __version__ } from "./index.js";
9
8
  import { getOTELTrace, getOTELContext } from "./singletons/otel.js";
10
9
  import { getUuidFromOtelSpanId } from "./experimental/otel/utils.js";
@@ -16,7 +15,7 @@ AsyncLocalStorageProviderSingleton.initializeGlobalInstance(new AsyncLocalStorag
16
15
  function maybeCreateOtelContext(runTree, projectName, tracer
17
16
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
18
17
  ) {
19
- if (!runTree || !getOtelEnabled()) {
18
+ if (!runTree || runTree.client.tracingMode !== "otel") {
20
19
  return;
21
20
  }
22
21
  const otel_trace = getOTELTrace();
@@ -7,7 +7,7 @@ exports.warnIfNotUuidV7 = warnIfNotUuidV7;
7
7
  exports.nonCryptographicUuid7Deterministic = nonCryptographicUuid7Deterministic;
8
8
  // Relaxed UUID validation regex (allows any valid UUID format including nil UUIDs)
9
9
  const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10
- const uuid_1 = require("uuid");
10
+ const index_js_1 = require("./uuid/src/index.cjs");
11
11
  const warn_js_1 = require("./warn.cjs");
12
12
  const xxhash_js_1 = require("./xxhash/xxhash.cjs");
13
13
  let UUID7_WARNING_EMITTED = false;
@@ -34,7 +34,7 @@ function uuid7FromTime(timestamp) {
34
34
  // may not set the internal timestamp used for stringification.
35
35
  // Providing a seq ensures the implementation updates its internal state
36
36
  // and encodes the provided milliseconds into the UUID bytes.
37
- return (0, uuid_1.v7)({ msecs, seq: 0 });
37
+ return (0, index_js_1.v7)({ msecs, seq: 0 });
38
38
  }
39
39
  /**
40
40
  * Get the version of a UUID string.
@@ -1,6 +1,6 @@
1
1
  // Relaxed UUID validation regex (allows any valid UUID format including nil UUIDs)
2
2
  const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
3
- import { v7 as uuidv7 } from "uuid";
3
+ import { v7 as uuidv7 } from "./uuid/src/index.js";
4
4
  import { warnOnce } from "./warn.js";
5
5
  import { XXH3_128, xxh128ToBytes } from "./xxhash/xxhash.js";
6
6
  let UUID7_WARNING_EMITTED = false;
@@ -9,6 +9,7 @@ exports.getLangSmithEnvironmentVariable = getLangSmithEnvironmentVariable;
9
9
  exports.setEnvironmentVariable = setEnvironmentVariable;
10
10
  exports.getShas = getShas;
11
11
  exports.getOtelEnabled = getOtelEnabled;
12
+ exports.resolveTracingMode = resolveTracingMode;
12
13
  // Inlined from https://github.com/flexdinesh/browser-or-node
13
14
  const index_js_1 = require("../index.cjs");
14
15
  let globalEnv;
@@ -218,3 +219,35 @@ function getOtelEnabled() {
218
219
  return (getEnvironmentVariable("OTEL_ENABLED") === "true" ||
219
220
  getLangSmithEnvironmentVariable("OTEL_ENABLED") === "true");
220
221
  }
222
+ const _VALID_TRACING_MODES = new Set(["langsmith", "otel"]);
223
+ /**
224
+ * Resolve the effective tracing mode from an explicit config value and
225
+ * environment variables.
226
+ *
227
+ * Priority: explicit argument > `LANGSMITH_TRACING_MODE` env var >
228
+ * legacy `OTEL_ENABLED` / `LANGSMITH_OTEL_ENABLED` env vars > `"langsmith"`.
229
+ */
230
+ function resolveTracingMode(configValue) {
231
+ if (configValue !== undefined) {
232
+ return configValue;
233
+ }
234
+ const envMode = getLangSmithEnvironmentVariable("TRACING_MODE");
235
+ if (envMode !== undefined && envMode !== "") {
236
+ const lower = envMode.toLowerCase();
237
+ if (!_VALID_TRACING_MODES.has(lower)) {
238
+ throw new Error(`Invalid LANGSMITH_TRACING_MODE=${JSON.stringify(envMode)}. ` +
239
+ `Must be one of: ${[..._VALID_TRACING_MODES].sort().join(", ")}`);
240
+ }
241
+ if (getOtelEnabled()) {
242
+ console.warn("Both LANGSMITH_TRACING_MODE and the legacy OTEL_ENABLED / " +
243
+ "LANGSMITH_OTEL_ENABLED env vars are set. " +
244
+ "LANGSMITH_TRACING_MODE takes precedence.");
245
+ }
246
+ return lower;
247
+ }
248
+ // Fall back to legacy env var
249
+ if (getOtelEnabled()) {
250
+ return "otel";
251
+ }
252
+ return "langsmith";
253
+ }
@@ -48,4 +48,13 @@ interface ICommitSHAs {
48
48
  */
49
49
  export declare function getShas(): ICommitSHAs;
50
50
  export declare function getOtelEnabled(): boolean;
51
+ export type TracingMode = "langsmith" | "otel";
52
+ /**
53
+ * Resolve the effective tracing mode from an explicit config value and
54
+ * environment variables.
55
+ *
56
+ * Priority: explicit argument > `LANGSMITH_TRACING_MODE` env var >
57
+ * legacy `OTEL_ENABLED` / `LANGSMITH_OTEL_ENABLED` env vars > `"langsmith"`.
58
+ */
59
+ export declare function resolveTracingMode(configValue?: TracingMode): TracingMode;
51
60
  export {};
package/dist/utils/env.js CHANGED
@@ -201,3 +201,35 @@ export function getOtelEnabled() {
201
201
  return (getEnvironmentVariable("OTEL_ENABLED") === "true" ||
202
202
  getLangSmithEnvironmentVariable("OTEL_ENABLED") === "true");
203
203
  }
204
+ const _VALID_TRACING_MODES = new Set(["langsmith", "otel"]);
205
+ /**
206
+ * Resolve the effective tracing mode from an explicit config value and
207
+ * environment variables.
208
+ *
209
+ * Priority: explicit argument > `LANGSMITH_TRACING_MODE` env var >
210
+ * legacy `OTEL_ENABLED` / `LANGSMITH_OTEL_ENABLED` env vars > `"langsmith"`.
211
+ */
212
+ export function resolveTracingMode(configValue) {
213
+ if (configValue !== undefined) {
214
+ return configValue;
215
+ }
216
+ const envMode = getLangSmithEnvironmentVariable("TRACING_MODE");
217
+ if (envMode !== undefined && envMode !== "") {
218
+ const lower = envMode.toLowerCase();
219
+ if (!_VALID_TRACING_MODES.has(lower)) {
220
+ throw new Error(`Invalid LANGSMITH_TRACING_MODE=${JSON.stringify(envMode)}. ` +
221
+ `Must be one of: ${[..._VALID_TRACING_MODES].sort().join(", ")}`);
222
+ }
223
+ if (getOtelEnabled()) {
224
+ console.warn("Both LANGSMITH_TRACING_MODE and the legacy OTEL_ENABLED / " +
225
+ "LANGSMITH_OTEL_ENABLED env vars are set. " +
226
+ "LANGSMITH_TRACING_MODE takes precedence.");
227
+ }
228
+ return lower;
229
+ }
230
+ // Fall back to legacy env var
231
+ if (getOtelEnabled()) {
232
+ return "otel";
233
+ }
234
+ return "langsmith";
235
+ }
@@ -61,44 +61,13 @@ function createDefaultReplacer(userReplacer) {
61
61
  return serializeWellKnownTypes(val);
62
62
  };
63
63
  }
64
- /**
65
- * Estimate the serialized JSON byte size of a value without actually
66
- * serializing it. Used on hot paths (enqueuing runs for batched tracing)
67
- * where the exact serialized size is not required -- only a reasonable
68
- * approximation for soft memory accounting.
69
- *
70
- * Walks the object graph in O(n) without allocating a JSON string,
71
- * avoiding the event-loop blocking that JSON.stringify causes on large
72
- * payloads.
73
- *
74
- * Accuracy notes (all estimates are approximate, never exact):
75
- * - Strings: UTF-8 byte length via Buffer.byteLength when available,
76
- * falling back to code-unit length for non-Node environments. Does
77
- * not account for escape expansion (\", \\, control chars, surrogate
78
- * escapes) which is usually a small fraction of total size.
79
- * - Binary data (Buffer / typed arrays / ArrayBuffer / DataView): sized
80
- * from their JSON.stringify representations where practical
81
- * ({ type: "Buffer", data: [...] } for Buffer, keyed objects for typed
82
- * arrays). DataView and ArrayBuffer themselves have no enumerable own
83
- * properties and serialize as "{}". Each byte contributes ~3.5
84
- * characters on average in Buffer's decimal-array representation
85
- * (digit(s) + comma).
86
- * - Other objects with toJSON(): we invoke toJSON() once and estimate
87
- * the result. This matches JSON.stringify semantics for libraries
88
- * like Decimal.js, Moment, Luxon, Mongoose documents, etc.
89
- * - Cycles: detected via an ancestor-path set that is pushed/popped
90
- * during recursion. This matches JSON.stringify semantics --
91
- * repeated references that are *not* on the current ancestor chain
92
- * (shared subobjects) are counted every time they appear, because
93
- * JSON.stringify will serialize them every time.
94
- * - No depth limit (JSON.stringify has none either).
95
- */
96
64
  function estimateSerializedSize(value) {
97
65
  try {
98
66
  // Ancestor set for cycle detection. An object is only treated as
99
67
  // circular if it appears on the current recursion path, not merely
100
68
  // if it has been seen before elsewhere in the graph.
101
69
  const ancestors = new Set();
70
+ let maxStringLen = 0;
102
71
  // In Node / Bun, Buffer.byteLength is a fast native way to get UTF-8
103
72
  // byte length without allocating an encoded copy. In other runtimes
104
73
  // we fall back to code-unit length (a small under-estimate for
@@ -108,7 +77,10 @@ function estimateSerializedSize(value) {
108
77
  : (s) => s.length;
109
78
  function estimateString(s) {
110
79
  // +2 for the surrounding quotes. Escape expansion is not counted.
111
- return byteLen(s) + 2;
80
+ const n = byteLen(s);
81
+ if (n > maxStringLen)
82
+ maxStringLen = n;
83
+ return n + 2;
112
84
  }
113
85
  // Size of a byte sequence when rendered as a JSON array of decimal
114
86
  // numbers: "[b0,b1,b2,...]". Each byte averages ~3.5 chars (value 0-9
@@ -279,13 +251,16 @@ function estimateSerializedSize(value) {
279
251
  ancestors.delete(obj);
280
252
  return size;
281
253
  }
282
- return estimate(value);
254
+ const size = estimate(value);
255
+ return { size, maxStringLen };
283
256
  }
284
257
  catch {
285
258
  // If the estimator itself hits an unexpected edge case, fall back to the
286
259
  // exact serialized size. This preserves correctness of queue-size
287
260
  // accounting at the cost of a slower hot path for that one payload.
288
- return serialize(value).length;
261
+ // We cannot cheaply recover maxStringLen here, so report 0: the worker
262
+ // gate will then fall back to sync serialization, which is safe.
263
+ return { size: serialize(value).length, maxStringLen: 0 };
289
264
  }
290
265
  }
291
266
  // Regular stringify
@@ -30,5 +30,18 @@
30
30
  * JSON.stringify will serialize them every time.
31
31
  * - No depth limit (JSON.stringify has none either).
32
32
  */
33
- export declare function estimateSerializedSize(value: unknown): number;
33
+ export interface EstimatedSize {
34
+ /** Approximate serialized JSON byte size. */
35
+ size: number;
36
+ /**
37
+ * Length (in UTF-8 bytes) of the longest single string value encountered
38
+ * anywhere in the payload graph. Callers can use this as a shape-aware
39
+ * dispatch signal -- for example, to decide whether to offload serialize
40
+ * to a worker thread (which only pays off when a payload contains one
41
+ * or more large strings, since V8 shares string storage across isolates
42
+ * via refcount).
43
+ */
44
+ maxStringLen: number;
45
+ }
46
+ export declare function estimateSerializedSize(value: unknown): EstimatedSize;
34
47
  export declare function serialize(obj: any, errorContext?: any, replacer?: any, spacer?: any, options?: any): Uint8Array<ArrayBuffer>;
@@ -57,44 +57,13 @@ function createDefaultReplacer(userReplacer) {
57
57
  return serializeWellKnownTypes(val);
58
58
  };
59
59
  }
60
- /**
61
- * Estimate the serialized JSON byte size of a value without actually
62
- * serializing it. Used on hot paths (enqueuing runs for batched tracing)
63
- * where the exact serialized size is not required -- only a reasonable
64
- * approximation for soft memory accounting.
65
- *
66
- * Walks the object graph in O(n) without allocating a JSON string,
67
- * avoiding the event-loop blocking that JSON.stringify causes on large
68
- * payloads.
69
- *
70
- * Accuracy notes (all estimates are approximate, never exact):
71
- * - Strings: UTF-8 byte length via Buffer.byteLength when available,
72
- * falling back to code-unit length for non-Node environments. Does
73
- * not account for escape expansion (\", \\, control chars, surrogate
74
- * escapes) which is usually a small fraction of total size.
75
- * - Binary data (Buffer / typed arrays / ArrayBuffer / DataView): sized
76
- * from their JSON.stringify representations where practical
77
- * ({ type: "Buffer", data: [...] } for Buffer, keyed objects for typed
78
- * arrays). DataView and ArrayBuffer themselves have no enumerable own
79
- * properties and serialize as "{}". Each byte contributes ~3.5
80
- * characters on average in Buffer's decimal-array representation
81
- * (digit(s) + comma).
82
- * - Other objects with toJSON(): we invoke toJSON() once and estimate
83
- * the result. This matches JSON.stringify semantics for libraries
84
- * like Decimal.js, Moment, Luxon, Mongoose documents, etc.
85
- * - Cycles: detected via an ancestor-path set that is pushed/popped
86
- * during recursion. This matches JSON.stringify semantics --
87
- * repeated references that are *not* on the current ancestor chain
88
- * (shared subobjects) are counted every time they appear, because
89
- * JSON.stringify will serialize them every time.
90
- * - No depth limit (JSON.stringify has none either).
91
- */
92
60
  export function estimateSerializedSize(value) {
93
61
  try {
94
62
  // Ancestor set for cycle detection. An object is only treated as
95
63
  // circular if it appears on the current recursion path, not merely
96
64
  // if it has been seen before elsewhere in the graph.
97
65
  const ancestors = new Set();
66
+ let maxStringLen = 0;
98
67
  // In Node / Bun, Buffer.byteLength is a fast native way to get UTF-8
99
68
  // byte length without allocating an encoded copy. In other runtimes
100
69
  // we fall back to code-unit length (a small under-estimate for
@@ -104,7 +73,10 @@ export function estimateSerializedSize(value) {
104
73
  : (s) => s.length;
105
74
  function estimateString(s) {
106
75
  // +2 for the surrounding quotes. Escape expansion is not counted.
107
- return byteLen(s) + 2;
76
+ const n = byteLen(s);
77
+ if (n > maxStringLen)
78
+ maxStringLen = n;
79
+ return n + 2;
108
80
  }
109
81
  // Size of a byte sequence when rendered as a JSON array of decimal
110
82
  // numbers: "[b0,b1,b2,...]". Each byte averages ~3.5 chars (value 0-9
@@ -275,13 +247,16 @@ export function estimateSerializedSize(value) {
275
247
  ancestors.delete(obj);
276
248
  return size;
277
249
  }
278
- return estimate(value);
250
+ const size = estimate(value);
251
+ return { size, maxStringLen };
279
252
  }
280
253
  catch {
281
254
  // If the estimator itself hits an unexpected edge case, fall back to the
282
255
  // exact serialized size. This preserves correctness of queue-size
283
256
  // accounting at the cost of a slower hot path for that one payload.
284
- return serialize(value).length;
257
+ // We cannot cheaply recover maxStringLen here, so report 0: the worker
258
+ // gate will then fall back to sync serialization, which is safe.
259
+ return { size: serialize(value).length, maxStringLen: 0 };
285
260
  }
286
261
  }
287
262
  // Regular stringify
@@ -48,7 +48,7 @@ exports._objectHash = _objectHash;
48
48
  exports.generateWrapperFromJestlikeMethods = generateWrapperFromJestlikeMethods;
49
49
  exports.isInTestContext = isInTestContext;
50
50
  const crypto_1 = __importDefault(require("crypto"));
51
- const uuid_1 = require("uuid");
51
+ const index_js_1 = require("../uuid/src/index.cjs");
52
52
  const os = __importStar(require("node:os"));
53
53
  const path = __importStar(require("node:path"));
54
54
  const fs = __importStar(require("node:fs/promises"));
@@ -137,7 +137,7 @@ function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
137
137
  catch (e) {
138
138
  // Naming collision
139
139
  if (e?.name === "LangSmithConflictError") {
140
- const ent = (0, uuid_1.v4)().slice(0, 6);
140
+ const ent = (0, index_js_1.v4)().slice(0, 6);
141
141
  experimentName = `${experimentName}-${ent}`;
142
142
  }
143
143
  else {
@@ -158,7 +158,7 @@ function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
158
158
  inputsHash: _objectHash(inputs),
159
159
  outputsHash: _objectHash(outputs ?? {}),
160
160
  });
161
- return (0, uuid_1.v5)(identifier, constants_js_1.UUID5_NAMESPACE);
161
+ return (0, index_js_1.v5)(identifier, constants_js_1.UUID5_NAMESPACE);
162
162
  }
163
163
  async function syncExample(params) {
164
164
  const { client, exampleId, inputs, outputs, metadata, split, createdAt, datasetId, } = params;
@@ -268,7 +268,7 @@ function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
268
268
  });
269
269
  return method(suiteName, () => {
270
270
  const startTime = new Date();
271
- const suiteUuid = (0, uuid_1.v4)();
271
+ const suiteUuid = (0, index_js_1.v4)();
272
272
  const environment = experimentConfig?.metadata?.ENVIRONMENT ??
273
273
  (0, env_js_1.getEnvironmentVariable)("ENVIRONMENT");
274
274
  const nodeEnv = experimentConfig?.metadata?.NODE_ENV ??
@@ -418,7 +418,7 @@ function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
418
418
  }
419
419
  const totalRuns = config?.repetitions ?? 1;
420
420
  for (let i = 0; i < totalRuns; i += 1) {
421
- const testUuid = (0, uuid_1.v4)().replace(/-/g, "").slice(0, 13);
421
+ const testUuid = (0, index_js_1.v4)().replace(/-/g, "").slice(0, 13);
422
422
  // Jest will not group tests under the same "describe" group if you await the test and
423
423
  // total runs is greater than 1.
424
424
  const resultsPath = path.join(os.tmpdir(), "langsmith_test_results", `${testUuid}.json`);
@@ -1,7 +1,7 @@
1
1
  /* eslint-disable import/no-extraneous-dependencies */
2
2
  /* eslint-disable @typescript-eslint/no-namespace */
3
3
  import crypto from "crypto";
4
- import { v4, v5 } from "uuid";
4
+ import { v4, v5 } from "../uuid/src/index.js";
5
5
  import * as os from "node:os";
6
6
  import * as path from "node:path";
7
7
  import * as fs from "node:fs/promises";
@@ -4,7 +4,7 @@ exports.wrapEvaluator = wrapEvaluator;
4
4
  exports.evaluatedBy = evaluatedBy;
5
5
  const traceable_js_1 = require("../../../traceable.cjs");
6
6
  const globals_js_1 = require("../globals.cjs");
7
- const uuid_1 = require("uuid");
7
+ const index_js_1 = require("../../uuid/src/index.cjs");
8
8
  function isEvaluationResult(x) {
9
9
  return (x != null &&
10
10
  typeof x === "object" &&
@@ -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
- let evalRunId = config?.runId ?? config?.id ?? (0, uuid_1.v7)();
25
+ let evalRunId = config?.runId ?? config?.id ?? (0, index_js_1.v7)();
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) => {
@@ -83,7 +83,7 @@ async function evaluatedBy(outputs, evaluator) {
83
83
  ].join("\n"));
84
84
  }
85
85
  const wrappedEvaluator = wrapEvaluator(evaluator);
86
- const evalRunId = (0, uuid_1.v7)();
86
+ const evalRunId = (0, index_js_1.v7)();
87
87
  const evalResult = await wrappedEvaluator({
88
88
  inputs: context.currentExample?.inputs ?? {},
89
89
  referenceOutputs: context?.currentExample?.outputs ?? {},
@@ -1,6 +1,6 @@
1
1
  import { ROOT, traceable } from "../../../traceable.js";
2
2
  import { testWrapperAsyncLocalStorageInstance, _logTestFeedback, trackingEnabled, } from "../globals.js";
3
- import { v7 } from "uuid";
3
+ import { v7 } from "../../uuid/src/index.js";
4
4
  function isEvaluationResult(x) {
5
5
  return (x != null &&
6
6
  typeof x === "object" &&