langsmith 0.5.26 → 0.6.1
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 +213 -27
- package/dist/client.d.ts +59 -11
- package/dist/client.js +212 -26
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/utils/env.cjs +1 -0
- package/dist/utils/env.js +1 -0
- package/dist/utils/profiles.cjs +292 -0
- package/dist/utils/profiles.d.ts +48 -0
- package/dist/utils/profiles.js +253 -0
- package/dist/utils/serialize_worker.cjs +1 -2
- package/dist/utils/serialize_worker.d.ts +1 -2
- package/dist/utils/serialize_worker.js +1 -2
- package/package.json +1 -14
- package/dist/experimental/opencode/index.cjs +0 -36
- package/dist/experimental/opencode/index.d.ts +0 -3
- package/dist/experimental/opencode/index.js +0 -32
- package/dist/experimental/opencode/tracer.cjs +0 -389
- package/dist/experimental/opencode/tracer.d.ts +0 -30
- package/dist/experimental/opencode/tracer.js +0 -385
- package/experimental/opencode.cjs +0 -1
- package/experimental/opencode.d.cts +0 -1
- package/experimental/opencode.d.ts +0 -1
- package/experimental/opencode.js +0 -1
package/dist/client.js
CHANGED
|
@@ -12,8 +12,15 @@ import { raiseForStatus, isLangSmithNotFoundError, isLangSmithConflictError, } f
|
|
|
12
12
|
import { promptCacheSingleton, } from "./utils/prompt_cache/index.js";
|
|
13
13
|
import * as fsUtils from "./utils/fs.js";
|
|
14
14
|
import { _shouldStreamForGlobalFetchImplementation, _getFetchImplementation, } from "./singletons/fetch.js";
|
|
15
|
+
import { DEFAULT_API_URL, hasValue, loadProfileClientConfig, } from "./utils/profiles.js";
|
|
15
16
|
import { serialize as serializePayloadForTracing, estimateSerializedSize, } from "./utils/fast-safe-stringify/index.js";
|
|
16
17
|
import { getSharedSerializeWorker, hasLargeString, } from "./utils/serialize_worker.js";
|
|
18
|
+
function assertPullPublicPromptAllowed(promptIdentifier, dangerouslyPullPublicPrompt) {
|
|
19
|
+
const [owner] = parseHubIdentifier(promptIdentifier);
|
|
20
|
+
if (owner !== "-" && !dangerouslyPullPublicPrompt) {
|
|
21
|
+
throw new Error("Pulling a public prompt by owner/name is disabled by default because prompts may contain untrusted serialized LangChain objects. If you trust this prompt, set `dangerouslyPullPublicPrompt: true` to acknowledge the risk.");
|
|
22
|
+
}
|
|
23
|
+
}
|
|
17
24
|
/**
|
|
18
25
|
* Catches timestamps without a timezone suffix.
|
|
19
26
|
*/
|
|
@@ -120,7 +127,6 @@ export const DEFAULT_MAX_SIZE_BYTES = 1024 * 1024 * 1024; // 1GB
|
|
|
120
127
|
const SERVER_INFO_REQUEST_TIMEOUT_MS = 10000;
|
|
121
128
|
/** Maximum number of operations to batch in a single request. */
|
|
122
129
|
const DEFAULT_BATCH_SIZE_LIMIT = 100;
|
|
123
|
-
const DEFAULT_API_URL = "https://api.smith.langchain.com";
|
|
124
130
|
export class AutoBatchQueue {
|
|
125
131
|
constructor(maxSizeBytes) {
|
|
126
132
|
Object.defineProperty(this, "items", {
|
|
@@ -153,19 +159,11 @@ export class AutoBatchQueue {
|
|
|
153
159
|
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/Promise
|
|
154
160
|
itemPromiseResolve = resolve;
|
|
155
161
|
});
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
-
|
|
161
|
-
// structural estimate instead. The estimate is only used for soft
|
|
162
|
-
// memory accounting (queue size limit and downstream async caller
|
|
163
|
-
// memory tracking), never for anything correctness-critical -- the
|
|
164
|
-
// real serialization still happens later, off the hot path, when the
|
|
165
|
-
// batch is assembled for sending.
|
|
166
|
-
const size = getLangSmithEnvironmentVariable("PERF_OPTIMIZATION") === "true"
|
|
167
|
-
? estimateSerializedSize(item.item).size
|
|
168
|
-
: serializePayloadForTracing(item.item, `Serializing run with id: ${item.item.id}`).length;
|
|
162
|
+
// Use a cheap structural estimate for soft memory accounting (queue size
|
|
163
|
+
// limit and downstream async caller memory tracking). The exact
|
|
164
|
+
// serialization still happens later, off the hot path, when the batch is
|
|
165
|
+
// assembled for sending.
|
|
166
|
+
const size = estimateSerializedSize(item.item).size;
|
|
169
167
|
// Check if adding this item would exceed the size limit
|
|
170
168
|
// Allow the run if the queue is empty (to support large single traces)
|
|
171
169
|
if (this.sizeBytes + size > this.maxSizeBytes && this.items.length > 0) {
|
|
@@ -232,15 +230,145 @@ export class Client {
|
|
|
232
230
|
return this._tracingMode;
|
|
233
231
|
}
|
|
234
232
|
get _fetch() {
|
|
235
|
-
|
|
233
|
+
const fetchImplementation = this.fetchImplementation || _getFetchImplementation(this.debug);
|
|
234
|
+
return (async (input, init) => {
|
|
235
|
+
let authHeader;
|
|
236
|
+
const profileManagedAuthorization = this.getProfileManagedAuthorizationHeader(init);
|
|
237
|
+
if (this.apiKey !== undefined) {
|
|
238
|
+
authHeader = { name: "x-api-key", value: `${this.apiKey}` };
|
|
239
|
+
}
|
|
240
|
+
else if (!this.hasExplicitAuthHeader(init, profileManagedAuthorization)) {
|
|
241
|
+
authHeader = await this.profileAuth?.getAuthHeader(fetchImplementation, init?.signal);
|
|
242
|
+
}
|
|
243
|
+
return fetchImplementation(input, this.applyCurrentAuthHeaders(init, authHeader, profileManagedAuthorization));
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
getProfileManagedAuthorizationHeader(init) {
|
|
247
|
+
if (!init?.headers || !this.profileAuth) {
|
|
248
|
+
return undefined;
|
|
249
|
+
}
|
|
250
|
+
const authorization = new Headers(init.headers).get("Authorization");
|
|
251
|
+
if (!hasValue(authorization)) {
|
|
252
|
+
return undefined;
|
|
253
|
+
}
|
|
254
|
+
return this.profileAuth.isProfileAuthorizationHeader(authorization ?? "")
|
|
255
|
+
? (authorization ?? undefined)
|
|
256
|
+
: undefined;
|
|
257
|
+
}
|
|
258
|
+
isProfileManagedAuthorizationHeader(value, profileManagedAuthorization) {
|
|
259
|
+
return (value === profileManagedAuthorization ||
|
|
260
|
+
this.profileAuth?.isProfileAuthorizationHeader(value) === true);
|
|
261
|
+
}
|
|
262
|
+
hasExplicitAuthHeader(init, profileManagedAuthorization) {
|
|
263
|
+
if (!init?.headers) {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
const headers = new Headers(init.headers);
|
|
267
|
+
if (hasValue(headers.get("x-api-key"))) {
|
|
268
|
+
return true;
|
|
269
|
+
}
|
|
270
|
+
const authorization = headers.get("Authorization");
|
|
271
|
+
if (!hasValue(authorization)) {
|
|
272
|
+
return false;
|
|
273
|
+
}
|
|
274
|
+
return !this.isProfileManagedAuthorizationHeader(authorization ?? "", profileManagedAuthorization);
|
|
275
|
+
}
|
|
276
|
+
applyCurrentAuthHeaders(init, authHeader, profileManagedAuthorization) {
|
|
277
|
+
if (!authHeader) {
|
|
278
|
+
return init;
|
|
279
|
+
}
|
|
280
|
+
const applyAuth = (headers) => {
|
|
281
|
+
if (this.apiKey !== undefined && authHeader.name === "x-api-key") {
|
|
282
|
+
headers.delete("Authorization");
|
|
283
|
+
if (!headers.has("x-api-key")) {
|
|
284
|
+
headers.set("x-api-key", authHeader.value);
|
|
285
|
+
}
|
|
286
|
+
return headers;
|
|
287
|
+
}
|
|
288
|
+
if (authHeader.name === "Authorization") {
|
|
289
|
+
if (hasValue(headers.get("x-api-key"))) {
|
|
290
|
+
return headers;
|
|
291
|
+
}
|
|
292
|
+
const authorization = headers.get("Authorization");
|
|
293
|
+
if (hasValue(authorization) &&
|
|
294
|
+
!this.isProfileManagedAuthorizationHeader(authorization ?? "", profileManagedAuthorization)) {
|
|
295
|
+
return headers;
|
|
296
|
+
}
|
|
297
|
+
headers.set("Authorization", authHeader.value);
|
|
298
|
+
return headers;
|
|
299
|
+
}
|
|
300
|
+
const authorization = headers.get("Authorization");
|
|
301
|
+
if (hasValue(authorization) &&
|
|
302
|
+
!this.isProfileManagedAuthorizationHeader(authorization ?? "", profileManagedAuthorization)) {
|
|
303
|
+
return headers;
|
|
304
|
+
}
|
|
305
|
+
if (hasValue(authorization)) {
|
|
306
|
+
headers.delete("Authorization");
|
|
307
|
+
}
|
|
308
|
+
if (!headers.has("x-api-key")) {
|
|
309
|
+
headers.set("x-api-key", authHeader.value);
|
|
310
|
+
}
|
|
311
|
+
return headers;
|
|
312
|
+
};
|
|
313
|
+
if (!init) {
|
|
314
|
+
return {
|
|
315
|
+
headers: { [authHeader.name]: authHeader.value },
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
if (init.headers instanceof Headers) {
|
|
319
|
+
return { ...init, headers: applyAuth(new Headers(init.headers)) };
|
|
320
|
+
}
|
|
321
|
+
if (Array.isArray(init.headers)) {
|
|
322
|
+
return { ...init, headers: applyAuth(new Headers(init.headers)) };
|
|
323
|
+
}
|
|
324
|
+
const headers = {
|
|
325
|
+
...(init.headers ?? {}),
|
|
326
|
+
};
|
|
327
|
+
const getHeaderKey = (name) => Object.keys(headers).find((key) => key.toLowerCase() === name);
|
|
328
|
+
const getHeader = (name) => {
|
|
329
|
+
const key = getHeaderKey(name);
|
|
330
|
+
return key ? headers[key] : undefined;
|
|
331
|
+
};
|
|
332
|
+
const hasApiKey = hasValue(getHeader("x-api-key"));
|
|
333
|
+
const authorization = getHeader("authorization");
|
|
334
|
+
const hasExplicitAuthorization = hasValue(authorization) &&
|
|
335
|
+
!this.isProfileManagedAuthorizationHeader(authorization ?? "", profileManagedAuthorization);
|
|
336
|
+
if (this.apiKey !== undefined && authHeader.name === "x-api-key") {
|
|
337
|
+
const authorizationKey = getHeaderKey("authorization");
|
|
338
|
+
if (authorizationKey) {
|
|
339
|
+
delete headers[authorizationKey];
|
|
340
|
+
}
|
|
341
|
+
if (!hasApiKey) {
|
|
342
|
+
headers["x-api-key"] = authHeader.value;
|
|
343
|
+
}
|
|
344
|
+
return { ...init, headers };
|
|
345
|
+
}
|
|
346
|
+
if (authHeader.name === "Authorization") {
|
|
347
|
+
if (!hasApiKey && !hasExplicitAuthorization) {
|
|
348
|
+
const authorizationKey = getHeaderKey("authorization");
|
|
349
|
+
if (authorizationKey && authorizationKey !== "Authorization") {
|
|
350
|
+
delete headers[authorizationKey];
|
|
351
|
+
}
|
|
352
|
+
headers.Authorization = authHeader.value;
|
|
353
|
+
}
|
|
354
|
+
return { ...init, headers };
|
|
355
|
+
}
|
|
356
|
+
if (!hasExplicitAuthorization) {
|
|
357
|
+
const authorizationKey = getHeaderKey("authorization");
|
|
358
|
+
if (authorizationKey) {
|
|
359
|
+
delete headers[authorizationKey];
|
|
360
|
+
}
|
|
361
|
+
if (!hasApiKey) {
|
|
362
|
+
headers["x-api-key"] = authHeader.value;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return { ...init, headers };
|
|
236
366
|
}
|
|
237
367
|
/**
|
|
238
368
|
* Serialize a payload for tracing, optionally offloading the work to a
|
|
239
|
-
* Node worker thread when
|
|
240
|
-
* supports worker_threads.
|
|
369
|
+
* Node worker thread when the runtime supports worker_threads.
|
|
241
370
|
*
|
|
242
371
|
* Falls back to synchronous serialization when:
|
|
243
|
-
* - the perf flag is off
|
|
244
372
|
* - manualFlushMode is enabled (serverless: worker boot cost > benefit)
|
|
245
373
|
* - worker_threads is unavailable (non-Node runtimes)
|
|
246
374
|
* - the payload contains values that can't be structured-cloned across
|
|
@@ -256,8 +384,7 @@ export class Client {
|
|
|
256
384
|
});
|
|
257
385
|
}
|
|
258
386
|
async _serializeBody(payload, errorContext) {
|
|
259
|
-
|
|
260
|
-
if (!perfOptIn || this.manualFlushMode) {
|
|
387
|
+
if (this.manualFlushMode) {
|
|
261
388
|
return serializePayloadForTracing(payload, errorContext);
|
|
262
389
|
}
|
|
263
390
|
// Shape-aware gate: worker offload pays for itself only when the
|
|
@@ -500,6 +627,12 @@ export class Client {
|
|
|
500
627
|
writable: true,
|
|
501
628
|
value: void 0
|
|
502
629
|
});
|
|
630
|
+
Object.defineProperty(this, "profileAuth", {
|
|
631
|
+
enumerable: true,
|
|
632
|
+
configurable: true,
|
|
633
|
+
writable: true,
|
|
634
|
+
value: void 0
|
|
635
|
+
});
|
|
503
636
|
Object.defineProperty(this, "multipartStreamingDisabled", {
|
|
504
637
|
enumerable: true,
|
|
505
638
|
configurable: true,
|
|
@@ -548,12 +681,15 @@ export class Client {
|
|
|
548
681
|
if (this.apiUrl.endsWith("/")) {
|
|
549
682
|
this.apiUrl = this.apiUrl.slice(0, -1);
|
|
550
683
|
}
|
|
551
|
-
|
|
684
|
+
const configuredApiKey = trimQuotes(config.apiKey ?? defaultConfig.apiKey);
|
|
685
|
+
this.apiKey = hasValue(configuredApiKey) ? configuredApiKey : undefined;
|
|
686
|
+
this.profileAuth =
|
|
687
|
+
this.apiKey !== undefined ? undefined : defaultConfig.profileAuth;
|
|
552
688
|
this.webUrl = trimQuotes(config.webUrl ?? defaultConfig.webUrl);
|
|
553
689
|
if (this.webUrl?.endsWith("/")) {
|
|
554
690
|
this.webUrl = this.webUrl.slice(0, -1);
|
|
555
691
|
}
|
|
556
|
-
this.workspaceId = trimQuotes(config.workspaceId ??
|
|
692
|
+
this.workspaceId = trimQuotes(config.workspaceId ?? defaultConfig.workspaceId);
|
|
557
693
|
this.timeout_ms = config.timeout_ms ?? 90_000;
|
|
558
694
|
this.caller = new AsyncCaller({
|
|
559
695
|
...(config.callerOptions ?? {}),
|
|
@@ -638,18 +774,31 @@ export class Client {
|
|
|
638
774
|
this._customHeaders = config.headers ?? {};
|
|
639
775
|
}
|
|
640
776
|
static getDefaultClientConfig() {
|
|
641
|
-
const
|
|
642
|
-
const
|
|
777
|
+
const profileConfig = loadProfileClientConfig();
|
|
778
|
+
const envApiKey = getLangSmithEnvironmentVariable("API_KEY");
|
|
779
|
+
const envApiUrl = getLangSmithEnvironmentVariable("ENDPOINT");
|
|
780
|
+
const envWorkspaceId = getLangSmithEnvironmentVariable("WORKSPACE_ID");
|
|
781
|
+
const envAuthSet = hasValue(envApiKey);
|
|
782
|
+
const apiUrl = envApiUrl ?? profileConfig.apiUrl ?? DEFAULT_API_URL;
|
|
783
|
+
const workspaceId = envWorkspaceId ?? profileConfig.workspaceId;
|
|
643
784
|
const hideInputs = getLangSmithEnvironmentVariable("HIDE_INPUTS") === "true";
|
|
644
785
|
const hideOutputs = getLangSmithEnvironmentVariable("HIDE_OUTPUTS") === "true";
|
|
645
786
|
const hideMetadata = getLangSmithEnvironmentVariable("HIDE_METADATA") === "true";
|
|
646
787
|
return {
|
|
647
788
|
apiUrl: apiUrl,
|
|
648
|
-
apiKey:
|
|
789
|
+
apiKey: envApiKey,
|
|
649
790
|
webUrl: undefined,
|
|
650
791
|
hideInputs: hideInputs,
|
|
651
792
|
hideOutputs: hideOutputs,
|
|
652
793
|
hideMetadata: hideMetadata,
|
|
794
|
+
workspaceId: workspaceId,
|
|
795
|
+
oauthAccessToken: !envAuthSet
|
|
796
|
+
? profileConfig.oauthAccessToken
|
|
797
|
+
: undefined,
|
|
798
|
+
oauthRefreshToken: !envAuthSet
|
|
799
|
+
? profileConfig.oauthRefreshToken
|
|
800
|
+
: undefined,
|
|
801
|
+
profileAuth: !envAuthSet ? profileConfig.profileAuth : undefined,
|
|
653
802
|
};
|
|
654
803
|
}
|
|
655
804
|
getHostUrl() {
|
|
@@ -701,9 +850,15 @@ export class Client {
|
|
|
701
850
|
...this._customHeaders,
|
|
702
851
|
};
|
|
703
852
|
// Required headers that should not be overridden
|
|
704
|
-
if (this.apiKey) {
|
|
853
|
+
if (this.apiKey !== undefined) {
|
|
705
854
|
headers["x-api-key"] = `${this.apiKey}`;
|
|
706
855
|
}
|
|
856
|
+
else {
|
|
857
|
+
const profileAuthHeader = this.profileAuth?.currentAuthHeader();
|
|
858
|
+
if (profileAuthHeader) {
|
|
859
|
+
headers[profileAuthHeader.name] = profileAuthHeader.value;
|
|
860
|
+
}
|
|
861
|
+
}
|
|
707
862
|
if (this.workspaceId) {
|
|
708
863
|
headers["x-tenant-id"] = this.workspaceId;
|
|
709
864
|
}
|
|
@@ -4460,7 +4615,24 @@ export class Client {
|
|
|
4460
4615
|
hub_model_provider: result.model_provider,
|
|
4461
4616
|
};
|
|
4462
4617
|
}
|
|
4618
|
+
/**
|
|
4619
|
+
* Pull a prompt commit from the LangSmith API.
|
|
4620
|
+
*
|
|
4621
|
+
* Public prompts referenced by owner/name cross a trust boundary because the
|
|
4622
|
+
* prompt manifest may contain serialized LangChain objects and configuration
|
|
4623
|
+
* that affect runtime behavior. For example, a prompt can intentionally
|
|
4624
|
+
* configure a model with a custom base URL, headers, model name, or other
|
|
4625
|
+
* constructor arguments. These are supported features, but they also mean the
|
|
4626
|
+
* prompt contents should be treated as executable configuration rather than
|
|
4627
|
+
* plain text.
|
|
4628
|
+
*
|
|
4629
|
+
* Set `dangerouslyPullPublicPrompt: true` only after reviewing and trusting
|
|
4630
|
+
* the prompt contents, not merely the publishing account. Prompts from your
|
|
4631
|
+
* own or your organization's account can still be unsafe if that account or
|
|
4632
|
+
* prompt was compromised.
|
|
4633
|
+
*/
|
|
4463
4634
|
async pullPromptCommit(promptIdentifier, options) {
|
|
4635
|
+
assertPullPublicPromptAllowed(promptIdentifier, options?.dangerouslyPullPublicPrompt);
|
|
4464
4636
|
// Check cache first if not skipped
|
|
4465
4637
|
const refreshFunc = this._fetchPromptFromApi.bind(this, promptIdentifier, options);
|
|
4466
4638
|
if (!options?.skipCache && this._promptCache) {
|
|
@@ -4480,12 +4652,26 @@ export class Client {
|
|
|
4480
4652
|
/**
|
|
4481
4653
|
* This method should not be used directly, use `import { pull } from "langchain/hub"` instead.
|
|
4482
4654
|
* Using this method directly returns the JSON string of the prompt rather than a LangChain object.
|
|
4655
|
+
*
|
|
4656
|
+
* Public prompts referenced by owner/name cross a trust boundary because the
|
|
4657
|
+
* prompt manifest may contain serialized LangChain objects and configuration
|
|
4658
|
+
* that affect runtime behavior. For example, a prompt can intentionally
|
|
4659
|
+
* configure a model with a custom base URL, headers, model name, or other
|
|
4660
|
+
* constructor arguments. These are supported features, but they also mean the
|
|
4661
|
+
* prompt contents should be treated as executable configuration rather than
|
|
4662
|
+
* plain text.
|
|
4663
|
+
*
|
|
4664
|
+
* Set `dangerouslyPullPublicPrompt: true` only after reviewing and trusting
|
|
4665
|
+
* the prompt contents, not merely the publishing account. Prompts from your
|
|
4666
|
+
* own or your organization's account can still be unsafe if that account or
|
|
4667
|
+
* prompt was compromised.
|
|
4483
4668
|
* @private
|
|
4484
4669
|
*/
|
|
4485
4670
|
async _pullPrompt(promptIdentifier, options) {
|
|
4486
4671
|
const promptObject = await this.pullPromptCommit(promptIdentifier, {
|
|
4487
4672
|
includeModel: options?.includeModel,
|
|
4488
4673
|
skipCache: options?.skipCache,
|
|
4674
|
+
dangerouslyPullPublicPrompt: options?.dangerouslyPullPublicPrompt,
|
|
4489
4675
|
});
|
|
4490
4676
|
const prompt = JSON.stringify(promptObject.manifest);
|
|
4491
4677
|
return prompt;
|
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 pnpm bump-version
|
|
21
|
-
exports.__version__ = "0.
|
|
21
|
+
exports.__version__ = "0.6.1";
|
package/dist/index.d.ts
CHANGED
|
@@ -5,4 +5,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.
|
|
8
|
+
export declare const __version__ = "0.6.1";
|
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 pnpm bump-version
|
|
8
|
-
export const __version__ = "0.
|
|
8
|
+
export const __version__ = "0.6.1";
|
package/dist/utils/env.cjs
CHANGED
package/dist/utils/env.js
CHANGED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ProfileAuth = exports.DEFAULT_API_URL = void 0;
|
|
37
|
+
exports.hasValue = hasValue;
|
|
38
|
+
exports.loadProfileClientConfig = loadProfileClientConfig;
|
|
39
|
+
const env_js_1 = require("./env.cjs");
|
|
40
|
+
const fsUtils = __importStar(require("./fs.cjs"));
|
|
41
|
+
exports.DEFAULT_API_URL = "https://api.smith.langchain.com";
|
|
42
|
+
const OAUTH_CLIENT_ID = "langsmith-cli";
|
|
43
|
+
const TOKEN_REFRESH_LEEWAY_MS = 60_000;
|
|
44
|
+
const TOKEN_REFRESH_TIMEOUT_MS = 10_000;
|
|
45
|
+
function isBrowserLikeRuntime() {
|
|
46
|
+
const env = (0, env_js_1.getEnv)();
|
|
47
|
+
return env === "browser" || env === "webworker";
|
|
48
|
+
}
|
|
49
|
+
function getProfileConfigPath() {
|
|
50
|
+
const explicitPath = (0, env_js_1.getEnvironmentVariable)("LANGSMITH_CONFIG_FILE");
|
|
51
|
+
if (explicitPath) {
|
|
52
|
+
return explicitPath;
|
|
53
|
+
}
|
|
54
|
+
const home = (0, env_js_1.getEnvironmentVariable)("HOME") ?? (0, env_js_1.getEnvironmentVariable)("USERPROFILE");
|
|
55
|
+
if (!home) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
return fsUtils.path.join(home, ".langsmith", "config.json");
|
|
59
|
+
}
|
|
60
|
+
function resolveProfileName(config) {
|
|
61
|
+
const envProfile = (0, env_js_1.getEnvironmentVariable)("LANGSMITH_PROFILE");
|
|
62
|
+
if (envProfile) {
|
|
63
|
+
return envProfile;
|
|
64
|
+
}
|
|
65
|
+
if (config.current_profile) {
|
|
66
|
+
return config.current_profile;
|
|
67
|
+
}
|
|
68
|
+
if (config.profiles?.default) {
|
|
69
|
+
return "default";
|
|
70
|
+
}
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
function loadProfileState() {
|
|
74
|
+
if (isBrowserLikeRuntime()) {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
const configPath = getProfileConfigPath();
|
|
78
|
+
if (!configPath || !fsUtils.existsSync(configPath)) {
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
try {
|
|
82
|
+
const config = JSON.parse(fsUtils.readFileSync(configPath));
|
|
83
|
+
const profileName = resolveProfileName(config);
|
|
84
|
+
const profile = profileName ? config.profiles?.[profileName] : undefined;
|
|
85
|
+
if (!profileName || !profile) {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
return { configPath, config, profileName, profile };
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function hasValue(value) {
|
|
95
|
+
return value !== undefined && value !== null && value.trim() !== "";
|
|
96
|
+
}
|
|
97
|
+
function trimConfigValue(value) {
|
|
98
|
+
return value?.trim().replace(/^["']|["']$/g, "");
|
|
99
|
+
}
|
|
100
|
+
function shouldRefreshProfileToken(profile) {
|
|
101
|
+
const oauth = profile.oauth;
|
|
102
|
+
if (!oauth?.refresh_token) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
if (!oauth.access_token) {
|
|
106
|
+
return true;
|
|
107
|
+
}
|
|
108
|
+
if (!oauth.expires_at) {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
const expiresAt = Date.parse(oauth.expires_at);
|
|
112
|
+
if (Number.isNaN(expiresAt)) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
return expiresAt <= Date.now() + TOKEN_REFRESH_LEEWAY_MS;
|
|
116
|
+
}
|
|
117
|
+
function normalizeConfigUrl(apiUrl) {
|
|
118
|
+
let normalized = apiUrl;
|
|
119
|
+
while (normalized.endsWith("/")) {
|
|
120
|
+
normalized = normalized.slice(0, -1);
|
|
121
|
+
}
|
|
122
|
+
const apiV1Suffix = "/api/v1";
|
|
123
|
+
return normalized.endsWith(apiV1Suffix)
|
|
124
|
+
? normalized.slice(0, -apiV1Suffix.length)
|
|
125
|
+
: normalized;
|
|
126
|
+
}
|
|
127
|
+
function applyTokenResponse(profile, token) {
|
|
128
|
+
profile.oauth ??= {};
|
|
129
|
+
if (token.access_token) {
|
|
130
|
+
profile.oauth.access_token = token.access_token;
|
|
131
|
+
}
|
|
132
|
+
if (token.refresh_token) {
|
|
133
|
+
profile.oauth.refresh_token = token.refresh_token;
|
|
134
|
+
}
|
|
135
|
+
if (typeof token.expires_in === "number" && token.expires_in > 0) {
|
|
136
|
+
profile.oauth.expires_at = new Date(Date.now() + token.expires_in * 1000).toISOString();
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
function getAbortReason(signal) {
|
|
140
|
+
return (signal.reason ??
|
|
141
|
+
new Error("The operation was aborted."));
|
|
142
|
+
}
|
|
143
|
+
async function waitForAbortSignal(promise, signal) {
|
|
144
|
+
if (!signal) {
|
|
145
|
+
return promise;
|
|
146
|
+
}
|
|
147
|
+
if (signal.aborted) {
|
|
148
|
+
throw getAbortReason(signal);
|
|
149
|
+
}
|
|
150
|
+
let cleanup;
|
|
151
|
+
const abortPromise = new Promise((_, reject) => {
|
|
152
|
+
const onAbort = () => {
|
|
153
|
+
reject(getAbortReason(signal));
|
|
154
|
+
};
|
|
155
|
+
signal.addEventListener("abort", onAbort, { once: true });
|
|
156
|
+
cleanup = () => {
|
|
157
|
+
signal.removeEventListener("abort", onAbort);
|
|
158
|
+
};
|
|
159
|
+
});
|
|
160
|
+
try {
|
|
161
|
+
return await Promise.race([promise, abortPromise]);
|
|
162
|
+
}
|
|
163
|
+
finally {
|
|
164
|
+
cleanup?.();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function loadProfileClientConfig() {
|
|
168
|
+
const state = loadProfileState();
|
|
169
|
+
const profile = state?.profile;
|
|
170
|
+
if (!state || !profile) {
|
|
171
|
+
return {};
|
|
172
|
+
}
|
|
173
|
+
const apiKey = trimConfigValue(profile.api_key);
|
|
174
|
+
const oauthAccessToken = trimConfigValue(profile.oauth?.access_token);
|
|
175
|
+
const oauthRefreshToken = trimConfigValue(profile.oauth?.refresh_token);
|
|
176
|
+
return {
|
|
177
|
+
apiUrl: profile.api_url,
|
|
178
|
+
apiKey,
|
|
179
|
+
workspaceId: profile.workspace_id,
|
|
180
|
+
oauthAccessToken,
|
|
181
|
+
oauthRefreshToken,
|
|
182
|
+
profileAuth: apiKey || oauthAccessToken || oauthRefreshToken
|
|
183
|
+
? new ProfileAuth(state)
|
|
184
|
+
: undefined,
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
class ProfileAuth {
|
|
188
|
+
constructor(state) {
|
|
189
|
+
Object.defineProperty(this, "state", {
|
|
190
|
+
enumerable: true,
|
|
191
|
+
configurable: true,
|
|
192
|
+
writable: true,
|
|
193
|
+
value: state
|
|
194
|
+
});
|
|
195
|
+
Object.defineProperty(this, "refreshPromise", {
|
|
196
|
+
enumerable: true,
|
|
197
|
+
configurable: true,
|
|
198
|
+
writable: true,
|
|
199
|
+
value: void 0
|
|
200
|
+
});
|
|
201
|
+
Object.defineProperty(this, "managedAuthorizationValue", {
|
|
202
|
+
enumerable: true,
|
|
203
|
+
configurable: true,
|
|
204
|
+
writable: true,
|
|
205
|
+
value: void 0
|
|
206
|
+
});
|
|
207
|
+
this.rememberProfileAuthHeader(this.currentAuthHeader());
|
|
208
|
+
}
|
|
209
|
+
currentAuthHeader() {
|
|
210
|
+
const header = currentAuthHeaderFromProfile(this.state.profile);
|
|
211
|
+
this.rememberProfileAuthHeader(header);
|
|
212
|
+
return header;
|
|
213
|
+
}
|
|
214
|
+
async getAuthHeader(fetchImplementation, signal) {
|
|
215
|
+
if (shouldRefreshProfileToken(this.state.profile)) {
|
|
216
|
+
if (!this.refreshPromise) {
|
|
217
|
+
this.refreshPromise = this.refreshOAuthToken(fetchImplementation).finally(() => {
|
|
218
|
+
this.refreshPromise = undefined;
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
await waitForAbortSignal(this.refreshPromise, signal);
|
|
222
|
+
}
|
|
223
|
+
const header = authHeaderFromProfile(this.state.profile);
|
|
224
|
+
this.rememberProfileAuthHeader(header);
|
|
225
|
+
return header;
|
|
226
|
+
}
|
|
227
|
+
isProfileAuthorizationHeader(value) {
|
|
228
|
+
return value === this.managedAuthorizationValue;
|
|
229
|
+
}
|
|
230
|
+
async refreshOAuthToken(fetchImplementation) {
|
|
231
|
+
const refreshToken = this.state.profile.oauth?.refresh_token;
|
|
232
|
+
if (!refreshToken) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
const refreshApiUrl = trimConfigValue(this.state.profile.api_url) ?? exports.DEFAULT_API_URL;
|
|
236
|
+
try {
|
|
237
|
+
const body = new URLSearchParams({
|
|
238
|
+
grant_type: "refresh_token",
|
|
239
|
+
client_id: OAUTH_CLIENT_ID,
|
|
240
|
+
refresh_token: refreshToken,
|
|
241
|
+
});
|
|
242
|
+
const response = await fetchImplementation(`${normalizeConfigUrl(refreshApiUrl)}/oauth/token`, {
|
|
243
|
+
method: "POST",
|
|
244
|
+
headers: {
|
|
245
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
246
|
+
},
|
|
247
|
+
body: body.toString(),
|
|
248
|
+
signal: AbortSignal.timeout(TOKEN_REFRESH_TIMEOUT_MS),
|
|
249
|
+
});
|
|
250
|
+
if (!response.ok) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
const token = (await response.json());
|
|
254
|
+
if (!token.access_token) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
applyTokenResponse(this.state.profile, token);
|
|
258
|
+
this.state.config.profiles ??= {};
|
|
259
|
+
this.state.config.profiles[this.state.profileName] = this.state.profile;
|
|
260
|
+
await fsUtils.writeFileAtomic(this.state.configPath, `${JSON.stringify(this.state.config, null, 2)}\n`);
|
|
261
|
+
}
|
|
262
|
+
catch {
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
rememberProfileAuthHeader(header) {
|
|
267
|
+
this.managedAuthorizationValue =
|
|
268
|
+
header?.name === "Authorization" ? header.value : undefined;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
exports.ProfileAuth = ProfileAuth;
|
|
272
|
+
function currentAuthHeaderFromProfile(profile) {
|
|
273
|
+
const oauthAccessToken = trimConfigValue(profile.oauth?.access_token);
|
|
274
|
+
if (oauthAccessToken) {
|
|
275
|
+
return { name: "Authorization", value: `Bearer ${oauthAccessToken}` };
|
|
276
|
+
}
|
|
277
|
+
if (trimConfigValue(profile.oauth?.refresh_token)) {
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
280
|
+
return authHeaderFromProfile(profile);
|
|
281
|
+
}
|
|
282
|
+
function authHeaderFromProfile(profile) {
|
|
283
|
+
const oauthAccessToken = trimConfigValue(profile.oauth?.access_token);
|
|
284
|
+
if (oauthAccessToken) {
|
|
285
|
+
return { name: "Authorization", value: `Bearer ${oauthAccessToken}` };
|
|
286
|
+
}
|
|
287
|
+
const apiKey = trimConfigValue(profile.api_key);
|
|
288
|
+
if (apiKey) {
|
|
289
|
+
return { name: "x-api-key", value: apiKey };
|
|
290
|
+
}
|
|
291
|
+
return undefined;
|
|
292
|
+
}
|