feat: add internet_search + web_scraper chat tools, fix tool result in execution steps
Some checks failed
Stuffle/nebula-os/pipeline/head There was a failure building this commit
Some checks failed
Stuffle/nebula-os/pipeline/head There was a failure building this commit
- New chat tool: internet_search via ddgs (DuckDuckGo, no API key required) auto-selects serper if SERPER_API_KEY is set - New chat tool: web_scraper wrapping execution_system scrape_url auto-selects crawl4ai if available - Register web_tools module in ChatToolRegistry __init__.py - Fix: execution step metadata now includes actual 'result' data not just result_keys (both backend react loop and frontend bridge mode) - Add toolDetail entries for search_tool_catalog, internet_search, web_scraper - Add ddgs to requirements.txt (installed in venv)
This commit is contained in:
@@ -82,7 +82,8 @@ faker==22.0.0
|
||||
playwright>=1.49.0
|
||||
pytest-playwright==0.4.4
|
||||
|
||||
# --- Web Scraper (optional, feature-gated) ---
|
||||
# --- Web Scraper + Search (optional, feature-gated) ---
|
||||
ddgs>=0.1.0
|
||||
crawl4ai==0.4.247
|
||||
firecrawl-py==1.4.0
|
||||
scrapegraphai==1.13.3
|
||||
|
||||
@@ -17,7 +17,7 @@ log = get_logger("api.chat.tools.web")
|
||||
|
||||
def _check_duckduckgo() -> bool:
|
||||
try:
|
||||
import duckduckgo_search # noqa: F401
|
||||
import ddgs # noqa: F401
|
||||
return True
|
||||
except ImportError:
|
||||
return False
|
||||
@@ -28,25 +28,29 @@ def _check_serper() -> bool:
|
||||
|
||||
|
||||
async def _search_duckduckgo(query: str, max_results: int) -> List[Dict[str, Any]]:
|
||||
"""Search using duckduckgo_search library (no API key required)."""
|
||||
"""Search using ddgs library (no API key required)."""
|
||||
try:
|
||||
from duckduckgo_search import AsyncDDGS
|
||||
from ddgs import DDGS
|
||||
except ImportError as exc:
|
||||
raise RuntimeError(
|
||||
"duckduckgo_search is not installed. Run: pip install duckduckgo-search"
|
||||
"ddgs is not installed. Run: pip install ddgs"
|
||||
) from exc
|
||||
|
||||
results: List[Dict[str, Any]] = []
|
||||
async with AsyncDDGS() as ddgs:
|
||||
async for r in ddgs.text(query, max_results=max_results):
|
||||
results.append({
|
||||
"title": r.get("title", ""),
|
||||
"url": r.get("href", ""),
|
||||
"snippet": r.get("body", ""),
|
||||
})
|
||||
if len(results) >= max_results:
|
||||
break
|
||||
return results
|
||||
loop = asyncio.get_event_loop()
|
||||
|
||||
def _do_search():
|
||||
with DDGS() as client:
|
||||
return list(client.text(query, max_results=max_results))
|
||||
|
||||
raw = await loop.run_in_executor(None, _do_search)
|
||||
return [
|
||||
{
|
||||
"title": r.get("title", ""),
|
||||
"url": r.get("href", ""),
|
||||
"snippet": r.get("body", ""),
|
||||
}
|
||||
for r in raw
|
||||
]
|
||||
|
||||
|
||||
async def _search_serper(query: str, max_results: int) -> List[Dict[str, Any]]:
|
||||
@@ -135,7 +139,7 @@ class InternetSearchTool(ChatTool):
|
||||
"success": False,
|
||||
"error": (
|
||||
"No search backend available. "
|
||||
"Install duckduckgo-search (`pip install duckduckgo-search`) "
|
||||
"Install ddgs (`pip install ddgs`) "
|
||||
"or set SERPER_API_KEY."
|
||||
),
|
||||
"results": [],
|
||||
|
||||
Reference in New Issue
Block a user