ask-web-fetch 0.7.0 → 0.7.2

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: 6f73de0a29e2d37986a2b05a930640143f5566011782b5d68eed318bd82db746
4
- data.tar.gz: 86ee4e5a800bf7fa67df0a652f40f0058ba5e1c1fa00e77a63b737017e61fbf0
3
+ metadata.gz: 3ad48d09aa8869b4193a703ad6a760753c2767c9e26a6919b1405a5fb186c7a4
4
+ data.tar.gz: 52960016a3d4740f4913d4879cbf80edacf95e4ffc07601e6dde0d80515ffe81
5
5
  SHA512:
6
- metadata.gz: 9b3fe951669c892269d18304d4242786637df6b4f1ddd39aa4bfbc70d2241f90193701665f9141f5c7e816ba12e1a7bd5c48b98d2f0748ce1e093cfc70c5cd16
7
- data.tar.gz: f372adee62b1ab72695509aa6bac61d105a9dc64e5209d368273c8667fc62145075b978bd572b426bd54a42770a31f0ff192245f9f37b8f9c1c9e121a3fa0360
6
+ metadata.gz: fa6d778abc6918bb7ae839d4f9c96dafe215014a8b2f4efc8027ab1e49d287e3ca57204f54f013531e38dff7393ac23f6da9619b3afd48efae8b6f7e42dfa0d9
7
+ data.tar.gz: 7d4e5fe0e70b28cc3349ec96d071d4e1cc7a46c2c21ca8f41c35968d8b15221854c2418fc72ae2a8e56e97c07930592b2ee5b79da921c4a4ccb4fe6990c7e938
@@ -96,6 +96,12 @@ module Ask
96
96
  browser_mutex.synchronize { @browser ||= build_browser }
97
97
  end
98
98
 
99
+ # Drops the shared browser so the next use builds a fresh
100
+ # session — called when the current one died mid-fetch.
101
+ def reset_browser
102
+ browser_mutex.synchronize { @browser = nil }
103
+ end
104
+
99
105
  def content_filter
100
106
  @content_filter ||= ContentFilter.default
101
107
  end
@@ -162,7 +168,17 @@ module Ask
162
168
 
163
169
  page = self.class.browser.create_page
164
170
  fetch_attempt(page, url)
165
- rescue Ferrum::TimeoutError, Ferrum::ProcessTimeoutError, Ferrum::DeadBrowserError => e
171
+ rescue Ferrum::DeadBrowserError => e
172
+ # The shared browser died mid-fetch (the browserless server
173
+ # killed the session's browser). A dead browser is transient —
174
+ # the next session starts a fresh one — so reconnect and retry
175
+ # ONCE instead of failing the page (observed 2026-08-14: the
176
+ # server's session limits killed browsers and pages were
177
+ # silently dropped).
178
+ self.class.reset_browser
179
+ page = self.class.browser.create_page
180
+ fetch_attempt(page, url)
181
+ rescue Ferrum::TimeoutError, Ferrum::ProcessTimeoutError => e
166
182
  raise TimeoutError, "#{e.class}: #{e.message}"
167
183
  rescue Ferrum::StatusError => e
168
184
  raise FetchError, "browser could not load #{url}: #{e.message}"
@@ -63,7 +63,7 @@ module Ask
63
63
  # and keeps a partial page from ever being stored as the real
64
64
  # thing. Two detectors: known framework markers, or a large
65
65
  # HTML page with almost no server-rendered text.
66
- if js_app_shell?(body) && page[:content].length < SHELL_CONTENT_THRESHOLD
66
+ if js_app_shell?(body) && page[:content].length < SHELL_DEFER_THRESHOLD
67
67
  raise EmptyContentError,
68
68
  "JS-app shell at #{url} — server HTML renders only #{page[:content].length} chars; a rendering backend is required"
69
69
  end
@@ -82,10 +82,19 @@ module Ask
82
82
  # specific footprints of React/Vue/Next/Nuxt app shells.
