web_search.go

 1package tools
 2
 3import (
 4	"context"
 5	_ "embed"
 6	"net/http"
 7	"time"
 8
 9	"charm.land/fantasy"
10)
11
12//go:embed web_search.md
13var webSearchToolDescription []byte
14
15// NewWebSearchTool creates a web search tool for sub-agents (no permissions needed).
16func NewWebSearchTool(client *http.Client) fantasy.AgentTool {
17	if client == nil {
18		client = &http.Client{
19			Timeout: 30 * time.Second,
20			Transport: &http.Transport{
21				MaxIdleConns:        100,
22				MaxIdleConnsPerHost: 10,
23				IdleConnTimeout:     90 * time.Second,
24			},
25		}
26	}
27
28	return fantasy.NewParallelAgentTool(
29		WebSearchToolName,
30		string(webSearchToolDescription),
31		func(ctx context.Context, params WebSearchParams, call fantasy.ToolCall) (fantasy.ToolResponse, error) {
32			if params.Query == "" {
33				return fantasy.NewTextErrorResponse("query is required"), nil
34			}
35
36			maxResults := params.MaxResults
37			if maxResults <= 0 {
38				maxResults = 10
39			}
40			if maxResults > 20 {
41				maxResults = 20
42			}
43
44			results, err := searchDuckDuckGo(ctx, client, params.Query, maxResults)
45			if err != nil {
46				return fantasy.NewTextErrorResponse("Failed to search: " + err.Error()), nil
47			}
48
49			return fantasy.NewTextResponse(formatSearchResults(results)), nil
50		})
51}