langsmith 0.5.0 → 0.5.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
@@ -495,7 +495,29 @@ class Client {
495
495
  // Cache metadata env vars once during construction to avoid repeatedly scanning process.env
496
496
  this.cachedLSEnvVarsForMetadata = (0, env_js_1.getLangSmithEnvVarsMetadata)();
497
497
  // Initialize prompt cache
498
- if (!config.disablePromptCache) {
498
+ // Handle backwards compatibility for deprecated `cache` parameter
499
+ if (config.cache !== undefined && config.disablePromptCache) {
500
+ (0, warn_js_1.warnOnce)("Both 'cache' and 'disablePromptCache' were provided. " +
501
+ "The 'cache' parameter is deprecated and will be removed in a future version. " +
502
+ "Using 'cache' parameter value.");
503
+ }
504
+ if (config.cache !== undefined) {
505
+ (0, warn_js_1.warnOnce)("The 'cache' parameter is deprecated and will be removed in a future version. " +
506
+ "Use 'configureGlobalPromptCache()' to configure the global cache, or " +
507
+ "'disablePromptCache: true' to disable caching for this client.");
508
+ // Handle old cache parameter
509
+ if (config.cache === false) {
510
+ this._promptCache = undefined;
511
+ }
512
+ else if (config.cache === true) {
513
+ this._promptCache = index_js_2.promptCacheSingleton;
514
+ }
515
+ else {
516
+ // Custom PromptCache instance provided
517
+ this._promptCache = config.cache;
518
+ }
519
+ }
520
+ else if (!config.disablePromptCache) {
499
521
  // Use the global singleton instance
500
522
  this._promptCache = index_js_2.promptCacheSingleton;
501
523
  }
package/dist/client.d.ts CHANGED
@@ -2,6 +2,7 @@ import type { OTELContext } from "./experimental/otel/types.js";
2
2
  import { AsyncCallerParams } from "./utils/async_caller.js";
3
3
  import { ComparativeExperiment, DataType, Dataset, DatasetDiffInfo, DatasetShareSchema, Example, ExampleCreate, ExampleUpdate, ExampleUpdateWithoutId, Feedback, FeedbackConfig, FeedbackIngestToken, KVMap, LangChainBaseMessage, LangSmithSettings, LikePromptResponse, Prompt, PromptCommit, PromptSortField, Run, RunCreate, RunUpdate, ScoreType, ExampleSearch, TimeDelta, TracerSession, TracerSessionResult, ValueType, AnnotationQueue, RunWithAnnotationQueueInfo, Attachments, UploadExamplesResponse, UpdateExamplesResponse, DatasetVersion, AnnotationQueueWithDetails } from "./schemas.js";
4
4
  import { EvaluationResult, EvaluationResults } from "./evaluation/evaluator.js";
5
+ import { PromptCache } from "./utils/prompt_cache/index.js";
5
6
  export interface ClientConfig {
6
7
  apiUrl?: string;
7
8
  apiKey?: string;
@@ -50,31 +51,39 @@ export interface ClientConfig {
50
51
  */
51
52
  fetchImplementation?: typeof fetch;
52
53
  /**
54
+ * Disable prompt caching for this client.
55
+ * By default, prompt caching is enabled globally.
56
+ */
57
+ disablePromptCache?: boolean;
58
+ /**
59
+ * @deprecated Use `configureGlobalPromptCache()` to configure caching, or
60
+ * `disablePromptCache: true` to disable it. This parameter is deprecated.
61
+ *
53
62
  * Configuration for caching. Can be:
54
- * - `true`: Enable caching with default settings
55
- * - `Cache` instance: Use custom cache configuration
56
- * - `undefined` or `false`: Disable caching (default)
63
+ * - `true`: Enable caching with default settings (uses global singleton)
64
+ * - `Cache`/`PromptCache` instance: Use custom cache configuration
65
+ * - `false`: Disable caching (equivalent to `disablePromptCache: true`)
57
66
  *
58
67
  * @example
59
68
  * ```typescript
60
- * import { Client, Cache } from "langsmith";
69
+ * import { Client, Cache, configureGlobalPromptCache } from "langsmith";
61
70
  *
62
71
  * // Enable with defaults
63
- * const client1 = new Client({ cache: true });
72
+ * const client1 = new Client({});
64
73
  *
65
74
  * // Or use custom configuration
66
- * const myCache = new Cache({
75
+ * import { configureGlobalPromptCache } from "langsmith";
76
+ * configureGlobalPromptCache({
67
77
  * maxSize: 100,
68
78
  * ttlSeconds: 3600, // 1 hour, or null for infinite TTL
69
79
  * });
70
- * const client2 = new Client({ cache: myCache });
80
+ * const client2 = new Client({});
81
+ *
82
+ * // Or disable for a specific client
83
+ * const client3 = new Client({ disablePromptCache: true });
71
84
  * ```
72
85
  */
73
- /**
74
- * Disable prompt caching for this client.
75
- * By default, prompt caching is enabled globally.
76
- */
77
- disablePromptCache?: boolean;
86
+ cache?: boolean | PromptCache;
78
87
  }
79
88
  /**
80
89
  * Represents the parameters for listing runs (spans) from the Langsmith server.
package/dist/client.js CHANGED
@@ -457,7 +457,29 @@ export class Client {
457
457
  // Cache metadata env vars once during construction to avoid repeatedly scanning process.env
458
458
  this.cachedLSEnvVarsForMetadata = getLangSmithEnvVarsMetadata();
459
459
  // Initialize prompt cache
460
- if (!config.disablePromptCache) {
460
+ // Handle backwards compatibility for deprecated `cache` parameter
461
+ if (config.cache !== undefined && config.disablePromptCache) {
462
+ warnOnce("Both 'cache' and 'disablePromptCache' were provided. " +
463
+ "The 'cache' parameter is deprecated and will be removed in a future version. " +
464
+ "Using 'cache' parameter value.");
465
+ }
466
+ if (config.cache !== undefined) {
467
+ warnOnce("The 'cache' parameter is deprecated and will be removed in a future version. " +
468
+ "Use 'configureGlobalPromptCache()' to configure the global cache, or " +
469
+ "'disablePromptCache: true' to disable caching for this client.");
470
+ // Handle old cache parameter
471
+ if (config.cache === false) {
472
+ this._promptCache = undefined;
473
+ }
474
+ else if (config.cache === true) {
475
+ this._promptCache = promptCacheSingleton;
476
+ }
477
+ else {
478
+ // Custom PromptCache instance provided
479
+ this._promptCache = config.cache;
480
+ }
481
+ }
482
+ else if (!config.disablePromptCache) {
461
483
  // Use the global singleton instance
462
484
  this._promptCache = promptCacheSingleton;
463
485
  }
@@ -2,11 +2,12 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports._ExperimentManager = void 0;
4
4
  exports.evaluate = evaluate;
5
+ exports._reorderResultRowsByExampleIndex = _reorderResultRowsByExampleIndex;
6
+ exports._mapWithConcurrency = _mapWithConcurrency;
5
7
  const index_js_1 = require("../index.cjs");
6
8
  const traceable_js_1 = require("../traceable.cjs");
7
9
  const _git_js_1 = require("../utils/_git.cjs");
8
10
  const _uuid_js_1 = require("../utils/_uuid.cjs");
9
- const async_caller_js_1 = require("../utils/async_caller.cjs");
10
11
  const atee_js_1 = require("../utils/atee.cjs");
11
12
  const env_js_1 = require("../utils/env.cjs");
12
13
  const error_js_1 = require("../utils/error.cjs");
@@ -14,10 +15,22 @@ const _random_name_js_1 = require("./_random_name.cjs");
14
15
  const evaluator_js_1 = require("./evaluator.cjs");
15
16
  const uuid_1 = require("uuid");
16
17
  const evaluate_comparative_js_1 = require("./evaluate_comparative.cjs");
18
+ const p_queue_js_1 = require("../utils/p-queue.cjs");
17
19
  // Implementation signature
18
20
  function evaluate(target, options) {
19
21
  return _evaluate(target, options);
20
22
  }
23
+ function _reorderResultRowsByExampleIndex(rows) {
24
+ const sortedRows = [...rows].sort((a, b) => a.exampleIndex - b.exampleIndex);
25
+ return {
26
+ orderedRows: sortedRows.map(({ run, example, evaluationResults }) => ({
27
+ run,
28
+ example,
29
+ evaluationResults,
30
+ })),
31
+ orderedRuns: sortedRows.map((row) => row.run),
32
+ };
33
+ }
21
34
  /**
22
35
  * Manage the execution of experiments.
23
36
  *
@@ -124,6 +137,12 @@ class _ExperimentManager {
124
137
  writable: true,
125
138
  value: void 0
126
139
  });
140
+ Object.defineProperty(this, "_resultRows", {
141
+ enumerable: true,
142
+ configurable: true,
143
+ writable: true,
144
+ value: void 0
145
+ });
127
146
  Object.defineProperty(this, "_examples", {
128
147
  enumerable: true,
129
148
  configurable: true,
@@ -210,6 +229,7 @@ class _ExperimentManager {
210
229
  this._runs = args.runs;
211
230
  this._evaluationResults = args.evaluationResults;
212
231
  this._summaryResults = args.summaryResults;
232
+ this._resultRows = args.resultRows;
213
233
  this._numRepetitions = args.numRepetitions;
214
234
  this._includeAttachments = args.includeAttachments;
215
235
  }
@@ -304,13 +324,24 @@ class _ExperimentManager {
304
324
  }
305
325
  async withPredictions(target, options) {
306
326
  const experimentResults = this._predict(target, options);
327
+ const [rowsForResults, rowsForRuns] = (0, atee_js_1.atee)(experimentResults);
307
328
  return new _ExperimentManager({
308
329
  examples: await this.getExamples(),
309
330
  experiment: this._experiment,
310
331
  metadata: this._metadata,
311
332
  client: this.client,
333
+ resultRows: (async function* () {
334
+ for await (const pred of rowsForResults) {
335
+ yield {
336
+ run: pred.run,
337
+ example: pred.example,
338
+ evaluationResults: { results: [] },
339
+ exampleIndex: pred.exampleIndex,
340
+ };
341
+ }
342
+ })(),
312
343
  runs: (async function* () {
313
- for await (const pred of experimentResults) {
344
+ for await (const pred of rowsForRuns) {
314
345
  yield pred.run;
315
346
  }
316
347
  })(),
@@ -320,19 +351,20 @@ class _ExperimentManager {
320
351
  async withEvaluators(evaluators, options) {
321
352
  const resolvedEvaluators = _resolveEvaluators(evaluators);
322
353
  const experimentResults = this._score(resolvedEvaluators, options);
323
- const [r1, r2] = (0, atee_js_1.atee)(experimentResults);
354
+ const [rowsForResults, rowsForRuns, rowsForEvaluations] = (0, atee_js_1.atee)(experimentResults, 3);
324
355
  return new _ExperimentManager({
325
356
  examples: await this.getExamples(),
326
357
  experiment: this._experiment,
327
358
  metadata: this._metadata,
328
359
  client: this.client,
360
+ resultRows: rowsForResults,
329
361
  runs: (async function* () {
330
- for await (const result of r1) {
362
+ for await (const result of rowsForRuns) {
331
363
  yield result.run;
332
364
  }
333
365
  })(),
334
366
  evaluationResults: (async function* () {
335
- for await (const result of r2) {
367
+ for await (const result of rowsForEvaluations) {
336
368
  yield result.evaluationResults;
337
369
  }
338
370
  })(),
@@ -347,30 +379,43 @@ class _ExperimentManager {
347
379
  experiment: this._experiment,
348
380
  metadata: this._metadata,
349
381
  client: this.client,
350
- runs: this.runs,
382
+ runs: this._runs,
351
383
  _runsArray: this._runsArray,
352
384
  evaluationResults: this._evaluationResults,
385
+ resultRows: this._resultRows,
353
386
  summaryResults: aggregateFeedbackGen,
354
387
  includeAttachments: this._includeAttachments,
355
388
  });
356
389
  }
357
390
  async *getResults() {
358
- const examples = await this.getExamples();
359
- const evaluationResults = [];
360
391
  if (!this._runsArray) {
361
392
  this._runsArray = [];
362
- for await (const run of this.runs) {
363
- this._runsArray.push(run);
364
- }
365
393
  }
366
- for await (const evaluationResult of this.evaluationResults) {
367
- evaluationResults.push(evaluationResult);
394
+ if (this._resultRows) {
395
+ for await (const result of this._resultRows) {
396
+ this._runsArray.push(result.run);
397
+ yield result;
398
+ }
399
+ return;
368
400
  }
369
- for (let i = 0; i < this._runsArray.length; i++) {
401
+ const examples = await this.getExamples();
402
+ const runsIterator = this.runs[Symbol.asyncIterator]();
403
+ const evaluationIterator = this.evaluationResults[Symbol.asyncIterator]();
404
+ for (let i = 0; i < examples.length; i++) {
405
+ const runResult = await runsIterator.next();
406
+ if (runResult.done) {
407
+ break;
408
+ }
409
+ const evaluationResult = await evaluationIterator.next();
410
+ const evaluationResults = evaluationResult.done
411
+ ? { results: [] }
412
+ : evaluationResult.value;
413
+ this._runsArray.push(runResult.value);
370
414
  yield {
371
- run: this._runsArray[i],
415
+ run: runResult.value,
372
416
  example: examples[i],
373
- evaluationResults: evaluationResults[i],
417
+ evaluationResults,
418
+ exampleIndex: i,
374
419
  };
375
420
  }
376
421
  }
@@ -400,29 +445,52 @@ class _ExperimentManager {
400
445
  async *_predict(target, options) {
401
446
  const maxConcurrency = options?.maxConcurrency ?? 0;
402
447
  const examples = await this.getExamples();
403
- if (maxConcurrency === 0) {
404
- for (const example of examples) {
405
- yield await _forward(target, example, this.experimentName, this._metadata, this.client, this._includeAttachments);
448
+ let hadPredictionError = false;
449
+ let shouldThrowEndError = false;
450
+ let endErrorToThrow;
451
+ try {
452
+ // maxConcurrency: 0 means sequential execution (matching Python behavior)
453
+ const queue = options?.queue ??
454
+ new p_queue_js_1.PQueue({
455
+ concurrency: maxConcurrency === 0 ? 1 : maxConcurrency,
456
+ });
457
+ const examplesWithIndex = examples.map((example, i) => ({
458
+ example,
459
+ exampleIndex: i,
460
+ }));
461
+ for await (const result of _mapWithConcurrency(examplesWithIndex, queue, (item) => _forward(target, item.example, this.experimentName, this._metadata, this.client, this._includeAttachments).then((forwardResult) => ({
462
+ ...forwardResult,
463
+ exampleIndex: item.exampleIndex,
464
+ })))) {
465
+ yield result;
406
466
  }
407
467
  }
408
- else {
409
- const caller = new async_caller_js_1.AsyncCaller({
410
- maxConcurrency,
411
- debug: this.client.debug,
412
- });
413
- const futures = [];
414
- for await (const example of examples) {
415
- futures.push(caller.call(_forward, target, example, this.experimentName, this._metadata, this.client, this._includeAttachments));
468
+ catch (error) {
469
+ hadPredictionError = true;
470
+ throw error;
471
+ }
472
+ finally {
473
+ try {
474
+ // Always attempt to close out the project metadata, even on prediction errors.
475
+ await this._end();
416
476
  }
417
- for await (const future of futures) {
418
- yield future;
477
+ catch (endError) {
478
+ if (hadPredictionError) {
479
+ console.error(`Error finalizing experiment: ${endError}`);
480
+ (0, error_js_1.printErrorStackTrace)(endError);
481
+ }
482
+ else {
483
+ shouldThrowEndError = true;
484
+ endErrorToThrow = endError;
485
+ }
419
486
  }
420
487
  }
421
- // Close out the project.
422
- await this._end();
488
+ if (shouldThrowEndError) {
489
+ throw endErrorToThrow;
490
+ }
423
491
  }
424
492
  async _runEvaluators(evaluators, currentResults, fields) {
425
- const { run, example, evaluationResults } = currentResults;
493
+ const { run, example, evaluationResults, exampleIndex } = currentResults;
426
494
  for (const evaluator of evaluators) {
427
495
  try {
428
496
  const options = {
@@ -448,6 +516,7 @@ class _ExperimentManager {
448
516
  run,
449
517
  example,
450
518
  evaluationResults,
519
+ exampleIndex,
451
520
  };
452
521
  }
453
522
  /**
@@ -458,28 +527,16 @@ class _ExperimentManager {
458
527
  * @param {number} maxConcurrency
459
528
  */
460
529
  async *_score(evaluators, options) {
461
- const { maxConcurrency = 0 } = options || {};
462
- if (maxConcurrency === 0) {
463
- for await (const currentResults of this.getResults()) {
464
- yield this._runEvaluators(evaluators, currentResults, {
465
- client: this.client,
466
- });
467
- }
468
- }
469
- else {
470
- const caller = new async_caller_js_1.AsyncCaller({
471
- maxConcurrency,
472
- debug: this.client.debug,
530
+ const { maxConcurrency = 0, queue: providedQueue } = options || {};
531
+ // maxConcurrency: 0 means sequential execution (matching Python behavior)
532
+ const queue = providedQueue ??
533
+ new p_queue_js_1.PQueue({
534
+ concurrency: maxConcurrency === 0 ? 1 : maxConcurrency,
473
535
  });
474
- const futures = [];
475
- for await (const currentResults of this.getResults()) {
476
- futures.push(caller.call(this._runEvaluators, evaluators, currentResults, {
477
- client: this.client,
478
- }));
479
- }
480
- for (const result of futures) {
481
- yield result;
482
- }
536
+ for await (const result of _mapWithConcurrency(this.getResults(), queue, (currentResults) => this._runEvaluators(evaluators, currentResults, {
537
+ client: this.client,
538
+ }))) {
539
+ yield result;
483
540
  }
484
541
  }
485
542
  async *_applySummaryEvaluators(summaryEvaluators) {
@@ -632,10 +689,14 @@ class ExperimentResults {
632
689
  }
633
690
  }
634
691
  async processData(manager) {
692
+ const unorderedResults = [];
635
693
  for await (const item of manager.getResults()) {
636
- this.results.push(item);
637
- this.processedCount++;
694
+ unorderedResults.push(item);
638
695
  }
696
+ const { orderedRows, orderedRuns } = _reorderResultRowsByExampleIndex(unorderedResults);
697
+ manager._runsArray = orderedRuns;
698
+ this.results = orderedRows;
699
+ this.processedCount = this.results.length;
639
700
  this.summaryResults = await manager.getSummaryScores();
640
701
  }
641
702
  get length() {
@@ -676,14 +737,40 @@ async function _evaluate(target, fields) {
676
737
  numRepetitions: fields.numRepetitions ?? 1,
677
738
  includeAttachments: standardFields.includeAttachments,
678
739
  }).start();
740
+ const targetConcurrency = standardFields.targetConcurrency ?? standardFields.maxConcurrency ?? 0;
741
+ const evaluationConcurrency = standardFields.evaluationConcurrency ?? standardFields.maxConcurrency ?? 0;
742
+ // Determine if we should use separate queues or a shared queue
743
+ const useSeparateQueues = standardFields.targetConcurrency !== undefined &&
744
+ standardFields.evaluationConcurrency !== undefined;
745
+ let sharedQueue;
746
+ let targetQueue;
747
+ let evaluationQueue;
748
+ if (useSeparateQueues) {
749
+ // Create separate queues for target and evaluation
750
+ if (targetConcurrency > 0) {
751
+ targetQueue = new p_queue_js_1.PQueue({ concurrency: targetConcurrency });
752
+ }
753
+ if (evaluationConcurrency > 0) {
754
+ evaluationQueue = new p_queue_js_1.PQueue({ concurrency: evaluationConcurrency });
755
+ }
756
+ }
757
+ else {
758
+ // Use a shared queue
759
+ const sharedConcurrency = standardFields.maxConcurrency ?? 0;
760
+ if (sharedConcurrency > 0) {
761
+ sharedQueue = new p_queue_js_1.PQueue({ concurrency: sharedConcurrency });
762
+ }
763
+ }
679
764
  if (_isCallable(target)) {
680
765
  manager = await manager.withPredictions(target, {
681
- maxConcurrency: fields.maxConcurrency,
766
+ maxConcurrency: targetConcurrency,
767
+ queue: useSeparateQueues ? targetQueue : sharedQueue,
682
768
  });
683
769
  }
684
770
  if (standardFields.evaluators) {
685
771
  manager = await manager.withEvaluators(standardFields.evaluators, {
686
- maxConcurrency: fields.maxConcurrency,
772
+ maxConcurrency: evaluationConcurrency,
773
+ queue: useSeparateQueues ? evaluationQueue : sharedQueue,
687
774
  });
688
775
  }
689
776
  if (standardFields.summaryEvaluators) {
@@ -868,6 +955,30 @@ async function _resolveExperiment(experiment, runs, client) {
868
955
  }
869
956
  return [undefined, undefined];
870
957
  }
958
+ /**
959
+ * Map over an iterable with bounded concurrency using p-queue.
960
+ * Results are yielded as soon as they resolve (input order is not preserved).
961
+ * The queue handles concurrency limits internally.
962
+ */
963
+ async function* _mapWithConcurrency(iterable, queue, mapper) {
964
+ const pending = new Set();
965
+ // Add all tasks to p-queue immediately (p-queue handles concurrency)
966
+ for await (const input of iterable) {
967
+ const task = queue
968
+ .add(() => mapper(input))
969
+ .then((value) => ({
970
+ value,
971
+ self: task,
972
+ }));
973
+ pending.add(task);
974
+ }
975
+ // Yield results as they complete
976
+ while (pending.size > 0) {
977
+ const { value, self } = await Promise.race(pending);
978
+ pending.delete(self);
979
+ yield value;
980
+ }
981
+ }
871
982
  function _isCallable(target) {
872
983
  return Boolean(typeof target === "function" ||
873
984
  ("invoke" in target && typeof target.invoke === "function"));
@@ -2,6 +2,7 @@ import { Client } from "../index.js";
2
2
  import { AttachmentInfo, Example, KVMap, Run, TracerSession } from "../schemas.js";
3
3
  import { EvaluationResult, EvaluationResults, RunEvaluator } from "./evaluator.js";
4
4
  import { ComparisonEvaluationResults, ComparativeEvaluator } from "./evaluate_comparative.js";
5
+ import { PQueueType } from "../utils/p-queue.js";
5
6
  export type TargetConfigT = KVMap & {
6
7
  attachments?: Record<string, AttachmentInfo>;
7
8
  callbacks?: any;
@@ -55,6 +56,7 @@ export type EvaluatorT = DeprecatedRunEvaluator | DeprecatedFunctionEvaluator |
55
56
  interface _ForwardResults {
56
57
  run: Run;
57
58
  example: Example;
59
+ exampleIndex: number;
58
60
  }
59
61
  interface _ExperimentManagerArgs {
60
62
  data?: DataT;
@@ -67,6 +69,7 @@ interface _ExperimentManagerArgs {
67
69
  examples?: Example[];
68
70
  numRepetitions?: number;
69
71
  _runsArray?: Run[];
72
+ resultRows?: AsyncGenerator<_ExperimentResultRowWithIndex>;
70
73
  includeAttachments?: boolean;
71
74
  }
72
75
  type BaseEvaluateOptions = {
@@ -85,7 +88,8 @@ type BaseEvaluateOptions = {
85
88
  */
86
89
  description?: string;
87
90
  /**
88
- * The maximum number of concurrent evaluations to run.
91
+ * The maximum concurrency to use for predictions/evaluations when a more
92
+ * specific concurrency option is not provided.
89
93
  * @default undefined
90
94
  */
91
95
  maxConcurrency?: number;
@@ -102,6 +106,18 @@ type BaseEvaluateOptions = {
102
106
  numRepetitions?: number;
103
107
  };
104
108
  export interface EvaluateOptions extends BaseEvaluateOptions {
109
+ /**
110
+ * The maximum number of concurrent predictions to run.
111
+ * If not provided, defaults to `maxConcurrency` when set.
112
+ * @default undefined
113
+ */
114
+ targetConcurrency?: number;
115
+ /**
116
+ * The maximum number of concurrent evaluators to run.
117
+ * If not provided, defaults to `maxConcurrency` when set.
118
+ * @default undefined
119
+ */
120
+ evaluationConcurrency?: number;
105
121
  /**
106
122
  * A list of evaluators to run on each example.
107
123
  * @default undefined
@@ -146,6 +162,13 @@ export interface ExperimentResultRow {
146
162
  example: Example;
147
163
  evaluationResults: EvaluationResults;
148
164
  }
165
+ interface _ExperimentResultRowWithIndex extends ExperimentResultRow {
166
+ exampleIndex: number;
167
+ }
168
+ export declare function _reorderResultRowsByExampleIndex(rows: _ExperimentResultRowWithIndex[]): {
169
+ orderedRows: ExperimentResultRow[];
170
+ orderedRuns: Run[];
171
+ };
149
172
  /**
150
173
  * Manage the execution of experiments.
151
174
  *
@@ -157,6 +180,7 @@ export declare class _ExperimentManager {
157
180
  _runs?: AsyncGenerator<Run>;
158
181
  _evaluationResults?: AsyncGenerator<EvaluationResults>;
159
182
  _summaryResults?: AsyncGenerator<(runsArray: Run[]) => AsyncGenerator<EvaluationResults, any, unknown>, any, unknown>;
183
+ _resultRows?: AsyncGenerator<_ExperimentResultRowWithIndex>;
160
184
  _examples?: Example[];
161
185
  _numRepetitions?: number;
162
186
  _runsArray?: Run[];
@@ -181,12 +205,14 @@ export declare class _ExperimentManager {
181
205
  start(): Promise<_ExperimentManager>;
182
206
  withPredictions(target: StandardTargetT, options?: {
183
207
  maxConcurrency?: number;
208
+ queue?: PQueueType;
184
209
  }): Promise<_ExperimentManager>;
185
210
  withEvaluators(evaluators: Array<EvaluatorT | RunEvaluator>, options?: {
186
211
  maxConcurrency?: number;
212
+ queue?: PQueueType;
187
213
  }): Promise<_ExperimentManager>;
188
214
  withSummaryEvaluators(summaryEvaluators: Array<SummaryEvaluatorT>): Promise<_ExperimentManager>;
189
- getResults(): AsyncGenerator<ExperimentResultRow>;
215
+ getResults(): AsyncGenerator<_ExperimentResultRowWithIndex>;
190
216
  getSummaryScores(): Promise<EvaluationResults>;
191
217
  /**
192
218
  * Run the target function or runnable on the examples.
@@ -196,10 +222,11 @@ export declare class _ExperimentManager {
196
222
  */
197
223
  _predict(target: StandardTargetT, options?: {
198
224
  maxConcurrency?: number;
225
+ queue?: PQueueType;
199
226
  }): AsyncGenerator<_ForwardResults>;
200
- _runEvaluators(evaluators: Array<RunEvaluator>, currentResults: ExperimentResultRow, fields: {
227
+ _runEvaluators(evaluators: Array<RunEvaluator>, currentResults: _ExperimentResultRowWithIndex, fields: {
201
228
  client: Client;
202
- }): Promise<ExperimentResultRow>;
229
+ }): Promise<_ExperimentResultRowWithIndex>;
203
230
  /**
204
231
  * Run the evaluators on the prediction stream.
205
232
  * Expects runs to be available in the manager.
@@ -209,7 +236,8 @@ export declare class _ExperimentManager {
209
236
  */
210
237
  _score(evaluators: Array<RunEvaluator>, options?: {
211
238
  maxConcurrency?: number;
212
- }): AsyncGenerator<ExperimentResultRow>;
239
+ queue?: PQueueType;
240
+ }): AsyncGenerator<_ExperimentResultRowWithIndex>;
213
241
  _applySummaryEvaluators(summaryEvaluators: Array<SummaryEvaluatorT>): AsyncGenerator<(runsArray: Run[]) => AsyncGenerator<EvaluationResults>>;
214
242
  _getDatasetVersion(): Promise<string | undefined>;
215
243
  _getDatasetSplits(): Promise<string[] | undefined>;
@@ -233,4 +261,10 @@ declare class ExperimentResults implements AsyncIterableIterator<ExperimentResul
233
261
  processData(manager: _ExperimentManager): Promise<void>;
234
262
  get length(): number;
235
263
  }
264
+ /**
265
+ * Map over an iterable with bounded concurrency using p-queue.
266
+ * Results are yielded as soon as they resolve (input order is not preserved).
267
+ * The queue handles concurrency limits internally.
268
+ */
269
+ export declare function _mapWithConcurrency<TInput, TOutput>(iterable: Iterable<TInput> | AsyncIterable<TInput>, queue: PQueueType, mapper: (value: TInput) => Promise<TOutput>): AsyncGenerator<TOutput>;
236
270
  export {};
@@ -2,7 +2,6 @@ import { Client } from "../index.js";
2
2
  import { isTraceableFunction, traceable } from "../traceable.js";
3
3
  import { getDefaultRevisionId, getGitInfo } from "../utils/_git.js";
4
4
  import { assertUuid } from "../utils/_uuid.js";
5
- import { AsyncCaller } from "../utils/async_caller.js";
6
5
  import { atee } from "../utils/atee.js";
7
6
  import { getLangSmithEnvVarsMetadata } from "../utils/env.js";
8
7
  import { printErrorStackTrace } from "../utils/error.js";
@@ -10,10 +9,22 @@ import { randomName } from "./_random_name.js";
10
9
  import { runEvaluator, } from "./evaluator.js";
11
10
  import { v4 as uuidv4 } from "uuid";
12
11
  import { evaluateComparative, } from "./evaluate_comparative.js";
12
+ import { PQueue } from "../utils/p-queue.js";
13
13
  // Implementation signature
14
14
  export function evaluate(target, options) {
15
15
  return _evaluate(target, options);
16
16
  }
17
+ export function _reorderResultRowsByExampleIndex(rows) {
18
+ const sortedRows = [...rows].sort((a, b) => a.exampleIndex - b.exampleIndex);
19
+ return {
20
+ orderedRows: sortedRows.map(({ run, example, evaluationResults }) => ({
21
+ run,
22
+ example,
23
+ evaluationResults,
24
+ })),
25
+ orderedRuns: sortedRows.map((row) => row.run),
26
+ };
27
+ }
17
28
  /**
18
29
  * Manage the execution of experiments.
19
30
  *
@@ -120,6 +131,12 @@ export class _ExperimentManager {
120
131
  writable: true,
121
132
  value: void 0
122
133
  });
134
+ Object.defineProperty(this, "_resultRows", {
135
+ enumerable: true,
136
+ configurable: true,
137
+ writable: true,
138
+ value: void 0
139
+ });
123
140
  Object.defineProperty(this, "_examples", {
124
141
  enumerable: true,
125
142
  configurable: true,
@@ -206,6 +223,7 @@ export class _ExperimentManager {
206
223
  this._runs = args.runs;
207
224
  this._evaluationResults = args.evaluationResults;
208
225
  this._summaryResults = args.summaryResults;
226
+ this._resultRows = args.resultRows;
209
227
  this._numRepetitions = args.numRepetitions;
210
228
  this._includeAttachments = args.includeAttachments;
211
229
  }
@@ -300,13 +318,24 @@ export class _ExperimentManager {
300
318
  }
301
319
  async withPredictions(target, options) {
302
320
  const experimentResults = this._predict(target, options);
321
+ const [rowsForResults, rowsForRuns] = atee(experimentResults);
303
322
  return new _ExperimentManager({
304
323
  examples: await this.getExamples(),
305
324
  experiment: this._experiment,
306
325
  metadata: this._metadata,
307
326
  client: this.client,
327
+ resultRows: (async function* () {
328
+ for await (const pred of rowsForResults) {
329
+ yield {
330
+ run: pred.run,
331
+ example: pred.example,
332
+ evaluationResults: { results: [] },
333
+ exampleIndex: pred.exampleIndex,
334
+ };
335
+ }
336
+ })(),
308
337
  runs: (async function* () {
309
- for await (const pred of experimentResults) {
338
+ for await (const pred of rowsForRuns) {
310
339
  yield pred.run;
311
340
  }
312
341
  })(),
@@ -316,19 +345,20 @@ export class _ExperimentManager {
316
345
  async withEvaluators(evaluators, options) {
317
346
  const resolvedEvaluators = _resolveEvaluators(evaluators);
318
347
  const experimentResults = this._score(resolvedEvaluators, options);
319
- const [r1, r2] = atee(experimentResults);
348
+ const [rowsForResults, rowsForRuns, rowsForEvaluations] = atee(experimentResults, 3);
320
349
  return new _ExperimentManager({
321
350
  examples: await this.getExamples(),
322
351
  experiment: this._experiment,
323
352
  metadata: this._metadata,
324
353
  client: this.client,
354
+ resultRows: rowsForResults,
325
355
  runs: (async function* () {
326
- for await (const result of r1) {
356
+ for await (const result of rowsForRuns) {
327
357
  yield result.run;
328
358
  }
329
359
  })(),
330
360
  evaluationResults: (async function* () {
331
- for await (const result of r2) {
361
+ for await (const result of rowsForEvaluations) {
332
362
  yield result.evaluationResults;
333
363
  }
334
364
  })(),
@@ -343,30 +373,43 @@ export class _ExperimentManager {
343
373
  experiment: this._experiment,
344
374
  metadata: this._metadata,
345
375
  client: this.client,
346
- runs: this.runs,
376
+ runs: this._runs,
347
377
  _runsArray: this._runsArray,
348
378
  evaluationResults: this._evaluationResults,
379
+ resultRows: this._resultRows,
349
380
  summaryResults: aggregateFeedbackGen,
350
381
  includeAttachments: this._includeAttachments,
351
382
  });
352
383
  }
353
384
  async *getResults() {
354
- const examples = await this.getExamples();
355
- const evaluationResults = [];
356
385
  if (!this._runsArray) {
357
386
  this._runsArray = [];
358
- for await (const run of this.runs) {
359
- this._runsArray.push(run);
360
- }
361
387
  }
362
- for await (const evaluationResult of this.evaluationResults) {
363
- evaluationResults.push(evaluationResult);
388
+ if (this._resultRows) {
389
+ for await (const result of this._resultRows) {
390
+ this._runsArray.push(result.run);
391
+ yield result;
392
+ }
393
+ return;
364
394
  }
365
- for (let i = 0; i < this._runsArray.length; i++) {
395
+ const examples = await this.getExamples();
396
+ const runsIterator = this.runs[Symbol.asyncIterator]();
397
+ const evaluationIterator = this.evaluationResults[Symbol.asyncIterator]();
398
+ for (let i = 0; i < examples.length; i++) {
399
+ const runResult = await runsIterator.next();
400
+ if (runResult.done) {
401
+ break;
402
+ }
403
+ const evaluationResult = await evaluationIterator.next();
404
+ const evaluationResults = evaluationResult.done
405
+ ? { results: [] }
406
+ : evaluationResult.value;
407
+ this._runsArray.push(runResult.value);
366
408
  yield {
367
- run: this._runsArray[i],
409
+ run: runResult.value,
368
410
  example: examples[i],
369
- evaluationResults: evaluationResults[i],
411
+ evaluationResults,
412
+ exampleIndex: i,
370
413
  };
371
414
  }
372
415
  }
@@ -396,29 +439,52 @@ export class _ExperimentManager {
396
439
  async *_predict(target, options) {
397
440
  const maxConcurrency = options?.maxConcurrency ?? 0;
398
441
  const examples = await this.getExamples();
399
- if (maxConcurrency === 0) {
400
- for (const example of examples) {
401
- yield await _forward(target, example, this.experimentName, this._metadata, this.client, this._includeAttachments);
442
+ let hadPredictionError = false;
443
+ let shouldThrowEndError = false;
444
+ let endErrorToThrow;
445
+ try {
446
+ // maxConcurrency: 0 means sequential execution (matching Python behavior)
447
+ const queue = options?.queue ??
448
+ new PQueue({
449
+ concurrency: maxConcurrency === 0 ? 1 : maxConcurrency,
450
+ });
451
+ const examplesWithIndex = examples.map((example, i) => ({
452
+ example,
453
+ exampleIndex: i,
454
+ }));
455
+ for await (const result of _mapWithConcurrency(examplesWithIndex, queue, (item) => _forward(target, item.example, this.experimentName, this._metadata, this.client, this._includeAttachments).then((forwardResult) => ({
456
+ ...forwardResult,
457
+ exampleIndex: item.exampleIndex,
458
+ })))) {
459
+ yield result;
402
460
  }
403
461
  }
404
- else {
405
- const caller = new AsyncCaller({
406
- maxConcurrency,
407
- debug: this.client.debug,
408
- });
409
- const futures = [];
410
- for await (const example of examples) {
411
- futures.push(caller.call(_forward, target, example, this.experimentName, this._metadata, this.client, this._includeAttachments));
462
+ catch (error) {
463
+ hadPredictionError = true;
464
+ throw error;
465
+ }
466
+ finally {
467
+ try {
468
+ // Always attempt to close out the project metadata, even on prediction errors.
469
+ await this._end();
412
470
  }
413
- for await (const future of futures) {
414
- yield future;
471
+ catch (endError) {
472
+ if (hadPredictionError) {
473
+ console.error(`Error finalizing experiment: ${endError}`);
474
+ printErrorStackTrace(endError);
475
+ }
476
+ else {
477
+ shouldThrowEndError = true;
478
+ endErrorToThrow = endError;
479
+ }
415
480
  }
416
481
  }
417
- // Close out the project.
418
- await this._end();
482
+ if (shouldThrowEndError) {
483
+ throw endErrorToThrow;
484
+ }
419
485
  }
420
486
  async _runEvaluators(evaluators, currentResults, fields) {
421
- const { run, example, evaluationResults } = currentResults;
487
+ const { run, example, evaluationResults, exampleIndex } = currentResults;
422
488
  for (const evaluator of evaluators) {
423
489
  try {
424
490
  const options = {
@@ -444,6 +510,7 @@ export class _ExperimentManager {
444
510
  run,
445
511
  example,
446
512
  evaluationResults,
513
+ exampleIndex,
447
514
  };
448
515
  }
449
516
  /**
@@ -454,28 +521,16 @@ export class _ExperimentManager {
454
521
  * @param {number} maxConcurrency
455
522
  */
456
523
  async *_score(evaluators, options) {
457
- const { maxConcurrency = 0 } = options || {};
458
- if (maxConcurrency === 0) {
459
- for await (const currentResults of this.getResults()) {
460
- yield this._runEvaluators(evaluators, currentResults, {
461
- client: this.client,
462
- });
463
- }
464
- }
465
- else {
466
- const caller = new AsyncCaller({
467
- maxConcurrency,
468
- debug: this.client.debug,
524
+ const { maxConcurrency = 0, queue: providedQueue } = options || {};
525
+ // maxConcurrency: 0 means sequential execution (matching Python behavior)
526
+ const queue = providedQueue ??
527
+ new PQueue({
528
+ concurrency: maxConcurrency === 0 ? 1 : maxConcurrency,
469
529
  });
470
- const futures = [];
471
- for await (const currentResults of this.getResults()) {
472
- futures.push(caller.call(this._runEvaluators, evaluators, currentResults, {
473
- client: this.client,
474
- }));
475
- }
476
- for (const result of futures) {
477
- yield result;
478
- }
530
+ for await (const result of _mapWithConcurrency(this.getResults(), queue, (currentResults) => this._runEvaluators(evaluators, currentResults, {
531
+ client: this.client,
532
+ }))) {
533
+ yield result;
479
534
  }
480
535
  }
481
536
  async *_applySummaryEvaluators(summaryEvaluators) {
@@ -627,10 +682,14 @@ class ExperimentResults {
627
682
  }
628
683
  }
629
684
  async processData(manager) {
685
+ const unorderedResults = [];
630
686
  for await (const item of manager.getResults()) {
631
- this.results.push(item);
632
- this.processedCount++;
687
+ unorderedResults.push(item);
633
688
  }
689
+ const { orderedRows, orderedRuns } = _reorderResultRowsByExampleIndex(unorderedResults);
690
+ manager._runsArray = orderedRuns;
691
+ this.results = orderedRows;
692
+ this.processedCount = this.results.length;
634
693
  this.summaryResults = await manager.getSummaryScores();
635
694
  }
636
695
  get length() {
@@ -671,14 +730,40 @@ async function _evaluate(target, fields) {
671
730
  numRepetitions: fields.numRepetitions ?? 1,
672
731
  includeAttachments: standardFields.includeAttachments,
673
732
  }).start();
733
+ const targetConcurrency = standardFields.targetConcurrency ?? standardFields.maxConcurrency ?? 0;
734
+ const evaluationConcurrency = standardFields.evaluationConcurrency ?? standardFields.maxConcurrency ?? 0;
735
+ // Determine if we should use separate queues or a shared queue
736
+ const useSeparateQueues = standardFields.targetConcurrency !== undefined &&
737
+ standardFields.evaluationConcurrency !== undefined;
738
+ let sharedQueue;
739
+ let targetQueue;
740
+ let evaluationQueue;
741
+ if (useSeparateQueues) {
742
+ // Create separate queues for target and evaluation
743
+ if (targetConcurrency > 0) {
744
+ targetQueue = new PQueue({ concurrency: targetConcurrency });
745
+ }
746
+ if (evaluationConcurrency > 0) {
747
+ evaluationQueue = new PQueue({ concurrency: evaluationConcurrency });
748
+ }
749
+ }
750
+ else {
751
+ // Use a shared queue
752
+ const sharedConcurrency = standardFields.maxConcurrency ?? 0;
753
+ if (sharedConcurrency > 0) {
754
+ sharedQueue = new PQueue({ concurrency: sharedConcurrency });
755
+ }
756
+ }
674
757
  if (_isCallable(target)) {
675
758
  manager = await manager.withPredictions(target, {
676
- maxConcurrency: fields.maxConcurrency,
759
+ maxConcurrency: targetConcurrency,
760
+ queue: useSeparateQueues ? targetQueue : sharedQueue,
677
761
  });
678
762
  }
679
763
  if (standardFields.evaluators) {
680
764
  manager = await manager.withEvaluators(standardFields.evaluators, {
681
- maxConcurrency: fields.maxConcurrency,
765
+ maxConcurrency: evaluationConcurrency,
766
+ queue: useSeparateQueues ? evaluationQueue : sharedQueue,
682
767
  });
683
768
  }
684
769
  if (standardFields.summaryEvaluators) {
@@ -863,6 +948,30 @@ async function _resolveExperiment(experiment, runs, client) {
863
948
  }
864
949
  return [undefined, undefined];
865
950
  }
951
+ /**
952
+ * Map over an iterable with bounded concurrency using p-queue.
953
+ * Results are yielded as soon as they resolve (input order is not preserved).
954
+ * The queue handles concurrency limits internally.
955
+ */
956
+ export async function* _mapWithConcurrency(iterable, queue, mapper) {
957
+ const pending = new Set();
958
+ // Add all tasks to p-queue immediately (p-queue handles concurrency)
959
+ for await (const input of iterable) {
960
+ const task = queue
961
+ .add(() => mapper(input))
962
+ .then((value) => ({
963
+ value,
964
+ self: task,
965
+ }));
966
+ pending.add(task);
967
+ }
968
+ // Yield results as they complete
969
+ while (pending.size > 0) {
970
+ const { value, self } = await Promise.race(pending);
971
+ pending.delete(self);
972
+ yield value;
973
+ }
974
+ }
866
975
  function _isCallable(target) {
867
976
  return Boolean(typeof target === "function" ||
868
977
  ("invoke" in target && typeof target.invoke === "function"));
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.promptCacheSingleton = exports.configureGlobalPromptCache = exports.PromptCache = exports.uuid7FromTime = exports.uuid7 = exports.getDefaultProjectName = exports.overrideFetchImplementation = exports.RunTree = exports.Client = void 0;
3
+ exports.__version__ = exports.promptCacheSingleton = exports.configureGlobalPromptCache = exports.PromptCache = exports.Cache = 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");
@@ -13,8 +13,9 @@ var uuid_js_1 = require("./uuid.cjs");
13
13
  Object.defineProperty(exports, "uuid7", { enumerable: true, get: function () { return uuid_js_1.uuid7; } });
14
14
  Object.defineProperty(exports, "uuid7FromTime", { enumerable: true, get: function () { return uuid_js_1.uuid7FromTime; } });
15
15
  var index_js_1 = require("./utils/prompt_cache/index.cjs");
16
+ Object.defineProperty(exports, "Cache", { enumerable: true, get: function () { return index_js_1.Cache; } });
16
17
  Object.defineProperty(exports, "PromptCache", { enumerable: true, get: function () { return index_js_1.PromptCache; } });
17
18
  Object.defineProperty(exports, "configureGlobalPromptCache", { enumerable: true, get: function () { return index_js_1.configureGlobalPromptCache; } });
18
19
  Object.defineProperty(exports, "promptCacheSingleton", { enumerable: true, get: function () { return index_js_1.promptCacheSingleton; } });
19
20
  // Update using yarn bump-version
20
- exports.__version__ = "0.5.0";
21
+ exports.__version__ = "0.5.1";
package/dist/index.d.ts CHANGED
@@ -4,5 +4,5 @@ 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
6
  export { uuid7, uuid7FromTime } from "./uuid.js";
7
- export { PromptCache, type CacheConfig, type CacheMetrics, configureGlobalPromptCache, promptCacheSingleton, } from "./utils/prompt_cache/index.js";
8
- export declare const __version__ = "0.5.0";
7
+ export { Cache, PromptCache, type CacheConfig, type CacheMetrics, configureGlobalPromptCache, promptCacheSingleton, } from "./utils/prompt_cache/index.js";
8
+ export declare const __version__ = "0.5.1";
package/dist/index.js CHANGED
@@ -3,6 +3,6 @@ export { RunTree } from "./run_trees.js";
3
3
  export { overrideFetchImplementation } from "./singletons/fetch.js";
4
4
  export { getDefaultProjectName } from "./utils/project.js";
5
5
  export { uuid7, uuid7FromTime } from "./uuid.js";
6
- export { PromptCache, configureGlobalPromptCache, promptCacheSingleton, } from "./utils/prompt_cache/index.js";
6
+ export { Cache, PromptCache, configureGlobalPromptCache, promptCacheSingleton, } from "./utils/prompt_cache/index.js";
7
7
  // Update using yarn bump-version
8
- export const __version__ = "0.5.0";
8
+ export const __version__ = "0.5.1";
@@ -5,7 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.AsyncCaller = void 0;
7
7
  const index_js_1 = __importDefault(require("../utils/p-retry/index.cjs"));
8
- const p_queue_1 = __importDefault(require("p-queue"));
8
+ const p_queue_js_1 = require("./p-queue.cjs");
9
9
  const STATUS_RETRYABLE = [
10
10
  408, // Request Timeout
11
11
  425, // Too Early
@@ -69,16 +69,7 @@ class AsyncCaller {
69
69
  this.maxConcurrency = params.maxConcurrency ?? Infinity;
70
70
  this.maxRetries = params.maxRetries ?? 6;
71
71
  this.maxQueueSizeBytes = params.maxQueueSizeBytes;
72
- if ("default" in p_queue_1.default) {
73
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
74
- this.queue = new p_queue_1.default.default({
75
- concurrency: this.maxConcurrency,
76
- });
77
- }
78
- else {
79
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
80
- this.queue = new p_queue_1.default({ concurrency: this.maxConcurrency });
81
- }
72
+ this.queue = new p_queue_js_1.PQueue({ concurrency: this.maxConcurrency });
82
73
  this.onFailedResponseHook = params?.onFailedResponseHook;
83
74
  }
84
75
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -1,3 +1,4 @@
1
+ import { PQueueType } from "./p-queue.js";
1
2
  type ResponseCallback = (response?: Response) => Promise<boolean>;
2
3
  export interface AsyncCallerParams {
3
4
  /**
@@ -44,7 +45,7 @@ export declare class AsyncCaller {
44
45
  protected maxConcurrency: AsyncCallerParams["maxConcurrency"];
45
46
  protected maxRetries: AsyncCallerParams["maxRetries"];
46
47
  protected maxQueueSizeBytes: AsyncCallerParams["maxQueueSizeBytes"];
47
- queue: typeof import("p-queue")["default"]["prototype"];
48
+ queue: PQueueType;
48
49
  private onFailedResponseHook?;
49
50
  private queueSizeBytes;
50
51
  constructor(params: AsyncCallerParams);
@@ -1,5 +1,5 @@
1
1
  import pRetry from "../utils/p-retry/index.js";
2
- import PQueueMod from "p-queue";
2
+ import { PQueue } from "./p-queue.js";
3
3
  const STATUS_RETRYABLE = [
4
4
  408, // Request Timeout
5
5
  425, // Too Early
@@ -63,16 +63,7 @@ export class AsyncCaller {
63
63
  this.maxConcurrency = params.maxConcurrency ?? Infinity;
64
64
  this.maxRetries = params.maxRetries ?? 6;
65
65
  this.maxQueueSizeBytes = params.maxQueueSizeBytes;
66
- if ("default" in PQueueMod) {
67
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
68
- this.queue = new PQueueMod.default({
69
- concurrency: this.maxConcurrency,
70
- });
71
- }
72
- else {
73
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
74
- this.queue = new PQueueMod({ concurrency: this.maxConcurrency });
75
- }
66
+ this.queue = new PQueue({ concurrency: this.maxConcurrency });
76
67
  this.onFailedResponseHook = params?.onFailedResponseHook;
77
68
  }
78
69
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PQueue = void 0;
7
+ const p_queue_1 = __importDefault(require("p-queue"));
8
+ // Helper to handle both default and named exports of p-queue
9
+ exports.PQueue = ("default" in p_queue_1.default ? p_queue_1.default.default : p_queue_1.default);
@@ -0,0 +1,2 @@
1
+ export declare const PQueue: typeof import("p-queue").default;
2
+ export type PQueueType = InstanceType<typeof PQueue>;
@@ -0,0 +1,3 @@
1
+ import PQueueMod from "p-queue";
2
+ // Helper to handle both default and named exports of p-queue
3
+ export const PQueue = ("default" in PQueueMod ? PQueueMod.default : PQueueMod);
@@ -9,7 +9,7 @@
9
9
  * that are swapped for browser builds via package.json browser field.
10
10
  */
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.promptCacheSingleton = exports.PromptCache = void 0;
12
+ exports.Cache = exports.promptCacheSingleton = exports.PromptCache = void 0;
13
13
  exports.configureGlobalPromptCache = configureGlobalPromptCache;
14
14
  const fs_js_1 = require("./fs.cjs");
15
15
  /**
@@ -321,3 +321,16 @@ exports.promptCacheSingleton = new PromptCache();
321
321
  function configureGlobalPromptCache(config) {
322
322
  exports.promptCacheSingleton.configure(config);
323
323
  }
324
+ /**
325
+ * @deprecated Use `PromptCache` instead. This is a deprecated alias.
326
+ *
327
+ * Deprecated alias for PromptCache. Use PromptCache instead.
328
+ */
329
+ class Cache extends PromptCache {
330
+ constructor(config = {}) {
331
+ console.warn("The 'Cache' class is deprecated and will be removed in a future version. " +
332
+ "Use 'PromptCache' instead.");
333
+ super(config);
334
+ }
335
+ }
336
+ exports.Cache = Cache;
@@ -154,3 +154,11 @@ export declare class PromptCache {
154
154
  * ```
155
155
  */
156
156
  export declare function configureGlobalPromptCache(config: CacheConfig): void;
157
+ /**
158
+ * @deprecated Use `PromptCache` instead. This is a deprecated alias.
159
+ *
160
+ * Deprecated alias for PromptCache. Use PromptCache instead.
161
+ */
162
+ export declare class Cache extends PromptCache {
163
+ constructor(config?: CacheConfig);
164
+ }
@@ -316,3 +316,15 @@ export const promptCacheSingleton = new PromptCache();
316
316
  export function configureGlobalPromptCache(config) {
317
317
  promptCacheSingleton.configure(config);
318
318
  }
319
+ /**
320
+ * @deprecated Use `PromptCache` instead. This is a deprecated alias.
321
+ *
322
+ * Deprecated alias for PromptCache. Use PromptCache instead.
323
+ */
324
+ export class Cache extends PromptCache {
325
+ constructor(config = {}) {
326
+ console.warn("The 'Cache' class is deprecated and will be removed in a future version. " +
327
+ "Use 'PromptCache' instead.");
328
+ super(config);
329
+ }
330
+ }
@@ -207,12 +207,38 @@ const wrapAnthropic = (anthropic, options) => {
207
207
  ...options,
208
208
  metadata: restMetadata,
209
209
  };
210
+ /**
211
+ * Transform system parameter into visible message for playground editability.
212
+ * This provides parity with the Python SDK behavior and enables system prompts
213
+ * to be viewed and edited in the LangSmith playground.
214
+ */
215
+ function processSystemMessage(params) {
216
+ if (!params.system) {
217
+ return params;
218
+ }
219
+ const processed = { ...params };
220
+ // Handle both string and ContentBlock[] formats
221
+ const systemContent = Array.isArray(params.system)
222
+ ? params.system
223
+ .map((block) => typeof block === "string" ? block : block.text)
224
+ .join("\n")
225
+ : params.system;
226
+ // Transform into first message
227
+ processed.messages = [
228
+ { role: "system", content: systemContent },
229
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
230
+ ...(params.messages || []),
231
+ ];
232
+ delete processed.system;
233
+ return processed;
234
+ }
210
235
  // Common configuration for messages.create
211
236
  const messagesCreateConfig = {
212
237
  name: "ChatAnthropic",
213
238
  run_type: "llm",
214
239
  aggregator: messageAggregator,
215
240
  argsConfigPath: [1, "langsmithExtra"],
241
+ processInputs: processSystemMessage,
216
242
  getInvocationParams: (payload) => {
217
243
  if (typeof payload !== "object" || payload == null)
218
244
  return undefined;
@@ -279,6 +305,7 @@ const wrapAnthropic = (anthropic, options) => {
279
305
  run_type: "llm",
280
306
  aggregator: messageAggregator,
281
307
  argsConfigPath: [1, "langsmithExtra"],
308
+ processInputs: processSystemMessage,
282
309
  getInvocationParams: messagesCreateConfig.getInvocationParams,
283
310
  processOutputs: processMessageOutput,
284
311
  ...cleanedOptions,
@@ -300,6 +327,7 @@ const wrapAnthropic = (anthropic, options) => {
300
327
  run_type: "llm",
301
328
  aggregator: messageAggregator,
302
329
  argsConfigPath: [1, "langsmithExtra"],
330
+ processInputs: processSystemMessage,
303
331
  getInvocationParams: messagesCreateConfig.getInvocationParams,
304
332
  processOutputs: processMessageOutput,
305
333
  ...cleanedOptions,
@@ -204,12 +204,38 @@ export const wrapAnthropic = (anthropic, options) => {
204
204
  ...options,
205
205
  metadata: restMetadata,
206
206
  };
207
+ /**
208
+ * Transform system parameter into visible message for playground editability.
209
+ * This provides parity with the Python SDK behavior and enables system prompts
210
+ * to be viewed and edited in the LangSmith playground.
211
+ */
212
+ function processSystemMessage(params) {
213
+ if (!params.system) {
214
+ return params;
215
+ }
216
+ const processed = { ...params };
217
+ // Handle both string and ContentBlock[] formats
218
+ const systemContent = Array.isArray(params.system)
219
+ ? params.system
220
+ .map((block) => typeof block === "string" ? block : block.text)
221
+ .join("\n")
222
+ : params.system;
223
+ // Transform into first message
224
+ processed.messages = [
225
+ { role: "system", content: systemContent },
226
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
227
+ ...(params.messages || []),
228
+ ];
229
+ delete processed.system;
230
+ return processed;
231
+ }
207
232
  // Common configuration for messages.create
208
233
  const messagesCreateConfig = {
209
234
  name: "ChatAnthropic",
210
235
  run_type: "llm",
211
236
  aggregator: messageAggregator,
212
237
  argsConfigPath: [1, "langsmithExtra"],
238
+ processInputs: processSystemMessage,
213
239
  getInvocationParams: (payload) => {
214
240
  if (typeof payload !== "object" || payload == null)
215
241
  return undefined;
@@ -276,6 +302,7 @@ export const wrapAnthropic = (anthropic, options) => {
276
302
  run_type: "llm",
277
303
  aggregator: messageAggregator,
278
304
  argsConfigPath: [1, "langsmithExtra"],
305
+ processInputs: processSystemMessage,
279
306
  getInvocationParams: messagesCreateConfig.getInvocationParams,
280
307
  processOutputs: processMessageOutput,
281
308
  ...cleanedOptions,
@@ -297,6 +324,7 @@ export const wrapAnthropic = (anthropic, options) => {
297
324
  run_type: "llm",
298
325
  aggregator: messageAggregator,
299
326
  argsConfigPath: [1, "langsmithExtra"],
327
+ processInputs: processSystemMessage,
300
328
  getInvocationParams: messagesCreateConfig.getInvocationParams,
301
329
  processOutputs: processMessageOutput,
302
330
  ...cleanedOptions,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langsmith",
3
- "version": "0.5.0",
3
+ "version": "0.5.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": [