Files
Classic298 069f49fcd2 refac: remove unreachable rate limit handler from DuckDuckGo web search (#28943)
The DuckDuckGo search path catches RatelimitException from the ddgs library. That exception is defined by the library but never raised anywhere in it, checked against the pinned 9.14.4 and against 9.11.3, so the handler could never run. The two fallbacks around it were dead for the same reason: ddgs.text() returns a non-empty list or raises, so None and an empty list are not outcomes it can produce.

Removing all three leaves one call and changes nothing observable. A refused or rate limited search already came out as a failed search, with the error shown to the user and the traceback in the log, and it still does.

The backend argument is now passed as backend or 'auto' rather than conditionally omitted, because 'auto' is the library's own default for that parameter, so every configured value including unset and empty resolves exactly as before. Verified by running the old and the new function side by side against a stubbed library covering normal results, the domain filter, all four backend settings and a failing search, with identical results in every case.
2026-08-23 12:59:05 -04:00

50 lines
1.6 KiB
Python

from __future__ import annotations
import logging
import urllib.request
from ddgs import DDGS
from open_webui.retrieval.web.main import SearchResult, get_filtered_results
log = logging.getLogger(__name__)
def search_duckduckgo(
query: str,
count: int,
filter_list: list[str | None] = None,
concurrent_requests: int | None = None,
backend: str | None = 'auto',
) -> list[SearchResult]:
"""
Search using DuckDuckGo's Search API and return the results as a list of SearchResult objects.
Args:
query (str): The query to search for
count (int): The number of results to return
backend (str): The search backend to use (auto, duckduckgo, google, brave, etc.)
Returns:
list[SearchResult]: A list of search results
"""
# The ddgs library (primp-based) does not auto-detect proxy env vars.
# Resolve via stdlib getproxies() — same pattern as the other loaders.
env_proxies = urllib.request.getproxies()
proxy = env_proxies.get('https') or env_proxies.get('http')
with DDGS(proxy=proxy) as ddgs:
if concurrent_requests:
ddgs.threads = concurrent_requests
search_results = ddgs.text(query, safesearch='moderate', max_results=count, backend=backend or 'auto')
if filter_list:
search_results = get_filtered_results(search_results, filter_list)
# Return the list of search results
return [
SearchResult(
link=result['href'],
title=result.get('title'),
snippet=result.get('body'),
)
for result in search_results
]