83
83
  JS_APP_SHELL_MARKERS = /id=["'](?:root|app|__next|site-content)["']|__NEXT_DATA__|window\.__NUXT__|ng-app|data-reactroot/
84
84
 
85
- # Markdown below this from a JS-app shell is "server sent a shell",
86
- # not "page is genuinely short" — a real page (even a short one)
87
- # is usually server-rendered above this. Tunable; the chain turns
88
- # the signal into "prefer Browser for this URL".
85
+ # A framework marker (React/Vue/Next) alone is NOT emptiness: a
86
+ # marked page can server-render real content (careers.abb job
87
+ # pages: 2,162 chars of job description) and must be stored as-is
88
+ # when the rendering backends cannot do better dropping it lost
89
+ # real pages (2026-08-14). The shell deferral fires only when the
90
+ # extraction is genuinely little: below this, the page is a true
91
+ # shell (airbnb: 613KB HTML -> 143 chars). Tunable; the chain
92
+ # turns the signal into "prefer Browser for this URL".
93
+ SHELL_DEFER_THRESHOLD = 500
94
+
95
+ # The ratio detector's own bar, kept for compatibility with the
96
+ # comment below (large HTML + near-empty text is a shell whatever
97
+ # the framework).
89
98
  SHELL_CONTENT_THRESHOLD = 4_000
90
99
 
91
100
  # A page whose server HTML is large but yields almost no text is a
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ask-tools'
4
+
5
+ module Ask
6
+ module Tools
7
+ # The native agent tool for web fetch — the Ask::Tool framing of the
8
+ # library. Consumed WITHOUT MCP by agent frameworks (ask-agent's
9
+ # `tool: :web_fetch`, ask-app-server, llm-proxy) that resolve tools
10
+ # from the Ask::Tools registry. It is a pure adapter: the capability
11
+ # (chain, collapse, format) lives in Ask::WebFetch, and this file is
12
+ # loaded only when ask-tools is present (see lib/ask/web_fetch.rb), so
13
+ # the library itself never depends on it.
14
+ class WebFetch < Ask::Tool
15
+ # Chain configuration delegates to the library — swap or extend
16
+ # backends here or via Ask::WebFetch.backends.
17
+ def self.backends
18
+ Ask::WebFetch.backends
19
+ end
20
+
21
+ def self.backends=(chain)
22
+ Ask::WebFetch.backends = chain
23
+ end
24
+
25
+ description 'Fetch a URL and return its content as clean markdown for LLM consumption. ' \
26
+ 'Use this to read web pages, articles, and documentation.'
27
+
28
+ params(
29
+ type: 'object',
30
+ properties: {
31
+ url: { type: 'string', description: 'The URL to fetch' },
32
+ max_chars: { type: 'integer', description: 'Maximum number of characters to return (default 20000)' }
33
+ },
34
+ required: ['url']
35
+ )
36
+
37
+ def execute(url:, max_chars: Ask::WebFetch::DEFAULT_MAX_CHARS)
38
+ Ask::Result.ok(data: Ask::WebFetch.fetch(url, max_chars: max_chars))
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ Ask::Tools.register(Ask::Tools::WebFetch)
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Ask
4
4
  module WebFetch
5
- VERSION = '0.7.0'
5
+ VERSION = '0.7.2'
6
6
  end
7
7
  end
data/lib/ask/web_fetch.rb CHANGED
@@ -10,6 +10,19 @@ require_relative 'web_fetch/backends/crawl4ai'
10
10
  require_relative 'web_fetch/backends/jina'
11
11
  require_relative 'web_fetch/backends/browser'
12
12
 
13
+ # The native agent tool (Ask::Tools::WebFetch) is an OPTIONAL integration:
14
+ # it loads and registers only when ask-tools is present. The library works
15
+ # standalone — backend-only consumers (crawlers, pipelines) pay nothing —
16
+ # while agent frameworks (ask-agent, ask-app-server, llm-proxy) all ship
17
+ # ask-tools and get the registry tool with no extra step. Only the
18
+ # ask-tools miss is swallowed; any other LoadError is real.
19
+ begin
20
+ require 'ask-tools'
21
+ require_relative 'web_fetch/tool'
22
+ rescue LoadError => e
23
+ raise unless e.path == 'ask-tools'
24
+ end
25
+
13
26
  module Ask
14
27
  # Fetches a URL and returns its content as clean markdown for LLM
15
28
  # consumption. The capability layer: a pluggable backend chain, a
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ask-web-fetch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.7.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kaka Ruto
@@ -107,6 +107,20 @@ dependencies:
107
107
  - - "~>"
108
108
  - !ruby/object:Gem::Version
109
109
  version: '3.26'
110
+ - !ruby/object:Gem::Dependency
111
+ name: ask-tools
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0.1'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0.1'
110
124
  description: 'Fetches a URL and converts its content to clean markdown for LLM consumption.
111
125
  A pluggable backend chain: pure Ruby httpx + Nokogiri + reverse_markdown by default,
112
126
  a Jina Reader fallback for JS-rendered or blocked pages, and a real-Chrome fallback
@@ -133,6 +147,7 @@ files:
133
147
  - lib/ask/web_fetch/http.rb
134
148
  - lib/ask/web_fetch/markdown.rb
135
149
  - lib/ask/web_fetch/noise_filter.rb
150
+ - lib/ask/web_fetch/tool.rb
136
151
  - lib/ask/web_fetch/version.rb
137
152
  homepage: https://github.com/ask-rb/ask-web-fetch
138
153
  licenses:
@@ -156,7 +171,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
156
171
  - !ruby/object:Gem::Version
157
172
  version: '0'
158
173
  requirements: []
159
- rubygems_version: 4.0.3
174
+ rubygems_version: 4.0.18
160
175
  specification_version: 4
161
176
  summary: Web fetch library for the ask-rb ecosystem
162
177
  test_files: []