fix requested changes

This commit is contained in:
umerkay 2026-04-28 14:35:51 +05:00 committed by chengyongru
parent bcfdd49fa4
commit 02cad2aa74
5 changed files with 76 additions and 74 deletions

View File

@ -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":

View File

@ -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

View File

@ -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",

View File

@ -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"

View File

@ -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"