Skip to content

Commit a095266

Browse files
authored
feat(js): Cache relevant env vars on client initialization for perf (#2061)
1 parent 2f1187b commit a095266

File tree

6 files changed

+58
-81
lines changed

6 files changed

+58
-81
lines changed

js/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "langsmith",
3-
"version": "0.3.72",
3+
"version": "0.3.73",
44
"description": "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform.",
55
"packageManager": "[email protected]",
66
"files": [

js/src/client.ts

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ import {
5858
} from "./utils/messages.js";
5959
import {
6060
getEnvironmentVariable,
61-
getLangChainEnvVarsMetadata,
61+
getLangSmithEnvVarsMetadata,
6262
getLangSmithEnvironmentVariable,
6363
getRuntimeEnvironment,
6464
getOtelEnabled,
@@ -422,10 +422,11 @@ type Thread = {
422422
};
423423

424424
export function mergeRuntimeEnvIntoRun<T extends RunCreate | RunUpdate>(
425-
run: T
425+
run: T,
426+
cachedEnvVars?: Record<string, string>
426427
): T {
427428
const runtimeEnv = getRuntimeEnvironment();
428-
const envVars = getLangChainEnvVarsMetadata();
429+
const envVars = cachedEnvVars ?? getLangSmithEnvVarsMetadata();
429430
const extra = run.extra ?? {};
430431
const metadata = extra.metadata;
431432
run.extra = {
@@ -670,6 +671,8 @@ export class Client implements LangSmithTracingClientInterface {
670671

671672
private fetchImplementation?: typeof fetch;
672673

674+
private cachedLSEnvVarsForMetadata?: Record<string, string>;
675+
673676
private get _fetch(): typeof fetch {
674677
return this.fetchImplementation || _getFetchImplementation(this.debug);
675678
}
@@ -731,6 +734,8 @@ export class Client implements LangSmithTracingClientInterface {
731734
if (getOtelEnabled()) {
732735
this.langSmithToOTELTranslator = new LangSmithToOTELTranslator();
733736
}
737+
// Cache metadata env vars once during construction to avoid repeatedly scanning process.env
738+
this.cachedLSEnvVarsForMetadata = getLangSmithEnvVarsMetadata();
734739
}
735740

736741
public static getDefaultClientConfig(): {
@@ -1145,7 +1150,10 @@ export class Client implements LangSmithTracingClientInterface {
11451150
private async processRunOperation(item: AutoBatchQueueItem) {
11461151
clearTimeout(this.autoBatchTimeout);
11471152
this.autoBatchTimeout = undefined;
1148-
item.item = mergeRuntimeEnvIntoRun(item.item as RunCreate);
1153+
item.item = mergeRuntimeEnvIntoRun(
1154+
item.item as RunCreate,
1155+
this.cachedLSEnvVarsForMetadata
1156+
);
11491157
const itemPromise = this.autoBatchQueue.push(item);
11501158
if (this.manualFlushMode) {
11511159
// Rely on manual flushing in serverless environments
@@ -1287,7 +1295,10 @@ export class Client implements LangSmithTracingClientInterface {
12871295
}).catch(console.error);
12881296
return;
12891297
}
1290-
const mergedRunCreateParam = mergeRuntimeEnvIntoRun(runCreate);
1298+
const mergedRunCreateParam = mergeRuntimeEnvIntoRun(
1299+
runCreate,
1300+
this.cachedLSEnvVarsForMetadata
1301+
);
12911302
if (options?.apiKey !== undefined) {
12921303
headers["x-api-key"] = options.apiKey;
12931304
}

js/src/evaluation/_runner.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { getDefaultRevisionId, getGitInfo } from "../utils/_git.js";
1212
import { assertUuid } from "../utils/_uuid.js";
1313
import { AsyncCaller } from "../utils/async_caller.js";
1414
import { atee } from "../utils/atee.js";
15-
import { getLangChainEnvVarsMetadata } from "../utils/env.js";
15+
import { getLangSmithEnvVarsMetadata } from "../utils/env.js";
1616
import { printErrorStackTrace } from "../utils/error.js";
1717
import { randomName } from "./_random_name.js";
1818
import {
@@ -374,7 +374,7 @@ export class _ExperimentManager {
374374
let metadata = args.metadata || {};
375375
if (!("revision_id" in metadata)) {
376376
metadata = {
377-
revision_id: getLangChainEnvVarsMetadata().revision_id,
377+
revision_id: getLangSmithEnvVarsMetadata().revision_id,
378378
...metadata,
379379
};
380380
}

js/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,4 @@ export { overrideFetchImplementation } from "./singletons/fetch.js";
2020
export { getDefaultProjectName } from "./utils/project.js";
2121

2222
// Update using yarn bump-version
23-
export const __version__ = "0.3.72";
23+
export const __version__ = "0.3.73";

js/src/tests/client.test.ts

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,8 @@
22
import { jest } from "@jest/globals";
33
import { Client } from "../client.js";
44
import {
5-
getEnvironmentVariables,
6-
getLangChainEnvVars,
7-
getLangChainEnvVarsMetadata,
5+
getLangSmithEnvironmentVariables,
6+
getLangSmithEnvVarsMetadata,
87
} from "../utils/env.js";
98
import {
109
isVersionGreaterOrEqual,
@@ -182,26 +181,17 @@ describe("Client", () => {
182181
// eslint-disable-next-line no-process-env
183182
process.env.SOME_RANDOM_THING = "random";
184183

185-
const envVars = getEnvironmentVariables();
186-
const langchainEnvVars = getLangChainEnvVars();
187-
const langchainMetadataEnvVars = getLangChainEnvVarsMetadata();
184+
const envVars = getLangSmithEnvironmentVariables();
185+
const langchainMetadataEnvVars = getLangSmithEnvVarsMetadata();
188186

189187
expect(envVars).toMatchObject({
190-
LANGCHAIN_REVISION_ID: "test_revision_id",
191-
LANGCHAIN_API_KEY: "fake_api_key",
192-
LANGCHAIN_OTHER_KEY: "test_other_key",
193-
LANGCHAIN_ENDPOINT: "https://example.com",
194-
SOME_RANDOM_THING: "random",
195-
LANGCHAIN_OTHER_NON_SENSITIVE_METADATA: "test_some_metadata",
196-
});
197-
expect(langchainEnvVars).toMatchObject({
198188
LANGCHAIN_REVISION_ID: "test_revision_id",
199189
LANGCHAIN_API_KEY: "fa********ey",
200190
LANGCHAIN_OTHER_KEY: "te**********ey",
201191
LANGCHAIN_ENDPOINT: "https://example.com",
202192
LANGCHAIN_OTHER_NON_SENSITIVE_METADATA: "test_some_metadata",
203193
});
204-
expect(langchainEnvVars).not.toHaveProperty("SOME_RANDOM_THING");
194+
expect(envVars).not.toHaveProperty("SOME_RANDOM_THING");
205195

206196
delete langchainMetadataEnvVars.LANGSMITH_TRACING;
207197
expect(langchainMetadataEnvVars).toEqual({

js/src/utils/env.ts

Lines changed: 33 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -86,46 +86,13 @@ export function getRuntimeEnvironment(): RuntimeEnvironment {
8686
}
8787

8888
/**
89-
* Retrieves the LangChain-specific environment variables from the current runtime environment.
90-
* Sensitive keys (containing the word "key", "token", or "secret") have their values redacted for security.
89+
* Retrieves the LangSmith-specific metadata from the current runtime environment.
9190
*
9291
* @returns {Record<string, string>}
93-
* - A record of LangChain-specific environment variables.
92+
* - A record of LangSmith-specific metadata environment variables.
9493
*/
95-
export function getLangChainEnvVars(): Record<string, string> {
96-
const allEnvVars = getEnvironmentVariables() || {};
97-
const envVars: Record<string, string> = {};
98-
99-
for (const [key, value] of Object.entries(allEnvVars)) {
100-
if (key.startsWith("LANGCHAIN_") && typeof value === "string") {
101-
envVars[key] = value;
102-
}
103-
}
104-
105-
for (const key in envVars) {
106-
if (
107-
(key.toLowerCase().includes("key") ||
108-
key.toLowerCase().includes("secret") ||
109-
key.toLowerCase().includes("token")) &&
110-
typeof envVars[key] === "string"
111-
) {
112-
const value = envVars[key];
113-
envVars[key] =
114-
value.slice(0, 2) + "*".repeat(value.length - 4) + value.slice(-2);
115-
}
116-
}
117-
118-
return envVars;
119-
}
120-
121-
/**
122-
* Retrieves the LangChain-specific metadata from the current runtime environment.
123-
*
124-
* @returns {Record<string, string>}
125-
* - A record of LangChain-specific metadata environment variables.
126-
*/
127-
export function getLangChainEnvVarsMetadata(): Record<string, string> {
128-
const allEnvVars = getEnvironmentVariables() || {};
94+
export function getLangSmithEnvVarsMetadata(): Record<string, string> {
95+
const allEnvVars = getLangSmithEnvironmentVariables();
12996
const envVars: Record<string, string> = {};
13097
const excluded = [
13198
"LANGCHAIN_API_KEY",
@@ -142,7 +109,6 @@ export function getLangChainEnvVarsMetadata(): Record<string, string> {
142109

143110
for (const [key, value] of Object.entries(allEnvVars)) {
144111
if (
145-
(key.startsWith("LANGCHAIN_") || key.startsWith("LANGSMITH_")) &&
146112
typeof value === "string" &&
147113
!excluded.includes(key) &&
148114
!key.toLowerCase().includes("key") &&
@@ -161,36 +127,46 @@ export function getLangChainEnvVarsMetadata(): Record<string, string> {
161127
}
162128

163129
/**
164-
* Retrieves the environment variables from the current runtime environment.
130+
* Retrieves only the LangChain/LangSmith-prefixed environment variables from the current runtime environment.
131+
* This is more efficient than copying all environment variables.
165132
*
166-
* This function is designed to operate in a variety of JS environments,
167-
* including Node.js, Deno, browsers, etc.
168-
*
169-
* @returns {Record<string, string> | undefined}
170-
* - A record of environment variables if available.
171-
* - `undefined` if the environment does not support or allows access to environment variables.
133+
* @returns {Record<string, string>}
134+
* - A record of LangChain/LangSmith environment variables.
172135
*/
173-
export function getEnvironmentVariables(): Record<string, string> | undefined {
136+
export function getLangSmithEnvironmentVariables(): Record<string, string> {
137+
const envVars: Record<string, string> = {};
138+
174139
try {
175140
// Check for Node.js environment
176141
// eslint-disable-next-line no-process-env
177142
if (typeof process !== "undefined" && process.env) {
178143
// eslint-disable-next-line no-process-env
179-
return Object.entries(process.env).reduce(
180-
(acc: { [key: string]: string }, [key, value]) => {
181-
acc[key] = String(value);
182-
return acc;
183-
},
184-
{}
185-
);
144+
for (const [key, value] of Object.entries(process.env)) {
145+
if (
146+
(key.startsWith("LANGCHAIN_") || key.startsWith("LANGSMITH_")) &&
147+
value != null
148+
) {
149+
if (
150+
(key.toLowerCase().includes("key") ||
151+
key.toLowerCase().includes("secret") ||
152+
key.toLowerCase().includes("token")) &&
153+
typeof value === "string"
154+
) {
155+
envVars[key] =
156+
value.slice(0, 2) +
157+
"*".repeat(value.length - 4) +
158+
value.slice(-2);
159+
} else {
160+
envVars[key] = value;
161+
}
162+
}
163+
}
186164
}
187-
// For browsers and other environments, we may not have direct access to env variables
188-
// Return undefined or any other fallback as required.
189-
return undefined;
190165
} catch (e) {
191166
// Catch any errors that might occur while trying to access environment variables
192-
return undefined;
193167
}
168+
169+
return envVars;
194170
}
195171

196172
export function getEnvironmentVariable(name: string): string | undefined {

0 commit comments

Comments
 (0)