langsmith 0.5.22 → 0.5.23

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
@@ -189,7 +189,19 @@ class AutoBatchQueue {
189
189
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise
190
190
  itemPromiseResolve = resolve;
191
191
  });
192
- const size = (0, index_js_3.serialize)(item.item, `Serializing run with id: ${item.item.id}`).length;
192
+ // By default we compute the exact serialized size here by stringifying
193
+ // the payload. This is expensive: JSON.stringify on large payloads
194
+ // blocks the event loop on the user's hot path.
195
+ //
196
+ // Opting into LANGSMITH_PERF_OPTIMIZATION=true switches to a cheap
197
+ // structural estimate instead. The estimate is only used for soft
198
+ // memory accounting (queue size limit and downstream async caller
199
+ // memory tracking), never for anything correctness-critical -- the
200
+ // real serialization still happens later, off the hot path, when the
201
+ // batch is assembled for sending.
202
+ const size = (0, env_js_1.getLangSmithEnvironmentVariable)("PERF_OPTIMIZATION") === "true"
203
+ ? (0, index_js_3.estimateSerializedSize)(item.item)
204
+ : (0, index_js_3.serialize)(item.item, `Serializing run with id: ${item.item.id}`).length;
193
205
  // Check if adding this item would exceed the size limit
194
206
  // Allow the run if the queue is empty (to support large single traces)
195
207
  if (this.sizeBytes + size > this.maxSizeBytes && this.items.length > 0) {
@@ -3821,7 +3833,7 @@ class Client {
3821
3833
  return json.commits[0].commit_hash;
3822
3834
  }
3823
3835
  async _likeOrUnlikePrompt(promptIdentifier, like) {
3824
- const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
3836
+ const [owner, promptName, _] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
3825
3837
  const body = JSON.stringify({ like: like });
3826
3838
  const response = await this.caller.call(async () => {
3827
3839
  const res = await this._fetch(`${this.apiUrl}/likes/${owner}/${promptName}`, {
@@ -3840,7 +3852,7 @@ class Client {
3840
3852
  return response.json();
3841
3853
  }
3842
3854
  async _getPromptUrl(promptIdentifier) {
3843
- const [owner, promptName, commitHash] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
3855
+ const [owner, promptName, commitHash] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
3844
3856
  if (!(await this._currentTenantIsOwner(owner))) {
3845
3857
  if (commitHash !== "latest") {
3846
3858
  return `${this.getHostUrl()}/hub/${owner}/${promptName}/${commitHash.substring(0, 8)}`;
@@ -3932,7 +3944,7 @@ class Client {
3932
3944
  * ```
3933
3945
  */
3934
3946
  async *listCommits(promptIdentifier) {
3935
- const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
3947
+ const [owner, promptName, _] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
3936
3948
  for await (const commits of this._getPaginated(`/commits/${owner}/${promptName}/`, new URLSearchParams(), (res) => res.commits)) {
3937
3949
  yield* commits;
3938
3950
  }
@@ -3995,7 +4007,7 @@ class Client {
3995
4007
  * ```
3996
4008
  */
3997
4009
  async getPrompt(promptIdentifier) {
3998
- const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
4010
+ const [owner, promptName, _] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
3999
4011
  const response = await this.caller.call(async () => {
4000
4012
  const res = await this._fetch(`${this.apiUrl}/repos/${owner}/${promptName}`, {
4001
4013
  method: "GET",
@@ -4052,7 +4064,7 @@ class Client {
4052
4064
  You can add a handle by creating a public prompt at:\n
4053
4065
  https://smith.langchain.com/prompts`);
4054
4066
  }
4055
- const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
4067
+ const [owner, promptName, _] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
4056
4068
  if (!(await this._currentTenantIsOwner(owner))) {
4057
4069
  throw await this._ownerConflictError("create a prompt", owner);
4058
4070
  }
@@ -4111,7 +4123,7 @@ class Client {
4111
4123
  if (!(await this.promptExists(promptIdentifier))) {
4112
4124
  throw new Error("Prompt does not exist, you must create it first.");
4113
4125
  }
4114
- const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
4126
+ const [owner, promptName, _] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
4115
4127
  const resolvedParentCommitHash = options?.parentCommitHash === "latest" || !options?.parentCommitHash
4116
4128
  ? await this._getLatestCommitHash(`${owner}/${promptName}`)
4117
4129
  : options?.parentCommitHash;
@@ -4309,7 +4321,7 @@ class Client {
4309
4321
  if (!(await this.promptExists(promptIdentifier))) {
4310
4322
  throw new Error("Prompt does not exist, you must create it first.");
4311
4323
  }
4312
- const [owner, promptName] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
4324
+ const [owner, promptName] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
4313
4325
  if (!(await this._currentTenantIsOwner(owner))) {
4314
4326
  throw await this._ownerConflictError("update a prompt", owner);
4315
4327
  }
@@ -4349,7 +4361,7 @@ class Client {
4349
4361
  if (!(await this.promptExists(promptIdentifier))) {
4350
4362
  throw new Error("Prompt does not exist, you must create it first.");
4351
4363
  }
4352
- const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
4364
+ const [owner, promptName, _] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
4353
4365
  if (!(await this._currentTenantIsOwner(owner))) {
4354
4366
  throw await this._ownerConflictError("delete a prompt", owner);
4355
4367
  }
@@ -4377,7 +4389,7 @@ class Client {
4377
4389
  * Fetch a prompt commit directly from the API (bypassing cache).
4378
4390
  */
4379
4391
  async _fetchPromptFromApi(promptIdentifier, options) {
4380
- const [owner, promptName, commitHash] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
4392
+ const [owner, promptName, commitHash] = (0, prompts_js_1.parseHubIdentifier)(promptIdentifier);
4381
4393
  const response = await this.caller.call(async () => {
4382
4394
  const res = await this._fetch(`${this.apiUrl}/commits/${owner}/${promptName}/${commitHash}${options?.includeModel ? "?include_model=true" : ""}`, {
4383
4395
  method: "GET",
@@ -4457,6 +4469,251 @@ class Client {
4457
4469
  });
4458
4470
  return url;
4459
4471
  }
4472
+ /**
4473
+ * Check if an agent repo exists.
4474
+ */
4475
+ async agentExists(identifier) {
4476
+ const [owner, name] = (0, prompts_js_1.parseHubIdentifier)(identifier);
4477
+ return this._repoExists(owner, name);
4478
+ }
4479
+ /**
4480
+ * Check if a skill repo exists.
4481
+ */
4482
+ async skillExists(identifier) {
4483
+ const [owner, name] = (0, prompts_js_1.parseHubIdentifier)(identifier);
4484
+ return this._repoExists(owner, name);
4485
+ }
4486
+ /**
4487
+ * Pull an agent directory from Hub.
4488
+ * @param identifier The identifier (owner/name[:version]).
4489
+ * @param options.version Commit hash or tag; overrides identifier's version.
4490
+ */
4491
+ async pullAgent(identifier, options) {
4492
+ return (await this._pullDirectory(identifier, "agent", options?.version));
4493
+ }
4494
+ /**
4495
+ * Pull a skill directory from Hub.
4496
+ */
4497
+ async pullSkill(identifier, options) {
4498
+ return (await this._pullDirectory(identifier, "skill", options?.version));
4499
+ }
4500
+ /**
4501
+ * Push an agent to Hub. Creates the repo if missing, patches metadata if
4502
+ * provided, then commits the given files.
4503
+ * @returns The URL of the resulting commit.
4504
+ */
4505
+ async pushAgent(identifier, options) {
4506
+ return this._pushDirectory(identifier, "agent", options);
4507
+ }
4508
+ /**
4509
+ * Push a skill to Hub.
4510
+ */
4511
+ async pushSkill(identifier, options) {
4512
+ return this._pushDirectory(identifier, "skill", options);
4513
+ }
4514
+ /**
4515
+ * Delete an agent and all its owned child file repos.
4516
+ */
4517
+ async deleteAgent(identifier) {
4518
+ return this._deleteDirectory(identifier);
4519
+ }
4520
+ /**
4521
+ * Delete a skill and all its owned child file repos.
4522
+ */
4523
+ async deleteSkill(identifier) {
4524
+ return this._deleteDirectory(identifier);
4525
+ }
4526
+ /**
4527
+ * List agent repos. Yields one at a time, auto-paginating.
4528
+ */
4529
+ async *listAgents(options) {
4530
+ yield* this._listReposByType("agent", options);
4531
+ }
4532
+ /**
4533
+ * List skill repos. Yields one at a time, auto-paginating.
4534
+ */
4535
+ async *listSkills(options) {
4536
+ yield* this._listReposByType("skill", options);
4537
+ }
4538
+ async *_listReposByType(repoType, options) {
4539
+ const params = new URLSearchParams();
4540
+ params.append("repo_type", repoType);
4541
+ params.append("is_archived", (!!options?.isArchived).toString());
4542
+ if (options?.isPublic !== undefined) {
4543
+ params.append("is_public", options.isPublic.toString());
4544
+ }
4545
+ if (options?.query) {
4546
+ params.append("query", options.query);
4547
+ }
4548
+ for await (const repos of this._getPaginated("/repos", params, (res) => res.repos)) {
4549
+ yield* repos;
4550
+ }
4551
+ }
4552
+ async _pullDirectory(identifier, repoType, version) {
4553
+ const [owner, name, parsedVersion] = (0, prompts_js_1.parseHubIdentifier)(identifier);
4554
+ const resolvedVersion = version ?? (parsedVersion !== "latest" ? parsedVersion : undefined);
4555
+ const url = new URL(`${this.apiUrl}/v1/platform/hub/repos/${owner}/${name}/directories`);
4556
+ url.searchParams.set("repo_type", repoType);
4557
+ if (resolvedVersion) {
4558
+ url.searchParams.set("commit", resolvedVersion);
4559
+ }
4560
+ const response = await this.caller.call(async () => {
4561
+ const res = await this._fetch(url.toString(), {
4562
+ method: "GET",
4563
+ headers: this._mergedHeaders,
4564
+ signal: AbortSignal.timeout(this.timeout_ms),
4565
+ ...this.fetchOptions,
4566
+ });
4567
+ await (0, error_js_1.raiseForStatus)(res, "pull directory");
4568
+ return res;
4569
+ });
4570
+ return (await response.json());
4571
+ }
4572
+ async _pushDirectory(identifier, repoType, options) {
4573
+ if (options.parentCommit !== undefined &&
4574
+ (options.parentCommit.length < 8 || options.parentCommit.length > 64)) {
4575
+ throw new Error("parent_commit must be 8-64 characters");
4576
+ }
4577
+ const [owner, name] = (0, prompts_js_1.parseHubIdentifier)(identifier);
4578
+ if (!(await this._currentTenantIsOwner(owner))) {
4579
+ throw await this._ownerConflictError(`push ${repoType}`, owner);
4580
+ }
4581
+ if (await this._repoExists(owner, name)) {
4582
+ if (options.description !== undefined ||
4583
+ options.readme !== undefined ||
4584
+ options.tags !== undefined ||
4585
+ options.isPublic !== undefined) {
4586
+ await this._updateRepoMetadata(owner, name, options);
4587
+ }
4588
+ }
4589
+ else {
4590
+ const REPO_HANDLE_PATTERN = /^[a-z][a-z0-9-_]*$/;
4591
+ if (!REPO_HANDLE_PATTERN.test(name)) {
4592
+ throw new Error(`Invalid repo_handle ${JSON.stringify(name)}: must match ${REPO_HANDLE_PATTERN}`);
4593
+ }
4594
+ await this._createRepo(name, repoType, options);
4595
+ }
4596
+ const body = { files: options.files };
4597
+ if (options.parentCommit) {
4598
+ body.parent_commit = options.parentCommit;
4599
+ }
4600
+ const response = await this.caller.call(async () => {
4601
+ const res = await this._fetch(`${this.apiUrl}/v1/platform/hub/repos/${owner}/${name}/directories/commits`, {
4602
+ method: "POST",
4603
+ headers: {
4604
+ ...this._mergedHeaders,
4605
+ "Content-Type": "application/json",
4606
+ },
4607
+ signal: AbortSignal.timeout(this.timeout_ms),
4608
+ ...this.fetchOptions,
4609
+ body: JSON.stringify(body),
4610
+ });
4611
+ await (0, error_js_1.raiseForStatus)(res, `push ${repoType}`);
4612
+ return res;
4613
+ });
4614
+ const data = (await response.json());
4615
+ const commitHash = data.commit.commit_hash;
4616
+ return `${this.getHostUrl()}/hub/${owner}/${name}:${commitHash.slice(0, 8)}`;
4617
+ }
4618
+ async _deleteDirectory(identifier) {
4619
+ const [owner, name] = (0, prompts_js_1.parseHubIdentifier)(identifier);
4620
+ if (!(await this._currentTenantIsOwner(owner))) {
4621
+ throw await this._ownerConflictError("delete", owner);
4622
+ }
4623
+ await this.caller.call(async () => {
4624
+ const res = await this._fetch(`${this.apiUrl}/v1/platform/hub/repos/${owner}/${name}/directories`, {
4625
+ method: "DELETE",
4626
+ headers: this._mergedHeaders,
4627
+ signal: AbortSignal.timeout(this.timeout_ms),
4628
+ ...this.fetchOptions,
4629
+ });
4630
+ await (0, error_js_1.raiseForStatus)(res, "delete directory");
4631
+ return res;
4632
+ });
4633
+ }
4634
+ async _repoExists(owner, name) {
4635
+ try {
4636
+ await this.caller.call(async () => {
4637
+ const res = await this._fetch(`${this.apiUrl}/repos/${owner}/${name}`, {
4638
+ method: "GET",
4639
+ headers: this._mergedHeaders,
4640
+ signal: AbortSignal.timeout(this.timeout_ms),
4641
+ ...this.fetchOptions,
4642
+ });
4643
+ await (0, error_js_1.raiseForStatus)(res, "check repo exists");
4644
+ return res;
4645
+ });
4646
+ return true;
4647
+ }
4648
+ catch (e) {
4649
+ if ((0, error_js_1.isLangSmithNotFoundError)(e)) {
4650
+ return false;
4651
+ }
4652
+ throw e;
4653
+ }
4654
+ }
4655
+ async _createRepo(name, repoType, options) {
4656
+ const body = {
4657
+ repo_handle: name,
4658
+ repo_type: repoType,
4659
+ is_public: !!options.isPublic,
4660
+ };
4661
+ if (options.description !== undefined)
4662
+ body.description = options.description;
4663
+ if (options.readme !== undefined)
4664
+ body.readme = options.readme;
4665
+ if (options.tags !== undefined)
4666
+ body.tags = options.tags;
4667
+ try {
4668
+ await this.caller.call(async () => {
4669
+ const res = await this._fetch(`${this.apiUrl}/repos/`, {
4670
+ method: "POST",
4671
+ headers: {
4672
+ ...this._mergedHeaders,
4673
+ "Content-Type": "application/json",
4674
+ },
4675
+ signal: AbortSignal.timeout(this.timeout_ms),
4676
+ ...this.fetchOptions,
4677
+ body: JSON.stringify(body),
4678
+ });
4679
+ await (0, error_js_1.raiseForStatus)(res, `create ${repoType}`);
4680
+ return res;
4681
+ });
4682
+ }
4683
+ catch (e) {
4684
+ if ((0, error_js_1.isLangSmithConflictError)(e)) {
4685
+ return;
4686
+ }
4687
+ throw e;
4688
+ }
4689
+ }
4690
+ async _updateRepoMetadata(owner, name, options) {
4691
+ const body = {};
4692
+ if (options.description !== undefined)
4693
+ body.description = options.description;
4694
+ if (options.readme !== undefined)
4695
+ body.readme = options.readme;
4696
+ if (options.tags !== undefined)
4697
+ body.tags = options.tags;
4698
+ if (options.isPublic !== undefined)
4699
+ body.is_public = options.isPublic;
4700
+ if (Object.keys(body).length === 0)
4701
+ return;
4702
+ await this.caller.call(async () => {
4703
+ const res = await this._fetch(`${this.apiUrl}/repos/${owner}/${name}`, {
4704
+ method: "PATCH",
4705
+ headers: {
4706
+ ...this._mergedHeaders,
4707
+ "Content-Type": "application/json",
4708
+ },
4709
+ signal: AbortSignal.timeout(this.timeout_ms),
4710
+ ...this.fetchOptions,
4711
+ body: JSON.stringify(body),
4712
+ });
4713
+ await (0, error_js_1.raiseForStatus)(res, "update repo metadata");
4714
+ return res;
4715
+ });
4716
+ }
4460
4717
  /**
4461
4718
  * Clone a public dataset to your own langsmith tenant.
4462
4719
  * This operation is idempotent. If you already have a dataset with the given name,
package/dist/client.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { OTELContext } from "./experimental/otel/types.js";
2
2
  import { AsyncCallerParams } from "./utils/async_caller.js";
3
- import { ComparativeExperiment, DataType, Dataset, DatasetDiffInfo, DatasetShareSchema, Example, ExampleCreate, ExampleUpdate, ExampleUpdateWithoutId, Feedback, FeedbackConfig, FeedbackIngestToken, KVMap, LangChainBaseMessage, LangSmithSettings, LikePromptResponse, Prompt, PromptCommit, PromptSortField, Run, RunCreate, RunUpdate, ScoreType, TimeDelta, TracerSession, TracerSessionResult, ValueType, AnnotationQueue, RunWithAnnotationQueueInfo, Attachments, UploadExamplesResponse, UpdateExamplesResponse, DatasetVersion, AnnotationQueueWithDetails, AnnotationQueueRubricItem, FeedbackConfigSchema } from "./schemas.js";
3
+ import { ComparativeExperiment, DataType, Dataset, DatasetDiffInfo, DatasetShareSchema, Example, ExampleCreate, ExampleUpdate, ExampleUpdateWithoutId, Feedback, FeedbackConfig, FeedbackIngestToken, KVMap, LangChainBaseMessage, LangSmithSettings, LikePromptResponse, Prompt, PromptCommit, PromptSortField, Run, RunCreate, RunUpdate, ScoreType, TimeDelta, TracerSession, TracerSessionResult, ValueType, AnnotationQueue, RunWithAnnotationQueueInfo, Attachments, UploadExamplesResponse, UpdateExamplesResponse, DatasetVersion, AnnotationQueueWithDetails, AnnotationQueueRubricItem, FeedbackConfigSchema, AgentContext, SkillContext, Entry } from "./schemas.js";
4
4
  import { EvaluationResult, EvaluationResults } from "./evaluation/evaluator.js";
5
5
  import { PromptCache } from "./utils/prompt_cache/index.js";
6
6
  export interface ClientConfig {
@@ -1306,6 +1306,83 @@ export declare class Client implements LangSmithTracingClientInterface {
1306
1306
  tags?: string[];
1307
1307
  commitDescription?: string;
1308
1308
  }): Promise<string>;
1309
+ /**
1310
+ * Check if an agent repo exists.
1311
+ */
1312
+ agentExists(identifier: string): Promise<boolean>;
1313
+ /**
1314
+ * Check if a skill repo exists.
1315
+ */
1316
+ skillExists(identifier: string): Promise<boolean>;
1317
+ /**
1318
+ * Pull an agent directory from Hub.
1319
+ * @param identifier The identifier (owner/name[:version]).
1320
+ * @param options.version Commit hash or tag; overrides identifier's version.
1321
+ */
1322
+ pullAgent(identifier: string, options?: {
1323
+ version?: string;
1324
+ }): Promise<AgentContext>;
1325
+ /**
1326
+ * Pull a skill directory from Hub.
1327
+ */
1328
+ pullSkill(identifier: string, options?: {
1329
+ version?: string;
1330
+ }): Promise<SkillContext>;
1331
+ /**
1332
+ * Push an agent to Hub. Creates the repo if missing, patches metadata if
1333
+ * provided, then commits the given files.
1334
+ * @returns The URL of the resulting commit.
1335
+ */
1336
+ pushAgent(identifier: string, options: {
1337
+ files: Record<string, Entry | null>;
1338
+ parentCommit?: string;
1339
+ description?: string;
1340
+ readme?: string;
1341
+ tags?: string[];
1342
+ isPublic?: boolean;
1343
+ }): Promise<string>;
1344
+ /**
1345
+ * Push a skill to Hub.
1346
+ */
1347
+ pushSkill(identifier: string, options: {
1348
+ files: Record<string, Entry | null>;
1349
+ parentCommit?: string;
1350
+ description?: string;
1351
+ readme?: string;
1352
+ tags?: string[];
1353
+ isPublic?: boolean;
1354
+ }): Promise<string>;
1355
+ /**
1356
+ * Delete an agent and all its owned child file repos.
1357
+ */
1358
+ deleteAgent(identifier: string): Promise<void>;
1359
+ /**
1360
+ * Delete a skill and all its owned child file repos.
1361
+ */
1362
+ deleteSkill(identifier: string): Promise<void>;
1363
+ /**
1364
+ * List agent repos. Yields one at a time, auto-paginating.
1365
+ */
1366
+ listAgents(options?: {
1367
+ isPublic?: boolean;
1368
+ isArchived?: boolean;
1369
+ query?: string;
1370
+ }): AsyncIterableIterator<Prompt>;
1371
+ /**
1372
+ * List skill repos. Yields one at a time, auto-paginating.
1373
+ */
1374
+ listSkills(options?: {
1375
+ isPublic?: boolean;
1376
+ isArchived?: boolean;
1377
+ query?: string;
1378
+ }): AsyncIterableIterator<Prompt>;
1379
+ private _listReposByType;
1380
+ private _pullDirectory;
1381
+ private _pushDirectory;
1382
+ private _deleteDirectory;
1383
+ private _repoExists;
1384
+ private _createRepo;
1385
+ private _updateRepoMetadata;
1309
1386
  /**
1310
1387
  * Clone a public dataset to your own langsmith tenant.
1311
1388
  * This operation is idempotent. If you already have a dataset with the given name,