mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
Merge branch 'main' into version
This commit is contained in:
+15
-11
@@ -177,17 +177,21 @@ func registerSharedTools(
|
||||
cfg.Tools.Web.Perplexity.APIKey(),
|
||||
cfg.Tools.Web.Perplexity.APIKeys(),
|
||||
),
|
||||
PerplexityMaxResults: cfg.Tools.Web.Perplexity.MaxResults,
|
||||
PerplexityEnabled: cfg.Tools.Web.Perplexity.Enabled,
|
||||
SearXNGBaseURL: cfg.Tools.Web.SearXNG.BaseURL,
|
||||
SearXNGMaxResults: cfg.Tools.Web.SearXNG.MaxResults,
|
||||
SearXNGEnabled: cfg.Tools.Web.SearXNG.Enabled,
|
||||
GLMSearchAPIKey: cfg.Tools.Web.GLMSearch.APIKey(),
|
||||
GLMSearchBaseURL: cfg.Tools.Web.GLMSearch.BaseURL,
|
||||
GLMSearchEngine: cfg.Tools.Web.GLMSearch.SearchEngine,
|
||||
GLMSearchMaxResults: cfg.Tools.Web.GLMSearch.MaxResults,
|
||||
GLMSearchEnabled: cfg.Tools.Web.GLMSearch.Enabled,
|
||||
Proxy: cfg.Tools.Web.Proxy,
|
||||
PerplexityMaxResults: cfg.Tools.Web.Perplexity.MaxResults,
|
||||
PerplexityEnabled: cfg.Tools.Web.Perplexity.Enabled,
|
||||
SearXNGBaseURL: cfg.Tools.Web.SearXNG.BaseURL,
|
||||
SearXNGMaxResults: cfg.Tools.Web.SearXNG.MaxResults,
|
||||
SearXNGEnabled: cfg.Tools.Web.SearXNG.Enabled,
|
||||
GLMSearchAPIKey: cfg.Tools.Web.GLMSearch.APIKey(),
|
||||
GLMSearchBaseURL: cfg.Tools.Web.GLMSearch.BaseURL,
|
||||
GLMSearchEngine: cfg.Tools.Web.GLMSearch.SearchEngine,
|
||||
GLMSearchMaxResults: cfg.Tools.Web.GLMSearch.MaxResults,
|
||||
GLMSearchEnabled: cfg.Tools.Web.GLMSearch.Enabled,
|
||||
BaiduSearchAPIKey: cfg.Tools.Web.BaiduSearch.APIKey(),
|
||||
BaiduSearchBaseURL: cfg.Tools.Web.BaiduSearch.BaseURL,
|
||||
BaiduSearchMaxResults: cfg.Tools.Web.BaiduSearch.MaxResults,
|
||||
BaiduSearchEnabled: cfg.Tools.Web.BaiduSearch.Enabled,
|
||||
Proxy: cfg.Tools.Web.Proxy,
|
||||
})
|
||||
if err != nil {
|
||||
logger.ErrorCF("agent", "Failed to create web search tool", map[string]any{"error": err.Error()})
|
||||
|
||||
+36
-7
@@ -1124,14 +1124,33 @@ func (c *GLMSearchConfig) SetAPIKey(key string) {
|
||||
c.secDirty = true
|
||||
}
|
||||
|
||||
type BaiduSearchConfig struct {
|
||||
Enabled bool `json:"enabled" env:"PICOCLAW_TOOLS_WEB_BAIDU_ENABLED"`
|
||||
BaseURL string `json:"base_url" env:"PICOCLAW_TOOLS_WEB_BAIDU_BASE_URL"`
|
||||
MaxResults int `json:"max_results" env:"PICOCLAW_TOOLS_WEB_BAIDU_MAX_RESULTS"`
|
||||
apiKey string
|
||||
secDirty bool
|
||||
}
|
||||
|
||||
// APIKey returns the Baidu search API key
|
||||
func (c *BaiduSearchConfig) APIKey() string {
|
||||
return c.apiKey
|
||||
}
|
||||
|
||||
func (c *BaiduSearchConfig) SetAPIKey(key string) {
|
||||
c.apiKey = key
|
||||
c.secDirty = true
|
||||
}
|
||||
|
||||
type WebToolsConfig struct {
|
||||
ToolConfig ` envPrefix:"PICOCLAW_TOOLS_WEB_"`
|
||||
Brave BraveConfig ` json:"brave"`
|
||||
Tavily TavilyConfig ` json:"tavily"`
|
||||
DuckDuckGo DuckDuckGoConfig ` json:"duckduckgo"`
|
||||
Perplexity PerplexityConfig ` json:"perplexity"`
|
||||
SearXNG SearXNGConfig ` json:"searxng"`
|
||||
GLMSearch GLMSearchConfig ` json:"glm_search"`
|
||||
ToolConfig ` envPrefix:"PICOCLAW_TOOLS_WEB_"`
|
||||
Brave BraveConfig ` json:"brave"`
|
||||
Tavily TavilyConfig ` json:"tavily"`
|
||||
DuckDuckGo DuckDuckGoConfig ` json:"duckduckgo"`
|
||||
Perplexity PerplexityConfig ` json:"perplexity"`
|
||||
SearXNG SearXNGConfig ` json:"searxng"`
|
||||
GLMSearch GLMSearchConfig ` json:"glm_search"`
|
||||
BaiduSearch BaiduSearchConfig ` json:"baidu_search"`
|
||||
// PreferNative controls whether to use provider-native web search when
|
||||
// the active LLM supports it (e.g. OpenAI web_search_preview). When true,
|
||||
// the client-side web_search tool is hidden to avoid duplicate search surfaces,
|
||||
@@ -1426,6 +1445,10 @@ func applySecurityConfig(cfg *Config, sec *SecurityConfig) error {
|
||||
cfg.Tools.Web.GLMSearch.apiKey = sec.Web.GLMSearch.APIKey
|
||||
}
|
||||
|
||||
if sec.Web.BaiduSearch != nil && sec.Web.BaiduSearch.APIKey != "" {
|
||||
cfg.Tools.Web.BaiduSearch.apiKey = sec.Web.BaiduSearch.APIKey
|
||||
}
|
||||
|
||||
if sec.Skills.Github != nil && sec.Skills.Github.Token != "" {
|
||||
cfg.Tools.Skills.Github.token = sec.Skills.Github.Token
|
||||
}
|
||||
@@ -1815,6 +1838,12 @@ func SaveConfig(path string, cfg *Config) error {
|
||||
}
|
||||
cfg.Tools.Web.GLMSearch.secDirty = false
|
||||
}
|
||||
if cfg.Tools.Web.BaiduSearch.secDirty {
|
||||
cfg.security.Web.BaiduSearch = &BaiduSearchSecurity{
|
||||
APIKey: cfg.Tools.Web.BaiduSearch.APIKey(),
|
||||
}
|
||||
cfg.Tools.Web.BaiduSearch.secDirty = false
|
||||
}
|
||||
if cfg.Tools.Skills.Github.secDirty {
|
||||
cfg.security.Skills.Github = &GithubSecurity{
|
||||
Token: cfg.Tools.Skills.Github.Token(),
|
||||
|
||||
@@ -419,6 +419,11 @@ func DefaultConfig() *Config {
|
||||
SearchEngine: "search_std",
|
||||
MaxResults: 5,
|
||||
},
|
||||
BaiduSearch: BaiduSearchConfig{
|
||||
Enabled: false,
|
||||
BaseURL: "https://qianfan.baidubce.com/v2/ai_search/web_search",
|
||||
MaxResults: 10,
|
||||
},
|
||||
},
|
||||
Cron: CronToolsConfig{
|
||||
ToolConfig: ToolConfig{
|
||||
|
||||
@@ -133,10 +133,11 @@ type IRCSecurity struct {
|
||||
}
|
||||
|
||||
type WebToolsSecurity struct {
|
||||
Brave *BraveSecurity `yaml:"brave,omitempty"`
|
||||
Tavily *TavilySecurity `yaml:"tavily,omitempty"`
|
||||
Perplexity *PerplexitySecurity `yaml:"perplexity,omitempty"`
|
||||
GLMSearch *GLMSearchSecurity `yaml:"glm_search,omitempty"`
|
||||
Brave *BraveSecurity `yaml:"brave,omitempty"`
|
||||
Tavily *TavilySecurity `yaml:"tavily,omitempty"`
|
||||
Perplexity *PerplexitySecurity `yaml:"perplexity,omitempty"`
|
||||
GLMSearch *GLMSearchSecurity `yaml:"glm_search,omitempty"`
|
||||
BaiduSearch *BaiduSearchSecurity `yaml:"baidu_search,omitempty"`
|
||||
}
|
||||
|
||||
type BraveSecurity struct {
|
||||
@@ -155,6 +156,10 @@ type GLMSearchSecurity struct {
|
||||
APIKey string `yaml:"api_key,omitempty"`
|
||||
}
|
||||
|
||||
type BaiduSearchSecurity struct {
|
||||
APIKey string `yaml:"api_key,omitempty" env:"PICOCLAW_TOOLS_WEB_BAIDU_API_KEY"`
|
||||
}
|
||||
|
||||
type SkillsSecurity struct {
|
||||
Github *GithubSecurity `yaml:"github,omitempty"`
|
||||
ClawHub *ClawHubSecurity `yaml:"clawhub,omitempty"`
|
||||
|
||||
+121
-22
@@ -613,39 +613,124 @@ func (p *GLMSearchProvider) Search(ctx context.Context, query string, count int)
|
||||
return strings.Join(lines, "\n"), nil
|
||||
}
|
||||
|
||||
type BaiduSearchProvider struct {
|
||||
apiKey string
|
||||
baseURL string
|
||||
proxy string
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
func (p *BaiduSearchProvider) Search(ctx context.Context, query string, count int) (string, error) {
|
||||
searchURL := p.baseURL
|
||||
if searchURL == "" {
|
||||
searchURL = "https://qianfan.baidubce.com/v2/ai_search/web_search"
|
||||
}
|
||||
|
||||
payload := map[string]any{
|
||||
"messages": []map[string]string{
|
||||
{
|
||||
"role": "user",
|
||||
"content": query,
|
||||
},
|
||||
},
|
||||
"search_source": "baidu_search_v2",
|
||||
"resource_type_filter": []map[string]any{{"type": "web", "top_k": count}},
|
||||
}
|
||||
|
||||
bodyBytes, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to marshal payload: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", searchURL, bytes.NewReader(bodyBytes))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create request: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+p.apiKey)
|
||||
|
||||
resp, err := p.client.Do(req)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("baidu search request failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read response: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return "", fmt.Errorf("baidu search API error %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
var result struct {
|
||||
References []struct {
|
||||
Title string `json:"title"`
|
||||
URL string `json:"url"`
|
||||
Content string `json:"content"`
|
||||
} `json:"references"`
|
||||
}
|
||||
if err := json.Unmarshal(body, &result); err != nil {
|
||||
return "", fmt.Errorf("failed to parse response: %w", err)
|
||||
}
|
||||
|
||||
if len(result.References) == 0 {
|
||||
return fmt.Sprintf("No results for: %s", query), nil
|
||||
}
|
||||
|
||||
lines := []string{fmt.Sprintf("Results for: %s (via Baidu Search)", query)}
|
||||
for i, item := range result.References {
|
||||
if i >= count {
|
||||
break
|
||||
}
|
||||
lines = append(lines, fmt.Sprintf("%d. %s\n %s", i+1, item.Title, item.URL))
|
||||
if item.Content != "" {
|
||||
lines = append(lines, fmt.Sprintf(" %s", item.Content))
|
||||
}
|
||||
}
|
||||
|
||||
return strings.Join(lines, "\n"), nil
|
||||
}
|
||||
|
||||
type WebSearchTool struct {
|
||||
provider SearchProvider
|
||||
maxResults int
|
||||
}
|
||||
|
||||
type WebSearchToolOptions struct {
|
||||
BraveAPIKeys []string
|
||||
BraveMaxResults int
|
||||
BraveEnabled bool
|
||||
TavilyAPIKeys []string
|
||||
TavilyBaseURL string
|
||||
TavilyMaxResults int
|
||||
TavilyEnabled bool
|
||||
DuckDuckGoMaxResults int
|
||||
DuckDuckGoEnabled bool
|
||||
PerplexityAPIKeys []string
|
||||
PerplexityMaxResults int
|
||||
PerplexityEnabled bool
|
||||
SearXNGBaseURL string
|
||||
SearXNGMaxResults int
|
||||
SearXNGEnabled bool
|
||||
GLMSearchAPIKey string
|
||||
GLMSearchBaseURL string
|
||||
GLMSearchEngine string
|
||||
GLMSearchMaxResults int
|
||||
GLMSearchEnabled bool
|
||||
Proxy string
|
||||
BraveAPIKeys []string
|
||||
BraveMaxResults int
|
||||
BraveEnabled bool
|
||||
TavilyAPIKeys []string
|
||||
TavilyBaseURL string
|
||||
TavilyMaxResults int
|
||||
TavilyEnabled bool
|
||||
DuckDuckGoMaxResults int
|
||||
DuckDuckGoEnabled bool
|
||||
PerplexityAPIKeys []string
|
||||
PerplexityMaxResults int
|
||||
PerplexityEnabled bool
|
||||
SearXNGBaseURL string
|
||||
SearXNGMaxResults int
|
||||
SearXNGEnabled bool
|
||||
GLMSearchAPIKey string
|
||||
GLMSearchBaseURL string
|
||||
GLMSearchEngine string
|
||||
GLMSearchMaxResults int
|
||||
GLMSearchEnabled bool
|
||||
BaiduSearchAPIKey string
|
||||
BaiduSearchBaseURL string
|
||||
BaiduSearchMaxResults int
|
||||
BaiduSearchEnabled bool
|
||||
Proxy string
|
||||
}
|
||||
|
||||
func NewWebSearchTool(opts WebSearchToolOptions) (*WebSearchTool, error) {
|
||||
var provider SearchProvider
|
||||
maxResults := 5
|
||||
// Priority: Perplexity > Brave > SearXNG > Tavily > DuckDuckGo > GLM Search
|
||||
// Priority: Perplexity > Brave > SearXNG > Tavily > DuckDuckGo > Baidu Search > GLM Search
|
||||
if opts.PerplexityEnabled && len(opts.PerplexityAPIKeys) > 0 {
|
||||
client, err := utils.CreateHTTPClient(opts.Proxy, perplexityTimeout)
|
||||
if err != nil {
|
||||
@@ -696,6 +781,20 @@ func NewWebSearchTool(opts WebSearchToolOptions) (*WebSearchTool, error) {
|
||||
if opts.DuckDuckGoMaxResults > 0 {
|
||||
maxResults = opts.DuckDuckGoMaxResults
|
||||
}
|
||||
} else if opts.BaiduSearchEnabled && opts.BaiduSearchAPIKey != "" {
|
||||
client, err := utils.CreateHTTPClient(opts.Proxy, perplexityTimeout)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create HTTP client for Baidu Search: %w", err)
|
||||
}
|
||||
provider = &BaiduSearchProvider{
|
||||
apiKey: opts.BaiduSearchAPIKey,
|
||||
baseURL: opts.BaiduSearchBaseURL,
|
||||
proxy: opts.Proxy,
|
||||
client: client,
|
||||
}
|
||||
if opts.BaiduSearchMaxResults > 0 {
|
||||
maxResults = opts.BaiduSearchMaxResults
|
||||
}
|
||||
} else if opts.GLMSearchEnabled && opts.GLMSearchAPIKey != "" {
|
||||
client, err := utils.CreateHTTPClient(opts.Proxy, searchTimeout)
|
||||
if err != nil {
|
||||
|
||||
Reference in New Issue
Block a user