diff --git a/crates/tui/src/config.rs b/crates/tui/src/config.rs index 63fd1e80..684e888a 100644 --- a/crates/tui/src/config.rs +++ b/crates/tui/src/config.rs @@ -648,9 +648,9 @@ impl SnapshotsConfig { #[serde(rename_all = "snake_case")] pub enum SearchProvider { /// Bing HTML scraping. No API key needed. - #[default] Bing, /// DuckDuckGo HTML scraping with Bing fallback. No API key needed. + #[default] #[serde(alias = "duckduckgo")] DuckDuckGo, /// Tavily AI Search API (). Requires api_key. @@ -714,7 +714,7 @@ pub struct SearchProviderResolution { /// Web search provider configuration (`[search]` table in config.toml). #[derive(Debug, Clone, Deserialize, Default)] pub struct SearchConfig { - /// Search provider: `bing` | `duckduckgo` | `tavily` | `bocha` | `metaso`. Default: `bing`. + /// Search provider: `bing` | `duckduckgo` | `tavily` | `bocha` | `metaso`. Default: `duckduckgo`. #[serde(default)] pub provider: Option, /// API key for Tavily, Bocha, or Metaso. Not required for Bing or DuckDuckGo. @@ -1105,9 +1105,9 @@ pub struct Config { #[serde(default)] pub snapshots: Option, - /// Web search provider configuration. When absent, defaults to Bing. - /// Set `provider` to `duckduckgo`, `tavily`, or `bocha` to use those - /// services instead; Tavily and Bocha also require an `api_key`. + /// Web search provider configuration. When absent, defaults to DuckDuckGo. + /// Set `provider` to `bing`, `tavily`, or `bocha` to use those services + /// instead; Tavily and Bocha also require an `api_key`. #[serde(default)] pub search: Option, @@ -4208,8 +4208,8 @@ mod tests { } #[test] - fn search_provider_defaults_to_bing() { - assert_eq!(SearchProvider::default(), SearchProvider::Bing); + fn search_provider_defaults_to_duckduckgo() { + assert_eq!(SearchProvider::default(), SearchProvider::DuckDuckGo); } #[test] @@ -4254,7 +4254,7 @@ mod tests { let resolution = Config::default().search_provider_resolution(); unsafe { EnvGuard::restore_var("DEEPSEEK_SEARCH_PROVIDER", prev) }; - assert_eq!(resolution.provider, SearchProvider::Bing); + assert_eq!(resolution.provider, SearchProvider::DuckDuckGo); assert_eq!(resolution.source, SearchProviderSource::Default); } diff --git a/crates/tui/src/core/engine.rs b/crates/tui/src/core/engine.rs index e81debe9..516a1f38 100644 --- a/crates/tui/src/core/engine.rs +++ b/crates/tui/src/core/engine.rs @@ -162,7 +162,7 @@ pub struct EngineConfig { pub strict_tool_mode: bool, /// Workshop / large-tool-output routing (#548). `None` disables routing. pub workshop: Option, - /// Which search backend `web_search` should use. Default: Bing. + /// Which search backend `web_search` should use. Default: DuckDuckGo. pub search_provider: crate::config::SearchProvider, /// API key for Tavily, Bocha, or Metaso. `None` for Bing or DuckDuckGo. /// Metaso also falls back to `METASO_API_KEY` env var, then a built-in key. diff --git a/crates/tui/src/main.rs b/crates/tui/src/main.rs index 473484dc..352e6ea2 100644 --- a/crates/tui/src/main.rs +++ b/crates/tui/src/main.rs @@ -3163,11 +3163,11 @@ fn doctor_search_provider_line(config: &Config) -> String { let switch_hint = if matches!( (search_provider.provider, search_provider.source), ( - crate::config::SearchProvider::Bing, + crate::config::SearchProvider::DuckDuckGo, crate::config::SearchProviderSource::Default ) ) { - "; set [search] provider = \"duckduckgo\" | \"tavily\" | \"bocha\" to switch" + "; set [search] provider = \"bing\" | \"tavily\" | \"bocha\" to switch" } else { "" }; @@ -5701,7 +5701,7 @@ mod doctor_endpoint_tests { } #[test] - fn doctor_search_provider_line_includes_default_source_and_switch_hint() { + fn doctor_search_provider_line_includes_duckduckgo_default_source_and_switch_hint() { let _guard = crate::test_support::lock_test_env(); let prev = std::env::var_os("DEEPSEEK_SEARCH_PROVIDER"); unsafe { std::env::remove_var("DEEPSEEK_SEARCH_PROVIDER") }; @@ -5712,9 +5712,10 @@ mod doctor_endpoint_tests { Some(value) => unsafe { std::env::set_var("DEEPSEEK_SEARCH_PROVIDER", value) }, None => unsafe { std::env::remove_var("DEEPSEEK_SEARCH_PROVIDER") }, } - assert!(line.contains("search_provider: bing")); + assert!(line.contains("search_provider: duckduckgo")); assert!(line.contains("source: default")); assert!(line.contains("[search] provider")); + assert!(line.contains("provider = \"bing\"")); } #[test] diff --git a/crates/tui/src/tools/spec.rs b/crates/tui/src/tools/spec.rs index f13c6516..3e16a81d 100644 --- a/crates/tui/src/tools/spec.rs +++ b/crates/tui/src/tools/spec.rs @@ -162,7 +162,7 @@ pub struct ToolContext { /// routing (e.g. in sub-agents and test contexts to avoid recursion). pub large_output_router: Option, - /// Which search backend `web_search` should use. Default: Bing. Set via + /// Which search backend `web_search` should use. Default: DuckDuckGo. Set via /// `[search] provider` in config.toml. pub search_provider: crate::config::SearchProvider, /// API key for Tavily, Bocha, or Metaso. `None` for Bing or DuckDuckGo. diff --git a/crates/tui/src/tools/web_search.rs b/crates/tui/src/tools/web_search.rs index cb64ce98..16c7b632 100644 --- a/crates/tui/src/tools/web_search.rs +++ b/crates/tui/src/tools/web_search.rs @@ -129,7 +129,7 @@ impl ToolSpec for WebSearchTool { } fn description(&self) -> &'static str { - "Search the web and return ranked results with URLs and snippets. Default backend is Bing; set `[search] provider = \"duckduckgo\" | \"tavily\" | \"bocha\"` in config.toml to switch backends. Use this instead of scraping search engines with `curl` in `exec_shell`. For a known canonical URL, prefer `fetch_url` directly." + "Search the web and return ranked results with URLs and snippets. Default backend is DuckDuckGo with Bing fallback; set `[search] provider = \"bing\" | \"tavily\" | \"bocha\"` in config.toml to switch backends. Use this instead of scraping search engines with `curl` in `exec_shell`. For a known canonical URL, prefer `fetch_url` directly." } fn input_schema(&self) -> Value { diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index b92cd8ff..a40b41f5 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -654,14 +654,16 @@ Use `codewhale-tui features list` to inspect known flags and their effective sta ## Web Search Provider -`web_search` uses Bing by default and does not require an API key. DuckDuckGo -remains selectable for users who explicitly want it, and Tavily or Bocha can be -selected when an API-backed provider is preferred. **Metaso** ([metaso.cn](https://metaso.cn)) +`web_search` uses DuckDuckGo by default and does not require an API key. The +DuckDuckGo path keeps a Bing fallback when DDG returns a bot challenge or no +parseable results. Bing remains selectable for users who explicitly want it, +and Tavily or Bocha can be selected when an API-backed provider is preferred. +**Metaso** ([metaso.cn](https://metaso.cn)) 100 searches/day free quota — set `METASO_API_KEY` or `[search] api_key` for a higher quota. ```toml [search] -provider = "bing" # bing | duckduckgo | tavily | bocha | metaso +provider = "duckduckgo" # duckduckgo | bing | tavily | bocha | metaso # api_key = "YOUR_KEY" # required for tavily and bocha; optional for metaso (100 searches/day free quota) ``` diff --git a/docs/TOOL_SURFACE.md b/docs/TOOL_SURFACE.md index 1038b93e..aa31fb4e 100644 --- a/docs/TOOL_SURFACE.md +++ b/docs/TOOL_SURFACE.md @@ -35,7 +35,7 @@ chosen over the available shell equivalent. Companion to `crates/tui/src/prompts |---|---| | `grep_files` | Regex search file contents within the workspace; structured matches + context lines. Pure-Rust (`regex` crate), no `rg`/`grep` shell-out. | | `file_search` | Fuzzy-match filenames (not contents). Use when you know roughly the name. | -| `web_search` | Bing by default; DuckDuckGo, Tavily, and Bocha are selectable in config. Ranked snippets + `ref_id` for citation. | +| `web_search` | DuckDuckGo by default with Bing fallback; Bing, Tavily, and Bocha are selectable in config. Ranked snippets + `ref_id` for citation. | | `fetch_url` | Direct HTTP GET on a known URL. Faster than `web_search` when the link is already known. HTML stripped to text by default. | ### Shell