junjiangao/dsh-web-search-tavily ↗★ 0
@deepseek-ai/dsh-web-search-tavily
Tavily-backed search provider for the DeepSeek Harness web capability seam (ctx.web): keyless mode, full official search parameters, settings section, and credentials support
安装
npx -p @deepseek-ai/dsh dsh plugin --profile web add github:junjiangao/dsh-web-search-tavily说明文档
阅读完整 README ↗@deepseek-ai/dsh-web-search-tavily
English | 中文
A Tavily-backed WebSearchProvider for the DeepSeek Harness web capability seam (ctx.web), modeled on the packages/web/web-search-* family. It registers a provider into ctx.web — it does not own ctx.web and does not register a model-facing tool (that is @deepseek-ai/dsh-tool-web).
Highlights:
- Keyless mode — with no API key anywhere (config, settings, credentials, environment), requests run in Tavily's keyless mode: no
Authorization,x-tavily-access-mode: keyless, and thedsh-web-search-tavily-keylessclient source. - Full official search surface — every Tavily search parameter is configurable: depth, topic, time range, dates, days, result count, include/exclude domains, answer, raw content, images, favicon, usage, auto parameters, exact match, language, country, and chunks per source.
- Settings UI + credentials — a
dsh-settingssection (editable from the web settings page) plusdsh-credentialsresolution; the key can also come from a literalapiKeyor the environment. - Standard bundle — declares
dsh.bundle, installable withdsh plugin add.
Install
This package is distributed as a GitHub repository (not published to npm). Install it into a profile with dsh plugin, which forwards to pnpm:
# GitHub spec — main branch
dsh plugin --profile add github:junjiangao/dsh-web-search-tavily
# pin a commit when you need reproducibility (a later push cannot change it)
# dsh plugin --profile add github:junjiangao/dsh-web-search-tavily#
# or a local checkout
dsh plugin --profile add /path/to/dsh-web-search-tavily
# or a packed tarball (no build permission needed)
pnpm pack
dsh plugin --profile add ./dsh-web-search-tavily-0.1.0.tgz
Built lib/ artifacts are committed to the repository, and the package declares no lifecycle scripts, so a git install needs no build permission — pnpm never asks for an allowBuilds entry. When you develop the plugin, rebuild with pnpm build and commit the updated lib/ together with the source change. (A source-only revision without lib/ would need the pnpm allowBuilds step; prefer the main-branch flow above.)
Activate tavily as the search provider
Registering the provider is not the same as selecting it. The web seam picks a provider by id:
searchProvideron thewebplugin row, orDSH_WEB_SEARCH_PROVIDER(only read when thewebrow carries nosearchProvider—dsh-basecurrently setssearchProvider: deepseek-official, so the environment variable alone has no effect).
With several search providers installed, pin tavily in the profile's cordis.patch.yml (a later layer than every bundle):
- id: web
config:
searchProvider: tavily # restate every other key the web row carries
This bundle deliberately only inserts its own plugin row and never overrides the web row: a patch row replaces the whole row config instead of deep-merging, and multiple provider bundles must not fight over it.
Config
All fields optional except where the schema default is noted. The same schema powers the settings section.
| Key | Default | Meaning |
|---|---|---|
apiKey | (none) | Literal Tavily API key. Prefer apiKeyEnv/credentials so no secret enters configuration files; a stored literal is redacted from settings descriptions. |
apiKeyEnv | TAVILY_API_KEY | Credential reference / environment variable carrying the key. |
baseURL | https://api.tavily.com | Endpoint base; /search is appended. TAVILY_BASE_URL environment fallback. |
searchDepth | basic | ultra-fast |
topic | (none) | general |
timeRange | (none) | day |
startDate / endDate | (none) | Absolute dates (YYYY-MM-DD) sent as start_date / end_date. |
days | (none) | Day window sent as days. Positive integer. |
maxResults | (none) | Default result count when a request carries no maxResults; clamped to Tavily's 20. |
includeDomains / excludeDomains | [] | Domain lists sent as include_domains / exclude_domains; empty lists are omitted. |
includeAnswer | false | true |
includeRawContent | false | true |
includeImages | false | Sent as include_images; image results are not surfaced by the seam (deferred). |
includeImageDescriptions | false | Sent as include_image_descriptions. |
includeFavicon | false | Sent as include_favicon; not surfaced (deferred). |
includeUsage | false | Sent as include_usage; not surfaced (deferred). |
autoParameters | false | Sent as auto_parameters. |
exactMatch | false | Sent as exact_match. |
language | (none) | Sent as language. |
filterByLanguage | false | Sent as filter_by_language. |
country | (none) | Sent as country. |
chunksPerSource | (none) | Sent as chunks_per_source. Positive integer. |
Key resolution order: literal apiKey → dsh-credentials (apiKeyEnv ref) → environment (apiKeyEnv, default TAVILY_API_KEY) → keyless.
# With key (env)
- id: web-search-tavily
name: '@deepseek-ai/dsh-web-search-tavily'
config:
apiKeyEnv: TAVILY_API_KEY
# With key (literal — prefer credentials/env)
- id: web-search-tavily
name: '@deepseek-ai/dsh-web-search-tavily'
config:
apiKey: !!js process.env.TAVILY_API_KEY
# Keyless — no key anywhere, the provider falls back to keyless mode
- id: web-search-tavily
name: '@deepseek-ai/dsh-web-search-tavily'
Keyless mode
When every key source is empty, requests carry no Authorization, send x-tavily-access-mode: keyless, and use the dsh-web-search-tavily-keyless client source — the same convention as the official Tavily SDK. Keyless mode is a legitimate provider state, not a configuration error. Tavily's server rate-limits keyless use and may ignore or downgrade some parameters (result count, depth, answer). Only search (and extract) exist in keyless mode; this provider only ever calls /search.
Settings UI and credentials
WEB_SEARCH_TAVILY_SETTINGS_NAMESPACE = settingsNamespace('web-search-tavily') installs the configuration as a settings section, so the web settings page can edit every field above and changes apply to the next search without re-registration. The recommended key path is the credentials service (written from the web Models/settings page) under the apiKeyEnv reference. A literal apiKey stored in settings is supported but persists in the settings document — prefer credentials or the environment.
Mapping
answer(whenincludeAnsweris enabled) →content.- Each result →
WebSearchSource:url,title,publishedAt←published_date, andsnippetpreferringcontentwithraw_contentas fallback. Blank fields are omitted; URL-less results are dropped. max_resultsis clamped to 20 (Tavily's documented bound); the seam still enforces the finalmaxResultstruncation (truncated).
Model Experience
Indirectly, through dsh-tool-web: the model sees the maxResults-bounded URLs, titles, snippets, and publication dates, plus the generated answer when includeAnswer is enabled. Provider failures surface as WebError WEB_PROVIDER_ERROR (message from the Tavily error body, including the keyless-limit envelope); cancellation surfaces as WEB_ABORTED. Credential-bearing requests reject redirects before the Location target is contacted.
KV Cache effect
No direct invalidation; the named consumer owns any request-prefix changes.
Development
pnpm install
pnpm build # tsc → lib/; commit lib/ together with source changes
pnpm test # vitest unit suite
pnpm test:coverage # per-file 100% gate on src/
pnpm test:e2e # real-API smoke; self-skips without $TAVILY_API_KEY
Unlike harness in-repo packages (which extend tsconfig.base.json and build lib/types + bundled lib/index.js), this standalone package builds with a single tsc pass into lib/. The published API is unchanged. To vendor it into deepseek-harness/packages/web/web-search-tavily, switch the peer/dev dependencies to workspace:^ and adjust the tsconfig to the harness layout.
Known Limitations and Deferred Work
- Only
searchis implemented — no Tavilyextract/crawl/map/research; keyless mode only permits search/extract anyway. - Keyless is server-rate-limited and may downgrade parameters; the plugin makes no local assumptions.
includeImages/includeImageDescriptions/includeFavicon/includeUsagepass through to the API but the seam has no surface for images/usage/favicons yet.- Selection stays user-owned: installing this bundle registers the provider; pinning
searchProvider: tavilyis a profile-layer decision (see above). - Abort classification is signal-based: a fetch abort or an already-aborted signal maps to
WEB_ABORTED.
License
MIT