From a0eba315ccd94a49da6f25b4a71b141099ed90c0 Mon Sep 17 00:00:00 2001 From: JoshuaBearup Date: Thu, 11 Jun 2026 22:41:40 +1000 Subject: [PATCH] fix(generator): add a per-call timeout to the LLM generation request callLLM() fetched api.anthropic.com with no timeout, so a stalled request hung the entire deploy indefinitely (observed: a generation call that never returned, leaving the deploy wedged with no error). Wrap the fetch in an AbortController with a configurable timeout (POLYRANGE_CALL_TIMEOUT_MS, default 150000ms). On expiry the request is aborted and callLLM rejects with "LLM call timed out after Nms", so the caller's retry logic can recover instead of hanging. Co-Authored-By: Claude Fable 5 --- generator/call-llm.mjs | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/generator/call-llm.mjs b/generator/call-llm.mjs index 0788a91..93b9e84 100644 --- a/generator/call-llm.mjs +++ b/generator/call-llm.mjs @@ -42,20 +42,34 @@ export async function callLLM({ system, user, maxTokens = 4096, expectJson = fal system, messages: [{ role: 'user', content: user }], } - const res = await fetch('https://api.anthropic.com/v1/messages', { - method: 'POST', - headers: { - 'content-type': 'application/json', - 'x-api-key': API_KEY, - 'anthropic-version': '2023-06-01', - }, - body: JSON.stringify(body), - }) - if (!res.ok) { - const txt = await res.text() - throw new Error(`LLM call failed: ${res.status} ${txt}`) + // Hard per-call timeout so a stalled request fails fast instead of hanging + // the whole deploy indefinitely. Configurable via POLYRANGE_CALL_TIMEOUT_MS. + const timeoutMs = Number(process.env.POLYRANGE_CALL_TIMEOUT_MS) || 150000 + const controller = new AbortController() + const timer = setTimeout(() => controller.abort(), timeoutMs) + let json + try { + const res = await fetch('https://api.anthropic.com/v1/messages', { + method: 'POST', + headers: { + 'content-type': 'application/json', + 'x-api-key': API_KEY, + 'anthropic-version': '2023-06-01', + }, + body: JSON.stringify(body), + signal: controller.signal, + }) + if (!res.ok) { + const txt = await res.text() + throw new Error(`LLM call failed: ${res.status} ${txt}`) + } + json = await res.json() + } catch (err) { + if (err.name === 'AbortError') throw new Error(`LLM call timed out after ${timeoutMs}ms`) + throw err + } finally { + clearTimeout(timer) } - const json = await res.json() const u = json.usage || {} USAGE.push({ model: body.model,