langsmith 0.3.41 → 0.3.43
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 +119 -38
- package/dist/client.d.ts +22 -2
- package/dist/client.js +119 -38
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/run_trees.cjs +55 -5
- package/dist/run_trees.d.ts +11 -2
- package/dist/run_trees.js +55 -5
- package/dist/utils/error.cjs +22 -1
- package/dist/utils/error.d.ts +5 -0
- package/dist/utils/error.js +19 -0
- package/package.json +1 -1
package/dist/client.cjs
CHANGED
|
@@ -152,6 +152,8 @@ class AutoBatchQueue {
|
|
|
152
152
|
action: item.action,
|
|
153
153
|
payload: item.item,
|
|
154
154
|
otelContext: item.otelContext,
|
|
155
|
+
apiKey: item.apiKey,
|
|
156
|
+
apiUrl: item.apiUrl,
|
|
155
157
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
156
158
|
itemPromiseResolve: itemPromiseResolve,
|
|
157
159
|
itemPromise,
|
|
@@ -189,6 +191,8 @@ class AutoBatchQueue {
|
|
|
189
191
|
action: it.action,
|
|
190
192
|
item: it.payload,
|
|
191
193
|
otelContext: it.otelContext,
|
|
194
|
+
apiKey: it.apiKey,
|
|
195
|
+
apiUrl: it.apiUrl,
|
|
192
196
|
})),
|
|
193
197
|
() => popped.forEach((it) => it.itemPromiseResolve()),
|
|
194
198
|
];
|
|
@@ -198,6 +202,7 @@ exports.AutoBatchQueue = AutoBatchQueue;
|
|
|
198
202
|
// 20 MB
|
|
199
203
|
exports.DEFAULT_BATCH_SIZE_LIMIT_BYTES = 20_971_520;
|
|
200
204
|
const SERVER_INFO_REQUEST_TIMEOUT = 2500;
|
|
205
|
+
const DEFAULT_API_URL = "https://api.smith.langchain.com";
|
|
201
206
|
class Client {
|
|
202
207
|
constructor(config = {}) {
|
|
203
208
|
Object.defineProperty(this, "apiKey", {
|
|
@@ -345,6 +350,12 @@ class Client {
|
|
|
345
350
|
writable: true,
|
|
346
351
|
value: void 0
|
|
347
352
|
});
|
|
353
|
+
Object.defineProperty(this, "multipartStreamingDisabled", {
|
|
354
|
+
enumerable: true,
|
|
355
|
+
configurable: true,
|
|
356
|
+
writable: true,
|
|
357
|
+
value: false
|
|
358
|
+
});
|
|
348
359
|
Object.defineProperty(this, "debug", {
|
|
349
360
|
enumerable: true,
|
|
350
361
|
configurable: true,
|
|
@@ -396,8 +407,7 @@ class Client {
|
|
|
396
407
|
}
|
|
397
408
|
static getDefaultClientConfig() {
|
|
398
409
|
const apiKey = (0, env_js_1.getLangSmithEnvironmentVariable)("API_KEY");
|
|
399
|
-
const apiUrl = (0, env_js_1.getLangSmithEnvironmentVariable)("ENDPOINT") ??
|
|
400
|
-
"https://api.smith.langchain.com";
|
|
410
|
+
const apiUrl = (0, env_js_1.getLangSmithEnvironmentVariable)("ENDPOINT") ?? DEFAULT_API_URL;
|
|
401
411
|
const hideInputs = (0, env_js_1.getLangSmithEnvironmentVariable)("HIDE_INPUTS") === "true";
|
|
402
412
|
const hideOutputs = (0, env_js_1.getLangSmithEnvironmentVariable)("HIDE_OUTPUTS") === "true";
|
|
403
413
|
return {
|
|
@@ -451,6 +461,11 @@ class Client {
|
|
|
451
461
|
}
|
|
452
462
|
return headers;
|
|
453
463
|
}
|
|
464
|
+
_getPlatformEndpointPath(path) {
|
|
465
|
+
// Check if apiUrl already ends with /v1 or /v1/ to avoid double /v1/v1/ paths
|
|
466
|
+
const needsV1Prefix = this.apiUrl.slice(-3) !== "/v1" && this.apiUrl.slice(-4) !== "/v1/";
|
|
467
|
+
return needsV1Prefix ? `/v1/platform/${path}` : `/platform/${path}`;
|
|
468
|
+
}
|
|
454
469
|
async processInputs(inputs) {
|
|
455
470
|
if (this.hideInputs === false) {
|
|
456
471
|
return inputs;
|
|
@@ -623,14 +638,33 @@ class Client {
|
|
|
623
638
|
done();
|
|
624
639
|
break;
|
|
625
640
|
}
|
|
626
|
-
const
|
|
627
|
-
|
|
641
|
+
const batchesByDestination = batch.reduce((acc, item) => {
|
|
642
|
+
const apiUrl = item.apiUrl ?? this.apiUrl;
|
|
643
|
+
const apiKey = item.apiKey ?? this.apiKey;
|
|
644
|
+
const isDefault = item.apiKey === this.apiKey && item.apiUrl === this.apiUrl;
|
|
645
|
+
const batchKey = isDefault ? "default" : `${apiUrl}|${apiKey}`;
|
|
646
|
+
if (!acc[batchKey]) {
|
|
647
|
+
acc[batchKey] = [];
|
|
648
|
+
}
|
|
649
|
+
acc[batchKey].push(item);
|
|
650
|
+
return acc;
|
|
651
|
+
}, {});
|
|
652
|
+
const batchPromises = [];
|
|
653
|
+
for (const [batchKey, batch] of Object.entries(batchesByDestination)) {
|
|
654
|
+
const batchPromise = this._processBatch(batch, {
|
|
655
|
+
apiUrl: batchKey === "default" ? undefined : batchKey.split("|")[0],
|
|
656
|
+
apiKey: batchKey === "default" ? undefined : batchKey.split("|")[1],
|
|
657
|
+
});
|
|
658
|
+
batchPromises.push(batchPromise);
|
|
659
|
+
}
|
|
660
|
+
// Wait for all batches to complete, then call the overall done callback
|
|
661
|
+
const allBatchesPromise = Promise.all(batchPromises).finally(done);
|
|
662
|
+
promises.push(allBatchesPromise);
|
|
628
663
|
}
|
|
629
664
|
return Promise.all(promises);
|
|
630
665
|
}
|
|
631
|
-
async _processBatch(batch,
|
|
666
|
+
async _processBatch(batch, options) {
|
|
632
667
|
if (!batch.length) {
|
|
633
|
-
done();
|
|
634
668
|
return;
|
|
635
669
|
}
|
|
636
670
|
try {
|
|
@@ -648,19 +682,16 @@ class Client {
|
|
|
648
682
|
};
|
|
649
683
|
const serverInfo = await this._ensureServerInfo();
|
|
650
684
|
if (serverInfo?.batch_ingest_config?.use_multipart_endpoint) {
|
|
651
|
-
await this.multipartIngestRuns(ingestParams);
|
|
685
|
+
await this.multipartIngestRuns(ingestParams, options);
|
|
652
686
|
}
|
|
653
687
|
else {
|
|
654
|
-
await this.batchIngestRuns(ingestParams);
|
|
688
|
+
await this.batchIngestRuns(ingestParams, options);
|
|
655
689
|
}
|
|
656
690
|
}
|
|
657
691
|
}
|
|
658
692
|
catch (e) {
|
|
659
693
|
console.error("Error exporting batch:", e);
|
|
660
694
|
}
|
|
661
|
-
finally {
|
|
662
|
-
done();
|
|
663
|
-
}
|
|
664
695
|
}
|
|
665
696
|
_sendBatchToOTELTranslator(batch) {
|
|
666
697
|
if (this.langSmithToOTELTranslator !== undefined) {
|
|
@@ -774,11 +805,14 @@ class Client {
|
|
|
774
805
|
}
|
|
775
806
|
return undefined;
|
|
776
807
|
}
|
|
777
|
-
async createRun(run) {
|
|
808
|
+
async createRun(run, options) {
|
|
778
809
|
if (!this._filterForSampling([run]).length) {
|
|
779
810
|
return;
|
|
780
811
|
}
|
|
781
|
-
const headers = {
|
|
812
|
+
const headers = {
|
|
813
|
+
...this.headers,
|
|
814
|
+
"Content-Type": "application/json",
|
|
815
|
+
};
|
|
782
816
|
const session_name = run.project_name;
|
|
783
817
|
delete run.project_name;
|
|
784
818
|
const runCreate = await this.prepareRunCreateOrUpdateInputs({
|
|
@@ -794,11 +828,16 @@ class Client {
|
|
|
794
828
|
action: "create",
|
|
795
829
|
item: runCreate,
|
|
796
830
|
otelContext,
|
|
831
|
+
apiKey: options?.apiKey,
|
|
832
|
+
apiUrl: options?.apiUrl,
|
|
797
833
|
}).catch(console.error);
|
|
798
834
|
return;
|
|
799
835
|
}
|
|
800
836
|
const mergedRunCreateParam = mergeRuntimeEnvIntoRunCreate(runCreate);
|
|
801
|
-
|
|
837
|
+
if (options?.apiKey !== undefined) {
|
|
838
|
+
headers["x-api-key"] = options.apiKey;
|
|
839
|
+
}
|
|
840
|
+
const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${options?.apiUrl ?? this.apiUrl}/runs`, {
|
|
802
841
|
method: "POST",
|
|
803
842
|
headers,
|
|
804
843
|
body: (0, index_js_2.serialize)(mergedRunCreateParam, `Creating run with id: ${mergedRunCreateParam.id}`),
|
|
@@ -811,7 +850,7 @@ class Client {
|
|
|
811
850
|
* Batch ingest/upsert multiple runs in the Langsmith system.
|
|
812
851
|
* @param runs
|
|
813
852
|
*/
|
|
814
|
-
async batchIngestRuns({ runCreates, runUpdates, }) {
|
|
853
|
+
async batchIngestRuns({ runCreates, runUpdates, }, options) {
|
|
815
854
|
if (runCreates === undefined && runUpdates === undefined) {
|
|
816
855
|
return;
|
|
817
856
|
}
|
|
@@ -866,16 +905,19 @@ class Client {
|
|
|
866
905
|
.map((item) => item.id)
|
|
867
906
|
.concat(batchChunks.patch.map((item) => item.id))
|
|
868
907
|
.join(",");
|
|
869
|
-
await this._postBatchIngestRuns((0, index_js_2.serialize)(batchChunks, `Ingesting runs with ids: ${runIds}`));
|
|
908
|
+
await this._postBatchIngestRuns((0, index_js_2.serialize)(batchChunks, `Ingesting runs with ids: ${runIds}`), options);
|
|
870
909
|
}
|
|
871
910
|
}
|
|
872
|
-
async _postBatchIngestRuns(body) {
|
|
911
|
+
async _postBatchIngestRuns(body, options) {
|
|
873
912
|
const headers = {
|
|
874
913
|
...this.headers,
|
|
875
914
|
"Content-Type": "application/json",
|
|
876
915
|
Accept: "application/json",
|
|
877
916
|
};
|
|
878
|
-
|
|
917
|
+
if (options?.apiKey !== undefined) {
|
|
918
|
+
headers["x-api-key"] = options.apiKey;
|
|
919
|
+
}
|
|
920
|
+
const response = await this.batchIngestCaller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${options?.apiUrl ?? this.apiUrl}/runs/batch`, {
|
|
879
921
|
method: "POST",
|
|
880
922
|
headers,
|
|
881
923
|
body: body,
|
|
@@ -888,7 +930,7 @@ class Client {
|
|
|
888
930
|
* Batch ingest/upsert multiple runs in the Langsmith system.
|
|
889
931
|
* @param runs
|
|
890
932
|
*/
|
|
891
|
-
async multipartIngestRuns({ runCreates, runUpdates, }) {
|
|
933
|
+
async multipartIngestRuns({ runCreates, runUpdates, }, options) {
|
|
892
934
|
if (runCreates === undefined && runUpdates === undefined) {
|
|
893
935
|
return;
|
|
894
936
|
}
|
|
@@ -1015,7 +1057,7 @@ class Client {
|
|
|
1015
1057
|
accumulatedContext.push(`trace=${payload.trace_id},id=${payload.id}`);
|
|
1016
1058
|
}
|
|
1017
1059
|
}
|
|
1018
|
-
await this._sendMultipartRequest(accumulatedParts, accumulatedContext.join("; "));
|
|
1060
|
+
await this._sendMultipartRequest(accumulatedParts, accumulatedContext.join("; "), options);
|
|
1019
1061
|
}
|
|
1020
1062
|
async _createNodeFetchBody(parts, boundary) {
|
|
1021
1063
|
// Create multipart form data manually using Blobs
|
|
@@ -1080,24 +1122,53 @@ class Client {
|
|
|
1080
1122
|
});
|
|
1081
1123
|
return stream;
|
|
1082
1124
|
}
|
|
1083
|
-
async _sendMultipartRequest(parts, context) {
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
const
|
|
1125
|
+
async _sendMultipartRequest(parts, context, options) {
|
|
1126
|
+
// Create multipart form data boundary
|
|
1127
|
+
const boundary = "----LangSmithFormBoundary" + Math.random().toString(36).slice(2);
|
|
1128
|
+
const isNodeFetch = (0, fetch_js_1._globalFetchImplementationIsNodeFetch)();
|
|
1129
|
+
const buildBuffered = () => this._createNodeFetchBody(parts, boundary);
|
|
1130
|
+
const buildStream = () => this._createMultipartStream(parts, boundary);
|
|
1131
|
+
const send = async (body) => {
|
|
1132
|
+
const headers = {
|
|
1133
|
+
...this.headers,
|
|
1134
|
+
"Content-Type": `multipart/form-data; boundary=${boundary}`,
|
|
1135
|
+
};
|
|
1136
|
+
if (options?.apiKey !== undefined) {
|
|
1137
|
+
headers["x-api-key"] = options.apiKey;
|
|
1138
|
+
}
|
|
1139
|
+
return this.batchIngestCaller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${options?.apiUrl ?? this.apiUrl}/runs/multipart`, {
|
|
1091
1140
|
method: "POST",
|
|
1092
|
-
headers
|
|
1093
|
-
...this.headers,
|
|
1094
|
-
"Content-Type": `multipart/form-data; boundary=${boundary}`,
|
|
1095
|
-
},
|
|
1141
|
+
headers,
|
|
1096
1142
|
body,
|
|
1097
1143
|
duplex: "half",
|
|
1098
1144
|
signal: AbortSignal.timeout(this.timeout_ms),
|
|
1099
1145
|
...this.fetchOptions,
|
|
1100
1146
|
});
|
|
1147
|
+
};
|
|
1148
|
+
try {
|
|
1149
|
+
let res;
|
|
1150
|
+
let streamedAttempt = false;
|
|
1151
|
+
// attempt stream only if not disabled and not using node-fetch
|
|
1152
|
+
if (!isNodeFetch && !this.multipartStreamingDisabled) {
|
|
1153
|
+
streamedAttempt = true;
|
|
1154
|
+
res = await send(await buildStream());
|
|
1155
|
+
}
|
|
1156
|
+
else {
|
|
1157
|
+
res = await send(await buildBuffered());
|
|
1158
|
+
}
|
|
1159
|
+
// if stream fails, fallback to buffered body
|
|
1160
|
+
if ((!this.multipartStreamingDisabled || streamedAttempt) &&
|
|
1161
|
+
res.status === 422 &&
|
|
1162
|
+
(options?.apiUrl ?? this.apiUrl) !== DEFAULT_API_URL) {
|
|
1163
|
+
console.warn(`Streaming multipart upload to ${options?.apiUrl ?? this.apiUrl}/runs/multipart failed. ` +
|
|
1164
|
+
`This usually means the host does not support chunked uploads. ` +
|
|
1165
|
+
`Retrying with a buffered upload for operation "${context}".`);
|
|
1166
|
+
// Disable streaming for future requests
|
|
1167
|
+
this.multipartStreamingDisabled = true;
|
|
1168
|
+
// retry with fully-buffered body
|
|
1169
|
+
res = await send(await buildBuffered());
|
|
1170
|
+
}
|
|
1171
|
+
// raise if still failing
|
|
1101
1172
|
await (0, error_js_1.raiseForStatus)(res, "ingest multipart runs", true);
|
|
1102
1173
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1103
1174
|
}
|
|
@@ -1105,7 +1176,7 @@ class Client {
|
|
|
1105
1176
|
console.warn(`${e.message.trim()}\n\nContext: ${context}`);
|
|
1106
1177
|
}
|
|
1107
1178
|
}
|
|
1108
|
-
async updateRun(runId, run) {
|
|
1179
|
+
async updateRun(runId, run, options) {
|
|
1109
1180
|
(0, _uuid_js_1.assertUuid)(runId);
|
|
1110
1181
|
if (run.inputs) {
|
|
1111
1182
|
run.inputs = await this.processInputs(run.inputs);
|
|
@@ -1132,6 +1203,8 @@ class Client {
|
|
|
1132
1203
|
action: "update",
|
|
1133
1204
|
item: data,
|
|
1134
1205
|
otelContext,
|
|
1206
|
+
apiKey: options?.apiKey,
|
|
1207
|
+
apiUrl: options?.apiUrl,
|
|
1135
1208
|
}).catch(console.error);
|
|
1136
1209
|
return;
|
|
1137
1210
|
}
|
|
@@ -1140,12 +1213,20 @@ class Client {
|
|
|
1140
1213
|
action: "update",
|
|
1141
1214
|
item: data,
|
|
1142
1215
|
otelContext,
|
|
1216
|
+
apiKey: options?.apiKey,
|
|
1217
|
+
apiUrl: options?.apiUrl,
|
|
1143
1218
|
}).catch(console.error);
|
|
1144
1219
|
}
|
|
1145
1220
|
return;
|
|
1146
1221
|
}
|
|
1147
|
-
const headers = {
|
|
1148
|
-
|
|
1222
|
+
const headers = {
|
|
1223
|
+
...this.headers,
|
|
1224
|
+
"Content-Type": "application/json",
|
|
1225
|
+
};
|
|
1226
|
+
if (options?.apiKey !== undefined) {
|
|
1227
|
+
headers["x-api-key"] = options.apiKey;
|
|
1228
|
+
}
|
|
1229
|
+
const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${options?.apiUrl ?? this.apiUrl}/runs/${runId}`, {
|
|
1149
1230
|
method: "PATCH",
|
|
1150
1231
|
headers,
|
|
1151
1232
|
body: (0, index_js_2.serialize)(run, `Serializing payload to update run with id: ${runId}`),
|
|
@@ -3072,7 +3153,7 @@ class Client {
|
|
|
3072
3153
|
const settings = await this._getSettings();
|
|
3073
3154
|
if (options?.isPublic && !settings.tenant_handle) {
|
|
3074
3155
|
throw new Error(`Cannot create a public prompt without first\n
|
|
3075
|
-
creating a LangChain Hub handle.
|
|
3156
|
+
creating a LangChain Hub handle.
|
|
3076
3157
|
You can add a handle by creating a public prompt at:\n
|
|
3077
3158
|
https://smith.langchain.com/prompts`);
|
|
3078
3159
|
}
|
|
@@ -3190,7 +3271,7 @@ class Client {
|
|
|
3190
3271
|
}
|
|
3191
3272
|
}
|
|
3192
3273
|
const datasetIdToUse = datasetId ?? updates[0]?.dataset_id;
|
|
3193
|
-
const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}
|
|
3274
|
+
const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}${this._getPlatformEndpointPath(`datasets/${datasetIdToUse}/examples`)}`, {
|
|
3194
3275
|
method: "PATCH",
|
|
3195
3276
|
headers: this.headers,
|
|
3196
3277
|
body: formData,
|
|
@@ -3268,7 +3349,7 @@ class Client {
|
|
|
3268
3349
|
}
|
|
3269
3350
|
}
|
|
3270
3351
|
}
|
|
3271
|
-
const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}
|
|
3352
|
+
const response = await this.caller.call((0, fetch_js_1._getFetchImplementation)(this.debug), `${this.apiUrl}${this._getPlatformEndpointPath(`datasets/${datasetId}/examples`)}`, {
|
|
3272
3353
|
method: "POST",
|
|
3273
3354
|
headers: this.headers,
|
|
3274
3355
|
body: formData,
|
package/dist/client.d.ts
CHANGED
|
@@ -242,6 +242,8 @@ type AutoBatchQueueItem = {
|
|
|
242
242
|
action: "create" | "update";
|
|
243
243
|
item: RunCreate | RunUpdate;
|
|
244
244
|
otelContext?: OTELContext;
|
|
245
|
+
apiKey?: string;
|
|
246
|
+
apiUrl?: string;
|
|
245
247
|
};
|
|
246
248
|
type Thread = {
|
|
247
249
|
filter: string;
|
|
@@ -267,6 +269,8 @@ export declare class AutoBatchQueue {
|
|
|
267
269
|
itemPromiseResolve: () => void;
|
|
268
270
|
itemPromise: Promise<void>;
|
|
269
271
|
size: number;
|
|
272
|
+
apiKey?: string;
|
|
273
|
+
apiUrl?: string;
|
|
270
274
|
}[];
|
|
271
275
|
sizeBytes: number;
|
|
272
276
|
peek(): {
|
|
@@ -276,6 +280,8 @@ export declare class AutoBatchQueue {
|
|
|
276
280
|
itemPromiseResolve: () => void;
|
|
277
281
|
itemPromise: Promise<void>;
|
|
278
282
|
size: number;
|
|
283
|
+
apiKey?: string;
|
|
284
|
+
apiUrl?: string;
|
|
279
285
|
};
|
|
280
286
|
push(item: AutoBatchQueueItem): Promise<void>;
|
|
281
287
|
pop(upToSizeBytes: number): [AutoBatchQueueItem[], () => void];
|
|
@@ -306,6 +312,7 @@ export declare class Client implements LangSmithTracingClientInterface {
|
|
|
306
312
|
private _getServerInfoPromise?;
|
|
307
313
|
private manualFlushMode;
|
|
308
314
|
private langSmithToOTELTranslator?;
|
|
315
|
+
private multipartStreamingDisabled;
|
|
309
316
|
debug: boolean;
|
|
310
317
|
constructor(config?: ClientConfig);
|
|
311
318
|
static getDefaultClientConfig(): {
|
|
@@ -317,6 +324,7 @@ export declare class Client implements LangSmithTracingClientInterface {
|
|
|
317
324
|
};
|
|
318
325
|
getHostUrl(): string;
|
|
319
326
|
private get headers();
|
|
327
|
+
private _getPlatformEndpointPath;
|
|
320
328
|
private processInputs;
|
|
321
329
|
private processOutputs;
|
|
322
330
|
private prepareRunCreateOrUpdateInputs;
|
|
@@ -340,7 +348,10 @@ export declare class Client implements LangSmithTracingClientInterface {
|
|
|
340
348
|
*/
|
|
341
349
|
flush(): Promise<void>;
|
|
342
350
|
private _cloneCurrentOTELContext;
|
|
343
|
-
createRun(run: CreateRunParams
|
|
351
|
+
createRun(run: CreateRunParams, options?: {
|
|
352
|
+
apiKey?: string;
|
|
353
|
+
apiUrl?: string;
|
|
354
|
+
}): Promise<void>;
|
|
344
355
|
/**
|
|
345
356
|
* Batch ingest/upsert multiple runs in the Langsmith system.
|
|
346
357
|
* @param runs
|
|
@@ -348,6 +359,9 @@ export declare class Client implements LangSmithTracingClientInterface {
|
|
|
348
359
|
batchIngestRuns({ runCreates, runUpdates, }: {
|
|
349
360
|
runCreates?: RunCreate[];
|
|
350
361
|
runUpdates?: RunUpdate[];
|
|
362
|
+
}, options?: {
|
|
363
|
+
apiKey?: string;
|
|
364
|
+
apiUrl?: string;
|
|
351
365
|
}): Promise<void>;
|
|
352
366
|
private _postBatchIngestRuns;
|
|
353
367
|
/**
|
|
@@ -357,11 +371,17 @@ export declare class Client implements LangSmithTracingClientInterface {
|
|
|
357
371
|
multipartIngestRuns({ runCreates, runUpdates, }: {
|
|
358
372
|
runCreates?: RunCreate[];
|
|
359
373
|
runUpdates?: RunUpdate[];
|
|
374
|
+
}, options?: {
|
|
375
|
+
apiKey?: string;
|
|
376
|
+
apiUrl?: string;
|
|
360
377
|
}): Promise<void>;
|
|
361
378
|
private _createNodeFetchBody;
|
|
362
379
|
private _createMultipartStream;
|
|
363
380
|
private _sendMultipartRequest;
|
|
364
|
-
updateRun(runId: string, run: RunUpdate
|
|
381
|
+
updateRun(runId: string, run: RunUpdate, options?: {
|
|
382
|
+
apiKey?: string;
|
|
383
|
+
apiUrl?: string;
|
|
384
|
+
}): Promise<void>;
|
|
365
385
|
readRun(runId: string, { loadChildRuns }?: {
|
|
366
386
|
loadChildRuns: boolean;
|
|
367
387
|
}): Promise<Run>;
|
package/dist/client.js
CHANGED
|
@@ -115,6 +115,8 @@ export class AutoBatchQueue {
|
|
|
115
115
|
action: item.action,
|
|
116
116
|
payload: item.item,
|
|
117
117
|
otelContext: item.otelContext,
|
|
118
|
+
apiKey: item.apiKey,
|
|
119
|
+
apiUrl: item.apiUrl,
|
|
118
120
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
119
121
|
itemPromiseResolve: itemPromiseResolve,
|
|
120
122
|
itemPromise,
|
|
@@ -152,6 +154,8 @@ export class AutoBatchQueue {
|
|
|
152
154
|
action: it.action,
|
|
153
155
|
item: it.payload,
|
|
154
156
|
otelContext: it.otelContext,
|
|
157
|
+
apiKey: it.apiKey,
|
|
158
|
+
apiUrl: it.apiUrl,
|
|
155
159
|
})),
|
|
156
160
|
() => popped.forEach((it) => it.itemPromiseResolve()),
|
|
157
161
|
];
|
|
@@ -160,6 +164,7 @@ export class AutoBatchQueue {
|
|
|
160
164
|
// 20 MB
|
|
161
165
|
export const DEFAULT_BATCH_SIZE_LIMIT_BYTES = 20_971_520;
|
|
162
166
|
const SERVER_INFO_REQUEST_TIMEOUT = 2500;
|
|
167
|
+
const DEFAULT_API_URL = "https://api.smith.langchain.com";
|
|
163
168
|
export class Client {
|
|
164
169
|
constructor(config = {}) {
|
|
165
170
|
Object.defineProperty(this, "apiKey", {
|
|
@@ -307,6 +312,12 @@ export class Client {
|
|
|
307
312
|
writable: true,
|
|
308
313
|
value: void 0
|
|
309
314
|
});
|
|
315
|
+
Object.defineProperty(this, "multipartStreamingDisabled", {
|
|
316
|
+
enumerable: true,
|
|
317
|
+
configurable: true,
|
|
318
|
+
writable: true,
|
|
319
|
+
value: false
|
|
320
|
+
});
|
|
310
321
|
Object.defineProperty(this, "debug", {
|
|
311
322
|
enumerable: true,
|
|
312
323
|
configurable: true,
|
|
@@ -358,8 +369,7 @@ export class Client {
|
|
|
358
369
|
}
|
|
359
370
|
static getDefaultClientConfig() {
|
|
360
371
|
const apiKey = getLangSmithEnvironmentVariable("API_KEY");
|
|
361
|
-
const apiUrl = getLangSmithEnvironmentVariable("ENDPOINT") ??
|
|
362
|
-
"https://api.smith.langchain.com";
|
|
372
|
+
const apiUrl = getLangSmithEnvironmentVariable("ENDPOINT") ?? DEFAULT_API_URL;
|
|
363
373
|
const hideInputs = getLangSmithEnvironmentVariable("HIDE_INPUTS") === "true";
|
|
364
374
|
const hideOutputs = getLangSmithEnvironmentVariable("HIDE_OUTPUTS") === "true";
|
|
365
375
|
return {
|
|
@@ -413,6 +423,11 @@ export class Client {
|
|
|
413
423
|
}
|
|
414
424
|
return headers;
|
|
415
425
|
}
|
|
426
|
+
_getPlatformEndpointPath(path) {
|
|
427
|
+
// Check if apiUrl already ends with /v1 or /v1/ to avoid double /v1/v1/ paths
|
|
428
|
+
const needsV1Prefix = this.apiUrl.slice(-3) !== "/v1" && this.apiUrl.slice(-4) !== "/v1/";
|
|
429
|
+
return needsV1Prefix ? `/v1/platform/${path}` : `/platform/${path}`;
|
|
430
|
+
}
|
|
416
431
|
async processInputs(inputs) {
|
|
417
432
|
if (this.hideInputs === false) {
|
|
418
433
|
return inputs;
|
|
@@ -585,14 +600,33 @@ export class Client {
|
|
|
585
600
|
done();
|
|
586
601
|
break;
|
|
587
602
|
}
|
|
588
|
-
const
|
|
589
|
-
|
|
603
|
+
const batchesByDestination = batch.reduce((acc, item) => {
|
|
604
|
+
const apiUrl = item.apiUrl ?? this.apiUrl;
|
|
605
|
+
const apiKey = item.apiKey ?? this.apiKey;
|
|
606
|
+
const isDefault = item.apiKey === this.apiKey && item.apiUrl === this.apiUrl;
|
|
607
|
+
const batchKey = isDefault ? "default" : `${apiUrl}|${apiKey}`;
|
|
608
|
+
if (!acc[batchKey]) {
|
|
609
|
+
acc[batchKey] = [];
|
|
610
|
+
}
|
|
611
|
+
acc[batchKey].push(item);
|
|
612
|
+
return acc;
|
|
613
|
+
}, {});
|
|
614
|
+
const batchPromises = [];
|
|
615
|
+
for (const [batchKey, batch] of Object.entries(batchesByDestination)) {
|
|
616
|
+
const batchPromise = this._processBatch(batch, {
|
|
617
|
+
apiUrl: batchKey === "default" ? undefined : batchKey.split("|")[0],
|
|
618
|
+
apiKey: batchKey === "default" ? undefined : batchKey.split("|")[1],
|
|
619
|
+
});
|
|
620
|
+
batchPromises.push(batchPromise);
|
|
621
|
+
}
|
|
622
|
+
// Wait for all batches to complete, then call the overall done callback
|
|
623
|
+
const allBatchesPromise = Promise.all(batchPromises).finally(done);
|
|
624
|
+
promises.push(allBatchesPromise);
|
|
590
625
|
}
|
|
591
626
|
return Promise.all(promises);
|
|
592
627
|
}
|
|
593
|
-
async _processBatch(batch,
|
|
628
|
+
async _processBatch(batch, options) {
|
|
594
629
|
if (!batch.length) {
|
|
595
|
-
done();
|
|
596
630
|
return;
|
|
597
631
|
}
|
|
598
632
|
try {
|
|
@@ -610,19 +644,16 @@ export class Client {
|
|
|
610
644
|
};
|
|
611
645
|
const serverInfo = await this._ensureServerInfo();
|
|
612
646
|
if (serverInfo?.batch_ingest_config?.use_multipart_endpoint) {
|
|
613
|
-
await this.multipartIngestRuns(ingestParams);
|
|
647
|
+
await this.multipartIngestRuns(ingestParams, options);
|
|
614
648
|
}
|
|
615
649
|
else {
|
|
616
|
-
await this.batchIngestRuns(ingestParams);
|
|
650
|
+
await this.batchIngestRuns(ingestParams, options);
|
|
617
651
|
}
|
|
618
652
|
}
|
|
619
653
|
}
|
|
620
654
|
catch (e) {
|
|
621
655
|
console.error("Error exporting batch:", e);
|
|
622
656
|
}
|
|
623
|
-
finally {
|
|
624
|
-
done();
|
|
625
|
-
}
|
|
626
657
|
}
|
|
627
658
|
_sendBatchToOTELTranslator(batch) {
|
|
628
659
|
if (this.langSmithToOTELTranslator !== undefined) {
|
|
@@ -736,11 +767,14 @@ export class Client {
|
|
|
736
767
|
}
|
|
737
768
|
return undefined;
|
|
738
769
|
}
|
|
739
|
-
async createRun(run) {
|
|
770
|
+
async createRun(run, options) {
|
|
740
771
|
if (!this._filterForSampling([run]).length) {
|
|
741
772
|
return;
|
|
742
773
|
}
|
|
743
|
-
const headers = {
|
|
774
|
+
const headers = {
|
|
775
|
+
...this.headers,
|
|
776
|
+
"Content-Type": "application/json",
|
|
777
|
+
};
|
|
744
778
|
const session_name = run.project_name;
|
|
745
779
|
delete run.project_name;
|
|
746
780
|
const runCreate = await this.prepareRunCreateOrUpdateInputs({
|
|
@@ -756,11 +790,16 @@ export class Client {
|
|
|
756
790
|
action: "create",
|
|
757
791
|
item: runCreate,
|
|
758
792
|
otelContext,
|
|
793
|
+
apiKey: options?.apiKey,
|
|
794
|
+
apiUrl: options?.apiUrl,
|
|
759
795
|
}).catch(console.error);
|
|
760
796
|
return;
|
|
761
797
|
}
|
|
762
798
|
const mergedRunCreateParam = mergeRuntimeEnvIntoRunCreate(runCreate);
|
|
763
|
-
|
|
799
|
+
if (options?.apiKey !== undefined) {
|
|
800
|
+
headers["x-api-key"] = options.apiKey;
|
|
801
|
+
}
|
|
802
|
+
const response = await this.caller.call(_getFetchImplementation(this.debug), `${options?.apiUrl ?? this.apiUrl}/runs`, {
|
|
764
803
|
method: "POST",
|
|
765
804
|
headers,
|
|
766
805
|
body: serializePayloadForTracing(mergedRunCreateParam, `Creating run with id: ${mergedRunCreateParam.id}`),
|
|
@@ -773,7 +812,7 @@ export class Client {
|
|
|
773
812
|
* Batch ingest/upsert multiple runs in the Langsmith system.
|
|
774
813
|
* @param runs
|
|
775
814
|
*/
|
|
776
|
-
async batchIngestRuns({ runCreates, runUpdates, }) {
|
|
815
|
+
async batchIngestRuns({ runCreates, runUpdates, }, options) {
|
|
777
816
|
if (runCreates === undefined && runUpdates === undefined) {
|
|
778
817
|
return;
|
|
779
818
|
}
|
|
@@ -828,16 +867,19 @@ export class Client {
|
|
|
828
867
|
.map((item) => item.id)
|
|
829
868
|
.concat(batchChunks.patch.map((item) => item.id))
|
|
830
869
|
.join(",");
|
|
831
|
-
await this._postBatchIngestRuns(serializePayloadForTracing(batchChunks, `Ingesting runs with ids: ${runIds}`));
|
|
870
|
+
await this._postBatchIngestRuns(serializePayloadForTracing(batchChunks, `Ingesting runs with ids: ${runIds}`), options);
|
|
832
871
|
}
|
|
833
872
|
}
|
|
834
|
-
async _postBatchIngestRuns(body) {
|
|
873
|
+
async _postBatchIngestRuns(body, options) {
|
|
835
874
|
const headers = {
|
|
836
875
|
...this.headers,
|
|
837
876
|
"Content-Type": "application/json",
|
|
838
877
|
Accept: "application/json",
|
|
839
878
|
};
|
|
840
|
-
|
|
879
|
+
if (options?.apiKey !== undefined) {
|
|
880
|
+
headers["x-api-key"] = options.apiKey;
|
|
881
|
+
}
|
|
882
|
+
const response = await this.batchIngestCaller.call(_getFetchImplementation(this.debug), `${options?.apiUrl ?? this.apiUrl}/runs/batch`, {
|
|
841
883
|
method: "POST",
|
|
842
884
|
headers,
|
|
843
885
|
body: body,
|
|
@@ -850,7 +892,7 @@ export class Client {
|
|
|
850
892
|
* Batch ingest/upsert multiple runs in the Langsmith system.
|
|
851
893
|
* @param runs
|
|
852
894
|
*/
|
|
853
|
-
async multipartIngestRuns({ runCreates, runUpdates, }) {
|
|
895
|
+
async multipartIngestRuns({ runCreates, runUpdates, }, options) {
|
|
854
896
|
if (runCreates === undefined && runUpdates === undefined) {
|
|
855
897
|
return;
|
|
856
898
|
}
|
|
@@ -977,7 +1019,7 @@ export class Client {
|
|
|
977
1019
|
accumulatedContext.push(`trace=${payload.trace_id},id=${payload.id}`);
|
|
978
1020
|
}
|
|
979
1021
|
}
|
|
980
|
-
await this._sendMultipartRequest(accumulatedParts, accumulatedContext.join("; "));
|
|
1022
|
+
await this._sendMultipartRequest(accumulatedParts, accumulatedContext.join("; "), options);
|
|
981
1023
|
}
|
|
982
1024
|
async _createNodeFetchBody(parts, boundary) {
|
|
983
1025
|
// Create multipart form data manually using Blobs
|
|
@@ -1042,24 +1084,53 @@ export class Client {
|
|
|
1042
1084
|
});
|
|
1043
1085
|
return stream;
|
|
1044
1086
|
}
|
|
1045
|
-
async _sendMultipartRequest(parts, context) {
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
const
|
|
1087
|
+
async _sendMultipartRequest(parts, context, options) {
|
|
1088
|
+
// Create multipart form data boundary
|
|
1089
|
+
const boundary = "----LangSmithFormBoundary" + Math.random().toString(36).slice(2);
|
|
1090
|
+
const isNodeFetch = _globalFetchImplementationIsNodeFetch();
|
|
1091
|
+
const buildBuffered = () => this._createNodeFetchBody(parts, boundary);
|
|
1092
|
+
const buildStream = () => this._createMultipartStream(parts, boundary);
|
|
1093
|
+
const send = async (body) => {
|
|
1094
|
+
const headers = {
|
|
1095
|
+
...this.headers,
|
|
1096
|
+
"Content-Type": `multipart/form-data; boundary=${boundary}`,
|
|
1097
|
+
};
|
|
1098
|
+
if (options?.apiKey !== undefined) {
|
|
1099
|
+
headers["x-api-key"] = options.apiKey;
|
|
1100
|
+
}
|
|
1101
|
+
return this.batchIngestCaller.call(_getFetchImplementation(this.debug), `${options?.apiUrl ?? this.apiUrl}/runs/multipart`, {
|
|
1053
1102
|
method: "POST",
|
|
1054
|
-
headers
|
|
1055
|
-
...this.headers,
|
|
1056
|
-
"Content-Type": `multipart/form-data; boundary=${boundary}`,
|
|
1057
|
-
},
|
|
1103
|
+
headers,
|
|
1058
1104
|
body,
|
|
1059
1105
|
duplex: "half",
|
|
1060
1106
|
signal: AbortSignal.timeout(this.timeout_ms),
|
|
1061
1107
|
...this.fetchOptions,
|
|
1062
1108
|
});
|
|
1109
|
+
};
|
|
1110
|
+
try {
|
|
1111
|
+
let res;
|
|
1112
|
+
let streamedAttempt = false;
|
|
1113
|
+
// attempt stream only if not disabled and not using node-fetch
|
|
1114
|
+
if (!isNodeFetch && !this.multipartStreamingDisabled) {
|
|
1115
|
+
streamedAttempt = true;
|
|
1116
|
+
res = await send(await buildStream());
|
|
1117
|
+
}
|
|
1118
|
+
else {
|
|
1119
|
+
res = await send(await buildBuffered());
|
|
1120
|
+
}
|
|
1121
|
+
// if stream fails, fallback to buffered body
|
|
1122
|
+
if ((!this.multipartStreamingDisabled || streamedAttempt) &&
|
|
1123
|
+
res.status === 422 &&
|
|
1124
|
+
(options?.apiUrl ?? this.apiUrl) !== DEFAULT_API_URL) {
|
|
1125
|
+
console.warn(`Streaming multipart upload to ${options?.apiUrl ?? this.apiUrl}/runs/multipart failed. ` +
|
|
1126
|
+
`This usually means the host does not support chunked uploads. ` +
|
|
1127
|
+
`Retrying with a buffered upload for operation "${context}".`);
|
|
1128
|
+
// Disable streaming for future requests
|
|
1129
|
+
this.multipartStreamingDisabled = true;
|
|
1130
|
+
// retry with fully-buffered body
|
|
1131
|
+
res = await send(await buildBuffered());
|
|
1132
|
+
}
|
|
1133
|
+
// raise if still failing
|
|
1063
1134
|
await raiseForStatus(res, "ingest multipart runs", true);
|
|
1064
1135
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1065
1136
|
}
|
|
@@ -1067,7 +1138,7 @@ export class Client {
|
|
|
1067
1138
|
console.warn(`${e.message.trim()}\n\nContext: ${context}`);
|
|
1068
1139
|
}
|
|
1069
1140
|
}
|
|
1070
|
-
async updateRun(runId, run) {
|
|
1141
|
+
async updateRun(runId, run, options) {
|
|
1071
1142
|
assertUuid(runId);
|
|
1072
1143
|
if (run.inputs) {
|
|
1073
1144
|
run.inputs = await this.processInputs(run.inputs);
|
|
@@ -1094,6 +1165,8 @@ export class Client {
|
|
|
1094
1165
|
action: "update",
|
|
1095
1166
|
item: data,
|
|
1096
1167
|
otelContext,
|
|
1168
|
+
apiKey: options?.apiKey,
|
|
1169
|
+
apiUrl: options?.apiUrl,
|
|
1097
1170
|
}).catch(console.error);
|
|
1098
1171
|
return;
|
|
1099
1172
|
}
|
|
@@ -1102,12 +1175,20 @@ export class Client {
|
|
|
1102
1175
|
action: "update",
|
|
1103
1176
|
item: data,
|
|
1104
1177
|
otelContext,
|
|
1178
|
+
apiKey: options?.apiKey,
|
|
1179
|
+
apiUrl: options?.apiUrl,
|
|
1105
1180
|
}).catch(console.error);
|
|
1106
1181
|
}
|
|
1107
1182
|
return;
|
|
1108
1183
|
}
|
|
1109
|
-
const headers = {
|
|
1110
|
-
|
|
1184
|
+
const headers = {
|
|
1185
|
+
...this.headers,
|
|
1186
|
+
"Content-Type": "application/json",
|
|
1187
|
+
};
|
|
1188
|
+
if (options?.apiKey !== undefined) {
|
|
1189
|
+
headers["x-api-key"] = options.apiKey;
|
|
1190
|
+
}
|
|
1191
|
+
const response = await this.caller.call(_getFetchImplementation(this.debug), `${options?.apiUrl ?? this.apiUrl}/runs/${runId}`, {
|
|
1111
1192
|
method: "PATCH",
|
|
1112
1193
|
headers,
|
|
1113
1194
|
body: serializePayloadForTracing(run, `Serializing payload to update run with id: ${runId}`),
|
|
@@ -3034,7 +3115,7 @@ export class Client {
|
|
|
3034
3115
|
const settings = await this._getSettings();
|
|
3035
3116
|
if (options?.isPublic && !settings.tenant_handle) {
|
|
3036
3117
|
throw new Error(`Cannot create a public prompt without first\n
|
|
3037
|
-
creating a LangChain Hub handle.
|
|
3118
|
+
creating a LangChain Hub handle.
|
|
3038
3119
|
You can add a handle by creating a public prompt at:\n
|
|
3039
3120
|
https://smith.langchain.com/prompts`);
|
|
3040
3121
|
}
|
|
@@ -3152,7 +3233,7 @@ export class Client {
|
|
|
3152
3233
|
}
|
|
3153
3234
|
}
|
|
3154
3235
|
const datasetIdToUse = datasetId ?? updates[0]?.dataset_id;
|
|
3155
|
-
const response = await this.caller.call(_getFetchImplementation(this.debug), `${this.apiUrl}
|
|
3236
|
+
const response = await this.caller.call(_getFetchImplementation(this.debug), `${this.apiUrl}${this._getPlatformEndpointPath(`datasets/${datasetIdToUse}/examples`)}`, {
|
|
3156
3237
|
method: "PATCH",
|
|
3157
3238
|
headers: this.headers,
|
|
3158
3239
|
body: formData,
|
|
@@ -3230,7 +3311,7 @@ export class Client {
|
|
|
3230
3311
|
}
|
|
3231
3312
|
}
|
|
3232
3313
|
}
|
|
3233
|
-
const response = await this.caller.call(_getFetchImplementation(this.debug), `${this.apiUrl}
|
|
3314
|
+
const response = await this.caller.call(_getFetchImplementation(this.debug), `${this.apiUrl}${this._getPlatformEndpointPath(`datasets/${datasetId}/examples`)}`, {
|
|
3234
3315
|
method: "POST",
|
|
3235
3316
|
headers: this.headers,
|
|
3236
3317
|
body: formData,
|
package/dist/index.cjs
CHANGED
|
@@ -10,4 +10,4 @@ Object.defineProperty(exports, "overrideFetchImplementation", { enumerable: true
|
|
|
10
10
|
var project_js_1 = require("./utils/project.cjs");
|
|
11
11
|
Object.defineProperty(exports, "getDefaultProjectName", { enumerable: true, get: function () { return project_js_1.getDefaultProjectName; } });
|
|
12
12
|
// Update using yarn bump-version
|
|
13
|
-
exports.__version__ = "0.3.
|
|
13
|
+
exports.__version__ = "0.3.43";
|
package/dist/index.d.ts
CHANGED
|
@@ -3,4 +3,4 @@ export type { Dataset, Example, TracerSession, Run, Feedback, RetrieverOutput, }
|
|
|
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
|
-
export declare const __version__ = "0.3.
|
|
6
|
+
export declare const __version__ = "0.3.43";
|
package/dist/index.js
CHANGED
|
@@ -3,4 +3,4 @@ export { RunTree } from "./run_trees.js";
|
|
|
3
3
|
export { overrideFetchImplementation } from "./singletons/fetch.js";
|
|
4
4
|
export { getDefaultProjectName } from "./utils/project.js";
|
|
5
5
|
// Update using yarn bump-version
|
|
6
|
-
export const __version__ = "0.3.
|
|
6
|
+
export const __version__ = "0.3.43";
|
package/dist/run_trees.cjs
CHANGED
|
@@ -40,9 +40,11 @@ exports.isRunnableConfigLike = isRunnableConfigLike;
|
|
|
40
40
|
const uuid = __importStar(require("uuid"));
|
|
41
41
|
const client_js_1 = require("./client.cjs");
|
|
42
42
|
const env_js_1 = require("./env.cjs");
|
|
43
|
+
const error_js_1 = require("./utils/error.cjs");
|
|
43
44
|
const constants_js_1 = require("./singletons/constants.cjs");
|
|
44
45
|
const env_js_2 = require("./utils/env.cjs");
|
|
45
46
|
const project_js_1 = require("./utils/project.cjs");
|
|
47
|
+
const env_js_3 = require("./utils/env.cjs");
|
|
46
48
|
const warn_js_1 = require("./utils/warn.cjs");
|
|
47
49
|
function stripNonAlphanumeric(input) {
|
|
48
50
|
return input.replace(/[-:.]/g, "");
|
|
@@ -300,6 +302,7 @@ class RunTree {
|
|
|
300
302
|
this.trace_id = this.id;
|
|
301
303
|
}
|
|
302
304
|
}
|
|
305
|
+
this.replicas = _ensureWriteReplicas(this.replicas);
|
|
303
306
|
this.execution_order ??= 1;
|
|
304
307
|
this.child_execution_order ??= 1;
|
|
305
308
|
if (!this.dotted_order) {
|
|
@@ -503,9 +506,12 @@ class RunTree {
|
|
|
503
506
|
try {
|
|
504
507
|
const runtimeEnv = (0, env_js_2.getRuntimeEnvironment)();
|
|
505
508
|
if (this.replicas && this.replicas.length > 0) {
|
|
506
|
-
for (const
|
|
507
|
-
const runCreate = this._remapForProject(projectName, runtimeEnv, true);
|
|
508
|
-
await this.client.createRun(runCreate
|
|
509
|
+
for (const { projectName, apiKey, apiUrl } of this.replicas) {
|
|
510
|
+
const runCreate = this._remapForProject(projectName ?? this.project_name, runtimeEnv, true);
|
|
511
|
+
await this.client.createRun(runCreate, {
|
|
512
|
+
apiKey,
|
|
513
|
+
apiUrl,
|
|
514
|
+
});
|
|
509
515
|
}
|
|
510
516
|
}
|
|
511
517
|
else {
|
|
@@ -525,8 +531,8 @@ class RunTree {
|
|
|
525
531
|
}
|
|
526
532
|
async patchRun() {
|
|
527
533
|
if (this.replicas && this.replicas.length > 0) {
|
|
528
|
-
for (const
|
|
529
|
-
const runData = this._remapForProject(projectName);
|
|
534
|
+
for (const { projectName, apiKey, apiUrl, updates } of this.replicas) {
|
|
535
|
+
const runData = this._remapForProject(projectName ?? this.project_name);
|
|
530
536
|
await this.client.updateRun(runData.id, {
|
|
531
537
|
inputs: runData.inputs,
|
|
532
538
|
outputs: runData.outputs,
|
|
@@ -542,6 +548,9 @@ class RunTree {
|
|
|
542
548
|
extra: runData.extra,
|
|
543
549
|
attachments: this.attachments,
|
|
544
550
|
...updates,
|
|
551
|
+
}, {
|
|
552
|
+
apiKey,
|
|
553
|
+
apiUrl,
|
|
545
554
|
});
|
|
546
555
|
}
|
|
547
556
|
}
|
|
@@ -742,3 +751,44 @@ function _parseDottedOrder(dottedOrder) {
|
|
|
742
751
|
return [timestamp, uuidStr];
|
|
743
752
|
});
|
|
744
753
|
}
|
|
754
|
+
function _getWriteReplicasFromEnv() {
|
|
755
|
+
const envVar = (0, env_js_2.getEnvironmentVariable)("LANGSMITH_RUNS_ENDPOINTS");
|
|
756
|
+
if (!envVar)
|
|
757
|
+
return [];
|
|
758
|
+
try {
|
|
759
|
+
const parsed = JSON.parse(envVar);
|
|
760
|
+
_checkEndpointEnvUnset(parsed);
|
|
761
|
+
return Object.entries(parsed).map(([url, key]) => ({
|
|
762
|
+
apiUrl: url.replace(/\/$/, ""),
|
|
763
|
+
apiKey: key,
|
|
764
|
+
}));
|
|
765
|
+
}
|
|
766
|
+
catch (e) {
|
|
767
|
+
if ((0, error_js_1.isConflictingEndpointsError)(e)) {
|
|
768
|
+
throw e;
|
|
769
|
+
}
|
|
770
|
+
console.warn("Invalid LANGSMITH_RUNS_ENDPOINTS – must be valid JSON mapping of url->apiKey");
|
|
771
|
+
return [];
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
function _ensureWriteReplicas(replicas) {
|
|
775
|
+
// If null -> fetch from env
|
|
776
|
+
if (replicas) {
|
|
777
|
+
return replicas.map((replica) => {
|
|
778
|
+
if (Array.isArray(replica)) {
|
|
779
|
+
return {
|
|
780
|
+
projectName: replica[0],
|
|
781
|
+
update: replica[1],
|
|
782
|
+
};
|
|
783
|
+
}
|
|
784
|
+
return replica;
|
|
785
|
+
});
|
|
786
|
+
}
|
|
787
|
+
return _getWriteReplicasFromEnv();
|
|
788
|
+
}
|
|
789
|
+
function _checkEndpointEnvUnset(parsed) {
|
|
790
|
+
if (Object.keys(parsed).length > 0 &&
|
|
791
|
+
(0, env_js_3.getLangSmithEnvironmentVariable)("ENDPOINT")) {
|
|
792
|
+
throw new error_js_1.ConflictingEndpointsError();
|
|
793
|
+
}
|
|
794
|
+
}
|
package/dist/run_trees.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ export interface RunTreeConfig {
|
|
|
27
27
|
trace_id?: string;
|
|
28
28
|
dotted_order?: string;
|
|
29
29
|
attachments?: Attachments;
|
|
30
|
-
replicas?: [
|
|
30
|
+
replicas?: Replica[];
|
|
31
31
|
}
|
|
32
32
|
export interface RunnableConfigLike {
|
|
33
33
|
/**
|
|
@@ -50,6 +50,15 @@ interface HeadersLike {
|
|
|
50
50
|
get(name: string): string | null;
|
|
51
51
|
set(name: string, value: string): void;
|
|
52
52
|
}
|
|
53
|
+
type ProjectReplica = [string, KVMap | undefined];
|
|
54
|
+
type WriteReplica = {
|
|
55
|
+
apiUrl?: string;
|
|
56
|
+
apiKey?: string;
|
|
57
|
+
projectName?: string;
|
|
58
|
+
updates?: KVMap | undefined;
|
|
59
|
+
fromEnv?: boolean;
|
|
60
|
+
};
|
|
61
|
+
type Replica = ProjectReplica | WriteReplica;
|
|
53
62
|
export declare class RunTree implements BaseRun {
|
|
54
63
|
private static sharedClient;
|
|
55
64
|
id: string;
|
|
@@ -82,7 +91,7 @@ export declare class RunTree implements BaseRun {
|
|
|
82
91
|
/**
|
|
83
92
|
* Projects to replicate this run to with optional updates.
|
|
84
93
|
*/
|
|
85
|
-
replicas?: [
|
|
94
|
+
replicas?: WriteReplica[];
|
|
86
95
|
constructor(originalConfig: RunTreeConfig | RunTree);
|
|
87
96
|
set metadata(metadata: KVMap);
|
|
88
97
|
get metadata(): KVMap;
|
package/dist/run_trees.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import * as uuid from "uuid";
|
|
2
2
|
import { Client } from "./client.js";
|
|
3
3
|
import { isTracingEnabled } from "./env.js";
|
|
4
|
+
import { isConflictingEndpointsError, ConflictingEndpointsError, } from "./utils/error.js";
|
|
4
5
|
import { _LC_CONTEXT_VARIABLES_KEY } from "./singletons/constants.js";
|
|
5
6
|
import { getEnvironmentVariable, getRuntimeEnvironment, } from "./utils/env.js";
|
|
6
7
|
import { getDefaultProjectName } from "./utils/project.js";
|
|
8
|
+
import { getLangSmithEnvironmentVariable } from "./utils/env.js";
|
|
7
9
|
import { warnOnce } from "./utils/warn.js";
|
|
8
10
|
function stripNonAlphanumeric(input) {
|
|
9
11
|
return input.replace(/[-:.]/g, "");
|
|
@@ -261,6 +263,7 @@ export class RunTree {
|
|
|
261
263
|
this.trace_id = this.id;
|
|
262
264
|
}
|
|
263
265
|
}
|
|
266
|
+
this.replicas = _ensureWriteReplicas(this.replicas);
|
|
264
267
|
this.execution_order ??= 1;
|
|
265
268
|
this.child_execution_order ??= 1;
|
|
266
269
|
if (!this.dotted_order) {
|
|
@@ -464,9 +467,12 @@ export class RunTree {
|
|
|
464
467
|
try {
|
|
465
468
|
const runtimeEnv = getRuntimeEnvironment();
|
|
466
469
|
if (this.replicas && this.replicas.length > 0) {
|
|
467
|
-
for (const
|
|
468
|
-
const runCreate = this._remapForProject(projectName, runtimeEnv, true);
|
|
469
|
-
await this.client.createRun(runCreate
|
|
470
|
+
for (const { projectName, apiKey, apiUrl } of this.replicas) {
|
|
471
|
+
const runCreate = this._remapForProject(projectName ?? this.project_name, runtimeEnv, true);
|
|
472
|
+
await this.client.createRun(runCreate, {
|
|
473
|
+
apiKey,
|
|
474
|
+
apiUrl,
|
|
475
|
+
});
|
|
470
476
|
}
|
|
471
477
|
}
|
|
472
478
|
else {
|
|
@@ -486,8 +492,8 @@ export class RunTree {
|
|
|
486
492
|
}
|
|
487
493
|
async patchRun() {
|
|
488
494
|
if (this.replicas && this.replicas.length > 0) {
|
|
489
|
-
for (const
|
|
490
|
-
const runData = this._remapForProject(projectName);
|
|
495
|
+
for (const { projectName, apiKey, apiUrl, updates } of this.replicas) {
|
|
496
|
+
const runData = this._remapForProject(projectName ?? this.project_name);
|
|
491
497
|
await this.client.updateRun(runData.id, {
|
|
492
498
|
inputs: runData.inputs,
|
|
493
499
|
outputs: runData.outputs,
|
|
@@ -503,6 +509,9 @@ export class RunTree {
|
|
|
503
509
|
extra: runData.extra,
|
|
504
510
|
attachments: this.attachments,
|
|
505
511
|
...updates,
|
|
512
|
+
}, {
|
|
513
|
+
apiKey,
|
|
514
|
+
apiUrl,
|
|
506
515
|
});
|
|
507
516
|
}
|
|
508
517
|
}
|
|
@@ -702,3 +711,44 @@ function _parseDottedOrder(dottedOrder) {
|
|
|
702
711
|
return [timestamp, uuidStr];
|
|
703
712
|
});
|
|
704
713
|
}
|
|
714
|
+
function _getWriteReplicasFromEnv() {
|
|
715
|
+
const envVar = getEnvironmentVariable("LANGSMITH_RUNS_ENDPOINTS");
|
|
716
|
+
if (!envVar)
|
|
717
|
+
return [];
|
|
718
|
+
try {
|
|
719
|
+
const parsed = JSON.parse(envVar);
|
|
720
|
+
_checkEndpointEnvUnset(parsed);
|
|
721
|
+
return Object.entries(parsed).map(([url, key]) => ({
|
|
722
|
+
apiUrl: url.replace(/\/$/, ""),
|
|
723
|
+
apiKey: key,
|
|
724
|
+
}));
|
|
725
|
+
}
|
|
726
|
+
catch (e) {
|
|
727
|
+
if (isConflictingEndpointsError(e)) {
|
|
728
|
+
throw e;
|
|
729
|
+
}
|
|
730
|
+
console.warn("Invalid LANGSMITH_RUNS_ENDPOINTS – must be valid JSON mapping of url->apiKey");
|
|
731
|
+
return [];
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
function _ensureWriteReplicas(replicas) {
|
|
735
|
+
// If null -> fetch from env
|
|
736
|
+
if (replicas) {
|
|
737
|
+
return replicas.map((replica) => {
|
|
738
|
+
if (Array.isArray(replica)) {
|
|
739
|
+
return {
|
|
740
|
+
projectName: replica[0],
|
|
741
|
+
update: replica[1],
|
|
742
|
+
};
|
|
743
|
+
}
|
|
744
|
+
return replica;
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
return _getWriteReplicasFromEnv();
|
|
748
|
+
}
|
|
749
|
+
function _checkEndpointEnvUnset(parsed) {
|
|
750
|
+
if (Object.keys(parsed).length > 0 &&
|
|
751
|
+
getLangSmithEnvironmentVariable("ENDPOINT")) {
|
|
752
|
+
throw new ConflictingEndpointsError();
|
|
753
|
+
}
|
|
754
|
+
}
|
package/dist/utils/error.cjs
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.LangSmithConflictError = void 0;
|
|
3
|
+
exports.ConflictingEndpointsError = exports.LangSmithConflictError = void 0;
|
|
4
4
|
exports.printErrorStackTrace = printErrorStackTrace;
|
|
5
5
|
exports.raiseForStatus = raiseForStatus;
|
|
6
|
+
exports.isConflictingEndpointsError = isConflictingEndpointsError;
|
|
6
7
|
function getErrorStackTrace(e) {
|
|
7
8
|
if (typeof e !== "object" || e == null)
|
|
8
9
|
return undefined;
|
|
@@ -96,3 +97,23 @@ async function raiseForStatus(response, context, consume) {
|
|
|
96
97
|
err.status = response.status;
|
|
97
98
|
throw err;
|
|
98
99
|
}
|
|
100
|
+
const ERR_CONFLICTING_ENDPOINTS = "ERR_CONFLICTING_ENDPOINTS";
|
|
101
|
+
class ConflictingEndpointsError extends Error {
|
|
102
|
+
constructor() {
|
|
103
|
+
super("You cannot provide both LANGSMITH_ENDPOINT / LANGCHAIN_ENDPOINT " +
|
|
104
|
+
"and LANGSMITH_RUNS_ENDPOINTS.");
|
|
105
|
+
Object.defineProperty(this, "code", {
|
|
106
|
+
enumerable: true,
|
|
107
|
+
configurable: true,
|
|
108
|
+
writable: true,
|
|
109
|
+
value: ERR_CONFLICTING_ENDPOINTS
|
|
110
|
+
});
|
|
111
|
+
this.name = "ConflictingEndpointsError"; // helpful in logs
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
exports.ConflictingEndpointsError = ConflictingEndpointsError;
|
|
115
|
+
function isConflictingEndpointsError(err) {
|
|
116
|
+
return (typeof err === "object" &&
|
|
117
|
+
err !== null &&
|
|
118
|
+
err.code === ERR_CONFLICTING_ENDPOINTS);
|
|
119
|
+
}
|
package/dist/utils/error.d.ts
CHANGED
|
@@ -43,3 +43,8 @@ export declare class LangSmithConflictError extends Error {
|
|
|
43
43
|
* @throws {Error} For all other non-ok responses
|
|
44
44
|
*/
|
|
45
45
|
export declare function raiseForStatus(response: Response, context: string, consume?: boolean): Promise<void>;
|
|
46
|
+
export declare class ConflictingEndpointsError extends Error {
|
|
47
|
+
readonly code = "ERR_CONFLICTING_ENDPOINTS";
|
|
48
|
+
constructor();
|
|
49
|
+
}
|
|
50
|
+
export declare function isConflictingEndpointsError(err: unknown): err is ConflictingEndpointsError;
|
package/dist/utils/error.js
CHANGED
|
@@ -90,3 +90,22 @@ export async function raiseForStatus(response, context, consume) {
|
|
|
90
90
|
err.status = response.status;
|
|
91
91
|
throw err;
|
|
92
92
|
}
|
|
93
|
+
const ERR_CONFLICTING_ENDPOINTS = "ERR_CONFLICTING_ENDPOINTS";
|
|
94
|
+
export class ConflictingEndpointsError extends Error {
|
|
95
|
+
constructor() {
|
|
96
|
+
super("You cannot provide both LANGSMITH_ENDPOINT / LANGCHAIN_ENDPOINT " +
|
|
97
|
+
"and LANGSMITH_RUNS_ENDPOINTS.");
|
|
98
|
+
Object.defineProperty(this, "code", {
|
|
99
|
+
enumerable: true,
|
|
100
|
+
configurable: true,
|
|
101
|
+
writable: true,
|
|
102
|
+
value: ERR_CONFLICTING_ENDPOINTS
|
|
103
|
+
});
|
|
104
|
+
this.name = "ConflictingEndpointsError"; // helpful in logs
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export function isConflictingEndpointsError(err) {
|
|
108
|
+
return (typeof err === "object" &&
|
|
109
|
+
err !== null &&
|
|
110
|
+
err.code === ERR_CONFLICTING_ENDPOINTS);
|
|
111
|
+
}
|