converk/dsh-ux-kit--plugins-model-capabilities0

dsh-plugin-model-capabilities

在 DSH 设置→模型的官方模型行里补上思考强度(llm-pi-ai)与多模态(llm-pi-ai / llm-deepseek),并提供 1M/128K 容量快捷填入

包名
dsh-plugin-model-capabilities
版本
0.1.0
最近更新
2026年9月10日

安装

此插件尚未提供可验证的 bundle,或兼容性检查未通过。请先阅读仓库说明。 阅读完整 README ↗

dsh-plugin-model-capabilities

在 DSH Web GUI 的 设置 → 模型 → 编辑提供方 → 自定义设置 → 模型行(展开「容量」) 里,给官方模型编辑器补上官方没有的能力字段。插件同时挂两个提供方卡片 key(同一份实现):

  • llm-pi-ai
    1. 思考强度:只提供 5 档 off / low / medium / high / max;每档可以改「过线拼写」(例如 high 在别的网关叫 ultra)。一个档都不勾 = 该模型不支持思考(写 reasoningEfforts: false)。
    2. 多模态:单选 文本 / 文本+图片(写 input)。
  • llm-deepseek:只加 多模态(写 inputModalities)。DeepSeek 适配器没有每模型思考强度——档位由提供方级 reasoningEffort: off|low|high|max + thinking: enabled|disabled 控制(settings.yamlllm-deepseek 分节根级),模型行里只读显示当前提供方级值。

两个命名空间都给官方「上下文窗口 / 最大输出」输入框加 1M / 128K 快捷按钮;新增模型行展开容量时自动填 1000000 / 131072

展开模型行时,控件顶部显示该模型当前配置摘要(DeepSeek 的「思考」位置显示提供方级值):

当前:思考 high + max · 多模态 文本+图片 · 上下文 1000000 / 输出 131072

改完点官方那个「保存」即可——插件会在官方写入完成后,把字段写进对应分节(llm-pi-aiinput / reasoningEffortsllm-deepseekinputModalities)的模型条目里。纯 client 侧插件,宿主半区为空壳;运行时零第三方依赖,只 require 页面种子模块 react / react/jsx-runtime

交互与同步时机

设置 → 模型 → 某个 llm-pi-ai / llm-deepseek 提供方 → 编辑 → 自定义设置 → 模型行 → 点「容量」箭头展开
  当前:思考 high + max · 多模态 文本+图片 · 上下文 1000000 / 输出 131072
  思考强度  [协议预置 ▾]        ← llm-pi-ai 才有;llm-deepseek 显示一行提供方级提示
            ☑ off   [留空 = 不发参数]
            ☐ low   [low]
            ☑ medium[medium]
            ☑ high  [high]
            ☑ max   [ultra]
            都不勾选 = 该模型不支持思考(写 false)
  多模态    ○ 文本   ○ 文本+图片
  [1M / 128K]   已改,点官方「保存」后写入
  • llm-deepseek 卡片里没有「思考强度」这一块,只显示一行提示:DeepSeek 适配器没有每模型思考强度;档位由提供方级 reasoningEffort / thinking 控制(settings.yaml)。;其余(多模态单选、1M / 128K、摘要)相同。

  • 官方模型行内部没有 Slot(官方只声明了 settings.models.provider-card 卡片级席位),所以插件在席位挂载后观察卡片 DOM,把控件注入到模型行展开后的「容量」区域(_modelAdvanced)。锚点是官方 class 的稳定后缀 _modelEntry / _modelRow / _modelAdvanced / _editorActions,并带 DOM 兜底;DSH 升级若改这些结构,需要重新核对(见「对接契约」)。

  • 展开即显示当前值:控件从用户层已存条目回填;摘要同时读官方「上下文窗口 / 最大输出」输入框的当前值(没有值就显示官方占位符 = 提供方默认)。

  • 同步时机:官方卡片把模型列表存在它自己的 React 草稿里,点「保存」才一次性写 settings。插件在官方写入之后再写自己的字段,避免被官方草稿用旧值覆盖。因此:

    • 控件改动先挂起,行内提示「已改,点官方『保存』后写入」;
    • 点官方「保存」:官方先写它自己的字段;插件捕获主按钮点击,等 settings/document-updated(或卡片关闭且官方没有产生写入)后用最新 revision 合成一次 settings.mutate
    • 点官方「取消」:挂起改动丢弃;
    • 写失败(例如 settings/conflictsettings-rejected)原样显示 Host 的 code/message,挂起改动保留,可再点一次「保存」重试。
  • 上下文 / 最大输出归官方管:插件不解析、不写这两个字段,只通过 1M / 128K 按钮走官方输入框的 input 事件(React 受控输入)把值收进官方草稿;官方「保存」时自然写入。

  • 没有「跟随目录」:插件总是写显式值——不勾档位写 false,勾了就写 dict,多模态写 ['text']['text','image']。内置目录的 minimal / xhigh 不显示也不管理(保存时若原本有这两个键,会被钉成「不支持」)。

  • 添加提供方草稿卡片configured === false)不注入:官方添加卡片用它打开时的 revision 一次性创建整个 profile,插件此时写入会抢 revision;等提供方保存后再进编辑页配置。

