yc1838
feat: add print logs for memory, search, and tool operations
f09a362
Raw
History Blame Contribute Delete
3.96 kB
from __future__ import annotations
from ddgs import DDGS
from tavily import TavilyClient, UsageLimitExceededError
from tavily.errors import ForbiddenError
_SEARCH_RECOVERY_HINT = (
"SEARCH_HINT: no useful results. Try: (1) rephrase with alternative keywords or narrower scope; "
"(2) for academic/scientific queries, use arxiv_search or crossref_search; "
"(3) for removed or historical pages, search via https://web.archive.org."
)
_SEARCH_LOG_PREVIEW_CHARS = 240
def _search_preview(text: str) -> str:
compact = " ".join(str(text).split())
if len(compact) <= _SEARCH_LOG_PREVIEW_CHARS:
return compact
return compact[: _SEARCH_LOG_PREVIEW_CHARS - 1] + "…"
def _print_search_log(message: str, *args) -> None:
text = message % args if args else message
print(text, flush=True)
def _ddg_search(query: str, max_results: int) -> str:
results = DDGS().text(query, max_results=max_results)
lines = [
f"- {r.get('title', '')} ({r.get('href', '')})\n {r.get('body', '')}"
for r in results
]
return "\n".join(lines) if lines else "No results."
def web_search(query: str, api_key: str, max_results: int = 5) -> str:
# 1. Try DuckDuckGo first
_print_search_log("[web_search] start provider=duckduckgo query=%r max_results=%d", query, max_results)
try:
ddg_results = _ddg_search(query, max_results)
except Exception as e:
ddg_results = f"DDG ERROR: {e}"
_print_search_log(
"[web_search] provider=duckduckgo status=error query=%r error=%r",
query,
str(e),
)
if ddg_results and "No results." not in ddg_results and not ddg_results.startswith("DDG ERROR"):
out = f"[Source: DuckDuckGo]\n{ddg_results}"
_print_search_log(
"[web_search] provider=duckduckgo status=success chars=%d preview=%r",
len(out),
_search_preview(out),
)
return out
_print_search_log(
"[web_search] provider=duckduckgo status=empty_or_failed chars=%d preview=%r",
len(ddg_results or ""),
_search_preview(ddg_results or ""),
)
# 2. Fallback to Tavily if DDG failed or returned nothing
if not api_key:
return f"[Source: DuckDuckGo (Empty)]\n{ddg_results}\n[Tavily fallback skipped: no key]\n{_SEARCH_RECOVERY_HINT}"
client = TavilyClient(api_key=api_key)
_print_search_log("[web_search] start provider=tavily query=%r max_results=%d", query, max_results)
try:
res = client.search(query=query, max_results=max_results)
lines: list[str] = []
for item in res.get("results", []):
lines.append(f"- {item['title']} ({item['url']})\n {item['content']}")
if not lines:
out = f"[Source: Tavily (DDG fallback: {ddg_results})]\nNo results.\n{_SEARCH_RECOVERY_HINT}"
_print_search_log(
"[web_search] provider=tavily status=empty chars=%d preview=%r",
len(out),
_search_preview(out),
)
return out
tavily_results = "\n".join(lines)
out = f"[Source: Tavily (DDG fallback: {ddg_results})]\n{tavily_results}"
_print_search_log(
"[web_search] provider=tavily status=success chars=%d preview=%r",
len(out),
_search_preview(out),
)
return out
except (UsageLimitExceededError, ForbiddenError) as e:
_print_search_log("[web_search] provider=tavily status=limit_or_forbidden query=%r error=%r", query, str(e))
return f"[Source: DuckDuckGo (Empty)]\n{ddg_results}\n[Tavily fallback failed: limit/forbidden: {e}]\n{_SEARCH_RECOVERY_HINT}"
except Exception as e:
_print_search_log("[web_search] provider=tavily status=error query=%r error=%r", query, str(e))
return f"[Source: DuckDuckGo (Empty)]\n{ddg_results}\n[Tavily fallback failed: {e}]\n{_SEARCH_RECOVERY_HINT}"