mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-05-20 16:42:25 +00:00
fix requested changes
This commit is contained in:
parent
bcfdd49fa4
commit
02cad2aa74
@ -19,15 +19,12 @@ from nanobot.utils.helpers import build_image_content_blocks
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
from olostep import AsyncOlostep, Olostep_BaseError
|
from olostep import AsyncOlostep, Olostep_BaseError
|
||||||
|
|
||||||
_OLOSTEP_AVAILABLE = True
|
|
||||||
except ImportError:
|
except ImportError:
|
||||||
AsyncOlostep = None
|
AsyncOlostep = None
|
||||||
|
|
||||||
class Olostep_BaseError(Exception):
|
class Olostep_BaseError(Exception):
|
||||||
"""Fallback error type when olostep package is unavailable."""
|
"""Fallback error type when olostep package is unavailable."""
|
||||||
|
pass
|
||||||
_OLOSTEP_AVAILABLE = False
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from nanobot.config.schema import WebSearchConfig
|
from nanobot.config.schema import WebSearchConfig
|
||||||
@ -111,11 +108,10 @@ class WebSearchTool(Tool):
|
|||||||
|
|
||||||
self.config = config if config is not None else WebSearchConfig()
|
self.config = config if config is not None else WebSearchConfig()
|
||||||
self.proxy = proxy
|
self.proxy = proxy
|
||||||
self.provider = (self.config.provider or "brave").strip().lower()
|
|
||||||
|
|
||||||
def _effective_provider(self) -> str:
|
def _effective_provider(self) -> str:
|
||||||
"""Resolve the backend that execute() will actually use."""
|
"""Resolve the backend that execute() will actually use."""
|
||||||
provider = self.provider or "brave"
|
provider = (self.config.provider or "brave").strip().lower()
|
||||||
if provider == "duckduckgo":
|
if provider == "duckduckgo":
|
||||||
return "duckduckgo"
|
return "duckduckgo"
|
||||||
if provider == "brave":
|
if provider == "brave":
|
||||||
@ -148,7 +144,7 @@ class WebSearchTool(Tool):
|
|||||||
return self._effective_provider() == "duckduckgo"
|
return self._effective_provider() == "duckduckgo"
|
||||||
|
|
||||||
async def execute(self, query: str, count: int | None = None, **kwargs: Any) -> str:
|
async def execute(self, query: str, count: int | None = None, **kwargs: Any) -> str:
|
||||||
provider = self.provider or "brave"
|
provider = (self.config.provider or "brave").strip().lower()
|
||||||
n = min(max(count or self.config.max_results, 1), 10)
|
n = min(max(count or self.config.max_results, 1), 10)
|
||||||
|
|
||||||
if provider == "olostep":
|
if provider == "olostep":
|
||||||
|
|||||||
@ -176,7 +176,7 @@ class GatewayConfig(Base):
|
|||||||
class WebSearchConfig(Base):
|
class WebSearchConfig(Base):
|
||||||
"""Web search tool configuration."""
|
"""Web search tool configuration."""
|
||||||
|
|
||||||
provider: str = "brave" # brave, tavily, duckduckgo, searxng, jina, kagi, olostep
|
provider: str = "duckduckgo" # brave, tavily, duckduckgo, searxng, jina, kagi, olostep
|
||||||
api_key: str = ""
|
api_key: str = ""
|
||||||
base_url: str = "" # SearXNG base URL
|
base_url: str = "" # SearXNG base URL
|
||||||
max_results: int = 5
|
max_results: int = 5
|
||||||
|
|||||||
@ -92,6 +92,9 @@ langsmith = [
|
|||||||
pdf = [
|
pdf = [
|
||||||
"pymupdf>=1.25.0",
|
"pymupdf>=1.25.0",
|
||||||
]
|
]
|
||||||
|
olostep = [
|
||||||
|
"olostep>=0.1.0",
|
||||||
|
]
|
||||||
dev = [
|
dev = [
|
||||||
"pytest>=9.0.0,<10.0.0",
|
"pytest>=9.0.0,<10.0.0",
|
||||||
"pytest-asyncio>=1.3.0,<2.0.0",
|
"pytest-asyncio>=1.3.0,<2.0.0",
|
||||||
|
|||||||
@ -1,66 +0,0 @@
|
|||||||
"""Tests for Olostep web search provider."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import asyncio
|
|
||||||
from types import SimpleNamespace
|
|
||||||
from unittest.mock import patch
|
|
||||||
|
|
||||||
import nanobot.agent.tools.web as web_mod
|
|
||||||
from nanobot.agent.tools.web import WebSearchTool
|
|
||||||
from nanobot.config.schema import WebSearchConfig
|
|
||||||
|
|
||||||
|
|
||||||
def test_olostep_search_formats_answer_and_sources():
|
|
||||||
calls: dict[str, str] = {}
|
|
||||||
|
|
||||||
class MockAsyncOlostep:
|
|
||||||
def __init__(self, api_key: str):
|
|
||||||
calls["api_key"] = api_key
|
|
||||||
self.answers = self
|
|
||||||
|
|
||||||
async def __aenter__(self):
|
|
||||||
return self
|
|
||||||
|
|
||||||
async def __aexit__(self, exc_type, exc, tb):
|
|
||||||
return None
|
|
||||||
|
|
||||||
async def create(self, task: str):
|
|
||||||
calls["task"] = task
|
|
||||||
return SimpleNamespace(
|
|
||||||
answer="Mocked Olostep answer",
|
|
||||||
sources=[SimpleNamespace(title="Example Source", url="https://example.com")],
|
|
||||||
)
|
|
||||||
|
|
||||||
with patch.object(web_mod, "AsyncOlostep", MockAsyncOlostep):
|
|
||||||
tool = WebSearchTool(config=WebSearchConfig(provider="olostep", api_key="olostep-key"))
|
|
||||||
result = asyncio.run(tool.execute(query="test query"))
|
|
||||||
|
|
||||||
assert calls["api_key"] == "olostep-key"
|
|
||||||
assert calls["task"] == "test query"
|
|
||||||
assert "Mocked Olostep answer" in result
|
|
||||||
assert "Example Source" in result
|
|
||||||
assert "https://example.com" in result
|
|
||||||
|
|
||||||
|
|
||||||
def test_olostep_missing_key_falls_back_to_duckduckgo():
|
|
||||||
class MockDDGS:
|
|
||||||
def __init__(self, **kw):
|
|
||||||
pass
|
|
||||||
|
|
||||||
def text(self, query, max_results=5):
|
|
||||||
return [{"title": "Fallback", "href": "https://ddg.example", "body": "fallback"}]
|
|
||||||
|
|
||||||
with patch.dict(web_mod.os.environ, {}, clear=False), patch("ddgs.DDGS", MockDDGS):
|
|
||||||
tool = WebSearchTool(config=WebSearchConfig(provider="olostep", api_key=""))
|
|
||||||
result = asyncio.run(tool.execute(query="test query"))
|
|
||||||
|
|
||||||
assert "Fallback" in result
|
|
||||||
|
|
||||||
|
|
||||||
def test_olostep_package_missing_returns_install_hint():
|
|
||||||
with patch.object(web_mod, "AsyncOlostep", None):
|
|
||||||
tool = WebSearchTool(config=WebSearchConfig(provider="olostep", api_key="olostep-key"))
|
|
||||||
result = asyncio.run(tool.execute(query="test query"))
|
|
||||||
|
|
||||||
assert result == "Error: olostep package not installed. Run: pip install olostep"
|
|
||||||
@ -281,3 +281,72 @@ async def test_duckduckgo_timeout_returns_error(monkeypatch):
|
|||||||
result = await tool.execute(query="test")
|
result = await tool.execute(query="test")
|
||||||
gate.set()
|
gate.set()
|
||||||
assert "Error" in result
|
assert "Error" in result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_olostep_search_formats_answer_and_sources(monkeypatch):
|
||||||
|
from types import SimpleNamespace
|
||||||
|
from unittest.mock import patch
|
||||||
|
import nanobot.agent.tools.web as web_mod
|
||||||
|
|
||||||
|
calls: dict[str, str] = {}
|
||||||
|
|
||||||
|
class MockAsyncOlostep:
|
||||||
|
def __init__(self, api_key: str):
|
||||||
|
calls["api_key"] = api_key
|
||||||
|
self.answers = self
|
||||||
|
|
||||||
|
async def __aenter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def __aexit__(self, exc_type, exc, tb):
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def create(self, task: str):
|
||||||
|
calls["task"] = task
|
||||||
|
return SimpleNamespace(
|
||||||
|
answer="Mocked Olostep answer",
|
||||||
|
sources=[SimpleNamespace(title="Example Source", url="https://example.com")],
|
||||||
|
)
|
||||||
|
|
||||||
|
with patch.object(web_mod, "AsyncOlostep", MockAsyncOlostep):
|
||||||
|
tool = _tool(provider="olostep", api_key="olostep-key")
|
||||||
|
result = await tool.execute(query="test query")
|
||||||
|
|
||||||
|
assert calls["api_key"] == "olostep-key"
|
||||||
|
assert calls["task"] == "test query"
|
||||||
|
assert "Mocked Olostep answer" in result
|
||||||
|
assert "Example Source" in result
|
||||||
|
assert "https://example.com" in result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_olostep_missing_key_falls_back_to_duckduckgo(monkeypatch):
|
||||||
|
from unittest.mock import patch
|
||||||
|
import nanobot.agent.tools.web as web_mod
|
||||||
|
|
||||||
|
class MockDDGS:
|
||||||
|
def __init__(self, **kw):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def text(self, query, max_results=5):
|
||||||
|
return [{"title": "Fallback", "href": "https://ddg.example", "body": "fallback"}]
|
||||||
|
|
||||||
|
monkeypatch.delenv("OLOSTEP_API_KEY", raising=False)
|
||||||
|
with patch("ddgs.DDGS", MockDDGS):
|
||||||
|
tool = _tool(provider="olostep", api_key="")
|
||||||
|
result = await tool.execute(query="test query")
|
||||||
|
|
||||||
|
assert "Fallback" in result
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_olostep_package_missing_returns_install_hint(monkeypatch):
|
||||||
|
from unittest.mock import patch
|
||||||
|
import nanobot.agent.tools.web as web_mod
|
||||||
|
|
||||||
|
with patch.object(web_mod, "AsyncOlostep", None):
|
||||||
|
tool = _tool(provider="olostep", api_key="olostep-key")
|
||||||
|
result = await tool.execute(query="test query")
|
||||||
|
|
||||||
|
assert result == "Error: olostep package not installed. Run: pip install olostep"
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user