Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/vision/describe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,9 @@ export async function describeImage(
if (!parsed.text.trim() && parsed.error) return { text: "", error: parsed.error };
return { text: parsed.text };
} catch (e) {
recordOutcome?.(e instanceof Error && e.name === "TimeoutError" ? "timeout" : "connect_error");
const callerAborted = abortSignal?.aborted && linkedSignal.signal.reason === abortSignal.reason;
const kind = e instanceof Error && e.name === "TimeoutError" ? "timeout" : "connect_error";
if (!callerAborted) recordOutcome?.(kind);
console.warn(`[vision] sidecar ${kind} (${Date.now() - t0}ms)`);
return { text: "", error: e instanceof Error ? e.message : String(e) };
} finally {
Expand Down
3 changes: 2 additions & 1 deletion src/web-search/executor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,9 @@ export async function runWebSearch(
detachBodyGuard();
}
} catch (e) {
recordOutcome?.(e instanceof Error && e.name === "TimeoutError" ? "timeout" : "connect_error");
const callerAborted = abortSignal?.aborted && linkedSignal.signal.reason === abortSignal.reason;
const kind = e instanceof Error && e.name === "TimeoutError" ? "timeout" : "connect_error";
if (!callerAborted) recordOutcome?.(kind);
console.warn(`[web-search] sidecar ${kind} for query "${query.slice(0, 80)}" (${Date.now() - t0}ms)`);
return { text: "", sources: [], error: e instanceof Error ? e.message : String(e) };
} finally {
Expand Down
6 changes: 6 additions & 0 deletions tests/sidecar-abort.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,20 +97,23 @@ describe("sidecar abort propagation", () => {
test("web-search sidecar fetch observes the WebSocket turn abort signal", async () => {
const getSignal = installAbortAwareFetch();
const turn = new AbortController();
const recorded: unknown[] = [];
const outcome = runWebSearch(
"current docs",
{ type: "web_search" },
forwardProvider,
new Headers({ authorization: "Bearer token" }),
{ model: "gpt-5.4-mini", reasoning: "low", timeoutMs: 30_000 },
turn.signal,
value => recorded.push(value),
);

const signal = getSignal();
expect(signal.aborted).toBe(false);
turn.abort("replacement turn");
expect(signal.aborted).toBe(true);
expect((await outcome).error).toBe("aborted by turn");
expect(recorded).toEqual([]);
});

test("web-search sidecar records HTTP and connect outcomes", async () => {
Expand Down Expand Up @@ -213,6 +216,7 @@ describe("sidecar abort propagation", () => {
test("vision sidecar fetch observes the WebSocket turn abort signal", async () => {
const getSignal = installAbortAwareFetch();
const turn = new AbortController();
const recorded: unknown[] = [];
const outcome = describeImage(
"data:image/png;base64,iVBORw0KGgo=",
"high",
Expand All @@ -221,13 +225,15 @@ describe("sidecar abort propagation", () => {
new Headers({ authorization: "Bearer token" }),
{ model: "gpt-5.4-mini", timeoutMs: 30_000 },
turn.signal,
value => recorded.push(value),
);

const signal = getSignal();
expect(signal.aborted).toBe(false);
turn.abort("replacement turn");
expect(signal.aborted).toBe(true);
expect((await outcome).error).toBe("aborted by turn");
expect(recorded).toEqual([]);
});

test("vision sidecar records HTTP and connect outcomes", async () => {
Expand Down
Loading