fix(qwen): preserve custom modelstudio providers

This commit is contained in:
Peter Steinberger
2026-04-27 12:24:05 +01:00
parent dca9fa471f
commit 5afa24a9fc
8 changed files with 205 additions and 8 deletions

View File

@@ -1,5 +1,6 @@
import { normalizeProviderId } from "../agents/provider-id.js";
import type { OpenClawConfig } from "../config/types.openclaw.js";
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
import { normalizePluginIdScope, serializePluginIdScope } from "./plugin-scope.js";
import { resolveProviderConfigApiOwnerHint } from "./provider-config-owner.js";
import { isPluginProvidersLoadInFlight, resolvePluginProviders } from "./providers.runtime.js";
@@ -28,6 +29,11 @@ function matchesProviderId(provider: ProviderPlugin, providerId: string): boolea
);
}
function matchesProviderLiteralId(provider: ProviderPlugin, providerId: string): boolean {
const normalized = normalizeLowercaseStringOrEmpty(providerId);
return !!normalized && normalizeLowercaseStringOrEmpty(provider.id) === normalized;
}
let cachedHookProvidersWithoutConfig = new WeakMap<
NodeJS.ProcessEnv,
Map<string, ProviderPlugin[]>
@@ -178,11 +184,14 @@ export function resolveProviderRuntimePlugin(params: {
bundledProviderAllowlistCompat: params.bundledProviderAllowlistCompat,
bundledProviderVitestCompat: params.bundledProviderVitestCompat,
installBundledRuntimeDeps: params.installBundledRuntimeDeps,
}).find(
(plugin) =>
matchesProviderId(plugin, params.provider) ||
(apiOwnerHint ? matchesProviderId(plugin, apiOwnerHint) : false),
);
}).find((plugin) => {
if (apiOwnerHint) {
return (
matchesProviderLiteralId(plugin, params.provider) || matchesProviderId(plugin, apiOwnerHint)
);
}
return matchesProviderId(plugin, params.provider);
});
}
export function resolveProviderHookPlugin(params: {

View File

@@ -1662,6 +1662,39 @@ describe("provider-runtime", () => {
);
});
it("does not match alias hooks when an exact custom provider declares a foreign api owner", () => {
const qwenPlugin: ProviderPlugin = {
id: "qwen",
label: "Qwen",
aliases: ["modelstudio"],
auth: [],
createStreamFn: vi.fn(() => vi.fn()),
};
resolvePluginProvidersMock.mockReturnValue([qwenPlugin]);
const plugin = resolveProviderRuntimePlugin({
provider: "modelstudio",
config: {
models: {
providers: {
modelstudio: {
api: "openai-completions",
baseUrl: "https://coding-intl.dashscope.aliyuncs.com/v1",
models: [],
},
},
},
} as never,
});
expect(plugin).toBeUndefined();
expect(resolvePluginProvidersMock).toHaveBeenCalledWith(
expect.objectContaining({
providerRefs: ["modelstudio", "openai-completions"],
}),
);
});
it("merges compat contributions from owner and foreign provider plugins", () => {
resolvePluginProvidersMock.mockImplementation((params) => {
const onlyPluginIds = params.onlyPluginIds ?? [];