langsmith 0.5.19 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (161) hide show
  1. package/dist/client.cjs +460 -22
  2. package/dist/client.d.ts +165 -1
  3. package/dist/client.js +464 -26
  4. package/dist/evaluation/_runner.cjs +4 -7
  5. package/dist/evaluation/_runner.js +2 -5
  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/experimental/anthropic/context.cjs +419 -49
  11. package/dist/experimental/anthropic/context.js +420 -50
  12. package/dist/experimental/anthropic/index.cjs +78 -10
  13. package/dist/experimental/anthropic/index.js +80 -12
  14. package/dist/experimental/anthropic/messages.cjs +53 -0
  15. package/dist/experimental/anthropic/messages.d.ts +6 -0
  16. package/dist/experimental/anthropic/messages.js +52 -0
  17. package/dist/experimental/anthropic/transcripts.cjs +144 -0
  18. package/dist/experimental/anthropic/transcripts.d.ts +22 -0
  19. package/dist/experimental/anthropic/transcripts.js +141 -0
  20. package/dist/experimental/anthropic/types.d.ts +1 -0
  21. package/dist/experimental/anthropic/usage.cjs +19 -20
  22. package/dist/experimental/anthropic/usage.d.ts +2 -1
  23. package/dist/experimental/anthropic/usage.js +18 -20
  24. package/dist/experimental/opencode/index.cjs +36 -0
  25. package/dist/experimental/opencode/index.d.ts +3 -0
  26. package/dist/experimental/opencode/index.js +32 -0
  27. package/dist/experimental/opencode/tracer.cjs +389 -0
  28. package/dist/experimental/opencode/tracer.d.ts +30 -0
  29. package/dist/experimental/opencode/tracer.js +385 -0
  30. package/dist/experimental/otel/setup.cjs +1 -1
  31. package/dist/experimental/otel/setup.js +1 -1
  32. package/dist/experimental/sandbox/client.cjs +308 -409
  33. package/dist/experimental/sandbox/client.d.ts +136 -157
  34. package/dist/experimental/sandbox/client.js +310 -411
  35. package/dist/experimental/sandbox/errors.cjs +1 -2
  36. package/dist/experimental/sandbox/errors.d.ts +1 -2
  37. package/dist/experimental/sandbox/errors.js +1 -2
  38. package/dist/experimental/sandbox/helpers.cjs +8 -98
  39. package/dist/experimental/sandbox/helpers.d.ts +0 -29
  40. package/dist/experimental/sandbox/helpers.js +9 -95
  41. package/dist/experimental/sandbox/index.cjs +6 -1
  42. package/dist/experimental/sandbox/index.d.ts +7 -2
  43. package/dist/experimental/sandbox/index.js +6 -1
  44. package/dist/experimental/sandbox/sandbox.cjs +71 -13
  45. package/dist/experimental/sandbox/sandbox.d.ts +39 -9
  46. package/dist/experimental/sandbox/sandbox.js +71 -13
  47. package/dist/experimental/sandbox/types.d.ts +147 -137
  48. package/dist/experimental/vercel/index.cjs +1 -1
  49. package/dist/experimental/vercel/index.js +1 -1
  50. package/dist/experimental/vercel/middleware.cjs +2 -1
  51. package/dist/experimental/vercel/middleware.js +2 -1
  52. package/dist/index.cjs +1 -1
  53. package/dist/index.d.ts +1 -1
  54. package/dist/index.js +1 -1
  55. package/dist/run_trees.cjs +8 -7
  56. package/dist/run_trees.d.ts +7 -0
  57. package/dist/run_trees.js +7 -6
  58. package/dist/schemas.d.ts +58 -0
  59. package/dist/singletons/otel.cjs +3 -2
  60. package/dist/singletons/otel.js +4 -3
  61. package/dist/singletons/traceable.cjs +1 -3
  62. package/dist/singletons/traceable.js +1 -3
  63. package/dist/traceable.cjs +2 -5
  64. package/dist/traceable.js +2 -5
  65. package/dist/utils/_git.cjs +2 -2
  66. package/dist/utils/_git.js +2 -2
  67. package/dist/utils/_uuid.cjs +2 -2
  68. package/dist/utils/_uuid.js +1 -1
  69. package/dist/utils/env.cjs +35 -2
  70. package/dist/utils/env.d.ts +9 -0
  71. package/dist/utils/env.js +34 -2
  72. package/dist/utils/error.cjs +9 -2
  73. package/dist/utils/error.d.ts +1 -0
  74. package/dist/utils/error.js +8 -2
  75. package/dist/utils/fast-safe-stringify/index.cjs +203 -0
  76. package/dist/utils/fast-safe-stringify/index.d.ts +46 -0
  77. package/dist/utils/fast-safe-stringify/index.js +202 -0
  78. package/dist/utils/jestlike/index.cjs +5 -5
  79. package/dist/utils/jestlike/index.js +1 -1
  80. package/dist/utils/jestlike/reporter.cjs +1 -1
  81. package/dist/utils/jestlike/reporter.js +1 -1
  82. package/dist/utils/jestlike/vendor/chain.cjs +2 -3
  83. package/dist/utils/jestlike/vendor/chain.js +2 -3
  84. package/dist/utils/jestlike/vendor/evaluatedBy.cjs +3 -3
  85. package/dist/utils/jestlike/vendor/evaluatedBy.js +1 -1
  86. package/dist/utils/prompts.cjs +7 -2
  87. package/dist/utils/prompts.d.ts +6 -1
  88. package/dist/utils/prompts.js +6 -1
  89. package/dist/utils/serialize_worker.cjs +388 -0
  90. package/dist/utils/serialize_worker.d.ts +66 -0
  91. package/dist/utils/serialize_worker.js +382 -0
  92. package/dist/utils/uuid/src/index.cjs +24 -0
  93. package/dist/utils/uuid/src/index.d.ts +10 -0
  94. package/dist/utils/uuid/src/index.js +9 -0
  95. package/dist/utils/uuid/src/max.cjs +3 -0
  96. package/dist/utils/uuid/src/max.d.ts +2 -0
  97. package/dist/utils/uuid/src/max.js +1 -0
  98. package/dist/utils/uuid/src/nil.cjs +3 -0
  99. package/dist/utils/uuid/src/nil.d.ts +2 -0
  100. package/dist/utils/uuid/src/nil.js +1 -0
  101. package/dist/utils/uuid/src/parse.cjs +23 -0
  102. package/dist/utils/uuid/src/parse.d.ts +3 -0
  103. package/dist/utils/uuid/src/parse.js +18 -0
  104. package/dist/utils/uuid/src/regex.cjs +3 -0
  105. package/dist/utils/uuid/src/regex.d.ts +2 -0
  106. package/dist/utils/uuid/src/regex.js +1 -0
  107. package/dist/utils/uuid/src/rng.cjs +10 -0
  108. package/dist/utils/uuid/src/rng.d.ts +1 -0
  109. package/dist/utils/uuid/src/rng.js +7 -0
  110. package/dist/utils/uuid/src/sha1.cjs +75 -0
  111. package/dist/utils/uuid/src/sha1.d.ts +2 -0
  112. package/dist/utils/uuid/src/sha1.js +73 -0
  113. package/dist/utils/uuid/src/stringify.cjs +55 -0
  114. package/dist/utils/uuid/src/stringify.d.ts +3 -0
  115. package/dist/utils/uuid/src/stringify.js +49 -0
  116. package/dist/utils/uuid/src/types.cjs +2 -0
  117. package/dist/utils/uuid/src/types.d.ts +22 -0
  118. package/dist/utils/uuid/src/types.js +1 -0
  119. package/dist/utils/uuid/src/v35.cjs +52 -0
  120. package/dist/utils/uuid/src/v35.d.ts +7 -0
  121. package/dist/utils/uuid/src/v35.js +44 -0
  122. package/dist/utils/uuid/src/v4.cjs +40 -0
  123. package/dist/utils/uuid/src/v4.d.ts +4 -0
  124. package/dist/utils/uuid/src/v4.js +35 -0
  125. package/dist/utils/uuid/src/v5.cjs +50 -0
  126. package/dist/utils/uuid/src/v5.d.ts +9 -0
  127. package/dist/utils/uuid/src/v5.js +9 -0
  128. package/dist/utils/uuid/src/v7.cjs +88 -0
  129. package/dist/utils/uuid/src/v7.d.ts +9 -0
  130. package/dist/utils/uuid/src/v7.js +82 -0
  131. package/dist/utils/uuid/src/validate.cjs +10 -0
  132. package/dist/utils/uuid/src/validate.d.ts +2 -0
  133. package/dist/utils/uuid/src/validate.js +5 -0
  134. package/dist/utils/uuid/src/version.cjs +13 -0
  135. package/dist/utils/uuid/src/version.d.ts +2 -0
  136. package/dist/utils/uuid/src/version.js +8 -0
  137. package/dist/utils/worker_threads.browser.cjs +16 -0
  138. package/dist/utils/worker_threads.browser.d.ts +14 -0
  139. package/dist/utils/worker_threads.browser.js +13 -0
  140. package/dist/utils/worker_threads.cjs +16 -0
  141. package/dist/utils/worker_threads.d.ts +13 -0
  142. package/dist/utils/worker_threads.js +13 -0
  143. package/dist/uuid.cjs +2 -2
  144. package/dist/uuid.js +1 -1
  145. package/dist/vitest/utils/esm.mjs +4 -4
  146. package/dist/wrappers/gemini.cjs +1 -1
  147. package/dist/wrappers/gemini.js +1 -1
  148. package/dist/wrappers/openai.cjs +2 -6
  149. package/dist/wrappers/openai.js +2 -6
  150. package/dist/wrappers/openai_agents.cjs +849 -0
  151. package/dist/wrappers/openai_agents.d.ts +92 -0
  152. package/dist/wrappers/openai_agents.js +845 -0
  153. package/experimental/opencode.cjs +1 -0
  154. package/experimental/opencode.d.cts +1 -0
  155. package/experimental/opencode.d.ts +1 -0
  156. package/experimental/opencode.js +1 -0
  157. package/package.json +47 -25
  158. package/wrappers/openai_agents.cjs +1 -0
  159. package/wrappers/openai_agents.d.cts +1 -0
  160. package/wrappers/openai_agents.d.ts +1 -0
  161. package/wrappers/openai_agents.js +1 -0
