diff --git a/config/config.example.json b/config/config.example.json index 9575039f8..6db124bc7 100644 --- a/config/config.example.json +++ b/config/config.example.json @@ -213,9 +213,11 @@ "enabled": true, "max_results": 5 }, - "perplexity": { + "grok": { "enabled": false, - "api_key": "pplx-xxx", + "api_key": "xai-xxx", + "endpoint": "https://api.x.ai/v1/chat/completions", + "default_model": "grok-4", "max_results": 5 }, "proxy": "" diff --git a/docs/tools_configuration.md b/docs/tools_configuration.md index 8aba1aa91..f4bdaa096 100644 --- a/docs/tools_configuration.md +++ b/docs/tools_configuration.md @@ -34,12 +34,14 @@ Web tools are used for web search and fetching. | `enabled` | bool | true | Enable DuckDuckGo search | | `max_results` | int | 5 | Maximum number of results | -### Perplexity +### Grok | Config | Type | Default | Description | |--------|------|---------|-------------| -| `enabled` | bool | false | Enable Perplexity search | -| `api_key` | string | - | Perplexity API key | +| `enabled` | bool | false | Enable Grok search | +| `api_key` | string | - | Grok API key | +| `endpoint` | string | `https://api.x.ai/v1/chat/completions` | Grok-compatible chat completions endpoint | +| `default_model` | string | `grok-4` | Default Grok model used by web search | | `max_results` | int | 5 | Maximum number of results | ## Exec Tool diff --git a/pkg/agent/loop.go b/pkg/agent/loop.go index 0603f4dc1..6a37f3018 100644 --- a/pkg/agent/loop.go +++ b/pkg/agent/loop.go @@ -107,9 +107,11 @@ func registerSharedTools( TavilyEnabled: cfg.Tools.Web.Tavily.Enabled, DuckDuckGoMaxResults: cfg.Tools.Web.DuckDuckGo.MaxResults, DuckDuckGoEnabled: cfg.Tools.Web.DuckDuckGo.Enabled, - PerplexityAPIKey: cfg.Tools.Web.Perplexity.APIKey, - PerplexityMaxResults: cfg.Tools.Web.Perplexity.MaxResults, - PerplexityEnabled: cfg.Tools.Web.Perplexity.Enabled, + GrokAPIKey: cfg.Tools.Web.Grok.APIKey, + GrokEndpoint: cfg.Tools.Web.Grok.Endpoint, + GrokModel: cfg.Tools.Web.Grok.DefaultModel, + GrokMaxResults: cfg.Tools.Web.Grok.MaxResults, + GrokEnabled: cfg.Tools.Web.Grok.Enabled, Proxy: cfg.Tools.Web.Proxy, }); searchTool != nil { agent.Tools.Register(searchTool) diff --git a/pkg/config/config.go b/pkg/config/config.go index 16559a2df..28fe8d490 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -444,17 +444,19 @@ type DuckDuckGoConfig struct { MaxResults int `json:"max_results" env:"PICOCLAW_TOOLS_WEB_DUCKDUCKGO_MAX_RESULTS"` } -type PerplexityConfig struct { - Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_PERPLEXITY_ENABLED"` - APIKey string `json:"api_key" env:"PICOCLAW_TOOLS_WEB_PERPLEXITY_API_KEY"` - MaxResults int `json:"max_results" env:"PICOCLAW_TOOLS_WEB_PERPLEXITY_MAX_RESULTS"` +type GrokConfig struct { + Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_GROK_ENABLED"` + APIKey string `json:"api_key" env:"PICOCLAW_TOOLS_WEB_GROK_API_KEY"` + Endpoint string `json:"endpoint" env:"PICOCLAW_TOOLS_WEB_GROK_ENDPOINT"` + DefaultModel string `json:"default_model" env:"PICOCLAW_TOOLS_WEB_GROK_DEFAULT_MODEL"` + MaxResults int `json:"max_results" env:"PICOCLAW_TOOLS_WEB_GROK_MAX_RESULTS"` } type WebToolsConfig struct { Brave BraveConfig `json:"brave"` Tavily TavilyConfig `json:"tavily"` DuckDuckGo DuckDuckGoConfig `json:"duckduckgo"` - Perplexity PerplexityConfig `json:"perplexity"` + Grok GrokConfig `json:"grok"` // Proxy is an optional proxy URL for web tools (http/https/socks5/socks5h). // For authenticated proxies, prefer HTTP_PROXY/HTTPS_PROXY env vars instead of embedding credentials in config. Proxy string `json:"proxy,omitempty" env:"PICOCLAW_TOOLS_WEB_PROXY"` diff --git a/pkg/config/defaults.go b/pkg/config/defaults.go index cf799140d..c6e79d968 100644 --- a/pkg/config/defaults.go +++ b/pkg/config/defaults.go @@ -287,10 +287,12 @@ func DefaultConfig() *Config { Enabled: true, MaxResults: 5, }, - Perplexity: PerplexityConfig{ - Enabled: false, - APIKey: "", - MaxResults: 5, + Grok: GrokConfig{ + Enabled: false, + APIKey: "", + Endpoint: "https://api.x.ai/v1/chat/completions", + DefaultModel: "grok-4", + MaxResults: 5, }, }, Cron: CronToolsConfig{ diff --git a/pkg/tools/web.go b/pkg/tools/web.go index 8ba2a723a..7ad66dbb2 100644 --- a/pkg/tools/web.go +++ b/pkg/tools/web.go @@ -319,16 +319,25 @@ func stripTags(content string) string { return reTags.ReplaceAllString(content, "") } -type PerplexitySearchProvider struct { - apiKey string - proxy string +type GrokSearchProvider struct { + apiKey string + endpoint string + model string + proxy string } -func (p *PerplexitySearchProvider) Search(ctx context.Context, query string, count int) (string, error) { - searchURL := "https://api.perplexity.ai/chat/completions" +func (p *GrokSearchProvider) Search(ctx context.Context, query string, count int) (string, error) { + searchURL := strings.TrimSpace(p.endpoint) + if searchURL == "" { + searchURL = "https://api.x.ai/v1/chat/completions" + } + model := strings.TrimSpace(p.model) + if model == "" { + model = "grok-4" + } payload := map[string]any{ - "model": "sonar", + "model": model, "messages": []map[string]string{ { "role": "system", @@ -372,7 +381,7 @@ func (p *PerplexitySearchProvider) Search(ctx context.Context, query string, cou } if resp.StatusCode != http.StatusOK { - return "", fmt.Errorf("Perplexity API error: %s", string(body)) + return "", fmt.Errorf("Grok API error: %s", string(body)) } var searchResp struct { @@ -391,7 +400,7 @@ func (p *PerplexitySearchProvider) Search(ctx context.Context, query string, cou return fmt.Sprintf("No results for: %s", query), nil } - return fmt.Sprintf("Results for: %s (via Perplexity)\n%s", query, searchResp.Choices[0].Message.Content), nil + return fmt.Sprintf("Results for: %s (via Grok)\n%s", query, searchResp.Choices[0].Message.Content), nil } type WebSearchTool struct { @@ -409,9 +418,11 @@ type WebSearchToolOptions struct { TavilyEnabled bool DuckDuckGoMaxResults int DuckDuckGoEnabled bool - PerplexityAPIKey string - PerplexityMaxResults int - PerplexityEnabled bool + GrokAPIKey string + GrokEndpoint string + GrokModel string + GrokMaxResults int + GrokEnabled bool Proxy string } @@ -419,11 +430,16 @@ func NewWebSearchTool(opts WebSearchToolOptions) *WebSearchTool { var provider SearchProvider maxResults := 5 - // Priority: Perplexity > Brave > Tavily > DuckDuckGo - if opts.PerplexityEnabled && opts.PerplexityAPIKey != "" { - provider = &PerplexitySearchProvider{apiKey: opts.PerplexityAPIKey, proxy: opts.Proxy} - if opts.PerplexityMaxResults > 0 { - maxResults = opts.PerplexityMaxResults + // Priority: Grok > Brave > Tavily > DuckDuckGo + if opts.GrokEnabled && opts.GrokAPIKey != "" { + provider = &GrokSearchProvider{ + apiKey: opts.GrokAPIKey, + endpoint: opts.GrokEndpoint, + model: opts.GrokModel, + proxy: opts.Proxy, + } + if opts.GrokMaxResults > 0 { + maxResults = opts.GrokMaxResults } } else if opts.BraveEnabled && opts.BraveAPIKey != "" { provider = &BraveSearchProvider{apiKey: opts.BraveAPIKey, proxy: opts.Proxy}