mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
fix(combo): align custom provider ids across creation and auth lookup (#3058)
* fix(combo): align custom provider ids * fix(combo): tighten alias resolution --------- Co-authored-by: minisforum <no@mail.com>
This commit is contained in:
@@ -2512,7 +2512,19 @@ function ComboFormModal({ isOpen, combo, onClose, onSave, activeProviders, combo
|
||||
};
|
||||
|
||||
const handleAddModel = (model) => {
|
||||
const nextEntry = { model: model.value, weight: 0 };
|
||||
const qualifiedModel = typeof model?.value === "string" ? model.value : "";
|
||||
const parsedModel = parseQualifiedModel(qualifiedModel);
|
||||
const resolvedProviderId =
|
||||
resolveComboBuilderProviderId(model?.providerId, builderProviders) ||
|
||||
resolveComboBuilderProviderId(parsedModel?.providerId, builderProviders) ||
|
||||
(typeof model?.providerId === "string" && model.providerId.trim()) ||
|
||||
parsedModel?.providerId ||
|
||||
null;
|
||||
const nextEntry = {
|
||||
model: qualifiedModel,
|
||||
...(resolvedProviderId ? { providerId: resolvedProviderId } : {}),
|
||||
weight: 0,
|
||||
};
|
||||
if (hasExactModelStepDuplicate(models, nextEntry)) {
|
||||
setBuilderError(
|
||||
getI18nOrFallback(
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { randomUUID, createHash } from "crypto";
|
||||
import {
|
||||
getProviderConnections,
|
||||
getProviderNodes,
|
||||
validateApiKey,
|
||||
updateProviderConnection,
|
||||
getSettings,
|
||||
@@ -44,6 +45,7 @@ import {
|
||||
import { looksLikeQuotaExhausted } from "@/shared/utils/classify429";
|
||||
import { getCodexModelScope } from "@omniroute/open-sse/executors/codex.ts";
|
||||
import {
|
||||
getProviderById,
|
||||
getProviderAlias,
|
||||
resolveProviderId,
|
||||
NOAUTH_PROVIDERS,
|
||||
@@ -791,7 +793,7 @@ export { fisherYatesShuffle, getNextFromDeckSync as getNextFromDeck };
|
||||
/**
|
||||
* Resolve provider aliases (e.g., nvidia -> nvidia_nim) for DB lookup
|
||||
*/
|
||||
function getProviderSearchPool(provider: string): string[] {
|
||||
async function getProviderSearchPool(provider: string): Promise<string[]> {
|
||||
const canonicalProvider = resolveProviderId(provider);
|
||||
const canonicalAlias = getProviderAlias(canonicalProvider);
|
||||
|
||||
@@ -802,7 +804,34 @@ function getProviderSearchPool(provider: string): string[] {
|
||||
return ["nvidia_nim", "nvidia"];
|
||||
}
|
||||
|
||||
return Array.from(new Set([provider, canonicalProvider, canonicalAlias].filter(Boolean)));
|
||||
const searchPool = new Set([provider, canonicalProvider, canonicalAlias].filter(Boolean));
|
||||
|
||||
// Built-in providers already resolve through static ids/aliases. Only
|
||||
// compatible/custom providers need provider_nodes expansion back to the
|
||||
// generated internal connection ids.
|
||||
if (getProviderById(canonicalProvider)) {
|
||||
return Array.from(searchPool);
|
||||
}
|
||||
|
||||
// Custom provider nodes are referenced by user-facing prefixes in combos
|
||||
// (for example "78code/gpt-5.4"), but live credentials are stored under
|
||||
// internal provider ids like openai-compatible-responses-<uuid>.
|
||||
try {
|
||||
const providerNodes = await getProviderNodes();
|
||||
for (const node of Array.isArray(providerNodes) ? providerNodes : []) {
|
||||
const nodeRecord = asRecord(node);
|
||||
const nodePrefix = typeof nodeRecord.prefix === "string" ? nodeRecord.prefix.trim() : "";
|
||||
const nodeId = typeof nodeRecord.id === "string" ? nodeRecord.id.trim() : "";
|
||||
if (!nodePrefix || !nodeId) continue;
|
||||
if (nodePrefix === provider || nodePrefix === canonicalProvider || nodePrefix === canonicalAlias) {
|
||||
searchPool.add(nodeId);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Best-effort alias expansion only.
|
||||
}
|
||||
|
||||
return Array.from(searchPool);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -864,7 +893,7 @@ export async function getProviderCredentials(
|
||||
);
|
||||
|
||||
// Fix #922: Check for aliases (nvidia/nvidia_nim) to ensure credentials are found
|
||||
const providersToSearch = getProviderSearchPool(provider);
|
||||
const providersToSearch = await getProviderSearchPool(provider);
|
||||
const connectionResults = await Promise.all(
|
||||
providersToSearch.map((p) => getProviderConnections({ provider: p, isActive: true }))
|
||||
);
|
||||
|
||||
@@ -149,3 +149,28 @@ test("#2778 matching logic: node with prefix=flymux and id=UUID-id still matches
|
||||
assert.ok(matchByPrefixOrId !== undefined, "Alias-based match must still work after the fix");
|
||||
assert.strictEqual(matchByPrefixOrId?.id, FAKE_UUID_NODE_ID);
|
||||
});
|
||||
|
||||
test("custom provider auth lookup search pool maps alias prefixes to internal provider ids", async () => {
|
||||
const authSrc = fs.readFileSync(path.resolve(__dirname, "../../src/sse/services/auth.ts"), "utf8");
|
||||
|
||||
assert.match(
|
||||
authSrc,
|
||||
/async function getProviderSearchPool\(provider: string\): Promise<string\[]>/,
|
||||
"getProviderSearchPool should be async so it can expand custom provider aliases via provider_nodes"
|
||||
);
|
||||
assert.match(
|
||||
authSrc,
|
||||
/getProviderNodes\(/,
|
||||
"auth lookup should read provider_nodes to map custom prefixes like 78code/micu back to internal provider ids"
|
||||
);
|
||||
assert.match(
|
||||
authSrc,
|
||||
/nodePrefix === provider \|\| nodePrefix === canonicalProvider \|\| nodePrefix === canonicalAlias/,
|
||||
"auth lookup should match provider node prefixes against the requested alias/canonical provider values"
|
||||
);
|
||||
assert.match(
|
||||
authSrc,
|
||||
/searchPool\.add\(nodeId\)/,
|
||||
"auth lookup should add the matched custom provider node id into the credential search pool"
|
||||
);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user