package websearch import ( "context" "encoding/json" "fmt" "net/http" "net/url" "strings" "time" "git.yeij.top/AskaEth/Cyrene_v2/internal/plugin" ) type Plugin struct { searxngURL string client *http.Client } func New(url string) *Plugin { return &Plugin{searxngURL: strings.TrimRight(url, "/"), client: &http.Client{Timeout: 15 * time.Second}} } func (p *Plugin) Name() string { return "web-search" } func (p *Plugin) Version() string { return "1.0.0" } func (p *Plugin) Description() string { return "网络搜索" } func (p *Plugin) Init(_ context.Context, _ plugin.PluginConfig) error { return nil } func (p *Plugin) Close() error { return nil } func (p *Plugin) Enabled() bool { return p.searxngURL != "" } func (p *Plugin) Tools() []plugin.ToolDefinition { return []plugin.ToolDefinition{{ Name: "web_search", Description: "Search the internet via SearXNG. Returns top results with title, URL and snippet.", Parameters: map[string]any{ "type": "object", "properties": map[string]any{ "query": map[string]string{"type": "string", "description": "Search query"}, }, "required": []string{"query"}, }, }} } type searxResult struct { Title string `json:"title"` URL string `json:"url"` Content string `json:"content"` } func (p *Plugin) Execute(ctx context.Context, _ string, params map[string]any) (string, error) { query, _ := params["query"].(string) if query == "" { return "", fmt.Errorf("missing query") } reqURL := fmt.Sprintf("%s/search?q=%s&format=json", p.searxngURL, url.QueryEscape(query)) req, err := http.NewRequestWithContext(ctx, "GET", reqURL, nil) if err != nil { return "", err } resp, err := p.client.Do(req) if err != nil { return "", fmt.Errorf("search request: %w", err) } defer resp.Body.Close() if resp.StatusCode != 200 { return "", fmt.Errorf("search API %d", resp.StatusCode) } var result struct { Results []searxResult `json:"results"` } if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return "", fmt.Errorf("parse results: %w", err) } if len(result.Results) == 0 { return fmt.Sprintf("搜索 %q 没有结果", query), nil } var sb strings.Builder fmt.Fprintf(&sb, "搜索 %q 的结果:\n", query) for i, r := range result.Results { if i >= 5 { break } fmt.Fprintf(&sb, "%d. %s\n %s\n %s\n", i+1, r.Title, r.URL, r.Content) } return sb.String(), nil }