写入位置

一律使用 owner props 的 provider.settingsPath(指向 providers.),不硬编码路径。

官方编辑器显示的内容插件写哪里
用户层已有 models[]改数组里的条目(整组改动合成一次 set models;其它条目与 name / compat / contextWindow / maxTokens 原样保留)
用户层没有 models[](显示的是内置目录的继承行)用官方编辑器当前列出的全部模型 id 物化 models[]:未编辑的条目只写 id,其余字段按 id 继续继承内置目录;同时删掉可能存在的 modelOverrides

第二种和官方编辑器你改任一官方字段时的行为一致(官方也会把继承行整体物化成 models[]),不会把目录压成一行:models[] 里列出了官方编辑器显示的全部 id。如果用户层同时有 models[]modelOverrides(本体拒绝的组合),插件直接报 storage-conflict,不写。

字段映射与校验(对齐 dsh-llm-pi-ai / dsh-llm-deepseek

llm-pi-aireasoningEfforts + input):

控件字段规则
思考强度:一个档都不勾reasoningEfforts: false该模型不支持思考
思考强度:勾了档位reasoningEfforts: { : }只写勾选的 5 档;未勾选的档位不写键,本体把它们钉成「不支持」off 勾选且留空写 null(不发参数);非 off 档必须非空字符串;只勾 off 会被拒(等于「不发参数」却没有思考档)
多模态:文本input: ['text']词表只有 text / image
多模态:文本+图片input: ['text', 'image']
1M / 128K 按钮官方 contextWindow / maxTokens 输入框写入 1000000 / 131072;由官方编辑器保存

llm-deepseek(只有多模态;思考强度是提供方级):

