fix: honor provider hook aliases in catalog filters

This commit is contained in:
Shakker
2026-04-22 03:08:13 +01:00
committed by Shakker
parent f9bac5038c
commit d6c7b468ea
3 changed files with 74 additions and 4 deletions

View File

@@ -1,5 +1,8 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { loadProviderCatalogModelsForList } from "./list.provider-catalog.js";
import {
loadProviderCatalogModelsForList,
resolveProviderCatalogPluginIdsForFilter,
} from "./list.provider-catalog.js";
const baseParams = {
cfg: {
@@ -47,4 +50,24 @@ describe("loadProviderCatalogModelsForList", () => {
expect.arrayContaining(["moonshot/kimi-k2.6"]),
);
});
it("recognizes bundled provider hook aliases before the unknown-provider short-circuit", async () => {
await expect(
resolveProviderCatalogPluginIdsForFilter({
cfg: baseParams.cfg,
env: baseParams.env,
providerFilter: "azure-openai-responses",
}),
).resolves.toEqual(["openai"]);
});
it("keeps unknown provider filters eligible for early empty results", async () => {
await expect(
resolveProviderCatalogPluginIdsForFilter({
cfg: baseParams.cfg,
env: baseParams.env,
providerFilter: "unknown-provider-for-catalog-test",
}),
).resolves.toBeUndefined();
});
});

View File

@@ -16,6 +16,28 @@ const DISCOVERY_ORDERS = ["simple", "profile", "paired", "late"] as const;
const SELF_HOSTED_DISCOVERY_PROVIDER_IDS = new Set(["lmstudio", "ollama", "sglang", "vllm"]);
const log = createSubsystemLogger("models/list-provider-catalog");
export async function resolveProviderCatalogPluginIdsForFilter(params: {
cfg: OpenClawConfig;
env?: NodeJS.ProcessEnv;
providerFilter: string;
}): Promise<string[] | undefined> {
const providerFilter = normalizeProviderId(params.providerFilter);
if (!providerFilter) {
return undefined;
}
const manifestPluginIds = resolveOwningPluginIdsForProvider({
provider: providerFilter,
config: params.cfg,
env: params.env,
});
if (manifestPluginIds) {
return manifestPluginIds;
}
const { resolveProviderContractPluginIdsForProviderAlias } =
await import("../../plugins/contracts/registry.js");
return resolveProviderContractPluginIdsForProviderAlias(providerFilter);
}
function modelFromProviderCatalog(params: {
provider: string;
providerConfig: ModelProviderConfig;
@@ -47,10 +69,10 @@ export async function loadProviderCatalogModelsForList(params: {
const env = params.env ?? process.env;
const providerFilter = params.providerFilter ? normalizeProviderId(params.providerFilter) : "";
const onlyPluginIds = providerFilter
? resolveOwningPluginIdsForProvider({
provider: providerFilter,
config: params.cfg,
? await resolveProviderCatalogPluginIdsForFilter({
cfg: params.cfg,
env,
providerFilter,
})
: undefined;
if (providerFilter && !onlyPluginIds) {

View File

@@ -1,3 +1,4 @@
import { normalizeProviderId } from "../../agents/provider-id.js";
import { normalizeLowercaseStringOrEmpty } from "../../shared/string-coerce.js";
import { loadBundledCapabilityRuntimeRegistry } from "../bundled-capability-runtime.js";
import {
@@ -723,6 +724,30 @@ export function resolveProviderContractPluginIdsForProvider(
return pluginIds.length > 0 ? pluginIds : undefined;
}
export function resolveProviderContractPluginIdsForProviderAlias(
providerId: string,
): string[] | undefined {
const normalizedProvider = normalizeProviderId(providerId);
if (!normalizedProvider) {
return undefined;
}
const pluginIds = uniqueStrings(
loadProviderContractEntriesForPluginIds(resolveBundledProviderContractPluginIds())
.filter((entry) => {
const providerIds = [
entry.provider.id,
...(entry.provider.aliases ?? []),
...(entry.provider.hookAliases ?? []),
];
return providerIds.some(
(candidate) => normalizeProviderId(candidate) === normalizedProvider,
);
})
.map((entry) => entry.pluginId),
).toSorted((left, right) => left.localeCompare(right));
return pluginIds.length > 0 ? pluginIds : undefined;
}
export function resolveProviderContractProvidersForPluginIds(
pluginIds: readonly string[],
): ProviderPlugin[] {