dsh-web-search-glm
Zhipu GLM search provider for the DeepSeek Harness (dsh) web seam — native web_search via the Anthropic-compatible API
- Stars
- 0
- Language
- JavaScript
- Created
- Aug 28, 2026
- Updated
- Aug 28, 2026
Introduction
dsh-web-search-glm
Zhipu GLM-backed search provider for the dsh (ctx.web) seam. It executes the native web_search_20250305 server tool through GLM's Anthropic-compatible endpoint and maps GLM's web_search_prime result blocks to normalized search sources.
How it works
The outgoing request is shaped exactly like the official @deepseek-ai/dsh-web-search-deepseek provider's: POST {baseURL}/messages carrying the web_search_20250305 server tool (bounded by max_uses), the anthropic-version header, redirect: "error", and full AbortSignal support.
The difference is entirely in the response mapping. Instead of Anthropic-standard web_search_tool_result blocks, GLM returns its own pair of blocks:
server_tool_useblocks namedweb_search_prime, paired withtool_resultblocks whosecontentis a stringified Python-repr list of search results.
The provider pairs server_tool_use with tool_result by id (multiple searches in one response are merged), parses the content (JSON.parse first, then a small hand-written Python-repr tokenizer), maps each {title, link, content} entry to {url, title, snippet}, and dedupes by url.
If the response contains no usable structured search results, the search fails with WEB_PROVIDER_ERROR — there is no scraping or prose fallback, matching the official provider's semantics. On non-2xx responses the server-side error message is passed through verbatim (still WEB_PROVIDER_ERROR). Other codes: WEB_PROVIDER_CREDENTIAL_MISSING when no API key resolves, WEB_ABORTED on cancellation.
Observed endpoint behavior (live test, 2026-08-27): GLM does not reject nonsense queries — it still executes the web search and returns ~10 sources, so the "no results" branch is rarely taken; this is endpoint behavior, not something the provider configures. Searches on this endpoint were billed under the GLM Coding Plan, like chat traffic.
Install
From the dsh plugin marketplace (once the package is listed):
dsh plugin --profile <profile> add dsh-web-search-glm
Or from a local checkout:
dsh plugin --profile <profile> add /path/to/dsh-web-search-glm
Either way, you must explicitly select the provider. dsh-web reads its selection from the web row's config — the dsh-base bundle pins deepseek-official, and a web: section in ~/.dsh/settings.yaml has no effect there. Override it in your profile's own patch layer, ~/.dsh/profiles/<profile>/cordis.patch.yml (applied after every bundle layer):
# ~/.dsh/profiles/web/cordis.patch.yml
- id: web
config:
searchProvider: glm
A patch replaces the targeted row's whole config — restate every key the base row owns (today just searchProvider).
Exporting DSH_WEB_SEARCH_PROVIDER=glm in the launching environment works too.
Without this, if the official @deepseek-ai/dsh-web-search-deepseek provider is also installed and available, more than one search provider is simultaneously available() and dsh-web throws WEB_PROVIDER_AMBIGUOUS rather than guessing.
Configuration
Settings live in the web-search-glm section of ~/.dsh/settings.yaml:
| Key | Default | Description |
|---|---|---|
apiKey | — | Literal API key (secret). Prefer the apiKeyEnv credential reference. |
apiKeyEnv | ZAI_API_KEY | Credential reference name. Keep it in sync with the apiKeyEnv of the zai provider in your dsh settings so both read one stored key. |
baseURL | https://open.bigmodel.cn/api/anthropic/v1 | Anthropic-compatible endpoint; /messages is appended by the provider. The overseas deployment can point to https://api.z.ai/api/anthropic/v1. |
model | glm-5.3 | Model that executes the native web search. Verified live — see Model notes. |
apiVersion | 2023-06-01 | Value of the anthropic-version header. |
maxTokens | 4096 | Upper bound on generated tokens for the Messages request. |
maxUses | 5 | Maximum web_search server-tool uses per request. |
Example:
# ~/.dsh/settings.yaml — plugin options (provider selection lives in the
# profile patch, see Install):
web-search-glm:
apiKeyEnv: ZAI_API_KEY
# baseURL: https://api.z.ai/api/anthropic/v1 # overseas endpoint
# model: glm-5.3-flash # verified alternative, see Model notes
Credential resolution chain. The key is resolved per search, in order: a literal apiKey set in the web-search-glm section → the credentials service via the apiKeyEnv reference → a launch-environment variable of the same name. If nothing resolves, the search fails with WEB_PROVIDER_CREDENTIAL_MISSING.
Endpoint environment fallback. If the web-search-glm section does not set baseURL, the launching environment's GLM_SEARCH_BASE_URL is consulted before the built-in default. The variable is deliberately distinct from any chat-completions base-URL variable — search speaks the Anthropic-compatible Messages API under its own address (mirroring upstream DEEPSEEK_SEARCH_BASE_URL).
Model notes
glm-5.3(default) — verified live on 2026-08-27: english and chinese queries each returned 10 sources through the native web search.glm-5.3-flash— verified live on 2026-08-27: all four smoke cases (english / chinese / nonsense / bad-key) behaved identically toglm-5.3, with real searches executed (english and chinese each returned 10 sources). A working alternative — setmodel: glm-5.3-flashto use it.glm-5-flash— does not exist on this endpoint. Every request is rejected by the server with[1214][modelCode:不存在](surfaced asWEB_PROVIDER_ERROR). Do not use this name.
The package default stays glm-5.3; it is not changed merely because the flash variant is available.
Compatibility
- Targets the dsh plugin seam protocol of
dsh >= 0.1.1-rc.2(peer dependencies@deepseek-ai/dsh-*^0.1.1-rc.2). - MIT License — see LICENSE.
Development
npm test— parser unit tests on the built-innode:testrunner; no extra dev dependencies. Fixtures are real captured GLM responses undertest/fixtures/.npm run capture— one-off live capture that refreshestest/fixtures/(scripts/capture.mjs). NeedsZAI_API_KEY(falls back toANTHROPIC_AUTH_TOKEN).npm run smoke— live-endpoint smoke run of four cases (english / chinese / nonsense / bad-key):node scripts/smoke.mjs [model]. NeedsZAI_API_KEY(falls back toANTHROPIC_AUTH_TOKEN).
The live scripts intentionally live in scripts/, not test/ — Node 26's node --test auto-discovery would execute them during npm test — and they are not shipped in the published npm package.