From e3a8f0e0a1abd8fed7264741bb500bacd015f4e5 Mon Sep 17 00:00:00 2001 From: 134 Date: Wed, 21 Oct 2026 10:00:23 +0800 Subject: [PATCH] fix(agent): consider the full fallback pool within the request deadline --- lib/ai/runtime/agent-model-failover.ts | 2 -- tests/integration/agent-multimodal-failover.test.ts | 9 ++++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/ai/runtime/agent-model-failover.ts b/lib/ai/runtime/agent-model-failover.ts index 8a609e42d..9180ec5fb 100644 --- a/lib/ai/runtime/agent-model-failover.ts +++ b/lib/ai/runtime/agent-model-failover.ts @@ -67,11 +67,9 @@ export function createAgentModelFailover(entries: AgentModelEntry[], circuit: Ag const totalSignal = signal ? AbortSignal.any([signal, deadline]) : deadline; const ordered = [...entries].sort((a, b) => Number(b.model.id === preferred) - Number(a.model.id === preferred)); let last: unknown; - let attempts = 0; for (const entry of ordered) { totalSignal.throwIfAborted(); if (await circuit.isPaused(entry)) continue; - if (attempts++ >= 4) break; const attemptSignal = AbortSignal.any([totalSignal, AbortSignal.timeout(AGENT_MODEL_ATTEMPT_MS)]); try { const result = await execute(entry, attemptSignal); diff --git a/tests/integration/agent-multimodal-failover.test.ts b/tests/integration/agent-multimodal-failover.test.ts index 87f1e83ea..c21530d2b 100644 --- a/tests/integration/agent-multimodal-failover.test.ts +++ b/tests/integration/agent-multimodal-failover.test.ts @@ -54,11 +54,14 @@ test("invalid input, content policy and cancellation neither quarantine nor retr assert.equal(s.faults.size, 0); }); -test("multiple failed backups are skipped; no model is tried twice and attempts are bounded", async () => { +test("every eligible backup is considered once; a fixed small retry count cannot strand a healthy later model", async () => { const s = store(); const calls: string[] = []; await assert.rejects(createAgentModelFailover(["a", "b", "c", "d", "e"].map(id => entry(id)), s.circuit)(async e => { calls.push(e.model.id); throw Object.assign(new Error("busy"), { status: 503 }); })); - assert.deepEqual(calls, ["a", "b", "c", "d"]); - assert.equal(s.faults.size, 4); + assert.deepEqual(calls, ["a", "b", "c", "d", "e"]); + assert.equal(s.faults.size, 5); + calls.length = 0; + const result = await createAgentModelFailover(["a", "b", "c", "d", "e", "healthy"].map(id => entry(id)), s.circuit)(async e => { calls.push(e.model.id); return "ok"; }); + assert.equal(result, "ok"); assert.deepEqual(calls, ["healthy"]); }); test("late success cannot clear a fault recorded by another in-flight request", async () => {