langsmith 0.1.43 → 0.1.45

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.js CHANGED
@@ -6,6 +6,7 @@ import { __version__ } from "./index.js";
6
6
  import { assertUuid } from "./utils/_uuid.js";
7
7
  import { warnOnce } from "./utils/warn.js";
8
8
  import { isVersionGreaterOrEqual, parsePromptIdentifier, } from "./utils/prompts.js";
9
+ import { raiseForStatus } from "./utils/error.js";
9
10
  async function mergeRuntimeEnvIntoRunCreates(runs) {
10
11
  const runtimeEnv = await getRuntimeEnvironment();
11
12
  const envVars = getLangChainEnvVarsMetadata();
@@ -46,14 +47,6 @@ const isLocalhost = (url) => {
46
47
  const hostname = strippedUrl.split("/")[0].split(":")[0];
47
48
  return (hostname === "localhost" || hostname === "127.0.0.1" || hostname === "::1");
48
49
  };
49
- const raiseForStatus = async (response, operation) => {
50
- // consume the response body to release the connection
51
- // https://undici.nodejs.org/#/?id=garbage-collection
52
- const body = await response.text();
53
- if (!response.ok) {
54
- throw new Error(`Failed to ${operation}: ${response.status} ${response.statusText} ${body}`);
55
- }
56
- };
57
50
  async function toArray(iterable) {
58
51
  const result = [];
59
52
  for await (const item of iterable) {
@@ -360,9 +353,7 @@ export class Client {
360
353
  signal: AbortSignal.timeout(this.timeout_ms),
361
354
  ...this.fetchOptions,
362
355
  });
363
- if (!response.ok) {
364
- throw new Error(`Failed to fetch ${path}: ${response.status} ${response.statusText}`);
365
- }
356
+ await raiseForStatus(response, `Failed to fetch ${path}`);
366
357
  return response;
367
358
  }
368
359
  async _get(path, queryParams) {
@@ -382,9 +373,7 @@ export class Client {
382
373
  signal: AbortSignal.timeout(this.timeout_ms),
383
374
  ...this.fetchOptions,
384
375
  });
385
- if (!response.ok) {
386
- throw new Error(`Failed to fetch ${path}: ${response.status} ${response.statusText}`);
387
- }
376
+ await raiseForStatus(response, `Failed to fetch ${path}`);
388
377
  const items = transform
389
378
  ? transform(await response.json())
390
379
  : await response.json();
@@ -501,12 +490,7 @@ export class Client {
501
490
  signal: AbortSignal.timeout(this.timeout_ms),
502
491
  ...this.fetchOptions,
503
492
  });
504
- if (!response.ok) {
505
- // consume the response body to release the connection
506
- // https://undici.nodejs.org/#/?id=garbage-collection
507
- await response.text();
508
- throw new Error("Failed to retrieve server info.");
509
- }
493
+ await raiseForStatus(response, "get server info");
510
494
  return response.json();
511
495
  }
512
496
  async batchEndpointIsSupported() {
@@ -555,7 +539,7 @@ export class Client {
555
539
  signal: AbortSignal.timeout(this.timeout_ms),
556
540
  ...this.fetchOptions,
557
541
  });
558
- await raiseForStatus(response, "create run");
542
+ await raiseForStatus(response, "create run", true);
559
543
  }
