mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-18 04:41:56 +00:00
refactor(web): delete legacy tools/web_providers/ directory + migrate ABC tests
Removes the legacy in-tree provider scaffolding that PR #25182 fully replaced with the plugin architecture: tools/web_providers/__init__.py (6 lines) tools/web_providers/base.py (89 lines — old ABCs) tools/web_providers/ARCHITECTURE.md (73 lines — old design doc) These were the staging-ground ABCs and provider modules that the plugin migration absorbed. All seven web providers now implement the single :class:`agent.web_search_provider.WebSearchProvider` ABC and live under ``plugins/web/<vendor>/``. Nothing else in the tree imports ``tools.web_providers`` — verified via grep before deletion. Test migration (tests/tools/test_web_providers.py) -------------------------------------------------- Rewrote ``TestWebProviderABCs`` to test the new unified ABC at :mod:`agent.web_search_provider`: - test_cannot_instantiate_abc_directly — abstract ``name`` + ``is_available`` - test_concrete_search_only_provider_works — exercise default ``supports_extract=False`` / ``supports_crawl=False`` flags - test_concrete_multi_capability_provider_works — exercise all three capabilities, async extract supported (declared sync here for simplicity; real plugins like parallel + firecrawl use async) - test_search_only_provider_skips_extract_and_crawl — verify ``supports_*()`` flags default to False so search-only providers don't have to implement extract() or crawl() The 9 other tests in the file (per-capability backend selection, DEFAULT_CONFIG merge, dispatcher routing) test public helpers in ``tools.web_tools`` that still exist and pass unchanged. agent/web_search_provider.py docstring updated to reflect that the legacy ABCs no longer exist; the response-shape contract is preserved bit-for-bit so external consumers see no behavioral change. Net diff -------- - tools/web_providers/ removed (-168 lines) - tests/tools/test_web_providers.py rewritten ABC section (+78/-30 net, same coverage, new API) - agent/web_search_provider.py docstring (-3/+5 lines) Verified -------- - 173/173 targeted web tests pass - 12/12 ABC contract tests pass with the new interface - No remaining grep hits for ``tools.web_providers`` outside of intentional historical references in plugin docstrings.
This commit is contained in:
parent
24fe60faa2
commit
39b4ebfcea
5 changed files with 102 additions and 198 deletions
|
|
@ -20,50 +20,121 @@ import pytest
|
|||
|
||||
|
||||
class TestWebProviderABCs:
|
||||
"""The ABCs enforce the interface contract."""
|
||||
"""The unified WebSearchProvider ABC enforces the interface contract.
|
||||
|
||||
def test_cannot_instantiate_search_provider(self):
|
||||
from tools.web_providers.base import WebSearchProvider
|
||||
After PR #25182, all seven providers are subclasses of
|
||||
:class:`agent.web_search_provider.WebSearchProvider`. The legacy
|
||||
in-tree ABCs at ``tools.web_providers.base`` (separate
|
||||
``WebSearchProvider`` + ``WebExtractProvider``) were deleted in the
|
||||
same PR — providers now advertise capabilities via
|
||||
``supports_search() / supports_extract() / supports_crawl()`` flags.
|
||||
"""
|
||||
|
||||
def test_cannot_instantiate_abc_directly(self):
|
||||
from agent.web_search_provider import WebSearchProvider
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
WebSearchProvider() # type: ignore[abstract]
|
||||
|
||||
def test_cannot_instantiate_extract_provider(self):
|
||||
from tools.web_providers.base import WebExtractProvider
|
||||
|
||||
with pytest.raises(TypeError):
|
||||
WebExtractProvider() # type: ignore[abstract]
|
||||
|
||||
def test_concrete_search_provider_works(self):
|
||||
from tools.web_providers.base import WebSearchProvider
|
||||
def test_concrete_search_only_provider_works(self):
|
||||
from agent.web_search_provider import WebSearchProvider
|
||||
|
||||
class Dummy(WebSearchProvider):
|
||||
def provider_name(self) -> str:
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return "dummy"
|
||||
def is_configured(self) -> bool:
|
||||
|
||||
@property
|
||||
def display_name(self) -> str:
|
||||
return "Dummy Search"
|
||||
|
||||
def is_available(self) -> bool:
|
||||
return True
|
||||
|
||||
def supports_search(self) -> bool:
|
||||
return True
|
||||
|
||||
def search(self, query: str, limit: int = 5) -> Dict[str, Any]:
|
||||
return {"success": True, "data": {"web": []}}
|
||||
|
||||
d = Dummy()
|
||||
assert d.provider_name() == "dummy"
|
||||
assert d.is_configured() is True
|
||||
assert d.name == "dummy"
|
||||
assert d.display_name == "Dummy Search"
|
||||
assert d.is_available() is True
|
||||
assert d.supports_search() is True
|
||||
assert d.supports_extract() is False # default
|
||||
assert d.supports_crawl() is False # default
|
||||
assert d.search("test")["success"] is True
|
||||
|
||||
def test_concrete_extract_provider_works(self):
|
||||
from tools.web_providers.base import WebExtractProvider
|
||||
def test_concrete_multi_capability_provider_works(self):
|
||||
from agent.web_search_provider import WebSearchProvider
|
||||
|
||||
class Dummy(WebExtractProvider):
|
||||
def provider_name(self) -> str:
|
||||
class Dummy(WebSearchProvider):
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return "dummy"
|
||||
def is_configured(self) -> bool:
|
||||
|
||||
@property
|
||||
def display_name(self) -> str:
|
||||
return "Dummy Multi"
|
||||
|
||||
def is_available(self) -> bool:
|
||||
return True
|
||||
def extract(self, urls: List[str], **kwargs) -> Dict[str, Any]:
|
||||
return {"success": True, "data": [{"url": urls[0], "content": "x"}]}
|
||||
|
||||
def supports_search(self) -> bool:
|
||||
return True
|
||||
|
||||
def supports_extract(self) -> bool:
|
||||
return True
|
||||
|
||||
def supports_crawl(self) -> bool:
|
||||
return True
|
||||
|
||||
def search(self, query: str, limit: int = 5) -> Dict[str, Any]:
|
||||
return {"success": True, "data": {"web": []}}
|
||||
|
||||
def extract(self, urls: List[str], **kwargs: Any) -> List[Dict[str, Any]]:
|
||||
return [{"url": urls[0], "content": "x"}]
|
||||
|
||||
def crawl(self, url: str, **kwargs: Any) -> Dict[str, Any]:
|
||||
return {"results": [{"url": url, "content": "x"}]}
|
||||
|
||||
d = Dummy()
|
||||
assert d.provider_name() == "dummy"
|
||||
assert d.extract(["https://example.com"])["success"] is True
|
||||
assert d.supports_search() is True
|
||||
assert d.supports_extract() is True
|
||||
assert d.supports_crawl() is True
|
||||
assert d.extract(["https://example.com"])[0]["url"] == "https://example.com"
|
||||
assert d.crawl("https://example.com")["results"][0]["url"] == "https://example.com"
|
||||
|
||||
def test_search_only_provider_skips_extract_and_crawl(self):
|
||||
"""Search-only providers don't have to implement extract() / crawl()."""
|
||||
from agent.web_search_provider import WebSearchProvider
|
||||
|
||||
class SearchOnly(WebSearchProvider):
|
||||
@property
|
||||
def name(self) -> str:
|
||||
return "search-only"
|
||||
|
||||
@property
|
||||
def display_name(self) -> str:
|
||||
return "Search Only"
|
||||
|
||||
def is_available(self) -> bool:
|
||||
return True
|
||||
|
||||
def supports_search(self) -> bool:
|
||||
return True
|
||||
|
||||
def search(self, query: str, limit: int = 5) -> Dict[str, Any]:
|
||||
return {"success": True, "data": {"web": []}}
|
||||
|
||||
# Should instantiate fine — extract/crawl have default
|
||||
# supports_*() returning False and aren't required to be
|
||||
# overridden when not advertised.
|
||||
s = SearchOnly()
|
||||
assert s.supports_search() is True
|
||||
assert s.supports_extract() is False
|
||||
assert s.supports_crawl() is False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue