DSH Web Search Z.AI
Native-feeling web_search for DeepSeek Harness (DSH) installs whose model route is Z.AI / GLM, delivered as one dependency-free Cordis plugin for the host web seam (ctx.web).
The shipped harness selects deepseek-official as its search provider, which requires a real DeepSeek API key. On an install that runs GLM through the zai provider, only ZAI_API_KEY exists — so every web_search fails with HTTP 401. This package registers a zai-official search provider that performs searches through Z.AI's Anthropic-compatible Messages API using the native web_search_20250305 server tool (glm-5.3), authenticating with the ZAI_API_KEY credential you already have, and flips the web seam's selection to it. The Profile Bundle patch carries both rows; a fresh install needs no manual composition editing.
How it works
- Sends one Messages request per search: model
glm-5.3, theweb_search_20250305server tool (max_uses: 3),max_tokens: 512— sources come from the tool-result blocks, so the completion budget stays small and inside the web tool's 60s timeout. - Z.AI's Anthropic dialect returns tool results as
tool_resultblocks whose content is a stringified Python-repr/JSON hybrid rather than standardweb_search_tool_resultblocks (which is exactly why the shippedweb-search-deepseekprovider cannot simply be repointed at Z.AI). A quote-aware scanner extractstitle/link/contentrecords from either shape, standard blocks included for forward compatibility. - Transient upstream failures (
Internal Network Failure, 5xx, timeouts) are retried up to 3 times; permanent errors surface verbatim with the HTTP status. - Each request is appended to the initiating session log as
web/zai-search-llm-requestfor observability, mirroring the shipped provider. - Registration goes through
web.registerSearchProvider, whose disposer is fiber-owned — removing the plugin row cleanly unregisters the provider and (on a fresh apply of the base config) restores the previous selection.
Requirements
- DeepSeek Harness with the
webseam (the standardwebprofile; any base-backed profile works). - A Z.AI API key stored as the
ZAI_API_KEYcredential — the web Models page writes it, or use your credentials store directly. No other credentials, environment variables, or npm dependencies.
Cost note: every search is a GLM model turn with server-side search. It bills like a small model call, not like a free search-API hit.
Install
Install into your web profile (the package carries its own Profile Bundle patch — no manual composition row is needed):
dsh plugin --profile web add @canary-builds/dsh-web-search-zai
Restart DSH. To update, run the same command again and restart.
If your profile's cordis.patch.yml already overrides the web row (any manual searchProvider selection), remove that override — profile patches apply after bundle patches and would win. Likewise, remove any manually inserted web-search-zai row before switching to the bundle install; two rows with the same id will collide.
Install from a git checkout (pre-release)
Until the first npm release, install from a clone:
git clone https://github.com/Canary-Builds/dsh-web-search-zai \
~/.dsh/profiles/web/plugins/dsh-web-search-zai
ln -sfn ../plugins/dsh-web-search-zai \
~/.dsh/profiles/web/node_modules/dsh-web-search-zai
Then add the rows to ~/.dsh/profiles/web/cordis.patch.yml (the same content as this package's cordis.patch.yml, with the row name dsh-web-search-zai matching the unscoped symlink):
- insert:
- id: web-search-zai
name: dsh-web-search-zai
- id: web
name: '@deepseek-ai/dsh-web'
config:
searchProvider: zai-official
fetchProvider: http
Profiles with patchReload: live pick the change up without a restart; otherwise restart DSH.
Switching back to DeepSeek
Store a real DeepSeek key as DEEPSEEK_API_KEY, remove this package (dsh plugin --profile web remove @canary-builds/dsh-web-search-zai), and restore searchProvider: deepseek-official in the web row. The shipped provider row is never touched by this bundle's patch while it is installed — but removing the bundle restores the base config on the next start.
Configuration
There is no settings card and no schema dependency — deliberately, to keep the package installable with zero npm dependencies. The knobs are the constants at the top of index.js:
| Constant | Default | Meaning |
|---|---|---|
MODEL |
glm-5.3 |
Model that performs the search turn |
MAX_USES |
3 |
Server-tool search passes per request |
MAX_TOKENS |
512 |
Completion budget (sources arrive in tool blocks) |
MAX_ATTEMPTS / RETRY_DELAY_MS |
3 / 700 |
Transient-failure retry policy |
REQUEST_TIMEOUT_MS |
45000 |
Per-attempt timeout, inside the web tool's budget |
API_KEY_REF |
ZAI_API_KEY |
Credentials-store reference |
Compatibility
- Targets the DSH
webseam contract (registerSearchProvider/WebSearchProvider) as shipped in0.1.x; the seam is stable, but a future DSH that changes the provider contract will need a re-test. - The Z.AI tool-result dialect is parsed tolerantly (Python-repr and JSON shapes, nested or flat). If Z.AI changes the shape entirely, searches fail with a descriptive
WEB_PROVIDER_ERRORnaming the endpoint — file an issue.
Behavior notes
- The provider reports
available()only when the credentials service is present; searches throwWEB_PROVIDER_CREDENTIAL_MISSINGwith setup instructions whenZAI_API_KEYis unset. - URLs are deduped across multiple search passes within one request; snippets are capped at 400 characters; the seam's
maxResultstruncation applies downstream. - Cancellation is honored end-to-end (
WEB_ABORTED), including mid-fetch.
License
MIT
Development
Single-file ESM plugin, no build step, no dependencies: edit index.js, restart DSH (or rely on a live profile patch reload after touching cordis.patch.yml). Functional smoke test: import apply, mount it on a mock ctx exposing web.registerSearchProvider and the credentials seam, and call provider.search({ query }). Report an issue · Canary Builds
No comments yet. Be the first to write one.