@deepseek-ai/dsh-llm-deepseek-api-key 0.2.1-rc.2 → 0.2.1-rc.4

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 CHANGED
@@ -3,26 +3,26 @@
3
3
  # After editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/llm/llm-deepseek-api-key/README.md
5
5
  /:
6
- en: 3bc991d01c87c9c6
7
- zh: ba63ebb46b72bc3e
6
+ en: d0825db7a1a9d8ee
7
+ zh: d0825db7a1a9d8ee
8
8
  /deepseek-ai-dsh-llm-deepseek-api-key:
9
9
  en: b6871195e386cc64
10
10
  zh: 742b05a3ca80883c
11
11
  /deepseek-ai-dsh-llm-deepseek-api-key/summary:
12
- en: ad5a00f16f233d42
13
- zh: 743d55599144836b
12
+ en: f937052cf8ff4812
13
+ zh: 899d61f5021136d6
14
14
  /deepseek-ai-dsh-llm-deepseek-api-key/table-of-contents:
15
15
  en: 23386452a66b2fbd
16
16
  zh: 4cf93c4bf17b3da2
17
17
  /deepseek-ai-dsh-llm-deepseek-api-key/use-this-package:
18
- en: 10333177c9033b9e
19
- zh: 069faa6a125cb6a9
18
+ en: 2aad8a660b90561d
19
+ zh: 3f425b3d1ea023e1
20
20
  /deepseek-ai-dsh-llm-deepseek-api-key/understand-the-implementation:
21
- en: 002ac925beb4e75b
22
- zh: 815d06f116604a09
21
+ en: d614c1ea3a9eb382
22
+ zh: 5ca070349bb22d56
23
23
  /deepseek-ai-dsh-llm-deepseek-api-key/further-exploration:
24
- en: e8bf99d78aa7035f
25
- zh: e4a74d2d48b6d271
24
+ en: 347659458510940c
25
+ zh: ccfb03e18b356a39
26
26
  /deepseek-ai-dsh-llm-deepseek-api-key/model-experience:
27
27
  en: 215e7ba838619b7b
28
28
  zh: a311da3843709f90
@@ -33,14 +33,14 @@
33
33
  en: e5624a2c7bb26e90
34
34
  zh: 44da0286a548f24a
35
35
  /deepseek-ai-dsh-llm-deepseek-api-key/model-experience/authenticated-model-requests/token-effect:
36
- en: fecefb75b719c317
37
- zh: f0c2322f657c5914
36
+ en: 9dd6430752202069
37
+ zh: 53068bf7f828156f
38
38
  /deepseek-ai-dsh-llm-deepseek-api-key/model-experience/authenticated-model-requests/kv-cache-effect:
39
- en: 2004df60d997da79
40
- zh: a215a6586b44746b
39
+ en: 8226f9d956013ad9
40
+ zh: bace9524b7acc1fa
41
41
  /deepseek-ai-dsh-llm-deepseek-api-key/known-limitations-and-deferred-work:
42
- en: d5880c883b1b678f
43
- zh: bf7a4da24ba75280
42
+ en: e0a41598c126c187
43
+ zh: 857ab65c27f41612
44
44
  /deepseek-ai-dsh-llm-deepseek-api-key/known-limitations-and-deferred-work/dev-note:
45
45
  en: 0525041e573e96b6
46
46
  zh: b05ed8d852498e6b
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "DeepSeek api-key authentication and model discovery."
2
+ description: "DeepSeek API-key authentication, model discovery, and the official model listing."
3
3
  kind: "package-reference"
4
4
  ---
5
5
 
@@ -9,7 +9,7 @@ English | [中文](README.zh.md)
9
9
 
10
10
  ## Summary
11
11
 
12
- Register authentication and model discovery for `deepseek-official`. This plugin shares the [Messages transport](../llm-deepseek/README.md) and owns credential resolution and catalog availability.
12
+ Register authentication, model discovery, and the official model listing for `deepseek-official`. This plugin shares the [Messages transport](../llm-deepseek/README.md) and owns credential resolution, catalog availability, and model admission.
13
13
 
14
14
  Authentication resolution returns the validated API key in `x-api-key` for both Messages and Files requests.
15
15
 
@@ -37,17 +37,30 @@ Authentication resolution returns the validated API key in `x-api-key` for both
37
37
 
