From 6df4d37bf4a8ee5c2f9fd96343f43790495609c6 Mon Sep 17 00:00:00 2001 From: Your Name Date: Tue, 24 Feb 2026 12:31:03 +0800 Subject: [PATCH] fix: hard-cap detail plan runtime with true timeout race --- app/api/canvas/detail-plan/route.ts | 31 ++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/app/api/canvas/detail-plan/route.ts b/app/api/canvas/detail-plan/route.ts index e7ebf0bc5..64a2b8fc6 100644 --- a/app/api/canvas/detail-plan/route.ts +++ b/app/api/canvas/detail-plan/route.ts @@ -17,8 +17,8 @@ export const maxDuration = 180; const DETAIL_PLAN_INLINE_MAX_IMAGES = 4; const DETAIL_PLAN_INLINE_TIMEOUT_MS = 4000; const DETAIL_PLAN_INLINE_RETRIES = 1; -const DETAIL_PLAN_MAIN_TIMEOUT_MS = 55000; -const DETAIL_PLAN_PREFLIGHT_TIMEOUT_MS = 35000; +const DETAIL_PLAN_MAIN_TIMEOUT_MS = 20000; +const DETAIL_PLAN_PREFLIGHT_TIMEOUT_MS = 10000; function isAbortError(error: unknown): boolean { const target = error as { @@ -63,14 +63,23 @@ function isVolcanoResponsesPlanner(config: { async function generateTextWithTimeout(params: any, timeoutMs: number) { const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), timeoutMs); + let timer: ReturnType | null = null; + const timeoutPromise = new Promise((_, reject) => { + timer = setTimeout(() => { + controller.abort(); + reject(new Error(`OperationTimeoutError: generateText timed out after ${timeoutMs}ms`)); + }, timeoutMs); + }); try { - return await generateText({ - ...params, - abortSignal: controller.signal, - } as any); + return await Promise.race([ + generateText({ + ...params, + abortSignal: controller.signal, + } as any), + timeoutPromise, + ]); } finally { - clearTimeout(timer); + if (timer) clearTimeout(timer); } } @@ -1749,7 +1758,11 @@ export async function POST(request: Request) { text = String(result?.text || ""); } catch (error) { const message = String((error as Error)?.message || ""); - if (isAbortError(error) || /invalid scheme/i.test(message)) { + if ( + isAbortError(error) || + /invalid scheme/i.test(message) || + /operationtimeouterror|timed out|timeout/i.test(message) + ) { console.error("[Canvas Detail Plan] Primary planning aborted, fallback applied:", error); const fallbackReason = /invalid scheme/i.test(message) ? "规划模型图片输入协议不兼容,已回退到稳定分镜模板。"