From 0c8e2261508e96ec56f1807000bc57ca7966eff8 Mon Sep 17 00:00:00 2001 From: Reinis Date: Sat, 10 Oct 2026 14:09:20 +0300 Subject: [PATCH] fix(proxy): single-count 5xx last-attempt fall-through Guard the 5xx branch recordRequest with if (attempt < maxAttempts - 1) so the final attempt counts once via the tail recorder (same pattern as f85cb2e throttle-403). Display-only: add throttle-403 legend chip + retitle other-4xx. Plan: 2026-10-10-5xx-last-attempt-double-count (amended: breaker 1/0 first-5xx-only feed). --- src/dashboard/public/app.js | 3 ++- src/proxy/server.ts | 18 +++++++++++------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/dashboard/public/app.js b/src/dashboard/public/app.js index 6ff30c5..ceb4d17 100644 --- a/src/dashboard/public/app.js +++ b/src/dashboard/public/app.js @@ -1421,7 +1421,8 @@ async function renderFailoverTuning() { 5xx feeds breaker burst-429 feeds breaker quota-429 → cooldown + failover - other 4xx count-only + throttle-403 → short cooldown + failover + other 4xx count-only transport errors never trip local outage never burns diff --git a/src/proxy/server.ts b/src/proxy/server.ts index bf4a93d..d21908b 100644 --- a/src/proxy/server.ts +++ b/src/proxy/server.ts @@ -558,13 +558,17 @@ export class ProxyServer { if (!repeatedServerError) { this.keyManager.markError(key.id) } - this.keyManager.recordRequest(key.id, { - statusCode: upstreamRes.status, - durationMs: duration, - model: prepared.model, - sessionId: upstreamSessionId ?? sessionKey ?? null, - successful: false, - }) + if (attempt < maxAttempts - 1) { + // Counted here; on the last attempt the tail recordRequest + // below counts the verbatim 5xx once (no double-count). + this.keyManager.recordRequest(key.id, { + statusCode: upstreamRes.status, + durationMs: duration, + model: prepared.model, + sessionId: upstreamSessionId ?? sessionKey ?? null, + successful: false, + }) + } attemptedKeyIds.add(key.id) if (circuitState === CircuitState.OPEN) {