mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-07-08 22:22:17 +02:00
feat(capabilities): add env-keyed searxng/linkup/baidu discover providers
This commit is contained in:
parent
adc39d1062
commit
329bc63b6c
6 changed files with 345 additions and 0 deletions
|
|
@ -0,0 +1,91 @@
|
|||
"""BaiduProvider maps the Baidu AI Search references to DiscoverHits, env-keyed.
|
||||
|
||||
Boundary mocked: httpx.AsyncClient + config key. NOT mocked: reference→hit 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
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
"""LinkupProvider maps the Linkup SDK results to DiscoverHits, env-keyed.
|
||||
|
||||
Boundary mocked: the LinkupClient SDK + config key. NOT mocked: result→hit 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
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
"""SearxngProvider maps the SearXNG service's sources to DiscoverHits.
|
||||
|
||||
Boundary mocked: the web_search_service module. NOT mocked: the source→hit 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue