langsmith 0.3.79 → 0.3.80-rc.1

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/client.cjs CHANGED
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.Client = exports.DEFAULT_UNCOMPRESSED_BATCH_SIZE_LIMIT_BYTES = exports.AutoBatchQueue = void 0;
36
+ exports.Client = exports.AutoBatchQueue = exports.DEFAULT_MAX_SIZE_BYTES = exports.DEFAULT_UNCOMPRESSED_BATCH_SIZE_LIMIT_BYTES = void 0;
37
37
  exports.mergeRuntimeEnvIntoRun = mergeRuntimeEnvIntoRun;
38
38
  const uuid = __importStar(require("uuid"));
39
39
  const translator_js_1 = require("./experimental/otel/translator.cjs");
@@ -125,8 +125,15 @@ function _formatFeedbackScore(score) {
125
125
  }
126
126
  return score;
127
127
  }
128
+ exports.DEFAULT_UNCOMPRESSED_BATCH_SIZE_LIMIT_BYTES = 24 * 1024 * 1024;
129
+ /** Default maximum memory (1GB) for queue size limits. */
130
+ exports.DEFAULT_MAX_SIZE_BYTES = 1024 * 1024 * 1024; // 1GB
131
+ const SERVER_INFO_REQUEST_TIMEOUT_MS = 10000;
132
+ /** Maximum number of operations to batch in a single request. */
133
+ const DEFAULT_BATCH_SIZE_LIMIT = 100;
134
+ const DEFAULT_API_URL = "https://api.smith.langchain.com";
128
135
  class AutoBatchQueue {
129
- constructor() {
136
+ constructor(maxSizeBytes) {
130
137
  Object.defineProperty(this, "items", {
131
138
  enumerable: true,
132
139
  configurable: true,
@@ -139,6 +146,13 @@ class AutoBatchQueue {
139
146
  writable: true,
140
147
  value: 0
141
148
  });
149
+ Object.defineProperty(this, "maxSizeBytes", {
150
+ enumerable: true,
151
+ configurable: true,
152
+ writable: true,
153
+ value: void 0
154
+ });
155
+ this.maxSizeBytes = maxSizeBytes ?? exports.DEFAULT_MAX_SIZE_BYTES;
142
156
  }
143
157
  peek() {
144
158
  return this.items[0];
@@ -151,6 +165,15 @@ class AutoBatchQueue {
151
165
  itemPromiseResolve = resolve;
152
166
  });
153
167
  const size = (0, index_js_2.serialize)(item.item, `Serializing run with id: ${item.item.id}`).length;
168
+ // Check if adding this item would exceed the size limit
169
+ // Allow the run if the queue is empty (to support large single traces)
170
+ if (this.sizeBytes + size > this.maxSizeBytes && this.items.length > 0) {
171
+ console.warn(`AutoBatchQueue size limit (${this.maxSizeBytes} bytes) exceeded. Dropping run with id: ${item.item.id}. ` +
172
+ `Current queue size: ${this.sizeBytes} bytes, attempted addition: ${size} bytes.`);
173
+ // Resolve immediately to avoid blocking caller
174
+ itemPromiseResolve();
175
+ return itemPromise;
176
+ }
154
177
  this.items.push({
155
178
  action: item.action,
156
179
  payload: item.item,
@@ -197,17 +220,13 @@ class AutoBatchQueue {
197
220
  otelContext: it.otelContext,
198
221
  apiKey: it.apiKey,
199
222
  apiUrl: it.apiUrl,
223
+ size: it.size,
200
224
  })),
201
225
  () => popped.forEach((it) => it.itemPromiseResolve()),
202
226
  ];
203
227
  }
204
228
  }
205
229
  exports.AutoBatchQueue = AutoBatchQueue;
