From 7d419cffd1d4bc047ec64c8ac5aa671be3d98397 Mon Sep 17 00:00:00 2001 From: Fini Date: Wed, 29 Apr 2026 09:49:41 +0800 Subject: [PATCH] feat(ab-corpus): exponential backoff + bump retries=2 on ark/deepseek MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ab-v3 left 36 ark empty + 15 timeout + 4 429 + 6 deepseek empty + 4 minimax timeout AFTER the existing retries=1 fired 88 times. Linear backoff 250ms*(attempt+1) was too tight when stepping up to retries=2 (500ms then 750ms isn't a typical Ark recovery window). Switches to exponential 250ms*4^attempt — 250 / 1000 / 4000ms spacing — and bumps ark + deepseek to retries=2. minimax opts in to retries=1 (its 4 errors were wall-clock timeouts, not the model's truncation that retry can't fix anyway). 10 existing retry tests still pass; the retries=2 case now sleeps 1.25s instead of 0.75s, still well under vitest's default timeout. --- scripts/ab-corpus/clients/ark.ts | 10 ++++++---- scripts/ab-corpus/clients/deepseek.ts | 8 ++++---- scripts/ab-corpus/clients/minimax.ts | 5 +++++ scripts/ab-corpus/clients/openai-compat.ts | 19 ++++++++++++------- 4 files changed, 27 insertions(+), 15 deletions(-) diff --git a/scripts/ab-corpus/clients/ark.ts b/scripts/ab-corpus/clients/ark.ts index 4bb093cdb..0013b06ce 100644 --- a/scripts/ab-corpus/clients/ark.ts +++ b/scripts/ab-corpus/clients/ark.ts @@ -47,9 +47,11 @@ export async function callArk(args: CallArkArgs): Promise { // failure was Ark returning empty `choices[0].message.content` or // exceeding the 120s wall clock, not a model-quality issue (the // model itself routed to the right element tool 80% of the time - // when it did respond). One retry is the minimum that flips most - // of those into successes; bumping higher would mostly waste - // budget on the genuinely broken minority. - retries: 1, + // when it did respond). retries=1 left 36 empty + 15 timeout + 4 + // 429 still leaking through ab-v3 (520 calls); retries=2 with + // exponential backoff gives the provider a 1000ms recovery window + // before the third attempt, which on Ark's coding tier is usually + // enough to clear the upstream brownout. + retries: 2, }); } diff --git a/scripts/ab-corpus/clients/deepseek.ts b/scripts/ab-corpus/clients/deepseek.ts index 1997be4b6..a05e02be4 100644 --- a/scripts/ab-corpus/clients/deepseek.ts +++ b/scripts/ab-corpus/clients/deepseek.ts @@ -40,9 +40,9 @@ export async function callDeepSeek(args: CallDeepSeekArgs): Promise chatter that characterizes minimax model-side failures. + retries: 1, }); } diff --git a/scripts/ab-corpus/clients/openai-compat.ts b/scripts/ab-corpus/clients/openai-compat.ts index 64e461fd3..36ad9b2ac 100644 --- a/scripts/ab-corpus/clients/openai-compat.ts +++ b/scripts/ab-corpus/clients/openai-compat.ts @@ -70,16 +70,20 @@ export interface CallOpenAICompatArgs { */ timeoutMs?: number; /** - * Retry attempts on transient errors. Default 0 (no retry). Set to 1 + * Retry attempts on transient errors. Default 0 (no retry). Set to 2 * for providers known to flake on empty content / timeout (Ark hosting - * GLM-5.1+Kimi-K2.6, DeepSeek api.deepseek.com). MiniMax / Bailian / - * Codex don't enable this — their ab-v2 failures were model-quality - * (truncation, malformed DSL), where retry burns budget for nothing. + * GLM-5.1+Kimi-K2.6, DeepSeek api.deepseek.com). 1 is enough for + * MiniMax (saw 4 timeouts in 104 ab-v3 runs); Codex / Bailian don't + * enable this — their ab-v2 failures were model-quality (truncation, + * malformed DSL), where retry burns budget for nothing. * * Retried: empty `choices[0].message.content`, abort/timeout, HTTP 5xx, * HTTP 429. NOT retried: HTTP 4xx other than 429 (auth / bad request). * - * Backoff: linear, 250ms × (attempt+1). + * Backoff: exponential, 250ms × 4^attempt — 250ms before retry 1, + * 1000ms before retry 2, 4000ms before retry 3. Linear backoff was + * too tight when retries=2 (500ms then 750ms is < a typical Ark + * recovery window); exponential gives the provider room to settle. */ retries?: number; } @@ -105,13 +109,14 @@ export async function callOpenAICompat(args: CallOpenAICompatArgs): Promise