import type {
SearchConfigRecord,
WebSearchProviderPlugin,
WebSearchProviderToolDefinition,
} from
"openclaw/plugin-sdk/provider-web-search" ;
import { createWebSearchProviderContractFields } from
"openclaw/plugin-sdk/provider-web-search-config-contract" ;
const BRAVE_CREDENTIAL_PATH =
"plugins.entries.brave.config.webSearch.apiKey" ;
type BraveWebSearchRuntime =
typeof import (
"./brave-web-search-provider.runtime.js" )
;
let braveWebSearchRuntimePromise: Promise<BraveWebSearchRuntime> | undefined;
function loadBraveWebSearchRuntime(): Promise<BraveWebSearchRuntime> {
braveWebSearchRuntimePromise ??= import ("./brave-web-search-provider.runtime.js" );
return braveWebSearchRuntimePromise;
}
const BraveSearchSchema = {
type: "object" ,
properties: {
query: { type: "string" , description: "Search query string." },
count: {
type: "number" ,
description: "Number of results to return (1-10)." ,
minimum: 1 ,
maximum: 10 ,
},
country: {
type: "string" ,
description:
"2-letter country code for region-specific results (e.g., 'DE', 'US', 'ALL'). Default: 'US'." ,
},
language: {
type: "string" ,
description: "ISO 639-1 language code for results (e.g., 'en', 'de', 'fr')." ,
},
freshness: {
type: "string" ,
description: "Filter by time: 'day' (24h), 'week', 'month', or 'year'." ,
},
date_after: {
type: "string" ,
description: "Only results published after this date (YYYY-MM-DD)." ,
},
date_before: {
type: "string" ,
description: "Only results published before this date (YYYY-MM-DD)." ,
},
search_lang: {
type: "string" ,
description:
"Brave language code for search results (e.g., 'en', 'de', 'en-gb', 'zh-hans', 'zh-hant', 'pt-br')." ,
},
ui_lang: {
type: "string" ,
description:
"Locale code for UI elements in language-region format (e.g., 'en-US', 'de-DE', 'fr-FR', 'tr-TR'). Must include region subtag." ,
},
},
} satisfies Record<string, unknown>;
function isRecord(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function resolveProviderWebSearchPluginConfig(
config: unknown,
pluginId: string,
): Record<string, unknown> | undefined {
if (!isRecord(config)) {
return undefined;
}
const plugins = isRecord(config.plugins) ? config.plugins : undefined;
const entries = isRecord(plugins?.entries) ? plugins.entries : undefined;
const entry = isRecord(entries?.[pluginId]) ? entries[pluginId] : undefined;
const pluginConfig = isRecord(entry?.config) ? entry.config : undefined;
return isRecord(pluginConfig?.webSearch) ? pluginConfig.webSearch : undefined;
}
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 = isRecord(searchConfig?.[key]) ? searchConfig?.[key] : {};
const next: Record<string, unknown> = {
...searchConfig,
[key]: {
...currentScoped,
...pluginConfig,
},
};
if (options?.mirrorApiKeyToTopLevel && pluginConfig.apiKey !== undefined) {
next.apiKey = pluginConfig.apiKey;
}
return next;
}
function resolveBraveMode(searchConfig?: Record<string, unknown>): "web" | "llm-context" {
const brave = isRecord(searchConfig?.brave) ? searchConfig.brave : undefined;
return brave?.mode === "llm-context" ? "llm-context" : "web" ;
}
function createBraveToolDefinition(
searchConfig?: SearchConfigRecord,
): WebSearchProviderToolDefinition {
const braveMode = resolveBraveMode(searchConfig);
return {
description:
braveMode === "llm-context"
? "Search the web using Brave Search LLM Context API. Returns pre-extracted page content (text chunks, tables, code blocks) optimized for LLM grounding."
: "Search the web using Brave Search API. Supports region-specific and localized search via country and language parameters. Returns titles, URLs, and snippets for fast research." ,
parameters: BraveSearchSchema,
execute: async (args) => {
const { executeBraveSearch } = await loadBraveWebSearchRuntime();
return await executeBraveSearch(args, searchConfig);
},
};
}
export function createBraveWebSearchProvider(): WebSearchProviderPlugin {
return {
id: "brave" ,
label: "Brave Search" ,
hint: "Structured results · country/language/time filters" ,
onboardingScopes: ["text-inference" ],
credentialLabel: "Brave Search API key" ,
envVars: ["BRAVE_API_KEY" ],
placeholder: "BSA..." ,
signupUrl: "https://brave.com/search/api/ ",
docsUrl: "https://docs.openclaw.ai/brave-search ",
autoDetectOrder: 10 ,
credentialPath: BRAVE_CREDENTIAL_PATH,
...createWebSearchProviderContractFields({
credentialPath: BRAVE_CREDENTIAL_PATH,
searchCredential: { type: "top-level" },
configuredCredential: { pluginId: "brave" },
}),
createTool: (ctx) =>
createBraveToolDefinition(
mergeScopedSearchConfig(
ctx.searchConfig,
"brave" ,
resolveProviderWebSearchPluginConfig(ctx.config, "brave" ),
{ mirrorApiKeyToTopLevel: true },
),
),
};
}
Messung V0.5 in Prozent C=99 H=100 G=99
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet am 2026-06-05)
¤
*© Formatika GbR, Deutschland