Class: Rubino::Tools::WebSearchTool

Inherits:
Base
  • Object
show all
Defined in:
lib/rubino/tools/websearch_tool.rb

Overview

Tool for performing web searches via external search APIs.

Backends, in priority order:

1. Tavily        (TAVILY_API_KEY)   — high-quality, preferred
2. SearXNG       (SEARXNG_URL)      — self-hosted, full web index
3. DuckDuckGo Instant Answer JSON   — keyless DEFAULT (no key needed)

Why not scrape html/lite.duckduckgo.com keyless? DuckDuckGo now serves an anomaly/bot-challenge page (zero results) to datacenter egress IPs, so the old single-regex HTML scrape returned “No results” 100% of the time — a silent failure that looked like success. The Instant Answer JSON API (api.duckduckgo.com) is keyless, returns structured JSON, and is NOT bot-walled, so it is the robust keyless default. Its coverage is narrower (topic/entity answers, not a full web index): when it yields nothing we degrade to an EXPLICIT “search unavailable” message that points the user at TAVILY_API_KEY / SEARXNG_URL — never a silent zero-results-that-looks-like-a-real-answer.

Instance Attribute Summary

Attributes inherited from Base

#cancel_token, #read_tracker, #stream_chunk, #stream_kind

Instance Method Summary collapse

Methods inherited from Base

#cancellation_requested?, #emit_chunk, #risky?, #to_tool_definition, workspace_root, workspace_roots

Instance Method Details

#call(arguments) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/rubino/tools/websearch_tool.rb', line 63

def call(arguments)
  query = arguments["query"] || arguments[:query]
  max_results = arguments["max_results"] || arguments[:max_results] || 5

  if ENV["TAVILY_API_KEY"]
    search_tavily(query, max_results)
  elsif ENV["SEARXNG_URL"]
    search_searxng(query, max_results)
  else
    search_ddg(query, max_results)
  end
rescue StandardError => e
  "Search error: #{e.message}"
end

#config_keyObject

Gated by ‘tools.web` (shared with webfetch), not `tools.websearch`.



32
33
34
# File 'lib/rubino/tools/websearch_tool.rb', line 32

def config_key
  "web"
end

#descriptionObject



36
37
38
39
40
# File 'lib/rubino/tools/websearch_tool.rb', line 36

def description
  "Search the web for information. Returns relevant results with titles, " \
    "URLs, and snippets. Useful for finding documentation, researching " \
    "dependencies, and answering questions about external topics."
end

#input_schemaObject



42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# File 'lib/rubino/tools/websearch_tool.rb', line 42

def input_schema
  {
    type: "object",
    properties: {
      query: {
        type: "string",
        description: "The search query"
      },
      max_results: {
        type: "integer",
        description: "Maximum number of results (default: 5)"
      }
    },
    required: %w[query]
  }
end

#nameObject



27
28
29
# File 'lib/rubino/tools/websearch_tool.rb', line 27

def name
  "websearch"
end

#risk_levelObject



59
60
61
# File 'lib/rubino/tools/websearch_tool.rb', line 59

def risk_level
  :low
end