diff --git a/services/search/providers.py b/services/search/providers.py index 60daa4844..a812b79c4 100644 --- a/services/search/providers.py +++ b/services/search/providers.py @@ -305,23 +305,28 @@ def searxng_search_api(query: str, count: Optional[int] = None, categories: str return parsed except Exception as e: logger.warning(f"SearXNG JSON API search failed: {e}") - html_results = searxng_search(query, max_results=count) + html_results = searxng_search(query, max_results=count, search_params=active_params) if html_results: logger.info(f"SearXNG HTML fallback returned {len(html_results)} results for: {query}") return html_results -def searxng_search(query, max_results=10): +def searxng_search(query, max_results=10, *, search_params=None): """Search using SearXNG instance - parsing HTML.""" instance = _get_search_instance() api_key = "" req_headers = {"User-Agent": WEB_FETCH_USER_AGENT} if api_key: req_headers["Authorization"] = f"Bearer {api_key}" + # Transport fallback must not change the user's retrieval constraints. + # In particular omit only JSON formatting, not publication time/category. + params = {key: value for key, value in (search_params or {}).items() + if key in {'categories', 'engines', 'language', 'time_range'}} + params.update({'q': query, 'safesearch': _safesearch_for('searxng')}) try: response = httpx.get( f"{instance}/search", - params={"q": query, "safesearch": _safesearch_for("searxng")}, + params=params, headers=req_headers, timeout=10, ) diff --git a/tests/test_service_search_provider_guards.py b/tests/test_service_search_provider_guards.py index ca7973499..1277370a9 100644 --- a/tests/test_service_search_provider_guards.py +++ b/tests/test_service_search_provider_guards.py @@ -11,6 +11,27 @@ from services.search import core from services.search import providers +def test_html_transport_fallback_preserves_query_constraints(monkeypatch): + seen = [] + class Response: + is_success = True + text = '

Report

Evidence

' + def get(*args, **kwargs): + seen.append(dict(kwargs['params'])) + if len(seen) == 1: + raise ValueError('JSON transport unavailable') + return Response() + monkeypatch.setattr(providers, '_get_search_instance', lambda: 'http://searx.test') + monkeypatch.setattr(providers, '_get_search_settings', lambda: {'search_safesearch': 'strict'}) + monkeypatch.setattr(providers, '_get_provider_key', lambda name: '') + monkeypatch.setattr(providers.httpx, 'get', get) + results = providers.searxng_search_api('AI news site:example.org', time_filter='week', engines='test-engine') + assert len(results) == 1 + assert len(seen) == 2 + assert seen[1] == {k: v for k, v in seen[0].items() if k != 'format'} + assert seen[1]['time_range'] == 'week' + + @pytest.mark.parametrize('query,category', [ ('AI developments this week', 'news'), ('recent developments in battery manufacturing', 'news'),