feat(capabilities): add env-keyed searxng/linkup/baidu discover providers

This commit is contained in:
CREDO23 2026-07-01 17:08:58 +02:00
parent adc39d1062
commit 329bc63b6c
6 changed files with 345 additions and 0 deletions

View file

@ -0,0 +1,91 @@
"""BaiduProvider maps the Baidu AI Search references to DiscoverHits, env-keyed.
Boundary mocked: httpx.AsyncClient + config key. NOT mocked: referencehit mapping.
"""
from __future__ import annotations
import pytest
import app.capabilities.web.discover.providers.baidu as baidu_module
from app.capabilities.web.discover.providers.baidu import BaiduProvider
from app.config import config
pytestmark = pytest.mark.unit
class _FakeResponse:
def __init__(self, payload):
self._payload = payload
def raise_for_status(self):
return None
def json(self):
return self._payload
class _FakeAsyncClient:
"""Minimal async-context httpx stand-in returning a canned payload."""
payload: dict = {}
def __init__(self, *args, **kwargs):
pass
async def __aenter__(self):
return self
async def __aexit__(self, *exc):
return False
async def post(self, url, headers=None, json=None):
return _FakeResponse(type(self).payload)
def _install(monkeypatch, payload):
monkeypatch.setattr(config, "BAIDU_API_KEY", "k")
_FakeAsyncClient.payload = payload
monkeypatch.setattr(baidu_module.httpx, "AsyncClient", _FakeAsyncClient)
def test_is_available_reflects_the_env_key(monkeypatch):
monkeypatch.setattr(config, "BAIDU_API_KEY", "k")
assert BaiduProvider().is_available() is True
monkeypatch.setattr(config, "BAIDU_API_KEY", None)
assert BaiduProvider().is_available() is False
async def test_maps_references_to_hits(monkeypatch):
_install(
monkeypatch,
{
"references": [
{"title": "Acme", "url": "https://acme.cn", "content": "hello"},
{"title": "No URL", "url": "", "content": "skip"},
]
},
)
hits = await BaiduProvider().search("acme", top_k=10)
assert [h.url for h in hits] == ["https://acme.cn"]
assert hits[0].title == "Acme"
assert hits[0].snippet == "hello"
assert hits[0].provider == "baidu"
async def test_respects_top_k(monkeypatch):
_install(
monkeypatch,
{
"references": [
{"title": f"n{i}", "url": f"https://{i}.cn", "content": "c"}
for i in range(5)
]
},
)
hits = await BaiduProvider().search("q", top_k=2)
assert len(hits) == 2

View file

@ -0,0 +1,75 @@
"""LinkupProvider maps the Linkup SDK results to DiscoverHits, env-keyed.
Boundary mocked: the LinkupClient SDK + config key. NOT mocked: resulthit mapping.
"""
from __future__ import annotations
import pytest
import app.capabilities.web.discover.providers.linkup as linkup_module
from app.capabilities.web.discover.providers.linkup import LinkupProvider
from app.config import config
pytestmark = pytest.mark.unit
class _Result:
def __init__(self, name, url, content):
self.name = name
self.url = url
self.content = content
self.type = "text"
class _Response:
def __init__(self, results):
self.results = results
class _FakeClient:
def __init__(self, api_key):
self.api_key = api_key
def search(self, *, query, depth, output_type):
return _Response(
[
_Result("Acme", "https://acme.com", "acme home"),
_Result("No URL", "", "skip"),
]
)
def test_is_available_reflects_the_env_key(monkeypatch):
monkeypatch.setattr(config, "LINKUP_API_KEY", "k")
assert LinkupProvider().is_available() is True
monkeypatch.setattr(config, "LINKUP_API_KEY", None)
assert LinkupProvider().is_available() is False
async def test_maps_results_to_hits(monkeypatch):
monkeypatch.setattr(config, "LINKUP_API_KEY", "k")
monkeypatch.setattr(linkup_module, "LinkupClient", _FakeClient)
hits = await LinkupProvider().search("acme", top_k=10)
assert [h.url for h in hits] == ["https://acme.com"]
assert hits[0].title == "Acme"
assert hits[0].snippet == "acme home"
assert hits[0].provider == "linkup"
async def test_respects_top_k(monkeypatch):
monkeypatch.setattr(config, "LINKUP_API_KEY", "k")
class _ManyClient(_FakeClient):
def search(self, *, query, depth, output_type):
return _Response(
[_Result(f"n{i}", f"https://{i}.com", "c") for i in range(5)]
)
monkeypatch.setattr(linkup_module, "LinkupClient", _ManyClient)
hits = await LinkupProvider().search("q", top_k=2)
assert len(hits) == 2

View file

@ -0,0 +1,58 @@
"""SearxngProvider maps the SearXNG service's sources to DiscoverHits.
Boundary mocked: the web_search_service module. NOT mocked: the sourcehit mapping.
"""
from __future__ import annotations
from unittest.mock import AsyncMock
import pytest
import app.capabilities.web.discover.providers.searxng as searxng_module
from app.capabilities.web.discover.providers.searxng import SearxngProvider
pytestmark = pytest.mark.unit
def _result(sources):
return ({"sources": sources}, [])
async def test_maps_sources_to_hits(monkeypatch):
provider = SearxngProvider()
monkeypatch.setattr(
searxng_module.web_search_service,
"search",
AsyncMock(
return_value=_result(
[
{"title": "Acme", "url": "https://acme.com", "description": "home"},
{
"title": "Docs",
"url": "https://acme.com/docs",
"description": "",
},
{"title": "no url", "url": "", "description": "skip me"},
]
)
),
)
hits = await provider.search("acme", top_k=5)
assert [h.url for h in hits] == ["https://acme.com", "https://acme.com/docs"]
assert hits[0].title == "Acme"
assert hits[0].snippet == "home"
assert hits[1].snippet is None # empty description normalizes to None
assert all(h.provider == "searxng" for h in hits)
def test_is_available_reflects_the_service(monkeypatch):
provider = SearxngProvider()
monkeypatch.setattr(searxng_module.web_search_service, "is_available", lambda: True)
assert provider.is_available() is True
monkeypatch.setattr(
searxng_module.web_search_service, "is_available", lambda: False
)
assert provider.is_available() is False