控件字段规则
多模态:文本inputModalities: ['text']词表只有 text / image(注意字段名是 inputModalities,不是 input
多模态:文本+图片inputModalities: ['text', 'image']
1M / 128K 按钮官方 contextWindow / maxTokens 输入框写入 1000000 / 131072;由官方编辑器保存
思考强度不写每模型字段DeepSeek 适配器没有每模型 reasoningEfforts;档位由 llm-deepseek 分节根级 reasoningEffort: off|low|high|max + thinking: enabled|disabled 控制,模型行只读显示当前提供方级值
  • 协议预置只有 OpenAI 兼容 / Anthropic 两个(外加「自定义」),只填档位表,绝不写 compatcompat.thinkingFormat 等协议字段仍由 settings.yaml 管理。
  • models[] 是数组,settings 服务的 path op 把数组当不透明值处理(不穿下标),所以模型数组的改动合成一次 set models,从保存前重新读到的最新用户层重建。

构建与检查

pnpm install
node build.mjs                                  # lib/index.js + lib/capability.js + lib/client.js(+.map)
node node_modules/typescript/bin/tsc --noEmit   # 0 错
node scripts/selftest.mjs                       # 纯逻辑 24 项全过

pnpm 10+ 默认拦截依赖构建脚本(会提示 ERR_PNPM_IGNORED_BUILDS: esbuild);esbuild 的平台二进制经 optionalDependencies 分发,postinstall 并非必需——直接 node build.mjs / node node_modules/typescript/bin/tsc --noEmit 即可,不要走 pnpm run

产物协议(AGENTS.md 3.4):lib/client.js 是经典脚本(非 ES Module),自注册 window.__ModuleLoader__.load({ id: "dsh-plugin-model-capabilities", factory }),工厂体内只 require("react") / require("react/jsx-runtime"),并带合法 sourcemap trailer //# sourceMappingURL=client.js.map

安装到 DSH

本包是独立 npm 包,不依赖仓库内任何其他插件:

dsh plugin --profile  add D:\env\Projects\dsh-plugins\plugins\model-capabilities

然后在 profile 的 cordis.patch.yml 里用 insert 形制挂载(裸 - name: 会被 dsh-app-bootapplyEntryPatches 跳过并 warn patch: id is required for non-insert patches):

- insert:
    - id: model-capabilities
      name: dsh-plugin-model-capabilities

patchReload: live 下保存 patch 即可重新组合,但浏览器需要刷新一次才会拿到新的 window.__DSH_BOOT__

对接的真实契约(本机 DSH 0.1.2-rc.1,部署于 D:\env\node-global\dsh-stable

  • 席位dsh-client-ui-settings-models/lib/types/client/slot-contract.d.tssettings.models.provider-card,kind keyed,scope root,owner = ProviderCardExtrasOwnerProps)与 store.d.tsProviderDirectoryEntrysettingsPath / declared 等);席位渲染点见该包 lib/client.js
  • 官方模型行 DOM(注入锚点):同包 lib/client.jsDeepSeekModelsEditor / ModelListEditor——section._modelCatalog > div._modelList > div._modelEntry > div._modelRow(模型 ID、显示名称、容量箭头、删除),展开后 div._modelAdvanced 里是两个 input(上下文窗口 / 最大输出);官方保存/取消按钮在 div._editorActions 里(最后一个是主按钮)。
  • 字段语义dsh-llm-pi-ai/lib/types/{config,catalog}.d.tslib/index.jsresolveModelReasoning / resolveRouteModelsreasoningEfforts 未勾选档写 nulloff 可留空、models[] 条目按 id 继承目录字段。
  • DeepSeek 字段语义dsh-llm-deepseek/lib/index.jscatalogModel schema——每模型只有 contextWindow / maxTokens / inputModalities / imagePixelBudget / imageMaxBytes没有 reasoningEffortsConfig.reasoningEffort: off|low|high|maxConfig.thinking: enabled|disabled 是分节根级字段。
  • Remotedsh-api-settings-controller/lib/typert.remote-client.d.tssettings/describesettings/mutate);一元调用一律解析为 RemoteResultinject 必须声明 remote.settings——ctx.remote 的子命名空间是 cordis Guard 代理,未声明直接抛 cannot get property "remote.settings" without inject
  • settings 服务dsh-settings/lib/types/index.jsapplyPathOp(数组不透明)与 mutate(revision 冲突 settings/conflict)。
  • localedsh-client-locale/lib/types/client/index.d.ts 的三参非类型化 register(ns, locale, dict) + bind(ns);插值 {name}
  • 样式令牌:官方 dsh-client-ui-settings-models/lib/client.js 内嵌 CSS(输入 h32 radius8、border --dsw-alias-border-l4bg-layer-1;小按钮 h28 radius14;label 12px/500);CSS 以 style[data-plugin-css="/.module.css"] + data-plugin 幂等注入。

写入后 DSH 如何生效(本机实测 + 官方讨论)