206
- exports.DEFAULT_UNCOMPRESSED_BATCH_SIZE_LIMIT_BYTES = 24 * 1024 * 1024;
207
- const SERVER_INFO_REQUEST_TIMEOUT_MS = 10000;
208
- /** Maximum number of operations to batch in a single request. */
209
- const DEFAULT_BATCH_SIZE_LIMIT = 100;
210
- const DEFAULT_API_URL = "https://api.smith.langchain.com";
211
230
  class Client {
212
231
  get _fetch() {
213
232
  return this.fetchImplementation || (0, fetch_js_1._getFetchImplementation)(this.debug);
@@ -295,7 +314,7 @@ class Client {
295
314
  enumerable: true,
296
315
  configurable: true,
297
316
  writable: true,
298
- value: new AutoBatchQueue()
317
+ value: void 0
299
318
  });
300
319
  Object.defineProperty(this, "autoBatchTimeout", {
301
320
  enumerable: true,
@@ -419,9 +438,12 @@ class Client {
419
438
  }
420
439
  this.debug = config.debug ?? this.debug;
421
440
  this.fetchImplementation = config.fetchImplementation;
441
+ // Use maxIngestMemoryBytes for both queues
442
+ const maxMemory = config.maxIngestMemoryBytes ?? exports.DEFAULT_MAX_SIZE_BYTES;
422
443
  this.batchIngestCaller = new async_caller_js_1.AsyncCaller({
423
444
  maxRetries: 2,
424
445
  maxConcurrency: this.traceBatchConcurrency,
446
+ maxQueueSizeBytes: maxMemory,
425
447
  ...(config.callerOptions ?? {}),
426
448
  onFailedResponseHook: handle429,
427
449
  debug: config.debug ?? this.debug,
@@ -431,6 +453,7 @@ class Client {
431
453
  this.hideOutputs =
432
454
  config.hideOutputs ?? config.anonymizer ?? defaultConfig.hideOutputs;
433
455
  this.autoBatchTracing = config.autoBatchTracing ?? this.autoBatchTracing;
456
+ this.autoBatchQueue = new AutoBatchQueue(maxMemory);
434
457
  this.blockOnRootRunFinalization =
435
458
  config.blockOnRootRunFinalization ?? this.blockOnRootRunFinalization;
436
459
  this.batchSizeBytesLimit = config.batchSizeBytesLimit;
@@ -731,6 +754,8 @@ class Client {
731
754
  if (!batch.length) {
732
755
  return;
733
756
  }
757
+ // Calculate total batch size for queue tracking
758
+ const batchSizeBytes = batch.reduce((sum, item) => sum + (item.size ?? 0), 0);
734
759
  try {
735
760
  if (this.langSmithToOTELTranslator !== undefined) {
736
761
  this._sendBatchToOTELTranslator(batch);
@@ -747,10 +772,17 @@ class Client {
747
772
  const serverInfo = await this._ensureServerInfo();
748
773
  if (serverInfo?.batch_ingest_config?.use_multipart_endpoint) {
749
774
  const useGzip = serverInfo?.instance_flags?.gzip_body_enabled;
750
- await this.multipartIngestRuns(ingestParams, { ...options, useGzip });
775
+ await this.multipartIngestRuns(ingestParams, {
776
+ ...options,
777
+ useGzip,
778
+ sizeBytes: batchSizeBytes,
779
+ });
751
780
  }
752
781
  else {
753
- await this.batchIngestRuns(ingestParams, options);
782
+ await this.batchIngestRuns(ingestParams, {
783
+ ...options,
784
+ sizeBytes: batchSizeBytes,
785
+ });
754
786
  }
755
787
  }
756
788
  }
@@ -1002,7 +1034,7 @@ class Client {
1002
1034
  if (options?.apiKey !== undefined) {
1003
1035
  headers["x-api-key"] = options.apiKey;
1004
1036
  }
1005
- await this.batchIngestCaller.call(async () => {
1037
+ await this.batchIngestCaller.callWithOptions({ sizeBytes: options?.sizeBytes }, async () => {
1006
1038
  const res = await this._fetch(`${options?.apiUrl ?? this.apiUrl}/runs/batch`, {
1007
1039
  method: "POST",
1008
1040
  headers,
@@ -1217,7 +1249,7 @@ class Client {
1217
1249
  const buildBuffered = () => this._createNodeFetchBody(parts, boundary);
1218
1250
  const buildStream = () => this._createMultipartStream(parts, boundary);
1219
1251
  const sendWithRetry = async (bodyFactory) => {
1220
- return this.batchIngestCaller.call(async () => {
1252
+ return this.batchIngestCaller.callWithOptions({ sizeBytes: options?.sizeBytes }, async () => {
1221
1253
  const body = await bodyFactory();
1222
1254
  const headers = {
1223
1255
  ...this.headers,
package/dist/client.d.ts CHANGED
@@ -16,6 +16,11 @@ export interface ClientConfig {
16
16
  batchSizeBytesLimit?: number;
17
17
  /** Maximum number of operations to batch in a single request. */
18
18
  batchSizeLimit?: number;
19
+ /**
20
+ * Maximum total memory (in bytes) for both the AutoBatchQueue and batchIngestCaller queue.
21
+ * When exceeded, runs/batches are dropped. Defaults to 1GB.
22
+ */
23
+ maxIngestMemoryBytes?: number;
19
24
  blockOnRootRunFinalization?: boolean;
20
25
  traceBatchConcurrency?: number;
21
26
  fetchOptions?: RequestInit;
@@ -258,6 +263,7 @@ type AutoBatchQueueItem = {
258
263
  otelContext?: OTELContext;
259
264
  apiKey?: string;
260
265
  apiUrl?: string;
266
+ size?: number;
261
267
  };
262
268
  type Thread = {
263
269
  filter: string;
@@ -275,6 +281,9 @@ type Thread = {
275
281
  last_error: string | null;
276
282
  };
277
283
  export declare function mergeRuntimeEnvIntoRun<T extends RunCreate | RunUpdate>(run: T, cachedEnvVars?: Record<string, string>): T;
284
+ export declare const DEFAULT_UNCOMPRESSED_BATCH_SIZE_LIMIT_BYTES: number;
285
+ /** Default maximum memory (1GB) for queue size limits. */
286
+ export declare const DEFAULT_MAX_SIZE_BYTES: number;
278
287
  export declare class AutoBatchQueue {
279
288
  items: {
280
289
  action: "create" | "update";
@@ -287,6 +296,8 @@ export declare class AutoBatchQueue {
287
296
  apiUrl?: string;
288
297
  }[];
289
298
  sizeBytes: number;
299
+ private maxSizeBytes;
300
+ constructor(maxSizeBytes?: number);
290
301
  peek(): {
291
302
  action: "create" | "update";
292
303
  payload: RunCreate | RunUpdate;
@@ -303,7 +314,6 @@ export declare class AutoBatchQueue {
303
314
  upToSize: number;
304
315
  }): [AutoBatchQueueItem[], () => void];
305
316
  }
306
- export declare const DEFAULT_UNCOMPRESSED_BATCH_SIZE_LIMIT_BYTES: number;
307
317
  export declare class Client implements LangSmithTracingClientInterface {
308
318
  private apiKey?;
309
319
  private apiUrl;
@@ -389,6 +399,7 @@ export declare class Client implements LangSmithTracingClientInterface {
389
399
  }, options?: {
390
400
  apiKey?: string;
391
401
  apiUrl?: string;
402
+ sizeBytes?: number;
392
403
  }): Promise<void>;
393
404
  private _postBatchIngestRuns;
394
405
  /**
@@ -402,6 +413,7 @@ export declare class Client implements LangSmithTracingClientInterface {
402
413
  apiKey?: string;
403
414
  apiUrl?: string;
404
415
  useGzip?: boolean;
416
+ sizeBytes?: number;
405
417
  }): Promise<void>;
406
418
  private _createNodeFetchBody;
407
419
  private _createMultipartStream;
package/dist/client.js CHANGED
@@ -88,8 +88,15 @@ function _formatFeedbackScore(score) {
88
88
  }
89
89
  return score;
90
90
  }
91
+ export const DEFAULT_UNCOMPRESSED_BATCH_SIZE_LIMIT_BYTES = 24 * 1024 * 1024;
92
+ /** Default maximum memory (1GB) for queue size limits. */
93
+ export const DEFAULT_MAX_SIZE_BYTES = 1024 * 1024 * 1024; // 1GB
94
+ const SERVER_INFO_REQUEST_TIMEOUT_MS = 10000;
95
+ /** Maximum number of operations to batch in a single request. */
96
+ const DEFAULT_BATCH_SIZE_LIMIT = 100;
97
+ const DEFAULT_API_URL = "https://api.smith.langchain.com";
91
98
  export class AutoBatchQueue {
92
- constructor() {
99
+ constructor(maxSizeBytes) {
93
100
  Object.defineProperty(this, "items", {
94
101
  enumerable: true,
95
102
  configurable: true,
@@ -102,6 +109,13 @@ export class AutoBatchQueue {
102
109
  writable: true,
103
110
  value: 0
104
111
  });
112
+ Object.defineProperty(this, "maxSizeBytes", {
113
+ enumerable: true,
114
+ configurable: true,
115
+ writable: true,
116
+ value: void 0
117
+ });
118
+ this.maxSizeBytes = maxSizeBytes ?? DEFAULT_MAX_SIZE_BYTES;
105
119
  }
106
120
  peek() {
107
121
  return this.items[0];
@@ -114,6 +128,15 @@ export class AutoBatchQueue {
114
128
  itemPromiseResolve = resolve;
115
129
  });
116
130
  const size = serializePayloadForTracing(item.item, `Serializing run with id: ${item.item.id}`).length;
131
+ // Check if adding this item would exceed the size limit
132
+ // Allow the run if the queue is empty (to support large single traces)
133
+ if (this.sizeBytes + size > this.maxSizeBytes && this.items.length > 0) {
134
+ console.warn(`AutoBatchQueue size limit (${this.maxSizeBytes} bytes) exceeded. Dropping run with id: ${item.item.id}. ` +
135
+ `Current queue size: ${this.sizeBytes} bytes, attempted addition: ${size} bytes.`);
136
+ // Resolve immediately to avoid blocking caller
137
+ itemPromiseResolve();
138
+ return itemPromise;
139
+ }
117
140
  this.items.push({
118
141
  action: item.action,
119
142
  payload: item.item,
@@ -160,16 +183,12 @@ export class AutoBatchQueue {
160
183
  otelContext: it.otelContext,
161
184
  apiKey: it.apiKey,
162
185
  apiUrl: it.apiUrl,
186
+ size: it.size,
163
187
  })),
164
188
  () => popped.forEach((it) => it.itemPromiseResolve()),
165
189
  ];
166
190
  }
167
191
  }
168
- export const DEFAULT_UNCOMPRESSED_BATCH_SIZE_LIMIT_BYTES = 24 * 1024 * 1024;
169
- const SERVER_INFO_REQUEST_TIMEOUT_MS = 10000;
170
- /** Maximum number of operations to batch in a single request. */
171
- const DEFAULT_BATCH_SIZE_LIMIT = 100;
172
- const DEFAULT_API_URL = "https://api.smith.langchain.com";
173
192
  export class Client {
174
193
  get _fetch() {
175
194
  return this.fetchImplementation || _getFetchImplementation(this.debug);
@@ -257,7 +276,7 @@ export class Client {
257
276
  enumerable: true,
258
277
  configurable: true,
259
278
  writable: true,
260
- value: new AutoBatchQueue()
279
+ value: void 0
261
280
  });
262
281
  Object.defineProperty(this, "autoBatchTimeout", {
263
282
  enumerable: true,
@@ -381,9 +400,12 @@ export class Client {
381
400
  }
382
401
  this.debug = config.debug ?? this.debug;
383
402
  this.fetchImplementation = config.fetchImplementation;
403
+ // Use maxIngestMemoryBytes for both queues
404
+ const maxMemory = config.maxIngestMemoryBytes ?? DEFAULT_MAX_SIZE_BYTES;
384
405
  this.batchIngestCaller = new AsyncCaller({
385
406
  maxRetries: 2,
386
407
  maxConcurrency: this.traceBatchConcurrency,
408
+ maxQueueSizeBytes: maxMemory,
387
409
  ...(config.callerOptions ?? {}),
388
410
  onFailedResponseHook: handle429,
389
411
  debug: config.debug ?? this.debug,
@@ -393,6 +415,7 @@ export class Client {
393
415
  this.hideOutputs =
394
416
  config.hideOutputs ?? config.anonymizer ?? defaultConfig.hideOutputs;
395
417
  this.autoBatchTracing = config.autoBatchTracing ?? this.autoBatchTracing;
418
+ this.autoBatchQueue = new AutoBatchQueue(maxMemory);
396
419
  this.blockOnRootRunFinalization =
397
420
  config.blockOnRootRunFinalization ?? this.blockOnRootRunFinalization;
398
421
  this.batchSizeBytesLimit = config.batchSizeBytesLimit;
@@ -693,6 +716,8 @@ export class Client {
693
716
  if (!batch.length) {
694
717
  return;
695
718
  }
719
+ // Calculate total batch size for queue tracking
720
+ const batchSizeBytes = batch.reduce((sum, item) => sum + (item.size ?? 0), 0);
696
721
  try {
697
722
  if (this.langSmithToOTELTranslator !== undefined) {
698
723
  this._sendBatchToOTELTranslator(batch);
@@ -709,10 +734,17 @@ export class Client {
709
734
  const serverInfo = await this._ensureServerInfo();
710
735
  if (serverInfo?.batch_ingest_config?.use_multipart_endpoint) {
711
736
  const useGzip = serverInfo?.instance_flags?.gzip_body_enabled;
712
- await this.multipartIngestRuns(ingestParams, { ...options, useGzip });
737
+ await this.multipartIngestRuns(ingestParams, {
738
+ ...options,
739
+ useGzip,
740
+ sizeBytes: batchSizeBytes,
741
+ });
713
742
  }
714
743
  else {
715
- await this.batchIngestRuns(ingestParams, options);
744
+ await this.batchIngestRuns(ingestParams, {
745
+ ...options,
746
+ sizeBytes: batchSizeBytes,
747
+ });
716
748
  }
717
749
  }
718
750
  }
@@ -964,7 +996,7 @@ export class Client {
964
996
  if (options?.apiKey !== undefined) {
965
997
  headers["x-api-key"] = options.apiKey;
966
998
  }
967
- await this.batchIngestCaller.call(async () => {
999
+ await this.batchIngestCaller.callWithOptions({ sizeBytes: options?.sizeBytes }, async () => {
968
1000
  const res = await this._fetch(`${options?.apiUrl ?? this.apiUrl}/runs/batch`, {
969
1001
  method: "POST",
970
1002
  headers,
@@ -1179,7 +1211,7 @@ export class Client {
1179
1211
  const buildBuffered = () => this._createNodeFetchBody(parts, boundary);
1180
1212
  const buildStream = () => this._createMultipartStream(parts, boundary);
1181
1213
  const sendWithRetry = async (bodyFactory) => {
1182
- return this.batchIngestCaller.call(async () => {
1214
+ return this.batchIngestCaller.callWithOptions({ sizeBytes: options?.sizeBytes }, async () => {
1183
1215
  const body = await bodyFactory();
1184
1216
  const headers = {
1185
1217
  ...this.headers,
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.__version__ = exports.getDefaultProjectName = exports.overrideFetchImplementation = exports.RunTree = exports.Client = void 0;
3
+ exports.__version__ = exports.uuid7FromTime = exports.uuid7 = exports.getDefaultProjectName = exports.overrideFetchImplementation = exports.RunTree = exports.Client = void 0;
4
4
  var client_js_1 = require("./client.cjs");
5
5
  Object.defineProperty(exports, "Client", { enumerable: true, get: function () { return client_js_1.Client; } });
6
6
  var run_trees_js_1 = require("./run_trees.cjs");
@@ -9,5 +9,8 @@ var fetch_js_1 = require("./singletons/fetch.cjs");
9
9
  Object.defineProperty(exports, "overrideFetchImplementation", { enumerable: true, get: function () { return fetch_js_1.overrideFetchImplementation; } });
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
+ var uuid_js_1 = require("./uuid.cjs");
13
+ Object.defineProperty(exports, "uuid7", { enumerable: true, get: function () { return uuid_js_1.uuid7; } });
14
+ Object.defineProperty(exports, "uuid7FromTime", { enumerable: true, get: function () { return uuid_js_1.uuid7FromTime; } });
12
15
  // Update using yarn bump-version
13
- exports.__version__ = "0.3.79";
16
+ exports.__version__ = "0.3.80-rc.1";
package/dist/index.d.ts CHANGED
@@ -3,4 +3,5 @@ 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.79";
6
+ export { uuid7, uuid7FromTime } from "./uuid.js";
7
+ export declare const __version__ = "0.3.80-rc.1";
package/dist/index.js CHANGED
@@ -2,5 +2,6 @@ export { Client, } from "./client.js";
2
2
  export { RunTree } from "./run_trees.js";
3
3
  export { overrideFetchImplementation } from "./singletons/fetch.js";
4
4
  export { getDefaultProjectName } from "./utils/project.js";
5
+ export { uuid7, uuid7FromTime } from "./uuid.js";
5
6
  // Update using yarn bump-version
6
- export const __version__ = "0.3.79";
7
+ export const __version__ = "0.3.80-rc.1";
@@ -1,43 +1,9 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
3
  exports.RunTree = void 0;
37
4
  exports.convertToDottedOrderFormat = convertToDottedOrderFormat;
38
5
  exports.isRunTree = isRunTree;
39
6
  exports.isRunnableConfigLike = isRunnableConfigLike;
40
- const uuid = __importStar(require("uuid"));
41
7
  const client_js_1 = require("./client.cjs");
42
8
  const env_js_1 = require("./env.cjs");
43
9
  const error_js_1 = require("./utils/error.cjs");
@@ -46,16 +12,18 @@ const env_js_2 = require("./utils/env.cjs");
46
12
  const project_js_1 = require("./utils/project.cjs");
47
13
  const env_js_3 = require("./utils/env.cjs");
48
14
  const warn_js_1 = require("./utils/warn.cjs");
15
+ const _uuid_js_1 = require("./utils/_uuid.cjs");
49
16
  function stripNonAlphanumeric(input) {
50
17
  return input.replace(/[-:.]/g, "");
51
18
  }
52
- function convertToDottedOrderFormat(epoch, runId, executionOrder = 1) {
19
+ function getMicrosecondPrecisionDatestring(epoch, executionOrder = 1) {
53
20
  // Date only has millisecond precision, so we use the microseconds to break
54
21
  // possible ties, avoiding incorrect run order
55
22
  const paddedOrder = executionOrder.toFixed(0).slice(0, 3).padStart(3, "0");
56
- const microsecondPrecisionDatestring = `${new Date(epoch)
57
- .toISOString()
58
- .slice(0, -1)}${paddedOrder}Z`;
23
+ return `${new Date(epoch).toISOString().slice(0, -1)}${paddedOrder}Z`;
24
+ }
25
+ function convertToDottedOrderFormat(epoch, runId, executionOrder = 1) {
26
+ const microsecondPrecisionDatestring = getMicrosecondPrecisionDatestring(epoch, executionOrder);
59
27
  return {
60
28
  dottedOrder: stripNonAlphanumeric(microsecondPrecisionDatestring) + runId,
61
29
  microsecondPrecisionDatestring,
@@ -315,6 +283,19 @@ class RunTree {
315
283
  delete config.id;
316
284
  }
317
285
  Object.assign(this, { ...defaultConfig, ...config, client });
286
+ this.execution_order ??= 1;
287
+ this.child_execution_order ??= 1;
288
+ // Generate serialized start time for ID generation
289
+ if (!this.dotted_order) {
290
+ this._serialized_start_time = getMicrosecondPrecisionDatestring(this.start_time, this.execution_order);
291
+ }
292
+ // Generate id from serialized start_time if not provided
293
+ if (!this.id) {
294
+ this.id = (0, _uuid_js_1.uuid7FromTime)(this._serialized_start_time ?? this.start_time);
295
+ }
296
+ if (config.id) {
297
+ (0, _uuid_js_1.warnIfNotUuidV7)(config.id, "run_id");
298
+ }
318
299
  if (!this.trace_id) {
319
300
  if (this.parent_run) {
320
301
  this.trace_id = this.parent_run.trace_id ?? this.id;
@@ -323,18 +304,22 @@ class RunTree {
323
304
  this.trace_id = this.id;
324
305
  }
325
306
  }
307
+ else if (config.trace_id) {
308
+ (0, _uuid_js_1.warnIfNotUuidV7)(config.trace_id, "trace_id");
309
+ }
310
+ if (config.parent_run_id) {
311
+ (0, _uuid_js_1.warnIfNotUuidV7)(config.parent_run_id, "parent_run_id");
312
+ }
326
313
  this.replicas = _ensureWriteReplicas(this.replicas);
327
- this.execution_order ??= 1;
328
- this.child_execution_order ??= 1;
314
+ // Now set the dotted order with the actual ID
329
315
  if (!this.dotted_order) {
330
- const { dottedOrder, microsecondPrecisionDatestring } = convertToDottedOrderFormat(this.start_time, this.id, this.execution_order);
316
+ const { dottedOrder } = convertToDottedOrderFormat(this.start_time, this.id, this.execution_order);
331
317
  if (this.parent_run) {
332
318
  this.dotted_order = this.parent_run.dotted_order + "." + dottedOrder;
333
319
  }
334
320
  else {
335
321
  this.dotted_order = dottedOrder;
336
322
  }
337
- this._serialized_start_time = microsecondPrecisionDatestring;
338
323
  }
339
324
  }
340
325
  set metadata(metadata) {
@@ -350,15 +335,15 @@ class RunTree {
350
335
  return this.extra?.metadata;
351
336
  }
352
337
  static getDefaultConfig() {
338
+ const start_time = Date.now();
353
339
  return {
354
- id: uuid.v4(),
355
340
  run_type: "chain",
356
341
  project_name: (0, project_js_1.getDefaultProjectName)(),
357
342
  child_runs: [],
358
343
  api_url: (0, env_js_2.getEnvironmentVariable)("LANGCHAIN_ENDPOINT") ?? "http://localhost:1984",
359
344
  api_key: (0, env_js_2.getEnvironmentVariable)("LANGCHAIN_API_KEY"),
360
345
  caller_options: {},
361
- start_time: Date.now(),
346
+ start_time,
362
347
  serialized: {},
363
348
  inputs: {},
364
349
  extra: {},
@@ -478,50 +463,10 @@ class RunTree {
478
463
  }
479
464
  _remapForProject(projectName, runtimeEnv, excludeChildRuns = true) {
480
465
  const baseRun = this._convertToCreate(this, runtimeEnv, excludeChildRuns);
481
- if (projectName === this.project_name) {
482
- return baseRun;
483
- }
484
- // Create a deterministic UUID mapping for this project
485
- const createRemappedId = (originalId) => {
486
- return uuid.v5(`${originalId}:${projectName}`, uuid.v5.DNS);
487
- };
488
- // Remap the current run's ID
489
- const newId = createRemappedId(baseRun.id);
490
- const newTraceId = baseRun.trace_id
491
- ? createRemappedId(baseRun.trace_id)
492
- : undefined;
493
- const newParentRunId = baseRun.parent_run_id
494
- ? createRemappedId(baseRun.parent_run_id)
495
- : undefined;
496
- let newDottedOrder;
497
- if (baseRun.dotted_order) {
498
- const segments = _parseDottedOrder(baseRun.dotted_order);
499
- const rebuilt = [];
500
- // Process all segments except the last one
501
- for (let i = 0; i < segments.length - 1; i++) {
502
- const [timestamp, segmentId] = segments[i];
503
- const remappedId = createRemappedId(segmentId);
504
- rebuilt.push(timestamp.toISOString().replace(/[-:]/g, "").replace(".", "") +
505
- remappedId);
506
- }
507
- // Process the last segment with the new run ID
508
- const [lastTimestamp] = segments[segments.length - 1];
509
- rebuilt.push(lastTimestamp.toISOString().replace(/[-:]/g, "").replace(".", "") +
510
- newId);
511
- newDottedOrder = rebuilt.join(".");
512
- }
513
- else {
514
- newDottedOrder = undefined;
515
- }
516
- const remappedRun = {
466
+ return {
517
467
  ...baseRun,
518
- id: newId,
519
- trace_id: newTraceId,
520
- parent_run_id: newParentRunId,
521
- dotted_order: newDottedOrder,
522
468
  session_name: projectName,
523
469
  };
524
- return remappedRun;
525
470
  }
526
471
  async postRun(excludeChildRuns = true) {
527
472
  try {
@@ -559,6 +504,9 @@ class RunTree {
559
504
  const runData = this._remapForProject(projectName ?? this.project_name);
560
505
  const updatePayload = {
561
506
  id: runData.id,
507
+ name: runData.name,
508
+ run_type: runData.run_type,
509
+ start_time: runData.start_time,
562
510
  outputs: runData.outputs,
563
511
  error: runData.error,
564
512
  parent_run_id: runData.parent_run_id,
@@ -589,6 +537,9 @@ class RunTree {
589
537
  else {
590
538
  try {
591
539
  const runUpdate = {
540
+ name: this.name,
541
+ run_type: this.run_type,
542
+ start_time: this._serialized_start_time ?? this.start_time,
592
543
  end_time: this.end_time,
593
544
  error: this.error,
594
545
  outputs: this.outputs,
@@ -770,24 +721,6 @@ function isRunnableConfigLike(x) {
770
721
  // Or it's an array with a LangChainTracerLike object within it
771
722
  containsLangChainTracerLike(x.callbacks)));
772
723
  }
773
- function _parseDottedOrder(dottedOrder) {
774
- const parts = dottedOrder.split(".");
775
- return parts.map((part) => {
776
- const timestampStr = part.slice(0, -36);
777
- const uuidStr = part.slice(-36);
778
- // Parse timestamp: "%Y%m%dT%H%M%S%fZ" format
779
- // Example: "20231215T143045123456Z"
780
- const year = parseInt(timestampStr.slice(0, 4));
781
- const month = parseInt(timestampStr.slice(4, 6)) - 1; // JS months are 0-indexed
782
- const day = parseInt(timestampStr.slice(6, 8));
783
- const hour = parseInt(timestampStr.slice(9, 11));
784
- const minute = parseInt(timestampStr.slice(11, 13));
785
- const second = parseInt(timestampStr.slice(13, 15));
786
- const microsecond = parseInt(timestampStr.slice(15, 21));
787
- const timestamp = new Date(year, month, day, hour, minute, second, microsecond / 1000);
788
- return [timestamp, uuidStr];
789
- });
790
- }
791
724
  function _getWriteReplicasFromEnv() {
792
725
  const envVar = (0, env_js_2.getEnvironmentVariable)("LANGSMITH_RUNS_ENDPOINTS");
793
726
  if (!envVar)
package/dist/run_trees.js CHANGED
@@ -1,4 +1,3 @@
1
- import * as uuid from "uuid";
2
1
  import { Client } from "./client.js";
3
2
  import { isTracingEnabled } from "./env.js";
4
3
  import { isConflictingEndpointsError, ConflictingEndpointsError, } from "./utils/error.js";
@@ -7,16 +6,18 @@ import { getEnvironmentVariable, getRuntimeEnvironment, } from "./utils/env.js";
7
6
  import { getDefaultProjectName } from "./utils/project.js";
8
7
  import { getLangSmithEnvironmentVariable } from "./utils/env.js";
9
8
  import { warnOnce } from "./utils/warn.js";
9
+ import { warnIfNotUuidV7, uuid7FromTime } from "./utils/_uuid.js";
10
10
  function stripNonAlphanumeric(input) {
11
11
  return input.replace(/[-:.]/g, "");
12
12
  }
13
- export function convertToDottedOrderFormat(epoch, runId, executionOrder = 1) {
13
+ function getMicrosecondPrecisionDatestring(epoch, executionOrder = 1) {
14
14
  // Date only has millisecond precision, so we use the microseconds to break
15
15
  // possible ties, avoiding incorrect run order
16
16
  const paddedOrder = executionOrder.toFixed(0).slice(0, 3).padStart(3, "0");
17
- const microsecondPrecisionDatestring = `${new Date(epoch)
18
- .toISOString()
19
- .slice(0, -1)}${paddedOrder}Z`;
17
+ return `${new Date(epoch).toISOString().slice(0, -1)}${paddedOrder}Z`;
18
+ }
19
+ export function convertToDottedOrderFormat(epoch, runId, executionOrder = 1) {
20
+ const microsecondPrecisionDatestring = getMicrosecondPrecisionDatestring(epoch, executionOrder);
20
21
  return {
21
22
  dottedOrder: stripNonAlphanumeric(microsecondPrecisionDatestring) + runId,
22
23
  microsecondPrecisionDatestring,
@@ -276,6 +277,19 @@ export class RunTree {
276
277
  delete config.id;
277
278
  }
278
279
  Object.assign(this, { ...defaultConfig, ...config, client });
280
+ this.execution_order ??= 1;
281
+ this.child_execution_order ??= 1;
282
+ // Generate serialized start time for ID generation
283
+ if (!this.dotted_order) {
284
+ this._serialized_start_time = getMicrosecondPrecisionDatestring(this.start_time, this.execution_order);
285
+ }
286
+ // Generate id from serialized start_time if not provided
287
+ if (!this.id) {
288
+ this.id = uuid7FromTime(this._serialized_start_time ?? this.start_time);
289
+ }
290
+ if (config.id) {
291
+ warnIfNotUuidV7(config.id, "run_id");
292
+ }
279
293
  if (!this.trace_id) {
280
294
  if (this.parent_run) {
281
295
  this.trace_id = this.parent_run.trace_id ?? this.id;
@@ -284,18 +298,22 @@ export class RunTree {
284
298
  this.trace_id = this.id;
285
299
  }
286
300
  }
301
+ else if (config.trace_id) {
302
+ warnIfNotUuidV7(config.trace_id, "trace_id");
303
+ }
304
+ if (config.parent_run_id) {
305
+ warnIfNotUuidV7(config.parent_run_id, "parent_run_id");
306
+ }
287
307
  this.replicas = _ensureWriteReplicas(this.replicas);
288
- this.execution_order ??= 1;
289
- this.child_execution_order ??= 1;
308
+ // Now set the dotted order with the actual ID
290
309
  if (!this.dotted_order) {
291
- const { dottedOrder, microsecondPrecisionDatestring } = convertToDottedOrderFormat(this.start_time, this.id, this.execution_order);
310
+ const { dottedOrder } = convertToDottedOrderFormat(this.start_time, this.id, this.execution_order);
292
311
  if (this.parent_run) {
293
312
  this.dotted_order = this.parent_run.dotted_order + "." + dottedOrder;
294
313
  }
295
314
  else {
296
315
  this.dotted_order = dottedOrder;
297
316
  }
298
- this._serialized_start_time = microsecondPrecisionDatestring;
299
317
  }
300
318
  }
301
319
  set metadata(metadata) {
@@ -311,15 +329,15 @@ export class RunTree {
311
329
  return this.extra?.metadata;
312
330
  }
313
331
  static getDefaultConfig() {
332
+ const start_time = Date.now();
314
333
  return {
315
- id: uuid.v4(),
316
334
  run_type: "chain",
317
335
  project_name: getDefaultProjectName(),
318
336
  child_runs: [],
319
337
  api_url: getEnvironmentVariable("LANGCHAIN_ENDPOINT") ?? "http://localhost:1984",
320
338
  api_key: getEnvironmentVariable("LANGCHAIN_API_KEY"),
321
339
  caller_options: {},
322
- start_time: Date.now(),
340
+ start_time,
323
341
  serialized: {},
324
342
  inputs: {},
325
343
  extra: {},
@@ -439,50 +457,10 @@ export class RunTree {
439
457
  }
440
458
  _remapForProject(projectName, runtimeEnv, excludeChildRuns = true) {
441
459
  const baseRun = this._convertToCreate(this, runtimeEnv, excludeChildRuns);
442
- if (projectName === this.project_name) {
443
- return baseRun;
444
- }
445
- // Create a deterministic UUID mapping for this project
446
- const createRemappedId = (originalId) => {
447
- return uuid.v5(`${originalId}:${projectName}`, uuid.v5.DNS);
448
- };
449
- // Remap the current run's ID
450
- const newId = createRemappedId(baseRun.id);
451
- const newTraceId = baseRun.trace_id
452
- ? createRemappedId(baseRun.trace_id)
453
- : undefined;
454
- const newParentRunId = baseRun.parent_run_id
455
- ? createRemappedId(baseRun.parent_run_id)
456
- : undefined;
457
- let newDottedOrder;
458
- if (baseRun.dotted_order) {
459
- const segments = _parseDottedOrder(baseRun.dotted_order);
460
- const rebuilt = [];
461
- // Process all segments except the last one
462
- for (let i = 0; i < segments.length - 1; i++) {
463
- const [timestamp, segmentId] = segments[i];
464
- const remappedId = createRemappedId(segmentId);
465
- rebuilt.push(timestamp.toISOString().replace(/[-:]/g, "").replace(".", "") +
466
- remappedId);
467
- }
468
- // Process the last segment with the new run ID
469
- const [lastTimestamp] = segments[segments.length - 1];
470
- rebuilt.push(lastTimestamp.toISOString().replace(/[-:]/g, "").replace(".", "") +
471
- newId);
472
- newDottedOrder = rebuilt.join(".");
473
- }
474
- else {
475
- newDottedOrder = undefined;
476
- }
477
- const remappedRun = {
460
+ return {
478
461
  ...baseRun,
479
- id: newId,
480
- trace_id: newTraceId,
481
- parent_run_id: newParentRunId,
482
- dotted_order: newDottedOrder,
483
462
  session_name: projectName,
484
463
  };
485
- return remappedRun;
486
464
  }
487
465
  async postRun(excludeChildRuns = true) {
488
466
  try {
@@ -520,6 +498,9 @@ export class RunTree {
520
498
  const runData = this._remapForProject(projectName ?? this.project_name);
521
499
  const updatePayload = {
522
500
  id: runData.id,
501
+ name: runData.name,
502
+ run_type: runData.run_type,
503
+ start_time: runData.start_time,
523
504
  outputs: runData.outputs,
524
505
  error: runData.error,
525
506
  parent_run_id: runData.parent_run_id,
@@ -550,6 +531,9 @@ export class RunTree {
550
531
  else {
551
532
  try {
552
533
  const runUpdate = {
534
+ name: this.name,
535
+ run_type: this.run_type,
536
+ start_time: this._serialized_start_time ?? this.start_time,
553
537
  end_time: this.end_time,
554
538
  error: this.error,
555
539
  outputs: this.outputs,
@@ -730,24 +714,6 @@ export function isRunnableConfigLike(x) {
730
714
  // Or it's an array with a LangChainTracerLike object within it
731
715
  containsLangChainTracerLike(x.callbacks)));
732
716
  }
733
- function _parseDottedOrder(dottedOrder) {
734
- const parts = dottedOrder.split(".");
735
- return parts.map((part) => {
736
- const timestampStr = part.slice(0, -36);
737
- const uuidStr = part.slice(-36);
738
- // Parse timestamp: "%Y%m%dT%H%M%S%fZ" format
739
- // Example: "20231215T143045123456Z"
740
- const year = parseInt(timestampStr.slice(0, 4));
741
- const month = parseInt(timestampStr.slice(4, 6)) - 1; // JS months are 0-indexed
742
- const day = parseInt(timestampStr.slice(6, 8));
743
- const hour = parseInt(timestampStr.slice(9, 11));
744
- const minute = parseInt(timestampStr.slice(11, 13));
745
- const second = parseInt(timestampStr.slice(13, 15));
746
- const microsecond = parseInt(timestampStr.slice(15, 21));
747
- const timestamp = new Date(year, month, day, hour, minute, second, microsecond / 1000);
748
- return [timestamp, uuidStr];
749
- });
750
- }
751
717
  function _getWriteReplicasFromEnv() {
752
718
  const envVar = getEnvironmentVariable("LANGSMITH_RUNS_ENDPOINTS");
753
719
  if (!envVar)
package/dist/schemas.d.ts CHANGED
@@ -152,6 +152,9 @@ export interface RunCreate extends BaseRun {
152
152
  }
153
153
  export interface RunUpdate {
154
154
  id?: string;
155
+ name?: string;
156
+ run_type?: string;
157
+ start_time?: number | string;
155
158
  end_time?: number | string;
156
159
  extra?: KVMap;
157
160
  tags?: string[];
@@ -1,8 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.assertUuid = assertUuid;
4
+ exports.uuid7FromTime = uuid7FromTime;
5
+ exports.getUuidVersion = getUuidVersion;
6
+ exports.warnIfNotUuidV7 = warnIfNotUuidV7;
4
7
  // Relaxed UUID validation regex (allows any valid UUID format including nil UUIDs)
5
8
  const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
9
+ const uuid_1 = require("uuid");
10
+ const warn_js_1 = require("./warn.cjs");
11
+ let UUID7_WARNING_EMITTED = false;
6
12
  function assertUuid(str, which) {
7
13
  // Use relaxed regex validation instead of strict uuid.validate()
8
14
  // This allows edge cases like nil UUIDs or test UUIDs that might not pass strict validation
@@ -14,3 +20,47 @@ function assertUuid(str, which) {
14
20
  }
15
21
  return str;
16
22
  }
23
+ /**
24
+ * Generate a UUID v7 from a timestamp.
25
+ *
26
+ * @param timestamp - The timestamp in milliseconds
27
+ * @returns A UUID v7 string
28
+ */
29
+ function uuid7FromTime(timestamp) {
30
+ const msecs = typeof timestamp === "string" ? Date.parse(timestamp) : timestamp;
31
+ // Work around uuid@10 behavior where providing only { msecs }
32
+ // may not set the internal timestamp used for stringification.
33
+ // Providing a seq ensures the implementation updates its internal state
34
+ // and encodes the provided milliseconds into the UUID bytes.
35
+ return (0, uuid_1.v7)({ msecs, seq: 0 });
36
+ }
37
+ /**
38
+ * Get the version of a UUID string.
39
+ * @param uuidStr - The UUID string to check
40
+ * @returns The version number (1-7) or null if invalid
41
+ */
42
+ function getUuidVersion(uuidStr) {
43
+ if (!UUID_REGEX.test(uuidStr)) {
44
+ return null;
45
+ }
46
+ // Version is in bits 48-51
47
+ // Format: xxxxxxxx-xxxx-Vxxx-xxxx-xxxxxxxxxxxx
48
+ const versionChar = uuidStr[14];
49
+ return parseInt(versionChar, 16);
50
+ }
51
+ /**
52
+ * Warn if a UUID is not version 7.
53
+ *
54
+ * @param uuidStr - The UUID string to check
55
+ * @param idType - The type of ID (e.g., "run_id", "trace_id") for the warning message
56
+ */
57
+ function warnIfNotUuidV7(uuidStr, _idType) {
58
+ const version = getUuidVersion(uuidStr);
59
+ if (version !== null && version !== 7 && !UUID7_WARNING_EMITTED) {
60
+ UUID7_WARNING_EMITTED = true;
61
+ (0, warn_js_1.warnOnce)(`LangSmith now uses UUID v7 for run and trace identifiers. ` +
62
+ `This warning appears when passing custom IDs. ` +
63
+ `Please use: import { uuidv7 } from 'langsmith'; const id = uuidv7(); ` +
64
+ `Future versions will require UUID v7.`);
65
+ }
66
+ }
@@ -1 +1,21 @@
1
1
  export declare function assertUuid(str: string, which?: string): string;
2
+ /**
3
+ * Generate a UUID v7 from a timestamp.
4
+ *
5
+ * @param timestamp - The timestamp in milliseconds
6
+ * @returns A UUID v7 string
7
+ */
8
+ export declare function uuid7FromTime(timestamp: number | string): string;
9
+ /**
10
+ * Get the version of a UUID string.
11
+ * @param uuidStr - The UUID string to check
12
+ * @returns The version number (1-7) or null if invalid
13
+ */
14
+ export declare function getUuidVersion(uuidStr: string): number | null;
15
+ /**
16
+ * Warn if a UUID is not version 7.
17
+ *
18
+ * @param uuidStr - The UUID string to check
19
+ * @param idType - The type of ID (e.g., "run_id", "trace_id") for the warning message
20
+ */
21
+ export declare function warnIfNotUuidV7(uuidStr: string, _idType: string): void;
@@ -1,5 +1,8 @@
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";
4
+ import { warnOnce } from "./warn.js";
5
+ let UUID7_WARNING_EMITTED = false;
3
6
  export function assertUuid(str, which) {
4
7
  // Use relaxed regex validation instead of strict uuid.validate()
5
8
  // This allows edge cases like nil UUIDs or test UUIDs that might not pass strict validation
@@ -11,3 +14,47 @@ export function assertUuid(str, which) {
11
14
  }
12
15
  return str;
13
16
  }
17
+ /**
18
+ * Generate a UUID v7 from a timestamp.
19
+ *
20
+ * @param timestamp - The timestamp in milliseconds
21
+ * @returns A UUID v7 string
22
+ */
23
+ export function uuid7FromTime(timestamp) {
24
+ const msecs = typeof timestamp === "string" ? Date.parse(timestamp) : timestamp;
25
+ // Work around uuid@10 behavior where providing only { msecs }
26
+ // may not set the internal timestamp used for stringification.
27
+ // Providing a seq ensures the implementation updates its internal state
28
+ // and encodes the provided milliseconds into the UUID bytes.
29
+ return uuidv7({ msecs, seq: 0 });
30
+ }
31
+ /**
32
+ * Get the version of a UUID string.
33
+ * @param uuidStr - The UUID string to check
34
+ * @returns The version number (1-7) or null if invalid
35
+ */
36
+ export function getUuidVersion(uuidStr) {
37
+ if (!UUID_REGEX.test(uuidStr)) {
38
+ return null;
39
+ }
40
+ // Version is in bits 48-51
41
+ // Format: xxxxxxxx-xxxx-Vxxx-xxxx-xxxxxxxxxxxx
42
+ const versionChar = uuidStr[14];
43
+ return parseInt(versionChar, 16);
44
+ }
45
+ /**
46
+ * Warn if a UUID is not version 7.
47
+ *
48
+ * @param uuidStr - The UUID string to check
49
+ * @param idType - The type of ID (e.g., "run_id", "trace_id") for the warning message
50
+ */
51
+ export function warnIfNotUuidV7(uuidStr, _idType) {
52
+ const version = getUuidVersion(uuidStr);
53
+ if (version !== null && version !== 7 && !UUID7_WARNING_EMITTED) {
54
+ UUID7_WARNING_EMITTED = true;
55
+ warnOnce(`LangSmith now uses UUID v7 for run and trace identifiers. ` +
56
+ `This warning appears when passing custom IDs. ` +
57
+ `Please use: import { uuidv7 } from 'langsmith'; const id = uuidv7(); ` +
58
+ `Future versions will require UUID v7.`);
59
+ }
60
+ }
@@ -40,6 +40,12 @@ class AsyncCaller {
40
40
  writable: true,
41
41
  value: void 0
42
42
  });
43
+ Object.defineProperty(this, "maxQueueSizeBytes", {
44
+ enumerable: true,
45
+ configurable: true,
46
+ writable: true,
47
+ value: void 0
48
+ });
43
49
  Object.defineProperty(this, "queue", {
44
50
  enumerable: true,
45
51
  configurable: true,
@@ -52,8 +58,15 @@ class AsyncCaller {
52
58
  writable: true,
53
59
  value: void 0
54
60
  });
61
+ Object.defineProperty(this, "queueSizeBytes", {
62
+ enumerable: true,
63
+ configurable: true,
64
+ writable: true,
65
+ value: 0
66
+ });
55
67
  this.maxConcurrency = params.maxConcurrency ?? Infinity;
56
68
  this.maxRetries = params.maxRetries ?? 6;
69
+ this.maxQueueSizeBytes = params.maxQueueSizeBytes ?? Infinity;
57
70
  if ("default" in p_queue_1.default) {
58
71
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
59
72
  this.queue = new p_queue_1.default.default({
@@ -68,8 +81,25 @@ class AsyncCaller {
68
81
  }
69
82
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
70
83
  call(callable, ...args) {
84
+ return this.callWithOptions({}, callable, ...args);
85
+ }
86
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
87
+ callWithOptions(options, callable, ...args) {
88
+ const sizeBytes = options.sizeBytes ?? 0;
89
+ // Check if adding this call would exceed the byte size limit
90
+ if (this.maxQueueSizeBytes !== undefined &&
91
+ this.maxQueueSizeBytes !== Infinity &&
92
+ sizeBytes > 0 &&
93
+ this.queueSizeBytes + sizeBytes > this.maxQueueSizeBytes) {
94
+ return Promise.reject(new Error(`Queue size limit (${this.maxQueueSizeBytes} bytes) exceeded. ` +
95
+ `Current queue size: ${this.queueSizeBytes} bytes, attempted addition: ${sizeBytes} bytes.`));
96
+ }
97
+ // Add to queue size tracking
98
+ if (sizeBytes > 0) {
99
+ this.queueSizeBytes += sizeBytes;
100
+ }
71
101
  const onFailedResponseHook = this.onFailedResponseHook;
72
- return this.queue.add(() => (0, p_retry_1.default)(() => callable(...args).catch((error) => {
102
+ let promise = this.queue.add(() => (0, p_retry_1.default)(() => callable(...args).catch((error) => {
73
103
  // eslint-disable-next-line no-instanceof/no-instanceof
74
104
  if (error instanceof Error) {
75
105
  throw error;
@@ -106,14 +136,16 @@ class AsyncCaller {
106
136
  retries: this.maxRetries,
107
137
  randomize: true,
108
138
  }), { throwOnTimeout: true });
109
- }
110
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
- callWithOptions(options, callable, ...args) {
112
- // Note this doesn't cancel the underlying request,
113
- // when available prefer to use the signal option of the underlying call
139
+ // Decrement queue size when the call completes (success or failure)
140
+ if (sizeBytes > 0) {
141
+ promise = promise.finally(() => {
142
+ this.queueSizeBytes -= sizeBytes;
143
+ });
144
+ }
145
+ // Handle signal cancellation
114
146
  if (options.signal) {
115
147
  return Promise.race([
116
- this.call(callable, ...args),
148
+ promise,
117
149
  new Promise((_, reject) => {
118
150
  options.signal?.addEventListener("abort", () => {
119
151
  reject(new Error("AbortError"));
@@ -121,7 +153,7 @@ class AsyncCaller {
121
153
  }),
122
154
  ]);
123
155
  }
124
- return this.call(callable, ...args);
156
+ return promise;
125
157
  }
126
158
  }
127
159
  exports.AsyncCaller = AsyncCaller;
@@ -10,11 +10,22 @@ export interface AsyncCallerParams {
10
10
  * with an exponential backoff between each attempt. Defaults to 6.
11
11
  */
12
12
  maxRetries?: number;
13
+ /**
14
+ * The maximum size of the queue buffer in bytes. When the queue reaches this size,
15
+ * new calls will be dropped instead of queued.
16
+ * Defaults to `Infinity`, which means no limit.
17
+ */
18
+ maxQueueSizeBytes?: number;
13
19
  onFailedResponseHook?: ResponseCallback;
14
20
  debug?: boolean;
15
21
  }
16
22
  export interface AsyncCallerCallOptions {
17
23
  signal?: AbortSignal;
24
+ /**
25
+ * The size of this call in bytes, used for queue size tracking.
26
+ * If not provided, size tracking is skipped for this call.
27
+ */
28
+ sizeBytes?: number;
18
29
  }
19
30
  /**
20
31
  * A class that can be used to make async calls with concurrency and retry logic.
@@ -32,8 +43,10 @@ export interface AsyncCallerCallOptions {
32
43
  export declare class AsyncCaller {
33
44
  protected maxConcurrency: AsyncCallerParams["maxConcurrency"];
34
45
  protected maxRetries: AsyncCallerParams["maxRetries"];
46
+ protected maxQueueSizeBytes: AsyncCallerParams["maxQueueSizeBytes"];
35
47
  queue: typeof import("p-queue")["default"]["prototype"];
36
48
  private onFailedResponseHook?;
49
+ private queueSizeBytes;
37
50
  constructor(params: AsyncCallerParams);
38
51
  call<A extends any[], T extends (...args: A) => Promise<any>>(callable: T, ...args: Parameters<T>): Promise<Awaited<ReturnType<T>>>;
39
52
  callWithOptions<A extends any[], T extends (...args: A) => Promise<any>>(options: AsyncCallerCallOptions, callable: T, ...args: Parameters<T>): Promise<Awaited<ReturnType<T>>>;
@@ -34,6 +34,12 @@ export class AsyncCaller {
34
34
  writable: true,
35
35
  value: void 0
36
36
  });
37
+ Object.defineProperty(this, "maxQueueSizeBytes", {
38
+ enumerable: true,
39
+ configurable: true,
40
+ writable: true,
41
+ value: void 0
42
+ });
37
43
  Object.defineProperty(this, "queue", {
38
44
  enumerable: true,
39
45
  configurable: true,
@@ -46,8 +52,15 @@ export class AsyncCaller {
46
52
  writable: true,
47
53
  value: void 0
48
54
  });
55
+ Object.defineProperty(this, "queueSizeBytes", {
56
+ enumerable: true,
57
+ configurable: true,
58
+ writable: true,
59
+ value: 0
60
+ });
49
61
  this.maxConcurrency = params.maxConcurrency ?? Infinity;
50
62
  this.maxRetries = params.maxRetries ?? 6;
63
+ this.maxQueueSizeBytes = params.maxQueueSizeBytes ?? Infinity;
51
64
  if ("default" in PQueueMod) {
52
65
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
66
  this.queue = new PQueueMod.default({
@@ -62,8 +75,25 @@ export class AsyncCaller {
62
75
  }
63
76
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
77
  call(callable, ...args) {
78
+ return this.callWithOptions({}, callable, ...args);
79
+ }
80
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
81
+ callWithOptions(options, callable, ...args) {
82
+ const sizeBytes = options.sizeBytes ?? 0;
83
+ // Check if adding this call would exceed the byte size limit
84
+ if (this.maxQueueSizeBytes !== undefined &&
85
+ this.maxQueueSizeBytes !== Infinity &&
86
+ sizeBytes > 0 &&
87
+ this.queueSizeBytes + sizeBytes > this.maxQueueSizeBytes) {
88
+ return Promise.reject(new Error(`Queue size limit (${this.maxQueueSizeBytes} bytes) exceeded. ` +
89
+ `Current queue size: ${this.queueSizeBytes} bytes, attempted addition: ${sizeBytes} bytes.`));
90
+ }
91
+ // Add to queue size tracking
92
+ if (sizeBytes > 0) {
93
+ this.queueSizeBytes += sizeBytes;
94
+ }
65
95
  const onFailedResponseHook = this.onFailedResponseHook;
66
- return this.queue.add(() => pRetry(() => callable(...args).catch((error) => {
96
+ let promise = this.queue.add(() => pRetry(() => callable(...args).catch((error) => {
67
97
  // eslint-disable-next-line no-instanceof/no-instanceof
68
98
  if (error instanceof Error) {
69
99
  throw error;
@@ -100,14 +130,16 @@ export class AsyncCaller {
100
130
  retries: this.maxRetries,
101
131
  randomize: true,
102
132
  }), { throwOnTimeout: true });
103
- }
104
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
105
- callWithOptions(options, callable, ...args) {
106
- // Note this doesn't cancel the underlying request,
107
- // when available prefer to use the signal option of the underlying call
133
+ // Decrement queue size when the call completes (success or failure)
134
+ if (sizeBytes > 0) {
135
+ promise = promise.finally(() => {
136
+ this.queueSizeBytes -= sizeBytes;
137
+ });
138
+ }
139
+ // Handle signal cancellation
108
140
  if (options.signal) {
109
141
  return Promise.race([
110
- this.call(callable, ...args),
142
+ promise,
111
143
  new Promise((_, reject) => {
112
144
  options.signal?.addEventListener("abort", () => {
113
145
  reject(new Error("AbortError"));
@@ -115,6 +147,6 @@ export class AsyncCaller {
115
147
  }),
116
148
  ]);
117
149
  }
118
- return this.call(callable, ...args);
150
+ return promise;
119
151
  }
120
152
  }
package/dist/uuid.cjs ADDED
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.uuid7FromTime = void 0;
4
+ exports.uuid7 = uuid7;
5
+ const uuid_1 = require("uuid");
6
+ var _uuid_js_1 = require("./utils/_uuid.cjs");
7
+ Object.defineProperty(exports, "uuid7FromTime", { enumerable: true, get: function () { return _uuid_js_1.uuid7FromTime; } });
8
+ /**
9
+ * Generate a random UUID v7 string.
10
+ */
11
+ function uuid7() {
12
+ return (0, uuid_1.v7)();
13
+ }
package/dist/uuid.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export { uuid7FromTime } from "./utils/_uuid.js";
2
+ /**
3
+ * Generate a random UUID v7 string.
4
+ */
5
+ export declare function uuid7(): string;
package/dist/uuid.js ADDED
@@ -0,0 +1,8 @@
1
+ import { v7 as uuidv7 } from "uuid";
2
+ export { uuid7FromTime } from "./utils/_uuid.js";
3
+ /**
4
+ * Generate a random UUID v7 string.
5
+ */
6
+ export function uuid7() {
7
+ return uuidv7();
8
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langsmith",
3
- "version": "0.3.79",
3
+ "version": "0.3.80-rc.1",
4
4
  "description": "Client library to connect to the LangSmith Observability and Evaluation Platform.",
5
5
  "packageManager": "yarn@1.22.19",
6
6
  "files": [