From a6ca4b7b9d841e12dbf6ce25cdf3d61ea88f5971 Mon Sep 17 00:00:00 2001 From: Saffron <263493777+itsmiso-ai@users.noreply.github.com> Date: Tue, 18 Aug 2026 02:34:57 +0000 Subject: [PATCH] fix(groomer): attribute LLM abort timeouts to the requested model The raw fetch AbortError carried no model info, so every timeout surfaced in GroomingRun.errorMessage as "This operation was aborted" with no way to tell which pool member served it. Re-throw with the requested model and timeout duration so the 25/24h hosted aborts can be correlated with the mac member, DISPATCH_GROOMER_REPO_CONTEXT_ENABLED prompts, or specific large issues. Fixes #747 Signed-off-by: Saffron <263493777+itsmiso-ai@users.noreply.github.com> --- src/lib/groomer/llm.test.ts | 18 ++++++++++++++++++ src/lib/groomer/llm.ts | 11 +++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/lib/groomer/llm.test.ts b/src/lib/groomer/llm.test.ts index b4545b03..a1daaebd 100644 --- a/src/lib/groomer/llm.test.ts +++ b/src/lib/groomer/llm.test.ts @@ -93,6 +93,24 @@ describe("callGroomerLLM", () => { ).rejects.toThrow(/parse/i); }); + it("attributes AbortError timeouts to the requested model", async () => { + // The raw fetch AbortError carries no model info — every timeout otherwise + // looks identical in GroomingRun.errorMessage, so aborts can't be correlated + // with the pool member that served them. + const abortError = Object.assign(new Error("This operation was aborted"), { name: "AbortError" }); + global.fetch = vi.fn().mockRejectedValue(abortError); + + await expect( + callGroomerLLM({ + baseUrl: "https://llm.example.com", + apiKey: "sk-test", + model: "self-hosted/mac-member", + prompt: "test", + timeoutMs: 60000, + }), + ).rejects.toThrow(/self-hosted\/mac-member.*60000ms/); + }); + it("includes authorization header", async () => { global.fetch = vi.fn().mockResolvedValue({ ok: true, diff --git a/src/lib/groomer/llm.ts b/src/lib/groomer/llm.ts index bf732aa1..548bc0b5 100644 --- a/src/lib/groomer/llm.ts +++ b/src/lib/groomer/llm.ts @@ -156,6 +156,17 @@ export async function callGroomerLLM(options: CallLlmOptions): Promise