dsh-web
zhu1090093659/dsh-webDeepSeek Harness(DSH)Web 插件聚合生态包 · 一切皆插件,创意工坊分发(dsh-market.com)
Doubao Search (Volcengine) web search provider for DeepSeek Harness — makes the standard web_search tool return real excerpts, not just titles and links.
Give DeepSeek Harness's standard web_search tool a search backend that returns
real excerpts, not just titles and links.
This is a search provider plugin for the harness's web capability seam (ctx.web).
It registers a Doubao Search (Volcengine) backend and points the seam at it, so the
model-facing tool @deepseek-ai/dsh-tool-web keeps working unchanged — only the
backend behind it changes.
English | 中文
The harness ships @deepseek-ai/dsh-web-search-deepseek, which performs search by
asking a model to call the server-side web_search tool over an
Anthropic-compatible Messages API. That route works, but the results it can surface
carry only a title and a URL — the response has no citation excerpts, so the
normalized sources[].snippet is always empty.
The Doubao Search API is a plain search endpoint that returns structured results,
including a per-result body. This plugin maps that body onto snippet, so every
source arrives with text the model can actually read and cite:
model-side web_search tool | this plugin (Doubao Search API) | |
|---|---|---|
sources[].title / .url | ✅ | ✅ |
sources[].snippet | ❌ always empty | ✅ real excerpt per source |
sources[].publishedAt | sometimes the 1970-01-01 epoch sentinel | real date, sentinel filtered out |
| Cost per search | one extra model turn (thousands of tokens) | one API call, no model tokens |
| Typical latency | 6–14 s | ~0.2–0.6 s |
>= 0.2.0-rc.2 (the ctx.web seam's provider API).>= 22.The endpoint defaults to https://open.feedcoopapi.com/search_api/web_search, which is
the public Doubao Search URL. No configuration is needed to reach it.
# straight from this repository
dsh plugin --profile web add github:gilsaia/dsh-web-search-doubao
# or from a local checkout
dsh plugin --profile web add /path/to/dsh-web-search-doubao
The package declares dsh.bundle.patch, so adding it also adds it to the profile's
bundle list, and its patch selects this provider:
- insert:
- id: web-search-doubao
name: 'dsh-web-search-doubao'
- id: web
config:
searchProvider: doubao
fetchProvider: http
fetchProvider is restated because a row patch replaces the targeted row's whole
config; omitting it would unset the fetch provider and break web_fetch.
About the
Issues with peer dependencies foundwarning: it is expected. The@deepseek-ai/dsh-*packages this plugin imports are provided by the harness itself at load time rather than installed into the profile, so pnpm reports them as missing peers. The harness resolves them when it mounts the plugin.
Restart the harness afterwards (or reload the profile), then run a search.
The plugin ships a client half. After one restart it adds a 联网搜索 (Web search) page to the harness settings sidebar, and everything below can be done there:
A save takes effect on the next search — no restart needed for either the config or the
key. The page mounts only while the host serves the web-search-doubao settings
namespace, so a deployment without the plugin shows no trace of it.
The plugin requires a key. It resolves one in this order:
apiKey config value;apiKeyEnv (default ARK_API_KEY), read through the
harness credentials service, falling back to the launching environment.So either export it:
export ARK_API_KEY=... # then start the harness
or set it in the plugin's config in the profile's patch layer:
- id: web-search-doubao
config:
apiKey: '...' # a secret; prefer apiKeyEnv so no key enters a config file
If the profile patch is still the shipped default, it contains a bare
[]. Replace that[]with your entries — appending a- id:item after it makes the file two YAML documents and the harness refuses to parse the overlay.
A missing key fails the search with WEB_PROVIDER_CREDENTIAL_MISSING and a message
naming the reference that was consulted — it never silently returns zero results.
| Field | Type | Default | Meaning |
|---|---|---|---|
apiKey | string (secret) | — | Literal API key. |
apiKeyEnv | credential ref | ARK_API_KEY | Credential reference resolved per search. |
baseURL | string | Doubao Search URL | Full endpoint; override for a proxy or a different region. |
searchType | web | image | web | image returns image results instead of pages. |
maxResults | 1–50 | 10 | Result count when a request carries no bound. |
timeRange | string | — | OneDay / OneWeek / OneMonth / OneYear, or YYYY-MM-DD..YYYY-MM-DD. |
authLevel | 0–2 | 0 | 1 restricts to authoritative sources. Web searches only. |
queryRewrite | boolean | false | Let the service rewrite a conversational query into a search-shaped one. |
needSummary | boolean | true | Request the excerpt surfaced as snippet. Turning this off empties snippet. |
baseURL can also be overridden with the DOUBAO_SEARCH_BASE_URL environment variable.
Every field is read per search, so a settings change applies to the next search without restarting the harness.
The plugin takes the seam's provider selection while it is mounted and hands the previous selection back when it unmounts, so the ordinary plugin switch is the switch:
doubao; searches use Doubao
Search.Nothing is written into your profile patch, so disabling genuinely returns the harness to
its pre-install state. That is why the bundle patch deliberately carries no - id: web
row patch: a row patch outlives the plugin being disabled and would leave the seam naming
a provider that no longer exists — the seam answers WEB_PROVIDER_CONFIGURED_MISSING and
does not fall back.
If you want the shipped provider to be usable while this plugin is off, configure it
directly; that is independent of this plugin. Its default endpoint is
https://api.deepseek.com/anthropic/v1, which will not accept a key minted for a different
platform:
- id: web-search-deepseek
config:
baseURL: https://ark.cn-beijing.volces.com/api/plan/v1 # an Anthropic-compatible endpoint
To pin the selection explicitly instead, do it in your own patch layer (applied after every
bundle layer) — but then keeping it pointed at a registered provider is your responsibility,
and disabling this plugin while it is pinned to doubao will error:
- id: web
config:
searchProvider: doubao
fetchProvider: http
# pure-function and plugin-contract tests: no network, no key
npm test
# live check against the real API; asserts sources carry snippets
ARK_API_KEY=... npm run test:live
To confirm the composition without booting:
dsh --profile web --dump-config | grep -A4 'id: web$'
# searchProvider: doubao
# fetchProvider: http
provider.js — DoubaoSearchProvider, the wire format, result mapping, and error
translation. It uses the native fetch client and does not go through ctx.llm.index.js — the Cordis plugin: name, inject: ['web'], the Config schema, and
apply(), which registers the provider into the seam.client/client.js — the browser half, declared as dsh.client and served by the host
under /plugins. Hand-authored against the loader's client-bundle envelope, so it
needs no build step and requires only the platform react. Every read and write goes
through the documented Remote namespaces (settings.describe / settings.mutate /
credentials.describe / credentials.set).cordis.patch.yml — the bundle patch that mounts the plugin. It does not touch the
seam's selection: apply() takes it on mount and returns it on disposal, so
enable/disable stays symmetric (see Turning it on and off).Two details worth knowing if you fork it:
Error object under
ResponseMetadata, so a successful status code is not enough — the body is checked
too.1970-01-01T08:00:00+08:00) for pages whose crawl
time is unknown. That is filtered out rather than surfaced as a publication date.@deepseek-ai/dsh-* peer dependencies are declared as open ranges (>=0.2.0-rc.2)
rather than pinned, so the plugin keeps working across harness release candidates.
DeepSeek Harness is in developer preview and may change the seam's API; if a future
version does, this plugin needs the same update as any other provider.
MIT