560
544
  /**
561
545
  * Batch ingest/upsert multiple runs in the Langsmith system.
@@ -655,7 +639,7 @@ export class Client {
655
639
  signal: AbortSignal.timeout(this.timeout_ms),
656
640
  ...this.fetchOptions,
657
641
  });
658
- await raiseForStatus(response, "batch create run");
642
+ await raiseForStatus(response, "batch create run", true);
659
643
  }
660
644
  async updateRun(runId, run) {
661
645
  assertUuid(runId);
@@ -692,7 +676,7 @@ export class Client {
692
676
  signal: AbortSignal.timeout(this.timeout_ms),
693
677
  ...this.fetchOptions,
694
678
  });
695
- await raiseForStatus(response, "update run");
679
+ await raiseForStatus(response, "update run", true);
696
680
  }
697
681
  async readRun(runId, { loadChildRuns } = { loadChildRuns: false }) {
698
682
  assertUuid(runId);
@@ -985,7 +969,7 @@ export class Client {
985
969
  signal: AbortSignal.timeout(this.timeout_ms),
986
970
  ...this.fetchOptions,
987
971
  });
988
- await raiseForStatus(response, "unshare run");
972
+ await raiseForStatus(response, "unshare run", true);
989
973
  }
990
974
  async readRunSharedLink(runId) {
991
975
  assertUuid(runId);
@@ -1070,7 +1054,7 @@ export class Client {
1070
1054
  signal: AbortSignal.timeout(this.timeout_ms),
1071
1055
  ...this.fetchOptions,
1072
1056
  });
1073
- await raiseForStatus(response, "unshare dataset");
1057
+ await raiseForStatus(response, "unshare dataset", true);
1074
1058
  }
1075
1059
  async readSharedDataset(shareToken) {
1076
1060
  assertUuid(shareToken);
@@ -1083,6 +1067,46 @@ export class Client {
1083
1067
  const dataset = await response.json();
1084
1068
  return dataset;
1085
1069
  }
1070
+ /**
1071
+ * Get shared examples.
1072
+ *
1073
+ * @param {string} shareToken The share token to get examples for. A share token is the UUID (or LangSmith URL, including UUID) generated when explicitly marking an example as public.
1074
+ * @param {Object} [options] Additional options for listing the examples.
1075
+ * @param {string[] | undefined} [options.exampleIds] A list of example IDs to filter by.
1076
+ * @returns {Promise<Example[]>} The shared examples.
1077
+ */
1078
+ async listSharedExamples(shareToken, options) {
1079
+ const params = {};
1080
+ if (options?.exampleIds) {
1081
+ params.id = options.exampleIds;
1082
+ }
1083
+ const urlParams = new URLSearchParams();
1084
+ Object.entries(params).forEach(([key, value]) => {
1085
+ if (Array.isArray(value)) {
1086
+ value.forEach((v) => urlParams.append(key, v));
1087
+ }
1088
+ else {
1089
+ urlParams.append(key, value);
1090
+ }
1091
+ });
1092
+ const response = await this.caller.call(fetch, `${this.apiUrl}/public/${shareToken}/examples?${urlParams.toString()}`, {
1093
+ method: "GET",
1094
+ headers: this.headers,
1095
+ signal: AbortSignal.timeout(this.timeout_ms),
1096
+ ...this.fetchOptions,
1097
+ });
1098
+ const result = await response.json();
1099
+ if (!response.ok) {
1100
+ if ("detail" in result) {
1101
+ throw new Error(`Failed to list shared examples.\nStatus: ${response.status}\nMessage: ${result.detail.join("\n")}`);
1102
+ }
1103
+ throw new Error(`Failed to list shared examples: ${response.status} ${response.statusText}`);
1104
+ }
1105
+ return result.map((example) => ({
1106
+ ...example,
1107
+ _hostUrl: this.getHostUrl(),
1108
+ }));
1109
+ }
1086
1110
  async createProject({ projectName, description = null, metadata = null, upsert = false, projectExtra = null, referenceDatasetId = null, }) {
1087
1111
  const upsert_ = upsert ? `?upsert=true` : "";
1088
1112
  const endpoint = `${this.apiUrl}/sessions${upsert_}`;
@@ -1105,10 +1129,8 @@ export class Client {
1105
1129
  signal: AbortSignal.timeout(this.timeout_ms),
1106
1130
  ...this.fetchOptions,
1107
1131
  });
1132
+ await raiseForStatus(response, "create project");
1108
1133
  const result = await response.json();
1109
- if (!response.ok) {
1110
- throw new Error(`Failed to create session ${projectName}: ${response.status} ${response.statusText}`);
1111
- }
1112
1134
  return result;
1113
1135
  }
