Skip to content

Commit 48edbfd

Browse files
authored
feat(js): Allow async functions in client hideInputs and hideOutputs (#1674)
1 parent 54cf671 commit 48edbfd

File tree

5 files changed

+168
-24
lines changed

5 files changed

+168
-24
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.19",
3+
"version": "0.3.20",
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: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,9 @@ export interface ClientConfig {
7373
callerOptions?: AsyncCallerParams;
7474
timeout_ms?: number;
7575
webUrl?: string;
76-
anonymizer?: (values: KVMap) => KVMap;
77-
hideInputs?: boolean | ((inputs: KVMap) => KVMap);
78-
hideOutputs?: boolean | ((outputs: KVMap) => KVMap);
76+
anonymizer?: (values: KVMap) => KVMap | Promise<KVMap>;
77+
hideInputs?: boolean | ((inputs: KVMap) => KVMap | Promise<KVMap>);
78+
hideOutputs?: boolean | ((outputs: KVMap) => KVMap | Promise<KVMap>);
7979
autoBatchTracing?: boolean;
8080
batchSizeBytesLimit?: number;
8181
blockOnRootRunFinalization?: boolean;
@@ -488,9 +488,9 @@ export class Client implements LangSmithTracingClientInterface {
488488

489489
private _tenantId: string | null = null;
490490

491-
private hideInputs?: boolean | ((inputs: KVMap) => KVMap);
491+
private hideInputs?: boolean | ((inputs: KVMap) => KVMap | Promise<KVMap>);
492492

493-
private hideOutputs?: boolean | ((outputs: KVMap) => KVMap);
493+
private hideOutputs?: boolean | ((outputs: KVMap) => KVMap | Promise<KVMap>);
494494

495495
private tracingSampleRate?: number;
496496

@@ -633,7 +633,7 @@ export class Client implements LangSmithTracingClientInterface {
633633
return headers;
634634
}
635635

636-
private processInputs(inputs: KVMap): KVMap {
636+
private async processInputs(inputs: KVMap): Promise<KVMap> {
637637
if (this.hideInputs === false) {
638638
return inputs;
639639
}
@@ -646,7 +646,7 @@ export class Client implements LangSmithTracingClientInterface {
646646
return inputs;
647647
}
648648

649-
private processOutputs(outputs: KVMap): KVMap {
649+
private async processOutputs(outputs: KVMap): Promise<KVMap> {
650650
if (this.hideOutputs === false) {
651651
return outputs;
652652
}
@@ -659,17 +659,21 @@ export class Client implements LangSmithTracingClientInterface {
659659
return outputs;
660660
}
661661

662-
private prepareRunCreateOrUpdateInputs(run: RunUpdate): RunUpdate;
663-
private prepareRunCreateOrUpdateInputs(run: RunCreate): RunCreate;
664-
private prepareRunCreateOrUpdateInputs(
662+
private async prepareRunCreateOrUpdateInputs(
663+
run: RunUpdate
664+
): Promise<RunUpdate>;
665+
private async prepareRunCreateOrUpdateInputs(
666+
run: RunCreate
667+
): Promise<RunCreate>;
668+
private async prepareRunCreateOrUpdateInputs(
665669
run: RunCreate | RunUpdate
666-
): RunCreate | RunUpdate {
670+
): Promise<RunCreate | RunUpdate> {
667671
const runParams = { ...run };
668672
if (runParams.inputs !== undefined) {
669-
runParams.inputs = this.processInputs(runParams.inputs);
673+
runParams.inputs = await this.processInputs(runParams.inputs);
670674
}
671675
if (runParams.outputs !== undefined) {
672-
runParams.outputs = this.processOutputs(runParams.outputs);
676+
runParams.outputs = await this.processOutputs(runParams.outputs);
673677
}
674678
return runParams;
675679
}
@@ -980,7 +984,7 @@ export class Client implements LangSmithTracingClientInterface {
980984
const session_name = run.project_name;
981985
delete run.project_name;
982986

983-
const runCreate: RunCreate = this.prepareRunCreateOrUpdateInputs({
987+
const runCreate: RunCreate = await this.prepareRunCreateOrUpdateInputs({
984988
session_name,
985989
...run,
986990
start_time: run.start_time ?? Date.now(),
@@ -1026,14 +1030,16 @@ export class Client implements LangSmithTracingClientInterface {
10261030
if (runCreates === undefined && runUpdates === undefined) {
10271031
return;
10281032
}
1029-
let preparedCreateParams =
1033+
let preparedCreateParams = await Promise.all(
10301034
runCreates?.map((create) =>
10311035
this.prepareRunCreateOrUpdateInputs(create)
1032-
) ?? [];
1033-
let preparedUpdateParams =
1036+
) ?? []
1037+
);
1038+
let preparedUpdateParams = await Promise.all(
10341039
runUpdates?.map((update) =>
10351040
this.prepareRunCreateOrUpdateInputs(update)
1036-
) ?? [];
1041+
) ?? []
1042+
);
10371043

10381044
if (preparedCreateParams.length > 0 && preparedUpdateParams.length > 0) {
10391045
const createById = preparedCreateParams.reduce(
@@ -1125,7 +1131,7 @@ export class Client implements LangSmithTracingClientInterface {
11251131
let preparedCreateParams = [];
11261132

11271133
for (const create of runCreates ?? []) {
1128-
const preparedCreate = this.prepareRunCreateOrUpdateInputs(create);
1134+
const preparedCreate = await this.prepareRunCreateOrUpdateInputs(create);
11291135
if (
11301136
preparedCreate.id !== undefined &&
11311137
preparedCreate.attachments !== undefined
@@ -1137,7 +1143,9 @@ export class Client implements LangSmithTracingClientInterface {
11371143
}
11381144
let preparedUpdateParams = [];
11391145
for (const update of runUpdates ?? []) {
1140-
preparedUpdateParams.push(this.prepareRunCreateOrUpdateInputs(update));
1146+
preparedUpdateParams.push(
1147+
await this.prepareRunCreateOrUpdateInputs(update)
1148+
);
11411149
}
11421150

11431151
// require trace_id and dotted_order
@@ -1322,11 +1330,11 @@ export class Client implements LangSmithTracingClientInterface {
13221330
public async updateRun(runId: string, run: RunUpdate): Promise<void> {
13231331
assertUuid(runId);
13241332
if (run.inputs) {
1325-
run.inputs = this.processInputs(run.inputs);
1333+
run.inputs = await this.processInputs(run.inputs);
13261334
}
13271335

13281336
if (run.outputs) {
1329-
run.outputs = this.processOutputs(run.outputs);
1337+
run.outputs = await this.processOutputs(run.outputs);
13301338
}
13311339
// TODO: Untangle types
13321340
const data: UpdateRunParams = { ...run, id: runId };

js/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,4 @@ export { RunTree, type RunTreeConfig } from "./run_trees.js";
1818
export { overrideFetchImplementation } from "./singletons/fetch.js";
1919

2020
// Update using yarn bump-version
21-
export const __version__ = "0.3.19";
21+
export const __version__ = "0.3.20";

js/src/tests/batch_client.test.ts

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,140 @@ describe.each(ENDPOINT_TYPES)(
155155
);
156156
});
157157

158+
it("should hide inputs and outputs", async () => {
159+
const client = new Client({
160+
apiKey: "test-api-key",
161+
autoBatchTracing: true,
162+
hideInputs: () => ({ hidden: "inputs" }),
163+
hideOutputs: () => ({ hidden: "outputs" }),
164+
});
165+
const callSpy = jest
166+
.spyOn((client as any).batchIngestCaller, "call")
167+
.mockResolvedValue({
168+
ok: true,
169+
text: () => "",
170+
});
171+
jest.spyOn(client as any, "_getServerInfo").mockImplementation(() => {
172+
return {
173+
version: "foo",
174+
batch_ingest_config: { ...extraBatchIngestConfig },
175+
};
176+
});
177+
const projectName = "__test_batch";
178+
179+
const runId = uuidv4();
180+
const dottedOrder = convertToDottedOrderFormat(
181+
new Date().getTime() / 1000,
182+
runId
183+
);
184+
await client.createRun({
185+
id: runId,
186+
project_name: projectName,
187+
name: "test_run",
188+
run_type: "llm",
189+
inputs: { text: "hello world" },
190+
outputs: { text: "hello world" },
191+
trace_id: runId,
192+
dotted_order: dottedOrder,
193+
});
194+
195+
await new Promise((resolve) => setTimeout(resolve, 300));
196+
197+
const calledRequestParam: any = callSpy.mock.calls[0][2];
198+
expect(await parseMockRequestBody(calledRequestParam?.body)).toEqual({
199+
post: [
200+
expect.objectContaining({
201+
id: runId,
202+
run_type: "llm",
203+
inputs: {
204+
hidden: "inputs",
205+
},
206+
outputs: {
207+
hidden: "outputs",
208+
},
209+
trace_id: runId,
210+
dotted_order: dottedOrder,
211+
}),
212+
],
213+
patch: [],
214+
});
215+
216+
expect(callSpy).toHaveBeenCalledWith(
217+
expect.any(Function),
218+
expectedTraceURL,
219+
expect.objectContaining({
220+
body: expect.any(endpointType === "batch" ? Uint8Array : ArrayBuffer),
221+
})
222+
);
223+
});
224+
225+
it("should hide inputs and outputs with an async function", async () => {
226+
const client = new Client({
227+
apiKey: "test-api-key",
228+
autoBatchTracing: true,
229+
hideInputs: async () => ({ hidden: "inputs" }),
230+
hideOutputs: async () => ({ hidden: "outputs" }),
231+
});
232+
const callSpy = jest
233+
.spyOn((client as any).batchIngestCaller, "call")
234+
.mockResolvedValue({
235+
ok: true,
236+
text: () => "",
237+
});
238+
jest.spyOn(client as any, "_getServerInfo").mockImplementation(() => {
239+
return {
240+
version: "foo",
241+
batch_ingest_config: { ...extraBatchIngestConfig },
242+
};
243+
});
244+
const projectName = "__test_batch";
245+
246+
const runId = uuidv4();
247+
const dottedOrder = convertToDottedOrderFormat(
248+
new Date().getTime() / 1000,
249+
runId
250+
);
251+
await client.createRun({
252+
id: runId,
253+
project_name: projectName,
254+
name: "test_run",
255+
run_type: "llm",
256+
inputs: { text: "hello world" },
257+
outputs: { text: "hello world" },
258+
trace_id: runId,
259+
dotted_order: dottedOrder,
260+
});
261+
262+
await new Promise((resolve) => setTimeout(resolve, 300));
263+
264+
const calledRequestParam: any = callSpy.mock.calls[0][2];
265+
expect(await parseMockRequestBody(calledRequestParam?.body)).toEqual({
266+
post: [
267+
expect.objectContaining({
268+
id: runId,
269+
run_type: "llm",
270+
inputs: {
271+
hidden: "inputs",
272+
},
273+
outputs: {
274+
hidden: "outputs",
275+
},
276+
trace_id: runId,
277+
dotted_order: dottedOrder,
278+
}),
279+
],
280+
patch: [],
281+
});
282+
283+
expect(callSpy).toHaveBeenCalledWith(
284+
expect.any(Function),
285+
expectedTraceURL,
286+
expect.objectContaining({
287+
body: expect.any(endpointType === "batch" ? Uint8Array : ArrayBuffer),
288+
})
289+
);
290+
});
291+
158292
it("should not throw an error if fetch fails for batch requests", async () => {
159293
const client = new Client({
160294
apiKey: "test-api-key",

js/src/tests/traceable_langchain.test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ describe("to langchain", () => {
3939
const result = await main({ text: "Hello world" });
4040
expect(result).toEqual("Hello world");
4141

42+
await awaitAllCallbacks();
43+
4244
expect(getAssumedTreeFromCalls(callSpy.mock.calls)).toMatchObject({
4345
nodes: [
4446
"main:0",

0 commit comments

Comments
 (0)