Files
Coder 81159a22e8
Some checks failed
build-and-publish / build-test (push) Failing after 1m6s
build-and-publish / publish (push) Has been skipped
chore: isolate shared dsh plugins into independent monorepo
2026-08-26 22:44:31 +07:00

53 lines
4.0 KiB
Markdown

# @deepseek-ai/dsh-web-search-searxng
English | [中文](README.zh.md)
A [SearXNG](https://docs.searxng.org)-backed `WebSearchProvider` for the harness [web capability seam](../web/README.md) (`ctx.web`). It calls a SearXNG instance's JSON API (`GET /search?format=json`) and maps the flat `results[]` into the seam's normalized `WebSearchResult`. It targets a self-hosted or private SearXNG: there is no single canonical public instance, so `baseURL` has no default, and the provider deliberately carries no API key or `Authorization` header.
This is an **implementation** package: it registers a provider into `ctx.web`, it does not own the `ctx.web` key and it does not register a model-facing tool (that is `@deepseek-ai/dsh-tool-web`). Like `@deepseek-ai/dsh-llm-deepseek`, it is a function/namespace plugin (`inject: ['web']`) that registers its backend, not a default-export service.
## Config
| Key | Default | Meaning |
|---|---|---|
| `baseURL` | (none) | SearXNG instance base; `/search` is appended. Empty/unparseable makes the provider unavailable. |
| `language` | `auto` | SearXNG `language` request value; `auto` lets the instance decide per user preferences. |
| `timeRange` | (unset) | SearXNG `time_range` recency filter: `day`, `week`, `month`, or `year`. Omitted sends no filter. |
```yaml
- id: web-search-searxng
name: '@deepseek-ai/dsh-web-search-searxng'
config:
baseURL: https://searx.example
```
The entry above is the base layer of the `web-search-searxng` Settings section: a user layer over it (a `settings.yaml` section or an in-session settings edit) reaches the NEXT search, because the provider projects the section per call rather than capturing it at registration. The seam's provider selection therefore never flickers when the instance or a filter changes. A section without `baseURL` still passes the schema but leaves the provider unavailable — no endpoint is guessed.
```yaml
# $DSH_HOME/settings.yaml
web-search-searxng:
baseURL: https://searx.example
language: auto
timeRange: day
```
SearXNG exposes no per-request result-count control — page size is instance configuration — so no `numResults` option exists; the seam enforces `maxResults` on the result.
## Mapping
SearXNG returns a flat `results[]` and no generated answer, so `content` is omitted. Each result maps to a `WebSearchSource`: `url``url`, `title``title`, `snippet``content`, `publishedAt``publishedDate`. Unlike the Exa provider it does **not** drop snippet-less entries: URL and title are still useful, so every result is kept. Provider failures (HTTP errors, network failure, unparseable or wrong-shape bodies) surface as `WebError` `WEB_PROVIDER_ERROR`; an aborted request surfaces as `WEB_ABORTED`. HTTP redirects are rejected before the `Location` target is contacted and surface as `WEB_PROVIDER_ERROR`. The request carries no `Authorization` header, so no credential can leak to a redirect target.
## Model Experience
Indirectly, through [`dsh-tool-web`](../tool-web/README.md), which retains this provider's `maxResults`-bounded URLs, titles, snippets, and publication dates or its exact `SearXNG search aborted`, `SearXNG search request failed: <error>`, and `SearXNG returned an unprocessable response body: <error>` failures under the consumer's error wrapper while generated answers and provider-private fields remain outside context.
#### KV Cache effect
No direct invalidation; the named consumer owns any request-prefix changes.
## Known Limitations and Deferred Work
- **Result count is not bounded at the request** — SearXNG page size is instance configuration, so the provider sends no count and the seam truncates on return; a single page (typically ~20 results) is fetched.
- **No aggregate-answer or infobox content is surfaced** — SearXNG `answers` and `infoboxes` are not mapped into `content`. No generated answer is trusted.
- **Abort classification is error-shape-based** — only a `DOMException` named `AbortError` maps to `WEB_ABORTED`; an abort carrying a custom reason (e.g. `dsh-timeout`'s `TimeoutReason`) surfaces as `WEB_PROVIDER_ERROR`.