mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:40:44 +00:00
fix: harden provider discovery metadata scope
This commit is contained in:
@@ -127,6 +127,7 @@ describe("models-config", () => {
|
||||
|
||||
it("threads startup provider discovery scope into implicit provider discovery", async () => {
|
||||
let observedProviderIds: readonly string[] | undefined;
|
||||
let observedEntriesOnly: boolean | undefined;
|
||||
let observedTimeoutMs: number | undefined;
|
||||
|
||||
await resolveProvidersForModelsJsonWithDeps(
|
||||
@@ -135,14 +136,17 @@ describe("models-config", () => {
|
||||
agentDir: "/tmp/openclaw-models-config-env-vars-test",
|
||||
env: {},
|
||||
providerDiscoveryProviderIds: ["openai"],
|
||||
providerDiscoveryEntriesOnly: true,
|
||||
providerDiscoveryTimeoutMs: 5000,
|
||||
},
|
||||
{
|
||||
resolveImplicitProviders: async ({
|
||||
providerDiscoveryProviderIds,
|
||||
providerDiscoveryEntriesOnly,
|
||||
providerDiscoveryTimeoutMs,
|
||||
}) => {
|
||||
observedProviderIds = providerDiscoveryProviderIds;
|
||||
observedEntriesOnly = providerDiscoveryEntriesOnly;
|
||||
observedTimeoutMs = providerDiscoveryTimeoutMs;
|
||||
return {};
|
||||
},
|
||||
@@ -150,6 +154,7 @@ describe("models-config", () => {
|
||||
);
|
||||
|
||||
expect(observedProviderIds).toEqual(["openai"]);
|
||||
expect(observedEntriesOnly).toBe(true);
|
||||
expect(observedTimeoutMs).toBe(5000);
|
||||
});
|
||||
|
||||
|
||||
@@ -194,7 +194,9 @@ export async function ensureOpenClawModelsJson(
|
||||
...(options.providerDiscoveryTimeoutMs !== undefined
|
||||
? { providerDiscoveryTimeoutMs: options.providerDiscoveryTimeoutMs }
|
||||
: {}),
|
||||
...(options.providerDiscoveryEntriesOnly === true ? { providerDiscoveryEntriesOnly: true } : {}),
|
||||
...(options.providerDiscoveryEntriesOnly === true
|
||||
? { providerDiscoveryEntriesOnly: true }
|
||||
: {}),
|
||||
});
|
||||
const cacheKey = modelsJsonReadyCacheKey(targetPath, fingerprint);
|
||||
const cached = MODELS_JSON_STATE.readyCache.get(cacheKey);
|
||||
|
||||
@@ -103,7 +103,6 @@ function expectBundledCompatLoadPath(params: {
|
||||
config: params.enablementCompat,
|
||||
onlyPluginIds: ["openai"],
|
||||
activate: false,
|
||||
onlyPluginIds: ["openai"],
|
||||
});
|
||||
}
|
||||
|
||||
@@ -123,13 +122,17 @@ function createCompatChainConfig() {
|
||||
return { cfg, allowlistCompat, enablementCompat };
|
||||
}
|
||||
|
||||
function setBundledCapabilityFixture(contractKey: string) {
|
||||
function setBundledCapabilityFixture(
|
||||
contractKey: string,
|
||||
pluginId = "openai",
|
||||
providerId = pluginId,
|
||||
) {
|
||||
mocks.loadPluginManifestRegistry.mockReturnValue({
|
||||
plugins: [
|
||||
{
|
||||
id: "openai",
|
||||
id: pluginId,
|
||||
origin: "bundled",
|
||||
contracts: { [contractKey]: ["openai"] },
|
||||
contracts: { [contractKey]: [providerId] },
|
||||
},
|
||||
{
|
||||
id: "custom-plugin",
|
||||
@@ -231,7 +234,7 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
expect(mocks.resolveRuntimePluginRegistry).toHaveBeenCalledWith();
|
||||
});
|
||||
|
||||
it("uses active non-speech capability providers even when cfg is passed", () => {
|
||||
it("uses active non-speech capability providers even when cfg has explicit plugin entries", () => {
|
||||
const active = createEmptyPluginRegistry();
|
||||
active.mediaUnderstandingProviders.push({
|
||||
pluginId: "deepgram",
|
||||
@@ -247,6 +250,7 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
const providers = resolvePluginCapabilityProviders({
|
||||
key: "mediaUnderstandingProviders",
|
||||
cfg: {
|
||||
plugins: { entries: { deepgram: { enabled: true } } },
|
||||
tools: {
|
||||
media: {
|
||||
models: [{ provider: "deepgram" }],
|
||||
@@ -404,7 +408,6 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
}),
|
||||
onlyPluginIds: ["microsoft"],
|
||||
activate: false,
|
||||
onlyPluginIds: ["microsoft"],
|
||||
});
|
||||
});
|
||||
|
||||
@@ -605,16 +608,7 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
nativeDocumentInputs: ["pdf"],
|
||||
},
|
||||
} as never);
|
||||
mocks.loadPluginManifestRegistry.mockReturnValue({
|
||||
plugins: [
|
||||
{
|
||||
id: "google",
|
||||
origin: "bundled",
|
||||
contracts: { mediaUnderstandingProviders: ["google"] },
|
||||
},
|
||||
] as never,
|
||||
diagnostics: [],
|
||||
});
|
||||
setBundledCapabilityFixture("mediaUnderstandingProviders", "google", "google");
|
||||
mocks.withBundledPluginEnablementCompat.mockReturnValue(compatConfig);
|
||||
mocks.withBundledPluginVitestCompat.mockReturnValue(compatConfig);
|
||||
mocks.resolveRuntimePluginRegistry.mockImplementation((params?: unknown) =>
|
||||
@@ -632,7 +626,29 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
config: compatConfig,
|
||||
onlyPluginIds: ["google"],
|
||||
activate: false,
|
||||
onlyPluginIds: ["openai"],
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
"imageGenerationProviders",
|
||||
"videoGenerationProviders",
|
||||
"musicGenerationProviders",
|
||||
] as const)("uses an explicit empty plugin scope for %s when no bundled owner exists", (key) => {
|
||||
const providers = resolvePluginCapabilityProviders({
|
||||
key,
|
||||
cfg: {} as OpenClawConfig,
|
||||
});
|
||||
|
||||
expectNoResolvedCapabilityProviders(providers as Array<{ id: string }>);
|
||||
expect(mocks.loadPluginManifestRegistry).toHaveBeenCalledWith({
|
||||
config: {},
|
||||
env: process.env,
|
||||
});
|
||||
expect(mocks.resolveRuntimePluginRegistry).toHaveBeenCalledWith();
|
||||
expect(mocks.resolveRuntimePluginRegistry).toHaveBeenCalledWith({
|
||||
config: expect.anything(),
|
||||
onlyPluginIds: [],
|
||||
activate: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -667,9 +683,8 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
|
||||
const snapshotLoadOptions = mocks.resolveRuntimePluginRegistry.mock.calls
|
||||
.map(([options]) => options)
|
||||
.filter(
|
||||
(options): options is { activate: boolean; onlyPluginIds?: string[] } =>
|
||||
Boolean(options && typeof options === "object" && "activate" in options),
|
||||
.filter((options): options is { activate: boolean; onlyPluginIds?: string[] } =>
|
||||
Boolean(options && typeof options === "object" && "activate" in options),
|
||||
);
|
||||
expect(snapshotLoadOptions.map((options) => options.onlyPluginIds)).toEqual([
|
||||
["minimax", "openai"],
|
||||
@@ -702,11 +717,10 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
|
||||
const snapshotLoadOptions = mocks.resolveRuntimePluginRegistry.mock.calls
|
||||
.map(([options]) => options)
|
||||
.filter(
|
||||
(options): options is { activate: boolean; onlyPluginIds?: string[] } =>
|
||||
Boolean(options && typeof options === "object" && "activate" in options),
|
||||
.filter((options): options is { activate: boolean; onlyPluginIds?: string[] } =>
|
||||
Boolean(options && typeof options === "object" && "activate" in options),
|
||||
);
|
||||
expect(snapshotLoadOptions.map((options) => options.onlyPluginIds)).toEqual([["openai"]]);
|
||||
expect(snapshotLoadOptions.map((options) => options.onlyPluginIds)).toEqual([["openai"], []]);
|
||||
});
|
||||
|
||||
it("loads only the bundled owner plugin for a targeted provider lookup", () => {
|
||||
@@ -772,7 +786,6 @@ describe("resolvePluginCapabilityProviders", () => {
|
||||
config: enablementCompat,
|
||||
onlyPluginIds: ["google"],
|
||||
activate: false,
|
||||
onlyPluginIds: ["google"],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -262,9 +262,6 @@ export function resolvePluginCapabilityProviders<K extends CapabilityProviderReg
|
||||
key: params.key,
|
||||
cfg: params.cfg,
|
||||
});
|
||||
if (pluginIds.length === 0 && MEDIA_GENERATION_CAPABILITY_KEYS.has(params.key)) {
|
||||
return activeProviders.map((entry) => entry.provider) as CapabilityProviderForKey<K>[];
|
||||
}
|
||||
const compatConfig = resolveCapabilityProviderConfig({
|
||||
key: params.key,
|
||||
cfg: params.cfg,
|
||||
|
||||
Reference in New Issue
Block a user