diff --git a/README.md b/README.md index 1a8b819..89d881f 100644 --- a/README.md +++ b/README.md @@ -194,9 +194,14 @@ picoclaw onboard }, "tools": { "web": { - "search": { + "brave": { + "enabled": false, "api_key": "YOUR_BRAVE_API_KEY", "max_results": 5 + }, + "duckduckgo": { + "enabled": true, + "max_results": 5 } } } @@ -507,8 +512,14 @@ picoclaw agent -m "Hello" }, "tools": { "web": { - "search": { - "api_key": "BSA..." + "brave": { + "enabled": false, + "api_key": "BSA...", + "max_results": 5 + }, + "duckduckgo": { + "enabled": true, + "max_results": 5 } } } @@ -564,9 +575,14 @@ Add the key to `~/.picoclaw/config.json` if using Brave: { "tools": { "web": { - "search": { + "brave": { + "enabled": false, "api_key": "YOUR_BRAVE_API_KEY", "max_results": 5 + }, + "duckduckgo": { + "enabled": true, + "max_results": 5 } } } diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index fac2856..4a5bbf3 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -63,8 +63,15 @@ func NewAgentLoop(cfg *config.Config, msgBus *bus.MessageBus, provider providers toolsRegistry.Register(tools.NewListDirTool(workspace, restrict)) toolsRegistry.Register(tools.NewExecTool(workspace, restrict)) - braveAPIKey := cfg.Tools.Web.Search.APIKey - toolsRegistry.Register(tools.NewWebSearchTool(braveAPIKey, cfg.Tools.Web.Search.MaxResults)) + if searchTool := tools.NewWebSearchTool(tools.WebSearchToolOptions{ + BraveAPIKey: cfg.Tools.Web.Brave.APIKey, + BraveMaxResults: cfg.Tools.Web.Brave.MaxResults, + BraveEnabled: cfg.Tools.Web.Brave.Enabled, + DuckDuckGoMaxResults: cfg.Tools.Web.DuckDuckGo.MaxResults, + DuckDuckGoEnabled: cfg.Tools.Web.DuckDuckGo.Enabled, + }); searchTool != nil { + toolsRegistry.Register(searchTool) + } toolsRegistry.Register(tools.NewWebFetchTool(50000)) // Register message tool diff --git a/pkg/config/config.go b/pkg/config/config.go index 56f1e19..608cbc7 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -157,13 +157,20 @@ type GatewayConfig struct { Port int `json:"port" env:"PICOCLAW_GATEWAY_PORT"` } -type WebSearchConfig struct { - APIKey string `json:"api_key" env:"PICOCLAW_TOOLS_WEB_SEARCH_API_KEY"` - MaxResults int `json:"max_results" env:"PICOCLAW_TOOLS_WEB_SEARCH_MAX_RESULTS"` +type BraveConfig struct { + Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_BRAVE_ENABLED"` + APIKey string `json:"api_key" env:"PICOCLAW_TOOLS_WEB_BRAVE_API_KEY"` + MaxResults int `json:"max_results" env:"PICOCLAW_TOOLS_WEB_BRAVE_MAX_RESULTS"` +} + +type DuckDuckGoConfig struct { + Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_DUCKDUCKGO_ENABLED"` + MaxResults int `json:"max_results" env:"PICOCLAW_TOOLS_WEB_DUCKDUCKGO_MAX_RESULTS"` } type WebToolsConfig struct { - Search WebSearchConfig `json:"search"` + Brave BraveConfig `json:"brave"` + DuckDuckGo DuckDuckGoConfig `json:"duckduckgo"` } type ToolsConfig struct { @@ -249,10 +256,15 @@ func DefaultConfig() *Config { }, Tools: ToolsConfig{ Web: WebToolsConfig{ - Search: WebSearchConfig{ + Brave: BraveConfig{ + Enabled: false, APIKey: "", MaxResults: 5, }, + DuckDuckGo: DuckDuckGoConfig{ + Enabled: true, + MaxResults: 5, + }, }, }, } diff --git a/pkg/migrate/config.go b/pkg/migrate/config.go index d7fa633..1559f92 100644 --- a/pkg/migrate/config.go +++ b/pkg/migrate/config.go @@ -212,12 +212,17 @@ func ConvertConfig(data map[string]interface{}) (*config.Config, []string, error if tools, ok := getMap(data, "tools"); ok { if web, ok := getMap(tools, "web"); ok { + // Migrate old "search" config to "brave" if api_key is present if search, ok := getMap(web, "search"); ok { if v, ok := getString(search, "api_key"); ok { - cfg.Tools.Web.Search.APIKey = v + cfg.Tools.Web.Brave.APIKey = v + if v != "" { + cfg.Tools.Web.Brave.Enabled = true + } } if v, ok := getFloat(search, "max_results"); ok { - cfg.Tools.Web.Search.MaxResults = int(v) + cfg.Tools.Web.Brave.MaxResults = int(v) + cfg.Tools.Web.DuckDuckGo.MaxResults = int(v) } } } @@ -271,8 +276,8 @@ func MergeConfig(existing, incoming *config.Config) *config.Config { existing.Channels.MaixCam = incoming.Channels.MaixCam } - if existing.Tools.Web.Search.APIKey == "" { - existing.Tools.Web.Search = incoming.Tools.Web.Search + if existing.Tools.Web.Brave.APIKey == "" { + existing.Tools.Web.Brave = incoming.Tools.Web.Brave } return existing diff --git a/pkg/tools/web.go b/pkg/tools/web.go index f03bafb..dec4b89 100644 --- a/pkg/tools/web.go +++ b/pkg/tools/web.go @@ -188,16 +188,31 @@ type WebSearchTool struct { maxResults int } -func NewWebSearchTool(apiKey string, maxResults int) *WebSearchTool { - if maxResults <= 0 || maxResults > 10 { - maxResults = 5 - } +type WebSearchToolOptions struct { + BraveAPIKey string + BraveMaxResults int + BraveEnabled bool + DuckDuckGoMaxResults int + DuckDuckGoEnabled bool +} +func NewWebSearchTool(opts WebSearchToolOptions) *WebSearchTool { var provider SearchProvider - if apiKey != "" { - provider = &BraveSearchProvider{apiKey: apiKey} - } else { + maxResults := 5 + + // Priority: Brave > DuckDuckGo + if opts.BraveEnabled && opts.BraveAPIKey != "" { + provider = &BraveSearchProvider{apiKey: opts.BraveAPIKey} + if opts.BraveMaxResults > 0 { + maxResults = opts.BraveMaxResults + } + } else if opts.DuckDuckGoEnabled { provider = &DuckDuckGoSearchProvider{} + if opts.DuckDuckGoMaxResults > 0 { + maxResults = opts.DuckDuckGoMaxResults + } + } else { + return nil } return &WebSearchTool{