Files
my-pi/pi-web-search
云服务部-叶林立andDevin <158243242+devin-ai-integration[bot]@users.noreply.github.com> ee1e6999d7 feat(pi-web-search): 统一 web_search/web_fetch 并由主模型路由搜索厂商
7 个按厂商命名的搜索工具收敛为 2 个统一工具,schema 与提示词不再暴露
provider 概念:web_search 由当前主模型按内置规则在 Tavily/Exa/Keenable
间路由(会话内 LRU 缓存,失败按可用性降级),web_fetch 固定走 Exa 并
静默降级。三家均改为纯 fetch 直连——Exa 直接对 mcp.exa.ai 发 JSON-RPC
并缓存会话,彻底移除 pi-mcp-adapter、@tavily/pi-extension 与
@keenable/pi-search 依赖。

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-10-19 20:13:37 -07:00
..

pi-web-search

Unified web search and fetch for Pi. The main model only ever sees two tools — web_search and web_fetch — provider selection happens inside the extension.

  • web_search(query, max_results?, site?, published_after?, published_before?, depth?) routes each query to Tavily, Exa, or Keenable using the session's current main model (ctx.model + ctx.modelRegistry.complete), guided by per-provider rules. Results include details.provider / details.routedBy for debugging.
  • web_fetch(urls, max_characters?) always reads pages via Exa (web_fetch_exa), degrading silently to Keenable then Tavily.

Providers (clients/)

Plain fetch calls — no MCP adapter, no upstream extension wrappers:

  • clients/tavily.ts — POST api.tavily.com/search|extract, Bearer key. Requires TAVILY_API_KEY.
  • clients/keenable.ts — api.keenable.ai, keyless /public endpoints by default, KEENABLE_API_KEY raises rate limits. Adapted from @keenable/pi-search.
  • clients/exa.ts — JSON-RPC over mcp.exa.ai/mcp (Streamable HTTP): initialize once, cache mcp-session-id, tools/call per request, re-init on session expiry. EXA_API_KEY optional (x-api-key header).

Keys are loaded from ~/.config/my-pi/search.env by extensions/search-config.ts (written by search_config.sh); explicit environment variables win.

Routing

router.ts asks the current model to answer with exactly one provider name, given a prompt containing the per-provider strengths and the query's requested filters. Decisions are cached per session (LRU, 32 entries). Failure paths are deterministic: an unroutable answer or a dead provider falls back across the remaining available providers — never a heuristic choice over the model.