langsmith 0.3.28-rc.1 → 0.3.28

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
@@ -1175,7 +1175,7 @@ class Client {
1175
1175
  * });
1176
1176
  */
1177
1177
  async *listRuns(props) {
1178
- const { projectId, projectName, parentRunId, traceId, referenceExampleId, startTime, executionOrder, isRoot, runType, error, id, query, filter, traceFilter, treeFilter, limit, select, } = props;
1178
+ const { projectId, projectName, parentRunId, traceId, referenceExampleId, startTime, executionOrder, isRoot, runType, error, id, query, filter, traceFilter, treeFilter, limit, select, order, } = props;
1179
1179
  let projectIds = [];
1180
1180
  if (projectId) {
1181
1181
  projectIds = Array.isArray(projectId) ? projectId : [projectId];
@@ -1234,6 +1234,7 @@ class Client {
1234
1234
  trace: traceId,
1235
1235
  select: select ? select : default_select,
1236
1236
  is_root: isRoot,
1237
+ order,
1237
1238
  };
1238
1239
  let runsYielded = 0;
1239
1240
  for await (const runs of this._getCursorPaginatedList("/runs/query", body)) {
@@ -1254,6 +1255,49 @@ class Client {
1254
1255
  }
1255
1256
  }
1256
1257
  }
1258
+ async *listGroupRuns(props) {
1259
+ const { projectId, projectName, groupBy, filter, startTime, endTime, limit, offset, } = props;
1260
+ const sessionId = projectId || (await this.readProject({ projectName })).id;
1261
+ const baseBody = {
1262
+ session_id: sessionId,
1263
+ group_by: groupBy,
1264
+ filter,
1265
+ start_time: startTime ? startTime.toISOString() : null,
1266
+ end_time: endTime ? endTime.toISOString() : null,
1267
+ limit: Number(limit) || 100,
1268
+ };
1269
+ let currentOffset = Number(offset) || 0;
1270
+ const path = "/runs/group";
1271
+ const url = `${this.apiUrl}${path}`;
1272
+ while (true) {
1273
+ const currentBody = {
1274
+ ...baseBody,
1275
+ offset: currentOffset,
1276
+ };
1277
+ // Remove undefined values from the payload
1278
+ const filteredPayload = Object.fromEntries(Object.entries(currentBody).filter(([_, value]) => value !== undefined));
1279
+ const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(), url, {
1280
+ method: "POST",
1281
+ headers: { ...this.headers, "Content-Type": "application/json" },
1282
+ body: JSON.stringify(filteredPayload),
1283
+ signal: AbortSignal.timeout(this.timeout_ms),
1284
+ ...this.fetchOptions,
1285
+ });
1286
+ await (0, error_js_1.raiseForStatus)(response, `Failed to fetch ${path}`);
1287
+ const items = await response.json();
1288
+ const { groups, total } = items;
1289
+ if (groups.length === 0) {
1290
+ break;
1291
+ }
1292
+ for (const thread of groups) {
1293
+ yield thread;
1294
+ }
1295
+ currentOffset += groups.length;
1296
+ if (currentOffset >= total) {
1297
+ break;
1298
+ }
1299
+ }
1300
+ }
1257
1301
  async getRunStats({ id, trace, parentRun, runType, projectNames, projectIds, referenceExampleIds, startTime, endTime, error, query, filter, traceFilter, treeFilter, isRoot, dataSourceType, }) {
1258
1302
  let projectIds_ = projectIds || [];
1259
1303
  if (projectNames) {
package/dist/client.d.ts CHANGED
@@ -54,6 +54,10 @@ interface ListRunsParams {
54
54
  * The ID of the parent run to filter by.
55
55
  */
56
56
  parentRunId?: string;
57
+ /**
58
+ * The order by run start date
59
+ */
60
+ order?: "asc" | "desc";
57
61
  /**
58
62
  * The ID of the reference example to filter by.
59
63
  */
@@ -118,6 +122,56 @@ interface ListRunsParams {
118
122
  */
119
123
  select?: string[];
120
124
  }
125
+ interface GroupRunsParams {
126
+ /**
127
+ * The ID or IDs of the project(s) to filter by.
128
+ */
129
+ projectId?: string;
130
+ /**
131
+ * The ID or IDs of the project(s) to filter by.
132
+ */
133
+ projectName?: string;
134
+ /**
135
+ * @example "conversation"
136
+ */
137
+ groupBy: string;
138
+ /**
139
+ * The filter string to apply.
140
+ *
141
+ * Run Filtering:
142
+ * Listing runs with query params is useful for simple queries, but doesn't support many common needs, such as filtering by metadata, tags, or other fields.
143
+ * LangSmith supports a filter query language to permit more complex filtering operations when fetching runs. This guide will provide a high level overview of the grammar as well as a few examples of when it can be useful.
144
+ * If you'd prefer a more visual guide, you can get a taste of the language by viewing the table of runs on any of your projects' pages. We provide some recommended filters to get you started that you can copy and use the SDK.
145
+ *
146
+ * Grammar:
147
+ * The filtering grammar is based on common comparators on fields in the run object. Supported comparators include:
148
+ * - gte (greater than or equal to)
149
+ * - gt (greater than)
150
+ * - lte (less than or equal to)
151
+ * - lt (less than)
152
+ * - eq (equal to)
153
+ * - neq (not equal to)
154
+ * - has (check if run contains a tag or metadata json blob)
155
+ * - search (search for a substring in a string field)
156
+ */
157
+ filter?: string;
158
+ /**
159
+ * The start time to filter by.
160
+ */
161
+ startTime?: Date;
162
+ /**
163
+ * The end time to filter by.
164
+ */
165
+ endTime?: Date;
166
+ /**
167
+ * The maximum number of runs to retrieve.
168
+ */
169
+ limit?: number;
170
+ /**
171
+ * The maximum number of runs to retrieve.
172
+ */
173
+ offset?: number;
174
+ }
121
175
  interface UploadCSVParams {
122
176
  csvFile: Blob;
123
177
  fileName: string;
@@ -187,6 +241,21 @@ type AutoBatchQueueItem = {
187
241
  action: "create" | "update";
188
242
  item: RunCreate | RunUpdate;
189
243
  };
244
+ type Thread = {
245
+ filter: string;
246
+ count: number;
247
+ total_tokens: number;
248
+ total_cost: number | null;
249
+ min_start_time: string;
250
+ max_start_time: string;
251
+ latency_p50: number;
252
+ latency_p99: number;
253
+ feedback_stats: any | null;
254
+ group_key: string;
255
+ first_inputs: string;
256
+ last_outputs: string;
257
+ last_error: string | null;
258
+ };
190
259
  export declare function mergeRuntimeEnvIntoRunCreate(run: RunCreate): RunCreate;
191
260
  export declare class AutoBatchQueue {
192
261
  items: {
@@ -376,6 +445,7 @@ export declare class Client implements LangSmithTracingClientInterface {
376
445
  * });
377
446
  */
378
447
  listRuns(props: ListRunsParams): AsyncIterable<Run>;
448
+ listGroupRuns(props: GroupRunsParams): AsyncIterable<Thread>;
379
449
  getRunStats({ id, trace, parentRun, runType, projectNames, projectIds, referenceExampleIds, startTime, endTime, error, query, filter, traceFilter, treeFilter, isRoot, dataSourceType, }: {
380
450
  id?: string[];
381
451
  trace?: string;
package/dist/client.js CHANGED
@@ -1137,7 +1137,7 @@ export class Client {
1137
1137
  * });
1138
1138
  */
1139
1139
  async *listRuns(props) {
1140
- const { projectId, projectName, parentRunId, traceId, referenceExampleId, startTime, executionOrder, isRoot, runType, error, id, query, filter, traceFilter, treeFilter, limit, select, } = props;
1140
+ const { projectId, projectName, parentRunId, traceId, referenceExampleId, startTime, executionOrder, isRoot, runType, error, id, query, filter, traceFilter, treeFilter, limit, select, order, } = props;
1141
1141
  let projectIds = [];
1142
1142
  if (projectId) {
1143
1143
  projectIds = Array.isArray(projectId) ? projectId : [projectId];
@@ -1196,6 +1196,7 @@ export class Client {
1196
1196
  trace: traceId,
1197
1197
  select: select ? select : default_select,
1198
1198
  is_root: isRoot,
1199
+ order,
1199
1200
  };
1200
1201
  let runsYielded = 0;
1201
1202
  for await (const runs of this._getCursorPaginatedList("/runs/query", body)) {
@@ -1216,6 +1217,49 @@ export class Client {
1216
1217
  }
1217
1218
  }
1218
1219
  }
1220
+ async *listGroupRuns(props) {
1221
+ const { projectId, projectName, groupBy, filter, startTime, endTime, limit, offset, } = props;
1222
+ const sessionId = projectId || (await this.readProject({ projectName })).id;
1223
+ const baseBody = {
1224
+ session_id: sessionId,
1225
+ group_by: groupBy,
1226
+ filter,
1227
+ start_time: startTime ? startTime.toISOString() : null,
1228
+ end_time: endTime ? endTime.toISOString() : null,
1229
+ limit: Number(limit) || 100,
1230
+ };
1231
+ let currentOffset = Number(offset) || 0;
1232
+ const path = "/runs/group";
1233
+ const url = `${this.apiUrl}${path}`;
1234
+ while (true) {
1235
+ const currentBody = {
1236
+ ...baseBody,
1237
+ offset: currentOffset,
1238
+ };
1239
+ // Remove undefined values from the payload
1240
+ const filteredPayload = Object.fromEntries(Object.entries(currentBody).filter(([_, value]) => value !== undefined));
1241
+ const response = await this.caller.call(_getFetchImplementation(), url, {
1242
+ method: "POST",
1243
+ headers: { ...this.headers, "Content-Type": "application/json" },
1244
+ body: JSON.stringify(filteredPayload),
1245
+ signal: AbortSignal.timeout(this.timeout_ms),
1246
+ ...this.fetchOptions,
1247
+ });
1248
+ await raiseForStatus(response, `Failed to fetch ${path}`);
1249
+ const items = await response.json();
1250
+ const { groups, total } = items;
1251
+ if (groups.length === 0) {
1252
+ break;
1253
+ }
1254
+ for (const thread of groups) {
1255
+ yield thread;
1256
+ }
1257
+ currentOffset += groups.length;
1258
+ if (currentOffset >= total) {
1259
+ break;
1260
+ }
1261
+ }
1262
+ }
1219
1263
  async getRunStats({ id, trace, parentRun, runType, projectNames, projectIds, referenceExampleIds, startTime, endTime, error, query, filter, traceFilter, treeFilter, isRoot, dataSourceType, }) {
1220
1264
  let projectIds_ = projectIds || [];
1221
1265
  if (projectNames) {
package/dist/index.cjs CHANGED
@@ -8,4 +8,4 @@ Object.defineProperty(exports, "RunTree", { enumerable: true, get: function () {
8
8
  var fetch_js_1 = require("./singletons/fetch.cjs");
9
9
  Object.defineProperty(exports, "overrideFetchImplementation", { enumerable: true, get: function () { return fetch_js_1.overrideFetchImplementation; } });
10
10
  // Update using yarn bump-version
11
- exports.__version__ = "0.3.28-rc.1";
11
+ exports.__version__ = "0.3.28";
package/dist/index.d.ts CHANGED
@@ -2,4 +2,4 @@ export { Client, type ClientConfig, type LangSmithTracingClientInterface, } from
2
2
  export type { Dataset, Example, TracerSession, Run, Feedback, RetrieverOutput, } from "./schemas.js";
3
3
  export { RunTree, type RunTreeConfig } from "./run_trees.js";
4
4
  export { overrideFetchImplementation } from "./singletons/fetch.js";
5
- export declare const __version__ = "0.3.28-rc.1";
5
+ export declare const __version__ = "0.3.28";
package/dist/index.js CHANGED
@@ -2,4 +2,4 @@ export { Client, } from "./client.js";
2
2
  export { RunTree } from "./run_trees.js";
3
3
  export { overrideFetchImplementation } from "./singletons/fetch.js";
4
4
  // Update using yarn bump-version
5
- export const __version__ = "0.3.28-rc.1";
5
+ export const __version__ = "0.3.28";
package/dist/vercel.cjs CHANGED
@@ -291,7 +291,7 @@ class AISDKExporter {
291
291
  enumerable: true,
292
292
  configurable: true,
293
293
  writable: true,
294
- value: []
294
+ value: {}
295
295
  });
296
296
  Object.defineProperty(this, "debug", {
297
297
  enumerable: true,
@@ -644,18 +644,13 @@ class AISDKExporter {
644
644
  _export(spans, resultCallback) {
645
645
  this.logDebug("exporting spans", spans);
646
646
  const typedSpans = spans
647
- .concat(this.pendingSpans)
647
+ .concat(Object.values(this.pendingSpans))
648
648
  .slice()
649
649
  // Parent spans should go before child spans in the final order,
650
650
  // but may have the same exact start time as their children.
651
651
  // They will end earlier, so break ties by end time.
652
652
  // TODO: Figure out why this happens.
653
653
  .sort((a, b) => sortByHr(a, b));
654
- // This is really important, as OTEL seems to do weird things with threads.
655
- // Don't use a while loop.
656
- this.pendingSpans = [];
657
- const skippedSpans = [];
658
- const potentialChildRootRunIds = [];
659
654
  for (const span of typedSpans) {
660
655
  const { traceId, spanId } = span.spanContext();
661
656
  const runId = (0, uuid_1.v5)(spanId, RUN_ID_NAMESPACE);
@@ -682,9 +677,12 @@ class AISDKExporter {
682
677
  // for retry later in order to determine whether their parents are tool calls
683
678
  // and should not be reparented below.
684
679
  if (parentRunId !== undefined && parentSpanInfo === undefined) {
685
- skippedSpans.push(span);
680
+ this.pendingSpans[spanId] = span;
686
681
  continue;
687
682
  }
683
+ else {
684
+ delete this.pendingSpans[spanId];
685
+ }
688
686
  this.traceByMap[traceId] ??= {
689
687
  childMap: {},
690
688
  nodeMap: {},
@@ -718,9 +716,6 @@ class AISDKExporter {
718
716
  console.log(`[${span.name}] ${runId}`, run);
719
717
  traceMap.childMap[parentRunId ?? ROOT] ??= [];
720
718
  traceMap.childMap[parentRunId ?? ROOT].push(traceMap.nodeMap[runId]);
721
- if (parentRunId != null) {
722
- potentialChildRootRunIds.push(parentRunId);
723
- }
724
719
  }
725
720
  const sampled = [];
726
721
  const actions = [];
@@ -728,7 +723,7 @@ class AISDKExporter {
728
723
  const traceMap = this.traceByMap[traceId];
729
724
  const queue = Object.keys(traceMap.childMap)
730
725
  .map((runId) => {
731
- if (runId === ROOT || potentialChildRootRunIds.includes(runId)) {
726
+ if (runId === ROOT) {
732
727
  return traceMap.childMap[runId];
733
728
  }
734
729
  return [];
@@ -787,13 +782,7 @@ class AISDKExporter {
787
782
  }
788
783
  }
789
784
  this.logDebug(`sampled runs to be sent to LangSmith`, sampled);
790
- Promise.all(sampled.map((run) => this.client.createRun(run)))
791
- .then(() => {
792
- // OTEL seems to do weird things with threads, so we need to queue any spans
793
- // with missing parents for retry at the end after async operations.
794
- this.pendingSpans = skippedSpans;
795
- })
796
- .then(() => resultCallback({ code: 0 }), (error) => resultCallback({ code: 1, error }));
785
+ Promise.all(sampled.map((run) => this.client.createRun(run))).then(() => resultCallback({ code: 0 }), (error) => resultCallback({ code: 1, error }));
797
786
  }
798
787
  export(spans, resultCallback) {
799
788
  this._export(spans, (result) => {
package/dist/vercel.js CHANGED
@@ -288,7 +288,7 @@ export class AISDKExporter {
288
288
  enumerable: true,
289
289
  configurable: true,
290
290
  writable: true,
291
- value: []
291
+ value: {}
292
292
  });
293
293
  Object.defineProperty(this, "debug", {
294
294
  enumerable: true,
@@ -641,18 +641,13 @@ export class AISDKExporter {
641
641
  _export(spans, resultCallback) {
642
642
  this.logDebug("exporting spans", spans);
643
643
  const typedSpans = spans
644
- .concat(this.pendingSpans)
644
+ .concat(Object.values(this.pendingSpans))
645
645
  .slice()
646
646
  // Parent spans should go before child spans in the final order,
647
647
  // but may have the same exact start time as their children.
648
648
  // They will end earlier, so break ties by end time.
649
649
  // TODO: Figure out why this happens.
650
650
  .sort((a, b) => sortByHr(a, b));
651
- // This is really important, as OTEL seems to do weird things with threads.
652
- // Don't use a while loop.
653
- this.pendingSpans = [];
654
- const skippedSpans = [];
655
- const potentialChildRootRunIds = [];
656
651
  for (const span of typedSpans) {
657
652
  const { traceId, spanId } = span.spanContext();
658
653
  const runId = uuid5(spanId, RUN_ID_NAMESPACE);
@@ -679,9 +674,12 @@ export class AISDKExporter {
679
674
  // for retry later in order to determine whether their parents are tool calls
680
675
  // and should not be reparented below.
681
676
  if (parentRunId !== undefined && parentSpanInfo === undefined) {
682
- skippedSpans.push(span);
677
+ this.pendingSpans[spanId] = span;
683
678
  continue;
684
679
  }
680
+ else {
681
+ delete this.pendingSpans[spanId];
682
+ }
685
683
  this.traceByMap[traceId] ??= {
686
684
  childMap: {},
687
685
  nodeMap: {},
@@ -715,9 +713,6 @@ export class AISDKExporter {
715
713
  console.log(`[${span.name}] ${runId}`, run);
716
714
  traceMap.childMap[parentRunId ?? ROOT] ??= [];
717
715
  traceMap.childMap[parentRunId ?? ROOT].push(traceMap.nodeMap[runId]);
718
- if (parentRunId != null) {
719
- potentialChildRootRunIds.push(parentRunId);
720
- }
721
716
  }
722
717
  const sampled = [];
723
718
  const actions = [];
@@ -725,7 +720,7 @@ export class AISDKExporter {
725
720
  const traceMap = this.traceByMap[traceId];
726
721
  const queue = Object.keys(traceMap.childMap)
727
722
  .map((runId) => {
728
- if (runId === ROOT || potentialChildRootRunIds.includes(runId)) {
723
+ if (runId === ROOT) {
729
724
  return traceMap.childMap[runId];
730
725
  }
731
726
  return [];
@@ -784,13 +779,7 @@ export class AISDKExporter {
784
779
  }
785
780
  }
786
781
  this.logDebug(`sampled runs to be sent to LangSmith`, sampled);
787
- Promise.all(sampled.map((run) => this.client.createRun(run)))
788
- .then(() => {
789
- // OTEL seems to do weird things with threads, so we need to queue any spans
790
- // with missing parents for retry at the end after async operations.
791
- this.pendingSpans = skippedSpans;
792
- })
793
- .then(() => resultCallback({ code: 0 }), (error) => resultCallback({ code: 1, error }));
782
+ Promise.all(sampled.map((run) => this.client.createRun(run))).then(() => resultCallback({ code: 0 }), (error) => resultCallback({ code: 1, error }));
794
783
  }
795
784
  export(spans, resultCallback) {
796
785
  this._export(spans, (result) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langsmith",
3
- "version": "0.3.28-rc.1",
3
+ "version": "0.3.28",
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": [