langsmith 0.0.6 → 0.0.8

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
@@ -14,7 +14,7 @@ LangSmith helps you and your team develop and evaluate language models and intel
14
14
 
15
15
  A typical workflow looks like:
16
16
 
17
- 1. Set up an account with LangSmith or host your [local server](https://docs.smith.langchain.com/docs/additional-resources/local_installation).
17
+ 1. Set up an account with LangSmith.
18
18
  2. Log traces.
19
19
  3. Debug, Create Datasets, and Evaluate Runs.
20
20
 
@@ -24,7 +24,7 @@ We'll walk through these steps in more detail below.
24
24
 
25
25
  Sign up for [LangSmith](https://smith.langchain.com/) using your GitHub, Discord accounts, or an email address and password. If you sign up with an email, make sure to verify your email address before logging in.
26
26
 
27
- Then, create a unique API key on the [Settings Page](https://smith.langchain.com/settings), which is found in the menu at the top right corner of the page.
27
+ Then, create a unique API key on the [Settings Page](https://smith.langchain.com/settings).
28
28
 
29
29
  Note: Save the API Key in a secure location. It will not be shown again.
30
30
 
@@ -211,7 +211,9 @@ for (const run of runs) {
211
211
 
212
212
  # Evaluating Runs
213
213
 
214
- You can run evaluations directly using the LangSmith client.
214
+ Check out the [LangSmith Testing & Evaluation dos](https://docs.smith.langchain.com/docs/evaluation/) for up-to-date workflows.
215
+
216
+ For generating automated feedback on individual runs, you can run evaluations directly using the LangSmith client.
215
217
 
216
218
  ```ts
217
219
  import { StringEvaluator } from "langsmith/evaluation";
package/dist/cli/main.cjs CHANGED
@@ -182,6 +182,8 @@ class SmithCommand {
182
182
  return new SmithCommand({ dockerComposeCommand });
183
183
  }
184
184
  async start(args) {
185
+ console.info("BY USING THIS SOFTWARE YOU AGREE TO THE TERMS OF SERVICE AT:");
186
+ console.info("https://smith.langchain.com/terms-of-service.pdf");
185
187
  if (args.dev) {
186
188
  (0, env_js_1.setEnvironmentVariable)("_LANGSMITH_IMAGE_PREFIX", "rc-");
187
189
  }
package/dist/cli/main.js CHANGED
@@ -156,6 +156,8 @@ class SmithCommand {
156
156
  return new SmithCommand({ dockerComposeCommand });
157
157
  }
158
158
  async start(args) {
159
+ console.info("BY USING THIS SOFTWARE YOU AGREE TO THE TERMS OF SERVICE AT:");
160
+ console.info("https://smith.langchain.com/terms-of-service.pdf");
159
161
  if (args.dev) {
160
162
  setEnvironmentVariable("_LANGSMITH_IMAGE_PREFIX", "rc-");
161
163
  }
package/dist/cli/main.ts CHANGED
@@ -169,6 +169,10 @@ class SmithCommand {
169
169
  }
170
170
 
171
171
  async start(args: any) {
172
+ console.info(
173
+ "BY USING THIS SOFTWARE YOU AGREE TO THE TERMS OF SERVICE AT:"
174
+ );
175
+ console.info("https://smith.langchain.com/terms-of-service.pdf");
172
176
  if (args.dev) {
173
177
  setEnvironmentVariable("_LANGSMITH_IMAGE_PREFIX", "rc-");
174
178
  }
package/dist/client.cjs CHANGED
@@ -41,6 +41,13 @@ const raiseForStatus = async (response, operation) => {
41
41
  throw new Error(`Failed to ${operation}: ${response.status} ${response.statusText} ${body}`);
42
42
  }
43
43
  };
44
+ async function toArray(iterable) {
45
+ const result = [];
46
+ for await (const item of iterable) {
47
+ result.push(item);
48
+ }
49
+ return result;
50
+ }
44
51
  class Client {
45
52
  constructor(config = {}) {
46
53
  Object.defineProperty(this, "apiKey", {
@@ -75,9 +82,12 @@ class Client {
75
82
  this.caller = new async_caller_js_1.AsyncCaller(config.callerOptions ?? {});
76
83
  }
77
84
  static getDefaultClientConfig() {
85
+ const apiKey = (0, env_js_1.getEnvironmentVariable)("LANGCHAIN_API_KEY");
86
+ const apiUrl = (0, env_js_1.getEnvironmentVariable)("LANGCHAIN_ENDPOINT") ??
87
+ (apiKey ? "https://api.smith.langchain.com" : "http://localhost:1984");
78
88
  return {
79
- apiUrl: (0, env_js_1.getEnvironmentVariable)("LANGCHAIN_ENDPOINT") ?? "http://localhost:1984",
80
- apiKey: (0, env_js_1.getEnvironmentVariable)("LANGCHAIN_API_KEY"),
89
+ apiUrl: apiUrl,
90
+ apiKey: apiKey,
81
91
  };
82
92
  }
83
93
  validateApiKeyIfHosted() {
@@ -106,6 +116,32 @@ class Client {
106
116
  }
107
117
  return response.json();
108
118
  }
119
+ async *_getPaginated(path, queryParams = new URLSearchParams()) {
120
+ let offset = Number(queryParams.get("offset")) || 0;
121
+ const limit = Number(queryParams.get("limit")) || 100;
122
+ while (true) {
123
+ queryParams.set("offset", String(offset));
124
+ queryParams.set("limit", String(limit));
125
+ const url = `${this.apiUrl}${path}?${queryParams}`;
126
+ const response = await this.caller.call(fetch, url, {
127
+ method: "GET",
128
+ headers: this.headers,
129
+ signal: AbortSignal.timeout(this.timeout_ms),
130
+ });
131
+ if (!response.ok) {
132
+ throw new Error(`Failed to fetch ${path}: ${response.status} ${response.statusText}`);
133
+ }
134
+ const items = await response.json();
135
+ if (items.length === 0) {
136
+ break;
137
+ }
138
+ yield items;
139
+ if (items.length < limit) {
140
+ break;
141
+ }
142
+ offset += items.length;
143
+ }
144
+ }
109
145
  async createRun(run) {
110
146
  const headers = { ...this.headers, "Content-Type": "application/json" };
111
147
  const extra = run.extra ?? {};
@@ -149,7 +185,7 @@ class Client {
149
185
  return run;
150
186
  }
151
187
  async _loadChildRuns(run) {
152
- const childRuns = await this.listRuns({ id: run.child_run_ids });
188
+ const childRuns = await toArray(this.listRuns({ id: run.child_run_ids }));
153
189
  const treemap = {};
154
190
  const runs = {};
155
191
  childRuns.sort((a, b) => a.execution_order - b.execution_order);
@@ -172,7 +208,7 @@ class Client {
172
208
  }
173
209
  return run;
174
210
  }
175
- async listRuns({ projectId, projectName, parentRunId, referenceExampleId, datasetId, startTime, endTime, executionOrder, runType, error, id, limit, offset, query, filter, orderBy, }) {
211
+ async *listRuns({ projectId, projectName, parentRunId, referenceExampleId, datasetId, startTime, endTime, executionOrder, runType, error, id, limit, offset, query, filter, orderBy, }) {
176
212
  const queryParams = new URLSearchParams();
177
213
  let projectId_ = projectId;
178
214
  if (projectName) {
@@ -228,7 +264,9 @@ class Client {
228
264
  if (orderBy !== undefined) {
229
265
  orderBy.map((order) => queryParams.append("order_by", order));
230
266
  }
231
- return this._get("/runs", queryParams);
267
+ for await (const runs of this._getPaginated("/runs", queryParams)) {
268
+ yield* runs;
269
+ }
232
270
  }
233
271
  async deleteRun(runId) {
234
272
  const response = await this.caller.call(fetch, `${this.apiUrl}/runs/${runId}`, {
@@ -287,8 +325,10 @@ class Client {
287
325
  }
288
326
  return result;
289
327
  }
290
- async listProjects() {
291
- return this._get("/sessions");
328
+ async *listProjects() {
329
+ for await (const projects of this._getPaginated("/sessions")) {
330
+ yield* projects;
331
+ }
292
332
  }
293
333
  async deleteProject({ projectId, projectName, }) {
294
334
  let projectId_;
@@ -399,17 +439,15 @@ class Client {
399
439
  }
400
440
  return result;
401
441
  }
402
- async listDatasets({ limit = 100, offset = 0, } = {}) {
442
+ async *listDatasets({ limit = 100, offset = 0, } = {}) {
403
443
  const path = "/datasets";
404
444
  const params = new URLSearchParams({
405
445
  limit: limit.toString(),
406
446
  offset: offset.toString(),
407
447
  });
408
- const response = await this._get(path, params);
409
- if (!Array.isArray(response)) {
410
- throw new Error(`Expected ${path} to return an array, but got ${response}`);
448
+ for await (const datasets of this._getPaginated(path, params)) {
449
+ yield* datasets;
411
450
  }
412
- return response;
413
451
  }
414
452
  async deleteDataset({ datasetId, datasetName, }) {
415
453
  let path = "/datasets";
@@ -472,7 +510,7 @@ class Client {
472
510
  const path = `/examples/${exampleId}`;
473
511
  return await this._get(path);
474
512
  }
475
- async listExamples({ datasetId, datasetName, limit, offset, } = {}) {
513
+ async *listExamples({ datasetId, datasetName, } = {}) {
476
514
  let datasetId_;
477
515
  if (datasetId !== undefined && datasetName !== undefined) {
478
516
  throw new Error("Must provide either datasetName or datasetId, not both");
@@ -487,15 +525,10 @@ class Client {
487
525
  else {
488
526
  throw new Error("Must provide a datasetName or datasetId");
489
527
  }
490
- const response = await this._get("/examples", new URLSearchParams({
491
- dataset: datasetId_,
492
- limit: limit?.toString() ?? "100",
493
- offset: offset?.toString() ?? "0",
494
- }));
495
- if (!Array.isArray(response)) {
496
- throw new Error(`Expected /examples to return an array, but got ${response}`);
528
+ const params = new URLSearchParams({ dataset: datasetId_ });
529
+ for await (const examples of this._getPaginated("/examples", params)) {
530
+ yield* examples;
497
531
  }
498
- return response;
499
532
  }
500
533
  async deleteExample(exampleId) {
501
534
  const path = `/examples/${exampleId}`;
@@ -602,19 +635,14 @@ class Client {
602
635
  }
603
636
  await response.json();
604
637
  }
605
- async listFeedback({ runIds, limit, offset, } = {}) {
638
+ async *listFeedback({ runIds, } = {}) {
606
639
  const queryParams = new URLSearchParams();
607
640
  if (runIds) {
608
641
  queryParams.append("run", runIds.join(","));
609
642
  }
610
- if (limit !== undefined) {
611
- queryParams.append("limit", limit.toString());
643
+ for await (const feedbacks of this._getPaginated("/feedback", queryParams)) {
644
+ yield* feedbacks;
612
645
  }
613
- if (offset !== undefined) {
614
- queryParams.append("offset", offset.toString());
615
- }
616
- const response = await this._get("/feedback", queryParams);
617
- return response;
618
646
  }
619
647
  }
620
648
  exports.Client = Client;
package/dist/client.d.ts CHANGED
@@ -64,13 +64,14 @@ export declare class Client {
64
64
  private validateApiKeyIfHosted;
65
65
  private get headers();
66
66
  private _get;
67
+ private _getPaginated;
67
68
  createRun(run: CreateRunParams): Promise<void>;
68
69
  updateRun(runId: string, run: RunUpdate): Promise<void>;
69
70
  readRun(runId: string, { loadChildRuns }?: {
70
71
  loadChildRuns: boolean;
71
72
  }): Promise<Run>;
72
73
  private _loadChildRuns;
73
- listRuns({ projectId, projectName, parentRunId, referenceExampleId, datasetId, startTime, endTime, executionOrder, runType, error, id, limit, offset, query, filter, orderBy, }: ListRunsParams): Promise<Run[]>;
74
+ listRuns({ projectId, projectName, parentRunId, referenceExampleId, datasetId, startTime, endTime, executionOrder, runType, error, id, limit, offset, query, filter, orderBy, }: ListRunsParams): AsyncIterable<Run>;
74
75
  deleteRun(runId: string): Promise<void>;
75
76
  createProject({ projectName, projectExtra, upsert, }: {
76
77
  projectName: string;
@@ -81,7 +82,7 @@ export declare class Client {
81
82
  projectId?: string;
82
83
  projectName?: string;
83
84
  }): Promise<TracerSessionResult>;
84
- listProjects(): Promise<TracerSession[]>;
85
+ listProjects(): AsyncIterable<TracerSession>;
85
86
  deleteProject({ projectId, projectName, }: {
86
87
  projectId?: string;
87
88
  projectName?: string;
@@ -98,7 +99,7 @@ export declare class Client {
98
99
  listDatasets({ limit, offset, }?: {
99
100
  limit?: number;
100
101
  offset?: number;
101
- }): Promise<Dataset[]>;
102
+ }): AsyncIterable<Dataset>;
102
103
  deleteDataset({ datasetId, datasetName, }: {
103
104
  datasetId?: string;
104
105
  datasetName?: string;
@@ -109,12 +110,10 @@ export declare class Client {
109
110
  createdAt?: Date;
110
111
  }): Promise<Example>;
111
112
  readExample(exampleId: string): Promise<Example>;
112
- listExamples({ datasetId, datasetName, limit, offset, }?: {
113
+ listExamples({ datasetId, datasetName, }?: {
113
114
  datasetId?: string;
114
115
  datasetName?: string;
115
- limit?: number;
116
- offset?: number;
117
- }): Promise<Example[]>;
116
+ }): AsyncIterable<Example>;
118
117
  deleteExample(exampleId: string): Promise<void>;
119
118
  updateExample(exampleId: string, update: ExampleUpdate): Promise<object>;
120
119
  evaluateRun(run: Run | string, evaluator: RunEvaluator, { sourceInfo, loadChildRuns, }?: {
@@ -131,10 +130,8 @@ export declare class Client {
131
130
  }): Promise<Feedback>;
132
131
  readFeedback(feedbackId: string): Promise<Feedback>;
133
132
  deleteFeedback(feedbackId: string): Promise<void>;
134
- listFeedback({ runIds, limit, offset, }?: {
133
+ listFeedback({ runIds, }?: {
135
134
  runIds?: string[];
136
- limit?: number;
137
- offset?: number;
138
- }): Promise<Feedback[]>;
135
+ }): AsyncIterable<Feedback>;
139
136
  }
140
137
  export {};
package/dist/client.js CHANGED
@@ -15,6 +15,13 @@ const raiseForStatus = async (response, operation) => {
15
15
  throw new Error(`Failed to ${operation}: ${response.status} ${response.statusText} ${body}`);
16
16
  }
17
17
  };
18
+ async function toArray(iterable) {
19
+ const result = [];
20
+ for await (const item of iterable) {
21
+ result.push(item);
22
+ }
23
+ return result;
24
+ }
18
25
  export class Client {
19
26
  constructor(config = {}) {
20
27
  Object.defineProperty(this, "apiKey", {
@@ -49,9 +56,12 @@ export class Client {
49
56
  this.caller = new AsyncCaller(config.callerOptions ?? {});
50
57
  }
51
58
  static getDefaultClientConfig() {
59
+ const apiKey = getEnvironmentVariable("LANGCHAIN_API_KEY");
60
+ const apiUrl = getEnvironmentVariable("LANGCHAIN_ENDPOINT") ??
61
+ (apiKey ? "https://api.smith.langchain.com" : "http://localhost:1984");
52
62
  return {
53
- apiUrl: getEnvironmentVariable("LANGCHAIN_ENDPOINT") ?? "http://localhost:1984",
54
- apiKey: getEnvironmentVariable("LANGCHAIN_API_KEY"),
63
+ apiUrl: apiUrl,
64
+ apiKey: apiKey,
55
65
  };
56
66
  }
57
67
  validateApiKeyIfHosted() {
@@ -80,6 +90,32 @@ export class Client {
80
90
  }
81
91
  return response.json();
82
92
  }
93
+ async *_getPaginated(path, queryParams = new URLSearchParams()) {
94
+ let offset = Number(queryParams.get("offset")) || 0;
95
+ const limit = Number(queryParams.get("limit")) || 100;
96
+ while (true) {
97
+ queryParams.set("offset", String(offset));
98
+ queryParams.set("limit", String(limit));
99
+ const url = `${this.apiUrl}${path}?${queryParams}`;
100
+ const response = await this.caller.call(fetch, url, {
101
+ method: "GET",
102
+ headers: this.headers,
103
+ signal: AbortSignal.timeout(this.timeout_ms),
104
+ });
105
+ if (!response.ok) {
106
+ throw new Error(`Failed to fetch ${path}: ${response.status} ${response.statusText}`);
107
+ }
108
+ const items = await response.json();
109
+ if (items.length === 0) {
110
+ break;
111
+ }
112
+ yield items;
113
+ if (items.length < limit) {
114
+ break;
115
+ }
116
+ offset += items.length;
117
+ }
118
+ }
83
119
  async createRun(run) {
84
120
  const headers = { ...this.headers, "Content-Type": "application/json" };
85
121
  const extra = run.extra ?? {};
@@ -123,7 +159,7 @@ export class Client {
123
159
  return run;
124
160
  }
125
161
  async _loadChildRuns(run) {
126
- const childRuns = await this.listRuns({ id: run.child_run_ids });
162
+ const childRuns = await toArray(this.listRuns({ id: run.child_run_ids }));
127
163
  const treemap = {};
128
164
  const runs = {};
129
165
  childRuns.sort((a, b) => a.execution_order - b.execution_order);
@@ -146,7 +182,7 @@ export class Client {
146
182
  }
147
183
  return run;
148
184
  }
149
- async listRuns({ projectId, projectName, parentRunId, referenceExampleId, datasetId, startTime, endTime, executionOrder, runType, error, id, limit, offset, query, filter, orderBy, }) {
185
+ async *listRuns({ projectId, projectName, parentRunId, referenceExampleId, datasetId, startTime, endTime, executionOrder, runType, error, id, limit, offset, query, filter, orderBy, }) {
150
186
  const queryParams = new URLSearchParams();
151
187
  let projectId_ = projectId;
152
188
  if (projectName) {
@@ -202,7 +238,9 @@ export class Client {
202
238
  if (orderBy !== undefined) {
203
239
  orderBy.map((order) => queryParams.append("order_by", order));
204
240
  }
205
- return this._get("/runs", queryParams);
241
+ for await (const runs of this._getPaginated("/runs", queryParams)) {
242
+ yield* runs;
243
+ }
206
244
  }
207
245
  async deleteRun(runId) {
208
246
  const response = await this.caller.call(fetch, `${this.apiUrl}/runs/${runId}`, {
@@ -261,8 +299,10 @@ export class Client {
261
299
  }
262
300
  return result;
263
301
  }
264
- async listProjects() {
265
- return this._get("/sessions");
302
+ async *listProjects() {
303
+ for await (const projects of this._getPaginated("/sessions")) {
304
+ yield* projects;
305
+ }
266
306
  }
267
307
  async deleteProject({ projectId, projectName, }) {
268
308
  let projectId_;
@@ -373,17 +413,15 @@ export class Client {
373
413
  }
374
414
  return result;
375
415
  }
376
- async listDatasets({ limit = 100, offset = 0, } = {}) {
416
+ async *listDatasets({ limit = 100, offset = 0, } = {}) {
377
417
  const path = "/datasets";
378
418
  const params = new URLSearchParams({
379
419
  limit: limit.toString(),
380
420
  offset: offset.toString(),
381
421
  });
382
- const response = await this._get(path, params);
383
- if (!Array.isArray(response)) {
384
- throw new Error(`Expected ${path} to return an array, but got ${response}`);
422
+ for await (const datasets of this._getPaginated(path, params)) {
423
+ yield* datasets;
385
424
  }
386
- return response;
387
425
  }
388
426
  async deleteDataset({ datasetId, datasetName, }) {
389
427
  let path = "/datasets";
@@ -446,7 +484,7 @@ export class Client {
446
484
  const path = `/examples/${exampleId}`;
447
485
  return await this._get(path);
448
486
  }
449
- async listExamples({ datasetId, datasetName, limit, offset, } = {}) {
487
+ async *listExamples({ datasetId, datasetName, } = {}) {
450
488
  let datasetId_;
451
489
  if (datasetId !== undefined && datasetName !== undefined) {
452
490
  throw new Error("Must provide either datasetName or datasetId, not both");
@@ -461,15 +499,10 @@ export class Client {
461
499
  else {
462
500
  throw new Error("Must provide a datasetName or datasetId");
463
501
  }
464
- const response = await this._get("/examples", new URLSearchParams({
465
- dataset: datasetId_,
466
- limit: limit?.toString() ?? "100",
467
- offset: offset?.toString() ?? "0",
468
- }));
469
- if (!Array.isArray(response)) {
470
- throw new Error(`Expected /examples to return an array, but got ${response}`);
502
+ const params = new URLSearchParams({ dataset: datasetId_ });
503
+ for await (const examples of this._getPaginated("/examples", params)) {
504
+ yield* examples;
471
505
  }
472
- return response;
473
506
  }
474
507
  async deleteExample(exampleId) {
475
508
  const path = `/examples/${exampleId}`;
@@ -576,18 +609,13 @@ export class Client {
576
609
  }
577
610
  await response.json();
578
611
  }
579
- async listFeedback({ runIds, limit, offset, } = {}) {
612
+ async *listFeedback({ runIds, } = {}) {
580
613
  const queryParams = new URLSearchParams();
581
614
  if (runIds) {
582
615
  queryParams.append("run", runIds.join(","));
583
616
  }
584
- if (limit !== undefined) {
585
- queryParams.append("limit", limit.toString());
617
+ for await (const feedbacks of this._getPaginated("/feedback", queryParams)) {
618
+ yield* feedbacks;
586
619
  }
587
- if (offset !== undefined) {
588
- queryParams.append("offset", offset.toString());
589
- }
590
- const response = await this._get("/feedback", queryParams);
591
- return response;
592
620
  }
593
621
  }
@@ -153,7 +153,6 @@ class RunTree {
153
153
  api_key: (0, env_js_1.getEnvironmentVariable)("LANGCHAIN_API_KEY"),
154
154
  caller_options: {},
155
155
  start_time: Date.now(),
156
- end_time: Date.now(),
157
156
  serialized: {},
158
157
  inputs: {},
159
158
  extra: {},
@@ -29,7 +29,7 @@ export declare class RunTree implements BaseRun {
29
29
  execution_order: number;
30
30
  child_execution_order: number;
31
31
  start_time: number;
32
- end_time: number;
32
+ end_time?: number;
33
33
  extra: KVMap;
34
34
  error?: string;
35
35
  serialized: object;
package/dist/run_trees.js CHANGED
@@ -127,7 +127,6 @@ export class RunTree {
127
127
  api_key: getEnvironmentVariable("LANGCHAIN_API_KEY"),
128
128
  caller_options: {},
129
129
  start_time: Date.now(),
130
- end_time: Date.now(),
131
130
  serialized: {},
132
131
  inputs: {},
133
132
  extra: {},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langsmith",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform.",
5
5
  "files": [
6
6
  "dist/",