From 8b152fd4f71b8839aeec87ca9dc54fd6bb0a668e Mon Sep 17 00:00:00 2001 From: Automation Date: Mon, 25 May 2026 22:33:31 +0200 Subject: [PATCH] Revert "fix(combos): repair context handoff ordering and add per-model timeout" This reverts commit 69dc6d02490a32e56fc34405a782dce5eaf4bede. --- open-sse/services/combo.ts | 55 +++++++++-------------------------- src/lib/db/contextHandoffs.ts | 2 +- 2 files changed, 15 insertions(+), 42 deletions(-) diff --git a/open-sse/services/combo.ts b/open-sse/services/combo.ts index 58c1dd331e..a30999006e 100644 --- a/open-sse/services/combo.ts +++ b/open-sse/services/combo.ts @@ -92,7 +92,6 @@ function isAllAccountsRateLimitedResponse( const MAX_COMBO_DEPTH = 3; const MAX_FALLBACK_WAIT_MS = 5000; const MAX_GLOBAL_ATTEMPTS = 30; -const COMBO_MODEL_TIMEOUT_MS = 30_000; // 30s per model attempt within a combo (default FETCH_TIMEOUT_MS=600s) function resolveDelayMs(value: unknown, fallback: number): number { const numericValue = Number(value); @@ -1646,39 +1645,13 @@ export async function handleComboChat({ : handleSingleModel; // ───────────────────────────────────────────────────────────────────────── - // ── Per-model timeout wrapper ──────────────────────────────────────────── - // Default FETCH_TIMEOUT_MS is 600s per model. For combos, we use a shorter - // per-model timeout so slow/hanging models don't block fallback. - const handleSingleModelWithTimeout = async (b, modelStr, target?) => { - let timeoutId; - const timeoutPromise = new Promise((resolve) => { - timeoutId = setTimeout(() => { - log.warn("COMBO", `Model ${modelStr} exceeded ${COMBO_MODEL_TIMEOUT_MS}ms timeout — falling back`); - resolve(new Response( - JSON.stringify({ error: { message: `Model ${modelStr} timed out` } }), - { status: 524, headers: { 'Content-Type': 'application/json' } } - )); - }, COMBO_MODEL_TIMEOUT_MS); - }); - try { - return await Promise.race([ - handleSingleModelWrapped(b, modelStr, target).catch((err) => { - return new Response(JSON.stringify({ error: { message: err.message } }), { status: 502, headers: { "Content-Type": "application/json" } }); - }), - timeoutPromise, - ]); - } finally { - clearTimeout(timeoutId); - } - }; - // Route to pinned model if context caching specifies one (Fix #679) if (pinnedModel) { log.info( "COMBO", `Bypassing strategy — routing directly to pinned context model: ${pinnedModel}` ); - return handleSingleModelWithTimeout(body, pinnedModel); + return handleSingleModelWrapped(body, pinnedModel); } // Route to round-robin handler if strategy matches @@ -1686,7 +1659,7 @@ export async function handleComboChat({ return handleRoundRobinCombo({ body, combo, - handleSingleModel: handleSingleModelWithTimeout, + handleSingleModel: handleSingleModelWrapped, isModelAvailable, log, settings, @@ -1730,7 +1703,7 @@ export async function handleComboChat({ const pipelineRaw = await handlePipelineCombo({ body, combo, - handleChatCore: handleSingleModelWithTimeout, + handleChatCore: handleSingleModel, log, settings, signal, @@ -2182,7 +2155,7 @@ export async function handleComboChat({ ); } } - const result = await handleSingleModelWithTimeout(body, modelStr, { + const result = await handleSingleModelWrapped(body, modelStr, { ...target, failoverBeforeRetry: config.failoverBeforeRetry, }); @@ -2245,6 +2218,14 @@ export async function handleComboChat({ relayOptions?.sessionId && !(body as Record)?.[SKIP_UNIVERSAL_HANDOFF_FLAG] ) { + recordSessionModelUsage( + relayOptions.sessionId, + combo.name, + modelStr, + provider, + target.connectionId + ); + const prevModel = getLastSessionModel(relayOptions.sessionId, combo.name); if (prevModel && prevModel !== modelStr) { const handoffSourceMessages = @@ -2261,17 +2242,9 @@ export async function handleComboChat({ prevModel, currModel: modelStr, universalConfig: universalHandoffConfig, - handleSingleModel: handleSingleModelWithTimeout, + handleSingleModel: handleSingleModelWrapped, }); } - - recordSessionModelUsage( - relayOptions.sessionId, - combo.name, - modelStr, - provider, - target.connectionId - ); } // Context-relay intentionally splits responsibilities: // combo.ts decides whether a successful turn should generate a handoff, @@ -2306,7 +2279,7 @@ export async function handleComboChat({ model: modelStr, expiresAt: resetCandidates[0] || null, config: relayConfig, - handleSingleModel: handleSingleModelWithTimeout, + handleSingleModel: handleSingleModelWrapped, }); } } diff --git a/src/lib/db/contextHandoffs.ts b/src/lib/db/contextHandoffs.ts index 71aaccd13c..6ef426c964 100644 --- a/src/lib/db/contextHandoffs.ts +++ b/src/lib/db/contextHandoffs.ts @@ -209,7 +209,7 @@ export function getLastSessionModel(sessionId: string, comboName: string): strin `SELECT model_str FROM session_model_history WHERE session_id = ? AND combo_name = ? - ORDER BY id DESC + ORDER BY used_at DESC LIMIT 1` ) .get(sessionId, comboName) as { model_str: string } | undefined;