langsmith 0.5.2 → 0.5.4

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
@@ -1321,7 +1321,6 @@ class Client {
1321
1321
  async _sendMultipartRequest(parts, context, options) {
1322
1322
  // Create multipart form data boundary
1323
1323
  const boundary = "----LangSmithFormBoundary" + Math.random().toString(36).slice(2);
1324
- const isNodeFetch = (0, fetch_js_1._globalFetchImplementationIsNodeFetch)();
1325
1324
  const buildBuffered = () => this._createNodeFetchBody(parts, boundary);
1326
1325
  const buildStream = () => this._createMultipartStream(parts, boundary);
1327
1326
  const sendWithRetry = async (bodyFactory) => {
@@ -1356,8 +1355,9 @@ class Client {
1356
1355
  try {
1357
1356
  let res;
1358
1357
  let streamedAttempt = false;
1359
- // attempt stream only if not disabled and not using node-fetch or Bun
1360
- if (!isNodeFetch &&
1358
+ const shouldStream = (0, fetch_js_1._shouldStreamForGlobalFetchImplementation)();
1359
+ // attempt stream only if not disabled and not using node-fetch or Bun;
1360
+ if (shouldStream &&
1361
1361
  !this.multipartStreamingDisabled &&
1362
1362
  (0, env_js_1.getEnv)() !== "bun") {
1363
1363
  streamedAttempt = true;
@@ -3229,6 +3229,115 @@ class Client {
3229
3229
  const [results] = await this._logEvaluationFeedback(evaluatorResponse, run, sourceInfo);
3230
3230
  return results;
3231
3231
  }
3232
+ /**
3233
+ * API for managing feedback configs
3234
+ */
3235
+ /**
3236
+ * Create a feedback configuration on the LangSmith API.
3237
+ *
3238
+ * This upserts: if an identical config already exists, it returns it.
3239
+ * If a conflicting config exists for the same key, a 400 error is raised.
3240
+ *
3241
+ * @param options - The options for creating a feedback config
3242
+ * @param options.feedbackKey - The unique key for this feedback config
3243
+ * @param options.feedbackConfig - The config specifying type, bounds, and categories
3244
+ * @param options.isLowerScoreBetter - Whether a lower score is better
3245
+ * @returns The created FeedbackConfigSchema object
3246
+ */
3247
+ async createFeedbackConfig(options) {
3248
+ const { feedbackKey, feedbackConfig, isLowerScoreBetter = false } = options;
3249
+ const body = {
3250
+ feedback_key: feedbackKey,
3251
+ feedback_config: feedbackConfig,
3252
+ is_lower_score_better: isLowerScoreBetter,
3253
+ };
3254
+ const response = await this.caller.call(async () => {
3255
+ const res = await this._fetch(`${this.apiUrl}/feedback-configs`, {
3256
+ method: "POST",
3257
+ headers: { ...this.headers, "Content-Type": "application/json" },
3258
+ signal: AbortSignal.timeout(this.timeout_ms),
3259
+ ...this.fetchOptions,
3260
+ body: JSON.stringify(body),
3261
+ });
3262
+ await (0, error_js_1.raiseForStatus)(res, "create feedback config");
3263
+ return res;
3264
+ });
3265
+ return response.json();
3266
+ }
3267
+ /**
3268
+ * List feedback configurations on the LangSmith API.
3269
+ * @param options - The options for listing feedback configs
3270
+ * @param options.feedbackKeys - Filter by specific feedback keys
3271
+ * @param options.nameContains - Filter by name substring
3272
+ * @param options.limit - The maximum number of configs to return
3273
+ * @returns An async iterator of FeedbackConfigSchema objects
3274
+ */
3275
+ async *listFeedbackConfigs(options = {}) {
3276
+ const { feedbackKeys, nameContains, limit } = options;
3277
+ const params = new URLSearchParams();
3278
+ if (feedbackKeys) {
3279
+ feedbackKeys.forEach((key) => {
3280
+ params.append("key", key);
3281
+ });
3282
+ }
3283
+ if (nameContains)
3284
+ params.append("name_contains", nameContains);
3285
+ params.append("limit", (limit !== undefined ? Math.min(limit, 100) : 100).toString());
3286
+ let count = 0;
3287
+ for await (const configs of this._getPaginated("/feedback-configs", params)) {
3288
+ yield* configs;
3289
+ count += configs.length;
3290
+ if (limit !== undefined && count >= limit)
3291
+ break;
3292
+ }
3293
+ }
3294
+ /**
3295
+ * Update a feedback configuration on the LangSmith API.
3296
+ * @param feedbackKey - The key of the feedback config to update
3297
+ * @param options - The options for updating the feedback config
3298
+ * @param options.feedbackConfig - The new feedback config
3299
+ * @param options.isLowerScoreBetter - Whether a lower score is better
3300
+ * @returns The updated FeedbackConfigSchema object
3301
+ */
3302
+ async updateFeedbackConfig(feedbackKey, options = {}) {
3303
+ const { feedbackConfig, isLowerScoreBetter } = options;
3304
+ const body = { feedback_key: feedbackKey };
3305
+ if (feedbackConfig !== undefined) {
3306
+ body.feedback_config = feedbackConfig;
3307
+ }
3308
+ if (isLowerScoreBetter !== undefined) {
3309
+ body.is_lower_score_better = isLowerScoreBetter;
3310
+ }
3311
+ const response = await this.caller.call(async () => {
3312
+ const res = await this._fetch(`${this.apiUrl}/feedback-configs`, {
3313
+ method: "PATCH",
3314
+ headers: { ...this.headers, "Content-Type": "application/json" },
3315
+ signal: AbortSignal.timeout(this.timeout_ms),
3316
+ ...this.fetchOptions,
3317
+ body: JSON.stringify(body),
3318
+ });
3319
+ await (0, error_js_1.raiseForStatus)(res, "update feedback config");
3320
+ return res;
3321
+ });
3322
+ return response.json();
3323
+ }
3324
+ /**
3325
+ * Delete a feedback configuration on the LangSmith API.
3326
+ * @param feedbackKey - The key of the feedback config to delete
3327
+ */
3328
+ async deleteFeedbackConfig(feedbackKey) {
3329
+ const params = new URLSearchParams({ feedback_key: feedbackKey });
3330
+ await this.caller.call(async () => {
3331
+ const res = await this._fetch(`${this.apiUrl}/feedback-configs?${params}`, {
3332
+ method: "DELETE",
3333
+ headers: this.headers,
3334
+ signal: AbortSignal.timeout(this.timeout_ms),
3335
+ ...this.fetchOptions,
3336
+ });
3337
+ await (0, error_js_1.raiseForStatus)(res, "delete feedback config", true);
3338
+ return res;
3339
+ });
3340
+ }
3232
3341
  /**
3233
3342
  * API for managing annotation queues
3234
3343
  */
@@ -3272,12 +3381,13 @@ class Client {
3272
3381
  * @returns The created AnnotationQueue object
3273
3382
  */
3274
3383
  async createAnnotationQueue(options) {
3275
- const { name, description, queueId, rubricInstructions } = options;
3384
+ const { name, description, queueId, rubricInstructions, rubricItems } = options;
3276
3385
  const body = {
3277
3386
  name,
3278
3387
  description,
3279
3388
  id: queueId || uuid.v4(),
3280
3389
  rubric_instructions: rubricInstructions,
3390
+ rubric_items: rubricItems,
3281
3391
  };
3282
3392
  const serializedBody = JSON.stringify(Object.fromEntries(Object.entries(body).filter(([_, v]) => v !== undefined)));
3283
3393
  const response = await this.caller.call(async () => {
@@ -3319,12 +3429,17 @@ class Client {
3319
3429
  * @param options.description - The new description for the annotation queue
3320
3430
  */
3321
3431
  async updateAnnotationQueue(queueId, options) {
3322
- const { name, description, rubricInstructions } = options;
3323
- const body = JSON.stringify({
3324
- name,
3325
- description,
3326
- rubric_instructions: rubricInstructions,
3327
- });
3432
+ const { name, description, rubricInstructions, rubricItems } = options;
3433
+ const bodyObj = {};
3434
+ if (name !== undefined)
3435
+ bodyObj.name = name;
3436
+ if (description !== undefined)
3437
+ bodyObj.description = description;
3438
+ if (rubricInstructions !== undefined)
3439
+ bodyObj.rubric_instructions = rubricInstructions;
3440
+ if (rubricItems !== undefined)
3441
+ bodyObj.rubric_items = rubricItems;
3442
+ const body = JSON.stringify(bodyObj);
3328
3443
  await this.caller.call(async () => {
3329
3444
  const res = await this._fetch(`${this.apiUrl}/annotation-queues/${(0, _uuid_js_1.assertUuid)(queueId, "queueId")}`, {
3330
3445
  method: "PATCH",
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, ExampleSearch, TimeDelta, TracerSession, TracerSessionResult, ValueType, AnnotationQueue, RunWithAnnotationQueueInfo, Attachments, UploadExamplesResponse, UpdateExamplesResponse, DatasetVersion, AnnotationQueueWithDetails } 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, ExampleSearch, TimeDelta, TracerSession, TracerSessionResult, ValueType, AnnotationQueue, RunWithAnnotationQueueInfo, Attachments, UploadExamplesResponse, UpdateExamplesResponse, DatasetVersion, AnnotationQueueWithDetails, AnnotationQueueRubricItem, FeedbackConfigSchema } 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 {
@@ -906,6 +906,56 @@ export declare class Client implements LangSmithTracingClientInterface {
906
906
  logEvaluationFeedback(evaluatorResponse: EvaluationResult | EvaluationResult[] | EvaluationResults, run?: Run, sourceInfo?: {
907
907
  [key: string]: any;
908
908
  }): Promise<EvaluationResult[]>;
909
+ /**
910
+ * API for managing feedback configs
911
+ */
912
+ /**
913
+ * Create a feedback configuration on the LangSmith API.
914
+ *
915
+ * This upserts: if an identical config already exists, it returns it.
916
+ * If a conflicting config exists for the same key, a 400 error is raised.
917
+ *
918
+ * @param options - The options for creating a feedback config
919
+ * @param options.feedbackKey - The unique key for this feedback config
920
+ * @param options.feedbackConfig - The config specifying type, bounds, and categories
921
+ * @param options.isLowerScoreBetter - Whether a lower score is better
922
+ * @returns The created FeedbackConfigSchema object
923
+ */
924
+ createFeedbackConfig(options: {
925
+ feedbackKey: string;
926
+ feedbackConfig: FeedbackConfig;
927
+ isLowerScoreBetter?: boolean;
928
+ }): Promise<FeedbackConfigSchema>;
929
+ /**
930
+ * List feedback configurations on the LangSmith API.
931
+ * @param options - The options for listing feedback configs
932
+ * @param options.feedbackKeys - Filter by specific feedback keys
933
+ * @param options.nameContains - Filter by name substring
934
+ * @param options.limit - The maximum number of configs to return
935
+ * @returns An async iterator of FeedbackConfigSchema objects
936
+ */
937
+ listFeedbackConfigs(options?: {
938
+ feedbackKeys?: string[];
939
+ nameContains?: string;
940
+ limit?: number;
941
+ }): AsyncIterableIterator<FeedbackConfigSchema>;
942
+ /**
943
+ * Update a feedback configuration on the LangSmith API.
944
+ * @param feedbackKey - The key of the feedback config to update
945
+ * @param options - The options for updating the feedback config
946
+ * @param options.feedbackConfig - The new feedback config
947
+ * @param options.isLowerScoreBetter - Whether a lower score is better
948
+ * @returns The updated FeedbackConfigSchema object
949
+ */
950
+ updateFeedbackConfig(feedbackKey: string, options?: {
951
+ feedbackConfig?: FeedbackConfig;
952
+ isLowerScoreBetter?: boolean;
953
+ }): Promise<FeedbackConfigSchema>;
954
+ /**
955
+ * Delete a feedback configuration on the LangSmith API.
956
+ * @param feedbackKey - The key of the feedback config to delete
957
+ */
958
+ deleteFeedbackConfig(feedbackKey: string): Promise<void>;
909
959
  /**
910
960
  * API for managing annotation queues
911
961
  */
@@ -937,6 +987,7 @@ export declare class Client implements LangSmithTracingClientInterface {
937
987
  description?: string;
938
988
  queueId?: string;
939
989
  rubricInstructions?: string;
990
+ rubricItems?: AnnotationQueueRubricItem[];
940
991
  }): Promise<AnnotationQueueWithDetails>;
941
992
  /**
942
993
  * Read an annotation queue with the specified queue ID.
@@ -952,9 +1003,10 @@ export declare class Client implements LangSmithTracingClientInterface {
952
1003
  * @param options.description - The new description for the annotation queue
953
1004
  */
954
1005
  updateAnnotationQueue(queueId: string, options: {
955
- name: string;
1006
+ name?: string;
956
1007
  description?: string;
957
1008
  rubricInstructions?: string;
1009
+ rubricItems?: AnnotationQueueRubricItem[];
958
1010
  }): Promise<void>;
959
1011
  /**
960
1012
  * Delete an annotation queue with the specified queue ID.
package/dist/client.js CHANGED
@@ -10,7 +10,7 @@ import { warnOnce } from "./utils/warn.js";
10
10
  import { parsePromptIdentifier } from "./utils/prompts.js";
11
11
  import { raiseForStatus, isLangSmithNotFoundError } from "./utils/error.js";
12
12
  import { promptCacheSingleton, } from "./utils/prompt_cache/index.js";
13
- import { _globalFetchImplementationIsNodeFetch, _getFetchImplementation, } from "./singletons/fetch.js";
13
+ import { _shouldStreamForGlobalFetchImplementation, _getFetchImplementation, } from "./singletons/fetch.js";
14
14
  import { serialize as serializePayloadForTracing } from "./utils/fast-safe-stringify/index.js";
15
15
  export function mergeRuntimeEnvIntoRun(run, cachedEnvVars, omitTracedRuntimeInfo) {
16
16
  if (omitTracedRuntimeInfo) {
@@ -1283,7 +1283,6 @@ export class Client {
1283
1283
  async _sendMultipartRequest(parts, context, options) {
1284
1284
  // Create multipart form data boundary
1285
1285
  const boundary = "----LangSmithFormBoundary" + Math.random().toString(36).slice(2);
1286
- const isNodeFetch = _globalFetchImplementationIsNodeFetch();
1287
1286
  const buildBuffered = () => this._createNodeFetchBody(parts, boundary);
1288
1287
  const buildStream = () => this._createMultipartStream(parts, boundary);
1289
1288
  const sendWithRetry = async (bodyFactory) => {
@@ -1318,8 +1317,9 @@ export class Client {
1318
1317
  try {
1319
1318
  let res;
1320
1319
  let streamedAttempt = false;
1321
- // attempt stream only if not disabled and not using node-fetch or Bun
1322
- if (!isNodeFetch &&
1320
+ const shouldStream = _shouldStreamForGlobalFetchImplementation();
1321
+ // attempt stream only if not disabled and not using node-fetch or Bun;
1322
+ if (shouldStream &&
1323
1323
  !this.multipartStreamingDisabled &&
1324
1324
  getEnv() !== "bun") {
1325
1325
  streamedAttempt = true;
@@ -3191,6 +3191,115 @@ export class Client {
3191
3191
  const [results] = await this._logEvaluationFeedback(evaluatorResponse, run, sourceInfo);
3192
3192
  return results;
3193
3193
  }
3194
+ /**
3195
+ * API for managing feedback configs
3196
+ */
3197
+ /**
3198
+ * Create a feedback configuration on the LangSmith API.
3199
+ *
3200
+ * This upserts: if an identical config already exists, it returns it.
3201
+ * If a conflicting config exists for the same key, a 400 error is raised.
3202
+ *
3203
+ * @param options - The options for creating a feedback config
3204
+ * @param options.feedbackKey - The unique key for this feedback config
3205
+ * @param options.feedbackConfig - The config specifying type, bounds, and categories
3206
+ * @param options.isLowerScoreBetter - Whether a lower score is better
3207
+ * @returns The created FeedbackConfigSchema object
3208
+ */
3209
+ async createFeedbackConfig(options) {
3210
+ const { feedbackKey, feedbackConfig, isLowerScoreBetter = false } = options;
3211
+ const body = {
3212
+ feedback_key: feedbackKey,
3213
+ feedback_config: feedbackConfig,
3214
+ is_lower_score_better: isLowerScoreBetter,
3215
+ };
3216
+ const response = await this.caller.call(async () => {
3217
+ const res = await this._fetch(`${this.apiUrl}/feedback-configs`, {
3218
+ method: "POST",
3219
+ headers: { ...this.headers, "Content-Type": "application/json" },
3220
+ signal: AbortSignal.timeout(this.timeout_ms),
3221
+ ...this.fetchOptions,
3222
+ body: JSON.stringify(body),
3223
+ });
3224
+ await raiseForStatus(res, "create feedback config");
3225
+ return res;
3226
+ });
3227
+ return response.json();
3228
+ }
3229
+ /**
3230
+ * List feedback configurations on the LangSmith API.
3231
+ * @param options - The options for listing feedback configs
3232
+ * @param options.feedbackKeys - Filter by specific feedback keys
3233
+ * @param options.nameContains - Filter by name substring
3234
+ * @param options.limit - The maximum number of configs to return
3235
+ * @returns An async iterator of FeedbackConfigSchema objects
3236
+ */
3237
+ async *listFeedbackConfigs(options = {}) {
3238
+ const { feedbackKeys, nameContains, limit } = options;
3239
+ const params = new URLSearchParams();
3240
+ if (feedbackKeys) {
3241
+ feedbackKeys.forEach((key) => {
3242
+ params.append("key", key);
3243
+ });
3244
+ }
3245
+ if (nameContains)
3246
+ params.append("name_contains", nameContains);
3247
+ params.append("limit", (limit !== undefined ? Math.min(limit, 100) : 100).toString());
3248
+ let count = 0;
3249
+ for await (const configs of this._getPaginated("/feedback-configs", params)) {
3250
+ yield* configs;
3251
+ count += configs.length;
3252
+ if (limit !== undefined && count >= limit)
3253
+ break;
3254
+ }
3255
+ }
3256
+ /**
3257
+ * Update a feedback configuration on the LangSmith API.
3258
+ * @param feedbackKey - The key of the feedback config to update
3259
+ * @param options - The options for updating the feedback config
3260
+ * @param options.feedbackConfig - The new feedback config
3261
+ * @param options.isLowerScoreBetter - Whether a lower score is better
3262
+ * @returns The updated FeedbackConfigSchema object
3263
+ */
3264
+ async updateFeedbackConfig(feedbackKey, options = {}) {
3265
+ const { feedbackConfig, isLowerScoreBetter } = options;
3266
+ const body = { feedback_key: feedbackKey };
3267
+ if (feedbackConfig !== undefined) {
3268
+ body.feedback_config = feedbackConfig;
3269
+ }
3270
+ if (isLowerScoreBetter !== undefined) {
3271
+ body.is_lower_score_better = isLowerScoreBetter;
3272
+ }
3273
+ const response = await this.caller.call(async () => {
3274
+ const res = await this._fetch(`${this.apiUrl}/feedback-configs`, {
3275
+ method: "PATCH",
3276
+ headers: { ...this.headers, "Content-Type": "application/json" },
3277
+ signal: AbortSignal.timeout(this.timeout_ms),
3278
+ ...this.fetchOptions,
3279
+ body: JSON.stringify(body),
3280
+ });
3281
+ await raiseForStatus(res, "update feedback config");
3282
+ return res;
3283
+ });
3284
+ return response.json();
3285
+ }
3286
+ /**
3287
+ * Delete a feedback configuration on the LangSmith API.
3288
+ * @param feedbackKey - The key of the feedback config to delete
3289
+ */
3290
+ async deleteFeedbackConfig(feedbackKey) {
3291
+ const params = new URLSearchParams({ feedback_key: feedbackKey });
3292
+ await this.caller.call(async () => {
3293
+ const res = await this._fetch(`${this.apiUrl}/feedback-configs?${params}`, {
3294
+ method: "DELETE",
3295
+ headers: this.headers,
3296
+ signal: AbortSignal.timeout(this.timeout_ms),
3297
+ ...this.fetchOptions,
3298
+ });
3299
+ await raiseForStatus(res, "delete feedback config", true);
3300
+ return res;
3301
+ });
3302
+ }
3194
3303
  /**
3195
3304
  * API for managing annotation queues
3196
3305
  */
@@ -3234,12 +3343,13 @@ export class Client {
3234
3343
  * @returns The created AnnotationQueue object
3235
3344
  */
3236
3345
  async createAnnotationQueue(options) {
3237
- const { name, description, queueId, rubricInstructions } = options;
3346
+ const { name, description, queueId, rubricInstructions, rubricItems } = options;
3238
3347
  const body = {
3239
3348
  name,
3240
3349
  description,
3241
3350
  id: queueId || uuid.v4(),
3242
3351
  rubric_instructions: rubricInstructions,
3352
+ rubric_items: rubricItems,
3243
3353
  };
3244
3354
  const serializedBody = JSON.stringify(Object.fromEntries(Object.entries(body).filter(([_, v]) => v !== undefined)));
3245
3355
  const response = await this.caller.call(async () => {
@@ -3281,12 +3391,17 @@ export class Client {
3281
3391
  * @param options.description - The new description for the annotation queue
3282
3392
  */
3283
3393
  async updateAnnotationQueue(queueId, options) {
3284
- const { name, description, rubricInstructions } = options;
3285
- const body = JSON.stringify({
3286
- name,
3287
- description,
3288
- rubric_instructions: rubricInstructions,
3289
- });
3394
+ const { name, description, rubricInstructions, rubricItems } = options;
3395
+ const bodyObj = {};
3396
+ if (name !== undefined)
3397
+ bodyObj.name = name;
3398
+ if (description !== undefined)
3399
+ bodyObj.description = description;
3400
+ if (rubricInstructions !== undefined)
3401
+ bodyObj.rubric_instructions = rubricInstructions;
3402
+ if (rubricItems !== undefined)
3403
+ bodyObj.rubric_items = rubricItems;
3404
+ const body = JSON.stringify(bodyObj);
3290
3405
  await this.caller.call(async () => {
3291
3406
  const res = await this._fetch(`${this.apiUrl}/annotation-queues/${assertUuid(queueId, "queueId")}`, {
3292
3407
  method: "PATCH",
@@ -783,13 +783,13 @@ async function _evaluate(target, fields) {
783
783
  return results;
784
784
  }
785
785
  async function _forward(fn, example, experimentName, metadata, client, includeAttachments) {
786
- let run = null;
786
+ let run = undefined;
787
787
  const _getRun = (r) => {
788
788
  run = r;
789
789
  };
790
790
  const defaultOptions = {
791
791
  reference_example_id: example.id,
792
- on_end: _getRun,
792
+ on_start: _getRun,
793
793
  project_name: experimentName,
794
794
  metadata: {
795
795
  ...metadata,
@@ -776,13 +776,13 @@ async function _evaluate(target, fields) {
776
776
  return results;
777
777
  }
778
778
  async function _forward(fn, example, experimentName, metadata, client, includeAttachments) {
779
- let run = null;
779
+ let run = undefined;
780
780
  const _getRun = (r) => {
781
781
  run = r;
782
782
  };
783
783
  const defaultOptions = {
784
784
  reference_example_id: example.id,
785
- on_end: _getRun,
785
+ on_start: _getRun,
786
786
  project_name: experimentName,
787
787
  metadata: {
788
788
  ...metadata,
package/dist/index.cjs CHANGED
@@ -18,4 +18,4 @@ Object.defineProperty(exports, "PromptCache", { enumerable: true, get: function
18
18
  Object.defineProperty(exports, "configureGlobalPromptCache", { enumerable: true, get: function () { return index_js_1.configureGlobalPromptCache; } });
19
19
  Object.defineProperty(exports, "promptCacheSingleton", { enumerable: true, get: function () { return index_js_1.promptCacheSingleton; } });
20
20
  // Update using yarn bump-version
21
- exports.__version__ = "0.5.2";
21
+ exports.__version__ = "0.5.4";
package/dist/index.d.ts CHANGED
@@ -1,8 +1,8 @@
1
1
  export { Client, type ClientConfig, type LangSmithTracingClientInterface, } from "./client.js";
2
- export type { Dataset, Example, TracerSession, Run, Feedback, RetrieverOutput, } from "./schemas.js";
2
+ export type { Dataset, Example, TracerSession, Run, Feedback, FeedbackConfigSchema, RetrieverOutput, } from "./schemas.js";
3
3
  export { RunTree, type RunTreeConfig } from "./run_trees.js";
4
4
  export { overrideFetchImplementation } from "./singletons/fetch.js";
5
5
  export { getDefaultProjectName } from "./utils/project.js";
6
6
  export { uuid7, uuid7FromTime } from "./uuid.js";
7
7
  export { Cache, PromptCache, type CacheConfig, type CacheMetrics, configureGlobalPromptCache, promptCacheSingleton, } from "./utils/prompt_cache/index.js";
8
- export declare const __version__ = "0.5.2";
8
+ export declare const __version__ = "0.5.4";
package/dist/index.js CHANGED
@@ -5,4 +5,4 @@ export { getDefaultProjectName } from "./utils/project.js";
5
5
  export { uuid7, uuid7FromTime } from "./uuid.js";
6
6
  export { Cache, PromptCache, configureGlobalPromptCache, promptCacheSingleton, } from "./utils/prompt_cache/index.js";
7
7
  // Update using yarn bump-version
8
- export const __version__ = "0.5.2";
8
+ export const __version__ = "0.5.4";
package/dist/schemas.d.ts CHANGED
@@ -457,9 +457,35 @@ export interface AnnotationQueue {
457
457
  /** The ID of the tenant associated with the annotation queue. */
458
458
  tenant_id: string;
459
459
  }
460
+ export interface AnnotationQueueRubricItem {
461
+ /** The feedback key this rubric item is associated with. */
462
+ feedback_key: string;
463
+ /** An optional description of the rubric item. */
464
+ description?: string | null;
465
+ /** Descriptions for categorical values. */
466
+ value_descriptions?: Record<string, string> | null;
467
+ /** Descriptions for continuous score values. */
468
+ score_descriptions?: Record<string, string> | null;
469
+ /** Whether this rubric item is required. */
470
+ is_required?: boolean | null;
471
+ }
460
472
  export interface AnnotationQueueWithDetails extends AnnotationQueue {
461
473
  /** The rubric instructions for the annotation queue. */
462
474
  rubric_instructions?: string;
475
+ /** The rubric items for the annotation queue. */
476
+ rubric_items?: AnnotationQueueRubricItem[];
477
+ }
478
+ export interface FeedbackConfigSchema {
479
+ /** The unique key identifying this feedback configuration. */
480
+ feedback_key: string;
481
+ /** The configuration specifying the type, bounds, and categories. */
482
+ feedback_config: FeedbackConfig;
483
+ /** The ID of the tenant that owns this feedback configuration. */
484
+ tenant_id: string;
485
+ /** When this feedback configuration was last modified. */
486
+ modified_at: string;
487
+ /** Whether a lower score is considered better for this feedback key. */
488
+ is_lower_score_better?: boolean | null;
463
489
  }
464
490
  export interface RunWithAnnotationQueueInfo extends BaseRun {
465
491
  /** The last time this run was reviewed. */
@@ -1,38 +1,38 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports._getFetchImplementation = exports._globalFetchImplementationIsNodeFetch = exports.clearFetchImplementation = exports.overrideFetchImplementation = void 0;
3
+ exports._getFetchImplementation = exports._shouldStreamForGlobalFetchImplementation = exports.clearFetchImplementation = exports.overrideFetchImplementation = void 0;
4
4
  const env_js_1 = require("../utils/env.cjs");
5
5
  // Wrap the default fetch call due to issues with illegal invocations
6
6
  // in some environments:
7
7
  // https://stackoverflow.com/questions/69876859/why-does-bind-fix-failed-to-execute-fetch-on-window-illegal-invocation-err
8
8
  // @ts-expect-error Broad typing to support a range of fetch implementations
9
9
  const DEFAULT_FETCH_IMPLEMENTATION = (...args) => fetch(...args);
10
+ let globalFetchSupportsWebStreaming = undefined;
10
11
  const LANGSMITH_FETCH_IMPLEMENTATION_KEY = Symbol.for("ls:fetch_implementation");
11
12
  /**
12
13
  * Overrides the fetch implementation used for LangSmith calls.
13
14
  * You should use this if you need to use an implementation of fetch
14
15
  * other than the default global (e.g. for dealing with proxies).
15
- * @param fetch The new fetch functino to use.
16
+ * @param fetch The new fetch function to use.
16
17
  */
17
- const overrideFetchImplementation = (fetch) => {
18
+ const overrideFetchImplementation = (fetch, supportsWebStreaming) => {
18
19
  globalThis[LANGSMITH_FETCH_IMPLEMENTATION_KEY] = fetch;
20
+ globalFetchSupportsWebStreaming = supportsWebStreaming;
19
21
  };
20
22
  exports.overrideFetchImplementation = overrideFetchImplementation;
21
23
  const clearFetchImplementation = () => {
22
24
  delete globalThis[LANGSMITH_FETCH_IMPLEMENTATION_KEY];
25
+ globalFetchSupportsWebStreaming = undefined;
23
26
  };
24
27
  exports.clearFetchImplementation = clearFetchImplementation;
25
- const _globalFetchImplementationIsNodeFetch = () => {
26
- const fetchImpl = globalThis[LANGSMITH_FETCH_IMPLEMENTATION_KEY];
27
- if (!fetchImpl)
28
- return false;
29
- // Check if the implementation has node-fetch specific properties
30
- return (typeof fetchImpl === "function" &&
31
- "Headers" in fetchImpl &&
32
- "Request" in fetchImpl &&
33
- "Response" in fetchImpl);
28
+ const _shouldStreamForGlobalFetchImplementation = () => {
29
+ const overriddenFetchImpl = globalThis[LANGSMITH_FETCH_IMPLEMENTATION_KEY];
30
+ if (overriddenFetchImpl === undefined) {
31
+ return true;
32
+ }
33
+ return globalFetchSupportsWebStreaming ?? false;
34
34
  };
35
- exports._globalFetchImplementationIsNodeFetch = _globalFetchImplementationIsNodeFetch;
35
+ exports._shouldStreamForGlobalFetchImplementation = _shouldStreamForGlobalFetchImplementation;
36
36
  /**
37
37
  * @internal
38
38
  */
@@ -2,8 +2,8 @@
2
2
  * Overrides the fetch implementation used for LangSmith calls.
3
3
  * You should use this if you need to use an implementation of fetch
4
4
  * other than the default global (e.g. for dealing with proxies).
5
- * @param fetch The new fetch functino to use.
5
+ * @param fetch The new fetch function to use.
6
6
  */
7
- export declare const overrideFetchImplementation: (fetch: (...args: any[]) => any) => void;
7
+ export declare const overrideFetchImplementation: (fetch: (...args: any[]) => any, supportsWebStreaming?: boolean) => void;
8
8
  export declare const clearFetchImplementation: () => void;
9
- export declare const _globalFetchImplementationIsNodeFetch: () => boolean;
9
+ export declare const _shouldStreamForGlobalFetchImplementation: () => boolean;
@@ -4,28 +4,28 @@ import { getLangSmithEnvironmentVariable } from "../utils/env.js";
4
4
  // https://stackoverflow.com/questions/69876859/why-does-bind-fix-failed-to-execute-fetch-on-window-illegal-invocation-err
5
5
  // @ts-expect-error Broad typing to support a range of fetch implementations
6
6
  const DEFAULT_FETCH_IMPLEMENTATION = (...args) => fetch(...args);
7
+ let globalFetchSupportsWebStreaming = undefined;
7
8
  const LANGSMITH_FETCH_IMPLEMENTATION_KEY = Symbol.for("ls:fetch_implementation");
8
9
  /**
9
10
  * Overrides the fetch implementation used for LangSmith calls.
10
11
  * You should use this if you need to use an implementation of fetch
11
12
  * other than the default global (e.g. for dealing with proxies).
12
- * @param fetch The new fetch functino to use.
13
+ * @param fetch The new fetch function to use.
13
14
  */
14
- export const overrideFetchImplementation = (fetch) => {
15
+ export const overrideFetchImplementation = (fetch, supportsWebStreaming) => {
15
16
  globalThis[LANGSMITH_FETCH_IMPLEMENTATION_KEY] = fetch;
17
+ globalFetchSupportsWebStreaming = supportsWebStreaming;
16
18
  };
17
19
  export const clearFetchImplementation = () => {
18
20
  delete globalThis[LANGSMITH_FETCH_IMPLEMENTATION_KEY];
21
+ globalFetchSupportsWebStreaming = undefined;
19
22
  };
20
- export const _globalFetchImplementationIsNodeFetch = () => {
21
- const fetchImpl = globalThis[LANGSMITH_FETCH_IMPLEMENTATION_KEY];
22
- if (!fetchImpl)
23
- return false;
24
- // Check if the implementation has node-fetch specific properties
25
- return (typeof fetchImpl === "function" &&
26
- "Headers" in fetchImpl &&
27
- "Request" in fetchImpl &&
28
- "Response" in fetchImpl);
23
+ export const _shouldStreamForGlobalFetchImplementation = () => {
24
+ const overriddenFetchImpl = globalThis[LANGSMITH_FETCH_IMPLEMENTATION_KEY];
25
+ if (overriddenFetchImpl === undefined) {
26
+ return true;
27
+ }
28
+ return globalFetchSupportsWebStreaming ?? false;
29
29
  };
30
30
  /**
31
31
  * @internal
@@ -123,7 +123,7 @@ async function handleEnd(params) {
123
123
  });
124
124
  }
125
125
  }
126
- const _populateUsageMetadata = (processedOutputs, runTree) => {
126
+ const _populateUsageMetadataAndOutputs = (processedOutputs, runTree) => {
127
127
  if (runTree !== undefined) {
128
128
  let usageMetadata;
129
129
  try {
@@ -139,6 +139,8 @@ const _populateUsageMetadata = (processedOutputs, runTree) => {
139
139
  };
140
140
  processedOutputs.usage_metadata = usageMetadata;
141
141
  }
142
+ // Set outputs on run tree as soon as available
143
+ runTree.outputs = processedOutputs;
142
144
  }
143
145
  };
144
146
  function isAsyncFn(fn) {
@@ -170,7 +172,7 @@ async function handleRunOutputs(params) {
170
172
  if (isAsyncFn(processOutputsFn)) {
171
173
  void outputs
172
174
  .then(async (processedOutputs) => {
173
- _populateUsageMetadata(processedOutputs, runTree);
175
+ _populateUsageMetadataAndOutputs(processedOutputs, runTree);
174
176
  await childRunEndPromises;
175
177
  await runTree?.end(processedOutputs);
176
178
  })
@@ -203,7 +205,7 @@ async function handleRunOutputs(params) {
203
205
  catch (e) {
204
206
  console.error("Error occurred during processOutputs. Sending unprocessed outputs:", e);
205
207
  }
206
- _populateUsageMetadata(outputs, runTree);
208
+ _populateUsageMetadataAndOutputs(outputs, runTree);
207
209
  void childRunEndPromises
208
210
  .then(async () => {
209
211
  try {
@@ -441,7 +443,7 @@ const convertSerializableArg = (arg, options) => {
441
443
  */
442
444
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
443
445
  function traceable(wrappedFunc, config) {
444
- const { aggregator, argsConfigPath, __finalTracedIteratorKey, processInputs, processOutputs, extractAttachments, ...runTreeConfig } = config ?? {};
446
+ const { aggregator, argsConfigPath, __finalTracedIteratorKey, processInputs, processOutputs, extractAttachments, on_start, ...runTreeConfig } = config ?? {};
445
447
  const processInputsFn = processInputs ?? ((x) => x);
446
448
  const processOutputsFn = processOutputs ?? ((x) => x);
447
449
  const extractAttachmentsFn = extractAttachments ?? ((...x) => [undefined, runInputsToMap(x)]);
@@ -577,6 +579,7 @@ function traceable(wrappedFunc, config) {
577
579
  const currentRunTree = (0, run_trees_js_1.isRunTree)(currentContext)
578
580
  ? currentContext
579
581
  : undefined;
582
+ on_start?.(currentRunTree);
580
583
  const otelContextManager = maybeCreateOtelContext(currentRunTree, config?.project_name, config?.tracer);
581
584
  const otel_context = (0, otel_js_1.getOTELContext)();
582
585
  const runWithContext = () => {
@@ -1,4 +1,4 @@
1
- import { RunTreeConfig } from "./run_trees.js";
1
+ import { RunTree, RunTreeConfig } from "./run_trees.js";
2
2
  import { Attachments, InvocationParamsSchema, KVMap } from "./schemas.js";
3
3
  import type { TraceableFunction } from "./singletons/types.js";
4
4
  import { OTELTracer } from "./experimental/otel/types.js";
@@ -18,6 +18,7 @@ export type TraceableConfig<Func extends (...args: any[]) => any> = Partial<Omit
18
18
  aggregator?: (args: any[]) => any;
19
19
  argsConfigPath?: [number] | [number, string];
20
20
  tracer?: OTELTracer;
21
+ on_start?: (runTree: RunTree | undefined) => void;
21
22
  __finalTracedIteratorKey?: string;
22
23
  __deferredSerializableArgOptions?: {
23
24
  depth?: number;
package/dist/traceable.js CHANGED
@@ -119,7 +119,7 @@ async function handleEnd(params) {
119
119
  });
120
120
  }
121
121
  }
122
- const _populateUsageMetadata = (processedOutputs, runTree) => {
122
+ const _populateUsageMetadataAndOutputs = (processedOutputs, runTree) => {
123
123
  if (runTree !== undefined) {
124
124
  let usageMetadata;
125
125
  try {
@@ -135,6 +135,8 @@ const _populateUsageMetadata = (processedOutputs, runTree) => {
135
135
  };
136
136
  processedOutputs.usage_metadata = usageMetadata;
137
137
  }
138
+ // Set outputs on run tree as soon as available
139
+ runTree.outputs = processedOutputs;
138
140
  }
139
141
  };
140
142
  function isAsyncFn(fn) {
@@ -166,7 +168,7 @@ async function handleRunOutputs(params) {
166
168
  if (isAsyncFn(processOutputsFn)) {
167
169
  void outputs
168
170
  .then(async (processedOutputs) => {
169
- _populateUsageMetadata(processedOutputs, runTree);
171
+ _populateUsageMetadataAndOutputs(processedOutputs, runTree);
170
172
  await childRunEndPromises;
171
173
  await runTree?.end(processedOutputs);
172
174
  })
@@ -199,7 +201,7 @@ async function handleRunOutputs(params) {
199
201
  catch (e) {
200
202
  console.error("Error occurred during processOutputs. Sending unprocessed outputs:", e);
201
203
  }
202
- _populateUsageMetadata(outputs, runTree);
204
+ _populateUsageMetadataAndOutputs(outputs, runTree);
203
205
  void childRunEndPromises
204
206
  .then(async () => {
205
207
  try {
@@ -437,7 +439,7 @@ const convertSerializableArg = (arg, options) => {
437
439
  */
438
440
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
439
441
  export function traceable(wrappedFunc, config) {
440
- const { aggregator, argsConfigPath, __finalTracedIteratorKey, processInputs, processOutputs, extractAttachments, ...runTreeConfig } = config ?? {};
442
+ const { aggregator, argsConfigPath, __finalTracedIteratorKey, processInputs, processOutputs, extractAttachments, on_start, ...runTreeConfig } = config ?? {};
441
443
  const processInputsFn = processInputs ?? ((x) => x);
442
444
  const processOutputsFn = processOutputs ?? ((x) => x);
443
445
  const extractAttachmentsFn = extractAttachments ?? ((...x) => [undefined, runInputsToMap(x)]);
@@ -573,6 +575,7 @@ export function traceable(wrappedFunc, config) {
573
575
  const currentRunTree = isRunTree(currentContext)
574
576
  ? currentContext
575
577
  : undefined;
578
+ on_start?.(currentRunTree);
576
579
  const otelContextManager = maybeCreateOtelContext(currentRunTree, config?.project_name, config?.tracer);
577
580
  const otel_context = getOTELContext();
578
581
  const runWithContext = () => {
@@ -517,6 +517,9 @@ function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
517
517
  const strippedErrorMessage = e.message.replace(constants_js_1.STRIP_ANSI_REGEX, "");
518
518
  const langsmithFriendlyError = new Error(strippedErrorMessage);
519
519
  langsmithFriendlyError.rawJestError = rawError;
520
+ if (testContext.testRootRunTree) {
521
+ testContext.testRootRunTree.outputs = loggedOutput;
522
+ }
520
523
  throw langsmithFriendlyError;
521
524
  }
522
525
  });
@@ -470,6 +470,9 @@ export function generateWrapperFromJestlikeMethods(methods, testRunnerName) {
470
470
  const strippedErrorMessage = e.message.replace(STRIP_ANSI_REGEX, "");
471
471
  const langsmithFriendlyError = new Error(strippedErrorMessage);
472
472
  langsmithFriendlyError.rawJestError = rawError;
473
+ if (testContext.testRootRunTree) {
474
+ testContext.testRootRunTree.outputs = loggedOutput;
475
+ }
473
476
  throw langsmithFriendlyError;
474
477
  }
475
478
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langsmith",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "Client library to connect to the LangSmith Observability and Evaluation Platform.",
5
5
  "packageManager": "yarn@1.22.19",
6
6
  "files": [
@@ -158,7 +158,7 @@
158
158
  "@ai-sdk/provider": "^3.0.0",
159
159
  "@anthropic-ai/claude-agent-sdk": "^0.2.34",
160
160
  "@google/genai": "^1.29.0",
161
- "@anthropic-ai/sdk": "^0.73.0",
161
+ "@anthropic-ai/sdk": "^0.74.0",
162
162
  "@babel/preset-env": "^7.22.4",
163
163
  "@faker-js/faker": "^8.4.1",
164
164
  "@jest/globals": "^29.5.0",
@@ -178,18 +178,18 @@
178
178
  "@typescript-eslint/parser": "^5.59.8",
179
179
  "ai": "^6.0.1",
180
180
  "babel-jest": "^30.2.0",
181
- "cross-env": "^7.0.3",
181
+ "cross-env": "^10.1.0",
182
182
  "dotenv": "^16.1.3",
183
183
  "eslint": "^8.41.0",
184
- "eslint-config-prettier": "^8.8.0",
184
+ "eslint-config-prettier": "^10.1.8",
185
185
  "eslint-plugin-import": "^2.27.5",
186
186
  "eslint-plugin-no-instanceof": "^1.0.1",
187
187
  "eslint-plugin-prettier": "^4.2.1",
188
188
  "jest": "^29.5.0",
189
189
  "langchain": "^0.3.29",
190
190
  "msw": "^2.11.2",
191
- "node-fetch": "^2.7.0",
192
- "openai": "^5.8.2",
191
+ "node-fetch": "^3.3.2",
192
+ "openai": "^6.18.0",
193
193
  "prettier": "^2.8.8",
194
194
  "ts-jest": "^29.1.0",
195
195
  "ts-node": "^10.9.1",