diff --git a/surfsense_backend/app/capabilities/web/discover/executor.py b/surfsense_backend/app/capabilities/web/discover/executor.py index 85f6bf83b..e98de86e4 100644 --- a/surfsense_backend/app/capabilities/web/discover/executor.py +++ b/surfsense_backend/app/capabilities/web/discover/executor.py @@ -27,7 +27,9 @@ def build_discover_executor( "(set a SearXNG host or a Linkup/Baidu key)." ) hits = await provider.search(payload.query, payload.top_k) - return DiscoverOutput(hits=hits) + # Enforce the verb's documented cap here, once, for every provider: + # some backends (e.g. SearXNG) treat `top_k` as a hint and over-return. + return DiscoverOutput(hits=hits[: payload.top_k]) return execute diff --git a/surfsense_backend/tests/unit/capabilities/web/discover/test_executor.py b/surfsense_backend/tests/unit/capabilities/web/discover/test_executor.py index 5d50d5f6f..bfc93a31b 100644 --- a/surfsense_backend/tests/unit/capabilities/web/discover/test_executor.py +++ b/surfsense_backend/tests/unit/capabilities/web/discover/test_executor.py @@ -77,3 +77,23 @@ async def test_self_disables_when_no_provider_is_configured(): with pytest.raises(NoDiscoverProviderError): await execute(DiscoverInput(query="q")) + + +async def test_caps_hits_to_top_k_when_provider_over_returns(): + # SearXNG treats `limit` as a hint and can return more rows than asked; the + # verb must honor its own documented `top_k` cap regardless of the provider. + over = _FakeProvider( + "searxng", + available=True, + hits=[_hit(f"https://{i}.com", "searxng") for i in range(5)], + ) + execute = build_discover_executor(providers=[over]) + + out = await execute(DiscoverInput(query="q", top_k=3)) + + assert len(out.hits) == 3 + assert [h.url for h in out.hits] == [ + "https://0.com", + "https://1.com", + "https://2.com", + ]