ask-web-search 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c390a4cff0c0fbb46d883f71912c424eaf1c56a131f812c48723b6c1fff160d9
4
- data.tar.gz: a926c135fd2a95191a583bb62bd8fb8b29ffda65eb477324c7ebef8abd41e3bf
3
+ metadata.gz: fe356e9cdc2a7ee736d51ad93d36dfcaaef72168b0a4b3d98bc19945b919dd4a
4
+ data.tar.gz: e334c6903ab48de0a78fee36c07933487dcab49648cc31bbe3adfd46653de01a
5
5
  SHA512:
6
- metadata.gz: 555e34c32eec8a00f2bd984cc306e463de6c25b5901b16bea73c4bd004274d5c9b3671c655a79054d807f4e1be247c3a039578aea1ac4506a4daed156b4efbe7
7
- data.tar.gz: 33f2a4597debe966b46ae98dcb6b85d44c3ab399fc65917eff99275c6183c327c04bc1016eb1af8437f47e3d391e5749e5f4d320b7964b920d60890c886ce02d
6
+ metadata.gz: d1fc95c583e7fc95a15ef32b7e1253302c6e2ba1edee41781445c912a1d8631cba839dac044fb94a1cc20d51d219960102cccec5d75eecbf1cef92de0f92e559
7
+ data.tar.gz: f56f4d8df11e3de5de981e0f5d182d8491cb12840f12dc4e546986fd9c4f6b82668313cd4270468215f460e5a29ece4ecb0e28bc4c301c88dfd39dc1656ae7e6
data/README.md CHANGED
@@ -1,8 +1,12 @@
1
1
  # ask-web-search
2
2
 
