mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-21 06:51:01 +00:00
refactor(web-search): share scoped provider config plumbing
This commit is contained in:
@@ -4,6 +4,7 @@ import {
|
||||
DEFAULT_SEARCH_COUNT,
|
||||
MAX_SEARCH_COUNT,
|
||||
formatCliCommand,
|
||||
mergeScopedSearchConfig,
|
||||
normalizeFreshness,
|
||||
normalizeToIsoDate,
|
||||
readCachedSearchPayload,
|
||||
@@ -607,21 +608,12 @@ export function createBraveWebSearchProvider(): WebSearchProviderPlugin {
|
||||
},
|
||||
createTool: (ctx) =>
|
||||
createBraveToolDefinition(
|
||||
(() => {
|
||||
const searchConfig = ctx.searchConfig as SearchConfigRecord | undefined;
|
||||
const pluginConfig = resolveProviderWebSearchPluginConfig(ctx.config, "brave");
|
||||
if (!pluginConfig) {
|
||||
return searchConfig;
|
||||
}
|
||||
return {
|
||||
...(searchConfig ?? {}),
|
||||
...(pluginConfig.apiKey === undefined ? {} : { apiKey: pluginConfig.apiKey }),
|
||||
brave: {
|
||||
...resolveBraveConfig(searchConfig),
|
||||
...pluginConfig,
|
||||
},
|
||||
} as SearchConfigRecord;
|
||||
})(),
|
||||
mergeScopedSearchConfig(
|
||||
ctx.searchConfig as SearchConfigRecord | undefined,
|
||||
"brave",
|
||||
resolveProviderWebSearchPluginConfig(ctx.config, "brave"),
|
||||
{ mirrorApiKeyToTopLevel: true },
|
||||
) as SearchConfigRecord | undefined,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import {
|
||||
enablePluginInConfig,
|
||||
getScopedCredentialValue,
|
||||
resolveProviderWebSearchPluginConfig,
|
||||
setScopedCredentialValue,
|
||||
setProviderWebSearchPluginConfigValue,
|
||||
type WebSearchProviderPlugin,
|
||||
} from "openclaw/plugin-sdk/provider-web-search";
|
||||
@@ -21,26 +23,6 @@ const GenericFirecrawlSearchSchema = Type.Object(
|
||||
{ additionalProperties: false },
|
||||
);
|
||||
|
||||
function getScopedCredentialValue(searchConfig?: Record<string, unknown>): unknown {
|
||||
const scoped = searchConfig?.firecrawl;
|
||||
if (!scoped || typeof scoped !== "object" || Array.isArray(scoped)) {
|
||||
return undefined;
|
||||
}
|
||||
return (scoped as Record<string, unknown>).apiKey;
|
||||
}
|
||||
|
||||
function setScopedCredentialValue(
|
||||
searchConfigTarget: Record<string, unknown>,
|
||||
value: unknown,
|
||||
): void {
|
||||
const scoped = searchConfigTarget.firecrawl;
|
||||
if (!scoped || typeof scoped !== "object" || Array.isArray(scoped)) {
|
||||
searchConfigTarget.firecrawl = { apiKey: value };
|
||||
return;
|
||||
}
|
||||
(scoped as Record<string, unknown>).apiKey = value;
|
||||
}
|
||||
|
||||
export function createFirecrawlWebSearchProvider(): WebSearchProviderPlugin {
|
||||
return {
|
||||
id: "firecrawl",
|
||||
@@ -53,8 +35,9 @@ export function createFirecrawlWebSearchProvider(): WebSearchProviderPlugin {
|
||||
autoDetectOrder: 60,
|
||||
credentialPath: "plugins.entries.firecrawl.config.webSearch.apiKey",
|
||||
inactiveSecretPaths: ["plugins.entries.firecrawl.config.webSearch.apiKey"],
|
||||
getCredentialValue: getScopedCredentialValue,
|
||||
setCredentialValue: setScopedCredentialValue,
|
||||
getCredentialValue: (searchConfig) => getScopedCredentialValue(searchConfig, "firecrawl"),
|
||||
setCredentialValue: (searchConfigTarget, value) =>
|
||||
setScopedCredentialValue(searchConfigTarget, "firecrawl", value),
|
||||
getConfiguredCredentialValue: (config) =>
|
||||
resolveProviderWebSearchPluginConfig(config, "firecrawl")?.apiKey,
|
||||
setConfiguredCredentialValue: (configTarget, value) => {
|
||||
|
||||
@@ -3,7 +3,9 @@ import {
|
||||
buildSearchCacheKey,
|
||||
buildUnsupportedSearchFilterResponse,
|
||||
DEFAULT_SEARCH_COUNT,
|
||||
getScopedCredentialValue,
|
||||
MAX_SEARCH_COUNT,
|
||||
mergeScopedSearchConfig,
|
||||
readCachedSearchPayload,
|
||||
readConfiguredSecretString,
|
||||
readNumberParam,
|
||||
@@ -14,6 +16,7 @@ import {
|
||||
resolveSearchCacheTtlMs,
|
||||
resolveSearchCount,
|
||||
resolveSearchTimeoutSeconds,
|
||||
setScopedCredentialValue,
|
||||
setProviderWebSearchPluginConfigValue,
|
||||
type SearchConfigRecord,
|
||||
type WebSearchProviderPlugin,
|
||||
@@ -250,20 +253,9 @@ export function createGeminiWebSearchProvider(): WebSearchProviderPlugin {
|
||||
autoDetectOrder: 20,
|
||||
credentialPath: "plugins.entries.google.config.webSearch.apiKey",
|
||||
inactiveSecretPaths: ["plugins.entries.google.config.webSearch.apiKey"],
|
||||
getCredentialValue: (searchConfig) => {
|
||||
const gemini = searchConfig?.gemini;
|
||||
return gemini && typeof gemini === "object" && !Array.isArray(gemini)
|
||||
? (gemini as Record<string, unknown>).apiKey
|
||||
: undefined;
|
||||
},
|
||||
setCredentialValue: (searchConfigTarget, value) => {
|
||||
const scoped = searchConfigTarget.gemini;
|
||||
if (!scoped || typeof scoped !== "object" || Array.isArray(scoped)) {
|
||||
searchConfigTarget.gemini = { apiKey: value };
|
||||
return;
|
||||
}
|
||||
(scoped as Record<string, unknown>).apiKey = value;
|
||||
},
|
||||
getCredentialValue: (searchConfig) => getScopedCredentialValue(searchConfig, "gemini"),
|
||||
setCredentialValue: (searchConfigTarget, value) =>
|
||||
setScopedCredentialValue(searchConfigTarget, "gemini", value),
|
||||
getConfiguredCredentialValue: (config) =>
|
||||
resolveProviderWebSearchPluginConfig(config, "google")?.apiKey,
|
||||
setConfiguredCredentialValue: (configTarget, value) => {
|
||||
@@ -271,20 +263,11 @@ export function createGeminiWebSearchProvider(): WebSearchProviderPlugin {
|
||||
},
|
||||
createTool: (ctx) =>
|
||||
createGeminiToolDefinition(
|
||||
(() => {
|
||||
const searchConfig = ctx.searchConfig as SearchConfigRecord | undefined;
|
||||
const pluginConfig = resolveProviderWebSearchPluginConfig(ctx.config, "google");
|
||||
if (!pluginConfig) {
|
||||
return searchConfig;
|
||||
}
|
||||
return {
|
||||
...(searchConfig ?? {}),
|
||||
gemini: {
|
||||
...resolveGeminiConfig(searchConfig),
|
||||
...pluginConfig,
|
||||
},
|
||||
} as SearchConfigRecord;
|
||||
})(),
|
||||
mergeScopedSearchConfig(
|
||||
ctx.searchConfig as SearchConfigRecord | undefined,
|
||||
"gemini",
|
||||
resolveProviderWebSearchPluginConfig(ctx.config, "google"),
|
||||
) as SearchConfigRecord | undefined,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,7 +3,9 @@ import {
|
||||
buildSearchCacheKey,
|
||||
buildUnsupportedSearchFilterResponse,
|
||||
DEFAULT_SEARCH_COUNT,
|
||||
getScopedCredentialValue,
|
||||
MAX_SEARCH_COUNT,
|
||||
mergeScopedSearchConfig,
|
||||
readCachedSearchPayload,
|
||||
readConfiguredSecretString,
|
||||
readNumberParam,
|
||||
@@ -13,6 +15,7 @@ import {
|
||||
resolveSearchCacheTtlMs,
|
||||
resolveSearchCount,
|
||||
resolveSearchTimeoutSeconds,
|
||||
setScopedCredentialValue,
|
||||
setProviderWebSearchPluginConfigValue,
|
||||
type SearchConfigRecord,
|
||||
type WebSearchProviderPlugin,
|
||||
@@ -322,20 +325,9 @@ export function createKimiWebSearchProvider(): WebSearchProviderPlugin {
|
||||
autoDetectOrder: 40,
|
||||
credentialPath: "plugins.entries.moonshot.config.webSearch.apiKey",
|
||||
inactiveSecretPaths: ["plugins.entries.moonshot.config.webSearch.apiKey"],
|
||||
getCredentialValue: (searchConfig) => {
|
||||
const kimi = searchConfig?.kimi;
|
||||
return kimi && typeof kimi === "object" && !Array.isArray(kimi)
|
||||
? (kimi as Record<string, unknown>).apiKey
|
||||
: undefined;
|
||||
},
|
||||
setCredentialValue: (searchConfigTarget, value) => {
|
||||
const scoped = searchConfigTarget.kimi;
|
||||
if (!scoped || typeof scoped !== "object" || Array.isArray(scoped)) {
|
||||
searchConfigTarget.kimi = { apiKey: value };
|
||||
return;
|
||||
}
|
||||
(scoped as Record<string, unknown>).apiKey = value;
|
||||
},
|
||||
getCredentialValue: (searchConfig) => getScopedCredentialValue(searchConfig, "kimi"),
|
||||
setCredentialValue: (searchConfigTarget, value) =>
|
||||
setScopedCredentialValue(searchConfigTarget, "kimi", value),
|
||||
getConfiguredCredentialValue: (config) =>
|
||||
resolveProviderWebSearchPluginConfig(config, "moonshot")?.apiKey,
|
||||
setConfiguredCredentialValue: (configTarget, value) => {
|
||||
@@ -343,20 +335,11 @@ export function createKimiWebSearchProvider(): WebSearchProviderPlugin {
|
||||
},
|
||||
createTool: (ctx) =>
|
||||
createKimiToolDefinition(
|
||||
(() => {
|
||||
const searchConfig = ctx.searchConfig as SearchConfigRecord | undefined;
|
||||
const pluginConfig = resolveProviderWebSearchPluginConfig(ctx.config, "moonshot");
|
||||
if (!pluginConfig) {
|
||||
return searchConfig;
|
||||
}
|
||||
return {
|
||||
...(searchConfig ?? {}),
|
||||
kimi: {
|
||||
...resolveKimiConfig(searchConfig),
|
||||
...pluginConfig,
|
||||
},
|
||||
} as SearchConfigRecord;
|
||||
})(),
|
||||
mergeScopedSearchConfig(
|
||||
ctx.searchConfig as SearchConfigRecord | undefined,
|
||||
"kimi",
|
||||
resolveProviderWebSearchPluginConfig(ctx.config, "moonshot"),
|
||||
) as SearchConfigRecord | undefined,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -7,8 +7,10 @@ import {
|
||||
import {
|
||||
buildSearchCacheKey,
|
||||
DEFAULT_SEARCH_COUNT,
|
||||
getScopedCredentialValue,
|
||||
MAX_SEARCH_COUNT,
|
||||
isoToPerplexityDate,
|
||||
mergeScopedSearchConfig,
|
||||
normalizeFreshness,
|
||||
normalizeToIsoDate,
|
||||
readCachedSearchPayload,
|
||||
@@ -19,6 +21,7 @@ import {
|
||||
resolveSearchCount,
|
||||
resolveSearchTimeoutSeconds,
|
||||
resolveSiteName,
|
||||
setScopedCredentialValue,
|
||||
setProviderWebSearchPluginConfigValue,
|
||||
throwWebSearchApiError,
|
||||
type SearchConfigRecord,
|
||||
@@ -658,20 +661,9 @@ export function createPerplexityWebSearchProvider(): WebSearchProviderPlugin {
|
||||
autoDetectOrder: 50,
|
||||
credentialPath: "plugins.entries.perplexity.config.webSearch.apiKey",
|
||||
inactiveSecretPaths: ["plugins.entries.perplexity.config.webSearch.apiKey"],
|
||||
getCredentialValue: (searchConfig) => {
|
||||
const perplexity = searchConfig?.perplexity;
|
||||
return perplexity && typeof perplexity === "object" && !Array.isArray(perplexity)
|
||||
? (perplexity as Record<string, unknown>).apiKey
|
||||
: undefined;
|
||||
},
|
||||
setCredentialValue: (searchConfigTarget, value) => {
|
||||
const scoped = searchConfigTarget.perplexity;
|
||||
if (!scoped || typeof scoped !== "object" || Array.isArray(scoped)) {
|
||||
searchConfigTarget.perplexity = { apiKey: value };
|
||||
return;
|
||||
}
|
||||
(scoped as Record<string, unknown>).apiKey = value;
|
||||
},
|
||||
getCredentialValue: (searchConfig) => getScopedCredentialValue(searchConfig, "perplexity"),
|
||||
setCredentialValue: (searchConfigTarget, value) =>
|
||||
setScopedCredentialValue(searchConfigTarget, "perplexity", value),
|
||||
getConfiguredCredentialValue: (config) =>
|
||||
resolveProviderWebSearchPluginConfig(config, "perplexity")?.apiKey,
|
||||
setConfiguredCredentialValue: (configTarget, value) => {
|
||||
@@ -679,17 +671,11 @@ export function createPerplexityWebSearchProvider(): WebSearchProviderPlugin {
|
||||
},
|
||||
resolveRuntimeMetadata: (ctx) => ({
|
||||
perplexityTransport: resolveRuntimeTransport({
|
||||
searchConfig: {
|
||||
...(ctx.searchConfig as SearchConfigRecord | undefined),
|
||||
perplexity: {
|
||||
...((ctx.searchConfig as SearchConfigRecord | undefined)?.perplexity as
|
||||
| Record<string, unknown>
|
||||
| undefined),
|
||||
...(resolveProviderWebSearchPluginConfig(ctx.config, "perplexity") as
|
||||
| Record<string, unknown>
|
||||
| undefined),
|
||||
},
|
||||
},
|
||||
searchConfig: mergeScopedSearchConfig(
|
||||
ctx.searchConfig as SearchConfigRecord | undefined,
|
||||
"perplexity",
|
||||
resolveProviderWebSearchPluginConfig(ctx.config, "perplexity"),
|
||||
) as SearchConfigRecord | undefined,
|
||||
resolvedKey: ctx.resolvedCredential?.value,
|
||||
keySource: ctx.resolvedCredential?.source ?? "missing",
|
||||
fallbackEnvVar: ctx.resolvedCredential?.fallbackEnvVar,
|
||||
@@ -697,20 +683,11 @@ export function createPerplexityWebSearchProvider(): WebSearchProviderPlugin {
|
||||
}),
|
||||
createTool: (ctx) =>
|
||||
createPerplexityToolDefinition(
|
||||
(() => {
|
||||
const searchConfig = ctx.searchConfig as SearchConfigRecord | undefined;
|
||||
const pluginConfig = resolveProviderWebSearchPluginConfig(ctx.config, "perplexity");
|
||||
if (!pluginConfig) {
|
||||
return searchConfig;
|
||||
}
|
||||
return {
|
||||
...(searchConfig ?? {}),
|
||||
perplexity: {
|
||||
...resolvePerplexityConfig(searchConfig),
|
||||
...pluginConfig,
|
||||
},
|
||||
} as SearchConfigRecord;
|
||||
})(),
|
||||
mergeScopedSearchConfig(
|
||||
ctx.searchConfig as SearchConfigRecord | undefined,
|
||||
"perplexity",
|
||||
resolveProviderWebSearchPluginConfig(ctx.config, "perplexity"),
|
||||
) as SearchConfigRecord | undefined,
|
||||
ctx.runtimeMetadata?.perplexityTransport as PerplexityTransport | undefined,
|
||||
),
|
||||
};
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import {
|
||||
enablePluginInConfig,
|
||||
getScopedCredentialValue,
|
||||
resolveProviderWebSearchPluginConfig,
|
||||
setScopedCredentialValue,
|
||||
setProviderWebSearchPluginConfigValue,
|
||||
type WebSearchProviderPlugin,
|
||||
} from "openclaw/plugin-sdk/provider-web-search";
|
||||
@@ -21,26 +23,6 @@ const GenericTavilySearchSchema = Type.Object(
|
||||
{ additionalProperties: false },
|
||||
);
|
||||
|
||||
function getScopedCredentialValue(searchConfig?: Record<string, unknown>): unknown {
|
||||
const scoped = searchConfig?.tavily;
|
||||
if (!scoped || typeof scoped !== "object" || Array.isArray(scoped)) {
|
||||
return undefined;
|
||||
}
|
||||
return (scoped as Record<string, unknown>).apiKey;
|
||||
}
|
||||
|
||||
function setScopedCredentialValue(
|
||||
searchConfigTarget: Record<string, unknown>,
|
||||
value: unknown,
|
||||
): void {
|
||||
const scoped = searchConfigTarget.tavily;
|
||||
if (!scoped || typeof scoped !== "object" || Array.isArray(scoped)) {
|
||||
searchConfigTarget.tavily = { apiKey: value };
|
||||
return;
|
||||
}
|
||||
(scoped as Record<string, unknown>).apiKey = value;
|
||||
}
|
||||
|
||||
export function createTavilyWebSearchProvider(): WebSearchProviderPlugin {
|
||||
return {
|
||||
id: "tavily",
|
||||
@@ -53,8 +35,9 @@ export function createTavilyWebSearchProvider(): WebSearchProviderPlugin {
|
||||
autoDetectOrder: 70,
|
||||
credentialPath: "plugins.entries.tavily.config.webSearch.apiKey",
|
||||
inactiveSecretPaths: ["plugins.entries.tavily.config.webSearch.apiKey"],
|
||||
getCredentialValue: getScopedCredentialValue,
|
||||
setCredentialValue: setScopedCredentialValue,
|
||||
getCredentialValue: (searchConfig) => getScopedCredentialValue(searchConfig, "tavily"),
|
||||
setCredentialValue: (searchConfigTarget, value) =>
|
||||
setScopedCredentialValue(searchConfigTarget, "tavily", value),
|
||||
getConfiguredCredentialValue: (config) =>
|
||||
resolveProviderWebSearchPluginConfig(config, "tavily")?.apiKey,
|
||||
setConfiguredCredentialValue: (configTarget, value) => {
|
||||
|
||||
@@ -3,7 +3,9 @@ import {
|
||||
buildSearchCacheKey,
|
||||
buildUnsupportedSearchFilterResponse,
|
||||
DEFAULT_SEARCH_COUNT,
|
||||
getScopedCredentialValue,
|
||||
MAX_SEARCH_COUNT,
|
||||
mergeScopedSearchConfig,
|
||||
readCachedSearchPayload,
|
||||
readConfiguredSecretString,
|
||||
readNumberParam,
|
||||
@@ -13,6 +15,7 @@ import {
|
||||
resolveSearchCacheTtlMs,
|
||||
resolveSearchCount,
|
||||
resolveSearchTimeoutSeconds,
|
||||
setScopedCredentialValue,
|
||||
setProviderWebSearchPluginConfigValue,
|
||||
type SearchConfigRecord,
|
||||
type WebSearchProviderPlugin,
|
||||
@@ -265,20 +268,9 @@ export function createGrokWebSearchProvider(): WebSearchProviderPlugin {
|
||||
autoDetectOrder: 30,
|
||||
credentialPath: "plugins.entries.xai.config.webSearch.apiKey",
|
||||
inactiveSecretPaths: ["plugins.entries.xai.config.webSearch.apiKey"],
|
||||
getCredentialValue: (searchConfig) => {
|
||||
const grok = searchConfig?.grok;
|
||||
return grok && typeof grok === "object" && !Array.isArray(grok)
|
||||
? (grok as Record<string, unknown>).apiKey
|
||||
: undefined;
|
||||
},
|
||||
setCredentialValue: (searchConfigTarget, value) => {
|
||||
const scoped = searchConfigTarget.grok;
|
||||
if (!scoped || typeof scoped !== "object" || Array.isArray(scoped)) {
|
||||
searchConfigTarget.grok = { apiKey: value };
|
||||
return;
|
||||
}
|
||||
(scoped as Record<string, unknown>).apiKey = value;
|
||||
},
|
||||
getCredentialValue: (searchConfig) => getScopedCredentialValue(searchConfig, "grok"),
|
||||
setCredentialValue: (searchConfigTarget, value) =>
|
||||
setScopedCredentialValue(searchConfigTarget, "grok", value),
|
||||
getConfiguredCredentialValue: (config) =>
|
||||
resolveProviderWebSearchPluginConfig(config, "xai")?.apiKey,
|
||||
setConfiguredCredentialValue: (configTarget, value) => {
|
||||
@@ -286,20 +278,11 @@ export function createGrokWebSearchProvider(): WebSearchProviderPlugin {
|
||||
},
|
||||
createTool: (ctx) =>
|
||||
createGrokToolDefinition(
|
||||
(() => {
|
||||
const searchConfig = ctx.searchConfig as SearchConfigRecord | undefined;
|
||||
const pluginConfig = resolveProviderWebSearchPluginConfig(ctx.config, "xai");
|
||||
if (!pluginConfig) {
|
||||
return searchConfig;
|
||||
}
|
||||
return {
|
||||
...(searchConfig ?? {}),
|
||||
grok: {
|
||||
...resolveGrokConfig(searchConfig),
|
||||
...pluginConfig,
|
||||
},
|
||||
} as SearchConfigRecord;
|
||||
})(),
|
||||
mergeScopedSearchConfig(
|
||||
ctx.searchConfig as SearchConfigRecord | undefined,
|
||||
"grok",
|
||||
resolveProviderWebSearchPluginConfig(ctx.config, "xai"),
|
||||
) as SearchConfigRecord | undefined,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -71,6 +71,37 @@ export function setScopedCredentialValue(
|
||||
(scoped as Record<string, unknown>).apiKey = value;
|
||||
}
|
||||
|
||||
export function mergeScopedSearchConfig(
|
||||
searchConfig: Record<string, unknown> | undefined,
|
||||
key: string,
|
||||
pluginConfig: Record<string, unknown> | undefined,
|
||||
options?: { mirrorApiKeyToTopLevel?: boolean },
|
||||
): Record<string, unknown> | undefined {
|
||||
if (!pluginConfig) {
|
||||
return searchConfig;
|
||||
}
|
||||
|
||||
const currentScoped =
|
||||
searchConfig?.[key] &&
|
||||
typeof searchConfig[key] === "object" &&
|
||||
!Array.isArray(searchConfig[key])
|
||||
? (searchConfig[key] as Record<string, unknown>)
|
||||
: {};
|
||||
const next: Record<string, unknown> = {
|
||||
...searchConfig,
|
||||
[key]: {
|
||||
...currentScoped,
|
||||
...pluginConfig,
|
||||
},
|
||||
};
|
||||
|
||||
if (options?.mirrorApiKeyToTopLevel && pluginConfig.apiKey !== undefined) {
|
||||
next.apiKey = pluginConfig.apiKey;
|
||||
}
|
||||
|
||||
return next;
|
||||
}
|
||||
|
||||
export function resolveSearchConfig(cfg?: OpenClawConfig): WebSearchConfig {
|
||||
const search = cfg?.tools?.web?.search;
|
||||
if (!search || typeof search !== "object") {
|
||||
|
||||
@@ -3,7 +3,10 @@ import { __testing as braveTesting } from "../../../extensions/brave/src/brave-w
|
||||
import { __testing as moonshotTesting } from "../../../extensions/moonshot/src/kimi-web-search-provider.js";
|
||||
import { __testing as perplexityTesting } from "../../../extensions/perplexity/web-search-provider.js";
|
||||
import { __testing as xaiTesting } from "../../../extensions/xai/src/grok-web-search-provider.js";
|
||||
import { buildUnsupportedSearchFilterResponse } from "../../plugin-sdk/provider-web-search.js";
|
||||
import {
|
||||
buildUnsupportedSearchFilterResponse,
|
||||
mergeScopedSearchConfig,
|
||||
} from "../../plugin-sdk/provider-web-search.js";
|
||||
import { withEnv } from "../../test-utils/env.js";
|
||||
const {
|
||||
inferPerplexityBaseUrlFromApiKey,
|
||||
@@ -223,6 +226,40 @@ describe("web_search unsupported filter response", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("web_search scoped config merge", () => {
|
||||
it("returns the original config when no plugin config exists", () => {
|
||||
const searchConfig = { provider: "grok", grok: { model: "grok-4-1-fast" } };
|
||||
expect(mergeScopedSearchConfig(searchConfig, "grok", undefined)).toBe(searchConfig);
|
||||
});
|
||||
|
||||
it("merges plugin config into the scoped provider object", () => {
|
||||
expect(
|
||||
mergeScopedSearchConfig({ provider: "grok", grok: { model: "old-model" } }, "grok", {
|
||||
model: "new-model",
|
||||
apiKey: "xai-test-key",
|
||||
}),
|
||||
).toEqual({
|
||||
provider: "grok",
|
||||
grok: { model: "new-model", apiKey: "xai-test-key" },
|
||||
});
|
||||
});
|
||||
|
||||
it("can mirror the plugin apiKey to the top level config", () => {
|
||||
expect(
|
||||
mergeScopedSearchConfig(
|
||||
{ provider: "brave", brave: { count: 5 } },
|
||||
"brave",
|
||||
{ apiKey: "brave-test-key" },
|
||||
{ mirrorApiKeyToTopLevel: true },
|
||||
),
|
||||
).toEqual({
|
||||
provider: "brave",
|
||||
apiKey: "brave-test-key",
|
||||
brave: { count: 5, apiKey: "brave-test-key" },
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("web_search kimi config resolution", () => {
|
||||
it("uses config apiKey when provided", () => {
|
||||
expect(resolveKimiApiKey({ apiKey: "kimi-test-key" })).toBe("kimi-test-key");
|
||||
|
||||
@@ -30,6 +30,7 @@ export {
|
||||
export {
|
||||
getScopedCredentialValue,
|
||||
getTopLevelCredentialValue,
|
||||
mergeScopedSearchConfig,
|
||||
resolveProviderWebSearchPluginConfig,
|
||||
setScopedCredentialValue,
|
||||
setProviderWebSearchPluginConfigValue,
|
||||
|
||||
Reference in New Issue
Block a user