diff --git a/changelog.d/fixes/auto-empty-pool-log-once.md b/changelog.d/fixes/auto-empty-pool-log-once.md new file mode 100644 index 00000000000..90d92ed82fb --- /dev/null +++ b/changelog.d/fixes/auto-empty-pool-log-once.md @@ -0,0 +1 @@ +- **fix(auto):** rate-limit `auto/ matched no connected models` warnings to once per minute per label (`open-sse/services/autoCombo/virtualFactory.ts`) diff --git a/open-sse/services/autoCombo/virtualFactory.ts b/open-sse/services/autoCombo/virtualFactory.ts index 87e1006a8e8..ee1438dde6f 100644 --- a/open-sse/services/autoCombo/virtualFactory.ts +++ b/open-sse/services/autoCombo/virtualFactory.ts @@ -43,6 +43,23 @@ export interface AutoComboSpec { family?: ModelFamily; } +/** Rate-limit empty-pool AUTO warns (same label can be resolved many times/min). */ +const emptyPoolWarnAt = new Map(); +export const EMPTY_POOL_WARN_INTERVAL_MS = 60_000; + +export function warnEmptyAutoPoolOnce(label: string, message: string, now = Date.now()): boolean { + const last = emptyPoolWarnAt.get(label) ?? 0; + if (now - last < EMPTY_POOL_WARN_INTERVAL_MS) return false; + emptyPoolWarnAt.set(label, now); + log.warn("AUTO", message); + return true; +} + +/** Test-only: reset the debounce map. */ +export function resetEmptyAutoPoolWarnStateForTests(): void { + emptyPoolWarnAt.clear(); +} + /** Minimal connection shape needed for virtual auto-combo factory */ interface VirtualFactoryConn extends ConnectionFields { id: string; @@ -665,8 +682,8 @@ export async function createVirtualAutoComboFromPrepared( // Family combos always degrade to an empty pool when unavailable — a family // is a hard identity constraint, not a soft optimization bias, so there is // no sensible "fall back to the full pool" behavior for it. - log.warn( - "AUTO", + warnEmptyAutoPoolOnce( + label, `${label} matched no connected models; returning an empty pool.${spec?.family ? "" : ' Set OMNIROUTE_AUTO_FREE_FALLBACK_TO_FULL_POOL=true to restore the legacy "use full pool" behavior.'}` ); effectivePool = []; diff --git a/tests/unit/auto-empty-pool-warn-once.test.ts b/tests/unit/auto-empty-pool-warn-once.test.ts new file mode 100644 index 00000000000..016b2e09970 --- /dev/null +++ b/tests/unit/auto-empty-pool-warn-once.test.ts @@ -0,0 +1,18 @@ +import assert from "node:assert/strict"; +import { test } from "node:test"; + +import { + EMPTY_POOL_WARN_INTERVAL_MS, + resetEmptyAutoPoolWarnStateForTests, + warnEmptyAutoPoolOnce, +} from "../../open-sse/services/autoCombo/virtualFactory.ts"; + +test("warnEmptyAutoPoolOnce emits at most once per label per interval", () => { + resetEmptyAutoPoolWarnStateForTests(); + const t0 = 1_000_000; + assert.equal(warnEmptyAutoPoolOnce("auto/zai", "empty", t0), true); + assert.equal(warnEmptyAutoPoolOnce("auto/zai", "empty", t0 + 1), false); + assert.equal(warnEmptyAutoPoolOnce("auto/zai", "empty", t0 + EMPTY_POOL_WARN_INTERVAL_MS - 1), false); + assert.equal(warnEmptyAutoPoolOnce("auto/other", "empty", t0 + 1), true); + assert.equal(warnEmptyAutoPoolOnce("auto/zai", "empty", t0 + EMPTY_POOL_WARN_INTERVAL_MS), true); +});