diff --git a/src/sse/services/auth.ts b/src/sse/services/auth.ts index 76e5ee1aa9..bcd9ae88d7 100644 --- a/src/sse/services/auth.ts +++ b/src/sse/services/auth.ts @@ -16,7 +16,10 @@ import { lockModel, hasPerModelQuota, } from "@omniroute/open-sse/services/accountFallback.ts"; -import { isLocalProvider, getPassthroughProviders } from "@omniroute/open-sse/config/providerRegistry.ts"; +import { + isLocalProvider, + getPassthroughProviders, +} from "@omniroute/open-sse/config/providerRegistry.ts"; import { COOLDOWN_MS } from "@omniroute/open-sse/config/constants.ts"; import { getCodexModelScope } from "@omniroute/open-sse/executors/codex.ts"; import * as log from "../utils/logger"; @@ -308,6 +311,16 @@ const markMutexes = new Map>(); // Re-export for backwards compat with existing test imports. export { fisherYatesShuffle, getNextFromDeckSync as getNextFromDeck }; +/** + * Resolve provider aliases (e.g., nvidia -> nvidia_nim) for DB lookup + */ +function getProviderSearchPool(provider: string): string[] { + const pool = [provider]; + if (provider === "nvidia") pool.push("nvidia_nim"); + if (provider === "nvidia_nim") pool.push("nvidia"); + return [...new Set(pool)]; +} + /** * Get provider credentials from localDb * Filters out unavailable accounts and returns the selected account based on strategy @@ -334,7 +347,14 @@ export async function getProviderCredentials( const allowSuppressedConnections = options.allowSuppressedConnections === true; const bypassQuotaPolicy = options.bypassQuotaPolicy === true; - const connectionsRaw = await getProviderConnections({ provider, isActive: true }); + // Fix #922: Check for aliases (nvidia/nvidia_nim) to ensure credentials are found + const providersToSearch = getProviderSearchPool(provider); + let connectionsRaw: any[] = []; + for (const p of providersToSearch) { + const results = await getProviderConnections({ provider: p, isActive: true }); + if (Array.isArray(results)) connectionsRaw.push(...results); + } + let connections = (Array.isArray(connectionsRaw) ? connectionsRaw : []) .map(toProviderConnection) .filter((conn) => conn.id.length > 0); @@ -349,7 +369,12 @@ export async function getProviderCredentials( if (connections.length === 0) { // Check all connections (including inactive) to see if rate limited - const allConnectionsRaw = await getProviderConnections({ provider }); + // Fix #922: Also search aliases here + let allConnectionsRaw: any[] = []; + for (const p of providersToSearch) { + const results = await getProviderConnections({ provider: p }); + if (Array.isArray(results)) allConnectionsRaw.push(...results); + } const allConnections = (Array.isArray(allConnectionsRaw) ? allConnectionsRaw : []) .map(toProviderConnection) .filter((conn) => conn.id.length > 0); @@ -732,9 +757,7 @@ export async function markAccountUnavailable( // model must NOT lock out other models on the same API key. if (hasPerModelQuota(provider) && model && (status === 429 || status === 404)) { const reason = status === 404 ? "not_found" : "rate_limited"; - const cooldown = status === 404 - ? COOLDOWN_MS.notFoundLocal - : COOLDOWN_MS.rateLimit; + const cooldown = status === 404 ? COOLDOWN_MS.notFoundLocal : COOLDOWN_MS.rateLimit; lockModel(provider, connectionId, model, reason, cooldown); // Update last error for observability (without changing terminal status) updateProviderConnection(connectionId, { @@ -820,7 +843,12 @@ export async function markAccountUnavailable( const isPassthroughProvider = provider && getPassthroughProviders().has(provider); const isPerModelQuotaProvider = hasPerModelQuota(provider); - if ((isLocalProvider(connBaseUrl) || isPerModelQuotaProvider) && status === 404 && provider && model) { + if ( + (isLocalProvider(connBaseUrl) || isPerModelQuotaProvider) && + status === 404 && + provider && + model + ) { const localCooldown = COOLDOWN_MS.notFoundLocal; lockModel(provider, connectionId, model, "not_found", localCooldown); log.info(