mirror of
https://github.com/open-webui/open-webui.git
synced 2026-10-03 02:23:51 +00:00
fix: honor requests_per_second in FireCrawl loader
`SafeFireCrawlLoader` inherits `RateLimitMixin` and accepts `requests_per_second` in its constructor, but neither `lazy_load` nor `alazy_load` ever invoked `_sync_wait_for_rate_limit` / `_wait_for_rate_limit`. The configured value (env `WEB_LOADER_CONCURRENT_REQUESTS`) was silently ignored: the loader fired requests as fast as the loop ran. `_safe_process_url` is not used because it's redundant for FireCrawl: FireCrawl fetches the target URL on the user's behalf, and `verify_ssl` is already honored in the FireCrawl payload (`skipTlsVerification`).
This commit is contained in:
parent
03edefb816
commit
28a4df7921
2 changed files with 39 additions and 0 deletions
|
|
@ -220,6 +220,7 @@ class SafeFireCrawlLoader(BaseLoader, RateLimitMixin, URLProcessingMixin):
|
|||
def lazy_load(self) -> Iterator[Document]:
|
||||
for url in self.web_paths:
|
||||
try:
|
||||
self._sync_wait_for_rate_limit()
|
||||
doc = scrape_firecrawl_url(
|
||||
self.api_url,
|
||||
self.api_key,
|
||||
|
|
@ -239,6 +240,7 @@ class SafeFireCrawlLoader(BaseLoader, RateLimitMixin, URLProcessingMixin):
|
|||
async def alazy_load(self):
|
||||
for url in self.web_paths:
|
||||
try:
|
||||
await self._wait_for_rate_limit()
|
||||
doc = await run_in_threadpool(
|
||||
scrape_firecrawl_url,
|
||||
self.api_url,
|
||||
|
|
|
|||
|
|
@ -126,6 +126,24 @@ class TestLazyLoad:
|
|||
with pytest.raises(requests.HTTPError):
|
||||
list(loader.lazy_load())
|
||||
|
||||
@patch('open_webui.retrieval.web.utils.scrape_firecrawl_url')
|
||||
def test_waits_for_rate_limit_before_each_scrape(self, mock_scrape):
|
||||
"""Each URL must trigger the rate limiter before the scrape, mirroring SafePlaywrightURLLoader"""
|
||||
events = []
|
||||
|
||||
def wait_side_effect():
|
||||
events.append('wait')
|
||||
|
||||
def scrape_side_effect(api_url, api_key, url, **kwargs):
|
||||
events.append('scrape')
|
||||
return _make_doc(url)
|
||||
|
||||
mock_scrape.side_effect = scrape_side_effect
|
||||
loader = _build_loader(web_paths=URLS[:3])
|
||||
with patch.object(loader, '_sync_wait_for_rate_limit', side_effect=wait_side_effect):
|
||||
list(loader.lazy_load())
|
||||
assert events == ['wait', 'scrape'] * 3, f'Expected wait-then-scrape per URL; got {events!r}'
|
||||
|
||||
|
||||
class TestAlazyLoad:
|
||||
"""Async `SafeFireCrawlLoader.alazy_load` -- same contract as the sync version."""
|
||||
|
|
@ -179,3 +197,22 @@ class TestAlazyLoad:
|
|||
loader = _build_loader(web_paths=URLS[:3], continue_on_failure=False)
|
||||
with pytest.raises(requests.HTTPError):
|
||||
await _collect(loader.alazy_load())
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@patch('open_webui.retrieval.web.utils.scrape_firecrawl_url')
|
||||
async def test_waits_for_rate_limit_before_each_scrape(self, mock_scrape):
|
||||
"""Async equivalent: each URL must await the rate limiter before scraping."""
|
||||
events = []
|
||||
|
||||
async def wait_side_effect():
|
||||
events.append('wait')
|
||||
|
||||
def scrape_side_effect(api_url, api_key, url, **kwargs):
|
||||
events.append('scrape')
|
||||
return _make_doc(url)
|
||||
|
||||
mock_scrape.side_effect = scrape_side_effect
|
||||
loader = _build_loader(web_paths=URLS[:3])
|
||||
with patch.object(loader, '_wait_for_rate_limit', side_effect=wait_side_effect):
|
||||
await _collect(loader.alazy_load())
|
||||
assert events == ['wait', 'scrape'] * 3, f'Expected wait-then-scrape per URL; got {events!r}'
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue