langsmith 0.1.39 → 0.1.40

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
@@ -31,6 +31,7 @@ const env_js_1 = require("./utils/env.cjs");
31
31
  const index_js_1 = require("./index.cjs");
32
32
  const _uuid_js_1 = require("./utils/_uuid.cjs");
33
33
  const warn_js_1 = require("./utils/warn.cjs");
34
+ const prompts_js_1 = require("./utils/prompts.cjs");
34
35
  async function mergeRuntimeEnvIntoRunCreates(runs) {
35
36
  const runtimeEnv = await (0, env_js_1.getRuntimeEnvironment)();
36
37
  const envVars = (0, env_js_1.getLangChainEnvVarsMetadata)();
@@ -268,6 +269,12 @@ class Client {
268
269
  writable: true,
269
270
  value: void 0
270
271
  });
272
+ Object.defineProperty(this, "settings", {
273
+ enumerable: true,
274
+ configurable: true,
275
+ writable: true,
276
+ value: void 0
277
+ });
271
278
  const defaultConfig = Client.getDefaultClientConfig();
272
279
  this.tracingSampleRate = getTracingSamplingRate();
273
280
  this.apiUrl = trimQuotes(config.apiUrl ?? defaultConfig.apiUrl) ?? "";
@@ -389,7 +396,7 @@ class Client {
389
396
  const response = await this._getResponse(path, queryParams);
390
397
  return response.json();
391
398
  }