38
38
  The endpoint and credential reference come from one configuration resolution. In-flight requests retain that snapshot; subsequent updates affect subsequent calls. Account login state cannot change this route’s credential.
39
39
 
40
- `models` is an independently configurable catalog for this provider; defaults and protocol capabilities come from the shared transport. Discovery does not probe inference endpoints. The settings namespace is the Cordis entry id, or the plugin name without an entry. Product profiles retain the official entry id `llm-deepseek` and use `llm-deepseek-account` for the account route.
40
+ `models` is an independently configurable catalog for this provider; defaults and protocol capabilities come from the shared transport. While `models` equals the built-in catalog and the Messages origin is the official API, `https://api.deepseek.com`, the route serves the official `GET https://api.deepseek.com/models` listing instead. Listing requests carry the API key in `x-api-key` and the harness `User-Agent`, and reject redirects; without an API key the route requests nothing. Any other endpoint, including one named by `baseURL` or `$DEEPSEEK_BASE_URL`, serves `models` and requests no listing. Discovery does not probe inference endpoints.
41
+
42
+ The first access waits for the listing up to `modelsTimeoutMs`. Later accesses use the loaded listing and refresh it in the background once it is older than `modelsMaxAgeMs`; an idle route sends no requests. A failed or empty listing keeps the previous one; before any listing loads, the route serves the built-in catalog of the current version (`deepseek-flash` and `deepseek-v4-pro`). A listing HTTP 401 fails only that load. Each loaded listing, the first failure of a failing streak with the catalog it falls back to, and each listing entry hidden because its declarations are unreadable or unusable are written to stderr with the `[llm-deepseek-api-key]` tag; stdout remains available for protocol messages, and secrets are never logged.
43
+
44
+ Listed models are offered, merged with this build's entries, and refused before network I/O with `CLIENT_UPGRADE_REQUIRED` or `MODEL_UNSUPPORTED` by the [account route's rules](../llm-deepseek-account/README.md#use-this-package); model ids the listing does not name pass through unchanged.
45
+
46
+ A `models` list that differs from the built-in catalog replaces the catalog and stops listing requests; the Models page stores an edited catalog there. Removing that list, as **Restore defaults** on the Models page does, returns the route to the listing, and the next access loads the listing again even when the cached one is still fresh. A list identical to the built-in catalog counts as uncustomized.
47
+
48
+ | Field | Default | Meaning |
49
+ |---|---|---|
50
+ | `modelsTimeoutMs` | `5,000` | Deadline of one listing request and of a first-access wait |
51
+ | `modelsMaxAgeMs` | `600,000` | Listing age after which the next access refreshes it; a failed load retries after the same age |
52
+
53
+ The settings namespace is the Cordis entry id, or the plugin name without an entry. Product profiles retain the official entry id `llm-deepseek` and use `llm-deepseek-account` for the account route.
41
54
 
42
55
  <a id="understand-the-implementation"></a>
43
56
  ## Understand the implementation
44
57
 
45
- Registrations and listeners dispose with the plugin. Shared Host wiring supplies attachments, request extensions, anonymous identity, and atomic retry-policy updates; this plugin registers only its own route.
58
+ Registrations and listeners dispose with the plugin, and disposal aborts an in-flight listing request. Shared Host wiring supplies attachments, request extensions, anonymous identity, and atomic retry-policy updates; this plugin registers only its own route and uses the transport's `connect` and `admit` hooks for the listed catalog and the gate. The listing request, its cache, and catalog resolution come from the shared transport; the cache lives in memory and is keyed by endpoint and a SHA-256 of the key, so another key loads its own listing. A `loader/volatile-update` listener discards the cached listing whenever the route starts or stops following it.
46
59
 
47
60
  <a id="further-exploration"></a>
48
61
  ## Further Exploration
49
62
 
50
- [LLM streaming](../../../docs/subsystems/llm-streaming.md) · [Messages](../llm-deepseek/README.md)
63
+ [LLM streaming](../../../docs/subsystems/llm-streaming.md) · [Messages](../llm-deepseek/README.md) · [Official listing decision](../../../.agents/notes/implemented/architecture/2026-10-22-deepseek-official-model-listing.md)
51
64
 
52
65
  <a id="model-experience"></a>
53
66
  ## Model Experience
@@ -60,17 +73,23 @@ Requests on `deepseek-official` add no model-visible text from this plugin; the
60
73
 
61
74
  #### Token effect
62
75
 
63
- Authentication and discovery add no input tokens; the selected model and request content determine actual usage.
76
+ Authentication, discovery, and the listing add no input tokens. The followed listing sets each model's context window and default output cap, which each request's logged context and header record.
64
77
 
65
78
  #### KV Cache effect
66
79
 
67
- Credentials and catalog availability do not enter model input; the protocol transport owns request-prefix serialization.
80
+ Credentials and catalog availability do not enter model input; the protocol transport owns request-prefix serialization. A listing change to a model's system prompt or tool update mode changes how later system prompt or tool updates are serialized.
68
81
 
69
82
  ## Known Limitations and Deferred Work
70
83
 
71
84
  <a id="known-limitations-and-deferred-work"></a>
72
85
 
73
- - The catalog is configuration metadata, not remote validation that a model is accepted; actual calls can still fail.
86
+ - A configured catalog is configuration metadata, not remote validation that a model is accepted; actual calls can still fail.
87
+ - The Models page shows the built-in catalog while the route follows the listing, so a model only the listing offers appears only in model selectors. Editing that catalog on the page stores the rows the page shows, which drops such a model until **Restore defaults**.
88
+ - A customized catalog does not receive models the listing adds later.
89
+ - Customization is judged against the running version's built-in catalog, so a saved `models` list equal to an earlier version's built-in catalog counts as customized once the built-in catalog changes and stops following the listing until **Restore defaults**.
90
+ - The listing cache lives in memory; a start without network serves the built-in catalog until a listing loads.
91
+ - A listing change during a running process reaches the Web model picker at its next catalog refresh; the plugin does not announce it.
92
+ - When the current version can serve no listed model, the route offers no models.
74
93
 
75
94
  <a id="dev-note"></a>
76
95
  ### Dev Note
package/README.zh.md CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
- description: "DeepSeek API key authentication and model discovery."
2
+ description: "DeepSeek API-key authentication, model discovery, and the official model listing."
3
3
  kind: "package-reference"
4
4
  ---
5
5
 
@@ -9,7 +9,7 @@ kind: "package-reference"
9
9
 
10
10
  ## 概述
11
11
 
12
- 为 `deepseek-official` 注册独立的鉴权和模型发现插件。通过 [llm-deepseek](../llm-deepseek/README.zh.md) 共用 Messages 请求实现;本包拥有凭据解析和目录可用性判断。
12
+ 为 `deepseek-official` 注册鉴权、模型发现和官方模型列表。通过 [llm-deepseek](../llm-deepseek/README.zh.md) 共用 Messages 请求实现;本包拥有凭据解析、目录可用性判断和模型准入。
13
13
 
14
14
  鉴权解析将验证后的 API key 放入 `x-api-key`,供 Messages 和 Files 请求共同使用。
15
15
 
@@ -37,17 +37,30 @@ kind: "package-reference"
37
37
 
38
38
  端点和凭据引用来自同一次配置解析;进行中的请求保留该快照,后续配置更新仅影响后续请求。账号登录态不影响该路由使用的凭据。
39
39
 
40
- `models` 是该 provider 独立的可配置目录;默认值和协议能力来自共享传输包。目录判断不探测推理端点。设置命名空间采用 Cordis entry id,没有 entry 时采用插件名。产品保留 official 的 `llm-deepseek` entry id,账号使用 `llm-deepseek-account`。
40
+ `models` 是该 provider 独立的可配置目录;默认值和协议能力来自共享传输包。当 `models` 等于内置目录、且 Messages 的 origin 是官方 API `https://api.deepseek.com` 时,路由改为提供官方 `GET https://api.deepseek.com/models` 列表。列表请求用 `x-api-key` 携带 API key,带上 harness 的 `User-Agent`,并拒绝重定向;没有 API key 时不发任何请求。其他端点,包括由 `baseURL` 或 `$DEEPSEEK_BASE_URL` 指定的端点,提供 `models`,不请求列表。目录判断不探测推理端点。
41
+
42
+ 第一次访问会等待列表,最长 `modelsTimeoutMs`。之后的访问直接使用已加载的列表,列表超过 `modelsMaxAgeMs` 后在后台刷新;路由空闲时不发请求。获取失败或得到空列表时保留上一份;还没有加载到任何列表时,路由提供当前版本内置的目录(`deepseek-flash` 与 `deepseek-v4-pro`)。列表请求返回 HTTP 401 只让这一次加载失败。每次加载成功、连续失败中的第一次失败及其退回的目录、以及每个因声明无法读取或不可用而被隐藏的列表条目,都会以 `[llm-deepseek-api-key]` 标签写到 stderr;stdout 保留给协议消息,且不记录任何密钥。
43
+
44
+ 列出的模型按[账号路由的规则](../llm-deepseek-account/README.zh.md#use-this-package)提供、与本版本的内置条目合并,并在网络请求之前以 `CLIENT_UPGRADE_REQUIRED` 或 `MODEL_UNSUPPORTED` 拒绝;列表没有列出的模型 id 照常透传。
45
+
46
+ 与内置目录不同的 `models` 列表会替换整个目录,并停止请求列表;模型页把编辑后的目录存在这里。删除这个列表(即模型页上的**恢复默认模型**)后,路由重新使用列表,即使缓存的列表尚未过期,下一次访问也会重新加载。与内置目录完全相同的列表视为未自定义。
47
+
48
+ | 字段 | 默认值 | 含义 |
49
+ |---|---|---|
50
+ | `modelsTimeoutMs` | `5,000` | 单次列表请求以及首次访问等待的上限 |
51
+ | `modelsMaxAgeMs` | `600,000` | 列表超过这个时长后,下一次访问时刷新;获取失败后同样在这个时长后重试 |
52
+
53
+ 设置命名空间采用 Cordis entry id,没有 entry 时采用插件名。产品保留 official 的 `llm-deepseek` entry id,账号使用 `llm-deepseek-account`。
41
54
 
42
55
  <a id="understand-the-implementation"></a>
43
56
  ## 理解实现
44
57
 
45
- 注册和监听随插件生命周期释放。共享 Host 绑定提供附件访问、请求扩展、匿名标识和重试策略的原子更新;本包只注册自己的路由。
58
+ 注册和监听随插件生命周期释放,释放时会中止进行中的列表请求。共享 Host 绑定提供附件访问、请求扩展、匿名标识和重试策略的原子更新;本包只注册自己的路由,并通过传输层的 `connect` 与 `admit` 钩子接入列表目录和门控。列表请求、缓存和目录解析来自共享传输包;缓存只在内存中,以端点和 API key 的 SHA-256 为键,因此换一个 key 会加载它自己的列表。一个 `loader/volatile-update` 监听器在路由开始或停止使用列表时丢弃缓存的列表。
46
59
 
47
60
  <a id="further-exploration"></a>
48
61
  ## 进一步探索
49
62
 
50
- [LLM streaming](../../../docs/subsystems/llm-streaming.zh.md) · [Messages](../llm-deepseek/README.zh.md)
63
+ [LLM streaming](../../../docs/subsystems/llm-streaming.zh.md) · [Messages](../llm-deepseek/README.zh.md) · [官方列表决策](../../../.agents/notes/implemented/architecture/2026-10-22-deepseek-official-model-listing.zh.md)
51
64
 
52
65
  <a id="model-experience"></a>
53
66
  ## 模型体验
@@ -60,17 +73,23 @@ kind: "package-reference"
60
73
 
61
74
  #### Token effect
62
75
 
63
- 鉴权和目录过滤不增加输入 token;实际调用的 token 由所选模型与请求内容决定。
76
+ 鉴权、目录过滤和列表都不增加输入 token。所使用的列表决定每个模型的上下文窗口和默认输出上限,每次请求记录的上下文和请求头会写明这些值。
64
77
 
65
78
  #### KV Cache effect
66
79
 
67
- 凭据与目录可用性不进入模型输入;协议传输拥有请求前缀的序列化。
80
+ 凭据与目录可用性不进入模型输入;协议传输拥有请求前缀的序列化。列表改变某个模型的 system prompt 模式或工具更新模式时,之后的 system prompt 更新或工具更新的序列化方式随之改变。
68
81
 
69
82
  ## 已知限制与后续工作
70
83
 
71
84
  <a id="known-limitations-and-deferred-work"></a>
72
85
 
73
- - 目录是配置元数据,不验证远端是否接受模型;实际调用仍可能失败。
86
+ - 配置的目录是配置元数据,不验证远端是否接受模型;实际调用仍可能失败。
87
+ - 路由使用列表时,模型页显示的仍是内置目录,因此只有列表提供的模型只出现在模型选择器中。在模型页编辑目录会保存页面上显示的行,这类模型会消失,直到点**恢复默认模型**。
88
+ - 自定义的目录收不到列表之后新增的模型。
89
+ - 是否自定义按当前版本的内置目录判断:保存的 `models` 列表若等于旧版本的内置目录,内置目录变化后就算作自定义,不再跟随列表,直到点**恢复默认模型**。
90
+ - 列表缓存只在内存中;没有网络的启动会一直提供内置目录,直到加载到列表。
91
+ - 进程运行期间的列表变化在 Web 模型选择器下一次刷新目录时生效;插件不主动通知。
92
+ - 当前版本不能用任何已列出的模型时,路由不提供模型。
74
93
 
75
94
  <a id="dev-note"></a>
76
95
  ### 开发备注
package/lib/index.js CHANGED
@@ -1,11 +1,14 @@
1
- import { LlmError, assertUsableApiKey } from "@deepseek-ai/dsh-llm";
1
+ import { APP_IDENTITY, LlmError, assertUsableApiKey } from "@deepseek-ai/dsh-llm";
2
2
  import { launchEnvironmentOf } from "@deepseek-ai/dsh-launch-environment";
3
- import { catalogModelInfo, deepSeekConfigFields, plainOptions as plainOptions$1, registerDeepSeekProvider, resolveAdapterOptions as resolveAdapterOptions$1 } from "@deepseek-ai/dsh-llm-deepseek";
3
+ import { ModelListingCache, PUBLIC_BASE_URL, catalogModelInfo, deepSeekConfigFields, isBuiltInCatalog, modelListingConfigFields, modelListingURL, plainOptions as plainOptions$1, refuseListedModel, registerDeepSeekProvider, requestModelListing, resolveAdapterOptions as resolveAdapterOptions$1, resolveListedCatalog, resolveListingTiming } from "@deepseek-ai/dsh-llm-deepseek";
4
4
  import z from "@deepseek-ai/schemastery";
5
5
  import { credentialRef } from "@deepseek-ai/dsh-credentials";
6
6
  //#region lib/types/config.js
7
+ /** Origin of the official API, the only endpoint whose `/models` listing this route follows. */
8
+ const OFFICIAL_ORIGIN = new URL(PUBLIC_BASE_URL).origin;
7
9
  const Config = z.object({
8
10
  ...deepSeekConfigFields,
11
+ ...modelListingConfigFields,
9
12
  apiKeyEnv: z.string().role("credential-ref").default("DEEPSEEK_API_KEY").volatile()
10
13
  });
11
14
  /** Read one validated provider configuration.
@@ -18,15 +21,23 @@ function plainOptions(config) {
18
21
  apiKeyEnv: config.apiKeyEnv.get()
19
22
  };
20
23
  }
21
- /** Resolve API-key and protocol settings together.
24
+ /** Resolve API-key, listing, and protocol settings together.
22
25
  * @param config - raw deployment settings.
23
26
  * @param environment - application launch environment.
24
- * @returns validated endpoint facts and the matching credential reference.
27
+ * @returns validated endpoint facts, the matching credential reference, and the listing the catalog follows.
25
28
  */
26
29
  function resolveAdapterOptions(config, environment) {
30
+ const { modelsTimeoutMs: _timeout, modelsMaxAgeMs: _maxAge, apiKeyEnv, ...messages } = config;
31
+ const timing = resolveListingTiming(config, "llm-deepseek-api-key");
32
+ const connection = resolveAdapterOptions$1(messages, environment);
33
+ const listed = new URL(connection.baseURL).origin === OFFICIAL_ORIGIN && isBuiltInCatalog(connection.models);
27
34
  return {
28
- ...resolveAdapterOptions$1(config, environment),
29
- apiKeyEnv: credentialRef(config.apiKeyEnv ?? "DEEPSEEK_API_KEY")
35
+ ...connection,
36
+ apiKeyEnv: credentialRef(apiKeyEnv ?? "DEEPSEEK_API_KEY"),
37
+ ...listed ? { listing: {
38
+ url: modelListingURL(connection.baseURL),
39
+ ...timing
40
+ } } : {}
30
41
  };
31
42
  }
32
43
  //#endregion
@@ -34,6 +45,8 @@ function resolveAdapterOptions(config, environment) {
34
45
  const name = "llm-deepseek-api-key";
35
46
  const inject = ["llm"];
36
47
  const PROVIDER = "deepseek-official";
48
+ /** Console tag of listing diagnostics; product profiles mount no console exporter for `ctx.logger`. */
49
+ const LOG_TAG = "[llm-deepseek-api-key]";
37
50
  function apply(ctx, config) {
38
51
  const options = () => resolveAdapterOptions(plainOptions(config), launchEnvironmentOf(ctx));
39
52
  options();
@@ -49,6 +62,52 @@ function apply(ctx, config) {
49
62
  }
50
63
  throw new LlmError(`llm-deepseek: no API key for provider route "${PROVIDER}"; store ${ref} through the credentials service (the web Models page writes it), or export ${ref} in the launching environment`, "MISSING_CREDENTIAL");
51
64
  };
65
+ /** The API key, or `undefined` when none is configured; other credential failures propagate. */
66
+ const presentApiKey = async (connection) => {
67
+ try {
68
+ return await resolveApiKey(connection);
69
+ } catch (error) {
70
+ if (error instanceof LlmError && error.code === "MISSING_CREDENTIAL") return void 0;
71
+ throw error;
72
+ }
73
+ };
74
+ const listings = new ModelListingCache({
75
+ credential: async () => presentApiKey(options()),
76
+ load: (url, apiKey, signal) => requestModelListing(url, { "x-api-key": apiKey }, signal),
77
+ now: () => Date.now(),
78
+ info: (message) => {
79
+ console.error(LOG_TAG, message);
80
+ },
81
+ warn: (message) => {
82
+ console.warn(LOG_TAG, message);
83
+ }
84
+ });
85
+ let reportedListing;
86
+ /** Resolve the catalog of one operation, reporting each listing's hidden entries once. */
87
+ const connectionOf = (connection, view) => {
88
+ if (view === void 0) return {
89
+ ...connection,
90
+ offered: connection.models,
91
+ refusals: /* @__PURE__ */ new Map()
92
+ };
93
+ const catalog = resolveListedCatalog({
94
+ listing: view.listing?.entries,
95
+ supported: connection.models,
96
+ overrides: {},
97
+ routeMaxTokens: connection.maxTokens,
98
+ clientVersion: APP_IDENTITY.version
99
+ });
100
+ if (view.listing !== reportedListing) {
101
+ reportedListing = view.listing;
102
+ for (const problem of catalog.problems) console.warn(LOG_TAG, problem);
103
+ }
104
+ return {
105
+ ...connection,
106
+ models: catalog.resolution,
107
+ offered: catalog.offered,
108
+ refusals: catalog.refusals
109
+ };
110
+ };
52
111
  ctx.llm.registerConfigurableProviders([{
53
112
  provider: PROVIDER,
54
113
  displayName: "DeepSeek",
@@ -56,20 +115,51 @@ function apply(ctx, config) {
56
115
  settingsPath: []
57
116
  }]);
58
117
  registerDeepSeekProvider(ctx, PROVIDER, {
59
- options,
60
118
  providerName: "DeepSeek",
61
- resolveAuth: async (connection) => ({ headers: { "x-api-key": await resolveApiKey(connection) } }),
62
- discoverModels: async (provider) => {
119
+ options: () => {
63
120
  const connection = options();
121
+ return connectionOf(connection, connection.listing === void 0 ? void 0 : listings.current());
122
+ },
123
+ connect: async (signal) => {
124
+ const connection = options();
125
+ if (connection.listing === void 0) return connectionOf(connection, void 0);
126
+ let view;
64
127
  try {
65
- await resolveApiKey(connection);
128
+ view = await listings.view(connection.listing, signal);
66
129
  } catch (error) {
67
- if (error instanceof LlmError && error.code === "MISSING_CREDENTIAL") return [];
68
- throw error;
130
+ if (signal?.aborted === true) throw error;
131
+ return connectionOf(connection, void 0);
69
132
  }
70
- return connection.models.map((model) => catalogModelInfo(provider, model));
133
+ return connectionOf(connection, view);
134
+ },
135
+ resolveAuth: async (connection) => ({ headers: { "x-api-key": await resolveApiKey(connection) } }),
136
+ admit: (connection, model) => {
137
+ refuseListedModel(model, connection.refusals.get(model));
138
+ },
139
+ discoverModels: async (provider) => {
140
+ const connection = options();
141
+ if (connection.listing === void 0) {
142
+ if (await presentApiKey(connection) === void 0) return [];
143
+ return connection.models.map((model) => catalogModelInfo(provider, model));
144
+ }
145
+ const view = await listings.view(connection.listing);
146
+ return view.authenticated ? connectionOf(connection, view).offered.map((model) => catalogModelInfo(provider, model)) : [];
147
+ }
148
+ });
149
+ let following = options().listing !== void 0;
150
+ ctx.on("loader/volatile-update", () => {
151
+ let next;
152
+ try {
153
+ next = options().listing !== void 0;
154
+ } catch (_rejectedConfiguration) {
155
+ return;
71
156
  }
157
+ if (next !== following) listings.invalidate();
158
+ following = next;
72
159
  });
160
+ ctx.effect(() => () => {
161
+ listings.dispose();
162
+ }, "llm-deepseek-api-key: model listing");
73
163
  }
74
164
  //#endregion
75
165
  export { Config, apply, inject, name, plainOptions, resolveAdapterOptions };
@@ -1,17 +1,18 @@
1
- /** API-key configuration resolved together with one Messages endpoint generation. */
1
+ /** API-key configuration resolved together with one Messages endpoint generation and its model listing. */
2
2
  import type { Volatile } from '@deepseek-ai/cordis';
3
3
  import z from '@deepseek-ai/schemastery';
4
4
  import { type CredentialRef } from '@deepseek-ai/dsh-credentials';
5
5
  import type { LaunchEnvironmentSnapshot } from '@deepseek-ai/dsh-launch-environment';
6
- import { type Config as ProtocolConfig } from '@deepseek-ai/dsh-llm-deepseek';
7
- import type { Options as ProtocolOptions, DeepSeekConnectionOptions } from '@deepseek-ai/dsh-llm-deepseek';
6
+ import type { Config as ProtocolConfig, DeepSeekConnectionOptions, ListingSettings, ModelListingConfig, ModelListingOptions, Options as ProtocolOptions } from '@deepseek-ai/dsh-llm-deepseek';
8
7
  /** Messages configuration with a per-request API-key reference. */
9
- export interface Config extends ProtocolConfig {
8
+ export interface Config extends ProtocolConfig, ModelListingConfig {
10
9
  /** Credential reference resolved per request; defaults to DEEPSEEK_API_KEY. */
11
10
  apiKeyEnv: Volatile<string>;
12
11
  }
13
12
  export declare const Config: z<Schemastery.ObjectS<NoInfer<{
14
13
  apiKeyEnv: z<string, string, "volatile-defined">;
14
+ modelsTimeoutMs: z<number, number, "volatile-defined">;
15
+ modelsMaxAgeMs: z<number, number, "volatile-defined">;
15
16
  baseURL: z<string, string, "volatile">;
16
17
  thinking: z<"enabled" | "disabled", "enabled" | "disabled", "volatile">;
17
18
  reasoningEffort: z<"low" | "off" | "high" | "max", "low" | "off" | "high" | "max", "volatile">;
@@ -32,6 +33,8 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
32
33
  retryPolicy: z<NoInfer<import("@deepseek-ai/dsh-llm").RetryPolicyConfig>, NoInfer<import("@deepseek-ai/dsh-llm").RetryPolicyConfig>, "volatile">;
33
34
  }>>, Schemastery.ObjectT<NoInfer<{
34
35
  apiKeyEnv: z<string, string, "volatile-defined">;
36
+ modelsTimeoutMs: z<number, number, "volatile-defined">;
37
+ modelsMaxAgeMs: z<number, number, "volatile-defined">;
35
38
  baseURL: z<string, string, "volatile">;
36
39
  thinking: z<"enabled" | "disabled", "enabled" | "disabled", "volatile">;
37
40
  reasoningEffort: z<"low" | "off" | "high" | "max", "low" | "off" | "high" | "max", "volatile">;
@@ -52,23 +55,28 @@ export declare const Config: z<Schemastery.ObjectS<NoInfer<{
52
55
  retryPolicy: z<NoInfer<import("@deepseek-ai/dsh-llm").RetryPolicyConfig>, NoInfer<import("@deepseek-ai/dsh-llm").RetryPolicyConfig>, "volatile">;
53
56
  }>>, "plain">;
54
57
  /** Plain deployment inputs for the API-key provider. */
55
- export type Options = ProtocolOptions & {
58
+ export type Options = ProtocolOptions & ModelListingOptions & {
56
59
  apiKeyEnv?: string;
57
60
  };
58
61
  /** Endpoint and credential reference captured from the same configuration generation. */
59
62
  export interface ResolvedDeepSeekOptions extends DeepSeekConnectionOptions {
60
63
  /** Credential reference used only for this connection snapshot. */
61
64
  apiKeyEnv: CredentialRef;
65
+ /**
66
+ * The official `/models` listing this generation follows; absent when `models`
67
+ * differs from the built-in catalog or the endpoint is not the official API.
68
+ */
69
+ listing?: ListingSettings;
62
70
  }
63
71
  /** Read one validated provider configuration.
64
72
  * @param config - live plugin configuration.
65
73
  * @returns detached resolver inputs.
66
74
  */
67
75
  export declare function plainOptions(config: Config): Options;
68
- /** Resolve API-key and protocol settings together.
76
+ /** Resolve API-key, listing, and protocol settings together.
69
77
  * @param config - raw deployment settings.
70
78
  * @param environment - application launch environment.
71
- * @returns validated endpoint facts and the matching credential reference.
79
+ * @returns validated endpoint facts, the matching credential reference, and the listing the catalog follows.
72
80
  */
73
81
  export declare function resolveAdapterOptions(config: Options, environment?: LaunchEnvironmentSnapshot): ResolvedDeepSeekOptions;
74
82
  //# sourceMappingURL=config.d.ts.map
@@ -1,4 +1,4 @@
1
- /** API-key authentication and credential-gated discovery for the official DeepSeek route. */
1
+ /** API-key authentication, credential-gated discovery, and the official model listing for the official DeepSeek route. */
2
2
  import type { Context } from '@deepseek-ai/cordis';
3
3
  import { Config } from './config.ts';
4
4
  export { Config, plainOptions, resolveAdapterOptions } from './config.ts';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@deepseek-ai/dsh-llm-deepseek-api-key",
3
3
  "description": "DeepSeek api-key provider authentication and discovery",
4
- "version": "0.2.1-rc.2",
4
+ "version": "0.2.1-rc.4",
5
5
  "publishConfig": {
6
6
  "access": "public"
7
7
  },
@@ -30,18 +30,18 @@
30
30
  "license": "MIT",
31
31
  "peerDependencies": {
32
32
  "@deepseek-ai/cordis": "~4.0.5",
33
- "@deepseek-ai/dsh-llm": "0.2.1-rc.2",
34
- "@deepseek-ai/dsh-llm-deepseek": "0.2.1-rc.2",
35
- "@deepseek-ai/dsh-launch-environment": "0.2.1-rc.2",
36
- "@deepseek-ai/dsh-credentials": "0.2.1-rc.2",
33
+ "@deepseek-ai/dsh-llm": "0.2.1-rc.4",
34
+ "@deepseek-ai/dsh-llm-deepseek": "0.2.1-rc.4",
35
+ "@deepseek-ai/dsh-launch-environment": "0.2.1-rc.4",
36
+ "@deepseek-ai/dsh-credentials": "0.2.1-rc.4",
37
37
  "@deepseek-ai/cordis-plugin-loader": "~1.0.6"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@deepseek-ai/cordis": "~4.0.5",
41
- "@deepseek-ai/dsh-llm": "0.2.1-rc.2",
42
- "@deepseek-ai/dsh-llm-deepseek": "0.2.1-rc.2",
43
- "@deepseek-ai/dsh-launch-environment": "0.2.1-rc.2",
44
- "@deepseek-ai/dsh-credentials": "0.2.1-rc.2",
41
+ "@deepseek-ai/dsh-llm": "0.2.1-rc.4",
42
+ "@deepseek-ai/dsh-llm-deepseek": "0.2.1-rc.4",
43
+ "@deepseek-ai/dsh-launch-environment": "0.2.1-rc.4",
44
+ "@deepseek-ai/dsh-credentials": "0.2.1-rc.4",
45
45
  "@deepseek-ai/cordis-plugin-loader": "~1.0.6"
46
46
  },
47
47
  "peerDependenciesMeta": {