refactor(combo): extract reset-aware quota block to combo/quotaStrategies.ts (QG v2 Fase 9 T5 D7b) (#4204)

This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-18 19:12:07 -03:00
committed by GitHub
parent 3135be8e7b
commit 565eb281a7
4 changed files with 905 additions and 826 deletions

View File

@@ -12,6 +12,7 @@
"_rebaseline_2026_06_18_qg9_combo_split_d6": "QG v2 Fase 9 T5 D6: combo.ts 4430->3819 — combo structure resolution extracted byte-identically to the new open-sse/services/combo/comboStructure.ts (638, <cap): runtime-step normalization, nested-combo/DAG expansion (resolveNestedComboTargets/expandRuntimeStep/getComboFromData/getComboModelsFromData/validateComboDAG/resolveNestedComboModels), weighted/direct resolution (resolveComboTargets/resolveWeightedTargets/getDirectComboTargets + composite-tier ordering), request-compatibility filtering (filterTargetsByRequestCompatibility + deriveRequestCompatibilityRequirements/getTargetCompatibilityFailures and their private helpers) and context-size sorting. Shared dedupeTargetsByExecutionKey + the toTrimmedString/toComboLike/getCombosArray normalizers also moved (dedupe to comboData.ts since both combo.ts and comboStructure use it). The 7 previously-public symbols are re-exported from combo.ts; the ~20 external consumers (chatCore.ts, /api/combos routes, embeddings, usage, catalog) are unchanged. shadowRouting.ts's resolveNestedComboTargets import redirected from the barrel to ./comboStructure.ts (removing the D4 temporary edge). Orphaned combo.ts imports dropped (getComboModelString/normalizeComboStep, getModelContextLimit, getResolvedModelCapabilities, getTargetProvider, MAX_COMBO_DEPTH, and 6 now-unused type imports). Pure move, no logic change.",
"_rebaseline_2026_06_18_qg9_combo_split_d8": "QG v2 Fase 9 T5 D8 (reduced): combo.ts 3819->3432 — auto-strategy scoring/intent/tag-routing extracted byte-identically to the new open-sse/services/combo/autoStrategy.ts (434, <cap): the quota-soft execution-candidate registry (the single _activeExecutionCandidates Map + setCandidateQuotaSoftPenalty/_registerExecutionCandidates/_unregisterExecutionCandidates, kept together for state cohesion), QUOTA_SOFT_DEPRIORITIZE_FACTOR, scoreAutoTargets, expandAutoComboCandidatePool, intent extraction (toTextContent/extractPromptForIntent/mapIntentToTaskType/toStringArray/getIntentConfig), applyRequestTagRouting and deriveComboSessionKey. buildAutoCandidates + its two private-only helpers calculateTargetContextAffinity/getBootstrapLatencyMs (and the DEFAULT_MODEL_P95_MS/MIN_HISTORY_SAMPLES/OUTPUT_TOKEN_RATIO consts they own) were DELIBERATELY KEPT in combo.ts: buildAutoCandidates is the sole user of the internal reset-window helpers (resolveResetWindowConfig/fetchResetAwareQuotaWithCache/calculateResetWindowAffinity + the ResetWindowConfig type), so keeping it there leaves those helpers private (no export) and avoids a combo <-> autoStrategy import cycle (a later task can move the reset-window block out of combo.ts without breaking autoStrategy). autoStrategy.ts never imports from the combo barrel. dedupeTargetsByExecutionKey was already in comboData.ts (D6) and is NOT re-moved. QUOTA_SOFT_DEPRIORITIZE_FACTOR + setCandidateQuotaSoftPenalty stay re-exported from combo.ts for chatCore.ts's dynamic import(\"../services/combo\"); scoreAutoTargets/expandAutoComboCandidatePool keep their public re-export too. Orphaned combo.ts imports dropped where moved-out symbols stopped being referenced; ProviderCandidate/AutoProviderCandidate/HistoricalLatencyStatsEntry types are imported into combo.ts for buildAutoCandidates. Pure move, no logic change.",
"_rebaseline_2026_06_18_qg9_combo_split_d7a": "QG v2 Fase 9 T5 D7a: combo.ts 3440->3398 (wc -l 3397 + 1, stacked on #4194 which added the passthrough-quota guards) — round-robin sticky state extracted byte-identically to the new open-sse/services/combo/rrState.ts (71, <cap). Moved exactly the 6 RR-state symbols: MAX_RR_COUNTERS, the two mutable module Maps rrCounters (new Map<string,number>) + rrStickyTargets, and the three helpers clampStickyRoundRobinTargetLimit/getStickyRoundRobinStartIndex/recordStickyRoundRobinSuccess. STATE COHESION: rrCounters and rrStickyTargets remain SINGLE instances defined once in rrState.ts; combo.ts imports the same references back and keeps mutating them directly in orderTargetsByResetAwareQuota/orderTargetsByResetWindow/handleRoundRobinCombo (no Map duplicated — sticky-3 round-robin behavior preserved, guarded by combo-routing-e2e.test.ts). The quota state left behind (MAX_RESET_AWARE_CACHE/resetAwareConnectionCache/resetAwareQuotaCache) stays in combo.ts for a later task (D7b). None of the 6 were ever public, so combo.ts imports all six back (no re-export). rrState.ts never imports from the combo barrel; it imports only ResolvedComboTarget from ./types.ts. Pure move, no logic change.",
"_rebaseline_2026_06_18_qg9_combo_split_d7b": "QG v2 Fase 9 T5 D7b: combo.ts 3398->2597 (wc -l 2596 + 1) — the reset-aware / reset-window quota block extracted byte-identically into two new leaves under open-sse/services/combo/ (both <cap). Split was size-driven (~818 source LOC > 800 cap): quotaScoring.ts (311) holds the PURE half (config consts/resolvers + window-math/scoring helpers, no state, no async); quotaStrategies.ts (568) holds the STATEFUL/async half — the two mutable caches resetAwareConnectionCache + resetAwareQuotaCache (new Map) + MAX_RESET_AWARE_CACHE kept as SINGLE instances next to their only readers/writers getQuotaAwareConnectionsForTarget + fetchResetAwareQuotaWithCache (STATE COHESION; grep 'resetAware*Cache = new Map' in combo.ts == 0), plus normalizeConnectionIds/filterAllowedConnectionIds/getTargetConnectionIds/mapWithConcurrency/preScreenTargets/orderTargetsByResetAwareQuota/orderTargetsByResetWindow. quotaStrategies imports rrCounters + MAX_RR_COUNTERS from ./rrState.ts (D7a) and the pure helpers from ./quotaScoring.ts; neither leaf imports the combo barrel. combo.ts imports back the 3 reset-window helpers buildAutoCandidates needs (resolveResetWindowConfig/fetchResetAwareQuotaWithCache/calculateResetWindowAffinity) + resolveSlaRoutingPolicy + the 3 orderers used by the orchestrator; preScreenTargets stays re-exported. Orphaned imports dropped (clamp01/PRE_SCREEN_CONCURRENCY/ProviderProfile/SlaRoutingPolicy/IsModelAvailable); hasPerModelQuota KEPT (used by the #4194 passthrough-quota guards that stay). buildAutoCandidates + calculateTargetContextAffinity/getBootstrapLatencyMs stay (D8). Pure move, no logic change.",
"_rebaseline_2026_06_18_8_2_sliding_window": "Fase 8.2 own growth: rateLimitManager.ts 1017->1022 (+5 = one import + one `await awaitProviderDefaultSlot(...)` call + a 2-line comment at the existing withRateLimit chokepoint). All sliding-window logic was extracted to the new open-sse/services/providerDefaultRateLimit.ts + open-sse/services/slidingWindowLimiter.ts (both <cap), NOT inlined. Thin wiring only; not further shrinkable.",
"_rebaseline_2026_06_18_8_1_no_thinking_alias": "Fase 8.1 own growth: catalog.ts 1435->1440 (+5 = appendNoThinkingVariants(finalModels) call + comment at the existing finalModels chokepoint) and chat.ts 1458->1471 (+13 = applyNoThinkingAlias(body) call + comment right after body.model is read, before model resolution). All real logic lives in the new open-sse/utils/noThinkingAlias.ts (<cap); both edits are thin wiring of tested helpers at the single correct integration point in each file. Not extractable.",
"_rebaseline_2026_06_18_4_4_midstream": "Fase 4.4 own growth: chatCore.ts 5980->6009 (+29 at the existing streaming-return chokepoint = capture streamRecovery.continueMidStream alongside .enabled; refactor the early-retry reopen thunk into a shared runUpstreamStream(body) helper — net DRY — and add the gated continueStream(assistantSoFar) thunk that re-runs the upstream with makeContinuationBody(bodyToSend, …), plus the onContinue log). All continuation logic (scanOpenAiSseText, makeContinuationBody, trimContinuationOverlap, the createRecoverableStream continuation path) lives in open-sse/services/streamRecovery.ts (<cap). Closes over the same per-attempt executor locals as the existing reopen; not extractable without hiding the dispatch boundary. Opt-in (default OFF).",
@@ -77,7 +78,7 @@
"open-sse/services/batchProcessor.ts": 828,
"open-sse/services/browserBackedChat.ts": 850,
"open-sse/services/claudeCodeCompatible.ts": 1202,
"open-sse/services/combo.ts": 3398,
"open-sse/services/combo.ts": 2597,
"open-sse/services/rateLimitManager.ts": 1035,
"open-sse/services/tokenRefresh.ts": 1997,
"open-sse/services/usage.ts": 3408,

View File

@@ -17,11 +17,9 @@ import {
recordProviderFailure,
isProviderExhaustedReason,
hasPerModelQuota,
type ProviderProfile,
} from "./accountFallback.ts";
import { FETCH_TIMEOUT_MS, RateLimitReason } from "../config/constants.ts";
import { errorResponse, unavailableResponse } from "../utils/error.ts";
import { clamp01 } from "../utils/number.ts";
import {
recordComboIntent,
recordComboRequest,
@@ -32,7 +30,6 @@ import {
resolveComboConfig,
getDefaultComboConfig,
resolveComboTargetTimeoutMs,
PRE_SCREEN_CONCURRENCY,
} from "./comboConfig.ts";
import {
maybeGenerateHandoff,
@@ -62,7 +59,7 @@ import { emit } from "../../src/lib/events/eventBus";
import { notifyWebhookEvent } from "../../src/lib/webhookDispatcher";
import { classifyWithConfig } from "./intentClassifier.ts";
import { selectProvider as selectAutoProvider } from "./autoCombo/engine.ts";
import { selectWithStrategy, type SlaRoutingPolicy } from "./autoCombo/routerStrategy.ts";
import { selectWithStrategy } from "./autoCombo/routerStrategy.ts";
import { parseAutoPrefix } from "./autoCombo/autoPrefix.ts";
import { handlePipelineCombo, buildPipelineResponse } from "./autoCombo/pipelineRouter.ts";
import { DEFAULT_WEIGHTS, type ProviderCandidate, type ScoringWeights } from "./autoCombo/scoring.ts";
@@ -91,7 +88,6 @@ import type {
ComboRetryAfter,
ComboErrorBody,
SingleModelTarget,
IsModelAvailable,
HandleComboChatOptions,
HandleRoundRobinOptions,
ResolvedComboTarget,
@@ -150,6 +146,19 @@ import {
expandAutoComboCandidatePool,
deriveComboSessionKey,
} from "./combo/autoStrategy.ts";
import {
resolveResetWindowConfig,
resolveSlaRoutingPolicy,
calculateResetWindowAffinity,
type ResetWindowConfig,
} from "./combo/quotaScoring.ts";
import {
fetchResetAwareQuotaWithCache,
preScreenTargets,
orderTargetsByResetAwareQuota,
orderTargetsByResetWindow,
type PreScreenResult,
} from "./combo/quotaStrategies.ts";
// Backward-compatible re-exports — these were public from combo.ts before the
// types extraction (Quality Gate v2 / Fase 9). Keep the external surface stable.
@@ -162,6 +171,9 @@ export type { SingleModelTarget, ResolvedComboTarget };
export { validateResponseQuality };
export { clampComboDepth, shouldSkipForPredictedTtft, shouldRecordProviderBreakerFailure };
export { resolveShadowTargets, scheduleShadowRouting };
// preScreenTargets was public from combo.ts before the reset-aware quota
// extraction (combo split D7b). Keep the external surface stable.
export { preScreenTargets };
export { resolveComboTargets, filterTargetsByRequestCompatibility };
export {
getComboFromData,
@@ -171,695 +183,13 @@ export {
validateComboDAG,
} from "./combo/comboStructure.ts";
const RESET_AWARE_SESSION_WINDOW_MS = 5 * 60 * 60 * 1000;
const RESET_AWARE_WEEKLY_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
const RESET_AWARE_SESSION_REMAINING_WEIGHT = 0.45;
const RESET_AWARE_SESSION_RESET_PRESSURE_WEIGHT = 0.55;
const RESET_AWARE_WEEKLY_REMAINING_WEIGHT = 0.25;
const RESET_AWARE_WEEKLY_RESET_PRESSURE_WEIGHT = 0.75;
const RESET_AWARE_CONNECTION_CACHE_TTL_MS = 30_000;
const RESET_AWARE_QUOTA_FETCH_CONCURRENCY = 5;
const RESET_AWARE_DEFAULTS = {
sessionWeight: 0.35,
weeklyWeight: 0.65,
tieBandPercent: 5,
exhaustionGuardPercent: 10,
};
const RESET_WINDOW_DEFAULT_TIE_BAND_MS = 60_000;
type ResetWindowName = (typeof RESET_WINDOW_NAMES)[number];
type QuotaFetchCacheConfig = {
quotaCacheTtlMs: number;
quotaCacheMaxStaleMs: number;
};
type ResetWindowConfig = ReturnType<typeof resolveResetWindowConfig>;
const MAX_RESET_AWARE_CACHE = 200;
const resetAwareConnectionCache = new Map<
string,
{ fetchedAt: number; connections: Array<Record<string, unknown>> }
>();
const resetAwareQuotaCache = new Map<
string,
{ fetchedAt: number; quota: unknown; refreshPromise: Promise<unknown> | null }
>();
function finiteNumberOrNull(value: unknown): number | null {
const numericValue = Number(value);
return Number.isFinite(numericValue) ? numericValue : null;
}
function getPercentConfig(value: unknown, fallback: number): number {
const numericValue = finiteNumberOrNull(value);
if (numericValue === null) return fallback;
return Math.max(0, Math.min(100, numericValue));
}
function getWeightConfig(value: unknown, fallback: number): number {
const numericValue = finiteNumberOrNull(value);
if (numericValue === null || numericValue < 0) return fallback;
return numericValue;
}
function getDurationConfig(value: unknown, fallback: number, max: number): number {
const numericValue = finiteNumberOrNull(value);
if (numericValue === null || numericValue < 0) return fallback;
return Math.min(max, Math.floor(numericValue));
}
function resolveResetAwareConfig(config: Record<string, unknown> | null | undefined) {
const sessionWeight = getWeightConfig(
config?.resetAwareSessionWeight,
RESET_AWARE_DEFAULTS.sessionWeight
);
const weeklyWeight = getWeightConfig(
config?.resetAwareWeeklyWeight,
RESET_AWARE_DEFAULTS.weeklyWeight
);
const totalWeight = sessionWeight + weeklyWeight;
const normalizedSessionWeight =
totalWeight > 0 ? sessionWeight / totalWeight : RESET_AWARE_DEFAULTS.sessionWeight;
return {
sessionWeight: normalizedSessionWeight,
weeklyWeight: 1 - normalizedSessionWeight,
tieBand:
getPercentConfig(config?.resetAwareTieBandPercent, RESET_AWARE_DEFAULTS.tieBandPercent) / 100,
exhaustionGuard:
getPercentConfig(
config?.resetAwareExhaustionGuardPercent,
RESET_AWARE_DEFAULTS.exhaustionGuardPercent
) / 100,
quotaCacheTtlMs: getDurationConfig(config?.resetAwareQuotaCacheTtlMs, 0, 300_000),
quotaCacheMaxStaleMs: getDurationConfig(config?.resetAwareQuotaCacheMaxStaleMs, 0, 3_600_000),
};
}
function resolveResetWindowConfig(config: Record<string, unknown> | null | undefined) {
const rawWindows = Array.isArray(config?.resetWindowWindows) ? config.resetWindowWindows : null;
const windows = rawWindows
?.filter((windowName): windowName is ResetWindowName =>
(RESET_WINDOW_NAMES as readonly string[]).includes(String(windowName))
)
.filter((windowName, index, array) => array.indexOf(windowName) === index);
const effectiveWindows =
windows && windows.length > 0
? windows
: config?.resetWindowIncludeSession === true
? (["weekly", "session"] as ResetWindowName[])
: (["weekly"] as ResetWindowName[]);
return {
windows: effectiveWindows,
tieBandMs: Math.max(
0,
finiteNumberOrNull(config?.resetWindowTieBandMs) ?? RESET_WINDOW_DEFAULT_TIE_BAND_MS
),
quotaCacheTtlMs: getDurationConfig(config?.resetWindowQuotaCacheTtlMs, 0, 300_000),
quotaCacheMaxStaleMs: getDurationConfig(config?.resetWindowQuotaCacheMaxStaleMs, 0, 3_600_000),
};
}
function resolveSlaRoutingPolicy(
config: Record<string, unknown> | null | undefined
): SlaRoutingPolicy | undefined {
if (!config) return undefined;
const nestedSla = isRecord(config.sla) ? config.sla : {};
const targetP95Ms = finiteNumberOrNull(config.slaTargetP95Ms ?? nestedSla.targetP95Ms);
const maxErrorRate = finiteNumberOrNull(config.slaMaxErrorRate ?? nestedSla.maxErrorRate);
const maxCostPer1MTokens = finiteNumberOrNull(
config.slaMaxCostPer1MTokens ?? nestedSla.maxCostPer1MTokens
);
const hardConstraints = config.slaHardConstraints ?? nestedSla.hardConstraints;
const policy: SlaRoutingPolicy = {};
if (targetP95Ms !== null && targetP95Ms > 0) policy.targetP95Ms = targetP95Ms;
if (maxErrorRate !== null && maxErrorRate >= 0) policy.maxErrorRate = clamp01(maxErrorRate);
if (maxCostPer1MTokens !== null && maxCostPer1MTokens > 0) {
policy.maxCostPer1MTokens = maxCostPer1MTokens;
}
if (typeof hardConstraints === "boolean") policy.hardConstraints = hardConstraints;
return Object.keys(policy).length > 0 ? policy : undefined;
}
function getResetAwareProvider(target: ResolvedComboTarget): string | null {
const provider = (target.providerId || target.provider || "").toLowerCase();
return provider || null;
}
function normalizeResetAt(value: unknown): string | null {
if (typeof value === "string" && value.trim().length > 0) return value.trim();
if (typeof value === "number" && Number.isFinite(value)) return String(value);
return null;
}
function parseResetTimeMs(resetAt: string | null | undefined): number {
if (!resetAt) return NaN;
const resetTime = Date.parse(resetAt);
if (Number.isFinite(resetTime)) return resetTime;
if (!/^\d+(?:\.\d+)?$/.test(resetAt)) return NaN;
const numericResetAt = Number(resetAt);
if (!Number.isFinite(numericResetAt)) return NaN;
return numericResetAt < 10_000_000_000 ? numericResetAt * 1000 : numericResetAt;
}
function getQuotaWindow(
quota: unknown,
key: "window5h" | "window7d" | "windowWeekly" | "windowMonthly"
): { percentUsed: number | null; resetAt: string | null } | null {
if (!isRecord(quota)) return null;
const window = quota[key];
if (!isRecord(window)) return null;
const percentUsed = finiteNumberOrNull(window.percentUsed);
const resetAt = normalizeResetAt(window.resetAt);
return { percentUsed, resetAt };
}
function normalizeWindowPercentUsed(value: unknown): number | null {
const numericValue = finiteNumberOrNull(value);
if (numericValue === null) return null;
if (numericValue > 1) return clamp01(numericValue / 100);
return clamp01(numericValue);
}
function getNamedQuotaWindow(
quota: unknown,
windowName: ResetWindowName
): { percentUsed: number | null; resetAt: string | null } | null {
if (!quota || !isRecord(quota)) return null;
if (windowName === "session") return getQuotaWindow(quota, "window5h");
if (windowName === "weekly") {
return getQuotaWindow(quota, "window7d") || getQuotaWindow(quota, "windowWeekly");
}
if (windowName === "monthly") return getQuotaWindow(quota, "windowMonthly");
return null;
}
function getWindowsMapQuotaWindow(
quota: unknown,
windowName: ResetWindowName
): { percentUsed: number | null; resetAt: string | null } | null {
if (!quota || !isRecord(quota) || !isRecord(quota.windows)) return null;
const candidates = Object.entries(quota.windows)
.map(([key, value]) => ({ key: key.toLowerCase(), value }))
.filter(({ key }) => key === windowName || key.startsWith(`${windowName} `));
if (candidates.length === 0) return null;
candidates.sort((a, b) => a.key.localeCompare(b.key));
const window = candidates[0].value;
if (!isRecord(window)) return null;
return {
percentUsed: normalizeWindowPercentUsed(window.percentUsed),
resetAt: normalizeResetAt(window.resetAt),
};
}
function resolveQuotaWindowByName(
quota: unknown,
windowName: ResetWindowName
): { percentUsed: number | null; resetAt: string | null } | null {
return getNamedQuotaWindow(quota, windowName) || getWindowsMapQuotaWindow(quota, windowName);
}
function getResetUrgency(resetAt: string | null | undefined, windowMs: number): number {
if (!resetAt) return 0.5;
const resetTime = parseResetTimeMs(resetAt);
if (!Number.isFinite(resetTime)) return 0.5;
const msUntilReset = resetTime - Date.now();
if (msUntilReset <= 0) return 1;
return clamp01(1 - msUntilReset / windowMs);
}
function scoreQuotaWindow(
remaining: number,
resetAt: string | null | undefined,
windowMs: number,
remainingWeight: number,
resetPressureWeight: number
): number {
const normalizedRemaining = clamp01(remaining);
const resetUrgency = getResetUrgency(resetAt, windowMs);
const resetPressure = resetUrgency * (1 - normalizedRemaining);
return remainingWeight * normalizedRemaining + resetPressureWeight * resetPressure;
}
function scoreResetAwareQuota(quota: unknown, config: ReturnType<typeof resolveResetAwareConfig>) {
if (!quota || !isRecord(quota)) return { score: 0.5 };
if (quota.limitReached === true) return { score: -Infinity };
const overallPercentUsed = clamp01(finiteNumberOrNull(quota.percentUsed) ?? 0.5);
const sessionWindow = getQuotaWindow(quota, "window5h");
const weeklyWindow = getQuotaWindow(quota, "window7d") || getQuotaWindow(quota, "windowWeekly");
const sessionRemaining = clamp01(1 - (sessionWindow?.percentUsed ?? overallPercentUsed));
const weeklyRemaining = clamp01(1 - (weeklyWindow?.percentUsed ?? overallPercentUsed));
const sessionScore = scoreQuotaWindow(
sessionRemaining,
sessionWindow?.resetAt,
RESET_AWARE_SESSION_WINDOW_MS,
RESET_AWARE_SESSION_REMAINING_WEIGHT,
RESET_AWARE_SESSION_RESET_PRESSURE_WEIGHT
);
const weeklyScore = scoreQuotaWindow(
weeklyRemaining,
weeklyWindow?.resetAt ?? normalizeResetAt(quota.resetAt),
RESET_AWARE_WEEKLY_WINDOW_MS,
RESET_AWARE_WEEKLY_REMAINING_WEIGHT,
RESET_AWARE_WEEKLY_RESET_PRESSURE_WEIGHT
);
let score = config.sessionWeight * sessionScore + config.weeklyWeight * weeklyScore;
if (config.exhaustionGuard > 0 && sessionRemaining < config.exhaustionGuard) {
score *= Math.max(0.05, sessionRemaining / config.exhaustionGuard);
}
return { score };
}
async function getQuotaAwareConnectionsForTarget(
target: ResolvedComboTarget,
connectionCache: Map<string, Array<Record<string, unknown>>>,
connectionLoadPromises: Map<string, Promise<Array<Record<string, unknown>>>>,
comboName: string,
log: { warn?: (...args: unknown[]) => void }
) {
const provider = getResetAwareProvider(target);
if (!provider || !getQuotaFetcher(provider)) return [];
if (!connectionCache.has(provider)) {
const cached = resetAwareConnectionCache.get(provider);
if (cached && Date.now() - cached.fetchedAt < RESET_AWARE_CONNECTION_CACHE_TTL_MS) {
connectionCache.set(provider, cached.connections);
return cached.connections;
}
if (!connectionLoadPromises.has(provider)) {
connectionLoadPromises.set(
provider,
(async () => {
try {
const connections = await getProviderConnections({ provider, isActive: true });
const activeConnections = Array.isArray(connections)
? (connections as Array<Record<string, unknown>>)
: [];
if (
!resetAwareConnectionCache.has(provider) &&
resetAwareConnectionCache.size >= MAX_RESET_AWARE_CACHE
) {
const oldest = resetAwareConnectionCache.keys().next().value;
if (oldest !== undefined) resetAwareConnectionCache.delete(oldest);
}
resetAwareConnectionCache.set(provider, {
connections: activeConnections,
fetchedAt: Date.now(),
});
return activeConnections;
} catch (error) {
log.warn?.("COMBO", "Reset-aware failed to load quota-aware connections.", {
comboName,
err: error,
operation: "getProviderConnections",
provider,
});
return [];
}
})()
);
}
const connections = await connectionLoadPromises.get(provider)!;
connectionCache.set(provider, connections);
}
return connectionCache.get(provider) || [];
}
function normalizeConnectionIds(value: unknown): string[] | null {
if (!Array.isArray(value)) return null;
const ids = value.filter(
(connectionId): connectionId is string =>
typeof connectionId === "string" && connectionId.trim().length > 0
);
return ids.length > 0 ? ids : null;
}
function filterAllowedConnectionIds(
connectionIds: string[],
apiKeyAllowedConnectionIds: string[] | null | undefined
): string[] {
const allowedIds = normalizeConnectionIds(apiKeyAllowedConnectionIds);
if (!allowedIds) return connectionIds;
const allowedSet = new Set(allowedIds);
return connectionIds.filter((connectionId) => allowedSet.has(connectionId));
}
function getTargetConnectionIds(
target: ResolvedComboTarget,
connections: Array<Record<string, unknown>>
): string[] {
let connectionIds: string[];
if (target.connectionId) {
return [target.connectionId];
}
if (Array.isArray(target.allowedConnectionIds) && target.allowedConnectionIds.length > 0) {
return target.allowedConnectionIds.filter(
(connectionId): connectionId is string =>
typeof connectionId === "string" && connectionId.trim().length > 0
);
}
connectionIds = connections
.map((connection) => (typeof connection.id === "string" ? connection.id : null))
.filter((connectionId): connectionId is string => !!connectionId);
return connectionIds;
}
async function mapWithConcurrency<T, R>(
items: T[],
concurrency: number,
mapper: (item: T, index: number) => Promise<R>
): Promise<R[]> {
const results = new Array<R>(items.length);
let nextIndex = 0;
const workerCount = Math.max(1, Math.min(concurrency, items.length));
await Promise.all(
Array.from({ length: workerCount }, async () => {
while (nextIndex < items.length) {
const currentIndex = nextIndex++;
results[currentIndex] = await mapper(items[currentIndex], currentIndex);
}
})
);
return results;
}
async function fetchResetAwareQuotaWithCache({
provider,
connectionId,
connection,
fetcher,
config,
log,
comboName,
}: {
provider: string;
connectionId: string;
connection?: Record<string, unknown>;
fetcher: (connectionId: string, connection?: Record<string, unknown>) => Promise<unknown>;
config: QuotaFetchCacheConfig;
log: { debug?: (...args: unknown[]) => void; warn?: (...args: unknown[]) => void };
comboName: string;
}): Promise<unknown> {
const cacheKey = `${provider}:${connectionId}`;
const ttlMs = config.quotaCacheTtlMs;
const maxStaleMs = config.quotaCacheMaxStaleMs;
const now = Date.now();
const cached = resetAwareQuotaCache.get(cacheKey);
if (ttlMs <= 0 && maxStaleMs <= 0) {
try {
return await fetcher(connectionId, connection);
} catch (error) {
log.warn?.("COMBO", "Reset-aware quota fetch failed.", {
comboName,
connectionId,
err: error,
operation: "quotaFetch",
provider,
});
return null;
}
}
const refresh = () => {
const existing = resetAwareQuotaCache.get(cacheKey);
if (existing?.refreshPromise != null) return existing.refreshPromise;
const refreshPromise = fetcher(connectionId, connection)
.then((quota) => {
if (quota) {
if (
!resetAwareQuotaCache.has(cacheKey) &&
resetAwareQuotaCache.size >= MAX_RESET_AWARE_CACHE
) {
const oldest = resetAwareQuotaCache.keys().next().value;
if (oldest !== undefined) resetAwareQuotaCache.delete(oldest);
}
resetAwareQuotaCache.set(cacheKey, {
quota,
fetchedAt: Date.now(),
refreshPromise: null,
});
} else {
resetAwareQuotaCache.delete(cacheKey);
}
return quota;
})
.catch((error) => {
const previous = resetAwareQuotaCache.get(cacheKey);
if (previous) {
if (
!resetAwareQuotaCache.has(cacheKey) &&
resetAwareQuotaCache.size >= MAX_RESET_AWARE_CACHE
) {
const oldest = resetAwareQuotaCache.keys().next().value;
if (oldest !== undefined) resetAwareQuotaCache.delete(oldest);
}
resetAwareQuotaCache.set(cacheKey, { ...previous, refreshPromise: null });
}
log.warn?.("COMBO", "Reset-aware quota fetch failed.", {
comboName,
connectionId,
err: error,
operation: "quotaFetch",
provider,
});
return null;
});
if (!resetAwareQuotaCache.has(cacheKey) && resetAwareQuotaCache.size >= MAX_RESET_AWARE_CACHE) {
const oldest = resetAwareQuotaCache.keys().next().value;
if (oldest !== undefined) resetAwareQuotaCache.delete(oldest);
}
resetAwareQuotaCache.set(cacheKey, {
quota: existing?.quota ?? cached?.quota ?? null,
fetchedAt: existing?.fetchedAt ?? cached?.fetchedAt ?? 0,
refreshPromise,
});
return refreshPromise;
};
if (ttlMs > 0 && cached) {
const age = now - cached.fetchedAt;
if (age <= ttlMs) return cached.quota;
if (maxStaleMs > 0 && age <= ttlMs + maxStaleMs) {
void refresh();
return cached.quota;
}
}
return refresh();
}
type PreScreenResult = { profile: ProviderProfile | null; available: boolean };
export async function preScreenTargets(
targets: ResolvedComboTarget[],
isModelAvailable?: IsModelAvailable | null
): Promise<Map<string, PreScreenResult>> {
if (targets.length === 0) {
return new Map();
}
const results = await mapWithConcurrency(
targets,
PRE_SCREEN_CONCURRENCY,
async (target): Promise<{ key: string; result: PreScreenResult }> => {
const profile = await getRuntimeProviderProfile(target.provider).catch(() => null);
const breaker = getCircuitBreaker(target.provider);
if (breaker.getStatus().state === "OPEN") {
return { key: target.executionKey, result: { profile, available: false } };
}
let available = true;
if (isModelAvailable) {
// IsModelAvailable may return a sync boolean or a Promise; Promise.resolve
// normalizes both so the .catch() never runs against a bare boolean.
available = await Promise.resolve(isModelAvailable(target.modelStr, target)).catch(
() => true
);
}
return { key: target.executionKey, result: { profile, available } };
}
);
const map = new Map<string, PreScreenResult>();
for (const { key, result } of results) {
map.set(key, result);
}
return map;
}
async function orderTargetsByResetAwareQuota(
targets: ResolvedComboTarget[],
comboName: string,
configSource: Record<string, unknown> | null | undefined,
log: { warn?: (...args: unknown[]) => void },
apiKeyAllowedConnectionIds?: string[] | null
) {
if (targets.length === 0) return targets;
const config = resolveResetAwareConfig(configSource);
const connectionCache = new Map<string, Array<Record<string, unknown>>>();
const connectionLoadPromises = new Map<string, Promise<Array<Record<string, unknown>>>>();
const quotaPromises = new Map<string, Promise<unknown>>();
const connectionById = new Map<string, Record<string, unknown>>();
const expandedTargets: ResolvedComboTarget[] = [];
const targetsWithConnections = await Promise.all(
targets.map(async (target) => ({
connections: await getQuotaAwareConnectionsForTarget(
target,
connectionCache,
connectionLoadPromises,
comboName,
log
),
target,
}))
);
for (const { target, connections } of targetsWithConnections) {
for (const connection of connections) {
if (typeof connection.id === "string") connectionById.set(connection.id, connection);
}
const unrestrictedConnectionIds = getTargetConnectionIds(target, connections);
const connectionIds = filterAllowedConnectionIds(
unrestrictedConnectionIds,
apiKeyAllowedConnectionIds
);
if (connectionIds.length === 0) {
if (
unrestrictedConnectionIds.length > 0 &&
normalizeConnectionIds(apiKeyAllowedConnectionIds)
) {
continue;
}
expandedTargets.push(target);
continue;
}
for (const connectionId of connectionIds) {
expandedTargets.push({
...target,
connectionId,
executionKey:
target.connectionId === connectionId
? target.executionKey
: `${target.executionKey}@${connectionId}`,
});
}
}
const scoredTargets = await mapWithConcurrency(
expandedTargets,
RESET_AWARE_QUOTA_FETCH_CONCURRENCY,
async (target, index) => {
let quota: unknown = null;
const provider = getResetAwareProvider(target);
const fetcher = provider ? getQuotaFetcher(provider) : null;
if (fetcher && provider && target.connectionId) {
const quotaKey = `${provider}:${target.connectionId}`;
if (!quotaPromises.has(quotaKey)) {
quotaPromises.set(
quotaKey,
fetchResetAwareQuotaWithCache({
provider,
connectionId: target.connectionId,
connection: connectionById.get(target.connectionId),
fetcher,
config,
log,
comboName,
})
);
}
quota = await quotaPromises.get(quotaKey)!;
}
const { score } = scoreResetAwareQuota(quota, config);
return { target, score, index };
}
);
scoredTargets.sort((a, b) => {
if (b.score !== a.score) return b.score - a.score;
return a.index - b.index;
});
const bestScore = scoredTargets[0]?.score ?? 0;
const tiedTargets = scoredTargets.filter((entry) => bestScore - entry.score <= config.tieBand);
let orderedTiedTargets = tiedTargets;
if (tiedTargets.length > 1) {
const key = `reset-aware:${comboName}`;
const counter = rrCounters.get(key) || 0;
if (!rrCounters.has(key) && rrCounters.size >= MAX_RR_COUNTERS) {
const oldest = rrCounters.keys().next().value;
if (oldest !== undefined) rrCounters.delete(oldest);
}
rrCounters.set(key, counter + 1);
const startIndex = counter % tiedTargets.length;
orderedTiedTargets = [...tiedTargets.slice(startIndex), ...tiedTargets.slice(0, startIndex)];
}
const tiedExecutionKeys = new Set(orderedTiedTargets.map((entry) => entry.target.executionKey));
return [
...orderedTiedTargets,
...scoredTargets.filter((entry) => !tiedExecutionKeys.has(entry.target.executionKey)),
].map((entry) => entry.target);
}
function getResetWindowTimestampMs(quota: unknown, windows: ResetWindowName[]): number {
if (!quota || !isRecord(quota) || quota.limitReached === true) return Infinity;
let selectedResetMs = Infinity;
for (const windowName of windows) {
const window = resolveQuotaWindowByName(quota, windowName);
const resetMs = parseResetTimeMs(window?.resetAt ?? null);
if (Number.isFinite(resetMs)) {
selectedResetMs = Math.min(selectedResetMs, resetMs);
}
}
if (!Number.isFinite(selectedResetMs)) {
selectedResetMs = parseResetTimeMs(normalizeResetAt(quota.resetAt));
}
return Number.isFinite(selectedResetMs) ? selectedResetMs : Infinity;
}
function getResetWindowHorizonMs(windows: ResetWindowName[]): number {
if (windows.includes("monthly")) return 30 * 24 * 60 * 60 * 1000;
if (windows.includes("weekly")) return RESET_AWARE_WEEKLY_WINDOW_MS;
return RESET_AWARE_SESSION_WINDOW_MS;
}
function calculateResetWindowAffinity(quota: unknown, config: ResetWindowConfig): number {
const resetMs = getResetWindowTimestampMs(quota, config.windows);
if (!Number.isFinite(resetMs)) return 0.5;
const msUntilReset = resetMs - Date.now();
if (msUntilReset <= 0) return 1;
return clamp01(1 - msUntilReset / getResetWindowHorizonMs(config.windows));
}
// Reset-aware / reset-window quota config, scoring, and window-math helpers were
// extracted to combo/quotaScoring.ts (pure) and the stateful cache + strategy
// orderers to combo/quotaStrategies.ts (combo split D7b). The two cache Maps
// (resetAwareConnectionCache, resetAwareQuotaCache) live as single instances in
// quotaStrategies.ts alongside their only readers/writers (state cohesion).
// combo.ts imports back the three reset-window helpers buildAutoCandidates +
// orchestration need, plus the strategy orderers and preScreenTargets (above).
// Bootstrap defaults from ClawRouter benchmark (used when no local latency history exists yet)
const DEFAULT_MODEL_P95_MS: Record<string, number> = {
@@ -1069,137 +399,6 @@ export async function buildAutoCandidates(
return candidates;
}
async function orderTargetsByResetWindow(
targets: ResolvedComboTarget[],
comboName: string,
configSource: Record<string, unknown> | null | undefined,
log: { warn?: (...args: unknown[]) => void },
apiKeyAllowedConnectionIds?: string[] | null
) {
if (targets.length === 0) return targets;
const config = resolveResetWindowConfig(configSource);
const connectionCache = new Map<string, Array<Record<string, unknown>>>();
const connectionLoadPromises = new Map<string, Promise<Array<Record<string, unknown>>>>();
const quotaPromises = new Map<string, Promise<unknown>>();
const connectionById = new Map<string, Record<string, unknown>>();
const expandedTargets: ResolvedComboTarget[] = [];
const targetsWithConnections = await Promise.all(
targets.map(async (target) => ({
connections: await getQuotaAwareConnectionsForTarget(
target,
connectionCache,
connectionLoadPromises,
comboName,
log
),
target,
}))
);
for (const { target, connections } of targetsWithConnections) {
for (const connection of connections) {
if (typeof connection.id === "string") connectionById.set(connection.id, connection);
}
const unrestrictedConnectionIds = getTargetConnectionIds(target, connections);
const connectionIds = filterAllowedConnectionIds(
unrestrictedConnectionIds,
apiKeyAllowedConnectionIds
);
if (connectionIds.length === 0) {
if (
unrestrictedConnectionIds.length > 0 &&
normalizeConnectionIds(apiKeyAllowedConnectionIds)
) {
continue;
}
expandedTargets.push(target);
continue;
}
for (const connectionId of connectionIds) {
expandedTargets.push({
...target,
connectionId,
executionKey:
target.connectionId === connectionId
? target.executionKey
: `${target.executionKey}@${connectionId}`,
});
}
}
const scoredTargets = await mapWithConcurrency(
expandedTargets,
RESET_AWARE_QUOTA_FETCH_CONCURRENCY,
async (target, index) => {
let quota: unknown = null;
const provider = getResetAwareProvider(target);
const fetcher = provider ? getQuotaFetcher(provider) : null;
if (fetcher && provider && target.connectionId) {
const quotaKey = `${provider}:${target.connectionId}`;
if (!quotaPromises.has(quotaKey)) {
quotaPromises.set(
quotaKey,
fetchResetAwareQuotaWithCache({
provider,
connectionId: target.connectionId,
connection: connectionById.get(target.connectionId),
fetcher,
config,
log,
comboName,
})
);
}
quota = await quotaPromises.get(quotaKey)!;
}
return {
target,
resetMs: getResetWindowTimestampMs(quota, config.windows),
index,
};
}
);
scoredTargets.sort((a, b) => {
if (a.resetMs !== b.resetMs) return a.resetMs - b.resetMs;
return a.index - b.index;
});
const bestResetMs = scoredTargets[0]?.resetMs ?? Infinity;
if (!Number.isFinite(bestResetMs) || config.tieBandMs <= 0) {
return scoredTargets.map((entry) => entry.target);
}
const tiedTargets = scoredTargets.filter(
(entry) => entry.resetMs - bestResetMs <= config.tieBandMs
);
if (tiedTargets.length <= 1) return scoredTargets.map((entry) => entry.target);
const key = `reset-window:${comboName}`;
const counter = rrCounters.get(key) || 0;
if (!rrCounters.has(key) && rrCounters.size >= MAX_RR_COUNTERS) {
const oldest = rrCounters.keys().next().value;
if (oldest !== undefined) rrCounters.delete(oldest);
}
rrCounters.set(key, counter + 1);
const startIndex = counter % tiedTargets.length;
const orderedTiedTargets = [
...tiedTargets.slice(startIndex),
...tiedTargets.slice(0, startIndex),
];
const tiedExecutionKeys = new Set(orderedTiedTargets.map((entry) => entry.target.executionKey));
return [
...orderedTiedTargets,
...scoredTargets.filter((entry) => !tiedExecutionKeys.has(entry.target.executionKey)),
].map((entry) => entry.target);
}
/**
* Handle combo chat with fallback.
* @param {Object} options

View File

@@ -0,0 +1,311 @@
/**
* Pure scoring + window-math helpers for the reset-aware / reset-window combo
* strategies. No module state, no async, no I/O — extracted byte-identically
* from combo.ts (QG v2 Fase 9 T5 D7b) as the pure half of the reset-aware quota
* block. The stateful/async half (cache Maps + connection/quota fetchers +
* orderTargets*) lives in ./quotaStrategies.ts, which imports the scoring
* helpers from here.
*
* Pure leaf: this module never imports from the combo barrel.
*/
import { clamp01 } from "../../utils/number.ts";
import { isRecord } from "./comboData.ts";
import type { SlaRoutingPolicy } from "../autoCombo/routerStrategy.ts";
import { RESET_WINDOW_NAMES } from "./types.ts";
import type { ResolvedComboTarget } from "./types.ts";
const RESET_AWARE_SESSION_WINDOW_MS = 5 * 60 * 60 * 1000;
const RESET_AWARE_WEEKLY_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
const RESET_AWARE_SESSION_REMAINING_WEIGHT = 0.45;
const RESET_AWARE_SESSION_RESET_PRESSURE_WEIGHT = 0.55;
const RESET_AWARE_WEEKLY_REMAINING_WEIGHT = 0.25;
const RESET_AWARE_WEEKLY_RESET_PRESSURE_WEIGHT = 0.75;
const RESET_AWARE_DEFAULTS = {
sessionWeight: 0.35,
weeklyWeight: 0.65,
tieBandPercent: 5,
exhaustionGuardPercent: 10,
};
const RESET_WINDOW_DEFAULT_TIE_BAND_MS = 60_000;
type ResetWindowName = (typeof RESET_WINDOW_NAMES)[number];
export type QuotaFetchCacheConfig = {
quotaCacheTtlMs: number;
quotaCacheMaxStaleMs: number;
};
export type ResetWindowConfig = ReturnType<typeof resolveResetWindowConfig>;
function finiteNumberOrNull(value: unknown): number | null {
const numericValue = Number(value);
return Number.isFinite(numericValue) ? numericValue : null;
}
function getPercentConfig(value: unknown, fallback: number): number {
const numericValue = finiteNumberOrNull(value);
if (numericValue === null) return fallback;
return Math.max(0, Math.min(100, numericValue));
}
function getWeightConfig(value: unknown, fallback: number): number {
const numericValue = finiteNumberOrNull(value);
if (numericValue === null || numericValue < 0) return fallback;
return numericValue;
}
function getDurationConfig(value: unknown, fallback: number, max: number): number {
const numericValue = finiteNumberOrNull(value);
if (numericValue === null || numericValue < 0) return fallback;
return Math.min(max, Math.floor(numericValue));
}
export function resolveResetAwareConfig(config: Record<string, unknown> | null | undefined) {
const sessionWeight = getWeightConfig(
config?.resetAwareSessionWeight,
RESET_AWARE_DEFAULTS.sessionWeight
);
const weeklyWeight = getWeightConfig(
config?.resetAwareWeeklyWeight,
RESET_AWARE_DEFAULTS.weeklyWeight
);
const totalWeight = sessionWeight + weeklyWeight;
const normalizedSessionWeight =
totalWeight > 0 ? sessionWeight / totalWeight : RESET_AWARE_DEFAULTS.sessionWeight;
return {
sessionWeight: normalizedSessionWeight,
weeklyWeight: 1 - normalizedSessionWeight,
tieBand:
getPercentConfig(config?.resetAwareTieBandPercent, RESET_AWARE_DEFAULTS.tieBandPercent) / 100,
exhaustionGuard:
getPercentConfig(
config?.resetAwareExhaustionGuardPercent,
RESET_AWARE_DEFAULTS.exhaustionGuardPercent
) / 100,
quotaCacheTtlMs: getDurationConfig(config?.resetAwareQuotaCacheTtlMs, 0, 300_000),
quotaCacheMaxStaleMs: getDurationConfig(config?.resetAwareQuotaCacheMaxStaleMs, 0, 3_600_000),
};
}
export function resolveResetWindowConfig(config: Record<string, unknown> | null | undefined) {
const rawWindows = Array.isArray(config?.resetWindowWindows) ? config.resetWindowWindows : null;
const windows = rawWindows
?.filter((windowName): windowName is ResetWindowName =>
(RESET_WINDOW_NAMES as readonly string[]).includes(String(windowName))
)
.filter((windowName, index, array) => array.indexOf(windowName) === index);
const effectiveWindows =
windows && windows.length > 0
? windows
: config?.resetWindowIncludeSession === true
? (["weekly", "session"] as ResetWindowName[])
: (["weekly"] as ResetWindowName[]);
return {
windows: effectiveWindows,
tieBandMs: Math.max(
0,
finiteNumberOrNull(config?.resetWindowTieBandMs) ?? RESET_WINDOW_DEFAULT_TIE_BAND_MS
),
quotaCacheTtlMs: getDurationConfig(config?.resetWindowQuotaCacheTtlMs, 0, 300_000),
quotaCacheMaxStaleMs: getDurationConfig(config?.resetWindowQuotaCacheMaxStaleMs, 0, 3_600_000),
};
}
export function resolveSlaRoutingPolicy(
config: Record<string, unknown> | null | undefined
): SlaRoutingPolicy | undefined {
if (!config) return undefined;
const nestedSla = isRecord(config.sla) ? config.sla : {};
const targetP95Ms = finiteNumberOrNull(config.slaTargetP95Ms ?? nestedSla.targetP95Ms);
const maxErrorRate = finiteNumberOrNull(config.slaMaxErrorRate ?? nestedSla.maxErrorRate);
const maxCostPer1MTokens = finiteNumberOrNull(
config.slaMaxCostPer1MTokens ?? nestedSla.maxCostPer1MTokens
);
const hardConstraints = config.slaHardConstraints ?? nestedSla.hardConstraints;
const policy: SlaRoutingPolicy = {};
if (targetP95Ms !== null && targetP95Ms > 0) policy.targetP95Ms = targetP95Ms;
if (maxErrorRate !== null && maxErrorRate >= 0) policy.maxErrorRate = clamp01(maxErrorRate);
if (maxCostPer1MTokens !== null && maxCostPer1MTokens > 0) {
policy.maxCostPer1MTokens = maxCostPer1MTokens;
}
if (typeof hardConstraints === "boolean") policy.hardConstraints = hardConstraints;
return Object.keys(policy).length > 0 ? policy : undefined;
}
export function getResetAwareProvider(target: ResolvedComboTarget): string | null {
const provider = (target.providerId || target.provider || "").toLowerCase();
return provider || null;
}
function normalizeResetAt(value: unknown): string | null {
if (typeof value === "string" && value.trim().length > 0) return value.trim();
if (typeof value === "number" && Number.isFinite(value)) return String(value);
return null;
}
function parseResetTimeMs(resetAt: string | null | undefined): number {
if (!resetAt) return NaN;
const resetTime = Date.parse(resetAt);
if (Number.isFinite(resetTime)) return resetTime;
if (!/^\d+(?:\.\d+)?$/.test(resetAt)) return NaN;
const numericResetAt = Number(resetAt);
if (!Number.isFinite(numericResetAt)) return NaN;
return numericResetAt < 10_000_000_000 ? numericResetAt * 1000 : numericResetAt;
}
function getQuotaWindow(
quota: unknown,
key: "window5h" | "window7d" | "windowWeekly" | "windowMonthly"
): { percentUsed: number | null; resetAt: string | null } | null {
if (!isRecord(quota)) return null;
const window = quota[key];
if (!isRecord(window)) return null;
const percentUsed = finiteNumberOrNull(window.percentUsed);
const resetAt = normalizeResetAt(window.resetAt);
return { percentUsed, resetAt };
}
function normalizeWindowPercentUsed(value: unknown): number | null {
const numericValue = finiteNumberOrNull(value);
if (numericValue === null) return null;
if (numericValue > 1) return clamp01(numericValue / 100);
return clamp01(numericValue);
}
function getNamedQuotaWindow(
quota: unknown,
windowName: ResetWindowName
): { percentUsed: number | null; resetAt: string | null } | null {
if (!quota || !isRecord(quota)) return null;
if (windowName === "session") return getQuotaWindow(quota, "window5h");
if (windowName === "weekly") {
return getQuotaWindow(quota, "window7d") || getQuotaWindow(quota, "windowWeekly");
}
if (windowName === "monthly") return getQuotaWindow(quota, "windowMonthly");
return null;
}
function getWindowsMapQuotaWindow(
quota: unknown,
windowName: ResetWindowName
): { percentUsed: number | null; resetAt: string | null } | null {
if (!quota || !isRecord(quota) || !isRecord(quota.windows)) return null;
const candidates = Object.entries(quota.windows)
.map(([key, value]) => ({ key: key.toLowerCase(), value }))
.filter(({ key }) => key === windowName || key.startsWith(`${windowName} `));
if (candidates.length === 0) return null;
candidates.sort((a, b) => a.key.localeCompare(b.key));
const window = candidates[0].value;
if (!isRecord(window)) return null;
return {
percentUsed: normalizeWindowPercentUsed(window.percentUsed),
resetAt: normalizeResetAt(window.resetAt),
};
}
function resolveQuotaWindowByName(
quota: unknown,
windowName: ResetWindowName
): { percentUsed: number | null; resetAt: string | null } | null {
return getNamedQuotaWindow(quota, windowName) || getWindowsMapQuotaWindow(quota, windowName);
}
function getResetUrgency(resetAt: string | null | undefined, windowMs: number): number {
if (!resetAt) return 0.5;
const resetTime = parseResetTimeMs(resetAt);
if (!Number.isFinite(resetTime)) return 0.5;
const msUntilReset = resetTime - Date.now();
if (msUntilReset <= 0) return 1;
return clamp01(1 - msUntilReset / windowMs);
}
function scoreQuotaWindow(
remaining: number,
resetAt: string | null | undefined,
windowMs: number,
remainingWeight: number,
resetPressureWeight: number
): number {
const normalizedRemaining = clamp01(remaining);
const resetUrgency = getResetUrgency(resetAt, windowMs);
const resetPressure = resetUrgency * (1 - normalizedRemaining);
return remainingWeight * normalizedRemaining + resetPressureWeight * resetPressure;
}
export function scoreResetAwareQuota(
quota: unknown,
config: ReturnType<typeof resolveResetAwareConfig>
) {
if (!quota || !isRecord(quota)) return { score: 0.5 };
if (quota.limitReached === true) return { score: -Infinity };
const overallPercentUsed = clamp01(finiteNumberOrNull(quota.percentUsed) ?? 0.5);
const sessionWindow = getQuotaWindow(quota, "window5h");
const weeklyWindow = getQuotaWindow(quota, "window7d") || getQuotaWindow(quota, "windowWeekly");
const sessionRemaining = clamp01(1 - (sessionWindow?.percentUsed ?? overallPercentUsed));
const weeklyRemaining = clamp01(1 - (weeklyWindow?.percentUsed ?? overallPercentUsed));
const sessionScore = scoreQuotaWindow(
sessionRemaining,
sessionWindow?.resetAt,
RESET_AWARE_SESSION_WINDOW_MS,
RESET_AWARE_SESSION_REMAINING_WEIGHT,
RESET_AWARE_SESSION_RESET_PRESSURE_WEIGHT
);
const weeklyScore = scoreQuotaWindow(
weeklyRemaining,
weeklyWindow?.resetAt ?? normalizeResetAt(quota.resetAt),
RESET_AWARE_WEEKLY_WINDOW_MS,
RESET_AWARE_WEEKLY_REMAINING_WEIGHT,
RESET_AWARE_WEEKLY_RESET_PRESSURE_WEIGHT
);
let score = config.sessionWeight * sessionScore + config.weeklyWeight * weeklyScore;
if (config.exhaustionGuard > 0 && sessionRemaining < config.exhaustionGuard) {
score *= Math.max(0.05, sessionRemaining / config.exhaustionGuard);
}
return { score };
}
export function getResetWindowTimestampMs(quota: unknown, windows: ResetWindowName[]): number {
if (!quota || !isRecord(quota) || quota.limitReached === true) return Infinity;
let selectedResetMs = Infinity;
for (const windowName of windows) {
const window = resolveQuotaWindowByName(quota, windowName);
const resetMs = parseResetTimeMs(window?.resetAt ?? null);
if (Number.isFinite(resetMs)) {
selectedResetMs = Math.min(selectedResetMs, resetMs);
}
}
if (!Number.isFinite(selectedResetMs)) {
selectedResetMs = parseResetTimeMs(normalizeResetAt(quota.resetAt));
}
return Number.isFinite(selectedResetMs) ? selectedResetMs : Infinity;
}
function getResetWindowHorizonMs(windows: ResetWindowName[]): number {
if (windows.includes("monthly")) return 30 * 24 * 60 * 60 * 1000;
if (windows.includes("weekly")) return RESET_AWARE_WEEKLY_WINDOW_MS;
return RESET_AWARE_SESSION_WINDOW_MS;
}
export function calculateResetWindowAffinity(quota: unknown, config: ResetWindowConfig): number {
const resetMs = getResetWindowTimestampMs(quota, config.windows);
if (!Number.isFinite(resetMs)) return 0.5;
const msUntilReset = resetMs - Date.now();
if (msUntilReset <= 0) return 1;
return clamp01(1 - msUntilReset / getResetWindowHorizonMs(config.windows));
}

View File

@@ -0,0 +1,568 @@
/**
* Stateful + async reset-aware / reset-window quota strategies for combo routing.
*
* Holds the two mutable module-level caches that back reset-aware routing
* (`resetAwareConnectionCache` for per-provider active connections and
* `resetAwareQuotaCache` for per-connection quota snapshots), plus the helpers
* that read/write them and the strategy orderers. Extracted byte-identically
* from combo.ts (QG v2 Fase 9 T5 D7b) — the larger, stateful half of the
* reset-aware quota block. The pure scoring/window-math half lives in
* ./quotaScoring.ts and is imported here.
*
* State cohesion: `resetAwareConnectionCache`, `resetAwareQuotaCache`, and
* `MAX_RESET_AWARE_CACHE` MUST remain single instances defined once here,
* alongside their only readers/writers (getQuotaAwareConnectionsForTarget,
* fetchResetAwareQuotaWithCache) — never duplicate a Map.
*
* Cross-module state: the tie-band round-robin in orderTargetsByResetAwareQuota
* and orderTargetsByResetWindow shares the same rrCounters Map from ./rrState.ts
* (D7a) so reset-aware tie rotation stays consistent with round-robin routing.
*
* Pure leaf: this module never imports from the combo barrel.
*/
import { getRuntimeProviderProfile, type ProviderProfile } from "../accountFallback.ts";
import { PRE_SCREEN_CONCURRENCY } from "../comboConfig.ts";
import { getQuotaFetcher } from "../quotaPreflight.ts";
import { getCircuitBreaker } from "../../../src/shared/utils/circuitBreaker";
import { getProviderConnections } from "../../../src/lib/db/providers";
import { MAX_RR_COUNTERS, rrCounters } from "./rrState.ts";
import type { ResolvedComboTarget, IsModelAvailable } from "./types.ts";
import {
resolveResetAwareConfig,
resolveResetWindowConfig,
getResetAwareProvider,
scoreResetAwareQuota,
getResetWindowTimestampMs,
type QuotaFetchCacheConfig,
} from "./quotaScoring.ts";
const RESET_AWARE_CONNECTION_CACHE_TTL_MS = 30_000;
const RESET_AWARE_QUOTA_FETCH_CONCURRENCY = 5;
const MAX_RESET_AWARE_CACHE = 200;
const resetAwareConnectionCache = new Map<
string,
{ fetchedAt: number; connections: Array<Record<string, unknown>> }
>();
const resetAwareQuotaCache = new Map<
string,
{ fetchedAt: number; quota: unknown; refreshPromise: Promise<unknown> | null }
>();
async function getQuotaAwareConnectionsForTarget(
target: ResolvedComboTarget,
connectionCache: Map<string, Array<Record<string, unknown>>>,
connectionLoadPromises: Map<string, Promise<Array<Record<string, unknown>>>>,
comboName: string,
log: { warn?: (...args: unknown[]) => void }
) {
const provider = getResetAwareProvider(target);
if (!provider || !getQuotaFetcher(provider)) return [];
if (!connectionCache.has(provider)) {
const cached = resetAwareConnectionCache.get(provider);
if (cached && Date.now() - cached.fetchedAt < RESET_AWARE_CONNECTION_CACHE_TTL_MS) {
connectionCache.set(provider, cached.connections);
return cached.connections;
}
if (!connectionLoadPromises.has(provider)) {
connectionLoadPromises.set(
provider,
(async () => {
try {
const connections = await getProviderConnections({ provider, isActive: true });
const activeConnections = Array.isArray(connections)
? (connections as Array<Record<string, unknown>>)
: [];
if (
!resetAwareConnectionCache.has(provider) &&
resetAwareConnectionCache.size >= MAX_RESET_AWARE_CACHE
) {
const oldest = resetAwareConnectionCache.keys().next().value;
if (oldest !== undefined) resetAwareConnectionCache.delete(oldest);
}
resetAwareConnectionCache.set(provider, {
connections: activeConnections,
fetchedAt: Date.now(),
});
return activeConnections;
} catch (error) {
log.warn?.("COMBO", "Reset-aware failed to load quota-aware connections.", {
comboName,
err: error,
operation: "getProviderConnections",
provider,
});
return [];
}
})()
);
}
const connections = await connectionLoadPromises.get(provider)!;
connectionCache.set(provider, connections);
}
return connectionCache.get(provider) || [];
}
function normalizeConnectionIds(value: unknown): string[] | null {
if (!Array.isArray(value)) return null;
const ids = value.filter(
(connectionId): connectionId is string =>
typeof connectionId === "string" && connectionId.trim().length > 0
);
return ids.length > 0 ? ids : null;
}
function filterAllowedConnectionIds(
connectionIds: string[],
apiKeyAllowedConnectionIds: string[] | null | undefined
): string[] {
const allowedIds = normalizeConnectionIds(apiKeyAllowedConnectionIds);
if (!allowedIds) return connectionIds;
const allowedSet = new Set(allowedIds);
return connectionIds.filter((connectionId) => allowedSet.has(connectionId));
}
function getTargetConnectionIds(
target: ResolvedComboTarget,
connections: Array<Record<string, unknown>>
): string[] {
let connectionIds: string[];
if (target.connectionId) {
return [target.connectionId];
}
if (Array.isArray(target.allowedConnectionIds) && target.allowedConnectionIds.length > 0) {
return target.allowedConnectionIds.filter(
(connectionId): connectionId is string =>
typeof connectionId === "string" && connectionId.trim().length > 0
);
}
connectionIds = connections
.map((connection) => (typeof connection.id === "string" ? connection.id : null))
.filter((connectionId): connectionId is string => !!connectionId);
return connectionIds;
}
async function mapWithConcurrency<T, R>(
items: T[],
concurrency: number,
mapper: (item: T, index: number) => Promise<R>
): Promise<R[]> {
const results = new Array<R>(items.length);
let nextIndex = 0;
const workerCount = Math.max(1, Math.min(concurrency, items.length));
await Promise.all(
Array.from({ length: workerCount }, async () => {
while (nextIndex < items.length) {
const currentIndex = nextIndex++;
results[currentIndex] = await mapper(items[currentIndex], currentIndex);
}
})
);
return results;
}
export async function fetchResetAwareQuotaWithCache({
provider,
connectionId,
connection,
fetcher,
config,
log,
comboName,
}: {
provider: string;
connectionId: string;
connection?: Record<string, unknown>;
fetcher: (connectionId: string, connection?: Record<string, unknown>) => Promise<unknown>;
config: QuotaFetchCacheConfig;
log: { debug?: (...args: unknown[]) => void; warn?: (...args: unknown[]) => void };
comboName: string;
}): Promise<unknown> {
const cacheKey = `${provider}:${connectionId}`;
const ttlMs = config.quotaCacheTtlMs;
const maxStaleMs = config.quotaCacheMaxStaleMs;
const now = Date.now();
const cached = resetAwareQuotaCache.get(cacheKey);
if (ttlMs <= 0 && maxStaleMs <= 0) {
try {
return await fetcher(connectionId, connection);
} catch (error) {
log.warn?.("COMBO", "Reset-aware quota fetch failed.", {
comboName,
connectionId,
err: error,
operation: "quotaFetch",
provider,
});
return null;
}
}
const refresh = () => {
const existing = resetAwareQuotaCache.get(cacheKey);
if (existing?.refreshPromise != null) return existing.refreshPromise;
const refreshPromise = fetcher(connectionId, connection)
.then((quota) => {
if (quota) {
if (
!resetAwareQuotaCache.has(cacheKey) &&
resetAwareQuotaCache.size >= MAX_RESET_AWARE_CACHE
) {
const oldest = resetAwareQuotaCache.keys().next().value;
if (oldest !== undefined) resetAwareQuotaCache.delete(oldest);
}
resetAwareQuotaCache.set(cacheKey, {
quota,
fetchedAt: Date.now(),
refreshPromise: null,
});
} else {
resetAwareQuotaCache.delete(cacheKey);
}
return quota;
})
.catch((error) => {
const previous = resetAwareQuotaCache.get(cacheKey);
if (previous) {
if (
!resetAwareQuotaCache.has(cacheKey) &&
resetAwareQuotaCache.size >= MAX_RESET_AWARE_CACHE
) {
const oldest = resetAwareQuotaCache.keys().next().value;
if (oldest !== undefined) resetAwareQuotaCache.delete(oldest);
}
resetAwareQuotaCache.set(cacheKey, { ...previous, refreshPromise: null });
}
log.warn?.("COMBO", "Reset-aware quota fetch failed.", {
comboName,
connectionId,
err: error,
operation: "quotaFetch",
provider,
});
return null;
});
if (!resetAwareQuotaCache.has(cacheKey) && resetAwareQuotaCache.size >= MAX_RESET_AWARE_CACHE) {
const oldest = resetAwareQuotaCache.keys().next().value;
if (oldest !== undefined) resetAwareQuotaCache.delete(oldest);
}
resetAwareQuotaCache.set(cacheKey, {
quota: existing?.quota ?? cached?.quota ?? null,
fetchedAt: existing?.fetchedAt ?? cached?.fetchedAt ?? 0,
refreshPromise,
});
return refreshPromise;
};
if (ttlMs > 0 && cached) {
const age = now - cached.fetchedAt;
if (age <= ttlMs) return cached.quota;
if (maxStaleMs > 0 && age <= ttlMs + maxStaleMs) {
void refresh();
return cached.quota;
}
}
return refresh();
}
export type PreScreenResult = { profile: ProviderProfile | null; available: boolean };
export async function preScreenTargets(
targets: ResolvedComboTarget[],
isModelAvailable?: IsModelAvailable | null
): Promise<Map<string, PreScreenResult>> {
if (targets.length === 0) {
return new Map();
}
const results = await mapWithConcurrency(
targets,
PRE_SCREEN_CONCURRENCY,
async (target): Promise<{ key: string; result: PreScreenResult }> => {
const profile = await getRuntimeProviderProfile(target.provider).catch(() => null);
const breaker = getCircuitBreaker(target.provider);
if (breaker.getStatus().state === "OPEN") {
return { key: target.executionKey, result: { profile, available: false } };
}
let available = true;
if (isModelAvailable) {
// IsModelAvailable may return a sync boolean or a Promise; Promise.resolve
// normalizes both so the .catch() never runs against a bare boolean.
available = await Promise.resolve(isModelAvailable(target.modelStr, target)).catch(
() => true
);
}
return { key: target.executionKey, result: { profile, available } };
}
);
const map = new Map<string, PreScreenResult>();
for (const { key, result } of results) {
map.set(key, result);
}
return map;
}
export async function orderTargetsByResetAwareQuota(
targets: ResolvedComboTarget[],
comboName: string,
configSource: Record<string, unknown> | null | undefined,
log: { warn?: (...args: unknown[]) => void },
apiKeyAllowedConnectionIds?: string[] | null
) {
if (targets.length === 0) return targets;
const config = resolveResetAwareConfig(configSource);
const connectionCache = new Map<string, Array<Record<string, unknown>>>();
const connectionLoadPromises = new Map<string, Promise<Array<Record<string, unknown>>>>();
const quotaPromises = new Map<string, Promise<unknown>>();
const connectionById = new Map<string, Record<string, unknown>>();
const expandedTargets: ResolvedComboTarget[] = [];
const targetsWithConnections = await Promise.all(
targets.map(async (target) => ({
connections: await getQuotaAwareConnectionsForTarget(
target,
connectionCache,
connectionLoadPromises,
comboName,
log
),
target,
}))
);
for (const { target, connections } of targetsWithConnections) {
for (const connection of connections) {
if (typeof connection.id === "string") connectionById.set(connection.id, connection);
}
const unrestrictedConnectionIds = getTargetConnectionIds(target, connections);
const connectionIds = filterAllowedConnectionIds(
unrestrictedConnectionIds,
apiKeyAllowedConnectionIds
);
if (connectionIds.length === 0) {
if (
unrestrictedConnectionIds.length > 0 &&
normalizeConnectionIds(apiKeyAllowedConnectionIds)
) {
continue;
}
expandedTargets.push(target);
continue;
}
for (const connectionId of connectionIds) {
expandedTargets.push({
...target,
connectionId,
executionKey:
target.connectionId === connectionId
? target.executionKey
: `${target.executionKey}@${connectionId}`,
});
}
}
const scoredTargets = await mapWithConcurrency(
expandedTargets,
RESET_AWARE_QUOTA_FETCH_CONCURRENCY,
async (target, index) => {
let quota: unknown = null;
const provider = getResetAwareProvider(target);
const fetcher = provider ? getQuotaFetcher(provider) : null;
if (fetcher && provider && target.connectionId) {
const quotaKey = `${provider}:${target.connectionId}`;
if (!quotaPromises.has(quotaKey)) {
quotaPromises.set(
quotaKey,
fetchResetAwareQuotaWithCache({
provider,
connectionId: target.connectionId,
connection: connectionById.get(target.connectionId),
fetcher,
config,
log,
comboName,
})
);
}
quota = await quotaPromises.get(quotaKey)!;
}
const { score } = scoreResetAwareQuota(quota, config);
return { target, score, index };
}
);
scoredTargets.sort((a, b) => {
if (b.score !== a.score) return b.score - a.score;
return a.index - b.index;
});
const bestScore = scoredTargets[0]?.score ?? 0;
const tiedTargets = scoredTargets.filter((entry) => bestScore - entry.score <= config.tieBand);
let orderedTiedTargets = tiedTargets;
if (tiedTargets.length > 1) {
const key = `reset-aware:${comboName}`;
const counter = rrCounters.get(key) || 0;
if (!rrCounters.has(key) && rrCounters.size >= MAX_RR_COUNTERS) {
const oldest = rrCounters.keys().next().value;
if (oldest !== undefined) rrCounters.delete(oldest);
}
rrCounters.set(key, counter + 1);
const startIndex = counter % tiedTargets.length;
orderedTiedTargets = [...tiedTargets.slice(startIndex), ...tiedTargets.slice(0, startIndex)];
}
const tiedExecutionKeys = new Set(orderedTiedTargets.map((entry) => entry.target.executionKey));
return [
...orderedTiedTargets,
...scoredTargets.filter((entry) => !tiedExecutionKeys.has(entry.target.executionKey)),
].map((entry) => entry.target);
}
export async function orderTargetsByResetWindow(
targets: ResolvedComboTarget[],
comboName: string,
configSource: Record<string, unknown> | null | undefined,
log: { warn?: (...args: unknown[]) => void },
apiKeyAllowedConnectionIds?: string[] | null
) {
if (targets.length === 0) return targets;
const config = resolveResetWindowConfig(configSource);
const connectionCache = new Map<string, Array<Record<string, unknown>>>();
const connectionLoadPromises = new Map<string, Promise<Array<Record<string, unknown>>>>();
const quotaPromises = new Map<string, Promise<unknown>>();
const connectionById = new Map<string, Record<string, unknown>>();
const expandedTargets: ResolvedComboTarget[] = [];
const targetsWithConnections = await Promise.all(
targets.map(async (target) => ({
connections: await getQuotaAwareConnectionsForTarget(
target,
connectionCache,
connectionLoadPromises,
comboName,
log
),
target,
}))
);
for (const { target, connections } of targetsWithConnections) {
for (const connection of connections) {
if (typeof connection.id === "string") connectionById.set(connection.id, connection);
}
const unrestrictedConnectionIds = getTargetConnectionIds(target, connections);
const connectionIds = filterAllowedConnectionIds(
unrestrictedConnectionIds,
apiKeyAllowedConnectionIds
);
if (connectionIds.length === 0) {
if (
unrestrictedConnectionIds.length > 0 &&
normalizeConnectionIds(apiKeyAllowedConnectionIds)
) {
continue;
}
expandedTargets.push(target);
continue;
}
for (const connectionId of connectionIds) {
expandedTargets.push({
...target,
connectionId,
executionKey:
target.connectionId === connectionId
? target.executionKey
: `${target.executionKey}@${connectionId}`,
});
}
}
const scoredTargets = await mapWithConcurrency(
expandedTargets,
RESET_AWARE_QUOTA_FETCH_CONCURRENCY,
async (target, index) => {
let quota: unknown = null;
const provider = getResetAwareProvider(target);
const fetcher = provider ? getQuotaFetcher(provider) : null;
if (fetcher && provider && target.connectionId) {
const quotaKey = `${provider}:${target.connectionId}`;
if (!quotaPromises.has(quotaKey)) {
quotaPromises.set(
quotaKey,
fetchResetAwareQuotaWithCache({
provider,
connectionId: target.connectionId,
connection: connectionById.get(target.connectionId),
fetcher,
config,
log,
comboName,
})
);
}
quota = await quotaPromises.get(quotaKey)!;
}
return {
target,
resetMs: getResetWindowTimestampMs(quota, config.windows),
index,
};
}
);
scoredTargets.sort((a, b) => {
if (a.resetMs !== b.resetMs) return a.resetMs - b.resetMs;
return a.index - b.index;
});
const bestResetMs = scoredTargets[0]?.resetMs ?? Infinity;
if (!Number.isFinite(bestResetMs) || config.tieBandMs <= 0) {
return scoredTargets.map((entry) => entry.target);
}
const tiedTargets = scoredTargets.filter(
(entry) => entry.resetMs - bestResetMs <= config.tieBandMs
);
if (tiedTargets.length <= 1) return scoredTargets.map((entry) => entry.target);
const key = `reset-window:${comboName}`;
const counter = rrCounters.get(key) || 0;
if (!rrCounters.has(key) && rrCounters.size >= MAX_RR_COUNTERS) {
const oldest = rrCounters.keys().next().value;
if (oldest !== undefined) rrCounters.delete(oldest);
}
rrCounters.set(key, counter + 1);
const startIndex = counter % tiedTargets.length;
const orderedTiedTargets = [
...tiedTargets.slice(startIndex),
...tiedTargets.slice(0, startIndex),
];
const tiedExecutionKeys = new Set(orderedTiedTargets.map((entry) => entry.target.executionKey));
return [
...orderedTiedTargets,
...scoredTargets.filter((entry) => !tiedExecutionKeys.has(entry.target.executionKey)),
].map((entry) => entry.target);
}