From 4fd790f7dd600c6bf0721fa70b8009c8ca1f97a8 Mon Sep 17 00:00:00 2001 From: Classic298 <27028174+Classic298@users.noreply.github.com> Date: Sun, 21 Dec 2025 13:18:00 +0100 Subject: [PATCH] feat: Apply WEB_SEARCH_CONCURRENT_REQUESTS to all search engines using semaphore (#20070) * sequential * zero default * fix --- backend/open_webui/config.py | 2 +- backend/open_webui/routers/retrieval.py | 42 ++++++++++++++----- .../admin/Settings/WebSearch.svelte | 29 +++++++------ 3 files changed, 49 insertions(+), 24 deletions(-) diff --git a/backend/open_webui/config.py b/backend/open_webui/config.py index cf2983d63f..c5ac40babd 100644 --- a/backend/open_webui/config.py +++ b/backend/open_webui/config.py @@ -2994,7 +2994,7 @@ WEB_SEARCH_DOMAIN_FILTER_LIST = PersistentConfig( WEB_SEARCH_CONCURRENT_REQUESTS = PersistentConfig( "WEB_SEARCH_CONCURRENT_REQUESTS", "rag.web.search.concurrent_requests", - int(os.getenv("WEB_SEARCH_CONCURRENT_REQUESTS", "10")), + int(os.getenv("WEB_SEARCH_CONCURRENT_REQUESTS", "0")), ) diff --git a/backend/open_webui/routers/retrieval.py b/backend/open_webui/routers/retrieval.py index 43839d0107..89aabf5573 100644 --- a/backend/open_webui/routers/retrieval.py +++ b/backend/open_webui/routers/retrieval.py @@ -2103,16 +2103,38 @@ async def process_web_search( f"trying to web search with {request.app.state.config.WEB_SEARCH_ENGINE, form_data.queries}" ) - search_tasks = [ - run_in_threadpool( - search_web, - request, - request.app.state.config.WEB_SEARCH_ENGINE, - query, - user, - ) - for query in form_data.queries - ] + # Use semaphore to limit concurrent requests based on WEB_SEARCH_CONCURRENT_REQUESTS + # 0 or None = unlimited (previous behavior), positive number = limited concurrency + # Set to 1 for sequential execution (rate-limited APIs like Brave free tier) + concurrent_limit = request.app.state.config.WEB_SEARCH_CONCURRENT_REQUESTS + + if concurrent_limit: + # Limited concurrency with semaphore + semaphore = asyncio.Semaphore(concurrent_limit) + + async def search_with_limit(query): + async with semaphore: + return await run_in_threadpool( + search_web, + request, + request.app.state.config.WEB_SEARCH_ENGINE, + query, + user, + ) + + search_tasks = [search_with_limit(query) for query in form_data.queries] + else: + # Unlimited parallel execution (previous behavior) + search_tasks = [ + run_in_threadpool( + search_web, + request, + request.app.state.config.WEB_SEARCH_ENGINE, + query, + user, + ) + for query in form_data.queries + ] search_results = await asyncio.gather(*search_tasks) diff --git a/src/lib/components/admin/Settings/WebSearch.svelte b/src/lib/components/admin/Settings/WebSearch.svelte index 588a5a6ac0..b2956fc729 100644 --- a/src/lib/components/admin/Settings/WebSearch.svelte +++ b/src/lib/components/admin/Settings/WebSearch.svelte @@ -630,19 +630,6 @@ /> - {:else if webConfig.WEB_SEARCH_ENGINE === 'ddgs' || webConfig.WEB_SEARCH_ENGINE === 'duckduckgo'} -
-
- {$i18n.t('Concurrent Requests')} -
- - -
{:else if webConfig.WEB_SEARCH_ENGINE === 'external'}
@@ -692,6 +679,22 @@ required />
+ +
+
+ + {$i18n.t('Concurrent Requests')} + +
+ + +