package/dist/client.cjs CHANGED
@@ -35,7 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
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
- const uuid = __importStar(require("uuid"));
38
+ const uuid = __importStar(require("./utils/uuid/src/index.cjs"));
39
39
  const translator_js_1 = require("./experimental/otel/translator.cjs");
40
40
  const otel_js_1 = require("./singletons/otel.cjs");
41
41
  const async_caller_js_1 = require("./utils/async_caller.cjs");
@@ -50,6 +50,13 @@ const index_js_2 = require("./utils/prompt_cache/index.cjs");
50
50
  const fsUtils = __importStar(require("./utils/fs.cjs"));
51
51
  const fetch_js_1 = require("./singletons/fetch.cjs");
52
52
  const index_js_3 = require("./utils/fast-safe-stringify/index.cjs");
53
+ const serialize_worker_js_1 = require("./utils/serialize_worker.cjs");
54
+ function assertPullPublicPromptAllowed(promptIdentifier, dangerouslyPullPublicPrompt) {
55
+ const [owner] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
56
+ if (owner !== "-" && !dangerouslyPullPublicPrompt) {
57
+ throw new Error("Pulling a public prompt by owner/name is disabled by default because prompts may contain untrusted serialized LangChain objects. If you trust this prompt, set `dangerouslyPullPublicPrompt: true` to acknowledge the risk.");
58
+ }
59
+ }
53
60
  /**
54
61
  * Catches timestamps without a timezone suffix.
55
62
  */
@@ -189,7 +196,11 @@ class AutoBatchQueue {
189
196
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise
190
197
  itemPromiseResolve = resolve;
191
198
  });
192
- const size = (0, index_js_3.serialize)(item.item, `Serializing run with id: ${item.item.id}`).length;
199
+ // Use a cheap structural estimate for soft memory accounting (queue size
200
+ // limit and downstream async caller memory tracking). The exact
201
+ // serialization still happens later, off the hot path, when the batch is
202
+ // assembled for sending.
203
+ const size = (0, index_js_3.estimateSerializedSize)(item.item).size;
193
204
  // Check if adding this item would exceed the size limit
194
205
  // Allow the run if the queue is empty (to support large single traces)
195
206
  if (this.sizeBytes + size > this.maxSizeBytes && this.items.length > 0) {
@@ -253,9 +264,64 @@ class AutoBatchQueue {
253
264
  }
254
265
  exports.AutoBatchQueue = AutoBatchQueue;
255
266
  class Client {
267
+ get tracingMode() {
268
+ return this._tracingMode;
269
+ }
256
270
  get _fetch() {
257
271
  return this.fetchImplementation || (0, fetch_js_1._getFetchImplementation)(this.debug);
258
272
  }
273
+ /**
274
+ * Serialize a payload for tracing, optionally offloading the work to a
275
+ * Node worker thread when the runtime supports worker_threads.
276
+ *
277
+ * Falls back to synchronous serialization when:
278
+ * - manualFlushMode is enabled (serverless: worker boot cost > benefit)
279
+ * - worker_threads is unavailable (non-Node runtimes)
280
+ * - the payload contains values that can't be structured-cloned across
281
+ * threads (functions, non-cloneable class instances, streams, etc.)
282
+ * - the worker throws for any other reason
283
+ *
284
+ * In all fallback cases the returned bytes are identical to the sync path.
285
+ */
286
+ _trackDrain(promise) {
287
+ this._pendingDrains.add(promise);
288
+ promise.finally(() => {
289
+ this._pendingDrains.delete(promise);
290
+ });
291
+ }
292
+ async _serializeBody(payload, errorContext) {
293
+ if (this.manualFlushMode) {
294
+ return (0, index_js_3.serialize)(payload, errorContext);
295
+ }
296
+ // Shape-aware gate: worker offload pays for itself only when the
297
+ // payload is dominated by one or more large strings (V8 can refcount
298
+ // those across isolates instead of copying). For structure-heavy
299
+ // payloads -- many keys, deep nesting, lots of small strings -- the
300
+ // structuredClone walk plus thread-hop cost exceeds the JSON.stringify
301
+ // cost we would pay inline, so we fall through to sync serialize.
302
+ if (!(0, serialize_worker_js_1.hasLargeString)(payload)) {
303
+ return (0, index_js_3.serialize)(payload, errorContext);
304
+ }
305
+ if (this._serializeWorker === undefined) {
306
+ this._serializeWorker = (0, serialize_worker_js_1.getSharedSerializeWorker)();
307
+ }
308
+ if (this._serializeWorker === null) {
309
+ return (0, index_js_3.serialize)(payload, errorContext);
310
+ }
311
+ try {
312
+ const bytes = await this._serializeWorker.serialize(payload);
313
+ if (bytes === null) {
314
+ // Worker subsystem unavailable; cache the null to skip re-entry.
315
+ this._serializeWorker = null;
316
+ return (0, index_js_3.serialize)(payload, errorContext);
317
+ }
318
+ return bytes;
319
+ }
320
+ catch {
321
+ // DataCloneError, worker crash, etc. Fall back silently.
322
+ return (0, index_js_3.serialize)(payload, errorContext);
323
+ }
324
+ }
259
325
  constructor(config = {}) {
260
326
  Object.defineProperty(this, "apiKey", {
261
327
  enumerable: true,
@@ -317,6 +383,12 @@ class Client {
317
383
  writable: true,
318
384
  value: void 0
319
385
  });
386
+ Object.defineProperty(this, "hideMetadata", {
387
+ enumerable: true,
388
+ configurable: true,
389
+ writable: true,
390
+ value: void 0
391
+ });
320
392
  Object.defineProperty(this, "omitTracedRuntimeInfo", {
321
393
  enumerable: true,
322
394
  configurable: true,
@@ -414,12 +486,35 @@ class Client {
414
486
  writable: true,
415
487
  value: false
416
488
  });
489
+ Object.defineProperty(this, "_serializeWorker", {
490
+ enumerable: true,
491
+ configurable: true,
492
+ writable: true,
493
+ value: void 0
494
+ });
495
+ /**
496
+ * Tracks in-flight drainAutoBatchQueue promises so awaitPendingTraceBatches
497
+ * can wait on them even if the flush involves async work (worker-thread
498
+ * serialize) that hasn't yet registered with batchIngestCaller.queue.
499
+ */
500
+ Object.defineProperty(this, "_pendingDrains", {
501
+ enumerable: true,
502
+ configurable: true,
503
+ writable: true,
504
+ value: new Set()
505
+ });
417
506
  Object.defineProperty(this, "langSmithToOTELTranslator", {
418
507
  enumerable: true,
419
508
  configurable: true,
420
509
  writable: true,
421
510
  value: void 0
422
511
  });
512
+ Object.defineProperty(this, "_tracingMode", {
513
+ enumerable: true,
514
+ configurable: true,
515
+ writable: true,
516
+ value: "langsmith"
517
+ });
423
518
  Object.defineProperty(this, "fetchImplementation", {
424
519
  enumerable: true,
425
520
  configurable: true,
@@ -529,6 +624,7 @@ class Client {
529
624
  config.hideInputs ?? config.anonymizer ?? defaultConfig.hideInputs;
530
625
  this.hideOutputs =
531
626
  config.hideOutputs ?? config.anonymizer ?? defaultConfig.hideOutputs;
627
+ this.hideMetadata = config.hideMetadata ?? defaultConfig.hideMetadata;
532
628
  this.omitTracedRuntimeInfo = config.omitTracedRuntimeInfo ?? false;
533
629
  this.autoBatchTracing = config.autoBatchTracing ?? this.autoBatchTracing;
534
630
  this.autoBatchQueue = new AutoBatchQueue(maxMemory);
@@ -538,7 +634,8 @@ class Client {
538
634
  this.batchSizeLimit = config.batchSizeLimit;
539
635
  this.fetchOptions = config.fetchOptions || {};
540
636
  this.manualFlushMode = config.manualFlushMode ?? this.manualFlushMode;
541
- if ((0, env_js_1.getOtelEnabled)()) {
637
+ this._tracingMode = (0, env_js_1.resolveTracingMode)(config.tracingMode);
638
+ if (this._tracingMode === "otel") {
542
639
  this.langSmithToOTELTranslator = new translator_js_1.LangSmithToOTELTranslator();
543
640
  }
544
641
  // Cache metadata env vars once during construction to avoid repeatedly scanning process.env
@@ -578,12 +675,14 @@ class Client {
578
675
  const apiUrl = (0, env_js_1.getLangSmithEnvironmentVariable)("ENDPOINT") ?? DEFAULT_API_URL;
579
676
  const hideInputs = (0, env_js_1.getLangSmithEnvironmentVariable)("HIDE_INPUTS") === "true";
580
677
  const hideOutputs = (0, env_js_1.getLangSmithEnvironmentVariable)("HIDE_OUTPUTS") === "true";
678
+ const hideMetadata = (0, env_js_1.getLangSmithEnvironmentVariable)("HIDE_METADATA") === "true";
581
679
  return {
582
680
  apiUrl: apiUrl,
583
681
  apiKey: apiKey,
584
682
  webUrl: undefined,
585
683
  hideInputs: hideInputs,
586
684
  hideOutputs: hideOutputs,
685
+ hideMetadata: hideMetadata,
587
686
  };
588
687
  }
589
688
  getHostUrl() {
@@ -615,6 +714,10 @@ class Client {
615
714
  this.webUrl = "https://aws.smith.langchain.com";
616
715
  return this.webUrl;
617
716
  }
717
+ else if (this.apiUrl.split(".", 1)[0].includes("apac")) {
718
+ this.webUrl = "https://apac.smith.langchain.com";
719
+ return this.webUrl;
720
+ }
618
721
  else if (this.apiUrl.split(".", 1)[0].includes("beta")) {
619
722
  this.webUrl = "https://beta.smith.langchain.com";
620
723
  return this.webUrl;
@@ -679,6 +782,18 @@ class Client {
679
782
  }
680
783
  return outputs;
681
784
  }
785
+ async processMetadata(metadata) {
786
+ if (this.hideMetadata === false) {
787
+ return metadata;
788
+ }
789
+ if (this.hideMetadata === true) {
790
+ return {};
791
+ }
792
+ if (typeof this.hideMetadata === "function") {
793
+ return this.hideMetadata(metadata);
794
+ }
795
+ return metadata;
796
+ }
682
797
  /**
683
798
  * Filter content from new_token events to prevent streaming LLM output
684
799
  * from being uploaded via events.
@@ -705,6 +820,12 @@ class Client {
705
820
  if (runParams.outputs !== undefined) {
706
821
  runParams.outputs = await this.processOutputs(runParams.outputs);
707
822
  }
823
+ if (runParams.extra != null && "metadata" in runParams.extra) {
824
+ runParams.extra = {
825
+ ...runParams.extra,
826
+ metadata: await this.processMetadata(runParams.extra.metadata),
827
+ };
828
+ }
708
829
  if (runParams.events !== undefined) {
709
830
  runParams.events = this._filterNewTokenEvents(runParams.events);
710
831
  }
@@ -1057,18 +1178,18 @@ class Client {
1057
1178
  const sizeLimit = await this._getBatchSizeLimit();
1058
1179
  if (this.autoBatchQueue.sizeBytes > sizeLimitBytes ||
1059
1180
  this.autoBatchQueue.items.length > sizeLimit) {
1060
- void this.drainAutoBatchQueue({
1181
+ this._trackDrain(this.drainAutoBatchQueue({
1061
1182
  batchSizeLimitBytes: sizeLimitBytes,
1062
1183
  batchSizeLimit: sizeLimit,
1063
- });
1184
+ }));
1064
1185
  }
1065
1186
  if (this.autoBatchQueue.items.length > 0) {
1066
1187
  this.autoBatchTimeout = setTimeout(() => {
1067
1188
  this.autoBatchTimeout = undefined;
1068
- void this.drainAutoBatchQueue({
1189
+ this._trackDrain(this.drainAutoBatchQueue({
1069
1190
  batchSizeLimitBytes: sizeLimitBytes,
1070
1191
  batchSizeLimit: sizeLimit,
1071
- });
1192
+ }));
1072
1193
  }, this.autoBatchAggregationDelayMs);
1073
1194
  }
1074
1195
  return itemPromise;
@@ -1077,7 +1198,7 @@ class Client {
1077
1198
  const response = await this.caller.call(async () => {
1078
1199
  const res = await this._fetch(`${this.apiUrl}/info`, {
1079
1200
  method: "GET",
1080
- headers: { Accept: "application/json" },
1201
+ headers: { ...this._mergedHeaders, Accept: "application/json" },
1081
1202
  signal: AbortSignal.timeout(SERVER_INFO_REQUEST_TIMEOUT_MS),
1082
1203
  ...this.fetchOptions,
1083
1204
  });
@@ -1248,7 +1369,7 @@ class Client {
1248
1369
  .map((item) => item.id)
1249
1370
  .concat(batchChunks.patch.map((item) => item.id))
1250
1371
  .join(",");
1251
- await this._postBatchIngestRuns((0, index_js_3.serialize)(batchChunks, `Ingesting runs with ids: ${runIds}`), options);
1372
+ await this._postBatchIngestRuns(await this._serializeBody(batchChunks, `Ingesting runs with ids: ${runIds}`), options);
1252
1373
  }
1253
1374
  }
1254
1375
  async _postBatchIngestRuns(body, options) {
@@ -1349,7 +1470,7 @@ class Client {
1349
1470
  const { inputs, outputs, events, extra, error, serialized, attachments, ...payload } = originalPayload;
1350
1471
  const fields = { inputs, outputs, events, extra, error, serialized };
1351
1472
  // encode the main run payload
1352
- const stringifiedPayload = (0, index_js_3.serialize)(payload, `Serializing for multipart ingestion of run with id: ${payload.id}`);
1473
+ const stringifiedPayload = await this._serializeBody(payload, `Serializing for multipart ingestion of run with id: ${payload.id}`);
1353
1474
  accumulatedParts.push({
1354
1475
  name: `${method}.${payload.id}`,
1355
1476
  payload: new Blob([stringifiedPayload], {
@@ -1361,7 +1482,7 @@ class Client {
1361
1482
  if (value === undefined) {
1362
1483
  continue;
1363
1484
  }
1364
- const stringifiedValue = (0, index_js_3.serialize)(value, `Serializing ${key} for multipart ingestion of run with id: ${payload.id}`);
1485
+ const stringifiedValue = await this._serializeBody(value, `Serializing ${key} for multipart ingestion of run with id: ${payload.id}`);
1365
1486
  accumulatedParts.push({
1366
1487
  name: `${method}.${payload.id}.${key}`,
1367
1488
  payload: new Blob([stringifiedValue], {
@@ -1552,6 +1673,12 @@ class Client {
1552
1673
  if (run.outputs) {
1553
1674
  run.outputs = await this.processOutputs(run.outputs);
1554
1675
  }
1676
+ if (run.extra != null && "metadata" in run.extra) {
1677
+ run.extra = {
1678
+ ...run.extra,
1679
+ metadata: await this.processMetadata(run.extra.metadata),
1680
+ };
1681
+ }
1555
1682
  if (run.events) {
1556
1683
  run.events = this._filterNewTokenEvents(run.events);
1557
1684
  }
@@ -2367,7 +2494,7 @@ class Client {
2367
2494
  // projectId querying
2368
2495
  return true;
2369
2496
  }
2370
- catch (e) {
2497
+ catch (_e) {
2371
2498
  return false;
2372
2499
  }
2373
2500
  }
@@ -3788,7 +3915,7 @@ class Client {
3788
3915
  return json.commits[0].commit_hash;
3789
3916
  }
3790
3917
  async _likeOrUnlikePrompt(promptIdentifier, like) {
3791
- const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
3918
+ const [owner, promptName, _] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
3792
3919
  const body = JSON.stringify({ like: like });
3793
3920
  const response = await this.caller.call(async () => {
3794
3921
  const res = await this._fetch(`${this.apiUrl}/likes/${owner}/${promptName}`, {
@@ -3807,7 +3934,7 @@ class Client {
3807
3934
  return response.json();
3808
3935
  }
3809
3936
  async _getPromptUrl(promptIdentifier) {
3810
- const [owner, promptName, commitHash] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
3937
+ const [owner, promptName, commitHash] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
3811
3938
  if (!(await this._currentTenantIsOwner(owner))) {
3812
3939
  if (commitHash !== "latest") {
3813
3940
  return `${this.getHostUrl()}/hub/${owner}/${promptName}/${commitHash.substring(0, 8)}`;
@@ -3899,7 +4026,7 @@ class Client {
3899
4026
  * ```
3900
4027
  */
3901
4028
  async *listCommits(promptIdentifier) {
3902
- const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
4029
+ const [owner, promptName, _] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
3903
4030
  for await (const commits of this._getPaginated(`/commits/${owner}/${promptName}/`, new URLSearchParams(), (res) => res.commits)) {
3904
4031
  yield* commits;
3905
4032
  }
@@ -3962,7 +4089,7 @@ class Client {
3962
4089
  * ```
3963
4090
  */
3964
4091
  async getPrompt(promptIdentifier) {
3965
- const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
4092
+ const [owner, promptName, _] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
3966
4093
  const response = await this.caller.call(async () => {
3967
4094
  const res = await this._fetch(`${this.apiUrl}/repos/${owner}/${promptName}`, {
3968
4095
  method: "GET",
@@ -4019,7 +4146,7 @@ class Client {
4019
4146
  You can add a handle by creating a public prompt at:\n
4020
4147
  https://smith.langchain.com/prompts`);
4021
4148
  }
4022
- const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
4149
+ const [owner, promptName, _] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
4023
4150
  if (!(await this._currentTenantIsOwner(owner))) {
4024
4151
  throw await this._ownerConflictError("create a prompt", owner);
4025
4152
  }
@@ -4078,7 +4205,7 @@ class Client {
4078
4205
  if (!(await this.promptExists(promptIdentifier))) {
4079
4206
  throw new Error("Prompt does not exist, you must create it first.");
4080
4207
  }
4081
- const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
4208
+ const [owner, promptName, _] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
4082
4209
  const resolvedParentCommitHash = options?.parentCommitHash === "latest" || !options?.parentCommitHash
4083
4210
  ? await this._getLatestCommitHash(`${owner}/${promptName}`)
4084
4211
  : options?.parentCommitHash;
@@ -4276,7 +4403,7 @@ class Client {
4276
4403
  if (!(await this.promptExists(promptIdentifier))) {
4277
4404
  throw new Error("Prompt does not exist, you must create it first.");
4278
4405
  }
4279
- const [owner, promptName] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
4406
+ const [owner, promptName] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
4280
4407
  if (!(await this._currentTenantIsOwner(owner))) {
4281
4408
  throw await this._ownerConflictError("update a prompt", owner);
4282
4409
  }
@@ -4316,7 +4443,7 @@ class Client {
4316
4443
  if (!(await this.promptExists(promptIdentifier))) {
4317
4444
  throw new Error("Prompt does not exist, you must create it first.");
4318
4445
  }
4319
- const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
4446
+ const [owner, promptName, _] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
4320
4447
  if (!(await this._currentTenantIsOwner(owner))) {
4321
4448
  throw await this._ownerConflictError("delete a prompt", owner);
4322
4449
  }
@@ -4344,7 +4471,7 @@ class Client {
4344
4471
  * Fetch a prompt commit directly from the API (bypassing cache).
4345
4472
  */
4346
4473
  async _fetchPromptFromApi(promptIdentifier, options) {
4347
- const [owner, promptName, commitHash] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
4474
+ const [owner, promptName, commitHash] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
4348
4475
  const response = await this.caller.call(async () => {
4349
4476
  const res = await this._fetch(`${this.apiUrl}/commits/${owner}/${promptName}/${commitHash}${options?.includeModel ? "?include_model=true" : ""}`, {
4350
4477
  method: "GET",
@@ -4362,9 +4489,28 @@ class Client {
4362
4489
  commit_hash: result.commit_hash,
4363
4490
  manifest: result.manifest,
4364
4491
  examples: result.examples,
4492
+ hub_model_config: result.model_config,
4493
+ hub_model_provider: result.model_provider,
4365
4494
  };
4366
4495
  }
4496
+ /**
4497
+ * Pull a prompt commit from the LangSmith API.
4498
+ *
4499
+ * Public prompts referenced by owner/name cross a trust boundary because the
4500
+ * prompt manifest may contain serialized LangChain objects and configuration
4501
+ * that affect runtime behavior. For example, a prompt can intentionally
4502
+ * configure a model with a custom base URL, headers, model name, or other
4503
+ * constructor arguments. These are supported features, but they also mean the
4504
+ * prompt contents should be treated as executable configuration rather than
4505
+ * plain text.
4506
+ *
4507
+ * Set `dangerouslyPullPublicPrompt: true` only after reviewing and trusting
4508
+ * the prompt contents, not merely the publishing account. Prompts from your
4509
+ * own or your organization's account can still be unsafe if that account or
4510
+ * prompt was compromised.
4511
+ */
4367
4512
  async pullPromptCommit(promptIdentifier, options) {
4513
+ assertPullPublicPromptAllowed(promptIdentifier, options?.dangerouslyPullPublicPrompt);
4368
4514
  // Check cache first if not skipped
4369
4515
  const refreshFunc = this._fetchPromptFromApi.bind(this, promptIdentifier, options);
4370
4516
  if (!options?.skipCache && this._promptCache) {
@@ -4384,12 +4530,26 @@ class Client {
4384
4530
  /**
4385
4531
  * This method should not be used directly, use `import { pull } from "langchain/hub"` instead.
4386
4532
  * Using this method directly returns the JSON string of the prompt rather than a LangChain object.
4533
+ *
4534
+ * Public prompts referenced by owner/name cross a trust boundary because the
4535
+ * prompt manifest may contain serialized LangChain objects and configuration
4536
+ * that affect runtime behavior. For example, a prompt can intentionally
4537
+ * configure a model with a custom base URL, headers, model name, or other
4538
+ * constructor arguments. These are supported features, but they also mean the
4539
+ * prompt contents should be treated as executable configuration rather than
4540
+ * plain text.
4541
+ *
4542
+ * Set `dangerouslyPullPublicPrompt: true` only after reviewing and trusting
4543
+ * the prompt contents, not merely the publishing account. Prompts from your
4544
+ * own or your organization's account can still be unsafe if that account or
4545
+ * prompt was compromised.
4387
4546
  * @private
4388
4547
  */
4389
4548
  async _pullPrompt(promptIdentifier, options) {
4390
4549
  const promptObject = await this.pullPromptCommit(promptIdentifier, {
4391
4550
  includeModel: options?.includeModel,
4392
4551
  skipCache: options?.skipCache,
4552
+ dangerouslyPullPublicPrompt: options?.dangerouslyPullPublicPrompt,
4393
4553
  });
4394
4554
  const prompt = JSON.stringify(promptObject.manifest);
4395
4555
  return prompt;
@@ -4424,6 +4584,251 @@ class Client {
4424
4584
  });
4425
4585
  return url;
4426
4586
  }
4587
+ /**
4588
+ * Check if an agent repo exists.
4589
+ */
4590
+ async agentExists(identifier) {
4591
+ const [owner, name] = (0, prompts_js_1.parseHubIdentifier)(identifier);
4592
+ return this._repoExists(owner, name);
4593
+ }
4594
+ /**
4595
+ * Check if a skill repo exists.
4596
+ */
4597
+ async skillExists(identifier) {
4598
+ const [owner, name] = (0, prompts_js_1.parseHubIdentifier)(identifier);
4599
+ return this._repoExists(owner, name);
4600
+ }
4601
+ /**
4602
+ * Pull an agent directory from Hub.
4603
+ * @param identifier The identifier (owner/name[:version]).
4604
+ * @param options.version Commit hash or tag; overrides identifier's version.
4605
+ */
4606
+ async pullAgent(identifier, options) {
4607
+ return (await this._pullDirectory(identifier, "agent", options?.version));
4608
+ }
4609
+ /**
4610
+ * Pull a skill directory from Hub.
4611
+ */
4612
+ async pullSkill(identifier, options) {
4613
+ return (await this._pullDirectory(identifier, "skill", options?.version));
4614
+ }
4615
+ /**
4616
+ * Push an agent to Hub. Creates the repo if missing, patches metadata if
4617
+ * provided, then commits the given files.
4618
+ * @returns The URL of the resulting commit.
4619
+ */
4620
+ async pushAgent(identifier, options) {
4621
+ return this._pushDirectory(identifier, "agent", options);
4622
+ }
4623
+ /**
4624
+ * Push a skill to Hub.
4625
+ */
4626
+ async pushSkill(identifier, options) {
4627
+ return this._pushDirectory(identifier, "skill", options);
4628
+ }
4629
+ /**
4630
+ * Delete an agent and all its owned child file repos.
4631
+ */
4632
+ async deleteAgent(identifier) {
4633
+ return this._deleteDirectory(identifier);
4634
+ }
4635
+ /**
4636
+ * Delete a skill and all its owned child file repos.
4637
+ */
4638
+ async deleteSkill(identifier) {
4639
+ return this._deleteDirectory(identifier);
4640
+ }
4641
+ /**
4642
+ * List agent repos. Yields one at a time, auto-paginating.
4643
+ */
4644
+ async *listAgents(options) {
4645
+ yield* this._listReposByType("agent", options);
4646
+ }
4647
+ /**
4648
+ * List skill repos. Yields one at a time, auto-paginating.
4649
+ */
4650
+ async *listSkills(options) {
4651
+ yield* this._listReposByType("skill", options);
4652
+ }
4653
+ async *_listReposByType(repoType, options) {
4654
+ const params = new URLSearchParams();
4655
+ params.append("repo_type", repoType);
4656
+ params.append("is_archived", (!!options?.isArchived).toString());
4657
+ if (options?.isPublic !== undefined) {
4658
+ params.append("is_public", options.isPublic.toString());
4659
+ }
4660
+ if (options?.query) {
4661
+ params.append("query", options.query);
4662
+ }
4663
+ for await (const repos of this._getPaginated("/repos", params, (res) => res.repos)) {
4664
+ yield* repos;
4665
+ }
4666
+ }
4667
+ async _pullDirectory(identifier, repoType, version) {
4668
+ const [owner, name, parsedVersion] = (0, prompts_js_1.parseHubIdentifier)(identifier);
4669
+ const resolvedVersion = version ?? (parsedVersion !== "latest" ? parsedVersion : undefined);
4670
+ const url = new URL(`${this.apiUrl}/v1/platform/hub/repos/${owner}/${name}/directories`);
4671
+ url.searchParams.set("repo_type", repoType);
4672
+ if (resolvedVersion) {
4673
+ url.searchParams.set("commit", resolvedVersion);
4674
+ }
4675
+ const response = await this.caller.call(async () => {
4676
+ const res = await this._fetch(url.toString(), {
4677
+ method: "GET",
4678
+ headers: this._mergedHeaders,
4679
+ signal: AbortSignal.timeout(this.timeout_ms),
4680
+ ...this.fetchOptions,
4681
+ });
4682
+ await (0, error_js_1.raiseForStatus)(res, "pull directory");
4683
+ return res;
4684
+ });
4685
+ return (await response.json());
4686
+ }
4687
+ async _pushDirectory(identifier, repoType, options) {
4688
+ if (options.parentCommit !== undefined &&
4689
+ (options.parentCommit.length < 8 || options.parentCommit.length > 64)) {
4690
+ throw new Error("parent_commit must be 8-64 characters");
4691
+ }
4692
+ const [owner, name] = (0, prompts_js_1.parseHubIdentifier)(identifier);
4693
+ if (!(await this._currentTenantIsOwner(owner))) {
4694
+ throw await this._ownerConflictError(`push ${repoType}`, owner);
4695
+ }
4696
+ if (await this._repoExists(owner, name)) {
4697
+ if (options.description !== undefined ||
4698
+ options.readme !== undefined ||
4699
+ options.tags !== undefined ||
4700
+ options.isPublic !== undefined) {
4701
+ await this._updateRepoMetadata(owner, name, options);
4702
+ }
4703
+ }
4704
+ else {
4705
+ const REPO_HANDLE_PATTERN = /^[a-z][a-z0-9-_]*$/;
4706
+ if (!REPO_HANDLE_PATTERN.test(name)) {
4707
+ throw new Error(`Invalid repo_handle ${JSON.stringify(name)}: must match ${REPO_HANDLE_PATTERN}`);
4708
+ }
4709
+ await this._createRepo(name, repoType, options);
4710
+ }
4711
+ const body = { files: options.files };
4712
+ if (options.parentCommit) {
4713
+ body.parent_commit = options.parentCommit;
4714
+ }
4715
+ const response = await this.caller.call(async () => {
4716
+ const res = await this._fetch(`${this.apiUrl}/v1/platform/hub/repos/${owner}/${name}/directories/commits`, {
4717
+ method: "POST",
4718
+ headers: {
4719
+ ...this._mergedHeaders,
4720
+ "Content-Type": "application/json",
4721
+ },
4722
+ signal: AbortSignal.timeout(this.timeout_ms),
4723
+ ...this.fetchOptions,
4724
+ body: JSON.stringify(body),
4725
+ });
4726
+ await (0, error_js_1.raiseForStatus)(res, `push ${repoType}`);
4727
+ return res;
4728
+ });
4729
+ const data = (await response.json());
4730
+ const commitHash = data.commit.commit_hash;
4731
+ return `${this.getHostUrl()}/hub/${owner}/${name}:${commitHash.slice(0, 8)}`;
4732
+ }
4733
+ async _deleteDirectory(identifier) {
4734
+ const [owner, name] = (0, prompts_js_1.parseHubIdentifier)(identifier);
4735
+ if (!(await this._currentTenantIsOwner(owner))) {
4736
+ throw await this._ownerConflictError("delete", owner);
4737
+ }
4738
+ await this.caller.call(async () => {
4739
+ const res = await this._fetch(`${this.apiUrl}/v1/platform/hub/repos/${owner}/${name}/directories`, {
4740
+ method: "DELETE",
4741
+ headers: this._mergedHeaders,
4742
+ signal: AbortSignal.timeout(this.timeout_ms),
4743
+ ...this.fetchOptions,
4744
+ });
4745
+ await (0, error_js_1.raiseForStatus)(res, "delete directory");
4746
+ return res;
4747
+ });
4748
+ }
4749
+ async _repoExists(owner, name) {
4750
+ try {
4751
+ await this.caller.call(async () => {
4752
+ const res = await this._fetch(`${this.apiUrl}/repos/${owner}/${name}`, {
4753
+ method: "GET",
4754
+ headers: this._mergedHeaders,
4755
+ signal: AbortSignal.timeout(this.timeout_ms),
4756
+ ...this.fetchOptions,
4757
+ });
4758
+ await (0, error_js_1.raiseForStatus)(res, "check repo exists");
4759
+ return res;
4760
+ });
4761
+ return true;
4762
+ }
4763
+ catch (e) {
4764
+ if ((0, error_js_1.isLangSmithNotFoundError)(e)) {
4765
+ return false;
4766
+ }
4767
+ throw e;
4768
+ }
4769
+ }
4770
+ async _createRepo(name, repoType, options) {
4771
+ const body = {
4772
+ repo_handle: name,
4773
+ repo_type: repoType,
4774
+ is_public: !!options.isPublic,
4775
+ };
4776
+ if (options.description !== undefined)
4777
+ body.description = options.description;
4778
+ if (options.readme !== undefined)
4779
+ body.readme = options.readme;
4780
+ if (options.tags !== undefined)
4781
+ body.tags = options.tags;
4782
+ try {
4783
+ await this.caller.call(async () => {
4784
+ const res = await this._fetch(`${this.apiUrl}/repos/`, {
4785
+ method: "POST",
4786
+ headers: {
4787
+ ...this._mergedHeaders,
4788
+ "Content-Type": "application/json",
4789
+ },
4790
+ signal: AbortSignal.timeout(this.timeout_ms),
4791
+ ...this.fetchOptions,
4792
+ body: JSON.stringify(body),
4793
+ });
4794
+ await (0, error_js_1.raiseForStatus)(res, `create ${repoType}`);
4795
+ return res;
4796
+ });
4797
+ }
4798
+ catch (e) {
4799
+ if ((0, error_js_1.isLangSmithConflictError)(e)) {
4800
+ return;
4801
+ }
4802
+ throw e;
4803
+ }
4804
+ }
4805
+ async _updateRepoMetadata(owner, name, options) {
4806
+ const body = {};
4807
+ if (options.description !== undefined)
4808
+ body.description = options.description;
4809
+ if (options.readme !== undefined)
4810
+ body.readme = options.readme;
4811
+ if (options.tags !== undefined)
4812
+ body.tags = options.tags;
4813
+ if (options.isPublic !== undefined)
4814
+ body.is_public = options.isPublic;
4815
+ if (Object.keys(body).length === 0)
4816
+ return;
4817
+ await this.caller.call(async () => {
4818
+ const res = await this._fetch(`${this.apiUrl}/repos/${owner}/${name}`, {
4819
+ method: "PATCH",
4820
+ headers: {
4821
+ ...this._mergedHeaders,
4822
+ "Content-Type": "application/json",
4823
+ },
4824
+ signal: AbortSignal.timeout(this.timeout_ms),
4825
+ ...this.fetchOptions,
4826
+ body: JSON.stringify(body),
4827
+ });
4828
+ await (0, error_js_1.raiseForStatus)(res, "update repo metadata");
4829
+ return res;
4830
+ });
4831
+ }
4427
4832
  /**
4428
4833
  * Clone a public dataset to your own langsmith tenant.
4429
4834
  * This operation is idempotent. If you already have a dataset with the given name,
@@ -4501,7 +4906,7 @@ class Client {
4501
4906
  throw new Error(`Invalid public ${kind} URL: ${urlOrToken}`);
4502
4907
  }
4503
4908
  }
4504
- catch (error) {
4909
+ catch (_error) {
4505
4910
  throw new Error(`Invalid public ${kind} URL or token: ${urlOrToken}`);
4506
4911
  }
4507
4912
  }
@@ -4555,6 +4960,12 @@ class Client {
4555
4960
  * ```
4556
4961
  */
4557
4962
  await new Promise((resolve) => setTimeout(resolve, 1));
4963
+ // Wait for any in-flight drains (whose serialize work may still be
4964
+ // in-progress on a worker thread and not yet registered with the
4965
+ // batchIngestCaller queue) before checking queue idleness.
4966
+ while (this._pendingDrains.size > 0) {
4967
+ await Promise.all([...this._pendingDrains]);
4968
+ }
4558
4969
  await Promise.all([
4559
4970
  ...this.autoBatchQueue.items.map(({ itemPromise }) => itemPromise),
4560
4971
  this.batchIngestCaller.queue.onIdle(),
@@ -4563,6 +4974,33 @@ class Client {
4563
4974
  await (0, otel_js_1.getDefaultOTLPTracerComponents)()?.DEFAULT_LANGSMITH_SPAN_PROCESSOR?.forceFlush();
4564
4975
  }
4565
4976
  }
4977
+ /**
4978
+ * Returns a string representation of the Client instance.
4979
+ * This method is called when the object is converted to a string
4980
+ * or logged, ensuring sensitive information like API keys is not exposed.
4981
+ *
4982
+ * @returns A string representation of the Client.
4983
+ */
4984
+ toString() {
4985
+ const params = [`apiUrl=${JSON.stringify(this.apiUrl)}`];
4986
+ if (this.webUrl !== undefined) {
4987
+ params.push(`webUrl=${JSON.stringify(this.webUrl)}`);
4988
+ }
4989
+ if (this.workspaceId !== undefined) {
4990
+ params.push(`workspaceId=${JSON.stringify(this.workspaceId)}`);
4991
+ }
4992
+ return `[LangSmithClient ${params.join(" ")}]`;
4993
+ }
4994
+ /**
4995
+ * Custom inspect method for Node.js.
4996
+ * This method is called when the object is inspected in the Node.js REPL
4997
+ * or with console.log, ensuring sensitive information like API keys is not exposed.
4998
+ *
4999
+ * @returns A string representation of the Client for inspection.
5000
+ */
5001
+ [Symbol.for("nodejs.util.inspect.custom")]() {
5002
+ return this.toString();
5003
+ }
4566
5004
  }
4567
5005
  exports.Client = Client;
4568
5006
  Object.defineProperty(Client, "_fallbackDirsCreated", {