langsmith 0.3.28 → 0.3.29

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
@@ -145,7 +145,7 @@ class AutoBatchQueue {
145
145
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise
146
146
  itemPromiseResolve = resolve;
147
147
  });
148
- const size = (0, index_js_2.serialize)(item.item).length;
148
+ const size = (0, index_js_2.serialize)(item.item, `Serializing run with id: ${item.item.id}`).length;
149
149
  this.items.push({
150
150
  action: item.action,
151
151
  payload: item.item,
@@ -736,7 +736,7 @@ class Client {
736
736
  const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/runs`, {
737
737
  method: "POST",
738
738
  headers,
739
- body: (0, index_js_2.serialize)(mergedRunCreateParam),
739
+ body: (0, index_js_2.serialize)(mergedRunCreateParam, `Creating run with id: ${mergedRunCreateParam.id}`),
740
740
  signal: AbortSignal.timeout(this.timeout_ms),
741
741
  ...this.fetchOptions,
742
742
  });
@@ -797,7 +797,11 @@ class Client {
797
797
  }
798
798
  }
799
799
  if (batchChunks.post.length > 0 || batchChunks.patch.length > 0) {
800
- await this._postBatchIngestRuns((0, index_js_2.serialize)(batchChunks));
800
+ const runIds = batchChunks.post
801
+ .map((item) => item.id)
802
+ .concat(batchChunks.patch.map((item) => item.id))
803
+ .join(",");
804
+ await this._postBatchIngestRuns((0, index_js_2.serialize)(batchChunks, `Ingesting runs with ids: ${runIds}`));
801
805
  }
802
806
  }
803
807
  async _postBatchIngestRuns(body) {
@@ -892,7 +896,7 @@ class Client {
892
896
  const { inputs, outputs, events, attachments, ...payload } = originalPayload;
893
897
  const fields = { inputs, outputs, events };
894
898
  // encode the main run payload
895
- const stringifiedPayload = (0, index_js_2.serialize)(payload);
899
+ const stringifiedPayload = (0, index_js_2.serialize)(payload, `Serializing for multipart ingestion of run with id: ${payload.id}`);
896
900
  accumulatedParts.push({
897
901
  name: `${method}.${payload.id}`,
898
902
  payload: new Blob([stringifiedPayload], {
@@ -904,7 +908,7 @@ class Client {
904
908
  if (value === undefined) {
905
909
  continue;
906
910
  }
907
- const stringifiedValue = (0, index_js_2.serialize)(value);
911
+ const stringifiedValue = (0, index_js_2.serialize)(value, `Serializing ${key} for multipart ingestion of run with id: ${payload.id}`);
908
912
  accumulatedParts.push({
909
913
  name: `${method}.${payload.id}.${key}`,
910
914
  payload: new Blob([stringifiedValue], {
@@ -948,34 +952,84 @@ class Client {
948
952
  }
949
953
  await this._sendMultipartRequest(accumulatedParts, accumulatedContext.join("; "));
950
954
  }
955
+ async _createNodeFetchBody(parts, boundary) {
956
+ // Create multipart form data manually using Blobs
957
+ const chunks = [];
958
+ for (const part of parts) {
959
+ // Add field boundary
960
+ chunks.push(new Blob([`--${boundary}\r\n`]));
961
+ chunks.push(new Blob([
962
+ `Content-Disposition: form-data; name="${part.name}"\r\n`,
963
+ `Content-Type: ${part.payload.type}\r\n\r\n`,
964
+ ]));
965
+ chunks.push(part.payload);
966
+ chunks.push(new Blob(["\r\n"]));
967
+ }
968
+ // Add final boundary
969
+ chunks.push(new Blob([`--${boundary}--\r\n`]));
970
+ // Combine all chunks into a single Blob
971
+ const body = new Blob(chunks);
972
+ // Convert Blob to ArrayBuffer for compatibility
973
+ const arrayBuffer = await body.arrayBuffer();
974
+ return arrayBuffer;
975
+ }
976
+ async _createMultipartStream(parts, boundary) {
977
+ const encoder = new TextEncoder();
978
+ // Create a ReadableStream for streaming the multipart data
979
+ // Only do special handling if we're using node-fetch
980
+ const stream = new ReadableStream({
981
+ async start(controller) {
982
+ // Helper function to write a chunk to the stream
983
+ const writeChunk = async (chunk) => {
984
+ if (typeof chunk === "string") {
985
+ controller.enqueue(encoder.encode(chunk));
986
+ }
987
+ else {
988
+ controller.enqueue(chunk);
989
+ }
990
+ };
991
+ // Write each part to the stream
992
+ for (const part of parts) {
993
+ // Write boundary and headers
994
+ await writeChunk(`--${boundary}\r\n`);
995
+ await writeChunk(`Content-Disposition: form-data; name="${part.name}"\r\n`);
996
+ await writeChunk(`Content-Type: ${part.payload.type}\r\n\r\n`);
997
+ // Write the payload
998
+ const payloadStream = part.payload.stream();
999
+ const reader = payloadStream.getReader();
1000
+ try {
1001
+ let result;
1002
+ while (!(result = await reader.read()).done) {
1003
+ controller.enqueue(result.value);
1004
+ }
1005
+ }
1006
+ finally {
1007
+ reader.releaseLock();
1008
+ }
1009
+ await writeChunk("\r\n");
1010
+ }
1011
+ // Write final boundary
1012
+ await writeChunk(`--${boundary}--\r\n`);
1013
+ controller.close();
1014
+ },
1015
+ });
1016
+ return stream;
1017
+ }
951
1018
  async _sendMultipartRequest(parts, context) {
952
1019
  try {
953
- // Create multipart form data manually using Blobs
1020
+ // Create multipart form data boundary
954
1021
  const boundary = "----LangSmithFormBoundary" + Math.random().toString(36).slice(2);
955
- const chunks = [];
956
- for (const part of parts) {
957
- // Add field boundary
958
- chunks.push(new Blob([`--${boundary}\r\n`]));
959
- chunks.push(new Blob([
960
- `Content-Disposition: form-data; name="${part.name}"\r\n`,
961
- `Content-Type: ${part.payload.type}\r\n\r\n`,
962
- ]));
963
- chunks.push(part.payload);
964
- chunks.push(new Blob(["\r\n"]));
965
- }
966
- // Add final boundary
967
- chunks.push(new Blob([`--${boundary}--\r\n`]));
968
- // Combine all chunks into a single Blob
969
- const body = new Blob(chunks);
970
- // Convert Blob to ArrayBuffer for compatibility
971
- const arrayBuffer = await body.arrayBuffer();
1022
+ const body = await ((0, fetch_js_1._globalFetchImplementationIsNodeFetch)()
1023
+ ? this._createNodeFetchBody(parts, boundary)
1024
+ : this._createMultipartStream(parts, boundary));
972
1025
  const res = await this.batchIngestCaller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/runs/multipart`, {
973
1026
  method: "POST",
974
1027
  headers: {
975
1028
  ...this.headers,
976
1029
  "Content-Type": `multipart/form-data; boundary=${boundary}`,
977
1030
  },
978
- body: arrayBuffer,
1031
+ body,
1032
+ duplex: "half",
979
1033
  signal: AbortSignal.timeout(this.timeout_ms),
980
1034
  ...this.fetchOptions,
981
1035
  });
@@ -1020,7 +1074,7 @@ class Client {
1020
1074
  const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}/runs/${runId}`, {
1021
1075
  method: "PATCH",
1022
1076
  headers,
1023
- body: (0, index_js_2.serialize)(run),
1077
+ body: (0, index_js_2.serialize)(run, `Serializing payload to update run with id: ${runId}`),
1024
1078
  signal: AbortSignal.timeout(this.timeout_ms),
1025
1079
  ...this.fetchOptions,
1026
1080
  });
@@ -1491,7 +1545,9 @@ class Client {
1491
1545
  const result = await response.json();
1492
1546
  if (!response.ok) {
1493
1547
  if ("detail" in result) {
1494
- throw new Error(`Failed to list shared examples.\nStatus: ${response.status}\nMessage: ${result.detail.join("\n")}`);
1548
+ throw new Error(`Failed to list shared examples.\nStatus: ${response.status}\nMessage: ${Array.isArray(result.detail)
1549
+ ? result.detail.join("\n")
1550
+ : "Unspecified error"}`);
1495
1551
  }
1496
1552
  throw new Error(`Failed to list shared examples: ${response.status} ${response.statusText}`);
1497
1553
  }
@@ -3006,14 +3062,14 @@ class Client {
3006
3062
  ...(example.split && { split: example.split }),
3007
3063
  };
3008
3064
  // Add main example data
3009
- const stringifiedExample = (0, index_js_2.serialize)(exampleBody);
3065
+ const stringifiedExample = (0, index_js_2.serialize)(exampleBody, `Serializing body for example with id: ${exampleId}`);
3010
3066
  const exampleBlob = new Blob([stringifiedExample], {
3011
3067
  type: "application/json",
3012
3068
  });
3013
3069
  formData.append(exampleId, exampleBlob);
3014
3070
  // Add inputs if present
3015
3071
  if (example.inputs) {
3016
- const stringifiedInputs = (0, index_js_2.serialize)(example.inputs);
3072
+ const stringifiedInputs = (0, index_js_2.serialize)(example.inputs, `Serializing inputs for example with id: ${exampleId}`);
3017
3073
  const inputsBlob = new Blob([stringifiedInputs], {
3018
3074
  type: "application/json",
3019
3075
  });
@@ -3021,7 +3077,7 @@ class Client {
3021
3077
  }
3022
3078
  // Add outputs if present
3023
3079
  if (example.outputs) {
3024
- const stringifiedOutputs = (0, index_js_2.serialize)(example.outputs);
3080
+ const stringifiedOutputs = (0, index_js_2.serialize)(example.outputs, `Serializing outputs whle updating example with id: ${exampleId}`);
3025
3081
  const outputsBlob = new Blob([stringifiedOutputs], {
3026
3082
  type: "application/json",
3027
3083
  });
@@ -3046,7 +3102,7 @@ class Client {
3046
3102
  }
3047
3103
  }
3048
3104
  if (example.attachments_operations) {
3049
- const stringifiedAttachmentsOperations = (0, index_js_2.serialize)(example.attachments_operations);
3105
+ const stringifiedAttachmentsOperations = (0, index_js_2.serialize)(example.attachments_operations, `Serializing attachments while updating example with id: ${exampleId}`);
3050
3106
  const attachmentsOperationsBlob = new Blob([stringifiedAttachmentsOperations], {
3051
3107
  type: "application/json",
3052
3108
  });
@@ -3092,14 +3148,14 @@ class Client {
3092
3148
  }),
3093
3149
  };
3094
3150
  // Add main example data
3095
- const stringifiedExample = (0, index_js_2.serialize)(exampleBody);
3151
+ const stringifiedExample = (0, index_js_2.serialize)(exampleBody, `Serializing body for uploaded example with id: ${exampleId}`);
3096
3152
  const exampleBlob = new Blob([stringifiedExample], {
3097
3153
  type: "application/json",
3098
3154
  });
3099
3155
  formData.append(exampleId, exampleBlob);
3100
3156
  // Add inputs if present
3101
3157
  if (example.inputs) {
3102
- const stringifiedInputs = (0, index_js_2.serialize)(example.inputs);
3158
+ const stringifiedInputs = (0, index_js_2.serialize)(example.inputs, `Serializing inputs for uploaded example with id: ${exampleId}`);
3103
3159
  const inputsBlob = new Blob([stringifiedInputs], {
3104
3160
  type: "application/json",
3105
3161
  });
@@ -3107,7 +3163,7 @@ class Client {
3107
3163
  }
3108
3164
  // Add outputs if present
3109
3165
  if (example.outputs) {
3110
- const stringifiedOutputs = (0, index_js_2.serialize)(example.outputs);
3166
+ const stringifiedOutputs = (0, index_js_2.serialize)(example.outputs, `Serializing outputs for uploaded example with id: ${exampleId}`);
3111
3167
  const outputsBlob = new Blob([stringifiedOutputs], {
3112
3168
  type: "application/json",
3113
3169
  });
package/dist/client.d.ts CHANGED
@@ -351,6 +351,8 @@ export declare class Client implements LangSmithTracingClientInterface {
351
351
  runCreates?: RunCreate[];
352
352
  runUpdates?: RunUpdate[];
353
353
  }): Promise<void>;
354
+ private _createNodeFetchBody;
355
+ private _createMultipartStream;
354
356
  private _sendMultipartRequest;
355
357
  updateRun(runId: string, run: RunUpdate): Promise<void>;
356
358
  readRun(runId: string, { loadChildRuns }?: {
package/dist/client.js CHANGED
@@ -7,7 +7,7 @@ import { assertUuid } from "./utils/_uuid.js";
7
7
  import { warnOnce } from "./utils/warn.js";
8
8
  import { parsePromptIdentifier } from "./utils/prompts.js";
9
9
  import { raiseForStatus } from "./utils/error.js";
10
- import { _getFetchImplementation } from "./singletons/fetch.js";
10
+ import { _globalFetchImplementationIsNodeFetch, _getFetchImplementation, } from "./singletons/fetch.js";
11
11
  import { serialize as serializePayloadForTracing } from "./utils/fast-safe-stringify/index.js";
12
12
  export function mergeRuntimeEnvIntoRunCreate(run) {
13
13
  const runtimeEnv = getRuntimeEnvironment();
@@ -108,7 +108,7 @@ export class AutoBatchQueue {
108
108
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise
109
109
  itemPromiseResolve = resolve;
110
110
  });
111
- const size = serializePayloadForTracing(item.item).length;
111
+ const size = serializePayloadForTracing(item.item, `Serializing run with id: ${item.item.id}`).length;
112
112
  this.items.push({
113
113
  action: item.action,
114
114
  payload: item.item,
@@ -698,7 +698,7 @@ export class Client {
698
698
  const response = await this.caller.call(_getFetchImplementation(this.debug), `${this.apiUrl}/runs`, {
699
699
  method: "POST",
700
700
  headers,
701
- body: serializePayloadForTracing(mergedRunCreateParam),
701
+ body: serializePayloadForTracing(mergedRunCreateParam, `Creating run with id: ${mergedRunCreateParam.id}`),
702
702
  signal: AbortSignal.timeout(this.timeout_ms),
703
703
  ...this.fetchOptions,
704
704
  });
@@ -759,7 +759,11 @@ export class Client {
759
759
  }
760
760
  }
761
761
  if (batchChunks.post.length > 0 || batchChunks.patch.length > 0) {
762
- await this._postBatchIngestRuns(serializePayloadForTracing(batchChunks));
762
+ const runIds = batchChunks.post
763
+ .map((item) => item.id)
764
+ .concat(batchChunks.patch.map((item) => item.id))
765
+ .join(",");
766
+ await this._postBatchIngestRuns(serializePayloadForTracing(batchChunks, `Ingesting runs with ids: ${runIds}`));
763
767
  }
764
768
  }
765
769
  async _postBatchIngestRuns(body) {
@@ -854,7 +858,7 @@ export class Client {
854
858
  const { inputs, outputs, events, attachments, ...payload } = originalPayload;
855
859
  const fields = { inputs, outputs, events };
856
860
  // encode the main run payload
857
- const stringifiedPayload = serializePayloadForTracing(payload);
861
+ const stringifiedPayload = serializePayloadForTracing(payload, `Serializing for multipart ingestion of run with id: ${payload.id}`);
858
862
  accumulatedParts.push({
859
863
  name: `${method}.${payload.id}`,
860
864
  payload: new Blob([stringifiedPayload], {
@@ -866,7 +870,7 @@ export class Client {
866
870
  if (value === undefined) {
867
871
  continue;
868
872
  }
869
- const stringifiedValue = serializePayloadForTracing(value);
873
+ const stringifiedValue = serializePayloadForTracing(value, `Serializing ${key} for multipart ingestion of run with id: ${payload.id}`);
870
874
  accumulatedParts.push({
871
875
  name: `${method}.${payload.id}.${key}`,
872
876
  payload: new Blob([stringifiedValue], {
@@ -910,34 +914,84 @@ export class Client {
910
914
  }
911
915
  await this._sendMultipartRequest(accumulatedParts, accumulatedContext.join("; "));
912
916
  }
917
+ async _createNodeFetchBody(parts, boundary) {
918
+ // Create multipart form data manually using Blobs
919
+ const chunks = [];
920
+ for (const part of parts) {
921
+ // Add field boundary
922
+ chunks.push(new Blob([`--${boundary}\r\n`]));
923
+ chunks.push(new Blob([
924
+ `Content-Disposition: form-data; name="${part.name}"\r\n`,
925
+ `Content-Type: ${part.payload.type}\r\n\r\n`,
926
+ ]));
927
+ chunks.push(part.payload);
928
+ chunks.push(new Blob(["\r\n"]));
929
+ }
930
+ // Add final boundary
931
+ chunks.push(new Blob([`--${boundary}--\r\n`]));
932
+ // Combine all chunks into a single Blob
933
+ const body = new Blob(chunks);
934
+ // Convert Blob to ArrayBuffer for compatibility
935
+ const arrayBuffer = await body.arrayBuffer();
936
+ return arrayBuffer;
937
+ }
938
+ async _createMultipartStream(parts, boundary) {
939
+ const encoder = new TextEncoder();
940
+ // Create a ReadableStream for streaming the multipart data
941
+ // Only do special handling if we're using node-fetch
942
+ const stream = new ReadableStream({
943
+ async start(controller) {
944
+ // Helper function to write a chunk to the stream
945
+ const writeChunk = async (chunk) => {
946
+ if (typeof chunk === "string") {
947
+ controller.enqueue(encoder.encode(chunk));
948
+ }
949
+ else {
950
+ controller.enqueue(chunk);
951
+ }
952
+ };
953
+ // Write each part to the stream
954
+ for (const part of parts) {
955
+ // Write boundary and headers
956
+ await writeChunk(`--${boundary}\r\n`);
957
+ await writeChunk(`Content-Disposition: form-data; name="${part.name}"\r\n`);
958
+ await writeChunk(`Content-Type: ${part.payload.type}\r\n\r\n`);
959
+ // Write the payload
960
+ const payloadStream = part.payload.stream();
961
+ const reader = payloadStream.getReader();
962
+ try {
963
+ let result;
964
+ while (!(result = await reader.read()).done) {
965
+ controller.enqueue(result.value);
966
+ }
967
+ }
968
+ finally {
969
+ reader.releaseLock();
970
+ }
971
+ await writeChunk("\r\n");
972
+ }
973
+ // Write final boundary
974
+ await writeChunk(`--${boundary}--\r\n`);
975
+ controller.close();
976
+ },
977
+ });
978
+ return stream;
979
+ }
913
980
  async _sendMultipartRequest(parts, context) {
914
981
  try {
915
- // Create multipart form data manually using Blobs
982
+ // Create multipart form data boundary
916
983
  const boundary = "----LangSmithFormBoundary" + Math.random().toString(36).slice(2);
917
- const chunks = [];
918
- for (const part of parts) {
919
- // Add field boundary
920
- chunks.push(new Blob([`--${boundary}\r\n`]));
921
- chunks.push(new Blob([
922
- `Content-Disposition: form-data; name="${part.name}"\r\n`,
923
- `Content-Type: ${part.payload.type}\r\n\r\n`,
924
- ]));
925
- chunks.push(part.payload);
926
- chunks.push(new Blob(["\r\n"]));
927
- }
928
- // Add final boundary
929
- chunks.push(new Blob([`--${boundary}--\r\n`]));
930
- // Combine all chunks into a single Blob
931
- const body = new Blob(chunks);
932
- // Convert Blob to ArrayBuffer for compatibility
933
- const arrayBuffer = await body.arrayBuffer();
984
+ const body = await (_globalFetchImplementationIsNodeFetch()
985
+ ? this._createNodeFetchBody(parts, boundary)
986
+ : this._createMultipartStream(parts, boundary));
934
987
  const res = await this.batchIngestCaller.call(_getFetchImplementation(this.debug), `${this.apiUrl}/runs/multipart`, {
935
988
  method: "POST",
936
989
  headers: {
937
990
  ...this.headers,
938
991
  "Content-Type": `multipart/form-data; boundary=${boundary}`,
939
992
  },
940
- body: arrayBuffer,
993
+ body,
994
+ duplex: "half",
941
995
  signal: AbortSignal.timeout(this.timeout_ms),
942
996
  ...this.fetchOptions,
943
997
  });
@@ -982,7 +1036,7 @@ export class Client {
982
1036
  const response = await this.caller.call(_getFetchImplementation(this.debug), `${this.apiUrl}/runs/${runId}`, {
983
1037
  method: "PATCH",
984
1038
  headers,
985
- body: serializePayloadForTracing(run),
1039
+ body: serializePayloadForTracing(run, `Serializing payload to update run with id: ${runId}`),
986
1040
  signal: AbortSignal.timeout(this.timeout_ms),
987
1041
  ...this.fetchOptions,
988
1042
  });
@@ -1453,7 +1507,9 @@ export class Client {
1453
1507
  const result = await response.json();
1454
1508
  if (!response.ok) {
1455
1509
  if ("detail" in result) {
1456
- throw new Error(`Failed to list shared examples.\nStatus: ${response.status}\nMessage: ${result.detail.join("\n")}`);
1510
+ throw new Error(`Failed to list shared examples.\nStatus: ${response.status}\nMessage: ${Array.isArray(result.detail)
1511
+ ? result.detail.join("\n")
1512
+ : "Unspecified error"}`);
1457
1513
  }
1458
1514
  throw new Error(`Failed to list shared examples: ${response.status} ${response.statusText}`);
1459
1515
  }
@@ -2968,14 +3024,14 @@ export class Client {
2968
3024
  ...(example.split && { split: example.split }),
2969
3025
  };
2970
3026
  // Add main example data
2971
- const stringifiedExample = serializePayloadForTracing(exampleBody);
3027
+ const stringifiedExample = serializePayloadForTracing(exampleBody, `Serializing body for example with id: ${exampleId}`);
2972
3028
  const exampleBlob = new Blob([stringifiedExample], {
2973
3029
  type: "application/json",
2974
3030
  });
2975
3031
  formData.append(exampleId, exampleBlob);
2976
3032
  // Add inputs if present
2977
3033
  if (example.inputs) {
2978
- const stringifiedInputs = serializePayloadForTracing(example.inputs);
3034
+ const stringifiedInputs = serializePayloadForTracing(example.inputs, `Serializing inputs for example with id: ${exampleId}`);
2979
3035
  const inputsBlob = new Blob([stringifiedInputs], {
2980
3036
  type: "application/json",
2981
3037
  });
@@ -2983,7 +3039,7 @@ export class Client {
2983
3039
  }
2984
3040
  // Add outputs if present
2985
3041
  if (example.outputs) {
2986
- const stringifiedOutputs = serializePayloadForTracing(example.outputs);
3042
+ const stringifiedOutputs = serializePayloadForTracing(example.outputs, `Serializing outputs whle updating example with id: ${exampleId}`);
2987
3043
  const outputsBlob = new Blob([stringifiedOutputs], {
2988
3044
  type: "application/json",
2989
3045
  });
@@ -3008,7 +3064,7 @@ export class Client {
3008
3064
  }
3009
3065
  }
3010
3066
  if (example.attachments_operations) {
3011
- const stringifiedAttachmentsOperations = serializePayloadForTracing(example.attachments_operations);
3067
+ const stringifiedAttachmentsOperations = serializePayloadForTracing(example.attachments_operations, `Serializing attachments while updating example with id: ${exampleId}`);
3012
3068
  const attachmentsOperationsBlob = new Blob([stringifiedAttachmentsOperations], {
3013
3069
  type: "application/json",
3014
3070
  });
@@ -3054,14 +3110,14 @@ export class Client {
3054
3110
  }),
3055
3111
  };
3056
3112
  // Add main example data
3057
- const stringifiedExample = serializePayloadForTracing(exampleBody);
3113
+ const stringifiedExample = serializePayloadForTracing(exampleBody, `Serializing body for uploaded example with id: ${exampleId}`);
3058
3114
  const exampleBlob = new Blob([stringifiedExample], {
3059
3115
  type: "application/json",
3060
3116
  });
3061
3117
  formData.append(exampleId, exampleBlob);
3062
3118
  // Add inputs if present
3063
3119
  if (example.inputs) {
3064
- const stringifiedInputs = serializePayloadForTracing(example.inputs);
3120
+ const stringifiedInputs = serializePayloadForTracing(example.inputs, `Serializing inputs for uploaded example with id: ${exampleId}`);
3065
3121
  const inputsBlob = new Blob([stringifiedInputs], {
3066
3122
  type: "application/json",
3067
3123
  });
@@ -3069,7 +3125,7 @@ export class Client {
3069
3125
  }
3070
3126
  // Add outputs if present
3071
3127
  if (example.outputs) {
3072
- const stringifiedOutputs = serializePayloadForTracing(example.outputs);
3128
+ const stringifiedOutputs = serializePayloadForTracing(example.outputs, `Serializing outputs for uploaded example with id: ${exampleId}`);
3073
3129
  const outputsBlob = new Blob([stringifiedOutputs], {
3074
3130
  type: "application/json",
3075
3131
  });
package/dist/index.cjs CHANGED
@@ -8,4 +8,4 @@ Object.defineProperty(exports, "RunTree", { enumerable: true, get: function () {
8
8
  var fetch_js_1 = require("./singletons/fetch.cjs");
9
9
  Object.defineProperty(exports, "overrideFetchImplementation", { enumerable: true, get: function () { return fetch_js_1.overrideFetchImplementation; } });
10
10
  // Update using yarn bump-version
11
- exports.__version__ = "0.3.28";
11
+ exports.__version__ = "0.3.29";
package/dist/index.d.ts CHANGED
@@ -2,4 +2,4 @@ export { Client, type ClientConfig, type LangSmithTracingClientInterface, } from
2
2
  export type { Dataset, Example, TracerSession, Run, Feedback, RetrieverOutput, } from "./schemas.js";
3
3
  export { RunTree, type RunTreeConfig } from "./run_trees.js";
4
4
  export { overrideFetchImplementation } from "./singletons/fetch.js";
5
- export declare const __version__ = "0.3.28";
5
+ export declare const __version__ = "0.3.29";
package/dist/index.js CHANGED
@@ -2,4 +2,4 @@ export { Client, } from "./client.js";
2
2
  export { RunTree } from "./run_trees.js";
3
3
  export { overrideFetchImplementation } from "./singletons/fetch.js";
4
4
  // Update using yarn bump-version
5
- export const __version__ = "0.3.28";
5
+ export const __version__ = "0.3.29";
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports._getFetchImplementation = exports.overrideFetchImplementation = void 0;
3
+ exports._getFetchImplementation = exports._globalFetchImplementationIsNodeFetch = 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:
@@ -18,6 +18,17 @@ const overrideFetchImplementation = (fetch) => {
18
18
  globalThis[LANGSMITH_FETCH_IMPLEMENTATION_KEY] = fetch;
19
19
  };
20
20
  exports.overrideFetchImplementation = overrideFetchImplementation;
21
+ const _globalFetchImplementationIsNodeFetch = () => {
22
+ const fetchImpl = globalThis[LANGSMITH_FETCH_IMPLEMENTATION_KEY];
23
+ if (!fetchImpl)
24
+ return false;
25
+ // Check if the implementation has node-fetch specific properties
26
+ return (typeof fetchImpl === "function" &&
27
+ "Headers" in fetchImpl &&
28
+ "Request" in fetchImpl &&
29
+ "Response" in fetchImpl);
30
+ };
31
+ exports._globalFetchImplementationIsNodeFetch = _globalFetchImplementationIsNodeFetch;
21
32
  /**
22
33
  * @internal
23
34
  */
@@ -5,3 +5,4 @@
5
5
  * @param fetch The new fetch functino to use.
6
6
  */
7
7
  export declare const overrideFetchImplementation: (fetch: (...args: any[]) => any) => void;
8
+ export declare const _globalFetchImplementationIsNodeFetch: () => boolean;
@@ -14,6 +14,16 @@ const LANGSMITH_FETCH_IMPLEMENTATION_KEY = Symbol.for("ls:fetch_implementation")
14
14
  export const overrideFetchImplementation = (fetch) => {
15
15
  globalThis[LANGSMITH_FETCH_IMPLEMENTATION_KEY] = fetch;
16
16
  };
17
+ export const _globalFetchImplementationIsNodeFetch = () => {
18
+ const fetchImpl = globalThis[LANGSMITH_FETCH_IMPLEMENTATION_KEY];
19
+ if (!fetchImpl)
20
+ return false;
21
+ // Check if the implementation has node-fetch specific properties
22
+ return (typeof fetchImpl === "function" &&
23
+ "Headers" in fetchImpl &&
24
+ "Request" in fetchImpl &&
25
+ "Response" in fetchImpl);
26
+ };
17
27
  /**
18
28
  * @internal
19
29
  */
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ROOT = exports.getCurrentRunTree = exports.AsyncLocalStorageProviderSingleton = void 0;
3
+ exports.ROOT = exports.AsyncLocalStorageProviderSingleton = void 0;
4
+ exports.getCurrentRunTree = getCurrentRunTree;
4
5
  exports.withRunTree = withRunTree;
5
6
  exports.isTraceableFunction = isTraceableFunction;
6
7
  const run_trees_js_1 = require("../run_trees.cjs");
@@ -28,24 +29,13 @@ class AsyncLocalStorageProvider {
28
29
  }
29
30
  }
30
31
  exports.AsyncLocalStorageProviderSingleton = new AsyncLocalStorageProvider();
31
- /**
32
- * Return the current run tree from within a traceable-wrapped function.
33
- * Will throw an error if called outside of a traceable function.
34
- *
35
- * @returns The run tree for the given context.
36
- */
37
- const getCurrentRunTree = () => {
32
+ function getCurrentRunTree(permitAbsentRunTree = false) {
38
33
  const runTree = exports.AsyncLocalStorageProviderSingleton.getInstance().getStore();
39
- if (!(0, run_trees_js_1.isRunTree)(runTree)) {
40
- throw new Error([
41
- "Could not get the current run tree.",
42
- "",
43
- "Please make sure you are calling this method within a traceable function and that tracing is enabled.",
44
- ].join("\n"));
34
+ if (!permitAbsentRunTree && !(0, run_trees_js_1.isRunTree)(runTree)) {
35
+ throw new Error("Could not get the current run tree.\n\nPlease make sure you are calling this method within a traceable function and that tracing is enabled.");
45
36
  }
46
37
  return runTree;
47
- };
48
- exports.getCurrentRunTree = getCurrentRunTree;
38
+ }
49
39
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
40
  function withRunTree(runTree, fn) {
51
41
  const storage = exports.AsyncLocalStorageProviderSingleton.getInstance();
@@ -15,7 +15,9 @@ export declare const AsyncLocalStorageProviderSingleton: AsyncLocalStorageProvid
15
15
  *
16
16
  * @returns The run tree for the given context.
17
17
  */
18
- export declare const getCurrentRunTree: () => RunTree;
18
+ export declare function getCurrentRunTree(): RunTree;
19
+ export declare function getCurrentRunTree(permitAbsentRunTree: false): RunTree;
20
+ export declare function getCurrentRunTree(permitAbsentRunTree: boolean): RunTree | undefined;
19
21
  export declare function withRunTree<Fn extends (...args: any[]) => any>(runTree: RunTree, fn: Fn): Promise<Awaited<ReturnType<Fn>>>;
20
22
  export declare const ROOT: unique symbol;
21
23
  export declare function isTraceableFunction(x: unknown): x is TraceableFunction<any>;
@@ -23,23 +23,13 @@ class AsyncLocalStorageProvider {
23
23
  }
24
24
  }
25
25
  export const AsyncLocalStorageProviderSingleton = new AsyncLocalStorageProvider();
26
- /**
27
- * Return the current run tree from within a traceable-wrapped function.
28
- * Will throw an error if called outside of a traceable function.
29
- *
30
- * @returns The run tree for the given context.
31
- */
32
- export const getCurrentRunTree = () => {
26
+ export function getCurrentRunTree(permitAbsentRunTree = false) {
33
27
  const runTree = AsyncLocalStorageProviderSingleton.getInstance().getStore();
34
- if (!isRunTree(runTree)) {
35
- throw new Error([
36
- "Could not get the current run tree.",
37
- "",
38
- "Please make sure you are calling this method within a traceable function and that tracing is enabled.",
39
- ].join("\n"));
28
+ if (!permitAbsentRunTree && !isRunTree(runTree)) {
29
+ throw new Error("Could not get the current run tree.\n\nPlease make sure you are calling this method within a traceable function and that tracing is enabled.");
40
30
  }
41
31
  return runTree;
42
- };
32
+ }
43
33
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
34
  export function withRunTree(runTree, fn) {
45
35
  const storage = AsyncLocalStorageProviderSingleton.getInstance();
@@ -15,7 +15,7 @@ import { TraceableFunction } from "./singletons/types.js";
15
15
  * @param config Additional metadata such as name, tags or providing
16
16
  * a custom LangSmith client instance
17
17
  */
18
- export declare function traceable<Func extends (...args: any[]) => any>(wrappedFunc: Func, config?: Partial<RunTreeConfig> & {
18
+ export declare function traceable<Func extends (...args: any[]) => any>(wrappedFunc: Func, config?: Partial<Omit<RunTreeConfig, "inputs" | "outputs">> & {
19
19
  aggregator?: (args: any[]) => any;
20
20
  argsConfigPath?: [number] | [number, string];
21
21
  __finalTracedIteratorKey?: string;
@@ -18,7 +18,7 @@ function encodeString(str) {
18
18
  return encoder.encode(str);
19
19
  }
20
20
  // Regular stringify
21
- function serialize(obj, replacer, spacer, options) {
21
+ function serialize(obj, errorContext, replacer, spacer, options) {
22
22
  try {
23
23
  const str = JSON.stringify(obj, replacer, spacer);
24
24
  return encodeString(str);
@@ -26,10 +26,10 @@ function serialize(obj, replacer, spacer, options) {
26
26
  catch (e) {
27
27
  // Fall back to more complex stringify if circular reference
28
28
  if (!e.message?.includes("Converting circular structure to JSON")) {
29
- console.warn("[WARNING]: LangSmith received unserializable value.");
29
+ console.warn(`[WARNING]: LangSmith received unserializable value.${errorContext ? `\nContext: ${errorContext}` : ""}`);
30
30
  return encodeString("[Unserializable]");
31
31
  }
32
- console.warn("[WARNING]: LangSmith received circular JSON. This will decrease tracer performance.");
32
+ console.warn(`[WARNING]: LangSmith received circular JSON. This will decrease tracer performance. ${errorContext ? `\nContext: ${errorContext}` : ""}`);
33
33
  if (typeof options === "undefined") {
34
34
  options = defaultOptions();
35
35
  }
@@ -1 +1 @@
1
- export declare function serialize(obj: any, replacer?: any, spacer?: any, options?: any): Uint8Array<ArrayBufferLike>;
1
+ export declare function serialize(obj: any, errorContext?: any, replacer?: any, spacer?: any, options?: any): Uint8Array<ArrayBufferLike>;
@@ -15,7 +15,7 @@ function encodeString(str) {
15
15
  return encoder.encode(str);
16
16
  }
17
17
  // Regular stringify
18
- export function serialize(obj, replacer, spacer, options) {
18
+ export function serialize(obj, errorContext, replacer, spacer, options) {
19
19
  try {
20
20
  const str = JSON.stringify(obj, replacer, spacer);
21
21
  return encodeString(str);
@@ -23,10 +23,10 @@ export function serialize(obj, replacer, spacer, options) {
23
23
  catch (e) {
24
24
  // Fall back to more complex stringify if circular reference
25
25
  if (!e.message?.includes("Converting circular structure to JSON")) {
26
- console.warn("[WARNING]: LangSmith received unserializable value.");
26
+ console.warn(`[WARNING]: LangSmith received unserializable value.${errorContext ? `\nContext: ${errorContext}` : ""}`);
27
27
  return encodeString("[Unserializable]");
28
28
  }
29
- console.warn("[WARNING]: LangSmith received circular JSON. This will decrease tracer performance.");
29
+ console.warn(`[WARNING]: LangSmith received circular JSON. This will decrease tracer performance. ${errorContext ? `\nContext: ${errorContext}` : ""}`);
30
30
  if (typeof options === "undefined") {
31
31
  options = defaultOptions();
32
32
  }
@@ -29,7 +29,7 @@ type PatchedOpenAIClient<T extends OpenAIType> = T & {
29
29
  } & {
30
30
  (arg: OpenAI.ChatCompletionCreateParamsNonStreaming, arg2?: OpenAI.RequestOptions & {
31
31
  langsmithExtra?: ExtraRunTreeConfig;
32
- }): APIPromise<OpenAI.ChatCompletionChunk>;
32
+ }): APIPromise<OpenAI.ChatCompletion>;
33
33
  };
34
34
  };
35
35
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "langsmith",
3
- "version": "0.3.28",
3
+ "version": "0.3.29",
4
4
  "description": "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform.",
5
5
  "packageManager": "yarn@1.22.19",
6
6
  "files": [
@@ -142,9 +142,9 @@
142
142
  "@langchain/openai": "^0.3.11",
143
143
  "@opentelemetry/api": "^1.9.0",
144
144
  "@opentelemetry/auto-instrumentations-node": "^0.58.0",
145
+ "@opentelemetry/sdk-node": "^0.200.0",
145
146
  "@opentelemetry/sdk-trace-base": "^2.0.0",
146
147
  "@opentelemetry/sdk-trace-node": "^2.0.0",
147
- "@opentelemetry/sdk-node": "^0.200.0",
148
148
  "@tsconfig/recommended": "^1.0.2",
149
149
  "@types/jest": "^29.5.1",
150
150
  "@typescript-eslint/eslint-plugin": "^5.59.8",
@@ -168,7 +168,7 @@
168
168
  "typedoc": "^0.27.6",
169
169
  "typedoc-plugin-expand-object-like-types": "^0.1.2",
170
170
  "typescript": "^5.4.5",
171
- "vitest": "^3.0.5",
171
+ "vitest": "^3.1.3",
172
172
  "zod": "^3.23.8"
173
173
  },
174
174
  "peerDependencies": {