langsmith 0.1.3 → 0.1.6

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/README.md CHANGED
@@ -91,8 +91,8 @@ Langsmith's `traceable` wrapper function makes it easy to trace any function or
91
91
 
92
92
  <!-- markdown-link-check-disable -->
93
93
 
94
- The easiest ways to trace calls from the [OpenAI SDK](https://platform.openai.com/docs/api-reference) with LangSmith
95
- is using the `traceable` wrapper function available in LangSmith 0.1.0 and up.
94
+ The easiest way to trace calls from the [OpenAI SDK](https://platform.openai.com/docs/api-reference) with LangSmith
95
+ is using the `wrapOpenAI` wrapper function available in LangSmith 0.1.3 and up.
96
96
 
97
97
  In order to use, you first need to set your LangSmith API key:
98
98
 
@@ -106,7 +106,7 @@ Next, you will need to install the LangSmith SDK and the OpenAI SDK:
106
106
  npm install langsmith openai
107
107
  ```
108
108
 
109
- After that, initialize your OpenAI client and wrap the client with `wrapOpenAI` method to enable tracing for Completion and Chat completion API:
109
+ After that, initialize your OpenAI client and wrap the client with `wrapOpenAI` method to enable tracing for the completions and chat completions methods:
110
110
 
111
111
  ```ts
112
112
  import { OpenAI } from "openai";
@@ -29,6 +29,8 @@ services:
29
29
  condition: service_healthy
30
30
  clickhouse-setup:
31
31
  condition: service_completed_successfully
32
+ postgres-setup:
33
+ condition: service_completed_successfully
32
34
  restart: always
33
35
  langchain-queue:
34
36
  image: langchain/${_LANGSMITH_IMAGE_PREFIX-}langchainplus-backend:${_LANGSMITH_IMAGE_VERSION:-latest}
@@ -44,6 +46,8 @@ services:
44
46
  condition: service_healthy
45
47
  clickhouse-setup:
46
48
  condition: service_completed_successfully
49
+ postgres-setup:
50
+ condition: service_completed_successfully
47
51
  restart: always
48
52
  langchain-hub:
49
53
  image: langchain/${_LANGSMITH_IMAGE_PREFIX-}langchainhub-backend:${_LANGSMITH_IMAGE_VERSION:-latest}
@@ -55,7 +59,15 @@ services:
55
59
  ports:
56
60
  - 1985:1985
57
61
  depends_on:
58
- - langchain-db
62
+ langchain-db:
63
+ condition: service_healthy
64
+ langchain-redis:
65
+ condition: service_healthy
66
+ clickhouse-setup:
67
+ condition: service_completed_successfully
68
+ postgres-setup:
69
+ condition: service_completed_successfully
70
+ restart: always
59
71
  langchain-db:
60
72
  image: postgres:14.7
61
73
  command:
@@ -114,13 +126,29 @@ services:
114
126
  depends_on:
115
127
  langchain-clickhouse:
116
128
  condition: service_healthy
117
- restart: "no"
129
+ restart: "on-failure:10"
118
130
  entrypoint:
119
131
  [
120
132
  "bash",
121
133
  "-c",
122
134
  "migrate -source file://clickhouse/migrations -database 'clickhouse://langchain-clickhouse:9000?username=default&password=password&database=default&x-multi-statement=true&x-migrations-table-engine=MergeTree' up",
123
135
  ]
136
+ postgres-setup:
137
+ image: langchain/${_LANGSMITH_IMAGE_PREFIX-}langchainplus-backend:${_LANGSMITH_IMAGE_VERSION:-latest}
138
+ depends_on:
139
+ langchain-db:
140
+ condition: service_healthy
141
+ environment:
142
+ - LANGCHAIN_ENV=local_docker
143
+ - LOG_LEVEL=warning
144
+ - LANGSMITH_LICENSE_KEY=${LANGSMITH_LICENSE_KEY}
145
+ restart: "on-failure:10"
146
+ entrypoint:
147
+ [
148
+ "bash",
149
+ "-c",
150
+ "alembic upgrade head",
151
+ ]
124
152
  volumes:
125
153
  langchain-db-data:
126
154
  langchain-redis-data:
package/dist/client.cjs CHANGED
@@ -98,6 +98,18 @@ function assertUuid(str) {
98
98
  throw new Error(`Invalid UUID: ${str}`);
99
99
  }
100
100
  }
101
+ const handle429 = async (response) => {
102
+ if (response?.status === 429) {
103
+ const retryAfter = parseInt(response.headers.get("retry-after") ?? "30", 10) * 1000;
104
+ if (retryAfter > 0) {
105
+ await new Promise((resolve) => setTimeout(resolve, retryAfter));
106
+ // Return directly after calling this check
107
+ return true;
108
+ }
109
+ }
110
+ // Fall back to existing status checks
111
+ return false;
112
+ };
101
113
  class Queue {
102
114
  constructor() {
103
115
  Object.defineProperty(this, "items", {
@@ -161,6 +173,12 @@ class Client {
161
173
  writable: true,
162
174
  value: void 0
163
175
  });
176
+ Object.defineProperty(this, "batchIngestCaller", {
177
+ enumerable: true,
178
+ configurable: true,
179
+ writable: true,
180
+ value: void 0
181
+ });
164
182
  Object.defineProperty(this, "timeout_ms", {
165
183
  enumerable: true,
166
184
  configurable: true,
@@ -244,9 +262,12 @@ class Client {
244
262
  this.apiUrl = trimQuotes(config.apiUrl ?? defaultConfig.apiUrl) ?? "";
245
263
  this.apiKey = trimQuotes(config.apiKey ?? defaultConfig.apiKey);
246
264
  this.webUrl = trimQuotes(config.webUrl ?? defaultConfig.webUrl);
247
- this.validateApiKeyIfHosted();
248
265
  this.timeout_ms = config.timeout_ms ?? 12000;
249
266
  this.caller = new async_caller_js_1.AsyncCaller(config.callerOptions ?? {});
267
+ this.batchIngestCaller = new async_caller_js_1.AsyncCaller({
268
+ ...(config.callerOptions ?? {}),
269
+ onFailedResponseHook: handle429,
270
+ });
250
271
  this.hideInputs = config.hideInputs ?? defaultConfig.hideInputs;
251
272
  this.hideOutputs = config.hideOutputs ?? defaultConfig.hideOutputs;
252
273
  this.autoBatchTracing = config.autoBatchTracing ?? this.autoBatchTracing;
@@ -267,12 +288,6 @@ class Client {
267
288
  hideOutputs: hideOutputs,
268
289
  };
269
290
  }
270
- validateApiKeyIfHosted() {
271
- const isLocal = isLocalhost(this.apiUrl);
272
- if (!isLocal && !this.apiKey) {
273
- throw new Error("API key must be provided when using hosted LangSmith API");
274
- }
275
- }
276
291
  getHostUrl() {
277
292
  if (this.webUrl) {
278
293
  return this.webUrl;
@@ -455,7 +470,9 @@ class Client {
455
470
  if (this.autoBatchQueue.size > 0) {
456
471
  this.autoBatchTimeout = setTimeout(() => {
457
472
  this.autoBatchTimeout = undefined;
458
- void this.drainAutoBatchQueue();
473
+ // This error would happen in the background and is uncatchable
474
+ // from the outside. So just log instead.
475
+ void this.drainAutoBatchQueue().catch(console.error);
459
476
  }, oldTimeout
460
477
  ? this.autoBatchAggregationDelayMs
461
478
  : this.autoBatchInitialDelayMs);
@@ -494,7 +511,7 @@ class Client {
494
511
  void this.processRunOperation({
495
512
  action: "create",
496
513
  item: runCreate,
497
- });
514
+ }).catch(console.error);
498
515
  return;
499
516
  }
500
517
  const mergedRunCreateParams = await mergeRuntimeEnvIntoRunCreates([
@@ -569,7 +586,7 @@ class Client {
569
586
  "Content-Type": "application/json",
570
587
  Accept: "application/json",
571
588
  };
572
- const response = await this.caller.call(fetch, `${this.apiUrl}/runs/batch`, {
589
+ const response = await this.batchIngestCaller.call(fetch, `${this.apiUrl}/runs/batch`, {
573
590
  method: "POST",
574
591
  headers,
575
592
  body: JSON.stringify(body),
@@ -600,7 +617,7 @@ class Client {
600
617
  return;
601
618
  }
602
619
  else {
603
- void this.processRunOperation({ action: "update", item: data });
620
+ void this.processRunOperation({ action: "update", item: data }).catch(console.error);
604
621
  }
605
622
  return;
606
623
  }
package/dist/client.d.ts CHANGED
@@ -77,6 +77,7 @@ export declare class Client {
77
77
  private apiUrl;
78
78
  private webUrl?;
79
79
  private caller;
80
+ private batchIngestCaller;
80
81
  private timeout_ms;
81
82
  private _tenantId;
82
83
  private hideInputs?;
@@ -98,7 +99,6 @@ export declare class Client {
98
99
  hideInputs?: boolean;
99
100
  hideOutputs?: boolean;
100
101
  };
101
- private validateApiKeyIfHosted;
102
102
  private getHostUrl;
103
103
  private get headers();
104
104
  private processInputs;
package/dist/client.js CHANGED
@@ -72,6 +72,18 @@ function assertUuid(str) {
72
72
  throw new Error(`Invalid UUID: ${str}`);
73
73
  }
74
74
  }
75
+ const handle429 = async (response) => {
76
+ if (response?.status === 429) {
77
+ const retryAfter = parseInt(response.headers.get("retry-after") ?? "30", 10) * 1000;
78
+ if (retryAfter > 0) {
79
+ await new Promise((resolve) => setTimeout(resolve, retryAfter));
80
+ // Return directly after calling this check
81
+ return true;
82
+ }
83
+ }
84
+ // Fall back to existing status checks
85
+ return false;
86
+ };
75
87
  export class Queue {
76
88
  constructor() {
77
89
  Object.defineProperty(this, "items", {
@@ -134,6 +146,12 @@ export class Client {
134
146
  writable: true,
135
147
  value: void 0
136
148
  });
149
+ Object.defineProperty(this, "batchIngestCaller", {
150
+ enumerable: true,
151
+ configurable: true,
152
+ writable: true,
153
+ value: void 0
154
+ });
137
155
  Object.defineProperty(this, "timeout_ms", {
138
156
  enumerable: true,
139
157
  configurable: true,
@@ -217,9 +235,12 @@ export class Client {
217
235
  this.apiUrl = trimQuotes(config.apiUrl ?? defaultConfig.apiUrl) ?? "";
218
236
  this.apiKey = trimQuotes(config.apiKey ?? defaultConfig.apiKey);
219
237
  this.webUrl = trimQuotes(config.webUrl ?? defaultConfig.webUrl);
220
- this.validateApiKeyIfHosted();
221
238
  this.timeout_ms = config.timeout_ms ?? 12000;
222
239
  this.caller = new AsyncCaller(config.callerOptions ?? {});
240
+ this.batchIngestCaller = new AsyncCaller({
241
+ ...(config.callerOptions ?? {}),
242
+ onFailedResponseHook: handle429,
243
+ });
223
244
  this.hideInputs = config.hideInputs ?? defaultConfig.hideInputs;
224
245
  this.hideOutputs = config.hideOutputs ?? defaultConfig.hideOutputs;
225
246
  this.autoBatchTracing = config.autoBatchTracing ?? this.autoBatchTracing;
@@ -240,12 +261,6 @@ export class Client {
240
261
  hideOutputs: hideOutputs,
241
262
  };
242
263
  }
243
- validateApiKeyIfHosted() {
244
- const isLocal = isLocalhost(this.apiUrl);
245
- if (!isLocal && !this.apiKey) {
246
- throw new Error("API key must be provided when using hosted LangSmith API");
247
- }
248
- }
249
264
  getHostUrl() {
250
265
  if (this.webUrl) {
251
266
  return this.webUrl;
@@ -428,7 +443,9 @@ export class Client {
428
443
  if (this.autoBatchQueue.size > 0) {
429
444
  this.autoBatchTimeout = setTimeout(() => {
430
445
  this.autoBatchTimeout = undefined;
431
- void this.drainAutoBatchQueue();
446
+ // This error would happen in the background and is uncatchable
447
+ // from the outside. So just log instead.
448
+ void this.drainAutoBatchQueue().catch(console.error);
432
449
  }, oldTimeout
433
450
  ? this.autoBatchAggregationDelayMs
434
451
  : this.autoBatchInitialDelayMs);
@@ -467,7 +484,7 @@ export class Client {
467
484
  void this.processRunOperation({
468
485
  action: "create",
469
486
  item: runCreate,
470
- });
487
+ }).catch(console.error);
471
488
  return;
472
489
  }
473
490
  const mergedRunCreateParams = await mergeRuntimeEnvIntoRunCreates([
@@ -542,7 +559,7 @@ export class Client {
542
559
  "Content-Type": "application/json",
543
560
  Accept: "application/json",
544
561
  };
545
- const response = await this.caller.call(fetch, `${this.apiUrl}/runs/batch`, {
562
+ const response = await this.batchIngestCaller.call(fetch, `${this.apiUrl}/runs/batch`, {
546
563
  method: "POST",
547
564
  headers,
548
565
  body: JSON.stringify(body),
@@ -573,7 +590,7 @@ export class Client {
573
590
  return;
574
591
  }
575
592
  else {
576
- void this.processRunOperation({ action: "update", item: data });
593
+ void this.processRunOperation({ action: "update", item: data }).catch(console.error);
577
594
  }
578
595
  return;
579
596
  }
package/dist/index.cjs CHANGED
@@ -6,4 +6,4 @@ Object.defineProperty(exports, "Client", { enumerable: true, get: function () {
6
6
  var run_trees_js_1 = require("./run_trees.cjs");
7
7
  Object.defineProperty(exports, "RunTree", { enumerable: true, get: function () { return run_trees_js_1.RunTree; } });
8
8
  // Update using yarn bump-version
9
- exports.__version__ = "0.1.3";
9
+ exports.__version__ = "0.1.6";
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  export { Client } from "./client.js";
2
2
  export type { Dataset, Example, TracerSession, Run, Feedback, } from "./schemas.js";
3
3
  export { RunTree, type RunTreeConfig } from "./run_trees.js";
4
- export declare const __version__ = "0.1.3";
4
+ export declare const __version__ = "0.1.6";
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
1
  export { Client } from "./client.js";
2
2
  export { RunTree } from "./run_trees.js";
3
3
  // Update using yarn bump-version
4
- export const __version__ = "0.1.3";
4
+ export const __version__ = "0.1.6";
@@ -23,7 +23,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
23
23
  return result;
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
- exports.isRunTree = exports.RunTree = exports.convertToDottedOrderFormat = void 0;
26
+ exports.isRunnableConfigLike = exports.isRunTree = exports.RunTree = exports.convertToDottedOrderFormat = void 0;
27
27
  const uuid = __importStar(require("uuid"));
28
28
  const env_js_1 = require("./utils/env.cjs");
29
29
  const client_js_1 = require("./client.cjs");
@@ -98,6 +98,12 @@ class RunTree {
98
98
  writable: true,
99
99
  value: void 0
100
100
  });
101
+ Object.defineProperty(this, "tags", {
102
+ enumerable: true,
103
+ configurable: true,
104
+ writable: true,
105
+ value: void 0
106
+ });
101
107
  Object.defineProperty(this, "error", {
102
108
  enumerable: true,
103
109
  configurable: true,
@@ -153,10 +159,11 @@ class RunTree {
153
159
  value: void 0
154
160
  });
155
161
  const defaultConfig = RunTree.getDefaultConfig();
156
- Object.assign(this, { ...defaultConfig, ...config });
162
+ const client = config.client ?? new client_js_1.Client();
163
+ Object.assign(this, { ...defaultConfig, ...config, client });
157
164
  if (!this.trace_id) {
158
165
  if (this.parent_run) {
159
- this.trace_id = this.parent_run.trace_id;
166
+ this.trace_id = this.parent_run.trace_id ?? this.id;
160
167
  }
161
168
  else {
162
169
  this.trace_id = this.id;
@@ -173,6 +180,35 @@ class RunTree {
173
180
  }
174
181
  }
175
182
  }
183
+ static fromRunnableConfig(config, props) {
184
+ // We only handle the callback manager case for now
185
+ const callbackManager = config?.callbacks;
186
+ let parentRun;
187
+ let projectName;
188
+ if (callbackManager) {
189
+ const parentRunId = callbackManager?.getParentRunId?.() ?? "";
190
+ const langChainTracer = callbackManager?.handlers?.find((handler) => handler?.name == "langchain_tracer");
191
+ parentRun = langChainTracer?.getRun?.(parentRunId);
192
+ projectName = langChainTracer?.projectName;
193
+ }
194
+ const deduppedTags = [
195
+ ...new Set((parentRun?.tags ?? []).concat(config?.tags ?? [])),
196
+ ];
197
+ const dedupedMetadata = {
198
+ ...parentRun?.extra?.metadata,
199
+ ...config?.metadata,
200
+ };
201
+ const rt = new RunTree({
202
+ name: props?.name ?? "<lambda>",
203
+ parent_run: parentRun,
204
+ tags: deduppedTags,
205
+ extra: {
206
+ metadata: dedupedMetadata,
207
+ },
208
+ project_name: projectName,
209
+ });
210
+ return rt;
211
+ }
176
212
  static getDefaultConfig() {
177
213
  return {
178
214
  id: uuid.v4(),
@@ -188,7 +224,6 @@ class RunTree {
188
224
  serialized: {},
189
225
  inputs: {},
190
226
  extra: {},
191
- client: new client_js_1.Client({}),
192
227
  };
193
228
  }
194
229
  async createChild(config) {
@@ -244,6 +279,7 @@ class RunTree {
244
279
  parent_run_id: parent_run_id,
245
280
  trace_id: run.trace_id,
246
281
  dotted_order: run.dotted_order,
282
+ tags: run.tags,
247
283
  };
248
284
  return persistedRun;
249
285
  }
@@ -268,6 +304,7 @@ class RunTree {
268
304
  events: this.events,
269
305
  dotted_order: this.dotted_order,
270
306
  trace_id: this.trace_id,
307
+ tags: this.tags,
271
308
  };
272
309
  await this.client.updateRun(this.id, runUpdate);
273
310
  }
@@ -279,3 +316,22 @@ function isRunTree(x) {
279
316
  typeof x.postRun === "function");
280
317
  }
281
318
  exports.isRunTree = isRunTree;
319
+ function containsLangChainTracerLike(x) {
320
+ return (Array.isArray(x) &&
321
+ x.some((callback) => {
322
+ return (typeof callback.name === "string" &&
323
+ callback.name === "langchain_tracer");
324
+ }));
325
+ }
326
+ function isRunnableConfigLike(x) {
327
+ // Check that it's an object with a callbacks arg
328
+ // that has either a CallbackManagerLike object with a langchain tracer within it
329
+ // or an array with a LangChainTracerLike object within it
330
+ return (x !== undefined &&
331
+ typeof x.callbacks === "object" &&
332
+ // Callback manager with a langchain tracer
333
+ (containsLangChainTracerLike(x.callbacks?.handlers) ||
334
+ // Or it's an array with a LangChainTracerLike object within it
335
+ containsLangChainTracerLike(x.callbacks)));
336
+ }
337
+ exports.isRunnableConfigLike = isRunnableConfigLike;
@@ -7,10 +7,12 @@ export interface RunTreeConfig {
7
7
  id?: string;
8
8
  project_name?: string;
9
9
  parent_run?: RunTree;
10
+ parent_run_id?: string;
10
11
  child_runs?: RunTree[];
11
12
  start_time?: number;
12
13
  end_time?: number;
13
14
  extra?: KVMap;
15
+ tags?: string[];
14
16
  error?: string;
15
17
  serialized?: object;
16
18
  inputs?: KVMap;
@@ -18,16 +20,34 @@ export interface RunTreeConfig {
18
20
  reference_example_id?: string;
19
21
  client?: Client;
20
22
  }
23
+ export interface RunnableConfigLike {
24
+ /**
25
+ * Tags for this call and any sub-calls (eg. a Chain calling an LLM).
26
+ * You can use these to filter calls.
27
+ */
28
+ tags?: string[];
29
+ /**
30
+ * Metadata for this call and any sub-calls (eg. a Chain calling an LLM).
31
+ * Keys should be strings, values should be JSON-serializable.
32
+ */
33
+ metadata?: Record<string, unknown>;
34
+ /**
35
+ * Callbacks for this call and any sub-calls (eg. a Chain calling an LLM).
36
+ * Tags are passed to all callbacks, metadata is passed to handle*Start callbacks.
37
+ */
38
+ callbacks?: any;
39
+ }
21
40
  export declare class RunTree implements BaseRun {
22
41
  id: string;
23
42
  name: RunTreeConfig["name"];
24
43
  run_type: string;
25
44
  project_name: string;
26
- parent_run?: RunTree;
45
+ parent_run?: BaseRun;
27
46
  child_runs: RunTree[];
28
47
  start_time: number;
29
48
  end_time?: number;
30
49
  extra: KVMap;
50
+ tags?: string[];
31
51
  error?: string;
32
52
  serialized: object;
33
53
  inputs: KVMap;
@@ -38,6 +58,11 @@ export declare class RunTree implements BaseRun {
38
58
  trace_id: string;
39
59
  dotted_order: string;
40
60
  constructor(config: RunTreeConfig);
61
+ static fromRunnableConfig(config: RunnableConfigLike, props: {
62
+ name: string;
63
+ tags?: string[];
64
+ metadata?: KVMap;
65
+ }): RunTree;
41
66
  private static getDefaultConfig;
42
67
  createChild(config: RunTreeConfig): Promise<RunTree>;
43
68
  end(outputs?: KVMap, error?: string, endTime?: number): Promise<void>;
@@ -46,3 +71,4 @@ export declare class RunTree implements BaseRun {
46
71
  patchRun(): Promise<void>;
47
72
  }
48
73
  export declare function isRunTree(x?: unknown): x is RunTree;
74
+ export declare function isRunnableConfigLike(x?: unknown): x is RunnableConfigLike;
package/dist/run_trees.js CHANGED
@@ -71,6 +71,12 @@ export class RunTree {
71
71
  writable: true,
72
72
  value: void 0
73
73
  });
74
+ Object.defineProperty(this, "tags", {
75
+ enumerable: true,
76
+ configurable: true,
77
+ writable: true,
78
+ value: void 0
79
+ });
74
80
  Object.defineProperty(this, "error", {
75
81
  enumerable: true,
76
82
  configurable: true,
@@ -126,10 +132,11 @@ export class RunTree {
126
132
  value: void 0
127
133
  });
128
134
  const defaultConfig = RunTree.getDefaultConfig();
129
- Object.assign(this, { ...defaultConfig, ...config });
135
+ const client = config.client ?? new Client();
136
+ Object.assign(this, { ...defaultConfig, ...config, client });
130
137
  if (!this.trace_id) {
131
138
  if (this.parent_run) {
132
- this.trace_id = this.parent_run.trace_id;
139
+ this.trace_id = this.parent_run.trace_id ?? this.id;
133
140
  }
134
141
  else {
135
142
  this.trace_id = this.id;
@@ -146,6 +153,35 @@ export class RunTree {
146
153
  }
147
154
  }
148
155
  }
156
+ static fromRunnableConfig(config, props) {
157
+ // We only handle the callback manager case for now
158
+ const callbackManager = config?.callbacks;
159
+ let parentRun;
160
+ let projectName;
161
+ if (callbackManager) {
162
+ const parentRunId = callbackManager?.getParentRunId?.() ?? "";
163
+ const langChainTracer = callbackManager?.handlers?.find((handler) => handler?.name == "langchain_tracer");
164
+ parentRun = langChainTracer?.getRun?.(parentRunId);
165
+ projectName = langChainTracer?.projectName;
166
+ }
167
+ const deduppedTags = [
168
+ ...new Set((parentRun?.tags ?? []).concat(config?.tags ?? [])),
169
+ ];
170
+ const dedupedMetadata = {
171
+ ...parentRun?.extra?.metadata,
172
+ ...config?.metadata,
173
+ };
174
+ const rt = new RunTree({
175
+ name: props?.name ?? "<lambda>",
176
+ parent_run: parentRun,
177
+ tags: deduppedTags,
178
+ extra: {
179
+ metadata: dedupedMetadata,
180
+ },
181
+ project_name: projectName,
182
+ });
183
+ return rt;
184
+ }
149
185
  static getDefaultConfig() {
150
186
  return {
151
187
  id: uuid.v4(),
@@ -161,7 +197,6 @@ export class RunTree {
161
197
  serialized: {},
162
198
  inputs: {},
163
199
  extra: {},
164
- client: new Client({}),
165
200
  };
166
201
  }
167
202
  async createChild(config) {
@@ -217,6 +252,7 @@ export class RunTree {
217
252
  parent_run_id: parent_run_id,
218
253
  trace_id: run.trace_id,
219
254
  dotted_order: run.dotted_order,
255
+ tags: run.tags,
220
256
  };
221
257
  return persistedRun;
222
258
  }
@@ -241,6 +277,7 @@ export class RunTree {
241
277
  events: this.events,
242
278
  dotted_order: this.dotted_order,
243
279
  trace_id: this.trace_id,
280
+ tags: this.tags,
244
281
  };
245
282
  await this.client.updateRun(this.id, runUpdate);
246
283
  }
@@ -250,3 +287,21 @@ export function isRunTree(x) {
250
287
  typeof x.createChild === "function" &&
251
288
  typeof x.postRun === "function");
252
289
  }
290
+ function containsLangChainTracerLike(x) {
291
+ return (Array.isArray(x) &&
292
+ x.some((callback) => {
293
+ return (typeof callback.name === "string" &&
294
+ callback.name === "langchain_tracer");
295
+ }));
296
+ }
297
+ export function isRunnableConfigLike(x) {
298
+ // Check that it's an object with a callbacks arg
299
+ // that has either a CallbackManagerLike object with a langchain tracer within it
300
+ // or an array with a LangChainTracerLike object within it
301
+ return (x !== undefined &&
302
+ typeof x.callbacks === "object" &&
303
+ // Callback manager with a langchain tracer
304
+ (containsLangChainTracerLike(x.callbacks?.handlers) ||
305
+ // Or it's an array with a LangChainTracerLike object within it
306
+ containsLangChainTracerLike(x.callbacks)));
307
+ }
package/dist/schemas.d.ts CHANGED
@@ -118,6 +118,7 @@ export interface RunUpdate {
118
118
  id?: string;
119
119
  end_time?: number;
120
120
  extra?: KVMap;
121
+ tags?: string[];
121
122
  error?: string;
122
123
  inputs?: KVMap;
123
124
  outputs?: KVMap;
@@ -36,6 +36,10 @@ function traceable(wrappedFunc, config) {
36
36
  currentRunTree = args[0];
37
37
  rawInputs = args.slice(1);
38
38
  }
39
+ else if ((0, run_trees_js_1.isRunnableConfigLike)(args[0])) {
40
+ currentRunTree = run_trees_js_1.RunTree.fromRunnableConfig(args[0], ensuredConfig);
41
+ rawInputs = args.slice(1);
42
+ }
39
43
  else if (previousRunTree !== undefined) {
40
44
  currentRunTree = await previousRunTree.createChild(ensuredConfig);
41
45
  rawInputs = args;
@@ -1,4 +1,4 @@
1
- import { RunTree, RunTreeConfig } from "./run_trees.js";
1
+ import { RunTree, RunTreeConfig, RunnableConfigLike } from "./run_trees.js";
2
2
  export type RunTreeLike = RunTree;
3
3
  type WrapArgReturnPair<Pair> = Pair extends [
4
4
  infer Args extends any[],
@@ -6,6 +6,7 @@ type WrapArgReturnPair<Pair> = Pair extends [
6
6
  ] ? {
7
7
  (...args: Args): Promise<Return>;
8
8
  (...args: [runTree: RunTreeLike, ...rest: Args]): Promise<Return>;
9
+ (...args: [config: RunnableConfigLike, ...rest: Args]): Promise<Return>;
9
10
  } : never;
10
11
  type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
11
12
  export type TraceableFunction<Func extends (...args: any[]) => any> = Func extends {
package/dist/traceable.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { AsyncLocalStorage } from "async_hooks";
2
- import { RunTree, isRunTree } from "./run_trees.js";
2
+ import { RunTree, isRunTree, isRunnableConfigLike, } from "./run_trees.js";
3
3
  const asyncLocalStorage = new AsyncLocalStorage();
4
4
  const isAsyncIterable = (x) => x != null &&
5
5
  typeof x === "object" &&
@@ -33,6 +33,10 @@ export function traceable(wrappedFunc, config) {
33
33
  currentRunTree = args[0];
34
34
  rawInputs = args.slice(1);
35
35
  }
36
+ else if (isRunnableConfigLike(args[0])) {
37
+ currentRunTree = RunTree.fromRunnableConfig(args[0], ensuredConfig);
38
+ rawInputs = args.slice(1);
39
+ }
36
40
  else if (previousRunTree !== undefined) {
37
41
  currentRunTree = await previousRunTree.createChild(ensuredConfig);
38
42
  rawInputs = args;
@@ -52,13 +52,21 @@ class AsyncCaller {
52
52
  writable: true,
53
53
  value: void 0
54
54
  });
55
+ Object.defineProperty(this, "onFailedResponseHook", {
56
+ enumerable: true,
57
+ configurable: true,
58
+ writable: true,
59
+ value: void 0
60
+ });
55
61
  this.maxConcurrency = params.maxConcurrency ?? Infinity;
56
62
  this.maxRetries = params.maxRetries ?? 6;
57
63
  const PQueue = "default" in p_queue_1.default ? p_queue_1.default.default : p_queue_1.default;
58
64
  this.queue = new PQueue({ concurrency: this.maxConcurrency });
65
+ this.onFailedResponseHook = params?.onFailedResponseHook;
59
66
  }
60
67
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
61
68
  call(callable, ...args) {
69
+ const onFailedResponseHook = this.onFailedResponseHook;
62
70
  return this.queue.add(() => (0, p_retry_1.default)(() => callable(...args).catch((error) => {
63
71
  // eslint-disable-next-line no-instanceof/no-instanceof
64
72
  if (error instanceof Error) {
@@ -68,7 +76,7 @@ class AsyncCaller {
68
76
  throw new Error(error);
69
77
  }
70
78
  }), {
71
- onFailedAttempt(error) {
79
+ async onFailedAttempt(error) {
72
80
  if (error.message.startsWith("Cancel") ||
73
81
  error.message.startsWith("TimeoutError") ||
74
82
  error.message.startsWith("AbortError")) {
@@ -79,7 +87,8 @@ class AsyncCaller {
79
87
  throw error;
80
88
  }
81
89
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
82
- const status = error?.response?.status;
90
+ const response = error?.response;
91
+ const status = response?.status;
83
92
  if (status) {
84
93
  if (STATUS_NO_RETRY.includes(+status)) {
85
94
  throw error;
@@ -87,6 +96,9 @@ class AsyncCaller {
87
96
  else if (STATUS_IGNORE.includes(+status)) {
88
97
  return;
89
98
  }
99
+ if (onFailedResponseHook) {
100
+ await onFailedResponseHook(response);
101
+ }
90
102
  }
91
103
  },
92
104
  // If needed we can change some of the defaults here,
@@ -1,3 +1,4 @@
1
+ type ResponseCallback = (response?: Response) => Promise<boolean>;
1
2
  export interface AsyncCallerParams {
2
3
  /**
3
4
  * The maximum number of concurrent calls that can be made.
@@ -9,6 +10,7 @@ export interface AsyncCallerParams {
9
10
  * with an exponential backoff between each attempt. Defaults to 6.
10
11
  */
11
12
  maxRetries?: number;
13
+ onFailedResponseHook?: ResponseCallback;
12
14
  }
13
15
  export interface AsyncCallerCallOptions {
14
16
  signal?: AbortSignal;
@@ -30,8 +32,10 @@ export declare class AsyncCaller {
30
32
  protected maxConcurrency: AsyncCallerParams["maxConcurrency"];
31
33
  protected maxRetries: AsyncCallerParams["maxRetries"];
32
34
  private queue;
35
+ private onFailedResponseHook?;
33
36
  constructor(params: AsyncCallerParams);
34
37
  call<A extends any[], T extends (...args: A) => Promise<any>>(callable: T, ...args: Parameters<T>): Promise<Awaited<ReturnType<T>>>;
35
38
  callWithOptions<A extends any[], T extends (...args: A) => Promise<any>>(options: AsyncCallerCallOptions, callable: T, ...args: Parameters<T>): Promise<Awaited<ReturnType<T>>>;
36
39
  fetch(...args: Parameters<typeof fetch>): ReturnType<typeof fetch>;
37
40
  }
41
+ export {};
@@ -46,13 +46,21 @@ export class AsyncCaller {
46
46
  writable: true,
47
47
  value: void 0
48
48
  });
49
+ Object.defineProperty(this, "onFailedResponseHook", {
50
+ enumerable: true,
51
+ configurable: true,
52
+ writable: true,
53
+ value: void 0
54
+ });
49
55
  this.maxConcurrency = params.maxConcurrency ?? Infinity;
50
56
  this.maxRetries = params.maxRetries ?? 6;
51
57
  const PQueue = "default" in PQueueMod ? PQueueMod.default : PQueueMod;
52
58
  this.queue = new PQueue({ concurrency: this.maxConcurrency });
59
+ this.onFailedResponseHook = params?.onFailedResponseHook;
53
60
  }
54
61
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
62
  call(callable, ...args) {
63
+ const onFailedResponseHook = this.onFailedResponseHook;
56
64
  return this.queue.add(() => pRetry(() => callable(...args).catch((error) => {
57
65
  // eslint-disable-next-line no-instanceof/no-instanceof
58
66
  if (error instanceof Error) {
@@ -62,7 +70,7 @@ export class AsyncCaller {
62
70
  throw new Error(error);
63
71
  }
64
72
  }), {
65
- onFailedAttempt(error) {
73
+ async onFailedAttempt(error) {
66
74
  if (error.message.startsWith("Cancel") ||
67
75
  error.message.startsWith("TimeoutError") ||
68
76
  error.message.startsWith("AbortError")) {
@@ -73,7 +81,8 @@ export class AsyncCaller {
73
81
  throw error;
74
82
  }
75
83
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
76
- const status = error?.response?.status;
84
+ const response = error?.response;
85
+ const status = response?.status;
77
86
  if (status) {
78
87
  if (STATUS_NO_RETRY.includes(+status)) {
79
88
  throw error;
@@ -81,6 +90,9 @@ export class AsyncCaller {
81
90
  else if (STATUS_IGNORE.includes(+status)) {
82
91
  return;
83
92
  }
93
+ if (onFailedResponseHook) {
94
+ await onFailedResponseHook(response);
95
+ }
84
96
  }
85
97
  },
86
98
  // If needed we can change some of the defaults here,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langsmith",
3
- "version": "0.1.3",
3
+ "version": "0.1.6",
4
4
  "description": "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform.",
5
5
  "packageManager": "yarn@1.22.19",
6
6
  "files": [
@@ -69,7 +69,8 @@
69
69
  "devDependencies": {
70
70
  "@babel/preset-env": "^7.22.4",
71
71
  "@jest/globals": "^29.5.0",
72
- "@langchain/core": "^0.1.28",
72
+ "@langchain/core": "^0.1.32",
73
+ "@langchain/langgraph": "^0.0.8",
73
74
  "@tsconfig/recommended": "^1.0.2",
74
75
  "@types/jest": "^29.5.1",
75
76
  "@typescript-eslint/eslint-plugin": "^5.59.8",