1114
1136
  async updateProject(projectId, { name = null, description = null, metadata = null, projectExtra = null, endTime = null, }) {
@@ -1130,10 +1152,8 @@ export class Client {
1130
1152
  signal: AbortSignal.timeout(this.timeout_ms),
1131
1153
  ...this.fetchOptions,
1132
1154
  });
1155
+ await raiseForStatus(response, "update project");
1133
1156
  const result = await response.json();
1134
- if (!response.ok) {
1135
- throw new Error(`Failed to update project ${projectId}: ${response.status} ${response.statusText}`);
1136
- }
1137
1157
  return result;
1138
1158
  }
1139
1159
  async hasProject({ projectId, projectName, }) {
@@ -1289,7 +1309,7 @@ export class Client {
1289
1309
  signal: AbortSignal.timeout(this.timeout_ms),
1290
1310
  ...this.fetchOptions,
1291
1311
  });
1292
- await raiseForStatus(response, `delete session ${projectId_} (${projectName})`);
1312
+ await raiseForStatus(response, `delete session ${projectId_} (${projectName})`, true);
1293
1313
  }
1294
1314
  async uploadCsv({ csvFile, fileName, inputKeys, outputKeys, description, dataType, name, }) {
1295
1315
  const url = `${this.apiUrl}/datasets/upload`;
@@ -1317,20 +1337,15 @@ export class Client {
1317
1337
  signal: AbortSignal.timeout(this.timeout_ms),
1318
1338
  ...this.fetchOptions,
1319
1339
  });
1320
- if (!response.ok) {
1321
- const result = await response.json();
1322
- if (result.detail && result.detail.includes("already exists")) {
1323
- throw new Error(`Dataset ${fileName} already exists`);
1324
- }
1325
- throw new Error(`Failed to upload CSV: ${response.status} ${response.statusText}`);
1326
- }
1340
+ await raiseForStatus(response, "upload CSV");
1327
1341
  const result = await response.json();
1328
1342
  return result;
1329
1343
  }
