mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-03 17:20:34 +00:00
refactor web search config ownership into extensions
This commit is contained in:
@@ -11,6 +11,19 @@ function asConfig(value: unknown): OpenClawConfig {
|
||||
return value as OpenClawConfig;
|
||||
}
|
||||
|
||||
function providerPluginId(provider: ProviderUnderTest): string {
|
||||
switch (provider) {
|
||||
case "gemini":
|
||||
return "google";
|
||||
case "grok":
|
||||
return "xai";
|
||||
case "kimi":
|
||||
return "moonshot";
|
||||
default:
|
||||
return provider;
|
||||
}
|
||||
}
|
||||
|
||||
async function runRuntimeWebTools(params: { config: OpenClawConfig; env?: NodeJS.ProcessEnv }) {
|
||||
const sourceConfig = structuredClone(params.config);
|
||||
const resolvedConfig = structuredClone(params.config);
|
||||
@@ -30,40 +43,32 @@ function createProviderSecretRefConfig(
|
||||
provider: ProviderUnderTest,
|
||||
envRefId: string,
|
||||
): OpenClawConfig {
|
||||
const search: Record<string, unknown> = {
|
||||
enabled: true,
|
||||
provider,
|
||||
};
|
||||
if (provider === "brave") {
|
||||
search.apiKey = { source: "env", provider: "default", id: envRefId };
|
||||
} else {
|
||||
search[provider] = {
|
||||
apiKey: { source: "env", provider: "default", id: envRefId },
|
||||
};
|
||||
}
|
||||
return asConfig({
|
||||
tools: {
|
||||
web: {
|
||||
search,
|
||||
search: {
|
||||
enabled: true,
|
||||
provider,
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
entries: {
|
||||
[providerPluginId(provider)]: {
|
||||
enabled: true,
|
||||
config: {
|
||||
webSearch: {
|
||||
apiKey: { source: "env", provider: "default", id: envRefId },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function readProviderKey(config: OpenClawConfig, provider: ProviderUnderTest): unknown {
|
||||
if (provider === "brave") {
|
||||
return config.tools?.web?.search?.apiKey;
|
||||
}
|
||||
if (provider === "gemini") {
|
||||
return config.tools?.web?.search?.gemini?.apiKey;
|
||||
}
|
||||
if (provider === "grok") {
|
||||
return config.tools?.web?.search?.grok?.apiKey;
|
||||
}
|
||||
if (provider === "kimi") {
|
||||
return config.tools?.web?.search?.kimi?.apiKey;
|
||||
}
|
||||
return config.tools?.web?.search?.perplexity?.apiKey;
|
||||
return config.plugins?.entries?.[providerPluginId(provider)]?.config?.webSearch?.apiKey;
|
||||
}
|
||||
|
||||
function expectInactiveFirecrawlSecretRef(params: {
|
||||
@@ -171,18 +176,40 @@ describe("runtime web tools resolution", () => {
|
||||
tools: {
|
||||
web: {
|
||||
search: {
|
||||
apiKey: { source: "env", provider: "default", id: "BRAVE_REF" },
|
||||
gemini: {
|
||||
apiKey: { source: "env", provider: "default", id: "GEMINI_REF" },
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
entries: {
|
||||
brave: {
|
||||
enabled: true,
|
||||
config: {
|
||||
webSearch: { apiKey: { source: "env", provider: "default", id: "BRAVE_REF" } },
|
||||
},
|
||||
grok: {
|
||||
apiKey: { source: "env", provider: "default", id: "GROK_REF" },
|
||||
},
|
||||
google: {
|
||||
enabled: true,
|
||||
config: {
|
||||
webSearch: { apiKey: { source: "env", provider: "default", id: "GEMINI_REF" } },
|
||||
},
|
||||
kimi: {
|
||||
apiKey: { source: "env", provider: "default", id: "KIMI_REF" },
|
||||
},
|
||||
xai: {
|
||||
enabled: true,
|
||||
config: {
|
||||
webSearch: { apiKey: { source: "env", provider: "default", id: "GROK_REF" } },
|
||||
},
|
||||
perplexity: {
|
||||
apiKey: { source: "env", provider: "default", id: "PERPLEXITY_REF" },
|
||||
},
|
||||
moonshot: {
|
||||
enabled: true,
|
||||
config: {
|
||||
webSearch: { apiKey: { source: "env", provider: "default", id: "KIMI_REF" } },
|
||||
},
|
||||
},
|
||||
perplexity: {
|
||||
enabled: true,
|
||||
config: {
|
||||
webSearch: { apiKey: { source: "env", provider: "default", id: "PERPLEXITY_REF" } },
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -199,13 +226,13 @@ describe("runtime web tools resolution", () => {
|
||||
|
||||
expect(metadata.search.providerSource).toBe("auto-detect");
|
||||
expect(metadata.search.selectedProvider).toBe("brave");
|
||||
expect(resolvedConfig.tools?.web?.search?.apiKey).toBe("brave-precedence-key");
|
||||
expect(readProviderKey(resolvedConfig, "brave")).toBe("brave-precedence-key");
|
||||
expect(context.warnings).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({ path: "tools.web.search.gemini.apiKey" }),
|
||||
expect.objectContaining({ path: "tools.web.search.grok.apiKey" }),
|
||||
expect.objectContaining({ path: "tools.web.search.kimi.apiKey" }),
|
||||
expect.objectContaining({ path: "tools.web.search.perplexity.apiKey" }),
|
||||
expect.objectContaining({ path: "plugins.entries.google.config.webSearch.apiKey" }),
|
||||
expect.objectContaining({ path: "plugins.entries.xai.config.webSearch.apiKey" }),
|
||||
expect.objectContaining({ path: "plugins.entries.moonshot.config.webSearch.apiKey" }),
|
||||
expect.objectContaining({ path: "plugins.entries.perplexity.config.webSearch.apiKey" }),
|
||||
]),
|
||||
);
|
||||
});
|
||||
@@ -216,12 +243,25 @@ describe("runtime web tools resolution", () => {
|
||||
tools: {
|
||||
web: {
|
||||
search: {
|
||||
apiKey: { source: "env", provider: "default", id: "BRAVE_API_KEY_REF" },
|
||||
gemini: {
|
||||
apiKey: {
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: "MISSING_GEMINI_API_KEY_REF",
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
entries: {
|
||||
brave: {
|
||||
enabled: true,
|
||||
config: {
|
||||
webSearch: {
|
||||
apiKey: { source: "env", provider: "default", id: "BRAVE_API_KEY_REF" },
|
||||
},
|
||||
},
|
||||
},
|
||||
google: {
|
||||
enabled: true,
|
||||
config: {
|
||||
webSearch: {
|
||||
apiKey: { source: "env", provider: "default", id: "MISSING_GEMINI_API_KEY_REF" },
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -236,8 +276,8 @@ describe("runtime web tools resolution", () => {
|
||||
expect(metadata.search.providerSource).toBe("auto-detect");
|
||||
expect(metadata.search.selectedProvider).toBe("brave");
|
||||
expect(metadata.search.selectedProviderKeySource).toBe("secretRef");
|
||||
expect(resolvedConfig.tools?.web?.search?.apiKey).toBe("brave-runtime-key");
|
||||
expect(resolvedConfig.tools?.web?.search?.gemini?.apiKey).toEqual({
|
||||
expect(readProviderKey(resolvedConfig, "brave")).toBe("brave-runtime-key");
|
||||
expect(readProviderKey(resolvedConfig, "gemini")).toEqual({
|
||||
source: "env",
|
||||
provider: "default",
|
||||
id: "MISSING_GEMINI_API_KEY_REF",
|
||||
@@ -246,7 +286,7 @@ describe("runtime web tools resolution", () => {
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
code: "SECRETS_REF_IGNORED_INACTIVE_SURFACE",
|
||||
path: "tools.web.search.gemini.apiKey",
|
||||
path: "plugins.entries.google.config.webSearch.apiKey",
|
||||
}),
|
||||
]),
|
||||
);
|
||||
@@ -261,9 +301,26 @@ describe("runtime web tools resolution", () => {
|
||||
tools: {
|
||||
web: {
|
||||
search: {
|
||||
apiKey: { source: "env", provider: "default", id: "MISSING_BRAVE_API_KEY_REF" },
|
||||
gemini: {
|
||||
apiKey: { source: "env", provider: "default", id: "GEMINI_API_KEY_REF" },
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
entries: {
|
||||
brave: {
|
||||
enabled: true,
|
||||
config: {
|
||||
webSearch: {
|
||||
apiKey: { source: "env", provider: "default", id: "MISSING_BRAVE_API_KEY_REF" },
|
||||
},
|
||||
},
|
||||
},
|
||||
google: {
|
||||
enabled: true,
|
||||
config: {
|
||||
webSearch: {
|
||||
apiKey: { source: "env", provider: "default", id: "GEMINI_API_KEY_REF" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -276,12 +333,12 @@ describe("runtime web tools resolution", () => {
|
||||
|
||||
expect(metadata.search.providerSource).toBe("auto-detect");
|
||||
expect(metadata.search.selectedProvider).toBe("gemini");
|
||||
expect(resolvedConfig.tools?.web?.search?.gemini?.apiKey).toBe("gemini-runtime-key");
|
||||
expect(readProviderKey(resolvedConfig, "gemini")).toBe("gemini-runtime-key");
|
||||
expect(context.warnings).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
code: "SECRETS_REF_IGNORED_INACTIVE_SURFACE",
|
||||
path: "tools.web.search.apiKey",
|
||||
path: "plugins.entries.brave.config.webSearch.apiKey",
|
||||
}),
|
||||
]),
|
||||
);
|
||||
@@ -297,8 +354,17 @@ describe("runtime web tools resolution", () => {
|
||||
web: {
|
||||
search: {
|
||||
provider: "invalid-provider",
|
||||
gemini: {
|
||||
apiKey: { source: "env", provider: "default", id: "GEMINI_API_KEY_REF" },
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
entries: {
|
||||
google: {
|
||||
enabled: true,
|
||||
config: {
|
||||
webSearch: {
|
||||
apiKey: { source: "env", provider: "default", id: "GEMINI_API_KEY_REF" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -312,7 +378,7 @@ describe("runtime web tools resolution", () => {
|
||||
expect(metadata.search.providerConfigured).toBeUndefined();
|
||||
expect(metadata.search.providerSource).toBe("auto-detect");
|
||||
expect(metadata.search.selectedProvider).toBe("gemini");
|
||||
expect(resolvedConfig.tools?.web?.search?.gemini?.apiKey).toBe("gemini-runtime-key");
|
||||
expect(readProviderKey(resolvedConfig, "gemini")).toBe("gemini-runtime-key");
|
||||
expect(metadata.search.diagnostics).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
@@ -337,8 +403,17 @@ describe("runtime web tools resolution", () => {
|
||||
web: {
|
||||
search: {
|
||||
provider: "gemini",
|
||||
gemini: {
|
||||
apiKey: { source: "env", provider: "default", id: "MISSING_GEMINI_API_KEY_REF" },
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
entries: {
|
||||
google: {
|
||||
enabled: true,
|
||||
config: {
|
||||
webSearch: {
|
||||
apiKey: { source: "env", provider: "default", id: "MISSING_GEMINI_API_KEY_REF" },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -361,7 +436,7 @@ describe("runtime web tools resolution", () => {
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
code: "WEB_SEARCH_KEY_UNRESOLVED_NO_FALLBACK",
|
||||
path: "tools.web.search.gemini.apiKey",
|
||||
path: "plugins.entries.google.config.webSearch.apiKey",
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
@@ -221,6 +221,9 @@ function setResolvedWebSearchApiKey(params: {
|
||||
env: params.env,
|
||||
bundledAllowlistCompat: true,
|
||||
}).find((entry) => entry.id === params.provider);
|
||||
if (provider?.setConfiguredCredentialValue) {
|
||||
provider.setConfiguredCredentialValue(params.resolvedConfig, params.value);
|
||||
}
|
||||
provider?.setCredentialValue(search, params.value);
|
||||
}
|
||||
|
||||
@@ -318,7 +321,9 @@ export async function resolveRuntimeWebTools(params: {
|
||||
|
||||
for (const provider of candidates) {
|
||||
const path = keyPathForProvider(provider);
|
||||
const value = provider.getCredentialValue(search);
|
||||
const value =
|
||||
provider.getConfiguredCredentialValue?.(params.sourceConfig) ??
|
||||
provider.getCredentialValue(search);
|
||||
const resolution = await resolveSecretInputWithEnvFallback({
|
||||
sourceConfig: params.sourceConfig,
|
||||
context: params.context,
|
||||
@@ -451,7 +456,9 @@ export async function resolveRuntimeWebTools(params: {
|
||||
if (provider.id === searchMetadata.selectedProvider) {
|
||||
continue;
|
||||
}
|
||||
const value = provider.getCredentialValue(search);
|
||||
const value =
|
||||
provider.getConfiguredCredentialValue?.(params.sourceConfig) ??
|
||||
provider.getCredentialValue(search);
|
||||
if (!hasConfiguredSecretRef(value, defaults)) {
|
||||
continue;
|
||||
}
|
||||
@@ -465,7 +472,9 @@ export async function resolveRuntimeWebTools(params: {
|
||||
}
|
||||
} else if (search && !searchEnabled) {
|
||||
for (const provider of providers) {
|
||||
const value = provider.getCredentialValue(search);
|
||||
const value =
|
||||
provider.getConfiguredCredentialValue?.(params.sourceConfig) ??
|
||||
provider.getCredentialValue(search);
|
||||
if (!hasConfiguredSecretRef(value, defaults)) {
|
||||
continue;
|
||||
}
|
||||
@@ -484,7 +493,9 @@ export async function resolveRuntimeWebTools(params: {
|
||||
if (provider.id === configuredProvider) {
|
||||
continue;
|
||||
}
|
||||
const value = provider.getCredentialValue(search);
|
||||
const value =
|
||||
provider.getConfiguredCredentialValue?.(params.sourceConfig) ??
|
||||
provider.getCredentialValue(search);
|
||||
if (!hasConfiguredSecretRef(value, defaults)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -733,6 +733,17 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
|
||||
includeInConfigure: true,
|
||||
includeInAudit: true,
|
||||
},
|
||||
{
|
||||
id: "plugins.entries.brave.config.webSearch.apiKey",
|
||||
targetType: "plugins.entries.brave.config.webSearch.apiKey",
|
||||
configFile: "openclaw.json",
|
||||
pathPattern: "plugins.entries.brave.config.webSearch.apiKey",
|
||||
secretShape: SECRET_INPUT_SHAPE,
|
||||
expectedResolvedValue: "string",
|
||||
includeInPlan: true,
|
||||
includeInConfigure: true,
|
||||
includeInAudit: true,
|
||||
},
|
||||
{
|
||||
id: "tools.web.search.gemini.apiKey",
|
||||
targetType: "tools.web.search.gemini.apiKey",
|
||||
@@ -744,6 +755,17 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
|
||||
includeInConfigure: true,
|
||||
includeInAudit: true,
|
||||
},
|
||||
{
|
||||
id: "plugins.entries.google.config.webSearch.apiKey",
|
||||
targetType: "plugins.entries.google.config.webSearch.apiKey",
|
||||
configFile: "openclaw.json",
|
||||
pathPattern: "plugins.entries.google.config.webSearch.apiKey",
|
||||
secretShape: SECRET_INPUT_SHAPE,
|
||||
expectedResolvedValue: "string",
|
||||
includeInPlan: true,
|
||||
includeInConfigure: true,
|
||||
includeInAudit: true,
|
||||
},
|
||||
{
|
||||
id: "tools.web.search.grok.apiKey",
|
||||
targetType: "tools.web.search.grok.apiKey",
|
||||
@@ -755,6 +777,17 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
|
||||
includeInConfigure: true,
|
||||
includeInAudit: true,
|
||||
},
|
||||
{
|
||||
id: "plugins.entries.xai.config.webSearch.apiKey",
|
||||
targetType: "plugins.entries.xai.config.webSearch.apiKey",
|
||||
configFile: "openclaw.json",
|
||||
pathPattern: "plugins.entries.xai.config.webSearch.apiKey",
|
||||
secretShape: SECRET_INPUT_SHAPE,
|
||||
expectedResolvedValue: "string",
|
||||
includeInPlan: true,
|
||||
includeInConfigure: true,
|
||||
includeInAudit: true,
|
||||
},
|
||||
{
|
||||
id: "tools.web.search.kimi.apiKey",
|
||||
targetType: "tools.web.search.kimi.apiKey",
|
||||
@@ -766,6 +799,17 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
|
||||
includeInConfigure: true,
|
||||
includeInAudit: true,
|
||||
},
|
||||
{
|
||||
id: "plugins.entries.moonshot.config.webSearch.apiKey",
|
||||
targetType: "plugins.entries.moonshot.config.webSearch.apiKey",
|
||||
configFile: "openclaw.json",
|
||||
pathPattern: "plugins.entries.moonshot.config.webSearch.apiKey",
|
||||
secretShape: SECRET_INPUT_SHAPE,
|
||||
expectedResolvedValue: "string",
|
||||
includeInPlan: true,
|
||||
includeInConfigure: true,
|
||||
includeInAudit: true,
|
||||
},
|
||||
{
|
||||
id: "tools.web.search.perplexity.apiKey",
|
||||
targetType: "tools.web.search.perplexity.apiKey",
|
||||
@@ -777,6 +821,28 @@ const SECRET_TARGET_REGISTRY: SecretTargetRegistryEntry[] = [
|
||||
includeInConfigure: true,
|
||||
includeInAudit: true,
|
||||
},
|
||||
{
|
||||
id: "plugins.entries.perplexity.config.webSearch.apiKey",
|
||||
targetType: "plugins.entries.perplexity.config.webSearch.apiKey",
|
||||
configFile: "openclaw.json",
|
||||
pathPattern: "plugins.entries.perplexity.config.webSearch.apiKey",
|
||||
secretShape: SECRET_INPUT_SHAPE,
|
||||
expectedResolvedValue: "string",
|
||||
includeInPlan: true,
|
||||
includeInConfigure: true,
|
||||
includeInAudit: true,
|
||||
},
|
||||
{
|
||||
id: "plugins.entries.firecrawl.config.webSearch.apiKey",
|
||||
targetType: "plugins.entries.firecrawl.config.webSearch.apiKey",
|
||||
configFile: "openclaw.json",
|
||||
pathPattern: "plugins.entries.firecrawl.config.webSearch.apiKey",
|
||||
secretShape: SECRET_INPUT_SHAPE,
|
||||
expectedResolvedValue: "string",
|
||||
includeInPlan: true,
|
||||
includeInConfigure: true,
|
||||
includeInAudit: true,
|
||||
},
|
||||
];
|
||||
|
||||
export { SECRET_TARGET_REGISTRY };
|
||||
|
||||
Reference in New Issue
Block a user