ask-web-search 0.8.0 → 0.9.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 +4 -4
- data/README.md +29 -22
- data/lib/ask/web_search/version.rb +1 -1
- data/lib/ask/web_search.rb +74 -27
- metadata +7 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b1b6fbfe4321cb25a1da21bfcdb45c3a4817c50c27edae5adc429b6383950a2d
|
|
4
|
+
data.tar.gz: fb594f9d216c1a474248f5c2ac2209404fae1566d14c8863bb2e1202c3dcfa5b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b2517586f4020e29f66c2d0130cb315395db80209896ef1203af37f2c92c2b6b59b8b9380bf4646c3217677eb70b4bec22a30ceb92460dc072aba8f8c2f12bee
|
|
7
|
+
data.tar.gz: e273bea7c63d08c06957e9b7cf8562b951a563ad8dc0c6d42258389995f2d057a58743db01c6fc53082fc9f568551ab614e737fb744c5b4ea4a84c6c61466fb7
|
data/README.md
CHANGED
|
@@ -9,21 +9,9 @@ depends only on ask-tools.
|
|
|
9
9
|
|
|
10
10
|
## Backends
|
|
11
11
|
|
|
12
|
-
Two interchangeable backends
|
|
12
|
+
Two interchangeable backends:
|
|
13
13
|
|
|
14
|
-
1. **
|
|
15
|
-
[agent.tinyfish.ai](https://agent.tinyfish.ai/api-keys) and export it:
|
|
16
|
-
|
|
17
|
-
```sh
|
|
18
|
-
export TINYFISH_API_KEY=...
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
That's the whole setup — no Docker, no SearXNG instance. Live
|
|
22
|
-
browser-rendered results, freshness windows, and the news /
|
|
23
|
-
research-paper verticals all work out of the box.
|
|
24
|
-
|
|
25
|
-
2. **SearXNG (self-hosted).** For users who want queries to stay local,
|
|
26
|
-
and as the automatic fallback when TinyFish fails: a running
|
|
14
|
+
1. **SearXNG (the default — for everyone).** Queries go to a local
|
|
27
15
|
[SearXNG](https://docs.searxng.org/) instance, default
|
|
28
16
|
`http://localhost:8888`. Start one with Docker:
|
|
29
17
|
|
|
@@ -34,14 +22,32 @@ Two interchangeable backends, chosen automatically at call time:
|
|
|
34
22
|
Or use the provided `docker-compose.yml` in the `searxng` directory of
|
|
35
23
|
this repository.
|
|
36
24
|
|
|
37
|
-
|
|
25
|
+
2. **TinyFish (opt-in — for people who'd rather hold an API key than set
|
|
26
|
+
up SearXNG).** Two steps: select the backend, and provide a key.
|
|
27
|
+
|
|
28
|
+
```sh
|
|
29
|
+
export SEARCH_BACKEND=tinyfish
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Get a free key (no credit card) at
|
|
33
|
+
[agent.tinyfish.ai](https://agent.tinyfish.ai/api-keys) and store it —
|
|
34
|
+
either in `~/.ask/credentials.yml` via ask-auth (one line:
|
|
35
|
+
`tinyfish_api_key: <key>`, file is 0600) or `export
|
|
36
|
+
TINYFISH_API_KEY=...`. Selecting tinyfish without a key raises an
|
|
37
|
+
onboarding error that says exactly that; holding a key without
|
|
38
|
+
selecting does nothing — SearXNG stays the default.
|
|
39
|
+
|
|
40
|
+
Selection precedence (first match wins):
|
|
41
|
+
|
|
42
|
+
1. `TINYFISH_SEARCH=0` — a hard-off that forces SearXNG
|
|
43
|
+
2. `Ask::WebSearch.backend = :tinyfish | :searxng` (in code)
|
|
44
|
+
3. `SEARCH_BACKEND=tinyfish | searxng` (env)
|
|
45
|
+
4. default → SearXNG
|
|
38
46
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
- `TINYFISH_SEARCH=0` disables TinyFish outright (SearXNG only, even
|
|
44
|
-
with a key).
|
|
47
|
+
When the tinyfish backend fails (network, rate limit) and a SearXNG
|
|
48
|
+
endpoint is explicitly configured, the call falls back to SearXNG
|
|
49
|
+
automatically — combined errors surface both failures. Invalid backend
|
|
50
|
+
names raise `ArgumentError` listing the valid ones.
|
|
45
51
|
|
|
46
52
|
## Installation
|
|
47
53
|
|
|
@@ -52,7 +58,8 @@ gem "ask-web-search"
|
|
|
52
58
|
## Configuration
|
|
53
59
|
|
|
54
60
|
```sh
|
|
55
|
-
export
|
|
61
|
+
export SEARCH_BACKEND=tinyfish # opt in to TinyFish (default: searxng)
|
|
62
|
+
export TINYFISH_API_KEY=... # TinyFish key (or tinyfish_api_key: in ~/.ask/credentials.yml)
|
|
56
63
|
export SEARXNG_URL=http://localhost:8888 # SearXNG endpoint (default shown)
|
|
57
64
|
```
|
|
58
65
|
|
data/lib/ask/web_search.rb
CHANGED
|
@@ -7,13 +7,15 @@ require_relative "web_search/version"
|
|
|
7
7
|
|
|
8
8
|
module Ask
|
|
9
9
|
# Searches the web and returns the results as clean, numbered markdown
|
|
10
|
-
# for LLM consumption. Two interchangeable backends:
|
|
11
|
-
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
14
|
-
# fails
|
|
15
|
-
#
|
|
16
|
-
#
|
|
10
|
+
# for LLM consumption. Two interchangeable backends: a local SearXNG
|
|
11
|
+
# instance — the default path for everyone — and TinyFish's hosted
|
|
12
|
+
# search API, strictly opt-in (SEARCH_BACKEND=tinyfish + a free API
|
|
13
|
+
# key) for users who'd rather hold a key than run SearXNG. When the
|
|
14
|
+
# tinyfish backend fails, an explicitly configured SearXNG endpoint is
|
|
15
|
+
# still the automatic fallback. The capability layer: one entry point
|
|
16
|
+
# (WebSearch.search). Tool framing — name, parameter schema, result
|
|
17
|
+
# wrapping — lives with the consumers (the MCP server, the agents)
|
|
18
|
+
# that call this library.
|
|
17
19
|
module WebSearch
|
|
18
20
|
# Search errors that carry engine diagnostics — the agent needs to
|
|
19
21
|
# know *which* engines failed and *why* (CAPTCHA, timeout, suspended)
|
|
@@ -46,12 +48,15 @@ module Ask
|
|
|
46
48
|
# unfiltered.
|
|
47
49
|
TIME_RANGES = %w[day week month year].freeze
|
|
48
50
|
|
|
49
|
-
# TinyFish's hosted search endpoint —
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
51
|
+
# TinyFish's hosted search endpoint — available when the tinyfish
|
|
52
|
+
# backend is selected (SEARCH_BACKEND=tinyfish). The bare host root
|
|
53
|
+
# per the official docs curl example; the /client.search.query path
|
|
54
|
+
# seen on the marketing pages 404s.
|
|
53
55
|
TINYFISH_SEARCH_URL = "https://api.search.tinyfish.ai"
|
|
54
56
|
|
|
57
|
+
# The selectable backends (see .backend for how one is chosen).
|
|
58
|
+
BACKENDS = %w[searxng tinyfish].freeze
|
|
59
|
+
|
|
55
60
|
# Our time_range → TinyFish's recency_minutes (SearXNG's month/year
|
|
56
61
|
# are approximate engine filters anyway; these are the equivalents).
|
|
57
62
|
TIME_RANGE_TO_MINUTES = {
|
|
@@ -72,6 +77,41 @@ module Ask
|
|
|
72
77
|
"research_paper" => "research_paper"
|
|
73
78
|
}.freeze
|
|
74
79
|
|
|
80
|
+
# The search backend — :searxng (the default path for everyone) or
|
|
81
|
+
# :tinyfish (opt-in for users who'd rather hold an API key than run
|
|
82
|
+
# SearXNG). Selection precedence:
|
|
83
|
+
#
|
|
84
|
+
# 1. TINYFISH_SEARCH=0 — a hard-off that forces :searxng
|
|
85
|
+
# 2. backend= setter (Ask::WebSearch.backend = :tinyfish)
|
|
86
|
+
# 3. SEARCH_BACKEND env ("searxng" | "tinyfish")
|
|
87
|
+
# 4. default :searxng
|
|
88
|
+
def self.backend
|
|
89
|
+
return :searxng if ENV["TINYFISH_SEARCH"] == "0"
|
|
90
|
+
return @backend if defined?(@backend) && @backend
|
|
91
|
+
|
|
92
|
+
raw = ENV["SEARCH_BACKEND"]
|
|
93
|
+
return :searxng if raw.to_s.empty?
|
|
94
|
+
|
|
95
|
+
normalize_backend(raw)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Selects the backend in code (:searxng / :tinyfish, or nil to fall
|
|
99
|
+
# back to env/default). Raises ArgumentError for anything else —
|
|
100
|
+
# config-time failure instead of a silently ignored value.
|
|
101
|
+
def self.backend=(name)
|
|
102
|
+
@backend = name.nil? ? nil : normalize_backend(name)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def self.normalize_backend(name)
|
|
106
|
+
value = name.to_s.strip.downcase
|
|
107
|
+
unless BACKENDS.include?(value)
|
|
108
|
+
raise ArgumentError, "invalid backend #{name.inspect} — use one of: #{BACKENDS.join(', ')}"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
value.to_sym
|
|
112
|
+
end
|
|
113
|
+
private_class_method :normalize_backend
|
|
114
|
+
|
|
75
115
|
# The TinyFish API key: ask-auth's chain when that gem is present
|
|
76
116
|
# (env override → ~/.ask/credentials.yml → …), raw ENV otherwise.
|
|
77
117
|
# nil when nothing resolves.
|
|
@@ -83,14 +123,12 @@ module Ask
|
|
|
83
123
|
nil
|
|
84
124
|
end
|
|
85
125
|
|
|
86
|
-
# True when
|
|
87
|
-
#
|
|
88
|
-
#
|
|
89
|
-
#
|
|
126
|
+
# True when searches should go to TinyFish — i.e. the backend was
|
|
127
|
+
# explicitly selected. Read fresh on every call so tests and config
|
|
128
|
+
# reloads don't need memo resets. Holding a key alone does NOT
|
|
129
|
+
# opt in: SearXNG stays the default path.
|
|
90
130
|
def self.use_tinyfish?
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
!tinyfish_api_key.to_s.empty?
|
|
131
|
+
backend == :tinyfish
|
|
94
132
|
end
|
|
95
133
|
|
|
96
134
|
# True when a SearXNG endpoint was configured explicitly (SEARXNG_URL
|
|
@@ -151,14 +189,15 @@ module Ask
|
|
|
151
189
|
end
|
|
152
190
|
|
|
153
191
|
# The full response: { results: [...], unresponsive: [[name,
|
|
154
|
-
# reason], ...] }. Routes to TinyFish when #use_tinyfish
|
|
155
|
-
#
|
|
156
|
-
#
|
|
157
|
-
#
|
|
158
|
-
#
|
|
159
|
-
#
|
|
160
|
-
#
|
|
161
|
-
# raw or
|
|
192
|
+
# reason], ...] }. Routes to TinyFish when #use_tinyfish? (the
|
|
193
|
+
# opt-in tinyfish backend — a keyless selection raises its
|
|
194
|
+
# onboarding error; a transport failure falls back to SearXNG when
|
|
195
|
+
# explicitly configured), otherwise to SearXNG — the default path
|
|
196
|
+
# for everyone. Same retry logic either way, but preserves the
|
|
197
|
+
# engine diagnostics the caller needs to explain an empty result.
|
|
198
|
+
# time_range / categories are normalized by the backends that build
|
|
199
|
+
# requests — idempotent, so callers may pass raw or
|
|
200
|
+
# already-normalized values.
|
|
162
201
|
def self.search_raw(query, time_range: nil, categories: nil)
|
|
163
202
|
return searxng_search_raw(query, time_range: time_range, categories: categories) unless use_tinyfish?
|
|
164
203
|
|
|
@@ -207,6 +246,14 @@ module Ask
|
|
|
207
246
|
# catches). time_range maps to recency_minutes; the FIRST
|
|
208
247
|
# recognized categories value maps to domain_type.
|
|
209
248
|
def self.tinyfish_search_raw(query, time_range: nil, categories: nil)
|
|
249
|
+
key = tinyfish_api_key
|
|
250
|
+
if key.to_s.empty?
|
|
251
|
+
raise Error, "TinyFish backend selected (SEARCH_BACKEND=tinyfish) but no API key resolves. " \
|
|
252
|
+
"Get a free key at https://agent.tinyfish.ai/api-keys, then add " \
|
|
253
|
+
"`tinyfish_api_key: <key>` to ~/.ask/credentials.yml or export " \
|
|
254
|
+
"TINYFISH_API_KEY — or drop SEARCH_BACKEND to stay on the default SearXNG path."
|
|
255
|
+
end
|
|
256
|
+
|
|
210
257
|
params = { query: query }
|
|
211
258
|
normalized_range = normalize_time_range(time_range)
|
|
212
259
|
params[:recency_minutes] = TIME_RANGE_TO_MINUTES[normalized_range] if normalized_range
|
|
@@ -224,7 +271,7 @@ module Ask
|
|
|
224
271
|
http.open_timeout = 5
|
|
225
272
|
http.read_timeout = 10
|
|
226
273
|
req = Net::HTTP::Get.new(uri)
|
|
227
|
-
req["X-API-Key"] =
|
|
274
|
+
req["X-API-Key"] = key
|
|
228
275
|
req["User-Agent"] = "ask-web-search/#{Ask::WebSearch::VERSION}"
|
|
229
276
|
res = http.request(req)
|
|
230
277
|
raise Error, "TinyFish returned #{res.code}: #{res.body[0, 200]}" unless res.code.start_with?("2")
|
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.
|
|
4
|
+
version: 0.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kaka Ruto
|
|
@@ -93,11 +93,12 @@ dependencies:
|
|
|
93
93
|
- - "~>"
|
|
94
94
|
- !ruby/object:Gem::Version
|
|
95
95
|
version: '13.0'
|
|
96
|
-
description: 'Web search returning results as clean numbered markdown. Two
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
(WebSearch.search); the native
|
|
100
|
-
that registers when
|
|
96
|
+
description: 'Web search returning results as clean numbered markdown. Two selectable
|
|
97
|
+
backends: a local SearXNG instance (SEARXNG_URL, the default path for everyone)
|
|
98
|
+
and TinyFish''s hosted API (opt-in: SEARCH_BACKEND=tinyfish plus a TINYFISH_API_KEY
|
|
99
|
+
or ask-auth credentials key). The capability layer (WebSearch.search); the native
|
|
100
|
+
Ask::Tools::WebSearch agent tool is an optional integration that registers when
|
|
101
|
+
ask-tools is present.'
|
|
101
102
|
email:
|
|
102
103
|
- kaka@myrrlabs.com
|
|
103
104
|
executables: []
|