1330
- async createDataset(name, { description, dataType, inputsSchema, outputsSchema, } = {}) {
1344
+ async createDataset(name, { description, dataType, inputsSchema, outputsSchema, metadata, } = {}) {
1331
1345
  const body = {
1332
1346
  name,
1333
1347
  description,
1348
+ extra: metadata ? { metadata } : undefined,
1334
1349
  };
1335
1350
  if (dataType) {
1336
1351
  body.data_type = dataType;
@@ -1348,13 +1363,7 @@ export class Client {
1348
1363
  signal: AbortSignal.timeout(this.timeout_ms),
1349
1364
  ...this.fetchOptions,
1350
1365
  });
1351
- if (!response.ok) {
1352
- const result = await response.json();
1353
- if (result.detail && result.detail.includes("already exists")) {
1354
- throw new Error(`Dataset ${name} already exists`);
1355
- }
1356
- throw new Error(`Failed to create dataset ${response.status} ${response.statusText}`);
1357
- }
1366
+ await raiseForStatus(response, "create dataset");
1358
1367
  const result = await response.json();
1359
1368
  return result;
1360
1369
  }
@@ -1443,7 +1452,7 @@ export class Client {
1443
1452
  .map((line) => JSON.parse(line));
1444
1453
  return dataset;
1445
1454
  }
1446
- async *listDatasets({ limit = 100, offset = 0, datasetIds, datasetName, datasetNameContains, } = {}) {
1455
+ async *listDatasets({ limit = 100, offset = 0, datasetIds, datasetName, datasetNameContains, metadata, } = {}) {
1447
1456
  const path = "/datasets";
1448
1457
  const params = new URLSearchParams({
1449
1458
  limit: limit.toString(),
@@ -1460,6 +1469,9 @@ export class Client {
1460
1469
  if (datasetNameContains !== undefined) {
1461
1470
  params.append("name_contains", datasetNameContains);
1462
1471
  }
1472
+ if (metadata !== undefined) {
1473
+ params.append("metadata", JSON.stringify(metadata));
1474
+ }
1463
1475
  for await (const datasets of this._getPaginated(path, params)) {
1464
1476
  yield* datasets;
1465
1477
  }
@@ -1483,9 +1495,7 @@ export class Client {
1483
1495
  signal: AbortSignal.timeout(this.timeout_ms),
1484
1496
  ...this.fetchOptions,
1485
1497
  });
1486
- if (!response.ok) {
1487
- throw new Error(`Failed to update dataset ${_datasetId}: ${response.status} ${response.statusText}`);
1488
- }
1498
+ await raiseForStatus(response, "update dataset");
1489
1499
  return (await response.json());
1490
1500
  }
1491
1501
  async deleteDataset({ datasetId, datasetName, }) {
@@ -1511,9 +1521,7 @@ export class Client {
1511
1521
  signal: AbortSignal.timeout(this.timeout_ms),
1512
1522
  ...this.fetchOptions,
1513
1523
  });
1514
- if (!response.ok) {
1515
- throw new Error(`Failed to delete ${path}: ${response.status} ${response.statusText}`);
1516
- }
1524
+ await raiseForStatus(response, `delete ${path}`);
1517
1525
  await response.json();
1518
1526
  }
1519
1527
  async indexDataset({ datasetId, datasetName, tag, }) {
@@ -1539,9 +1547,7 @@ export class Client {
1539
1547
  signal: AbortSignal.timeout(this.timeout_ms),
1540
1548
  ...this.fetchOptions,
1541
1549
  });
1542
- if (!response.ok) {
1543
- throw new Error(`Failed to index dataset ${datasetId_}: ${response.status} ${response.statusText}`);
1544
- }
1550
+ await raiseForStatus(response, "index dataset");
1545
1551
  await response.json();
1546
1552
  }
1547
1553
  /**
@@ -1580,9 +1586,7 @@ export class Client {
1580
1586
  signal: AbortSignal.timeout(this.timeout_ms),
1581
1587
  ...this.fetchOptions,
1582
1588
  });
1583
- if (!response.ok) {
1584
- throw new Error(`Failed to fetch similar examples: ${response.status} ${response.statusText}`);
1585
- }
1589
+ await raiseForStatus(response, "fetch similar examples");
1586
1590
  const result = await response.json();
1587
1591
  return result["examples"];
1588
1592
  }
@@ -1615,9 +1619,7 @@ export class Client {
1615
1619
  signal: AbortSignal.timeout(this.timeout_ms),
1616
1620
  ...this.fetchOptions,
1617
1621
  });
1618
- if (!response.ok) {
1619
- throw new Error(`Failed to create example: ${response.status} ${response.statusText}`);
1620
- }
1622
+ await raiseForStatus(response, "create example");
1621
1623
  const result = await response.json();
1622
1624
  return result;
1623
1625
  }
@@ -1652,9 +1654,7 @@ export class Client {
1652
1654
  signal: AbortSignal.timeout(this.timeout_ms),
1653
1655
  ...this.fetchOptions,
1654
1656
  });
1655
- if (!response.ok) {
1656
- throw new Error(`Failed to create examples: ${response.status} ${response.statusText}`);
1657
- }
1657
+ await raiseForStatus(response, "create examples");
1658
1658
  const result = await response.json();
1659
1659
  return result;
1660
1660
  }
@@ -1747,9 +1747,7 @@ export class Client {
1747
1747
  signal: AbortSignal.timeout(this.timeout_ms),
1748
1748
  ...this.fetchOptions,
1749
1749
  });
1750
- if (!response.ok) {
1751
- throw new Error(`Failed to delete ${path}: ${response.status} ${response.statusText}`);
1752
- }
1750
+ await raiseForStatus(response, `delete ${path}`);
1753
1751
  await response.json();
1754
1752
  }
1755
1753
  async updateExample(exampleId, update) {
@@ -1761,9 +1759,7 @@ export class Client {
1761
1759
  signal: AbortSignal.timeout(this.timeout_ms),
1762
1760
  ...this.fetchOptions,
1763
1761
  });
1764
- if (!response.ok) {
1765
- throw new Error(`Failed to update example ${exampleId}: ${response.status} ${response.statusText}`);
1766
- }
1762
+ await raiseForStatus(response, "update example");
1767
1763
  const result = await response.json();
1768
1764
  return result;
1769
1765
  }
@@ -1775,9 +1771,7 @@ export class Client {
1775
1771
  signal: AbortSignal.timeout(this.timeout_ms),
1776
1772
  ...this.fetchOptions,
1777
1773
  });
1778
- if (!response.ok) {
1779
- throw new Error(`Failed to update examples: ${response.status} ${response.statusText}`);
1780
- }
1774
+ await raiseForStatus(response, "update examples");
1781
1775
  const result = await response.json();
1782
1776
  return result;
1783
1777
  }
@@ -1840,7 +1834,7 @@ export class Client {
1840
1834
  signal: AbortSignal.timeout(this.timeout_ms),
1841
1835
  ...this.fetchOptions,
1842
1836
  });
1843
- await raiseForStatus(response, "update dataset splits");
1837
+ await raiseForStatus(response, "update dataset splits", true);
1844
1838
  }
1845
1839
  /**
1846
1840
  * @deprecated This method is deprecated and will be removed in future LangSmith versions, use `evaluate` from `langsmith/evaluation` instead.
@@ -1906,7 +1900,7 @@ export class Client {
1906
1900
  signal: AbortSignal.timeout(this.timeout_ms),
1907
1901
  ...this.fetchOptions,
1908
1902
  });
1909
- await raiseForStatus(response, "create feedback");
1903
+ await raiseForStatus(response, "create feedback", true);
1910
1904
  return feedback;
1911
1905
  }
1912
1906
  async updateFeedback(feedbackId, { score, value, correction, comment, }) {
@@ -1931,7 +1925,7 @@ export class Client {
1931
1925
  signal: AbortSignal.timeout(this.timeout_ms),
1932
1926
  ...this.fetchOptions,
1933
1927
  });
1934
- await raiseForStatus(response, "update feedback");
1928
+ await raiseForStatus(response, "update feedback", true);
1935
1929
  }
1936
1930
  async readFeedback(feedbackId) {
1937
1931
  assertUuid(feedbackId);
@@ -1948,9 +1942,7 @@ export class Client {
1948
1942
  signal: AbortSignal.timeout(this.timeout_ms),
1949
1943
  ...this.fetchOptions,
1950
1944
  });
1951
- if (!response.ok) {
1952
- throw new Error(`Failed to delete ${path}: ${response.status} ${response.statusText}`);
1953
- }
1945
+ await raiseForStatus(response, `delete ${path}`);
1954
1946
  await response.json();
1955
1947
  }
1956
1948
  async *listFeedback({ runIds, feedbackKeys, feedbackSourceTypes, } = {}) {
@@ -2143,9 +2135,7 @@ export class Client {
2143
2135
  signal: AbortSignal.timeout(this.timeout_ms),
2144
2136
  ...this.fetchOptions,
2145
2137
  });
2146
- if (!response.ok) {
2147
- throw new Error(`Failed to ${like ? "like" : "unlike"} prompt: ${response.status} ${await response.text()}`);
2148
- }
2138
+ await raiseForStatus(response, `${like ? "like" : "unlike"} prompt`);
2149
2139
  return await response.json();
2150
2140
  }
2151
2141
  async _getPromptUrl(promptIdentifier) {
@@ -2209,9 +2199,7 @@ export class Client {
2209
2199
  if (response.status === 404) {
2210
2200
  return null;
2211
2201
  }
2212
- if (!response.ok) {
2213
- throw new Error(`Failed to get prompt: ${response.status} ${await response.text()}`);
2214
- }
2202
+ await raiseForStatus(response, "get prompt");
2215
2203
  const result = await response.json();
2216
2204
  if (result.repo) {
2217
2205
  return result.repo;
@@ -2246,9 +2234,7 @@ export class Client {
2246
2234
  signal: AbortSignal.timeout(this.timeout_ms),
2247
2235
  ...this.fetchOptions,
2248
2236
  });
2249
- if (!response.ok) {
2250
- throw new Error(`Failed to create prompt: ${response.status} ${await response.text()}`);
2251
- }
2237
+ await raiseForStatus(response, "create prompt");
2252
2238
  const { repo } = await response.json();
2253
2239
  return repo;
2254
2240
  }
@@ -2271,9 +2257,7 @@ export class Client {
2271
2257
  signal: AbortSignal.timeout(this.timeout_ms),
2272
2258
  ...this.fetchOptions,
2273
2259
  });
2274
- if (!response.ok) {
2275
- throw new Error(`Failed to create commit: ${response.status} ${await response.text()}`);
2276
- }
2260
+ await raiseForStatus(response, "create commit");
2277
2261
  const result = await response.json();
2278
2262
  return this._getPromptUrl(`${owner}/${promptName}${result.commit_hash ? `:${result.commit_hash}` : ""}`);
2279
2263
  }
@@ -2310,9 +2294,7 @@ export class Client {
2310
2294
  signal: AbortSignal.timeout(this.timeout_ms),
2311
2295
  ...this.fetchOptions,
2312
2296
  });
2313
- if (!response.ok) {
2314
- throw new Error(`HTTP Error: ${response.status} - ${await response.text()}`);
2315
- }
2297
+ await raiseForStatus(response, "update prompt");
2316
2298
  return response.json();
2317
2299
  }
2318
2300
  async deletePrompt(promptIdentifier) {
@@ -2351,9 +2333,7 @@ export class Client {
2351
2333
  signal: AbortSignal.timeout(this.timeout_ms),
2352
2334
  ...this.fetchOptions,
2353
2335
  });
2354
- if (!response.ok) {
2355
- throw new Error(`Failed to pull prompt commit: ${response.status} ${response.statusText}`);
2356
- }
2336
+ await raiseForStatus(response, "pull prompt commit");
2357
2337
  const result = await response.json();
2358
2338
  return {
2359
2339
  owner,
@@ -2406,4 +2386,85 @@ export class Client {
2406
2386
  });
2407
2387
  return url;
2408
2388
  }
2389
+ /**
2390
+ * Clone a public dataset to your own langsmith tenant.
2391
+ * This operation is idempotent. If you already have a dataset with the given name,
2392
+ * this function will do nothing.
2393
+
2394
+ * @param {string} tokenOrUrl The token of the public dataset to clone.
2395
+ * @param {Object} [options] Additional options for cloning the dataset.
2396
+ * @param {string} [options.sourceApiUrl] The URL of the langsmith server where the data is hosted. Defaults to the API URL of your current client.
2397
+ * @param {string} [options.datasetName] The name of the dataset to create in your tenant. Defaults to the name of the public dataset.
2398
+ * @returns {Promise<void>}
2399
+ */
2400
+ async clonePublicDataset(tokenOrUrl, options = {}) {
2401
+ const { sourceApiUrl = this.apiUrl, datasetName } = options;
2402
+ const [parsedApiUrl, tokenUuid] = this.parseTokenOrUrl(tokenOrUrl, sourceApiUrl);
2403
+ const sourceClient = new Client({
2404
+ apiUrl: parsedApiUrl,
2405
+ // Placeholder API key not needed anymore in most cases, but
2406
+ // some private deployments may have API key-based rate limiting
2407
+ // that would cause this to fail if we provide no value.
2408
+ apiKey: "placeholder",
2409
+ });
2410
+ const ds = await sourceClient.readSharedDataset(tokenUuid);
2411
+ const finalDatasetName = datasetName || ds.name;
2412
+ try {
2413
+ if (await this.hasDataset({ datasetId: finalDatasetName })) {
2414
+ console.log(`Dataset ${finalDatasetName} already exists in your tenant. Skipping.`);
2415
+ return;
2416
+ }
2417
+ }
2418
+ catch (_) {
2419
+ // `.hasDataset` will throw an error if the dataset does not exist.
2420
+ // no-op in that case
2421
+ }
2422
+ // Fetch examples first, then create the dataset
2423
+ const examples = await sourceClient.listSharedExamples(tokenUuid);
2424
+ const dataset = await this.createDataset(finalDatasetName, {
2425
+ description: ds.description,
2426
+ dataType: ds.data_type || "kv",
2427
+ inputsSchema: ds.inputs_schema_definition ?? undefined,
2428
+ outputsSchema: ds.outputs_schema_definition ?? undefined,
2429
+ });
2430
+ try {
2431
+ await this.createExamples({
2432
+ inputs: examples.map((e) => e.inputs),
2433
+ outputs: examples.flatMap((e) => (e.outputs ? [e.outputs] : [])),
2434
+ datasetId: dataset.id,
2435
+ });
2436
+ }
2437
+ catch (e) {
2438
+ console.error(`An error occurred while creating dataset ${finalDatasetName}. ` +
2439
+ "You should delete it manually.");
2440
+ throw e;
2441
+ }
2442
+ }
2443
+ parseTokenOrUrl(urlOrToken, apiUrl, numParts = 2, kind = "dataset") {
2444
+ // Try parsing as UUID
2445
+ try {
2446
+ assertUuid(urlOrToken); // Will throw if it's not a UUID.
2447
+ return [apiUrl, urlOrToken];
2448
+ }
2449
+ catch (_) {
2450
+ // no-op if it's not a uuid
2451
+ }
2452
+ // Parse as URL
2453
+ try {
2454
+ const parsedUrl = new URL(urlOrToken);
2455
+ const pathParts = parsedUrl.pathname
2456
+ .split("/")
2457
+ .filter((part) => part !== "");
2458
+ if (pathParts.length >= numParts) {
2459
+ const tokenUuid = pathParts[pathParts.length - numParts];
2460
+ return [apiUrl, tokenUuid];
2461
+ }
2462
+ else {
2463
+ throw new Error(`Invalid public ${kind} URL: ${urlOrToken}`);
2464
+ }
2465
+ }
2466
+ catch (error) {
2467
+ throw new Error(`Invalid public ${kind} URL or token: ${urlOrToken}`);
2468
+ }
2469
+ }
2409
2470
  }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.evaluate = void 0;
3
+ exports._ExperimentManager = exports.evaluate = void 0;
4
4
  const index_js_1 = require("../index.cjs");
5
5
  const langchain_js_1 = require("../langchain.cjs");
6
6
  const traceable_js_1 = require("../traceable.cjs");
@@ -232,17 +232,40 @@ class _ExperimentManager {
232
232
  }
233
233
  return projectMetadata;
234
234
  }
