Class: RubyLLM::Toolbox::Search::SearXNG

Inherits:
Object
  • Object
show all
Defined in:
lib/ruby_llm/toolbox/search/searxng.rb

Overview

SearXNG adapter — a self-hosted, keyless metasearch alternative. Point it at your own instance with config.searxng_url; no API key or third-party account is involved, which is the whole appeal. SearXNG’s JSON response often carries instant “answers”, which are surfaced as the answer field.

Select it with config.search_adapter = :searxng and config.searxng_url.

The base URL is operator-configured infrastructure (frequently on a private network), so it is deliberately NOT run through the SSRF guard: reaching an internal SearXNG host is the intended behavior, not an attack.

Instance Method Summary collapse

Constructor Details

#initialize(base_url:, user_agent: nil, timeout: 10) ⇒ SearXNG

Returns a new instance of SearXNG.



22
23
24
25
26
# File 'lib/ruby_llm/toolbox/search/searxng.rb', line 22

def initialize(base_url:, user_agent: nil, timeout: 10)
  @base_url = base_url.to_s.sub(%r{/+\z}, "")
  @user_agent = user_agent
  @timeout = timeout
end

Instance Method Details

#get_json(url, params) ⇒ Object

Seam for tests.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/ruby_llm/toolbox/search/searxng.rb', line 41

def get_json(url, params)
  uri = URI.parse(url)
  uri.query = URI.encode_www_form(params)
  request = Net::HTTP::Get.new(uri)
  request["Accept"] = "application/json"
  request["User-Agent"] = @user_agent if @user_agent

  response = Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == "https",
                             open_timeout: @timeout, read_timeout: @timeout) do |http|
    http.request(request)
  end

  raise Error, "SearXNG returned HTTP #{response.code}" unless response.is_a?(Net::HTTPSuccess)

  JSON.parse(response.body)
rescue JSON::ParserError => e
  raise Error, "invalid JSON from SearXNG (#{e.message}) — is format=json enabled on the instance?"
rescue SocketError, Net::OpenTimeout, Net::ReadTimeout => e
  raise Error, e.message
end

#search(query, max_results: 5) ⇒ Object

Raises:



28
29
30
31
32
33
34
35
36
37
38
# File 'lib/ruby_llm/toolbox/search/searxng.rb', line 28

def search(query, max_results: 5)
  raise Error, "missing SearXNG URL (set config.searxng_url)" if @base_url.empty?

  data = get_json("#{@base_url}/search", q: query, format: "json")

  results = Array(data["results"]).first(max_results).map do |r|
    { title: r["title"], url: r["url"], content: r["content"] }
  end
  answer = Array(data["answers"]).map(&:to_s).reject(&:empty?).join(" ")
  { answer: (answer.empty? ? nil : answer), results: results }
end