Merge origin/main into main

This commit is contained in:
Xubin Ren
2026-04-11 09:32:05 +00:00
19 changed files with 1423 additions and 34 deletions
+40
View File
@@ -356,6 +356,46 @@ async def test_connect_mcp_servers_enabled_tools_warns_on_unknown_entries(
assert "Available wrapped names: mcp_test_demo" in warnings[-1]
@pytest.mark.asyncio
async def test_connect_mcp_servers_one_failure_does_not_block_others(
monkeypatch: pytest.MonkeyPatch,
) -> None:
sessions = {"good": _make_fake_session(["demo"])}
class _SelectiveClientSession:
def __init__(self, read: object, _write: object) -> None:
self._session = sessions[read]
async def __aenter__(self) -> object:
return self._session
async def __aexit__(self, exc_type, exc, tb) -> bool:
return False
@asynccontextmanager
async def _selective_stdio_client(params: object):
if params.command == "bad":
raise RuntimeError("boom")
yield params.command, object()
monkeypatch.setattr(sys.modules["mcp"], "ClientSession", _SelectiveClientSession)
monkeypatch.setattr(sys.modules["mcp.client.stdio"], "stdio_client", _selective_stdio_client)
registry = ToolRegistry()
stacks = await connect_mcp_servers(
{
"good": MCPServerConfig(command="good"),
"bad": MCPServerConfig(command="bad"),
},
registry,
)
for stack in stacks.values():
await stack.aclose()
assert registry.tool_names == ["mcp_good_demo"]
assert set(stacks) == {"good"}
# ---------------------------------------------------------------------------
# MCPResourceWrapper tests
# ---------------------------------------------------------------------------
+38
View File
@@ -120,6 +120,27 @@ async def test_jina_search(monkeypatch):
assert "https://jina.ai" in result
@pytest.mark.asyncio
async def test_kagi_search(monkeypatch):
async def mock_get(self, url, **kw):
assert "kagi.com/api/v0/search" in url
assert kw["headers"]["Authorization"] == "Bot kagi-key"
assert kw["params"] == {"q": "test", "limit": 2}
return _response(json={
"data": [
{"t": 0, "title": "Kagi Result", "url": "https://kagi.com", "snippet": "Premium search"},
{"t": 1, "list": ["ignored related search"]},
]
})
monkeypatch.setattr(httpx.AsyncClient, "get", mock_get)
tool = _tool(provider="kagi", api_key="kagi-key")
result = await tool.execute(query="test", count=2)
assert "Kagi Result" in result
assert "https://kagi.com" in result
assert "ignored related search" not in result
@pytest.mark.asyncio
async def test_unknown_provider():
tool = _tool(provider="unknown")
@@ -189,6 +210,23 @@ async def test_jina_422_falls_back_to_duckduckgo(monkeypatch):
assert "DuckDuckGo fallback" in result
@pytest.mark.asyncio
async def test_kagi_fallback_to_duckduckgo_when_no_key(monkeypatch):
class MockDDGS:
def __init__(self, **kw):
pass
def text(self, query, max_results=5):
return [{"title": "Fallback", "href": "https://ddg.example", "body": "DuckDuckGo fallback"}]
monkeypatch.setattr("ddgs.DDGS", MockDDGS)
monkeypatch.delenv("KAGI_API_KEY", raising=False)
tool = _tool(provider="kagi", api_key="")
result = await tool.execute(query="test")
assert "Fallback" in result
@pytest.mark.asyncio
async def test_jina_search_uses_path_encoded_query(monkeypatch):
calls = {}