插件写的是 llm-pi-ai.providers..models[] 条目里的 reasoningEfforts / inputllm-deepseek.providers..models[] 条目里的 inputModalities(上下文与最大输出由官方输入框写同一条目的 contextWindow / maxTokens)。这不是「写了没人读」:

  • 本机实测:用部署里的官方 @deepseek-ai/dsh-llm-pi-aiapply + PiAiAdapter 解析一份与插件写出的完全相同的配置(input: [text, image]reasoningEfforts: { off: null, low: low, medium: medium, high: high, max: ultra }contextWindow: 1000000maxTokens: 131072),适配器返回:
    • listModels('demo')inputModalities: ['text','image']
    • resolveModel('demo','demo-vision-thinker')context.contextWindow: 1000000defaultMaxTokens: 131072reasoning.efforts: [off, low, medium, high, max]max 的过线拼写是 ultra
  • 图片门禁dsh-api-session-controller 在附加图片时检查 inputModalities,不包含 image 就直接拒绝(Model "..." does not support image input.);dsh-llm 的请求投影也用它决定是否把图片发给模型。所以 input: ['text','image'] 就是「这个模型支持多模态」的开关。
  • DeepSeek 多模态dsh-llm-deepseekcatalogModel schema 里 inputModalities 就是每模型字段(默认 ["text"],允许 ["text","image"]);适配器把它投影成 LlmModelInfo.inputModalities,同一个 dsh-api-session-controller 门禁生效。官方讨论 #5267 正是要求把 inputModalities 暴露到 Web 模型编辑器,插件补的就是这个入口。
  • 思考强度:官方 README(dsh-llm-pi-ai/README.zh.md)明确 reasoningEfforts 的每个键是选择器提供的等级、值是该等级过线的拼写(max: ultra 可以改名);dsh-client-ui-model-selectionmodel.reasoning.efforts 生成 composer 的档位选项。DeepSeek 侧没有每模型字段,档位来自 Config.reasoningEffort / Config.thinking(提供方级)。
  • 官方讨论
  • 写入位置就是官方唯一读的地方:providers..models[](或 modelOverrides.);dsh-llm-pi-airesolveRouteModelsinput / contextWindow / maxTokens / reasoningEfforts 合并成 pi-ai Model,适配器再通过 modelInfo 暴露给会话与 composer。

自测覆盖

scripts/selftest.mjs 直接 import 构建产物 lib/capability.js,24 项覆盖:

  • 只有 off/low/medium/high/max 五档;draftFromEntry 忽略 minimal / xhigh
  • 一个档都不勾 → false;只勾 off 被拒;非 off 档空拼写被拒;未勾选档不写键;off 留空写 null
  • 多模态始终显式 ['text'] / ['text','image']
  • 用户层已有 models[] → 一次 set models,保留其它条目与 name / compat / description / contextWindow / maxTokens
  • 没有 models[] → 用官方编辑器可见 id 完整物化 models[] 并删 modelOverrides;模型还没保存 / 没有可见 id / modelsmodelOverrides 并存分别被拒;
  • 改动与已存值相同不产生 op;清空档位写 false 且保留 name / compat;协议预置只填档位表、绝不写 compat1M / 128K 预设的确切值;
  • DeepSeekdraftFromEntryinputModalitiesmanageReasoning: false 不产生 reasoningEffortsbuildModelOps 只写 inputModalities 并保留 contextWindow / maxTokens / description / imagePixelBudget / imageMaxBytes,不加 reasoningEfforts

边界与已知限制

  • 不 import / 依赖任何其他插件,不共享 utils;删除本插件不影响 DSH 与其他插件(AGENTS.md 4.1 / 4.4)。
  • 只写 llm-pi-ai / llm-deepseek 分节下 providers. 的模型条目(llm-deepseek 只写 inputModalities),不动其他用户配置。
  • settings.models.provider-card 上占 llm-pi-aillm-deepseek 两个 key(同一份实现),不注册第二个席位。
  • DeepSeek 适配器没有每模型思考强度;插件不伪造该字段,模型行只读显示提供方级 reasoningEffort / thinking,要改请直接改 settings.yamlllm-deepseek 分节根级。
  • 官方模型行内部无 Slot,注入依赖官方 DOM 结构;若 DSH 升级改了 _modelEntry / _modelAdvanced / _editorActions 这些 class 后缀或行结构,控件可能不再出现,需要按「对接契约」重新核对。
  • 官方模型目录头里的按钮(恢复默认模型 / 获取可用模型)全部保留,插件不隐藏、不拦截它们。
  • 必须点官方「保存」才会落盘;只改插件控件不点保存不会写入(行内有提示)。
  • 没有「跟随目录」:插件总是写显式值,所以对内置目录的继承模型,第一次保存会把 reasoningEfforts / input(或 inputModalities)写成显式值(不会再把目录能力当作默认)。