Skip to content

Commit 9c623e3

Browse files
committed
fix: prevent unhandled rejection with bad cancellation timing
1 parent 77fc3e6 commit 9c623e3

4 files changed

Lines changed: 53 additions & 7 deletions

File tree

deno.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@alphaxiv/agents",
3-
"version": "0.6.14",
3+
"version": "0.6.15",
44
"license": "MIT",
55
"fmt": {
66
"lineWidth": 120

deno.lock

Lines changed: 12 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/agent.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -733,10 +733,9 @@ export class Agent<zO = unknown, zI = unknown, const Tools extends AnyTool[] = [
733733
`Provider ${adapter.provider} did not use unique tool use id: ${part.tool_use_id}`,
734734
);
735735
trace = generate();
736-
pendingTools.set(
737-
part.tool_use_id,
738-
this.#runTool(part, signal, toolSignal, trace, agentTrace),
739-
);
736+
const toolPromise = this.#runTool(part, signal, toolSignal, trace, agentTrace);
737+
toolPromise.catch(() => {});
738+
pendingTools.set(part.tool_use_id, toolPromise);
740739
}
741740

742741
// Message tracing

tests/simple/model-output.test.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,3 +274,40 @@ Deno.test("ModelOutput carries history up to the point of termination", async ()
274274
const toolUses = run.history.filter((h) => h.type === "tool_use");
275275
assert(toolUses.length > 0, "history should contain the tool_use item");
276276
});
277+
278+
Deno.test("ModelOutput settling while the model is still streaming is not an unhandled rejection", async () => {
279+
const outputTool = new Tool({
280+
name: "output_tool",
281+
description: "Returns model output",
282+
parameters: z.void(),
283+
execute: () => new ModelOutput("done"),
284+
});
285+
286+
const agent = new Agent({
287+
model: {
288+
provider: "slow",
289+
model: "slow",
290+
async *stream() {
291+
yield { type: "tool_use", index: 0, tool_use_id: "id-slow", kind: outputTool.name };
292+
await new Promise((resolve) => setTimeout(resolve, 10));
293+
return { inputTokens: 0, outputTokens: 0 };
294+
},
295+
},
296+
instructions: "Model output during stream test",
297+
tools: [outputTool],
298+
});
299+
300+
const rejections: unknown[] = [];
301+
const onRejection = (event: PromiseRejectionEvent) => {
302+
event.preventDefault();
303+
rejections.push(event.reason);
304+
};
305+
globalThis.addEventListener("unhandledrejection", onRejection);
306+
try {
307+
const run = await agent.run("go");
308+
assertEquals(run.output, "done");
309+
assertEquals(rejections, []);
310+
} finally {
311+
globalThis.removeEventListener("unhandledrejection", onRejection);
312+
}
313+
});

0 commit comments

Comments
 (0)