3
- A web search tool for the ask-rb ecosystem. Provides `Ask::Tools::WebSearch`, which
4
- searches the web via a local [SearXNG](https://docs.searxng.org/) instance and returns
5
- formatted results for LLM consumption.
3
+ [![Gem Version](https://badge.fury.io/rb/ask-web-search.svg)](https://badge.fury.io/rb/ask-web-search)
4
+
5
+ A web search tool for the ask-rb ecosystem. It provides
6
+ `Ask::Tools::WebSearch`, which searches the web via a local
7
+ [SearXNG](https://docs.searxng.org/) instance and returns numbered markdown
8
+ results for LLM consumption. It has no Rails dependencies; it depends only on
9
+ ask-tools.
6
10
 
7
11
  ## Prerequisites
8
12
 
@@ -14,21 +18,11 @@ Start one with Docker:
14
18
  docker run -d --name searxng -p 8888:8080 searxng/searxng
15
19
  ```
16
20
 
17
- Or use the provided `docker-compose.yml`:
18
-
19
- ```sh
20
- cd searxng
21
- docker compose up -d
22
- ```
21
+ Or use the provided `docker-compose.yml` in the `searxng` directory of this
22
+ repository:
23
23
 
24
24
  ## Installation
25
25
 
26
- ```sh
27
- gem install ask-web-search
28
- ```
29
-
30
- Or in your Gemfile:
31
-
32
26
  ```ruby
33
27
  gem "ask-web-search"
34
28
  ```
@@ -43,7 +37,7 @@ export SEARXNG_URL=http://localhost:8888
43
37
 
44
38
  Defaults to `http://localhost:8888`.
45
39
 
46
- ## Usage
40
+ ## Quick Start
47
41
 
48
42
  ```ruby
49
43
  require "ask/web_search"
@@ -51,35 +45,12 @@ require "ask/web_search"
51
45
  tool = Ask::Tools::WebSearch.new
52
46
  result = tool.execute(query: "ruby programming language")
53
47
  puts result
54
- # => 1. Ruby — A Programmer's Best Friend
55
- # https://www.ruby-lang.org
56
- # Ruby is a dynamic, open-source programming language...
57
- ```
58
-
59
- ### With ask-rb Chat
60
-
61
- ```ruby
62
- chat = Ask::Agent::Chat.new(
63
- model: "deepseek-v4-flash",
64
- tools: [Ask::Tools::WebSearch.new]
65
- )
66
-
67
- chat.ask("What's the latest on AI? Search the web.")
68
- ```
69
-
70
- ### With ask-rb Agent
71
-
72
- ```ruby
73
- agent = Ask::Agent.new(tools: [Ask::Tools::WebSearch])
74
- agent.run("Find recent news about SpaceX")
75
48
  ```
76
49
 
77
- ## Output Format
78
-
79
50
  Results are returned as a numbered markdown-like string:
80
51
 
81
52
  ```
82
- 1. Ruby A Programmer's Best Friend
53
+ 1. Ruby - A Programmer's Best Friend
83
54
  https://www.ruby-lang.org
84
55
  Ruby is a dynamic, open-source programming language...
85
56
 
@@ -90,11 +61,16 @@ Results are returned as a numbered markdown-like string:
90
61
 
91
62
  If no results are found, returns `"No results found."`.
92
63
 
64
+ ## Full documentation
65
+
66
+ The full ask-rb documentation lives at https://ask-rb.github.io/ask-docs.
67
+ [Core: Web Search](https://ask-rb.github.io/ask-docs/core/web-search) covers
68
+ ask-web-search in depth, including the ask-agent integration and the MCP
69
+ server. API reference: https://ask-rb.github.io/ask-docs/reference/api.
70
+
93
71
  ## Development
94
72
 
95
- ```sh
96
- git clone https://github.com/ask-rb/ask-web-search
97
- cd ask-web-search
73
+ ```
98
74
  bundle install
99
75
  bundle exec rake test
100
76
  ```
@@ -1,17 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "ask-tools"
2
- require "net/http"
3
- require "uri"
4
- require "json"
5
4
 
6
5
  module Ask
7
6
  module Tools
7
+ # The native agent tool for web search — the Ask::Tool framing of the
8
+ # library. Consumed WITHOUT MCP by agent frameworks (ask-agent's
9
+ # `tool: :web_search`, ask-app-server, llm-proxy) that resolve tools
10
+ # from the Ask::Tools registry. A pure adapter: the capability
11
+ # (search, parsing, formatting) lives in Ask::WebSearch, and this file
12
+ # loads only when ask-tools is present (see lib/ask/web_search.rb).
8
13
  class WebSearch < Ask::Tool
9
14
  def self.searxng_url
10
- @searxng_url || ENV.fetch("SEARXNG_URL", "http://localhost:8888")
15
+ Ask::WebSearch.searxng_url
11
16
  end
12
17
 
13
18
  def self.searxng_url=(url)
14
- @searxng_url = url
19
+ Ask::WebSearch.searxng_url = url
15
20
  end
16
21
 
17
22
  description "Search the web for current information. Use this to get up-to-date results, recent events, or facts that may have changed."
@@ -25,49 +30,10 @@ module Ask
25
30
  )
26
31
 
27
32
  def execute(query:)
28
- results = search(query)
29
- Ask::Result.ok(data: format_results(results))
30
- end
31
-
32
- private
33
-
34
- def search(query)
35
- uri = URI("#{self.class.searxng_url}/search?q=#{URI.encode_www_form_component(query)}&format=json")
36
- http = Net::HTTP.new(uri.host, uri.port)
37
- http.open_timeout = 5
38
- http.read_timeout = 10
39
- req = Net::HTTP::Get.new(uri)
40
- req["User-Agent"] = "ask-web-search/1.0"
41
- res = http.request(req)
42
- raise "SearXNG returned #{res.code}: #{res.body}" unless res.code.start_with?("2")
43
-
44
- data = JSON.parse(res.body)
45
- parse_results(data)
46
- end
47
-
48
- def parse_results(data)
49
- results = []
50
- data.fetch("results", []).each do |r|
51
- results << { url: r["url"], title: r["title"], content: r["content"] }
52
- end
53
- data.fetch("infoboxes", []).each do |ib|
54
- results << { url: ib["id"], title: ib["infobox"], content: ib["content"] }
55
- end
56
- results.uniq { |r| r[:url] }
57
- end
58
-
59
- def format_results(results)
60
- return "No results found." if results.empty?
61
-
62
- results.each_with_index.map do |r, i|
63
- line = "#{i + 1}. #{r[:title]}"
64
- line += "\n #{r[:url]}"
65
- line += "\n #{r[:content]}" if r[:content] && !r[:content].empty?
66
- line
67
- end.join("\n\n")
33
+ Ask::Result.ok(data: Ask::WebSearch.search(query))
68
34
  end
69
35
  end
70
36
  end
71
37
  end
72
38
 
73
- Ask::Tools.register(Ask::Tools::WebSearch) if defined?(Ask::Tools)
39
+ Ask::Tools.register(Ask::Tools::WebSearch)
@@ -1,5 +1,5 @@
1
1
  module Ask
2
2
  module WebSearch
3
- VERSION = "0.2.3"
3
+ VERSION = "0.3.0"
4
4
  end
5
5
  end
@@ -1,2 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "net/http"
4
+ require "uri"
5
+ require "json"
1
6
  require_relative "web_search/version"
2
- require_relative "web_search/tool"
7
+
8
+ module Ask
9
+ # Searches the web via a local SearXNG instance and returns the results
10
+ # as clean, numbered markdown for LLM consumption. The capability layer:
11
+ # one entry point (WebSearch.search) and a configurable endpoint. Tool
12
+ # framing — name, parameter schema, result wrapping — lives with the
13
+ # consumers (the MCP server, the agents) that call this library.
14
+ module WebSearch
15
+ # Local SearXNG endpoint. Defaults to the standard local instance;
16
+ # override with SEARXNG_URL or WebSearch.searxng_url=.
17
+ def self.searxng_url
18
+ @searxng_url || ENV.fetch("SEARXNG_URL", "http://localhost:8888")
19
+ end
20
+
21
+ def self.searxng_url=(url)
22
+ @searxng_url = url
23
+ end
24
+
25
+ # Searches +query+ and returns the results as numbered markdown
26
+ # ("No results found." when SearXNG found nothing). Raises on
27
+ # connection/HTTP failures — the caller decides how to surface them.
28
+ def self.search(query)
29
+ format_results(search_results(query))
30
+ end
31
+
32
+ # The raw result list: { url:, title:, content: } entries from the
33
+ # results and infoboxes, deduplicated by url.
34
+ def self.search_results(query)
35
+ uri = URI("#{searxng_url}/search?q=#{URI.encode_www_form_component(query)}&format=json")
36
+ http = Net::HTTP.new(uri.host, uri.port)
37
+ http.open_timeout = 5
38
+ http.read_timeout = 10
39
+ req = Net::HTTP::Get.new(uri)
40
+ req["User-Agent"] = "ask-web-search/#{Ask::WebSearch::VERSION}"
41
+ res = http.request(req)
42
+ raise "SearXNG returned #{res.code}: #{res.body}" unless res.code.start_with?("2")
43
+
44
+ parse_results(JSON.parse(res.body))
45
+ end
46
+
47
+ def self.parse_results(data)
48
+ results = []
49
+ data.fetch("results", []).each do |r|
50
+ results << { url: r["url"], title: r["title"], content: r["content"] }
51
+ end
52
+ data.fetch("infoboxes", []).each do |ib|
53
+ results << { url: ib["id"], title: ib["infobox"], content: ib["content"] }
54
+ end
55
+ results.uniq { |r| r[:url] }
56
+ end
57
+ private_class_method :parse_results
58
+
59
+ def self.format_results(results)
60
+ return "No results found." if results.empty?
61
+
62
+ results.each_with_index.map do |r, i|
63
+ line = "#{i + 1}. #{r[:title]}"
64
+ line += "\n #{r[:url]}"
65
+ line += "\n #{r[:content]}" if r[:content] && !r[:content].empty?
66
+ line
67
+ end.join("\n\n")
68
+ end
69
+ private_class_method :format_results
70
+ end
71
+ end
72
+
73
+ # The native agent tool (Ask::Tools::WebSearch) is an OPTIONAL integration:
74
+ # it loads and registers only when ask-tools is present. The library works
75
+ # standalone — consumers that only call WebSearch.search pay nothing —
76
+ # while agent frameworks (ask-agent, ask-app-server, llm-proxy) all ship
77
+ # ask-tools and get the registry tool with no extra step. Only the
78
+ # ask-tools miss is swallowed; any other LoadError is real.
79
+ begin
80
+ require "ask-tools"
81
+ require_relative "web_search/tool"
82
+ rescue LoadError => e
83
+ raise unless e.path == "ask-tools"
84
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ask-web-search
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.3
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kaka Ruto
@@ -16,7 +16,7 @@ dependencies:
16
16
  - - ">="
17
17
  - !ruby/object:Gem::Version
18
18
  version: '0.1'
19
- type: :runtime
19
+ type: :development
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
@@ -79,9 +79,10 @@ dependencies:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
81
  version: '13.0'
82
- description: Provides Ask::Tools::WebSearch, a tool that searches the web via SearXNG
83
- (local instance). Works with any ask-rb chat or agent. Configure endpoint via SEARXNG_URL
84
- env var.
82
+ description: Searches the web via a local SearXNG instance and returns the results
83
+ as clean numbered markdown. The capability layer (WebSearch.search); the native
84
+ Ask::Tools::WebSearch agent tool is an optional integration that registers when
85
+ ask-tools is present. Configure endpoint via SEARXNG_URL env var.
85
86
  email:
86
87
  - kaka@myrrlabs.com
87
88
  executables: []
@@ -117,5 +118,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
118
  requirements: []
118
119
  rubygems_version: 4.0.3
119
120
  specification_version: 4
120
- summary: Web search tool for the ask-rb ecosystem
121
+ summary: Web search library for the ask-rb ecosystem
121
122
  test_files: []