Skip to content

Commit

Permalink
[JS] Catch any errors in create/update run (#1044)
Browse files Browse the repository at this point in the history
  • Loading branch information
hinthornw authored Sep 26, 2024
1 parent 163ab8e commit 22903fe
Show file tree
Hide file tree
Showing 5 changed files with 74 additions and 40 deletions.
2 changes: 1 addition & 1 deletion js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "langsmith",
"version": "0.1.60",
"version": "0.1.61",
"description": "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform.",
"packageManager": "[email protected]",
"files": [
Expand Down
2 changes: 1 addition & 1 deletion js/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@ export { RunTree, type RunTreeConfig } from "./run_trees.js";
export { overrideFetchImplementation } from "./singletons/fetch.js";

// Update using yarn bump-version
export const __version__ = "0.1.60";
export const __version__ = "0.1.61";
58 changes: 33 additions & 25 deletions js/src/run_trees.ts
Original file line number Diff line number Diff line change
Expand Up @@ -356,36 +356,44 @@ export class RunTree implements BaseRun {
}

async postRun(excludeChildRuns = true): Promise<void> {
const runtimeEnv = await getRuntimeEnvironment();
const runCreate = await this._convertToCreate(this, runtimeEnv, true);
await this.client.createRun(runCreate);

if (!excludeChildRuns) {
warnOnce(
"Posting with excludeChildRuns=false is deprecated and will be removed in a future version."
);
for (const childRun of this.child_runs) {
await childRun.postRun(false);
try {
const runtimeEnv = await getRuntimeEnvironment();
const runCreate = await this._convertToCreate(this, runtimeEnv, true);
await this.client.createRun(runCreate);

if (!excludeChildRuns) {
warnOnce(
"Posting with excludeChildRuns=false is deprecated and will be removed in a future version."
);
for (const childRun of this.child_runs) {
await childRun.postRun(false);
}
}
} catch (error) {
console.error(`Error in postRun for run ${this.id}:`, error);
}
}

async patchRun(): Promise<void> {
const runUpdate: RunUpdate = {
end_time: this.end_time,
error: this.error,
inputs: this.inputs,
outputs: this.outputs,
parent_run_id: this.parent_run?.id,
reference_example_id: this.reference_example_id,
extra: this.extra,
events: this.events,
dotted_order: this.dotted_order,
trace_id: this.trace_id,
tags: this.tags,
};

await this.client.updateRun(this.id, runUpdate);
try {
const runUpdate: RunUpdate = {
end_time: this.end_time,
error: this.error,
inputs: this.inputs,
outputs: this.outputs,
parent_run_id: this.parent_run?.id,
reference_example_id: this.reference_example_id,
extra: this.extra,
events: this.events,
dotted_order: this.dotted_order,
trace_id: this.trace_id,
tags: this.tags,
};

await this.client.updateRun(this.id, runUpdate);
} catch (error) {
console.error(`Error in patchRun for run ${this.id}`, error);
}
}

toJSON() {
Expand Down
22 changes: 22 additions & 0 deletions js/src/tests/traceable.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1031,3 +1031,25 @@ test("argsConfigPath", async () => {
},
});
});

test("traceable continues execution when client throws error", async () => {
const errorClient = {
createRun: jest.fn().mockRejectedValue(new Error("Client error") as never),
updateRun: jest.fn().mockRejectedValue(new Error("Client error") as never),
};

const tracedFunction = traceable(
async (value: number): Promise<number> => value * 2,
{
client: errorClient as unknown as Client,
name: "errorTest",
tracingEnabled: true,
}
);

const result = await tracedFunction(5);

expect(result).toBe(10);
expect(errorClient.createRun).toHaveBeenCalled();
expect(errorClient.updateRun).toHaveBeenCalled();
});
30 changes: 17 additions & 13 deletions js/src/traceable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -617,20 +617,24 @@ export function traceable<Func extends (...args: any[]) => any>(
if (isGenerator(wrappedFunc) && isIteratorLike(rawOutput)) {
const chunks = gatherAll(rawOutput);

await currentRunTree?.end(
handleRunOutputs(
await handleChunks(
chunks.reduce<unknown[]>((memo, { value, done }) => {
if (!done || typeof value !== "undefined") {
memo.push(value);
}

return memo;
}, [])
try {
await currentRunTree?.end(
handleRunOutputs(
await handleChunks(
chunks.reduce<unknown[]>((memo, { value, done }) => {
if (!done || typeof value !== "undefined") {
memo.push(value);
}

return memo;
}, [])
)
)
)
);
await handleEnd();
);
await handleEnd();
} catch (e) {
console.error("Error occurred during handleEnd:", e);
}

return (function* () {
for (const ret of chunks) {
Expand Down

0 comments on commit 22903fe

Please sign in to comment.