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}