dsh-search-providers
Đã xác minh@mini-z/dsh-search-providers · v0.3.0 · MIT
Modular DSH search plugin with Codex-first web search, page fetching, and capability-aware automatic failover.
Cài đặt
dsh plugin add @mini-z/dsh-search-providers Xác nhận layer đã áp bằng dsh --profile default --dump-config — xem hướng dẫn cài plugin.
Mã nguồn
Phát hành lên npm mà không có repository công khai. Hãy kiểm tra nội dung package trước khi cài.
Thẻ
Tác giả
Readme
@mini-z/dsh-search-providers
A modular DeepSeek Harness (DSH) plugin that provides web_search and web_fetch through multiple search services with capability-aware fallback and cooldown handling.
Codex is the default web_search provider. If its static credential is unavailable, or a request fails, the plugin proceeds through the configured fallback chain.
Latest update — 0.3.0
- Breaking: native
web_fetchtakeover. The plugin now supplies both native web tool backends and no longer registersread_page. - Remove page-reading focus parameters completely. Use
web_fetch({ url }); there is no fetchqueryparameter or TinyFish fetchpurposeforwarding. Search keywords are unchanged. - Extracted evidence is returned in native
body.content, with explicit truncation and unknown origin status (statusCode: 0) reporting. - Preserve TinyFish → Exa → Firecrawl fetch fallback, cooldown, cancellation, and a 200-second aggregate deadline.
- Existing JSON configuration and Codex credential formats remain supported. Update the plugin and restart DSH. See migration details and the changelog.
Upgrading from 0.1.x? Move provider settings from .env or inline Cordis configuration into ~/.dsh/search-providers.json. Official endpoints are built in, and fallback attempts now have a 55-second outer timeout. See configuration migration.
Features
- Replaces the native
web_searchprovider through the DSH web seam. - Replaces the native
web_fetchprovider for clean page extraction; no separateread_pagetool is registered. - Uses Codex-first search routing by default.
- Filters fallback candidates by capability, so search-only providers are never used for page fetching.
- Temporarily cools failing provider capabilities instead of repeatedly calling them.
- Disables only confirmed authentication failures until DSH restarts.
- Propagates caller cancellation without cooling providers or continuing fallback.
- Supports custom active-provider and fallback order configuration.
- Includes a key-free demo provider for wiring tests.
Provider support and default routing
| Provider | web_search |
web_fetch |
Credential |
|---|---|---|---|
| Codex | ✅ | ❌ | Static credential JSON referenced by providers.codex.credentialFile |
| TinyFish | ✅ | ✅ | providers.tinyfish.apiKey in JSON |
| Tavily | ✅ | ❌ | providers.tavily.apiKey in JSON |
| Exa | ✅ | ✅ | providers.exa.apiKey in JSON |
| Firecrawl | ✅ | ✅ | providers.firecrawl.apiKey in JSON |
| Demo | ✅ | ✅ | None |
Default web_search order:
- Codex
- TinyFish
- Tavily
- Exa
- Firecrawl
Default web_fetch order:
- TinyFish
- Exa
- Firecrawl
Codex and Tavily are skipped automatically for web_fetch because they do not implement page fetching. Providers without usable credentials are skipped without being marked as failed.
Native web_fetch migration
The bundle selects search-providers for both web.searchProvider and web.fetchProvider. DSH continues to own the native tool schemas, system guidance, and result cards; the plugin supplies the backends. It no longer registers read_page.
- Call
web_fetch({ url }). Page reading accepts no focus question: the formerqueryfield has been removed from both the tool interface and internal fetch request type, including TinyFish's fetchpurposemapping. Search query parameters are unchanged. - Results use the native
{ url, statusCode, body: { kind: 'text', content }, truncated }shape. Summary, extracted Markdown, links, uncertainty, and fallback warnings are rendered insidebody.content, not separate top-level fields. - Extraction providers do not expose the origin HTTP status or final redirect URL. The adapter returns
statusCode: 0(unknown, not HTTP success), retains the requested URL, and includes an explicit warning. Do not use this backend for HTTP status/redirect diagnostics. - Content is capped at 20,000 characters and outgoing links at 20; either cap sets
truncated: true. Native DSH presentation may apply an additional display cap. - The default fetch route is TinyFish → Exa → Firecrawl. There is no automatic fallback to DSH's direct HTTP provider. URLs are sent to third-party extraction services, unlike local HTTP retrieval; do not pass private or sensitive URLs.
- Existing JSON provider settings need no changes. Install the rebuilt plugin into the profile and restart DSH; editing a separate source checkout does not update an installed npm copy.
Installation
Install from npm
npx -y @deepseek-ai/dsh plugin --profile web add @mini-z/dsh-search-providers
Replace web with another DSH profile name when needed. Restart the corresponding DSH process after installation or configuration changes.
Install from a local checkout or tarball
npx -y @deepseek-ai/dsh plugin --profile web add /path/to/dsh-search-providers
The npm package and release tarballs include the built dsh/ artifacts. A local source checkout only needs rebuilding after source changes:
bun install --frozen-lockfile
bun run build
Configuration
The independent JSON configuration loader replaces inline Cordis settings and environment-variable configuration. Cordis accepts only the optional configFile setting; when omitted, it defaults to ~/.dsh/search-providers.json.
The package-root search-providers.example.json is included in the package. Copy it to your chosen configuration path and supply credentials for at least one provider. For example, ~/.dsh/search-providers.json:
{
"provider": "codex",
"fallbackChain": ["tinyfish", "tavily", "exa", "firecrawl"],
"providers": {
"codex": {
"credentialFile": "./codex-credential.json",
"timeoutMs": 55000
},
"exa": {
"apiKey": "replace-with-your-api-key",
"timeoutMs": 55000
}
}
}
The JSON fields are provider, fallbackChain, and providers. Provider entries support apiKey, timeoutMs, provider-specific options, and credentialFile for Codex. Official endpoints for Codex, TinyFish, Tavily, Exa, and Firecrawl are built in and cannot be overridden. The default provider request timeout is 55 seconds. Restrict configuration and credential files to the owning user; do not commit secrets.
File paths and loading
- Relative
configFilepaths resolve from the DSH process's current working directory, not the Cordis profile directory. - Both
~/and~\expand to the user's home directory. - Relative
providers.codex.credentialFilepaths resolve from the JSON configuration file's directory, not the process working directory. This field also supports~/and~\home expansion. - The JSON configuration is loaded once at plugin startup. Restart the corresponding DSH process after changes; there is no live reload.
- A missing default file logs a warning and uses empty configuration, with no credentials.
- An explicitly specified missing file, invalid JSON, or a schema-invalid configuration throws a clear error rather than silently falling back.
- The plugin no longer reads environment variables, including API keys, endpoints, timeouts, Codex credentials, and demo mode. Values in
~/.dsh/.envare not used by this plugin.
Override the JSON configuration path
To use a different file, add an id-targeted override to ~/.dsh/profiles/<profile>/cordis.patch.yml:
- id: search-providers
config:
configFile: ~/.dsh/search-providers-work.json
Omit configFile to use the default path. Do not put provider settings or secrets in this Cordis override.
Change the active provider or fallback chain
Set provider and fallbackChain in the JSON file, for example:
{
"provider": "exa",
"fallbackChain": ["codex", "tinyfish", "tavily", "firecrawl"],
"providers": {
"exa": {
"apiKey": "replace-with-your-api-key",
"timeoutMs": 55000
}
}
}
Routing rules:
provideris always attempted first when it is available and supports the requested capability.fallbackChaincontains the providers attempted afterward.- Duplicate provider ids are removed while preserving order.
fallbackChain: []disables fallback and uses only the active provider.- Providers that do not support the requested capability are omitted from attempts and diagnostics.
For TinyFish, Tavily, Exa, and Firecrawl, provider entries support apiKey, timeoutMs, and provider-specific options. Generic options remains supported, but does not allow endpoint overrides. TinyFish uses built-in official endpoints for both search and page fetching.
Codex uses credentialFile and timeoutMs from its provider configuration. Its endpoint is fixed, and it intentionally ignores apiKey and options; credentials come exclusively from the file selected by providers.codex.credentialFile.
Migration from inline Cordis and environment settings
- Create the independent JSON configuration using the packaged example. Move inline Cordis
provider,providers, andfallbackChainvalues into that JSON file. Old inline provider/fallback settings are rejected with a migration message; they are not merged or silently ignored. - Move each provider's
*_API_KEYand*_TIMEOUT_MSvalues into its JSONapiKeyandtimeoutMsfields. MoveCODEX_CREDENTIAL_FILEtoproviders.codex.credentialFileandCODEX_TIMEOUT_MStoproviders.codex.timeoutMs. Check relative credential paths against the JSON file directory. Do not migrate*_BASE_URLvalues: official endpoints are built in. - Replace
SEARCH_PROVIDERS_DEMOwith"provider": "demo"in JSON when demo mode is wanted. Environment variables no longer configure any provider. - Remove any existing provider
baseUrlfields and TinyFishoptions.fetchBaseUrlfrom the JSON configuration; these legacy endpoint overrides are rejected. Remove an emptyoptionsobject if nothing remains. Keep API keys, timeouts, routing (providerandfallbackChain), CodexcredentialFile, and other supported provider options. - Remove the old inline fields from the Cordis plugin configuration, leaving only an optional
configFile. Restart DSH to load the new file.
Fallback, cooldown, and cancellation behavior
The plugin rechecks provider state before every attempt instead of relying on a stale snapshot. A provider whose cooldown expires while other providers are being tried may participate again in the same request.
| Failure class | Effect |
|---|---|
| Confirmed authentication failure | Provider disabled globally until DSH restarts |
| HTTP 429/432, quota, or rate limit | Current capability cooled for 5 minutes |
| Network error or timeout | Current capability cooled for 30 seconds |
| Other request failure | Current capability cooled for 10 seconds |
Transient state is capability-scoped: a search failure does not cool web_fetch, and a fetch failure does not cool web_search. Authentication disablement is provider-wide.
The first eligible provider uses its configured request timeout. Each subsequent fallback attempt has an additional 55-second outer limit, which also stops providers that ignore AbortSignal. The bundle sets the native web_fetch tool's fetchTimeoutMs to 200 seconds to preserve the former page-reading aggregate deadline. These request deadlines are separate from the cooldown durations in the table above.
Caller cancellation is different from failure: it propagates immediately, preserves the original abort reason, does not continue fallback, and does not alter provider state.
Successful search output identifies the provider that actually answered and reports fallback, cooling, and disabled-provider state. Final errors list the providers genuinely attempted and the current live state.
Codex credential file
Codex uses the standalone ChatGPT Codex search endpoint and is independent of model/login plugins. It only reads an existing static credential file; it never starts a login, refreshes a token, checks local expiry timestamps, or writes credential data.
Minimal supported JSON:
{
"access_token": "eyJ...",
"account_id": "account-id"
}
The parser also accepts credential and tokens wrappers, access as an alternative token field, and accountId as an alternative account field. If the account id is omitted, the plugin reads the standard chatgpt_account_id claim from the access token. Missing or malformed credentials make Codex unavailable. Codex HTTP 401/403 responses are treated as confirmed credential failures and trigger the normal fallback chain.
Account-store files such as .openai-codex-auth.json are also supported: the parser reads access and accountId from the credentials array entry matching activeAccountId. Without an active selector, only a single-entry array is accepted; ambiguous or missing matches are not guessed. The store is read-only, and the original formats remain supported.
Restrict the credential file to the owning user. Codex does not implement web_fetch, and its standalone endpoint does not apply this plugin's recency, domain, location, language, or maximum-result filters.
Usage
After loading the plugin and configuring at least one provider, ask the model to search:
Search the web for "latest Rust memory safety features".
Successful results start with the provider that actually answered:
[Search provider: TinyFish]
Read a specific page with:
Read this page: https://example.com/article
web_fetch returns the native fetch result, with summary, extracted content, outgoing links, uncertainty, and operational warnings rendered in body.content.
Demo mode
Select the key-free demo provider in your JSON configuration file:
{
"provider": "demo",
"fallbackChain": []
}
Restart DSH after changing the file. Demo mode returns canned search and page-fetch results for integration testing; no environment variable is needed or read.
Development
bun install --frozen-lockfile
bun run typecheck
bun run test
bun run build
Main components:
src/providers/types.ts— provider interfaces and result types.src/providers/registry.ts— provider registration and active-provider selection.src/providers/cooldown.ts— authentication disablement and capability cooldown state.src/providers/*.ts— provider implementations.src/tools/web-search.ts— DSH web seam adapter and search fallback.src/tools/read-page.ts—web_fetchregistration and fetch fallback.src/index.ts— plugin entry point and default provider order.
To add a provider, implement SearchProvider, register it in src/index.ts, and add its configuration resolution.
Publishing
- Bump the version in
package.json. - Run the development checks and inspect
bun pm pack --dry-run. - Log in with
npm login. - Publish the public scoped package with
npm publish --access public.
Published npm versions are immutable; every release requires a new version number.
License
MIT. The Codex interoperability implementation was developed with reference to dsh-codex-connect (Apache-2.0); no runtime dependency on that package is required.