@deepseek-ai/dsh-web-search-deepseek 0.1.7-rc.2 → 0.2.0-rc.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +15 -12
- package/README.md +12 -7
- package/README.zh.md +12 -7
- package/lib/index.js +61 -15
- package/lib/types/index.d.ts +4 -2
- package/lib/types/provider.d.ts +15 -1
- package/package.json +20 -13
package/README.i18n.yaml
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
en: d2c50aae9d869902
|
|
10
10
|
zh: ee9f3f0ff575cbf9
|
|
11
11
|
/deepseek-ai-dsh-web-search-deepseek/summary:
|
|
12
|
-
en:
|
|
13
|
-
zh:
|
|
12
|
+
en: 1810224f58c8d39b
|
|
13
|
+
zh: 2e253cb447ee49c4
|
|
14
14
|
/deepseek-ai-dsh-web-search-deepseek/table-of-contents:
|
|
15
15
|
en: d152484eb41ac6b4
|
|
16
16
|
zh: 09388d293f9be9cb
|
|
@@ -18,11 +18,14 @@
|
|
|
18
18
|
en: e0d352d6402d9d3b
|
|
19
19
|
zh: a449234557ef8bd6
|
|
20
20
|
/deepseek-ai-dsh-web-search-deepseek/use-this-package/when-to-choose-it:
|
|
21
|
-
en:
|
|
22
|
-
zh:
|
|
21
|
+
en: 0187c8af8d2d98c5
|
|
22
|
+
zh: 1c9607b3a662bcaa
|
|
23
23
|
/deepseek-ai-dsh-web-search-deepseek/use-this-package/minimal-configuration:
|
|
24
|
-
en:
|
|
25
|
-
zh:
|
|
24
|
+
en: 0553b6c926c44cf4
|
|
25
|
+
zh: 5cd3221d3e8eebd9
|
|
26
|
+
/deepseek-ai-dsh-web-search-deepseek/use-this-package/authentication:
|
|
27
|
+
en: 3e49592f15e94825
|
|
28
|
+
zh: c1bcf6dbb946149a
|
|
26
29
|
/deepseek-ai-dsh-web-search-deepseek/use-this-package/what-a-search-returns:
|
|
27
30
|
en: e30442c90365357b
|
|
28
31
|
zh: 9c9e2f1182963a8a
|
|
@@ -36,14 +39,14 @@
|
|
|
36
39
|
en: 9842c69ad3636bb3
|
|
37
40
|
zh: 3cdc3f816cbdd0cb
|
|
38
41
|
/deepseek-ai-dsh-web-search-deepseek/understand-the-implementation/design-philosophy:
|
|
39
|
-
en:
|
|
40
|
-
zh:
|
|
42
|
+
en: 1b1760efbf4568c1
|
|
43
|
+
zh: fca06c959e51ce16
|
|
41
44
|
/deepseek-ai-dsh-web-search-deepseek/understand-the-implementation/source-map:
|
|
42
45
|
en: d354ae3f0f21f7c6
|
|
43
46
|
zh: 95eb16d30d9c7a9c
|
|
44
47
|
/deepseek-ai-dsh-web-search-deepseek/understand-the-implementation/request-flow:
|
|
45
|
-
en:
|
|
46
|
-
zh:
|
|
48
|
+
en: d6469ab9ac915762
|
|
49
|
+
zh: 07ddf7b12c748eaf
|
|
47
50
|
/deepseek-ai-dsh-web-search-deepseek/further-exploration:
|
|
48
51
|
en: 59a97f197c044508
|
|
49
52
|
zh: de191dd060c31801
|
|
@@ -66,8 +69,8 @@
|
|
|
66
69
|
en: 666f5753c99783ab
|
|
67
70
|
zh: b44328e99bcc45c9
|
|
68
71
|
/deepseek-ai-dsh-web-search-deepseek/model-experience/conversation-tool-result-indirectly/what-the-model-sees:
|
|
69
|
-
en:
|
|
70
|
-
zh:
|
|
72
|
+
en: 0764d19c9b82aa98
|
|
73
|
+
zh: 88b0ba22ef64225b
|
|
71
74
|
/deepseek-ai-dsh-web-search-deepseek/model-experience/conversation-tool-result-indirectly/token-effect:
|
|
72
75
|
en: 01ec43a9b616139d
|
|
73
76
|
zh: 0a5b7c28fde34bcf
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
|
|
|
9
9
|
|
|
10
10
|
## Summary
|
|
11
11
|
|
|
12
|
-
With `dsh-web-search-deepseek`, the harness searches the web through DeepSeek's native search using an existing `DEEPSEEK_API_KEY`. Choose it when a deployment wants DeepSeek native search and accepts that one search costs a full model turn in latency and tokens, because DeepSeek exposes no dedicated search endpoint. Results come from the structured search blocks DeepSeek returns, never from scraping text out of a reply. A missing credential fails the call with a structured error; a response without a search-result block fails loudly rather than degrading. The model-facing `web_search` tool lives in `dsh-tool-web`.
|
|
12
|
+
With `dsh-web-search-deepseek`, the harness searches the web through DeepSeek's native search using the DeepSeek account sign-in or an existing `DEEPSEEK_API_KEY`. Choose it when a deployment wants DeepSeek native search and accepts that one search costs a full model turn in latency and tokens, because DeepSeek exposes no dedicated search endpoint. Results come from the structured search blocks DeepSeek returns, never from scraping text out of a reply. A missing credential fails the call with a structured error; a response without a search-result block fails loudly rather than degrading. The model-facing `web_search` tool lives in `dsh-tool-web`.
|
|
13
13
|
|
|
14
14
|
## Table of Contents
|
|
15
15
|
|
|
@@ -29,7 +29,7 @@ Mount the provider in a composition that already loads the web service; it regis
|
|
|
29
29
|
|
|
30
30
|
### When to choose it
|
|
31
31
|
|
|
32
|
-
Choose this backend when a deployment wants DeepSeek's native server-side web search and
|
|
32
|
+
Choose this backend when a deployment wants DeepSeek's native server-side web search and its users either sign in to a DeepSeek account or hold a `DEEPSEEK_API_KEY` — the provider reuses those credentials as [Authentication](#authentication) describes. One search is heavier than a dedicated retrieval endpoint: DeepSeek runs the search inside a full model turn, so expect one Messages call's latency and generated tokens per search, with up to `maxUses` server-side searches per request. Avoid it when per-search cost or latency dominates.
|
|
33
33
|
|
|
34
34
|
### Minimal configuration
|
|
35
35
|
|
|
@@ -45,8 +45,8 @@ Load the web service and the provider; the key resolves from `ctx.credentials` w
|
|
|
45
45
|
|
|
46
46
|
| Field | Default | Meaning |
|
|
47
47
|
|---|---|---|
|
|
48
|
-
| `apiKey` | omitted | Literal DeepSeek API key; prefer `apiKeyEnv` so no secret enters configuration. A non-empty literal wins |
|
|
49
|
-
| `apiKeyEnv` | `DEEPSEEK_API_KEY` | Credential reference resolved for each search through `ctx.credentials`, or from the process environment when that service is absent. A
|
|
48
|
+
| `apiKey` | omitted | Literal DeepSeek API key; prefer `apiKeyEnv` so no secret enters configuration. A non-empty literal wins over `apiKeyEnv`; an account token wins over both |
|
|
49
|
+
| `apiKeyEnv` | `DEEPSEEK_API_KEY` | Credential reference resolved for each search through `ctx.credentials`, or from the process environment when that service is absent. A search that needs an API key and finds none fails as `WEB_PROVIDER_CREDENTIAL_MISSING` |
|
|
50
50
|
| `baseURL` | `https://api.deepseek.com/anthropic/v1` | Anthropic-compatible endpoint base; `/messages` is appended. Falls back to `$DEEPSEEK_SEARCH_BASE_URL`; an unparseable value makes the provider unavailable |
|
|
51
51
|
| `model` | `deepseek-v4-flash` | Anthropic-format model name |
|
|
52
52
|
| `apiVersion` | `2023-06-01` | `anthropic-version` header value |
|
|
@@ -55,6 +55,11 @@ Load the web service and the provider; the key resolves from `ctx.credentials` w
|
|
|
55
55
|
|
|
56
56
|
The generated [configuration catalog](../../../docs/config-catalog.md#deepseek-aidsh-web-search-deepseek) lists every accepted field. Each search captures options from the live Config references.
|
|
57
57
|
|
|
58
|
+
<a id="authentication"></a>
|
|
59
|
+
### Authentication
|
|
60
|
+
|
|
61
|
+
A search authenticates with the DeepSeek account when the latest `request/context` event of the initiating Session names the `deepseek-account` provider route and `ctx.deepseekAccount` resolves a token for the search endpoint. The account service resolves one only while signed in and only for its deployment-configured inference origin, `https://api.deepseek.com` by default. That search sends only `x-dsh-auth-token`, even when an API key is configured. Every other search, including a call without an initiating Session and a search whose endpoint has another origin, sends the API key as both `x-api-key` and `Authorization: Bearer`. An HTTP 401 response to an account-authenticated search fails as `WEB_PROVIDER_ERROR` with sign-in guidance instead of endpoint guidance, and leaves the account signed in.
|
|
62
|
+
|
|
58
63
|
### What a search returns
|
|
59
64
|
|
|
60
65
|
`content` is always omitted: DeepSeek's provider prose is not trusted as an answer. `sources[]` comes from `web_search_result` items inside `web_search_tool_result` blocks — `url` and `title` directly, and `publishedAt` from `page_age` — with snippets joined from URL-keyed `cited_text` entries where an excerpt exists. Results are deduplicated by URL, and because DeepSeek exposes no result-count knob, the service enforces `maxResults` by truncating and flagging.
|
|
@@ -82,7 +87,7 @@ This section explains the design decisions behind the provider; the observable b
|
|
|
82
87
|
The provider is built on two commitments:
|
|
83
88
|
|
|
84
89
|
- **Structured blocks only.** DeepSeek runs the search server-side and returns structured `web_search_tool_result` blocks; the provider parses those blocks and never scrapes URLs out of model prose. In strict mode, a response with no such block throws `WEB_PROVIDER_ERROR` instead of degrading.
|
|
85
|
-
- **
|
|
90
|
+
- **Conversation credentials, resolved per search.** The provider adds no secret: a search from a Session on the account route uses that account's token, and every other search reuses the `DEEPSEEK_API_KEY` reference. The auxiliary request endpoint stays independent through `$DEEPSEEK_SEARCH_BASE_URL`. A mounted credentials service is authoritative; without one the provider falls back to the launching process environment. Resolving per call means a key stored or rotated in the Web Models page, or an account sign-in, reaches the next search without a restart.
|
|
86
91
|
|
|
87
92
|
### Source map
|
|
88
93
|
|
|
@@ -95,7 +100,7 @@ The provider is built on two commitments:
|
|
|
95
100
|
|
|
96
101
|
### Request flow
|
|
97
102
|
|
|
98
|
-
Each search captures the current Config values into provider options — endpoint, model, key reference, limits — then resolves the credential reference through `ctx.credentials` (or the environment), appends the log-only session event, and dispatches the Messages request with the native `web_search` server tool. The response's `web_search_tool_result` blocks become `sources[]`; `cited_text` entries from text blocks are joined to their URLs as snippets; results are deduplicated by URL; and the service enforces the requested source bound on the way back.
|
|
103
|
+
Each search captures the current Config values into provider options — endpoint, model, key reference, limits — then asks `ctx.deepseekAccount` for a token when the initiating Session uses the account route, otherwise resolves the credential reference through `ctx.credentials` (or the environment), appends the log-only session event, and dispatches the Messages request with the native `web_search` server tool. The response's `web_search_tool_result` blocks become `sources[]`; `cited_text` entries from text blocks are joined to their URLs as snippets; results are deduplicated by URL; and the service enforces the requested source bound on the way back.
|
|
99
104
|
|
|
100
105
|
</details>
|
|
101
106
|
|
|
@@ -136,7 +141,7 @@ Independent of the conversation request cache. The auxiliary instruction and nat
|
|
|
136
141
|
|
|
137
142
|
#### What the model sees
|
|
138
143
|
|
|
139
|
-
Through `dsh-tool-web`, the conversation model sees deduplicated URLs, titles, dates, and citation snippets from structured search blocks; provider prose is not trusted as an answer. This provider's exact failures include the actionable missing-credential message, `DeepSeek search credential resolution failed: <error>`, and `DeepSeek search aborted`.
|
|
144
|
+
Through `dsh-tool-web`, the conversation model sees deduplicated URLs, titles, dates, and citation snippets from structured search blocks; provider prose is not trusted as an answer. This provider's exact failures include the actionable missing-credential message, which also names DeepSeek Account sign-in, `DeepSeek search credential resolution failed: <error>`, and `DeepSeek search aborted`. An HTTP 401 to an account-authenticated search appends an instruction to guide the user to sign in to DeepSeek again. Other request, HTTP, native-search, and response-body failures append the resolved endpoint and the conditional configuration instruction described above. The consumer owns the error wrapper.
|
|
140
145
|
|
|
141
146
|
#### Token effect
|
|
142
147
|
|
package/README.zh.md
CHANGED
|
@@ -9,7 +9,7 @@ kind: "package-reference"
|
|
|
9
9
|
|
|
10
10
|
## 概述
|
|
11
11
|
|
|
12
|
-
有了 `dsh-web-search-deepseek`,harness 可以通过 DeepSeek 原生搜索检索 web
|
|
12
|
+
有了 `dsh-web-search-deepseek`,harness 可以通过 DeepSeek 原生搜索检索 web,使用 DeepSeek 账号登录或部署已有的 `DEEPSEEK_API_KEY`。当部署希望使用 DeepSeek 原生搜索、并接受一次搜索在延迟与 token 上消耗一个完整模型轮次时选择它,因为 DeepSeek 不提供专用搜索端点。结果来自 DeepSeek 返回的结构化搜索块,绝不会从回复文本中抓取。凭据缺失时调用以结构化错误失败;响应缺少搜索结果块时会明确报错,而非降级。面向模型的 `web_search` 工具位于 `dsh-tool-web`。
|
|
13
13
|
|
|
14
14
|
## 目录
|
|
15
15
|
|
|
@@ -29,7 +29,7 @@ kind: "package-reference"
|
|
|
29
29
|
|
|
30
30
|
### 何时选择
|
|
31
31
|
|
|
32
|
-
当部署希望使用 DeepSeek 原生服务端 web
|
|
32
|
+
当部署希望使用 DeepSeek 原生服务端 web 搜索、且其用户登录 DeepSeek 账号或持有 `DEEPSEEK_API_KEY` 时选择此后端——提供方按[鉴权](#authentication)所述复用这些凭据。一次搜索比专用检索端点更重:DeepSeek 在完整模型轮次内执行搜索,因此每次搜索都要预期一次 Messages 调用的延迟与生成 token,每次请求最多 `maxUses` 次服务端搜索。当单次搜索的成本或延迟占主导时避免使用它。
|
|
33
33
|
|
|
34
34
|
### 最小配置
|
|
35
35
|
|
|
@@ -45,8 +45,8 @@ kind: "package-reference"
|
|
|
45
45
|
|
|
46
46
|
| 字段 | 默认值 | 含义 |
|
|
47
47
|
|---|---|---|
|
|
48
|
-
| `apiKey` | 未设置 | DeepSeek API 密钥字面值;优先使用 `apiKeyEnv
|
|
49
|
-
| `apiKeyEnv` | `DEEPSEEK_API_KEY` | 每次搜索通过 `ctx.credentials`
|
|
48
|
+
| `apiKey` | 未设置 | DeepSeek API 密钥字面值;优先使用 `apiKeyEnv`,避免密钥进入配置。非空字面值优先于 `apiKeyEnv`;账号 token 优先于两者 |
|
|
49
|
+
| `apiKeyEnv` | `DEEPSEEK_API_KEY` | 每次搜索通过 `ctx.credentials` 解析的凭据引用;没有该服务时从进程环境解析。需要 API 密钥却解析不到时,搜索以 `WEB_PROVIDER_CREDENTIAL_MISSING` 失败 |
|
|
50
50
|
| `baseURL` | `https://api.deepseek.com/anthropic/v1` | Anthropic 兼容端点基址;追加 `/messages`。缺省时回退到 `$DEEPSEEK_SEARCH_BASE_URL`;无法解析时提供方不可用 |
|
|
51
51
|
| `model` | `deepseek-v4-flash` | Anthropic 格式模型名称 |
|
|
52
52
|
| `apiVersion` | `2023-06-01` | `anthropic-version` 标头值 |
|
|
@@ -55,6 +55,11 @@ kind: "package-reference"
|
|
|
55
55
|
|
|
56
56
|
生成的[配置目录](../../../docs/config-catalog.zh.md#deepseek-aidsh-web-search-deepseek) 列出所有接受的字段。每次搜索从即时 Config 引用捕获选项。
|
|
57
57
|
|
|
58
|
+
<a id="authentication"></a>
|
|
59
|
+
### 鉴权
|
|
60
|
+
|
|
61
|
+
当发起会话最近的 `request/context` 事件指明 `deepseek-account` 提供方路由,且 `ctx.deepseekAccount` 为搜索端点解析出 token 时,搜索使用 DeepSeek 账号鉴权。账号服务仅在已登录时、且仅对部署配置的推理 origin(默认 `https://api.deepseek.com`)解析 token。这类搜索只发送 `x-dsh-auth-token`,即使配置了 API 密钥也是如此。其余所有搜索,包括没有发起会话的调用以及端点属于其他 origin 的搜索,都把 API 密钥同时作为 `x-api-key` 与 `Authorization: Bearer` 发送。账号鉴权的搜索收到 HTTP 401 时以 `WEB_PROVIDER_ERROR` 失败,附带登录指引而非端点指引,账号保持登录。
|
|
62
|
+
|
|
58
63
|
### 搜索返回什么
|
|
59
64
|
|
|
60
65
|
`content` 始终省略:DeepSeek 的提供方文本不作为答案受到信任。`sources[]` 来自 `web_search_tool_result` 块内的 `web_search_result` 条目——`url` 和 `title` 直接取自同名字段,`publishedAt` 取自 `page_age`——snippet 在存在摘录时按 URL 关联的 `cited_text` 条目拼接。结果按 URL 去重,且由于 DeepSeek 不公开结果数量旋钮,服务通过截断并标记来强制执行 `maxResults`。
|
|
@@ -82,7 +87,7 @@ kind: "package-reference"
|
|
|
82
87
|
本提供方建立在两项承诺之上:
|
|
83
88
|
|
|
84
89
|
- **只取结构化块。** DeepSeek 在服务端执行搜索并返回结构化的 `web_search_tool_result` 块;提供方解析这些块,绝不从模型文本中抓取 URL。严格模式下,没有此类块的响应会抛出 `WEB_PROVIDER_ERROR`,而非降级。
|
|
85
|
-
-
|
|
90
|
+
- **会话凭据,逐次解析。** 提供方不新增密钥:来自账号路由会话的搜索使用该账号的 token,其余搜索复用 `DEEPSEEK_API_KEY` 引用。辅助请求端点通过 `$DEEPSEEK_SEARCH_BASE_URL` 保持独立。已挂载的凭据服务具有权威性;没有该服务时回退到启动进程的环境。按次解析意味着在 Web 的 Models 页中存储或轮换的密钥,或一次账号登录,无需重启即可用于下一次搜索。
|
|
86
91
|
|
|
87
92
|
### 源码地图
|
|
88
93
|
|
|
@@ -95,7 +100,7 @@ kind: "package-reference"
|
|
|
95
100
|
|
|
96
101
|
### 请求流程
|
|
97
102
|
|
|
98
|
-
每次搜索先把当前 Config
|
|
103
|
+
每次搜索先把当前 Config 段投影为提供方选项——端点、模型、密钥引用、上限——然后在发起会话使用账号路由时向 `ctx.deepseekAccount` 请求 token,否则通过 `ctx.credentials`(或环境)解析凭据引用,追加仅用于日志的会话事件,并以原生 `web_search` 服务器工具分发 Messages 请求。响应中的 `web_search_tool_result` 块变为 `sources[]`;文本块中的 `cited_text` 条目按其 URL 拼接为 snippet;结果按 URL 去重;服务在返回路径上强制执行请求的来源上限。
|
|
99
104
|
|
|
100
105
|
</details>
|
|
101
106
|
|
|
@@ -136,7 +141,7 @@ kind: "package-reference"
|
|
|
136
141
|
|
|
137
142
|
#### 模型看到的内容
|
|
138
143
|
|
|
139
|
-
通过 `dsh-tool-web`,会话模型会看到结构化搜索块中去重后的 URL、标题、日期与引用 snippet
|
|
144
|
+
通过 `dsh-tool-web`,会话模型会看到结构化搜索块中去重后的 URL、标题、日期与引用 snippet;提供方文本不会作为答案受到信任。该提供方的具体失败消息包括带有处理指引的凭据缺失消息(其中也提到 DeepSeek 账号登录)、`DeepSeek search credential resolution failed: <error>` 和 `DeepSeek search aborted`。账号鉴权的搜索收到 HTTP 401 时,会追加引导用户重新登录 DeepSeek 的指引。其他请求、HTTP、原生搜索和响应正文失败会追加已解析端点及前述条件式配置指引。错误包装属于消费方。
|
|
140
145
|
|
|
141
146
|
#### Token 影响
|
|
142
147
|
|
package/lib/index.js
CHANGED
|
@@ -98,13 +98,13 @@ var DeepSeekSearchProvider = class {
|
|
|
98
98
|
}
|
|
99
99
|
available() {
|
|
100
100
|
const options = this.resolveOptions();
|
|
101
|
-
return ((options.apiKey?.length ?? 0) > 0 || options.resolveApiKey !== void 0) && URL.canParse(options.baseURL) && isPositiveInteger(options.maxTokens) && isPositiveInteger(options.maxUses);
|
|
101
|
+
return ((options.apiKey?.length ?? 0) > 0 || options.resolveApiKey !== void 0 || options.resolveAccountToken !== void 0) && URL.canParse(options.baseURL) && isPositiveInteger(options.maxTokens) && isPositiveInteger(options.maxUses);
|
|
102
102
|
}
|
|
103
103
|
async search(request, signal) {
|
|
104
104
|
const options = this.resolveOptions();
|
|
105
|
-
const apiKey = await this.apiKey(options, signal);
|
|
106
|
-
throwIfSearchAborted(signal);
|
|
107
105
|
const endpoint = `${options.baseURL}/messages`;
|
|
106
|
+
const auth = await this.authHeaders(options, endpoint, signal);
|
|
107
|
+
throwIfSearchAborted(signal);
|
|
108
108
|
const body = {
|
|
109
109
|
model: options.model,
|
|
110
110
|
max_tokens: options.maxTokens,
|
|
@@ -133,8 +133,7 @@ var DeepSeekSearchProvider = class {
|
|
|
133
133
|
method: "POST",
|
|
134
134
|
redirect: "error",
|
|
135
135
|
headers: {
|
|
136
|
-
|
|
137
|
-
"authorization": `Bearer ${apiKey}`,
|
|
136
|
+
...auth.headers,
|
|
138
137
|
"anthropic-version": options.apiVersion,
|
|
139
138
|
"content-type": "application/json",
|
|
140
139
|
"accept": "application/json",
|
|
@@ -148,7 +147,8 @@ var DeepSeekSearchProvider = class {
|
|
|
148
147
|
throw searchEndpointError(endpoint, `DeepSeek search request failed: ${String(error)}`, error);
|
|
149
148
|
}
|
|
150
149
|
if (!response.ok) {
|
|
151
|
-
|
|
150
|
+
const status = response.status;
|
|
151
|
+
let message = `DeepSeek API error (HTTP ${status})`;
|
|
152
152
|
try {
|
|
153
153
|
const parsed = await response.json();
|
|
154
154
|
const detail = typeof parsed.error === "string" ? parsed.error : parsed.error?.message ?? parsed.message;
|
|
@@ -156,6 +156,7 @@ var DeepSeekSearchProvider = class {
|
|
|
156
156
|
} catch (error) {
|
|
157
157
|
if (signal?.aborted === true || isAbortError(error)) throw searchAborted(signal, error);
|
|
158
158
|
}
|
|
159
|
+
if (status === 401 && auth.kind === "account") throw accountRejectedError(message);
|
|
159
160
|
throw searchEndpointError(endpoint, message);
|
|
160
161
|
}
|
|
161
162
|
try {
|
|
@@ -166,7 +167,30 @@ var DeepSeekSearchProvider = class {
|
|
|
166
167
|
}
|
|
167
168
|
}
|
|
168
169
|
/**
|
|
169
|
-
* Resolve one operation's
|
|
170
|
+
* Resolve one operation's authentication headers without retaining a credential on the provider.
|
|
171
|
+
* @param options - the caller's snapshot, so the credential and the endpoint it is sent to come from one section.
|
|
172
|
+
* @param endpoint - the Messages endpoint this operation dispatches to.
|
|
173
|
+
* @param signal - abort signal for the surrounding search.
|
|
174
|
+
* @returns the account-token header when one resolves, otherwise the API-key headers, tagged by credential kind.
|
|
175
|
+
*/
|
|
176
|
+
async authHeaders(options, endpoint, signal) {
|
|
177
|
+
const { resolveAccountToken } = options;
|
|
178
|
+
const token = resolveAccountToken === void 0 ? void 0 : await resolveCredential(() => resolveAccountToken(endpoint), signal);
|
|
179
|
+
if (token !== void 0 && token.length > 0) return {
|
|
180
|
+
kind: "account",
|
|
181
|
+
headers: { "x-dsh-auth-token": token }
|
|
182
|
+
};
|
|
183
|
+
const apiKey = await this.apiKey(options, signal);
|
|
184
|
+
return {
|
|
185
|
+
kind: "api-key",
|
|
186
|
+
headers: {
|
|
187
|
+
"x-api-key": apiKey,
|
|
188
|
+
"authorization": `Bearer ${apiKey}`
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Resolve one operation's API key without retaining it on the provider.
|
|
170
194
|
* @param options - the caller's snapshot, so the key and the endpoint it is sent to come from one section.
|
|
171
195
|
* @param signal - abort signal for the surrounding search.
|
|
172
196
|
* @returns the resolved key.
|
|
@@ -174,17 +198,33 @@ var DeepSeekSearchProvider = class {
|
|
|
174
198
|
async apiKey(options, signal) {
|
|
175
199
|
throwIfSearchAborted(signal);
|
|
176
200
|
if (options.apiKey !== void 0 && options.apiKey.length > 0) return options.apiKey;
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
resolved = await abortable(options.resolveApiKey?.() ?? Promise.resolve(void 0), signal);
|
|
180
|
-
} catch (error) {
|
|
181
|
-
if (signal?.aborted === true || isAbortError(error)) throw searchAborted(signal, error);
|
|
182
|
-
throw new WebError(`DeepSeek search credential resolution failed: ${String(error)}`, "WEB_PROVIDER_ERROR", { cause: error });
|
|
183
|
-
}
|
|
201
|
+
const { resolveApiKey } = options;
|
|
202
|
+
const resolved = resolveApiKey === void 0 ? void 0 : await resolveCredential(resolveApiKey, signal);
|
|
184
203
|
if (resolved !== void 0 && resolved.length > 0) return resolved;
|
|
185
|
-
throw new WebError(`DeepSeek search has no API key for "${options.apiKeyEnv ?? "DEEPSEEK_API_KEY"}"; store it through the credentials service (the web Models page writes it), export it in the launching environment, or set a literal "apiKey" in the web-search-deepseek config`, "WEB_PROVIDER_CREDENTIAL_MISSING");
|
|
204
|
+
throw new WebError(`DeepSeek search has no API key for "${options.apiKeyEnv ?? "DEEPSEEK_API_KEY"}"; store it through the credentials service (the web Models page writes it), export it in the launching environment, or set a literal "apiKey" in the web-search-deepseek config; a conversation using a DeepSeek Account model searches with the account sign-in instead`, "WEB_PROVIDER_CREDENTIAL_MISSING");
|
|
186
205
|
}
|
|
187
206
|
};
|
|
207
|
+
/**
|
|
208
|
+
* Run one credential resolver under the search's cancellation signal. An
|
|
209
|
+
* already-cancelled search never starts the resolver.
|
|
210
|
+
* @param resolve - the resolver; a synchronous throw is mapped like a rejection.
|
|
211
|
+
* @param signal - abort signal for the surrounding search.
|
|
212
|
+
* @returns the resolved credential, or undefined when the resolver supplied none.
|
|
213
|
+
* @throws {@link WebError} `WEB_ABORTED` on cancellation, `WEB_PROVIDER_ERROR` when the resolver fails.
|
|
214
|
+
*/
|
|
215
|
+
async function resolveCredential(resolve, signal) {
|
|
216
|
+
throwIfSearchAborted(signal);
|
|
217
|
+
try {
|
|
218
|
+
return await abortable(resolve(), signal);
|
|
219
|
+
} catch (error) {
|
|
220
|
+
if (signal?.aborted === true || isAbortError(error)) throw searchAborted(signal, error);
|
|
221
|
+
throw new WebError(`DeepSeek search credential resolution failed: ${String(error)}`, "WEB_PROVIDER_ERROR", { cause: error });
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
/** Replace endpoint guidance with sign-in guidance when DeepSeek rejects the account token. */
|
|
225
|
+
function accountRejectedError(message) {
|
|
226
|
+
return new WebError(`${message}\n\nDeepSeek rejected the account sign-in used for this web search. Guide the user to sign in to DeepSeek again; the search endpoint does not need changing.`, "WEB_PROVIDER_ERROR");
|
|
227
|
+
}
|
|
188
228
|
/** Add endpoint recovery instructions to failures that occur after request dispatch begins. */
|
|
189
229
|
function searchEndpointError(endpoint, message, cause) {
|
|
190
230
|
return new WebError(`${message}\n\nThe web search request used endpoint ${JSON.stringify(endpoint)}. Search endpoint configuration is separate from chat. If that endpoint is not intended, guide the user to Settings > Plugins > Plugin configuration > Web search, where they can change and save Endpoint. If that settings page is unavailable, the user can set DEEPSEEK_SEARCH_BASE_URL or configure web-search-deepseek.baseURL to a trusted Anthropic-compatible Messages API base. Only the user should choose or change the endpoint.`, "WEB_PROVIDER_ERROR", cause === void 0 ? void 0 : { cause });
|
|
@@ -247,6 +287,8 @@ const Config = z.object({
|
|
|
247
287
|
* `$DEEPSEEK_BASE_URL` and selected protocol.
|
|
248
288
|
*/
|
|
249
289
|
const SEARCH_BASE_URL_ENV = "DEEPSEEK_SEARCH_BASE_URL";
|
|
290
|
+
/** Provider route id `dsh-llm-deepseek-account` registers; `request/context` events record it per Session. */
|
|
291
|
+
const ACCOUNT_PROVIDER = "deepseek-account";
|
|
250
292
|
/** Settings namespace carrying this provider's endpoint, model, and key reference. */
|
|
251
293
|
const WEB_SEARCH_DEEPSEEK_SETTINGS_NAMESPACE = "web-search-deepseek";
|
|
252
294
|
/**
|
|
@@ -262,6 +304,10 @@ function resolveOptions(ctx, config) {
|
|
|
262
304
|
const literalApiKey = config.apiKey !== void 0 && config.apiKey.length > 0 ? config.apiKey : void 0;
|
|
263
305
|
return {
|
|
264
306
|
...literalApiKey === void 0 ? {} : { apiKey: literalApiKey },
|
|
307
|
+
resolveAccountToken: async (endpoint) => {
|
|
308
|
+
if (ctx.get("agents")?.currentInitiator()?.session.requestContext()?.provider !== ACCOUNT_PROVIDER) return void 0;
|
|
309
|
+
return await ctx.get("deepseekAccount")?.resolveToken(endpoint);
|
|
310
|
+
},
|
|
265
311
|
resolveApiKey: async () => {
|
|
266
312
|
const credentials = ctx.get("credentials");
|
|
267
313
|
if (credentials !== void 0) return (await credentials.resolve(apiKeyEnv))?.value;
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Register a DeepSeek-backed provider in `ctx.web`. It calls the Anthropic-compatible Messages API
|
|
3
|
-
* with native `web_search_20250305`.
|
|
4
|
-
*
|
|
3
|
+
* with native `web_search_20250305`. A search initiated by a Session on the DeepSeek account route
|
|
4
|
+
* authenticates with the account token when the account service allows the search endpoint;
|
|
5
|
+
* every other search reuses `DEEPSEEK_API_KEY`. The provider does not reuse `DEEPSEEK_BASE_URL`;
|
|
6
|
+
* auxiliary search has its own endpoint configuration.
|
|
5
7
|
* @module @deepseek-ai/dsh-web-search-deepseek
|
|
6
8
|
*/
|
|
7
9
|
import type { Volatile } from '@deepseek-ai/cordis';
|
package/lib/types/provider.d.ts
CHANGED
|
@@ -65,6 +65,12 @@ declare module '@deepseek-ai/dsh-session/types' {
|
|
|
65
65
|
}
|
|
66
66
|
/** Resolved provider options (the plugin's `apply` supplies credential and constant defaults). */
|
|
67
67
|
export interface DeepSeekSearchProviderOptions {
|
|
68
|
+
/**
|
|
69
|
+
* Resolve the DeepSeek account token for one search endpoint. A token takes
|
|
70
|
+
* precedence over every API key and is sent only as `x-dsh-auth-token`;
|
|
71
|
+
* `undefined` selects API-key authentication.
|
|
72
|
+
*/
|
|
73
|
+
resolveAccountToken?: (endpoint: string) => Promise<string | undefined>;
|
|
68
74
|
/** Literal DeepSeek API key; when present it wins over {@link resolveApiKey}. */
|
|
69
75
|
apiKey?: string;
|
|
70
76
|
/** Resolve the current DeepSeek API key for one search operation. */
|
|
@@ -127,7 +133,15 @@ export declare class DeepSeekSearchProvider implements WebSearchProvider {
|
|
|
127
133
|
available(): boolean;
|
|
128
134
|
search(request: WebSearchRequest, signal?: AbortSignal): Promise<WebSearchResult>;
|
|
129
135
|
/**
|
|
130
|
-
* Resolve one operation's
|
|
136
|
+
* Resolve one operation's authentication headers without retaining a credential on the provider.
|
|
137
|
+
* @param options - the caller's snapshot, so the credential and the endpoint it is sent to come from one section.
|
|
138
|
+
* @param endpoint - the Messages endpoint this operation dispatches to.
|
|
139
|
+
* @param signal - abort signal for the surrounding search.
|
|
140
|
+
* @returns the account-token header when one resolves, otherwise the API-key headers, tagged by credential kind.
|
|
141
|
+
*/
|
|
142
|
+
private authHeaders;
|
|
143
|
+
/**
|
|
144
|
+
* Resolve one operation's API key without retaining it on the provider.
|
|
131
145
|
* @param options - the caller's snapshot, so the key and the endpoint it is sent to come from one section.
|
|
132
146
|
* @param signal - abort signal for the surrounding search.
|
|
133
147
|
* @returns the resolved key.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-web-search-deepseek",
|
|
3
3
|
"description": "DeepSeek-backed search provider (native web_search via the Anthropic-compatible API) for the DeepSeek Harness web capability seam (ctx.web)",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0-rc.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -27,24 +27,31 @@
|
|
|
27
27
|
],
|
|
28
28
|
"license": "MIT",
|
|
29
29
|
"peerDependencies": {
|
|
30
|
-
"@deepseek-ai/dsh-agent": "0.
|
|
31
|
-
"@deepseek-ai/dsh-credentials": "0.
|
|
32
|
-
"@deepseek-ai/dsh-
|
|
33
|
-
"@deepseek-ai/dsh-
|
|
30
|
+
"@deepseek-ai/dsh-agent": "0.2.0-rc.2",
|
|
31
|
+
"@deepseek-ai/dsh-credentials": "0.2.0-rc.2",
|
|
32
|
+
"@deepseek-ai/dsh-deepseek-account": "0.2.0-rc.2",
|
|
33
|
+
"@deepseek-ai/dsh-session": "0.2.0-rc.2",
|
|
34
|
+
"@deepseek-ai/dsh-web": "0.2.0-rc.2",
|
|
34
35
|
"@deepseek-ai/cordis": "~4.0.4",
|
|
35
|
-
"@deepseek-ai/dsh-
|
|
36
|
+
"@deepseek-ai/dsh-launch-environment": "0.2.0-rc.2"
|
|
36
37
|
},
|
|
37
38
|
"dependencies": {
|
|
38
39
|
"@deepseek-ai/schemastery": "~3.18.4"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
|
-
"@deepseek-ai/dsh-agent": "0.
|
|
42
|
-
"@deepseek-ai/dsh-credentials": "0.
|
|
43
|
-
"@deepseek-ai/dsh-credentials-local": "0.
|
|
44
|
-
"@deepseek-ai/dsh-
|
|
45
|
-
"@deepseek-ai/dsh-
|
|
46
|
-
"@deepseek-ai/dsh-
|
|
42
|
+
"@deepseek-ai/dsh-agent": "0.2.0-rc.2",
|
|
43
|
+
"@deepseek-ai/dsh-credentials": "0.2.0-rc.2",
|
|
44
|
+
"@deepseek-ai/dsh-credentials-local": "0.2.0-rc.2",
|
|
45
|
+
"@deepseek-ai/dsh-deepseek-account": "0.2.0-rc.2",
|
|
46
|
+
"@deepseek-ai/dsh-launch-environment": "0.2.0-rc.2",
|
|
47
|
+
"@deepseek-ai/dsh-session": "0.2.0-rc.2",
|
|
48
|
+
"@deepseek-ai/dsh-http-proxy": "0.2.0-rc.2",
|
|
47
49
|
"@deepseek-ai/cordis": "~4.0.4",
|
|
48
|
-
"@deepseek-ai/dsh-
|
|
50
|
+
"@deepseek-ai/dsh-web": "0.2.0-rc.2"
|
|
51
|
+
},
|
|
52
|
+
"peerDependenciesMeta": {
|
|
53
|
+
"@deepseek-ai/dsh-deepseek-account": {
|
|
54
|
+
"optional": true
|
|
55
|
+
}
|
|
49
56
|
}
|
|
50
57
|
}
|