mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 15:30:47 +00:00
fix(web_search): skip redundant provider re-resolution for external Brave plugin
Guards the secondary resolveProviders call with `!allProviders.some(p => p.id === rawProvider)` so it only fires when the first pass genuinely missed the configured provider. Eliminates the spurious `WEB_SEARCH_PROVIDER_INVALID_AUTODETECT` warning and incorrect `providerSource: "none"` for external Brave plugin installs. Fixes #77676.
This commit is contained in:
@@ -433,6 +433,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Exec approvals: enforce allowlist `argPattern` argument restrictions on Linux and macOS as well as Windows, so an entry like `{ pattern: "python3", argPattern: "^safe\.py$" }` no longer silently relaxes to a path-only match on non-Windows hosts. (#75143) Thanks @eleqtrizit.
|
||||
- Agents/compaction: disable Pi auto-compaction whenever OpenClaw effectively owns safeguard compaction, including provider-backed safeguard mode, so Pi and OpenClaw no longer fight over long-session compaction. Fixes #73003. (#73839) Thanks @bradhallett.
|
||||
- Telegram/streaming: finalize text replies by stopping the edited stream message instead of sending a second answer bubble, so Telegram turns cannot duplicate the streamed final response. (#77947) Thanks @obviyus.
|
||||
- web_search/Brave: fix provider selection when Brave is installed as an external plugin and `tools.web.search.provider: "brave"` is explicitly configured — a redundant provider re-resolution at startup could race and return an empty list, causing a spurious `WEB_SEARCH_PROVIDER_INVALID_AUTODETECT` warning and treating the explicitly configured provider as absent. Fixes #77676. Thanks @openperf.
|
||||
|
||||
## 2026.5.3-1
|
||||
|
||||
|
||||
@@ -234,7 +234,11 @@ export async function resolveRuntimeWebProviderSurface<
|
||||
) {
|
||||
configuredBundledPluginId = undefined;
|
||||
}
|
||||
if (params.rawProvider && !configuredBundledPluginId) {
|
||||
if (
|
||||
params.rawProvider &&
|
||||
!configuredBundledPluginId &&
|
||||
!allProviders.some((provider) => provider.id === params.rawProvider)
|
||||
) {
|
||||
const resolveManifestContractOwnerPluginId = await loadResolveManifestContractOwnerPluginId();
|
||||
configuredBundledPluginId = resolveManifestContractOwnerPluginId({
|
||||
contract: params.contract,
|
||||
|
||||
@@ -1564,4 +1564,94 @@ describe("runtime web tools resolution", () => {
|
||||
expect(resolveBundledWebFetchProvidersFromPublicArtifactsMock).not.toHaveBeenCalled();
|
||||
expect(resolvePluginWebFetchProvidersMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
describe("when brave is installed as an external plugin and explicitly configured", () => {
|
||||
const externalBraveImpl = ({
|
||||
value,
|
||||
origin,
|
||||
}: {
|
||||
value: string;
|
||||
origin?: string;
|
||||
}): string | undefined => {
|
||||
if (origin === "bundled" && value === "brave") {
|
||||
return undefined;
|
||||
}
|
||||
return (
|
||||
{
|
||||
brave: "brave",
|
||||
firecrawl: "firecrawl",
|
||||
gemini: "google",
|
||||
grok: "xai",
|
||||
kimi: "moonshot",
|
||||
perplexity: "perplexity",
|
||||
} as Record<string, string | undefined>
|
||||
)[value];
|
||||
};
|
||||
|
||||
const defaultImpl = ({ value }: { value: string }): string | undefined =>
|
||||
(
|
||||
({
|
||||
brave: "brave",
|
||||
firecrawl: "firecrawl",
|
||||
gemini: "google",
|
||||
grok: "xai",
|
||||
kimi: "moonshot",
|
||||
perplexity: "perplexity",
|
||||
}) as Record<string, string | undefined>
|
||||
)[value];
|
||||
|
||||
beforeEach(() => {
|
||||
loadInstalledPluginIndexInstallRecordsSyncMock.mockReturnValue({
|
||||
brave: { source: "npm", spec: "@openclaw/brave-search" },
|
||||
});
|
||||
resolveManifestContractOwnerPluginIdMock.mockImplementation(externalBraveImpl);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
resolveManifestContractOwnerPluginIdMock.mockImplementation(defaultImpl);
|
||||
});
|
||||
|
||||
it("selects the configured provider without re-invoking provider discovery when found in the first pass", async () => {
|
||||
resolvePluginWebSearchProvidersMock
|
||||
.mockReturnValueOnce(buildTestWebSearchProviders())
|
||||
.mockReturnValueOnce([]);
|
||||
|
||||
const { metadata, context } = await runRuntimeWebTools({
|
||||
config: asConfig({
|
||||
tools: {
|
||||
web: {
|
||||
search: {
|
||||
provider: "brave",
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
entries: {
|
||||
brave: {
|
||||
config: {
|
||||
webSearch: {
|
||||
apiKey: "brave-api-key", // pragma: allowlist secret
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
expect(metadata.search.selectedProvider).toBe("brave");
|
||||
expect(metadata.search.providerSource).toBe("configured");
|
||||
expect(metadata.search.selectedProviderKeySource).toBe("config");
|
||||
expect(context.warnings).not.toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ code: "WEB_SEARCH_PROVIDER_INVALID_AUTODETECT" }),
|
||||
]),
|
||||
);
|
||||
expect(resolvePluginWebSearchProvidersMock).toHaveBeenCalledTimes(1);
|
||||
expect(
|
||||
resolveBundledExplicitWebSearchProvidersFromPublicArtifactsMock,
|
||||
).not.toHaveBeenCalled();
|
||||
expect(resolveBundledWebSearchProvidersFromPublicArtifactsMock).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user