deepseek-harness
deepseek-ai
DeepSeek Harness: Everything is a Plugin.
PROJECT TOPICS
PROJECT README
Multi-backend web_search provider for DeepSeek Harness (DSH), registered into the web capability seam (ctx.web) under one stable provider id (web-search-ext).
The built-in web_search tool is backend-pluggable; the in-box default (deepseek-official) requires a DeepSeek API key. This plugin provides a key-free-capable alternative with automatic failover between backends.
| Backend | With key | Without key |
|---|---|---|
| Exa | REST POST https://api.exa.ai/search (higher limits, highlight snippets) |
Anonymous hosted MCP POST https://mcp.exa.ai/mcp (JSON-RPC 2.0, documented public fallback, rate-limited → HTTP 429) |
| Firecrawl | POST https://api.firecrawl.dev/v2/search (Bearer) |
Keyless requests when firecrawlKeyless: true (unofficial; may be rate-limited or removed) |
On any backend failure (429, 401/402/403, 5xx, network, malformed body) the search fails over to the next backend. A 429 additionally starts a per-backend cooldown (default 60 s) so a saturated backend is skipped on subsequent searches. When all backends fail, the error lists each failure (including cooldown state).
The provider is deliberately small and uniform; a backend is:
async function <name>Search(options, apiKey?, request, signal) that returns { sources, truncated: false } and throws WebError (WEB_RATE_LIMIT for 429, WEB_PROVIDER_ERROR otherwise, aborts as WEB_ABORTED);plan in search() when its availability condition holds (key present, or keyless mode allowed);Config schema and key resolution in resolveKey().No other code changes: failover, cooldown, abort handling, and snippet bounding all apply automatically. A self-hosted SearXNG instance (JSON API) is the natural next candidate — no key, no rate limit of your own, fully local.
dsh plugin --profile web add dsh-web-search-ext
# or from a local checkout:
dsh plugin --profile web add ./path/to/dsh-web-search-ext
Installing a plugin requires restarting the running dsh web process (the profile bundle list is resolved at boot). Config changes afterwards are hot — no restart.
The bundle patch selects this provider for the web_search tool by setting web.searchProvider: web-search-ext. The official deepseek-official provider stays registered but unused; an explicit selection also prevents WEB_PROVIDER_AMBIGUOUS.
Any of these, in order of precedence per backend:
exaApiKey / firecrawlApiKey)EXA_API_KEY / FIRECRAWL_API_KEY (the Web "Models" page writes them; ~/.dsh/.credentials.yaml)No keys at all still works: Exa uses its anonymous MCP endpoint and Firecrawl is tried keyless.
Settings namespace web-search-ext in ~/.dsh/settings.yaml (hot-reloaded):
web-search-ext:
preferred: exa # exa | firecrawl — which backend to try first
# exaApiKeyEnv: EXA_API_KEY # defaults shown
# firecrawlApiKeyEnv: FIRECRAWL_API_KEY
# exaApiUrl: https://api.exa.ai/search
# exaMcpUrl: https://mcp.exa.ai/mcp
# firecrawlBaseUrl: https://api.firecrawl.dev/v2
numResults: 8 # default result count when the tool doesn't cap it
maxSnippetChars: 500 # snippet length bound
rateLimitCooldownSec: 60 # skip a 429'd backend this long; 0 disables
firecrawlKeyless: true # allow keyless Firecrawl requests
Or select this provider without the bundle patch: DSH_WEB_SEARCH_PROVIDER=web-search-ext.
dsh plugin --profile web remove dsh-web-search-ext # then restart dsh web
authorization header of their own backend's requests — never in bodies, never to the other backend, never in error messages.postinstall/prepare.maxSnippetChars) and Firecrawl's page-markdown descriptions are stripped of image links before entering model context.mcp.exa.ai/mcp returned structured Title:/URL:/Highlights: results without credentials.X402_PAYMENT_REQUIRED) — treated as "no key", not an error to surface.data.web[] (v1 shape data[] is also handled).node test/failover.test.mjs — 10 mocked failover/mapping scenarios + live smoke calls.CLASSIFICATION EVIDENCE
系统优先读取 GitHub Topics,再与站内分类词典和词根规则比对。当前命中: 无有效分类标签。