feat(web): configurable web search providers with fallback
Add multi-provider web search support: Brave (default), Tavily, DuckDuckGo, and SearXNG. Falls back to DuckDuckGo when provider credentials are missing. Providers are dispatched via a map with register_provider() for plugin extensibility. - WebSearchConfig with env-var resolution and from_legacy() bridge - Config migration for legacy flat keys (tavilyApiKey, searxngBaseUrl) - SearXNG URL validation, explicit error for unknown providers - ddgs package (replaces deprecated duckduckgo-search) - 16 tests covering all providers, fallback, env resolution, edge cases - docs/web-search.md with full config reference Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
95
docs/web-search.md
Normal file
95
docs/web-search.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# Web Search Providers
|
||||
|
||||
NanoBot supports multiple web search providers. Configure in `~/.nanobot/config.json` under `tools.web.search`.
|
||||
|
||||
| Provider | Key | Env var |
|
||||
|----------|-----|---------|
|
||||
| `brave` (default) | `apiKey` | `BRAVE_API_KEY` |
|
||||
| `tavily` | `apiKey` | `TAVILY_API_KEY` |
|
||||
| `searxng` | `baseUrl` | `SEARXNG_BASE_URL` |
|
||||
| `duckduckgo` | — | — |
|
||||
|
||||
Each provider uses the same `apiKey` field — set the provider and key together. If no provider is specified but `apiKey` is given, Brave is assumed.
|
||||
|
||||
When credentials are missing and `fallbackToDuckduckgo` is `true` (the default), searches fall back to DuckDuckGo automatically.
|
||||
|
||||
## Examples
|
||||
|
||||
**Brave** (default — just set the key):
|
||||
|
||||
```json
|
||||
{
|
||||
"tools": {
|
||||
"web": {
|
||||
"search": {
|
||||
"apiKey": "BSA..."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Tavily:**
|
||||
|
||||
```json
|
||||
{
|
||||
"tools": {
|
||||
"web": {
|
||||
"search": {
|
||||
"provider": "tavily",
|
||||
"apiKey": "tvly-..."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**SearXNG** (self-hosted, no API key needed):
|
||||
|
||||
```json
|
||||
{
|
||||
"tools": {
|
||||
"web": {
|
||||
"search": {
|
||||
"provider": "searxng",
|
||||
"baseUrl": "https://searx.example"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**DuckDuckGo** (no credentials required):
|
||||
|
||||
```json
|
||||
{
|
||||
"tools": {
|
||||
"web": {
|
||||
"search": {
|
||||
"provider": "duckduckgo"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
|-----|------|---------|-------------|
|
||||
| `provider` | string | `"brave"` | Search backend |
|
||||
| `apiKey` | string | `""` | API key for the selected provider |
|
||||
| `baseUrl` | string | `""` | Base URL for SearXNG (appends `/search`) |
|
||||
| `maxResults` | integer | `5` | Default results per search |
|
||||
| `fallbackToDuckduckgo` | boolean | `true` | Fall back to DuckDuckGo when credentials are missing |
|
||||
|
||||
## Custom providers
|
||||
|
||||
Plugins can register additional providers at runtime via the dispatch dict:
|
||||
|
||||
```python
|
||||
async def my_search(query: str, n: int) -> str:
|
||||
...
|
||||
|
||||
tool._provider_dispatch["my-engine"] = my_search
|
||||
```
|
||||
Reference in New Issue
Block a user