feat: - Introduced per-provider strategy overrides in settings, allowing for more flexible connection management.

- Added new provider models: DeepSeek 3.1, DeepSeek 3.2, and Qwen3 Coder Next.
- Implemented UI changes to support round-robin strategy with sticky limits in the provider detail page.
- Improved logging to display connection names instead of IDs for better clarity.
This commit is contained in:
decolua
2026-03-11 18:04:38 +07:00
parent 2470ef84de
commit fe49b61dfb
7 changed files with 114 additions and 34 deletions
+2 -3
View File
@@ -156,8 +156,7 @@ async function handleSingleModelChat(body, modelStr, clientRawRequest = null, re
}
// Log account selection
const accountId = credentials.connectionId.slice(0, 8);
log.info("AUTH", `Using ${provider} account: ${accountId}...`);
log.info("AUTH", `\x1b[32mUsing ${provider} account: ${credentials.connectionName}\x1b[0m`);
const refreshedCredentials = await checkAndRefreshToken(provider, credentials);
@@ -202,7 +201,7 @@ async function handleSingleModelChat(body, modelStr, clientRawRequest = null, re
const { shouldFallback } = await markAccountUnavailable(credentials.connectionId, result.status, result.error, provider, model);
if (shouldFallback) {
log.warn("AUTH", `Account ${accountId}... unavailable (${result.status}), trying fallback`);
log.warn("AUTH", `Account ${credentials.connectionName} unavailable (${result.status}), trying fallback`);
excludeConnectionId = credentials.connectionId;
lastError = result.error;
lastStatus = result.status;
+2 -3
View File
@@ -103,8 +103,7 @@ export async function handleEmbeddings(request) {
return errorResponse(lastStatus || HTTP_STATUS.SERVICE_UNAVAILABLE, lastError || "All accounts unavailable");
}
const accountId = credentials.connectionId.slice(0, 8);
log.info("AUTH", `Using ${provider} account: ${accountId}...`);
log.info("AUTH", `\x1b[32mUsing ${provider} account: ${credentials.connectionName}\x1b[0m`);
const refreshedCredentials = await checkAndRefreshToken(provider, credentials);
@@ -131,7 +130,7 @@ export async function handleEmbeddings(request) {
const { shouldFallback } = await markAccountUnavailable(credentials.connectionId, result.status, result.error, provider, model);
if (shouldFallback) {
log.warn("AUTH", `Account ${accountId}... unavailable (${result.status}), trying fallback`);
log.warn("AUTH", `Account ${credentials.connectionName} unavailable (${result.status}), trying fallback`);
excludeConnectionId = credentials.connectionId;
lastError = result.error;
lastStatus = result.status;
+8 -4
View File
@@ -72,11 +72,13 @@ export async function getProviderCredentials(provider, excludeConnectionId = nul
}
const settings = await getSettings();
const strategy = settings.fallbackStrategy || "fill-first";
// Per-provider strategy overrides global setting
const providerOverride = (settings.providerStrategies || {})[providerId] || {};
const strategy = providerOverride.fallbackStrategy || settings.fallbackStrategy || "fill-first";
let connection;
if (strategy === "round-robin") {
const stickyLimit = settings.stickyRoundRobinLimit || 3;
const stickyLimit = providerOverride.stickyRoundRobinLimit || settings.stickyRoundRobinLimit || 3;
// Sort by lastUsed (most recent first) to find current candidate
const byRecency = [...availableConnections].sort((a, b) => {
@@ -178,7 +180,8 @@ export async function markAccountUnavailable(connectionId, status, errorText, pr
});
const lockKey = Object.keys(lockUpdate)[0];
log.warn("AUTH", `${connectionId.slice(0, 8)} locked ${lockKey} for ${Math.round(cooldownMs / 1000)}s [${status}]`);
const connName = conn?.displayName || conn?.name || conn?.email || connectionId.slice(0, 8);
log.warn("AUTH", `${connName} locked ${lockKey} for ${Math.round(cooldownMs / 1000)}s [${status}]`);
if (provider && status && reason) {
console.error(`${provider} [${status}]: ${reason}`);
@@ -228,7 +231,8 @@ export async function clearAccountError(connectionId, currentConnection, model =
}
await updateProviderConnection(connectionId, clearObj);
log.info("AUTH", `Account ${connectionId.slice(0, 8)} cleared lock for model=${model || "__all"}`);
const connName = conn?.displayName || conn?.name || conn?.email || connectionId.slice(0, 8);
log.info("AUTH", `Account ${connName} cleared lock for model=${model || "__all"}`);
}
/**