235
- async _getProject(firstExample) {
235
+ async _createProject(firstExample, projectMetadata) {
236
+ // Create the project, updating the experimentName until we find a unique one.
236
237
  let project;
237
- if (!this._experiment) {
238
+ const originalExperimentName = this._experimentName;
239
+ for (let i = 0; i < 10; i++) {
238
240
  try {
239
- const projectMetadata = await this._getExperimentMetadata();
240
241
  project = await this.client.createProject({
241
- projectName: this.experimentName,
242
+ projectName: this._experimentName,
242
243
  referenceDatasetId: firstExample.dataset_id,
243
244
  metadata: projectMetadata,
244
245
  description: this._description,
245
246
  });
247
+ return project;
248
+ }
249
+ catch (e) {
250
+ // Naming collision
251
+ if (e?.name === "LangSmithConflictError") {
252
+ const ent = (0, uuid_1.v4)().slice(0, 6);
253
+ this._experimentName = `${originalExperimentName}-${ent}`;
254
+ }
255
+ else {
256
+ throw e;
257
+ }
258
+ }
259
+ }
260
+ throw new Error("Could not generate a unique experiment name within 10 attempts." +
261
+ " Please try again with a different name.");
262
+ }
263
+ async _getProject(firstExample) {
264
+ let project;
265
+ if (!this._experiment) {
266
+ try {
267
+ const projectMetadata = await this._getExperimentMetadata();
268
+ project = await this._createProject(firstExample, projectMetadata);
246
269
  this._experiment = project;
247
270
  }
248
271
  catch (e) {
@@ -554,6 +577,7 @@ class _ExperimentManager {
554
577
  });
555
578
  }
556
579
  }
580
+ exports._ExperimentManager = _ExperimentManager;
557
581
  /**
558
582
  * Represents the results of an evaluate() call.
559
583
  * This class provides an iterator interface to iterate over the experiment results
@@ -88,7 +88,7 @@ interface ExperimentResultRow {
88
88
  * Supports lazily running predictions and evaluations in parallel to facilitate
89
89
  * result streaming and early debugging.
90
90
  */
91
- declare class _ExperimentManager {
91
+ export declare class _ExperimentManager {
92
92
  _data?: DataT;
93
93
  _runs?: AsyncGenerator<Run>;
94
94
  _evaluationResults?: AsyncGenerator<EvaluationResults>;
@@ -110,6 +110,7 @@ declare class _ExperimentManager {
110
110
  constructor(args: _ExperimentManagerArgs);
111
111
  _getExperiment(): TracerSession;
112
112
  _getExperimentMetadata(): Promise<KVMap>;
113
+ _createProject(firstExample: Example, projectMetadata: KVMap): Promise<TracerSession>;
113
114
  _getProject(firstExample: Example): Promise<TracerSession>;
114
115
  protected _printExperimentStart(): Promise<void>;
115
116
  start(): Promise<_ExperimentManager>;
@@ -23,7 +23,7 @@ target, options) {
23
23
  * Supports lazily running predictions and evaluations in parallel to facilitate
24
24
  * result streaming and early debugging.
25
25
  */
26
- class _ExperimentManager {
26
+ export class _ExperimentManager {
27
27
  get experimentName() {
28
28
  if (this._experimentName) {
29
29
  return this._experimentName;
@@ -228,17 +228,40 @@ class _ExperimentManager {
228
228
  }
229
229
  return projectMetadata;
230
230
  }
231
- async _getProject(firstExample) {
231
+ async _createProject(firstExample, projectMetadata) {
232
+ // Create the project, updating the experimentName until we find a unique one.
232
233
  let project;
233
- if (!this._experiment) {
234
+ const originalExperimentName = this._experimentName;
235
+ for (let i = 0; i < 10; i++) {
234
236
  try {
235
- const projectMetadata = await this._getExperimentMetadata();
236
237
  project = await this.client.createProject({
237
- projectName: this.experimentName,
238
+ projectName: this._experimentName,
238
239
  referenceDatasetId: firstExample.dataset_id,
239
240
  metadata: projectMetadata,
240
241
  description: this._description,
241
242
  });
243
+ return project;
244
+ }
245
+ catch (e) {
246
+ // Naming collision
247
+ if (e?.name === "LangSmithConflictError") {
248
+ const ent = uuidv4().slice(0, 6);
249
+ this._experimentName = `${originalExperimentName}-${ent}`;
250
+ }
251
+ else {
252
+ throw e;
253
+ }
254
+ }
255
+ }
256
+ throw new Error("Could not generate a unique experiment name within 10 attempts." +
257
+ " Please try again with a different name.");
258
+ }
259
+ async _getProject(firstExample) {
260
+ let project;
261
+ if (!this._experiment) {
262
+ try {
263
+ const projectMetadata = await this._getExperimentMetadata();
264
+ project = await this._createProject(firstExample, projectMetadata);
242
265
  this._experiment = project;
243
266
  }
244
267
  catch (e) {
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.43";
9
+ exports.__version__ = "0.1.45";
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { Client } from "./client.js";
1
+ export { Client, type ClientConfig } from "./client.js";
2
2
  export type { Dataset, Example, TracerSession, Run, Feedback, RetrieverOutput, } from "./schemas.js";
3
3
  export { RunTree, type RunTreeConfig } from "./run_trees.js";
4
- export declare const __version__ = "0.1.43";
4
+ export declare const __version__ = "0.1.45";
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.43";
4
+ export const __version__ = "0.1.45";