jarvis-cloud / backend /tools /web_search_tools.py
Jarvis2345's picture
Squash history — remove all prior commits (secret hygiene, S4)
a31f556
Raw
History Blame
3.28 kB
# backend/tasks/tools/web_search_tools.py
import asyncio
import json
import urllib.request
import urllib.parse
import os
async def search_web(query: str) -> dict:
"""Search web using Brave Search API (fallback to DuckDuckGo if no key)"""
brave_api_key = os.environ.get("BRAVE_API_KEY")
if not brave_api_key:
# Fallback to DuckDuckGo
def _duck_search():
url = f"https://api.duckduckgo.com/?q={urllib.parse.quote(query)}&format=json"
req = urllib.request.Request(url, headers={'User-Agent': 'Mozilla/5.0'})
try:
with urllib.request.urlopen(req, timeout=10) as response:
data = json.loads(response.read().decode('utf-8'))
return {"results": [{"title": "Abstract", "snippet": data.get('AbstractText', ''), "url": data.get('AbstractURL', '')}]}
except Exception as e:
return {"error": str(e)}
return await asyncio.to_thread(_duck_search)
def _brave_search():
url = f"https://api.search.brave.com/res/v1/web/search?q={urllib.parse.quote(query)}"
req = urllib.request.Request(url, headers={
'Accept': 'application/json',
'Accept-Encoding': 'gzip',
'X-Subscription-Token': brave_api_key
})
try:
with urllib.request.urlopen(req, timeout=10) as response:
if response.info().get('Content-Encoding') == 'gzip':
import gzip
data = gzip.decompress(response.read()).decode('utf-8')
else:
data = response.read().decode('utf-8')
parsed = json.loads(data)
results = []
for item in parsed.get('web', {}).get('results', [])[:5]:
results.append({
"title": item.get("title"),
"snippet": item.get("description"),
"url": item.get("url")
})
return {"results": results}
except Exception as e:
return {"error": str(e)}
return await asyncio.to_thread(_brave_search)
async def fetch_page(url: str) -> dict:
"""Fetch page via Playwright, bypassing CAPTCHAs if encountered"""
from backend.tools.browser_tools import get_ephemeral_context, stealth_async
from backend.tools.captcha_solver import solve_captcha_if_present
try:
async with get_ephemeral_context() as context:
page = await context.new_page()
if stealth_async:
await stealth_async(page)
await page.goto(url, wait_until="domcontentloaded", timeout=15000)
await page.wait_for_timeout(2000)
# Auto-solve CAPTCHA if Cloudflare/Turnstile/hCaptcha hard-blocks the article
await solve_captcha_if_present(page)
# Extract main text
content = await page.evaluate("document.body.innerText")
await page.close()
return {"status": "success", "content": content[:5000]} # Limit to 5000 chars for context limits
except Exception as e:
return {"error": str(e)}