392
- async *_getPaginated(path, queryParams = new URLSearchParams()) {
399
+ async *_getPaginated(path, queryParams = new URLSearchParams(), transform) {
393
400
  let offset = Number(queryParams.get("offset")) || 0;
394
401
  const limit = Number(queryParams.get("limit")) || 100;
395
402
  while (true) {
@@ -405,7 +412,9 @@ class Client {
405
412
  if (!response.ok) {
406
413
  throw new Error(`Failed to fetch ${path}: ${response.status} ${response.statusText}`);
407
414
  }
408
- const items = await response.json();
415
+ const items = transform
416
+ ? transform(await response.json())
417
+ : await response.json();
409
418
  if (items.length === 0) {
410
419
  break;
411
420
  }
@@ -536,6 +545,12 @@ class Client {
536
545
  }
537
546
  return true;
538
547
  }
548
+ async _getSettings() {
549
+ if (!this.settings) {
550
+ this.settings = this._get("/settings");
551
+ }
552
+ return await this.settings;
553
+ }
539
554
  async createRun(run) {
540
555
  if (!this._filterForSampling([run]).length) {
541
556
  return;
@@ -2035,5 +2050,307 @@ class Client {
2035
2050
  const [results] = await this._logEvaluationFeedback(evaluatorResponse, run, sourceInfo);
2036
2051
  return results;
2037
2052
  }
2053
+ async _currentTenantIsOwner(owner) {
2054
+ const settings = await this._getSettings();
2055
+ return owner == "-" || settings.tenant_handle === owner;
2056
+ }
2057
+ async _ownerConflictError(action, owner) {
2058
+ const settings = await this._getSettings();
2059
+ return new Error(`Cannot ${action} for another tenant.\n
2060
+ Current tenant: ${settings.tenant_handle}\n
2061
+ Requested tenant: ${owner}`);
2062
+ }
2063
+ async _getLatestCommitHash(promptOwnerAndName) {
2064
+ const res = await this.caller.call(fetch, `${this.apiUrl}/commits/${promptOwnerAndName}/?limit=${1}&offset=${0}`, {
2065
+ method: "GET",
2066
+ headers: this.headers,
2067
+ signal: AbortSignal.timeout(this.timeout_ms),
2068
+ ...this.fetchOptions,
2069
+ });
2070
+ const json = await res.json();
2071
+ if (!res.ok) {
2072
+ const detail = typeof json.detail === "string"
2073
+ ? json.detail
2074
+ : JSON.stringify(json.detail);
2075
+ const error = new Error(`Error ${res.status}: ${res.statusText}\n${detail}`);
2076
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
2077
+ error.statusCode = res.status;
2078
+ throw error;
2079
+ }
2080
+ if (json.commits.length === 0) {
2081
+ return undefined;
2082
+ }
2083
+ return json.commits[0].commit_hash;
2084
+ }
2085
+ async _likeOrUnlikePrompt(promptIdentifier, like) {
2086
+ const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
2087
+ const response = await this.caller.call(fetch, `${this.apiUrl}/likes/${owner}/${promptName}`, {
2088
+ method: "POST",
2089
+ body: JSON.stringify({ like: like }),
2090
+ headers: { ...this.headers, "Content-Type": "application/json" },
2091
+ signal: AbortSignal.timeout(this.timeout_ms),
2092
+ ...this.fetchOptions,
2093
+ });
2094
+ if (!response.ok) {
2095
+ throw new Error(`Failed to ${like ? "like" : "unlike"} prompt: ${response.status} ${await response.text()}`);
2096
+ }
2097
+ return await response.json();
2098
+ }
2099
+ async _getPromptUrl(promptIdentifier) {
2100
+ const [owner, promptName, commitHash] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
2101
+ if (!(await this._currentTenantIsOwner(owner))) {
2102
+ if (commitHash !== "latest") {
2103
+ return `${this.getHostUrl()}/hub/${owner}/${promptName}/${commitHash.substring(0, 8)}`;
2104
+ }
2105
+ else {
2106
+ return `${this.getHostUrl()}/hub/${owner}/${promptName}`;
2107
+ }
2108
+ }
2109
+ else {
2110
+ const settings = await this._getSettings();
2111
+ if (commitHash !== "latest") {
2112
+ return `${this.getHostUrl()}/prompts/${promptName}/${commitHash.substring(0, 8)}?organizationId=${settings.id}`;
2113
+ }
2114
+ else {
2115
+ return `${this.getHostUrl()}/prompts/${promptName}?organizationId=${settings.id}`;
2116
+ }
2117
+ }
2118
+ }
2119
+ async promptExists(promptIdentifier) {
2120
+ const prompt = await this.getPrompt(promptIdentifier);
2121
+ return !!prompt;
2122
+ }
2123
+ async likePrompt(promptIdentifier) {
2124
+ return this._likeOrUnlikePrompt(promptIdentifier, true);
2125
+ }
2126
+ async unlikePrompt(promptIdentifier) {
2127
+ return this._likeOrUnlikePrompt(promptIdentifier, false);
2128
+ }
2129
+ async *listCommits(promptOwnerAndName) {
2130
+ for await (const commits of this._getPaginated(`/commits/${promptOwnerAndName}/`, {}, (res) => res.commits)) {
2131
+ yield* commits;
2132
+ }
2133
+ }
2134
+ async *listPrompts(options) {
2135
+ const params = new URLSearchParams();
2136
+ params.append("sort_field", options?.sortField ?? "updated_at");
2137
+ params.append("sort_direction", "desc");
2138
+ params.append("is_archived", (!!options?.isArchived).toString());
2139
+ if (options?.isPublic !== undefined) {
2140
+ params.append("is_public", options.isPublic.toString());
2141
+ }
2142
+ if (options?.query) {
2143
+ params.append("query", options.query);
2144
+ }
2145
+ for await (const prompts of this._getPaginated("/repos", params, (res) => res.repos)) {
2146
+ yield* prompts;
2147
+ }
2148
+ }
2149
+ async getPrompt(promptIdentifier) {
2150
+ const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
2151
+ const response = await this.caller.call(fetch, `${this.apiUrl}/repos/${owner}/${promptName}`, {
2152
+ method: "GET",
2153
+ headers: this.headers,
2154
+ signal: AbortSignal.timeout(this.timeout_ms),
2155
+ ...this.fetchOptions,
2156
+ });
2157
+ if (response.status === 404) {
2158
+ return null;
2159
+ }
2160
+ if (!response.ok) {
2161
+ throw new Error(`Failed to get prompt: ${response.status} ${await response.text()}`);
2162
+ }
2163
+ const result = await response.json();
2164
+ if (result.repo) {
2165
+ return result.repo;
2166
+ }
2167
+ else {
2168
+ return null;
2169
+ }
2170
+ }
2171
+ async createPrompt(promptIdentifier, options) {
2172
+ const settings = await this._getSettings();
2173
+ if (options?.isPublic && !settings.tenant_handle) {
2174
+ throw new Error(`Cannot create a public prompt without first\n
2175
+ creating a LangChain Hub handle.
2176
+ You can add a handle by creating a public prompt at:\n
2177
+ https://smith.langchain.com/prompts`);
2178
+ }
2179
+ const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
2180
+ if (!(await this._currentTenantIsOwner(owner))) {
2181
+ throw await this._ownerConflictError("create a prompt", owner);
2182
+ }
2183
+ const data = {
2184
+ repo_handle: promptName,
2185
+ ...(options?.description && { description: options.description }),
2186
+ ...(options?.readme && { readme: options.readme }),
2187
+ ...(options?.tags && { tags: options.tags }),
2188
+ is_public: !!options?.isPublic,
2189
+ };
2190
+ const response = await this.caller.call(fetch, `${this.apiUrl}/repos/`, {
2191
+ method: "POST",
2192
+ headers: { ...this.headers, "Content-Type": "application/json" },
2193
+ body: JSON.stringify(data),
2194
+ signal: AbortSignal.timeout(this.timeout_ms),
2195
+ ...this.fetchOptions,
2196
+ });
2197
+ if (!response.ok) {
2198
+ throw new Error(`Failed to create prompt: ${response.status} ${await response.text()}`);
2199
+ }
2200
+ const { repo } = await response.json();
2201
+ return repo;
2202
+ }
2203
+ async createCommit(promptIdentifier, object, options) {
2204
+ if (!(await this.promptExists(promptIdentifier))) {
2205
+ throw new Error("Prompt does not exist, you must create it first.");
2206
+ }
2207
+ const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
2208
+ const resolvedParentCommitHash = options?.parentCommitHash === "latest" || !options?.parentCommitHash
2209
+ ? await this._getLatestCommitHash(`${owner}/${promptName}`)
2210
+ : options?.parentCommitHash;
2211
+ const payload = {
2212
+ manifest: JSON.parse(JSON.stringify(object)),
2213
+ parent_commit: resolvedParentCommitHash,
2214
+ };
2215
+ const response = await this.caller.call(fetch, `${this.apiUrl}/commits/${owner}/${promptName}`, {
2216
+ method: "POST",
2217
+ headers: { ...this.headers, "Content-Type": "application/json" },
2218
+ body: JSON.stringify(payload),
2219
+ signal: AbortSignal.timeout(this.timeout_ms),
2220
+ ...this.fetchOptions,
2221
+ });
2222
+ if (!response.ok) {
2223
+ throw new Error(`Failed to create commit: ${response.status} ${await response.text()}`);
2224
+ }
2225
+ const result = await response.json();
2226
+ return this._getPromptUrl(`${owner}/${promptName}${result.commit_hash ? `:${result.commit_hash}` : ""}`);
2227
+ }
2228
+ async updatePrompt(promptIdentifier, options) {
2229
+ if (!(await this.promptExists(promptIdentifier))) {
2230
+ throw new Error("Prompt does not exist, you must create it first.");
2231
+ }
2232
+ const [owner, promptName] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
2233
+ if (!(await this._currentTenantIsOwner(owner))) {
2234
+ throw await this._ownerConflictError("update a prompt", owner);
2235
+ }
2236
+ const payload = {};
2237
+ if (options?.description !== undefined)
2238
+ payload.description = options.description;
2239
+ if (options?.readme !== undefined)
2240
+ payload.readme = options.readme;
2241
+ if (options?.tags !== undefined)
2242
+ payload.tags = options.tags;
2243
+ if (options?.isPublic !== undefined)
2244
+ payload.is_public = options.isPublic;
2245
+ if (options?.isArchived !== undefined)
2246
+ payload.is_archived = options.isArchived;
2247
+ // Check if payload is empty
2248
+ if (Object.keys(payload).length === 0) {
2249
+ throw new Error("No valid update options provided");
2250
+ }
2251
+ const response = await this.caller.call(fetch, `${this.apiUrl}/repos/${owner}/${promptName}`, {
2252
+ method: "PATCH",
2253
+ body: JSON.stringify(payload),
2254
+ headers: {
2255
+ ...this.headers,
2256
+ "Content-Type": "application/json",
2257
+ },
2258
+ signal: AbortSignal.timeout(this.timeout_ms),
2259
+ ...this.fetchOptions,
2260
+ });
2261
+ if (!response.ok) {
2262
+ throw new Error(`HTTP Error: ${response.status} - ${await response.text()}`);
2263
+ }
2264
+ return response.json();
2265
+ }
2266
+ async deletePrompt(promptIdentifier) {
2267
+ if (!(await this.promptExists(promptIdentifier))) {
2268
+ throw new Error("Prompt does not exist, you must create it first.");
2269
+ }
2270
+ const [owner, promptName, _] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
2271
+ if (!(await this._currentTenantIsOwner(owner))) {
2272
+ throw await this._ownerConflictError("delete a prompt", owner);
2273
+ }
2274
+ const response = await this.caller.call(fetch, `${this.apiUrl}/repos/${owner}/${promptName}`, {
2275
+ method: "DELETE",
2276
+ headers: this.headers,
2277
+ signal: AbortSignal.timeout(this.timeout_ms),
2278
+ ...this.fetchOptions,
2279
+ });
2280
+ return await response.json();
2281
+ }
2282
+ async pullPromptCommit(promptIdentifier, options) {
2283
+ const [owner, promptName, commitHash] = (0, prompts_js_1.parsePromptIdentifier)(promptIdentifier);
2284
+ const serverInfo = await this._getServerInfo();
2285
+ const useOptimization = (0, prompts_js_1.isVersionGreaterOrEqual)(serverInfo.version, "0.5.23");
2286
+ let passedCommitHash = commitHash;
2287
+ if (!useOptimization && commitHash === "latest") {
2288
+ const latestCommitHash = await this._getLatestCommitHash(`${owner}/${promptName}`);
2289
+ if (!latestCommitHash) {
2290
+ throw new Error("No commits found");
2291
+ }
2292
+ else {
2293
+ passedCommitHash = latestCommitHash;
2294
+ }
2295
+ }
2296
+ const response = await this.caller.call(fetch, `${this.apiUrl}/commits/${owner}/${promptName}/${passedCommitHash}${options?.includeModel ? "?include_model=true" : ""}`, {
2297
+ method: "GET",
2298
+ headers: this.headers,
2299
+ signal: AbortSignal.timeout(this.timeout_ms),
2300
+ ...this.fetchOptions,
2301
+ });
2302
+ if (!response.ok) {
2303
+ throw new Error(`Failed to pull prompt commit: ${response.status} ${response.statusText}`);
2304
+ }
2305
+ const result = await response.json();
2306
+ return {
2307
+ owner,
2308
+ repo: promptName,
2309
+ commit_hash: result.commit_hash,
2310
+ manifest: result.manifest,
2311
+ examples: result.examples,
2312
+ };
2313
+ }
2314
+ /**
2315
+ *
2316
+ * This method should not be used directly, use `import { pull } from "langchain/hub"` instead.
2317
+ * Using this method directly returns the JSON string of the prompt rather than a LangChain object.
2318
+ * @private
2319
+ *
2320
+ */
2321
+ async _pullPrompt(promptIdentifier, options) {
2322
+ const promptObject = await this.pullPromptCommit(promptIdentifier, {
2323
+ includeModel: options?.includeModel,
2324
+ });
2325
+ const prompt = JSON.stringify(promptObject.manifest);
2326
+ return prompt;
2327
+ }
2328
+ async pushPrompt(promptIdentifier, options) {
2329
+ // Create or update prompt metadata
2330
+ if (await this.promptExists(promptIdentifier)) {
2331
+ await this.updatePrompt(promptIdentifier, {
2332
+ description: options?.description,
2333
+ readme: options?.readme,
2334
+ tags: options?.tags,
2335
+ isPublic: options?.isPublic,
2336
+ });
2337
+ }
2338
+ else {
2339
+ await this.createPrompt(promptIdentifier, {
2340
+ description: options?.description,
2341
+ readme: options?.readme,
2342
+ tags: options?.tags,
2343
+ isPublic: options?.isPublic,
2344
+ });
2345
+ }
2346
+ if (!options?.object) {
2347
+ return await this._getPromptUrl(promptIdentifier);
2348
+ }
2349
+ // Create a commit with the new manifest
2350
+ const url = await this.createCommit(promptIdentifier, options?.object, {
2351
+ parentCommitHash: options?.parentCommitHash,
2352
+ });
2353
+ return url;
2354
+ }
2038
2355
  }
2039
2356
  exports.Client = Client;
package/dist/client.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { AsyncCallerParams } from "./utils/async_caller.js";
2
- import { ComparativeExperiment, DataType, Dataset, DatasetDiffInfo, DatasetShareSchema, Example, ExampleUpdate, ExampleUpdateWithId, Feedback, FeedbackConfig, FeedbackIngestToken, KVMap, LangChainBaseMessage, Run, RunCreate, RunUpdate, ScoreType, TimeDelta, TracerSession, TracerSessionResult, ValueType } from "./schemas.js";
2
+ import { ComparativeExperiment, DataType, Dataset, DatasetDiffInfo, DatasetShareSchema, Example, ExampleUpdate, ExampleUpdateWithId, Feedback, FeedbackConfig, FeedbackIngestToken, KVMap, LangChainBaseMessage, LangSmithSettings, LikePromptResponse, Prompt, PromptCommit, PromptSortField, Run, RunCreate, RunUpdate, ScoreType, TimeDelta, TracerSession, TracerSessionResult, ValueType } from "./schemas.js";
3
3
  import { EvaluationResult, EvaluationResults, RunEvaluator } from "./evaluation/evaluator.js";
4
4
  interface ClientConfig {
5
5
  apiUrl?: string;
@@ -176,6 +176,7 @@ export declare class Client {
176
176
  private autoBatchAggregationDelayMs;
177
177
  private serverInfo;
178
178
  private fetchOptions;
179
+ private settings;
179
180
  constructor(config?: ClientConfig);
180
181
  static getDefaultClientConfig(): {
181
182
  apiUrl: string;
@@ -198,6 +199,7 @@ export declare class Client {
198
199
  private processRunOperation;
199
200
  protected _getServerInfo(): Promise<any>;
200
201
  protected batchEndpointIsSupported(): Promise<boolean>;
202
+ protected _getSettings(): Promise<LangSmithSettings>;
201
203
  createRun(run: CreateRunParams): Promise<void>;
202
204
  /**
203
205
  * Batch ingest/upsert multiple runs in the Langsmith system.
@@ -537,5 +539,59 @@ export declare class Client {
537
539
  logEvaluationFeedback(evaluatorResponse: EvaluationResult | EvaluationResults, run?: Run, sourceInfo?: {
538
540
  [key: string]: any;
539
541
  }): Promise<EvaluationResult[]>;
542
+ protected _currentTenantIsOwner(owner: string): Promise<boolean>;
543
+ protected _ownerConflictError(action: string, owner: string): Promise<Error>;
544
+ protected _getLatestCommitHash(promptOwnerAndName: string): Promise<string | undefined>;
545
+ protected _likeOrUnlikePrompt(promptIdentifier: string, like: boolean): Promise<LikePromptResponse>;
546
+ protected _getPromptUrl(promptIdentifier: string): Promise<string>;
547
+ promptExists(promptIdentifier: string): Promise<boolean>;
548
+ likePrompt(promptIdentifier: string): Promise<LikePromptResponse>;
549
+ unlikePrompt(promptIdentifier: string): Promise<LikePromptResponse>;
550
+ listCommits(promptOwnerAndName: string): AsyncIterableIterator<PromptCommit>;
551
+ listPrompts(options?: {
552
+ isPublic?: boolean;
553
+ isArchived?: boolean;
554
+ sortField?: PromptSortField;
555
+ query?: string;
556
+ }): AsyncIterableIterator<Prompt>;
557
+ getPrompt(promptIdentifier: string): Promise<Prompt | null>;
558
+ createPrompt(promptIdentifier: string, options?: {
559
+ description?: string;
560
+ readme?: string;
561
+ tags?: string[];
562
+ isPublic?: boolean;
563
+ }): Promise<Prompt>;
564
+ createCommit(promptIdentifier: string, object: any, options?: {
565
+ parentCommitHash?: string;
566
+ }): Promise<string>;
567
+ updatePrompt(promptIdentifier: string, options?: {
568
+ description?: string;
569
+ readme?: string;
570
+ tags?: string[];
571
+ isPublic?: boolean;
572
+ isArchived?: boolean;
573
+ }): Promise<Record<string, any>>;
574
+ deletePrompt(promptIdentifier: string): Promise<void>;
575
+ pullPromptCommit(promptIdentifier: string, options?: {
576
+ includeModel?: boolean;
577
+ }): Promise<PromptCommit>;
578
+ /**
579
+ *
580
+ * This method should not be used directly, use `import { pull } from "langchain/hub"` instead.
581
+ * Using this method directly returns the JSON string of the prompt rather than a LangChain object.
582
+ * @private
583
+ *
584
+ */
585
+ _pullPrompt(promptIdentifier: string, options?: {
586
+ includeModel?: boolean;
587
+ }): Promise<any>;
588
+ pushPrompt(promptIdentifier: string, options?: {
589
+ object?: any;
590
+ parentCommitHash?: string;
591
+ isPublic?: boolean;
592
+ description?: string;
593
+ readme?: string;
594
+ tags?: string[];
595
+ }): Promise<string>;
540
596
  }
541
597
  export {};