From 5dd772d5370748ca7cf8d5c53b2cac013e484d99 Mon Sep 17 00:00:00 2001 From: Yichen Jiang Date: Thu, 24 Sep 2026 13:20:44 +0800 Subject: [PATCH] docs(llm): document the DeepSeek account listing, overrides, and version gate --- ...eek-account-remote-model-catalog.i18n.yaml | 19 ++++++++ ...4-deepseek-account-remote-model-catalog.md | 36 ++++++++++++++++ ...eepseek-account-remote-model-catalog.zh.md | 36 ++++++++++++++++ docs/module-graph.md | 3 +- docs/module-graph.zh.md | 3 +- docs/user/guide/providers.i18n.yaml | 3 ++ docs/user/guide/providers.md | 15 +++++++ docs/user/guide/providers.zh.md | 15 +++++++ .../llm/llm-deepseek-account/README.i18n.yaml | 32 +++++++------- packages/llm/llm-deepseek-account/README.md | 43 +++++++++++++++---- .../llm/llm-deepseek-account/README.zh.md | 43 +++++++++++++++---- packages/llm/llm-deepseek/README.i18n.yaml | 8 ++-- packages/llm/llm-deepseek/README.md | 3 +- packages/llm/llm-deepseek/README.zh.md | 3 +- 14 files changed, 220 insertions(+), 42 deletions(-) create mode 100644 .agents/notes/implemented/architecture/2026-09-24-deepseek-account-remote-model-catalog.i18n.yaml create mode 100644 .agents/notes/implemented/architecture/2026-09-24-deepseek-account-remote-model-catalog.md create mode 100644 .agents/notes/implemented/architecture/2026-09-24-deepseek-account-remote-model-catalog.zh.md diff --git a/.agents/notes/implemented/architecture/2026-09-24-deepseek-account-remote-model-catalog.i18n.yaml b/.agents/notes/implemented/architecture/2026-09-24-deepseek-account-remote-model-catalog.i18n.yaml new file mode 100644 index 0000000000..d04f960cdf --- /dev/null +++ b/.agents/notes/implemented/architecture/2026-09-24-deepseek-account-remote-model-catalog.i18n.yaml @@ -0,0 +1,19 @@ +# Bilingual-pair consistency record for 2026-09-24-deepseek-account-remote-model-catalog.md (docs/i18n/README.md): per heading +# section, a hash of its English and Chinese blocks outside code blocks and generated regions. +# After editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write .agents/notes/implemented/architecture/2026-09-24-deepseek-account-remote-model-catalog.md +/agent-note-deepseek-account-models-come-from-the-account-listing-and-gate-by-client-version: + en: e6d82714ac89a7ae + zh: 54c14278e749e7e1 +/agent-note-deepseek-account-models-come-from-the-account-listing-and-gate-by-client-version/problem: + en: 2eeb916eb65c66e7 + zh: 7bf5c0abb8bb61e4 +/agent-note-deepseek-account-models-come-from-the-account-listing-and-gate-by-client-version/decision: + en: 9af362037f6872d0 + zh: e4f8e3aefb0b8727 +/agent-note-deepseek-account-models-come-from-the-account-listing-and-gate-by-client-version/alternatives-considered: + en: 7c7880fdb0cc95f6 + zh: 2b8a539192626789 +/agent-note-deepseek-account-models-come-from-the-account-listing-and-gate-by-client-version/consequences: + en: 352ece6c2decaaf7 + zh: 1d0c3620fd26a35c diff --git a/.agents/notes/implemented/architecture/2026-09-24-deepseek-account-remote-model-catalog.md b/.agents/notes/implemented/architecture/2026-09-24-deepseek-account-remote-model-catalog.md new file mode 100644 index 0000000000..52768b6854 --- /dev/null +++ b/.agents/notes/implemented/architecture/2026-09-24-deepseek-account-remote-model-catalog.md @@ -0,0 +1,36 @@ +# Agent Note: DeepSeek account models come from the account listing and gate by client version + +Status: implemented + +English | [中文](2026-09-24-deepseek-account-remote-model-catalog.zh.md) + +## Problem + +The account route's catalog was deployment configuration: a build advertised the models it shipped with, a new model reached users only through a release, and nothing kept an older build from requesting a model it cannot drive. The account service publishes per-account model metadata at `GET /models`, and that listing can name models an older build does not understand. + +## Decision + +- [`llm-deepseek-account`](../../../../packages/llm/llm-deepseek-account/README.md) loads the listing lazily for each credential, waits for it once up to `modelsTimeoutMs`, refreshes it in the background after `modelsMaxAgeMs`, keeps the last good listing when a load fails or returns no model, and serves the bundled catalog before any listing loads. The cache lives in memory and is keyed by endpoint and token fingerprint, so per-account rollouts stay separate. +- A listed model is available when its `client_requirements["deepseek-harness"].min_version` is at most this build's version in semantic-version order. Without that declaration, only ids the bundled catalog knows are available; the others stay hidden, because a build cannot tell which future version supports them. A request for a refused listed model fails before network I/O with `CLIENT_UPGRADE_REQUIRED` or `MODEL_UNSUPPORTED`. +- Listed capabilities replace bundled ones field by field; the bundled entry keeps client-side knowledge such as tool-update mode and image budgets; `modelOverrides` applies last and reuses the `models` entry fields and validation. `max_output_tokens` bounds the request default instead of becoming it. +- The shared transport gains only two provider hooks, `connect` and `admit`; listing, gating, and merging stay in the account package. + +## Alternatives considered + +**Server-side filtering by client version only.** Codex sends `client_version` and lets its backend drop models. An older build then cannot show that a newer version supports a model, and a listing that omits a declaration would expose an unknown model. + +**Default parameters for unknown models.** Reasonix and Pi drive unknown ids with generic defaults, and MiniMax treats that as a defect. A model that needs client changes would run with the wrong behavior. + +**Whole-listing validation.** Codex, ZCode, and Kimi reject the whole listing on one bad field. One malformed row would then send every account back to the bundled catalog. + +**Persisting the last good listing.** A durable cache adds a persistence format and its migration duty; the bundled catalog already covers offline starts. + +**Keeping `models` with merge-by-id semantics.** The API-key route's `models` replaces its catalog, so the same field name with merge semantics would invert its meaning between sibling routes. + +**Generalizing the shared transport.** Per-model effort levels, an output-capability field, and catalog-change announcements would widen the Messages package for one consumer. The listed effort levels match the bundled defaults, and a model that needs different ones needs a release, which the gate enforces. + +## Consequences + +- An older build never offers a model it does not know unless the service declares a minimum version that build satisfies, and the service must declare `min_version` for a future build to show that an upgrade enables a model. +- A modified client can still request any id; the service enforces version floors only if it rejects such requests itself. +- The first account access after sign-in may wait up to `modelsTimeoutMs`; an offline start uses bundled capabilities until a listing loads; a listing change during a running process reaches the Web picker at its next catalog refresh. diff --git a/.agents/notes/implemented/architecture/2026-09-24-deepseek-account-remote-model-catalog.zh.md b/.agents/notes/implemented/architecture/2026-09-24-deepseek-account-remote-model-catalog.zh.md new file mode 100644 index 0000000000..ac5328530f --- /dev/null +++ b/.agents/notes/implemented/architecture/2026-09-24-deepseek-account-remote-model-catalog.zh.md @@ -0,0 +1,36 @@ +# Agent Note: DeepSeek 账号模型来自账号列表,并按客户端版本门控 + +Status: implemented + +[English](2026-09-24-deepseek-account-remote-model-catalog.md) | 中文 + +## 问题 + +账号路由的目录原本是部署配置:每个构建只提供它发布时自带的模型,新模型只能随发版到达用户,也没有办法阻止旧构建请求它驱动不了的模型。账号服务在 `GET /models` 上按账号发布模型元数据,这份列表可能包含旧构建不认识的模型。 + +## 决策 + +- [`llm-deepseek-account`](../../../../packages/llm/llm-deepseek-account/README.zh.md) 按凭据懒加载列表,首次访问最多等待 `modelsTimeoutMs`,超过 `modelsMaxAgeMs` 后在后台刷新;获取失败或返回的列表中没有模型时保留上一份,还没有加载到任何列表时使用内置目录。缓存只在内存中,以端点和 token 指纹为键,因此按账号灰度互不干扰。 +- 列出的模型在其 `client_requirements["deepseek-harness"].min_version` 按 semver 顺序不高于本构建版本时可用。没有这个声明时,只有内置目录认识的 id 可用,其余保持隐藏,因为构建无法判断将来哪个版本会支持它们。请求被拒绝的已列出模型时,在网络请求之前以 `CLIENT_UPGRADE_REQUIRED` 或 `MODEL_UNSUPPORTED` 失败。 +- 列表中的能力逐字段覆盖内置能力;内置条目保留工具更新模式、图片预算等客户端知识;`modelOverrides` 最后叠加,复用 `models` 条目的字段与校验。`max_output_tokens` 只限制请求默认值,不直接成为默认值。 +- 共享传输层只新增 `connect` 与 `admit` 两个提供方钩子;列表、门控与合并都留在账号包中。 + +## 考虑过的替代方案 + +**只在服务端按客户端版本过滤。** Codex 发送 `client_version`,由后端去掉模型。这样旧构建无法提示新版本支持某个模型,而且列表漏掉声明时会暴露未知模型。 + +**未知模型套用默认参数。** Reasonix 和 Pi 用通用默认值驱动未知 id,MiniMax 把这种做法当作缺陷。需要客户端改动的模型会以错误的方式运行。 + +**整份列表严格校验。** Codex、ZCode 和 Kimi 会因为一个坏字段拒绝整份列表。一条格式错误的记录就会让所有账号退回内置目录。 + +**持久化上一次成功的列表。** 持久缓存要新增持久化格式并承担迁移责任;内置目录已经覆盖离线启动。 + +**保留 `models` 并改为按 id 合并。** API key 路由的 `models` 会替换整个目录,同名字段在兄弟路由上改成合并语义会让含义相反。 + +**扩展共享传输层。** 按模型的推理档位、输出能力字段和目录变更通知,会为一个使用方扩大 Messages 包的范围。列表中的推理档位与内置默认值一致;需要不同档位的模型需要发版适配,门控正是用来保证这一点。 + +## 影响 + +- 旧构建永远不会提供它不认识的模型,除非服务端声明了该构建满足的最低版本;服务端必须声明 `min_version`,将来的构建才能提示升级后可用。 +- 改过的客户端仍然可以请求任意 id;只有服务端自己拒绝这类请求时,版本下限才会被强制执行。 +- 登录后的第一次账号访问最多等待 `modelsTimeoutMs`;离线启动在加载到列表之前使用内置能力;进程运行中途的列表变化要等 Web 选择器下一次刷新目录时才会显示。 diff --git a/docs/module-graph.md b/docs/module-graph.md index b15903d4d5..199da54101 100644 --- a/docs/module-graph.md +++ b/docs/module-graph.md @@ -695,6 +695,7 @@ flowchart TD pkg_llm_deepseek_account --> pkg_launch_environment pkg_llm_deepseek_account --> pkg_llm pkg_llm_deepseek_account --> pkg_llm_deepseek + pkg_llm_deepseek_account --> pkg_timeout pkg_tools --> pkg_agent pkg_tools --> pkg_invariants pkg_tools --> pkg_llm @@ -1564,7 +1565,7 @@ flowchart TD | [`terminal`](../packages/terminal/terminal) | `terminal` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand) | | [`loader-smoke`](../packages/test-support/loader-smoke) | `test-support` | [`agent`](../packages/core/agent), [`http-proxy`](../packages/util/http-proxy), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) | | [`workflow`](../packages/workflow/workflow) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) | -| [`llm-deepseek-account`](../packages/llm/llm-deepseek-account) | `llm` | [`deepseek-account`](../packages/credentials/deepseek-account), [`launch-environment`](../packages/util/launch-environment), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek) | +| [`llm-deepseek-account`](../packages/llm/llm-deepseek-account) | `llm` | [`deepseek-account`](../packages/credentials/deepseek-account), [`launch-environment`](../packages/util/launch-environment), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek), [`timeout`](../packages/util/timeout) | | [`tools`](../packages/core/tools) | `core` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`ptc-runtime`](../packages/ptc-runtime/ptc-runtime), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`user-approval`](../packages/interaction/user-approval) | | [`command-goal`](../packages/goal/command-goal) | `goal` | [`commands`](../packages/interaction/commands), [`goal`](../packages/goal/goal), [`llm`](../packages/llm/llm) | | [`goal-round-driver`](../packages/goal/goal-round-driver) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) | diff --git a/docs/module-graph.zh.md b/docs/module-graph.zh.md index 62a60aef0d..9b095c949c 100644 --- a/docs/module-graph.zh.md +++ b/docs/module-graph.zh.md @@ -697,6 +697,7 @@ flowchart TD pkg_llm_deepseek_account --> pkg_launch_environment pkg_llm_deepseek_account --> pkg_llm pkg_llm_deepseek_account --> pkg_llm_deepseek + pkg_llm_deepseek_account --> pkg_timeout pkg_tools --> pkg_agent pkg_tools --> pkg_invariants pkg_tools --> pkg_llm @@ -1566,7 +1567,7 @@ flowchart TD | [`terminal`](../packages/terminal/terminal) | `terminal` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand) | | [`loader-smoke`](../packages/test-support/loader-smoke) | `test-support` | [`agent`](../packages/core/agent), [`http-proxy`](../packages/util/http-proxy), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) | | [`workflow`](../packages/workflow/workflow) | `workflow` | [`agent`](../packages/core/agent), [`brand`](../packages/util/brand), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) | -| [`llm-deepseek-account`](../packages/llm/llm-deepseek-account) | `llm` | [`deepseek-account`](../packages/credentials/deepseek-account), [`launch-environment`](../packages/util/launch-environment), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek) | +| [`llm-deepseek-account`](../packages/llm/llm-deepseek-account) | `llm` | [`deepseek-account`](../packages/credentials/deepseek-account), [`launch-environment`](../packages/util/launch-environment), [`llm`](../packages/llm/llm), [`llm-deepseek`](../packages/llm/llm-deepseek), [`timeout`](../packages/util/timeout) | | [`tools`](../packages/core/tools) | `core` | [`agent`](../packages/core/agent), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`ptc-runtime`](../packages/ptc-runtime/ptc-runtime), [`sandbox`](../packages/sandbox/sandbox), [`sandbox-policy`](../packages/sandbox/sandbox-policy), [`scope`](../packages/core/scope), [`session`](../packages/core/session), [`system-prompt`](../packages/core/system-prompt), [`user-approval`](../packages/interaction/user-approval) | | [`command-goal`](../packages/goal/command-goal) | `goal` | [`commands`](../packages/interaction/commands), [`goal`](../packages/goal/goal), [`llm`](../packages/llm/llm) | | [`goal-round-driver`](../packages/goal/goal-round-driver) | `goal` | [`agent`](../packages/core/agent), [`goal`](../packages/goal/goal), [`invariants`](../packages/runtime-diagnostics/invariants), [`llm`](../packages/llm/llm), [`session`](../packages/core/session) | diff --git a/docs/user/guide/providers.i18n.yaml b/docs/user/guide/providers.i18n.yaml index 6f9b6e315e..02b48ad6fa 100644 --- a/docs/user/guide/providers.i18n.yaml +++ b/docs/user/guide/providers.i18n.yaml @@ -26,6 +26,9 @@ /configure-models/advanced-configuration/image-input: en: 75350cb615da03a4 zh: 6f8b3510085e4ac4 +/configure-models/advanced-configuration/deepseek-account-models: + en: fc09d02a89b211a7 + zh: e0f23a8912a24178 /configure-models/advanced-configuration/reasoning-effort: en: f7581a0760fdfd2c zh: dc6eaa16f74a0e9f diff --git a/docs/user/guide/providers.md b/docs/user/guide/providers.md index 773ff6722a..2cc34350e6 100644 --- a/docs/user/guide/providers.md +++ b/docs/user/guide/providers.md @@ -108,6 +108,21 @@ In pi-ai configuration, every list must name at least one modality except a mode Both fields state a claim about your endpoint rather than checking it. A model that declares images its endpoint does not serve is not caught here; the provider rejects the request instead. +### DeepSeek account models + +A signed-in DeepSeek account lists its models from the account service, so the Models page offers no model editor for it. Adjust a single model with `modelOverrides` in `$DSH_HOME/profiles//cordis.patch.yml`; the fields match a DeepSeek `models` entry, and unset fields keep the listed values: + +```yaml +- id: llm-deepseek-account + config: + modelOverrides: + deepseek-flash: + contextWindow: 524288 + maxTokens: 131072 +``` + +A model that needs a newer DeepSeek Harness fails with `CLIENT_UPGRADE_REQUIRED`, which names the version to install. The [account provider reference](../../../packages/llm/llm-deepseek-account/README.md) owns the listing, fallback, and gating rules. + ### Reasoning effort The model picker offers an **Effort** menu for a model that declares reasoning levels. A built-in provider's models inherit their levels from the installed catalog. A model you enter by hand declares none, so the Effort entry does not appear in the menu and the endpoint's own default decides whether the model thinks. Declare the levels with `reasoningEfforts` in `$DSH_HOME/profiles//cordis.patch.yml`: diff --git a/docs/user/guide/providers.zh.md b/docs/user/guide/providers.zh.md index 6d618ed1e9..6b37446a07 100644 --- a/docs/user/guide/providers.zh.md +++ b/docs/user/guide/providers.zh.md @@ -108,6 +108,21 @@ DeepSeek 将省略的 `inputModalities` 视为纯文本,并拒绝空列表。 这两个字段都是对你端点的断言,而不是对它的检查。声明了端点并不提供的图片能力的模型不会在这里被拦下,改由提供商拒绝该请求。 +### DeepSeek 账号的模型 + +已登录 DeepSeek 账号的模型列表来自账号服务,因此模型页不为它提供模型编辑器。要调整单个模型,在 `$DSH_HOME/profiles//cordis.patch.yml` 中使用 `modelOverrides`;字段与 DeepSeek 的 `models` 条目相同,未设置的字段沿用列表中的值: + +```yaml +- id: llm-deepseek-account + config: + modelOverrides: + deepseek-flash: + contextWindow: 524288 + maxTokens: 131072 +``` + +需要更新版 DeepSeek Harness 的模型会以 `CLIENT_UPGRADE_REQUIRED` 失败,并写明需要安装的版本。列表、兜底和门控规则见[账号提供方参考](../../../packages/llm/llm-deepseek-account/README.zh.md)。 + ### 推理等级 对于声明了推理等级的模型,模型选择器会提供**推理等级**菜单。内置提供商的模型从已安装目录继承其等级。手动录入的模型不声明任何等级,因此模型菜单里不会出现推理等级项,由端点自身的默认值决定模型是否思考。请在 `$DSH_HOME/profiles//cordis.patch.yml` 中用 `reasoningEfforts` 声明等级: diff --git a/packages/llm/llm-deepseek-account/README.i18n.yaml b/packages/llm/llm-deepseek-account/README.i18n.yaml index fd1e9e0ed0..a2834971c9 100644 --- a/packages/llm/llm-deepseek-account/README.i18n.yaml +++ b/packages/llm/llm-deepseek-account/README.i18n.yaml @@ -3,26 +3,26 @@ # After editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write packages/llm/llm-deepseek-account/README.md /: - en: 5bbc9f976d7dcfda - zh: 5de13a931c992603 + en: fc1ff226d5dca691 + zh: 770a99ef368c4767 /deepseek-ai-dsh-llm-deepseek-account: en: 8fa1148e0ee9d351 zh: 634ec9d79d5ffbf0 /deepseek-ai-dsh-llm-deepseek-account/summary: - en: d859ec7a1db8a41b - zh: b05ff7d19cb94667 + en: b80e34cdfb51009d + zh: eda44bcc181793d8 /deepseek-ai-dsh-llm-deepseek-account/table-of-contents: en: 23386452a66b2fbd zh: 4cf93c4bf17b3da2 /deepseek-ai-dsh-llm-deepseek-account/use-this-package: - en: 4fde33a269d60030 - zh: 22e522f4a3e25e8c + en: 969df791cb4efbe8 + zh: b44fe9c4393b9b46 /deepseek-ai-dsh-llm-deepseek-account/understand-the-implementation: - en: 20ba5b235f0d989b - zh: b18aec33828852ea + en: cc8535ce34519743 + zh: bdcf63f2b7a2f7f1 /deepseek-ai-dsh-llm-deepseek-account/further-exploration: - en: e8bf99d78aa7035f - zh: e4a74d2d48b6d271 + en: 30a69b9c66d89580 + zh: fcc026a6373c1bee /deepseek-ai-dsh-llm-deepseek-account/model-experience: en: 215e7ba838619b7b zh: a311da3843709f90 @@ -33,14 +33,14 @@ en: bd62360e6e6965ab zh: 519626fc34672187 /deepseek-ai-dsh-llm-deepseek-account/model-experience/authenticated-model-requests/token-effect: - en: fecefb75b719c317 - zh: f0c2322f657c5914 + en: b0f531baf29daf52 + zh: c89837e35981c318 /deepseek-ai-dsh-llm-deepseek-account/model-experience/authenticated-model-requests/kv-cache-effect: - en: 2004df60d997da79 - zh: a215a6586b44746b + en: db6aea22ebba25e1 + zh: caf4817596204df4 /deepseek-ai-dsh-llm-deepseek-account/known-limitations-and-deferred-work: - en: d5880c883b1b678f - zh: bf7a4da24ba75280 + en: 44471d7941db6d0a + zh: 36f5ccf23fb4ad4f /deepseek-ai-dsh-llm-deepseek-account/known-limitations-and-deferred-work/dev-note: en: 0525041e573e96b6 zh: b05ed8d852498e6b diff --git a/packages/llm/llm-deepseek-account/README.md b/packages/llm/llm-deepseek-account/README.md index 13b6df830e..830036c608 100644 --- a/packages/llm/llm-deepseek-account/README.md +++ b/packages/llm/llm-deepseek-account/README.md @@ -1,5 +1,5 @@ --- -description: "DeepSeek account authentication and model discovery." +description: "DeepSeek account authentication, model listing, and model gating." kind: "package-reference" --- @@ -9,7 +9,7 @@ English | [中文](README.zh.md) ## Summary -Register authentication and model discovery for `deepseek-account`. This plugin shares the [Messages transport](../llm-deepseek/README.md) and owns credential resolution and catalog availability. +Register authentication, the account model listing, and model gating for `deepseek-account`. This plugin shares the [Messages transport](../llm-deepseek/README.md) and owns credential resolution, the account catalog, and model admission. Authentication resolution returns `x-dsh-auth-token` and a failure callback capturing the same token. HTTP 401 classification and rejection stay in this provider; a rejected older request cannot clear a replacement login. @@ -25,28 +25,49 @@ Authentication resolution returns `x-dsh-auth-token` and a failure callback capt ## Use this package -Only `deepseekAccount.resolveToken(baseURL)` supplies the token; the account service validates the destination. Signed-out or ineligible requests fail with `ACCOUNT_SIGN_IN_REQUIRED` and discovery returns an empty catalog. The route never falls back to an API key. Other credential lookup errors propagate. +Only `deepseekAccount.resolveToken(url)` supplies the token; the account service validates the destination. Signed-out or ineligible requests fail with `ACCOUNT_SIGN_IN_REQUIRED` and discovery returns an empty catalog. The route never falls back to an API key. Other credential lookup errors propagate. + +The route's catalog comes from the account's `GET /models` listing. `modelsURL` defaults to `/models` at the origin of the Messages `baseURL` (`https://api.deepseek.com/models` for the official endpoint) and must share that origin. Listing requests carry the account token in `x-dsh-auth-token` and the harness `User-Agent`, and reject redirects. + +The first access of a credential 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`. A failed or empty listing keeps the previous one; before any listing loads, the route serves this build's bundled catalog (`deepseek-flash` and `deepseek-v4-pro`). A listing HTTP 401 does not remove the stored login. Each account token has its own listing, so the service can roll a model out to some accounts first. + +A listed model is offered only when this build can serve it. A model that declares `client_requirements["deepseek-harness"].min_version` is offered when this build's version is at least that version in semantic-version order, including prerelease tags such as `0.2.0-alpha.1` and `0.2.0-rc.1`. A model without that declaration is offered only when the bundled catalog knows its id; other listed models stay hidden. A request for a listed model this build cannot serve fails before network I/O with `CLIENT_UPGRADE_REQUIRED`, which names the required version, or with `MODEL_UNSUPPORTED`. Model ids the listing does not name pass through unchanged. + +Offered models take the listing's name, `context_window`, input modalities, and Messages system prompt mode over the bundled entry for the same id, which keeps its tool-update mode, image budgets, and description. `max_output_tokens` bounds the request default: a request that names no cap sends the smaller of `maxTokens` and that capability. The listing's effort levels are not read; every model offers `off`, `low`, `high`, and `max`. + +`modelOverrides` adjusts single models without replacing the catalog. Keys are model ids; values take the fields of a [Messages `models` entry](../llm-deepseek/README.md#minimal-configuration), and unset fields inherit. Overrides apply over the listing and the bundled catalog; they cannot add models or make a refused model available. An override that names a model neither the listing nor this build describes, or that the listed model rejects, becomes the provider diagnostic on the Models page, which has no editor for this route. The route has no `models` field; a configuration that still sets it fails at load and names `modelOverrides`. Edit `$DSH_HOME/profiles//cordis.patch.yml`; the next request uses the change. ```yaml - id: llm-deepseek-account name: '@deepseek-ai/dsh-llm-deepseek-account' config: reasoningEffort: high + modelOverrides: + deepseek-flash: + contextWindow: 524288 + maxTokens: 131072 ``` +| Field | Default | Meaning | +|---|---|---| +| `modelsURL` | `/models` at the `baseURL` origin | Listing endpoint; must share the Messages origin | +| `modelsTimeoutMs` | `5,000` | Deadline of one listing request and of a first-access wait | +| `modelsMaxAgeMs` | `600,000` | Listing age after which the next access refreshes it; a failed load retries after the same age | +| `modelOverrides` | `{}` | Field overrides by model id | + An account request HTTP 401 maps to `ACCOUNT_TOKEN_INVALID` independently of the response body. The account service removes the credential only if it still matches the request token. HTTP 403 and other failures do not clear the login. The account module owns task cancellation after sign-out; this plugin does not inspect Agents. -`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. +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. ## Understand the implementation -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. No invariant companion is published: discovery derives directly from configuration and credentials without an independent copy. +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 catalog and the gate. The listing cache lives in memory and is keyed by endpoint and a SHA-256 of the token. No invariant companion is published: the cache is the only copy of the remote catalog, and offered models and refusals derive from one resolution of it. ## Further Exploration -[LLM streaming](../../../docs/subsystems/llm-streaming.md) · [Messages](../llm-deepseek/README.md) +[LLM streaming](../../../docs/subsystems/llm-streaming.md) · [Messages](../llm-deepseek/README.md) · [Account catalog decision](../../../.agents/notes/implemented/architecture/2026-09-24-deepseek-account-remote-model-catalog.md) ## Model Experience @@ -59,17 +80,21 @@ Requests on `deepseek-account` add no model-visible text from this plugin; the s #### Token effect -Authentication and discovery add no input tokens; the selected model and request content determine actual usage. +Authentication and the listing add no input tokens. The resolved catalog sets each model's context window and default output cap, which each request's logged context and header record. #### KV Cache effect -Credentials and catalog availability do not enter model input; the protocol transport owns request-prefix serialization. +Credentials and catalog availability do not enter model input; the protocol transport owns request-prefix serialization. A catalog change to a model's system prompt mode changes how later system prompt updates are serialized. ## Known Limitations and Deferred Work -- The catalog is configuration metadata, not remote validation that a model is accepted; actual calls can still fail. +- The listing cache lives in memory; a start without network serves bundled capabilities until a listing loads. +- A listing change during a running process reaches the Web model picker at its next catalog refresh; the plugin does not announce it. +- The gate runs in this client; a modified client can still request any id unless the service rejects it. +- The listing's effort levels are ignored. +- An override can set but not remove `systemPromptUpdate` and `toolUpdate` declarations. ### Dev Note diff --git a/packages/llm/llm-deepseek-account/README.zh.md b/packages/llm/llm-deepseek-account/README.zh.md index f2ca9fe281..9da12ca446 100644 --- a/packages/llm/llm-deepseek-account/README.zh.md +++ b/packages/llm/llm-deepseek-account/README.zh.md @@ -1,5 +1,5 @@ --- -description: "DeepSeek 账号 authentication and model discovery." +description: "DeepSeek 账号鉴权、模型列表与模型门控。" kind: "package-reference" --- @@ -9,7 +9,7 @@ kind: "package-reference" ## 概述 -为 `deepseek-account` 注册独立的鉴权和模型发现插件。通过 [llm-deepseek](../llm-deepseek/README.zh.md) 共用 Messages 请求实现;本包拥有凭据解析和目录可用性判断。 +为 `deepseek-account` 注册鉴权、账号模型列表和模型门控。通过 [llm-deepseek](../llm-deepseek/README.zh.md) 共用 Messages 请求实现;本包负责凭据解析、账号目录和模型准入。 鉴权解析返回 `x-dsh-auth-token` 和捕获同一 token 的失败回调。HTTP 401 的分类和凭据失效处理由此提供方负责;被拒绝的旧请求不能清除替换后的登录。 @@ -25,28 +25,49 @@ kind: "package-reference" ## 使用此包 -账号 token 仅由 `deepseekAccount.resolveToken(baseURL)` 提供,目标地址必须满足账号服务的来源校验。未登录或目标不允许账号鉴权时,请求以 `ACCOUNT_SIGN_IN_REQUIRED` 失败,目录为空;不会回退到 API key。其他凭据查询错误继续向调用者报告。 +账号 token 仅由 `deepseekAccount.resolveToken(url)` 提供,目标地址必须通过账号服务的来源校验。未登录或目标不允许账号鉴权时,请求以 `ACCOUNT_SIGN_IN_REQUIRED` 失败,目录为空;不会回退到 API key。其他凭据查询错误继续向调用者报告。 + +路由的目录来自账号的 `GET /models` 列表。`modelsURL` 默认取 Messages `baseURL` 的 origin 加 `/models`(官方端点即 `https://api.deepseek.com/models`),且必须与该 origin 相同。列表请求用 `x-dsh-auth-token` 携带账号 token,带上 harness 的 `User-Agent`,并拒绝重定向。 + +同一凭据的第一次访问会等待列表,最长 `modelsTimeoutMs`。之后的访问直接使用已加载的列表,列表超过 `modelsMaxAgeMs` 后在后台刷新。获取失败或得到空列表时保留上一份;还没有加载到任何列表时,路由提供本版本内置的目录(`deepseek-flash` 与 `deepseek-v4-pro`)。列表请求返回 HTTP 401 不会清除已保存的登录。每个账号 token 有自己的列表,因此服务端可以先对部分账号开放某个模型。 + +只有本版本能用的模型才会出现在目录里。声明了 `client_requirements["deepseek-harness"].min_version` 的模型,在本版本的版本号按 semver 顺序不低于该版本时提供,预发布号(如 `0.2.0-alpha.1`、`0.2.0-rc.1`)同样参与比较。没有这个声明的模型,只有内置目录认识它的 id 时才提供;其余列出的模型保持隐藏。请求一个本版本不能用的已列出模型时,会在网络请求之前失败:`CLIENT_UPGRADE_REQUIRED` 会写明所需版本,否则为 `MODEL_UNSUPPORTED`。列表没有列出的模型 id 照常透传。 + +提供的模型以列表中的名称、`context_window`、输入模态和 Messages system prompt 模式覆盖同 id 的内置条目,内置条目保留工具更新模式、图片预算和描述。`max_output_tokens` 限制请求默认值:请求没有指定上限时,发送 `maxTokens` 与该能力上限中较小的一个。列表中的推理档位不被读取;每个模型都提供 `off`、`low`、`high`、`max`。 + +`modelOverrides` 调整单个模型而不替换整个目录。键是模型 id;值的字段与 [Messages `models` 条目](../llm-deepseek/README.zh.md#minimal-configuration)相同,未设置的字段沿用下层的值。覆盖作用在列表和内置目录之上;不能新增模型,也不能让被拒绝的模型变得可用。覆盖指向的模型既不在列表里、本版本也不认识,或者被列出的模型拒绝时,会成为模型页上该提供方的诊断信息;模型页不为此路由提供编辑器。此路由没有 `models` 字段;配置里仍写了它时,加载即失败并提示改用 `modelOverrides`。修改 `$DSH_HOME/profiles//cordis.patch.yml`,下一次请求即生效。 ```yaml - id: llm-deepseek-account name: '@deepseek-ai/dsh-llm-deepseek-account' config: reasoningEffort: high + modelOverrides: + deepseek-flash: + contextWindow: 524288 + maxTokens: 131072 ``` +| 字段 | 默认值 | 含义 | +|---|---|---| +| `modelsURL` | `baseURL` origin 下的 `/models` | 列表端点;必须与 Messages 同源 | +| `modelsTimeoutMs` | `5,000` | 单次列表请求以及首次访问等待的上限 | +| `modelsMaxAgeMs` | `600,000` | 列表超过这个时长后,下一次访问时刷新;获取失败后同样在这个时长后重试 | +| `modelOverrides` | `{}` | 按模型 id 的字段覆盖 | + 账号请求的 HTTP 401 与响应正文无关:映射为 `ACCOUNT_TOKEN_INVALID`,并要求账号服务仅在当前 token 仍与请求所用 token 相同时清除凭据。403 和其他错误不会触发清除。账号模块负责退登后打断任务,本插件不遍历 Agent。 -`models` 是该 provider 独立的可配置目录;默认值和协议能力来自共享传输包。目录判断不探测推理端点。设置命名空间采用 Cordis entry id,没有 entry 时采用插件名。产品保留 official 的 `llm-deepseek` entry id,账号使用 `llm-deepseek-account`。 +设置命名空间采用 Cordis entry id,没有 entry 时采用插件名。产品保留 official 的 `llm-deepseek` entry id,账号使用 `llm-deepseek-account`。 ## 理解实现 -注册和监听随插件生命周期释放。共享 Host 绑定提供附件访问、请求扩展、匿名标识和重试策略的原子更新;本包只注册自己的路由。无需 invariant companion:目录直接由同一次配置和凭据读取派生,不维护独立副本。 +注册和监听随插件生命周期释放,释放时会中止进行中的列表请求。共享 Host 绑定提供附件访问、请求扩展、匿名标识和重试策略的原子更新;本包只注册自己的路由,并通过传输层的 `connect` 与 `admit` 钩子接入目录和门控。列表缓存只在内存中,以端点和 token 的 SHA-256 为键。无需 invariant companion:缓存是远端目录的唯一副本,提供的模型与拒绝集合都来自对它的同一次解析。 ## 进一步探索 -[LLM streaming](../../../docs/subsystems/llm-streaming.zh.md) · [Messages](../llm-deepseek/README.zh.md) +[LLM streaming](../../../docs/subsystems/llm-streaming.zh.md) · [Messages](../llm-deepseek/README.zh.md) · [账号目录决策](../../../.agents/notes/implemented/architecture/2026-09-24-deepseek-account-remote-model-catalog.zh.md) ## 模型体验 @@ -59,17 +80,21 @@ kind: "package-reference" #### Token effect -鉴权和目录过滤不增加输入 token;实际调用的 token 由所选模型与请求内容决定。 +鉴权和列表不增加输入 token。解析后的目录决定每个模型的上下文窗口和默认输出上限,每次请求记录的上下文与请求头会记下这些值。 #### KV Cache effect -凭据与目录可用性不进入模型输入;协议传输拥有请求前缀的序列化。 +凭据与目录可用性不进入模型输入;协议传输拥有请求前缀的序列化。目录中某个模型的 system prompt 模式变化,会改变之后 system prompt 更新的序列化方式。 ## 已知限制与后续工作 -- 目录是配置元数据,不验证远端是否接受模型;实际调用仍可能失败。 +- 列表缓存只在内存中;没有网络时启动,在加载到列表之前使用内置能力。 +- 进程运行中途的列表变化,要等 Web 模型选择器下一次刷新目录时才会显示;插件不会主动通知。 +- 门控在客户端执行;改过的客户端仍可以请求任意 id,除非服务端拒绝。 +- 列表中的推理档位被忽略。 +- 覆盖可以设置、但不能移除 `systemPromptUpdate` 与 `toolUpdate` 声明。 ### 开发备注 diff --git a/packages/llm/llm-deepseek/README.i18n.yaml b/packages/llm/llm-deepseek/README.i18n.yaml index 677265edec..b851411448 100644 --- a/packages/llm/llm-deepseek/README.i18n.yaml +++ b/packages/llm/llm-deepseek/README.i18n.yaml @@ -18,8 +18,8 @@ en: fe27b41cb85cd031 zh: 52d833f0909d94f1 /deepseek-ai-dsh-llm-deepseek/use-this-package/when-to-choose-it: - en: cbeb14a9a602c2c9 - zh: c78c0d26c5791085 + en: 6850d08bc091ad23 + zh: 2906e6cf1dfed6ce /deepseek-ai-dsh-llm-deepseek/use-this-package/minimal-configuration: en: cfc2de7ef728dbf9 zh: 2ce1a406061a71aa @@ -39,8 +39,8 @@ en: ec03fc11584fc698 zh: a3f8645b0b817f67 /deepseek-ai-dsh-llm-deepseek/use-this-package/failures-and-recovery: - en: d31bd24c8c419f88 - zh: 91205cb3cd754f39 + en: 1cbfbabee449e756 + zh: 5b13926e079a131e /deepseek-ai-dsh-llm-deepseek/understand-the-implementation: en: 1598ac8a8bb9b6bf zh: fdd089c5ae7722cd diff --git a/packages/llm/llm-deepseek/README.md b/packages/llm/llm-deepseek/README.md index acf981e614..7ab08ec44b 100644 --- a/packages/llm/llm-deepseek/README.md +++ b/packages/llm/llm-deepseek/README.md @@ -35,6 +35,7 @@ The adapter accepts the LLM service's [request-only user inputs](../llm/README.m Choose this adapter for DeepSeek's official API or a Messages-compatible gateway through `baseURL`. Choose `dsh-llm-pi-ai` when the same composition also routes other providers or hand-declared gateways through pi-ai's catalogs; the two adapters can be mounted together because their route names do not collide. Registering any other adapter for `deepseek-official` fails with `DUPLICATE_ADAPTER`. + ### Minimal configuration ```yaml @@ -117,7 +118,7 @@ Successful Files responses must contain valid JSON. JSON decoding failures from Non-2xx responses fail with stable codes: `AUTH` (401/403), `QUOTA`, `RATE_LIMIT`, `CONTEXT_WINDOW_EXCEEDED`, `INVALID_REQUEST`, `SERVER`, and `HTTP_` otherwise; pre-response transport failures throw `TRANSPORT`, caller aborts throw `ABORTED`, and stream-idle expiry throws `TIMEOUT`. Request-extension preparation, field collision, or post-2xx acceptance fails with `REQUEST_EXTENSION`. A normalized-image rejection names every plausible attachment and its durable position when the provider does not identify a file id. Stale-file rejection invalidates the named mappings (or every mapping used by the attempt) and permits one replacement model request. Protocol violations throw `STREAM_CLOSED` or `MALFORMED_RESPONSE`, and a terminal `stop` with no content blocks becomes `EMPTY_RESPONSE`, which the default retry policy retries. A request on the official route without an API key fails with `MISSING_CREDENTIAL`, and a malformed credential fails with `INVALID_CREDENTIAL` naming the reference to fix — never any part of the key. -Provider plugins own catalog availability; only the account route requires a stored grant for discovery. Their catalogs are configured independently; the transport supplies shared default model metadata and capability resolution. +Provider plugins own catalog availability: the API-key route serves its configured `models`, and the account route serves its account listing (see the [account provider](../llm-deepseek-account/README.md)). The transport supplies the bundled catalog, the catalog-entry field schemas, and capability resolution. A provider may also supply `connect(signal)`, which resolves the connection for exact-model lookups and prepared calls after provider-owned work such as a catalog load, and `admit(connection, model)`, which refuses a model before network I/O. ----- diff --git a/packages/llm/llm-deepseek/README.zh.md b/packages/llm/llm-deepseek/README.zh.md index 3e61069be8..bce17716fe 100644 --- a/packages/llm/llm-deepseek/README.zh.md +++ b/packages/llm/llm-deepseek/README.zh.md @@ -35,6 +35,7 @@ kind: "package-reference" 面向 DeepSeek 官方 API,或通过 `baseURL` 连接兼容 Messages 的网关时,选择本适配器。当同一组合还要通过 pi-ai 目录路由其他提供方或手工声明的网关时,选择 `dsh-llm-pi-ai`;两个适配器可以同时挂载,因为它们的路由名不冲突。为 `deepseek-official` 注册任何其他适配器会以 `DUPLICATE_ADAPTER` 失败。 + ### 最小配置 ```yaml @@ -117,7 +118,7 @@ Files 模式通过 `maxRequestFilesBytes` 与 `maxImagesPerRequest` 限制保留 非 2xx 响应以稳定 code 失败:`AUTH`(401/403)、`QUOTA`、`RATE_LIMIT`、`CONTEXT_WINDOW_EXCEEDED`、`INVALID_REQUEST`、`SERVER` 以及其他情况的 `HTTP_`;响应前传输失败抛出 `TRANSPORT`,调用方中止抛出 `ABORTED`,流空闲超时抛出 `TIMEOUT`。请求扩展准备、字段冲突或 2xx 后接受失败使用 `REQUEST_EXTENSION`。当提供方未指出 file id 时,规范化图片拒绝会列出所有可能附件及其持久位置。陈旧文件拒绝会使点名映射(或该次尝试使用的全部映射)失效,并允许一次替换模型请求。协议违规抛出 `STREAM_CLOSED` 或 `MALFORMED_RESPONSE`;不带内容块的终止 `stop` 变成 `EMPTY_RESPONSE`,默认重试策略会重试它。官方路由缺少 API Key 的请求以 `MISSING_CREDENTIAL` 失败;格式错误的凭据以 `INVALID_CREDENTIAL` 失败,并点名需要修复的引用——绝不包含密钥的任何部分。 -提供方插件负责目录可用性;仅账号路由要求存有凭据才能发现模型。两者的目录独立配置;传输层提供共享的默认模型元数据和能力解析。 +提供方插件负责目录可用性:API key 路由提供配置中的 `models`,账号路由提供账号的模型列表(见[账号提供方](../llm-deepseek-account/README.zh.md))。传输层提供内置目录、目录条目字段的 schema 和能力解析。提供方还可以提供 `connect(signal)`:在提供方自己的工作(例如加载目录)完成后,为精确模型查询和预备调用解析连接;以及 `admit(connection, model)`:在网络请求之前拒绝某个模型。 -----