fix(combo): preserve OpenCode Free oc/ prefix for connections (#10180)

This commit is contained in:
AStupidBear
2026-08-13 11:41:03 +08:00
committed by GitHub
parent 7b2cc8c0f2
commit c2a8505bd9
2 changed files with 42 additions and 5 deletions

View File

@@ -556,6 +556,17 @@ function buildModelOptions(
return modelMap;
}
function rewriteQualifiedModelPrefix(
modelMap: Map<string, ComboBuilderModelOption>,
providerId: string,
routingPrefix: string
): void {
if (routingPrefix === providerId) return;
for (const option of modelMap.values()) {
option.qualifiedModel = `${routingPrefix}/${option.id}`;
}
}
/**
* #6957: some providers' own catalogs assign the identical display `name` to
* several distinct model ids (e.g. Mistral's "codestral-latest" alias renders
@@ -684,6 +695,12 @@ export async function getComboBuilderOptions(): Promise<ComboBuilderOptionsPaylo
customModels
);
// #2901 follow-up: a configured OpenCode connection shadows the no-auth
// entry below, so it must receive the same `oc/` routing prefix. The raw
// `opencode/` prefix is reserved by model parsing for the api-key tier.
const routingPrefix = providerId === "opencode" ? providerVisual.alias : providerId;
rewriteQualifiedModelPrefix(modelMap, providerId, routingPrefix);
const normalizedConnections =
expandConnectionOptions(providerConnections).sort(compareConnections);
@@ -749,11 +766,7 @@ export async function getComboBuilderOptions(): Promise<ComboBuilderOptionsPaylo
// (manual ALIAS_TO_PROVIDER_ID override), while "oc/<model>" resolves to the
// no-auth "opencode" provider. Rewrite qualifiedModel to the alias prefix.
const routingPrefix = noAuthProvider.alias || providerId;
if (routingPrefix !== providerId) {
for (const opt of modelMap.values()) {
opt.qualifiedModel = `${routingPrefix}/${opt.id}`;
}
}
rewriteQualifiedModelPrefix(modelMap, providerId, routingPrefix);
const displayName = (providerEntryName(providerId) ||
getProviderDisplayName(providerId, null) ||

View File

@@ -22,6 +22,7 @@ const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-combo-pre
process.env.DATA_DIR = TEST_DATA_DIR;
const core = await import("../../src/lib/db/core.ts");
const providersDb = await import("../../src/lib/db/providers.ts");
const { getComboBuilderOptions } = await import("../../src/lib/combos/builderOptions.ts");
const { parseModel } = await import("../../open-sse/services/model.ts");
@@ -52,6 +53,29 @@ test("#2901 no-auth OpenCode combo models use the oc/ prefix (not opencode/)", a
}
});
test("#2901 configured OpenCode connections also use the oc/ prefix", async () => {
await providersDb.createProviderConnection({
provider: "opencode",
authType: "apikey",
name: "OpenCode Free test connection",
apiKey: "test-key",
});
const payload = await getComboBuilderOptions();
const opencode = payload.providers.find(
(provider) => provider.providerId === "opencode" && provider.connectionCount > 0
);
assert.ok(opencode, "configured OpenCode provider must appear in the combo builder");
const bigPickle = opencode.models.find((model) => model.id === "big-pickle");
assert.ok(bigPickle, "big-pickle must be listed under the configured opencode provider");
assert.equal(
bigPickle.qualifiedModel,
"oc/big-pickle",
"configured opencode combo entries must use the 'oc/' routing alias"
);
});
test("#2901 the oc/ prefix actually resolves back to the no-auth opencode provider", () => {
// Guards the premise: opencode/ misroutes to opencode-zen, oc/ is correct.
assert.equal(parseModel("oc/big-pickle").provider, "opencode");