feat: add round-robin routing strategy

Implements a round-robin (least recently used) account selection strategy
alongside the existing fill-first priority system. Adds a toggle in the
Profile dashboard to switch between strategies.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Catalin Stanciu
2026-01-09 17:25:28 +07:00
committed by decolua
co-authored by Claude Sonnet 4.5
parent 9c3d6f4ad8
commit 9ebd7d3062
3 changed files with 91 additions and 5 deletions
+23 -4
View File
@@ -1,16 +1,16 @@
import { getProviderConnections, validateApiKey, updateProviderConnection } from "@/lib/localDb";
import { getProviderConnections, validateApiKey, updateProviderConnection, getSettings } from "@/lib/localDb";
import { isAccountUnavailable, getUnavailableUntil } from "open-sse/services/accountFallback.js";
import * as log from "../utils/logger.js";
/**
* Get provider credentials from localDb
* Filters out unavailable accounts and returns the highest priority available account
* Filters out unavailable accounts and returns the selected account based on strategy
* @param {string} provider - Provider name
* @param {string|null} excludeConnectionId - Connection ID to exclude (for retry with next account)
*/
export async function getProviderCredentials(provider, excludeConnectionId = null) {
const connections = await getProviderConnections({ provider, isActive: true });
if (connections.length === 0) {
log.warn("AUTH", `No credentials for ${provider}`);
return null;
@@ -28,7 +28,26 @@ export async function getProviderCredentials(provider, excludeConnectionId = nul
return null;
}
const connection = availableConnections[0];
const settings = await getSettings();
const strategy = settings.fallbackStrategy || "fill-first";
let connection;
if (strategy === "round-robin") {
// Sort by lastUsed (nulls first) to pick the least recently used
const sorted = [...availableConnections].sort((a, b) => {
if (!a.lastUsedAt && !b.lastUsedAt) return (a.priority || 999) - (b.priority || 999);
if (!a.lastUsedAt) return -1;
if (!b.lastUsedAt) return 1;
return new Date(a.lastUsedAt) - new Date(b.lastUsedAt);
});
connection = sorted[0];
// Update lastUsedAt asynchronously
updateProviderConnection(connection.id, { lastUsedAt: new Date().toISOString() }).catch(() => {});
} else {
// Default: fill-first (already sorted by priority in getProviderConnections)
connection = availableConnections[0];
}
return {
apiKey: connection.apiKey,