dsh-model-groups 0.3.8 → 0.3.10

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/CHANGELOG.md ADDED
@@ -0,0 +1,81 @@
1
+ # 更新记录
2
+
3
+ 完整版本列表与安装包:[GitHub Releases](https://github.com/betsunninda9-lgtm/dsh-model-groups/releases)。
4
+
5
+ ## 0.3.10 · 2026-11-14
6
+
7
+ - 新增密钥分组的识图、思考强度批量开关:开则全开,关则全关,两个能力独立。
8
+ - 保留原版模型行和详情控件,单个模型仍可使用原控件修改;新模型沿用最近一次批量设置,重复拉取保留已有修改。
9
+ - 所有修改沿用原表单保存按钮;思考开关仅声明可选强度,不强制高强度请求。
10
+ - 兼容旧测试版配置与补丁升级,保留精确还原配方。
11
+ - 更新后重启 DSH、刷新网页,按提示更新多密钥补丁,再重启并刷新。已更新 beta.2 补丁的用户无需重复应用相同补丁。
12
+ - [使用与检查说明](docs/capabilities-beta.md)。
13
+
14
+ ## 0.3.10-beta.2 · 测试版
15
+
16
+ - 恢复原版模型行和详情控件,删除跟随选项、能力状态行、例外计数和首次启用选择。
17
+ - 分组上方仅保留识图、思考强度两个批量开关:开则全开,关则全关。之后仍可使用原模型控件单独修改。
18
+ - 新模型采用分组最近一次批量设置;重复拉取保留已有模型的单独修改。再次操作批量开关会重新统一当前所有模型。
19
+ - beta.1 配置保存时转换为原生字段,保留当前实际能力;保留旧补丁的升级和精确还原配方。
20
+ - [安装与验收说明](docs/capabilities-beta.md)。未合入正式主分支,未发布 npm。
21
+
22
+ ## 0.3.10-beta.1 · 测试版
23
+
24
+ - 密钥分组可保存识图、思考强度的默认规则;首次启用时可保留已有模型设置,或让全部模型跟随分组。
25
+ - 新增或拉取的新模型自动跟随,已有模型的单独设置按模型 ID 保留。每个能力可分别跟随、单独开启或关闭;旧设置可保留原有配置和目录继承。
26
+ - 保留自定义思考档位参数;默认开启仅提供强度选择,不强制请求使用高强度。
27
+ - 所有操作仍修改编辑草稿,由原保存按钮提交;本测试版需要同时更新设置页和后端多密钥补丁。
28
+ - 测试安装与验收见 [测试说明](docs/capabilities-beta.md)。未合入正式主分支,未发布 npm 正式版。
29
+
30
+ ## 0.3.9 · 2026-11-14
31
+
32
+ ### 新增
33
+
34
+ - 网页设置页支持在同一供应商内调整密钥分组顺序、在组内调整模型顺序。手机长按三横线手柄约 400 毫秒后拖动,电脑也可以操作。
35
+ - 拖动时显示蓝色插入线和“松手后放在这里”,列表边缘支持自动滚动;手柄也支持上下方向键。
36
+ - 排序沿用原表单的保存按钮;未保存的输入、展开状态和密钥组身份会保留。保存后,聊天模型菜单使用相同顺序。
37
+ - 插件设置页分别显示前端和后端版本,方便确认更新是否已加载。
38
+
39
+ ### 修复
40
+
41
+ - 修复测试版中密钥分组排序保存后,退出设置又恢复原顺序的问题。现在保存明确的顺序值,确保运行中的配置、重新打开的设置页和模型选择列表一致。
42
+ - 保留旧版补丁的升级与还原配方,支持从 0.3.8 和本次测试版直接更新。
43
+
44
+ ### 更新步骤
45
+
46
+ 1. 在原 profile 更新插件,无需先卸载:
47
+
48
+ ```bash
49
+ dsh plugin --profile web add dsh-model-groups@0.3.9 --registry=https://registry.npmjs.org/
50
+ ```
51
+
52
+ 2. 重启原 DSH 引擎并刷新网页,确认插件前端、后端均为 `0.3.9`。
53
+ 3. 在 **设置 → 模型分组** 执行 **更新多密钥补丁**。本次同时更新设置页和后端补丁。
54
+ 4. 再次重启引擎并刷新网页。拖动排序后,点击原表单保存按钮。
55
+
56
+ 仍支持 DSH `0.2.2-rc.1`、Node.js 22 或更新版本。本次修改的是网页界面。
57
+
58
+ ### 验证
59
+
60
+ - beta.2 完整隔离环境回归:298 项,287 通过,0 失败,11 个历史或环境条件跳过。
61
+ - 三个已知后端构建验证真实配置更新、YAML 保存重读及模型菜单排序;18 个升级与还原场景通过。
62
+ - 用户已实机验收 beta.2 的排序修复;正式版沿用该实现。
63
+
64
+ ## 0.3.8 · 2026-11-12
65
+
66
+ - 修复 Anthropic 协议在结尾带 `/v1` 的中转地址上重复拼接 `/v1/v1/messages` 的问题;模型探测和实际请求使用相同规则。
67
+ - 安装后需更新多密钥补丁,重启 DSH 引擎并刷新网页。
68
+ - [完整发布说明](https://github.com/betsunninda9-lgtm/dsh-model-groups/releases/tag/v0.3.8)。
69
+
70
+ ## 0.3.7 · 2026-11-11
71
+
72
+ - 补充图文配置与安全关闭、卸载指南,简化设置页的重启和刷新提示。
73
+ - [完整发布说明](https://github.com/betsunninda9-lgtm/dsh-model-groups/releases/tag/v0.3.7)。
74
+
75
+ ## 0.3.6 · 2026-11-11
76
+
77
+ - 多密钥配置支持分别设置 API 协议,继续支持跟随供应商。
78
+ - 完善旧版本直接升级、预检诊断和前后端版本一致性保护。
79
+ - [完整发布说明](https://github.com/betsunninda9-lgtm/dsh-model-groups/releases/tag/v0.3.6)。
80
+
81
+ 更早的正式版与历史预览版请查看 [全部发布记录](https://github.com/betsunninda9-lgtm/dsh-model-groups/releases)。
package/README.md CHANGED
@@ -4,6 +4,24 @@
4
4
 
5
5
  支持 **DSH `0.2.2-rc.1`**、**Node.js 22 或更新版本**。本插件适用于 DSH 网页端,不替换原生 App 的模型菜单。
6
6
 
7
+ ## 本次更新 · 0.3.10
8
+
9
+ - **分组批量开关**:识图和思考强度分别控制,开启则当前组全开,关闭则全关。
10
+ - **保留原版模型界面**:单个模型仍在原详情中单独调整;之后新增的模型沿用最近一次批量设置,重新拉取保留已有模型的单独修改。
11
+ - **沿用原保存按钮**:两个能力独立修改,思考开启提供档位选择,不强制使用高强度。
12
+
13
+ 安装后重启 DSH、刷新网页,在 **设置 → 模型分组** 更新多密钥补丁,再重启并刷新。已使用 beta.2 且补丁就绪的用户无需重复应用相同补丁。[能力开关使用说明](docs/capabilities-beta.md)。
14
+
15
+ ## 上次正式更新 · 0.3.9
16
+
17
+ - **手机也能排序**:在设置页长按三横线手柄约 400 毫秒,即可调整密钥分组及组内模型顺序;蓝色插入线会提示落点,靠近边缘可自动滚动。
18
+ - **保存后顺序保留**:使用原表单保存按钮,退出再进入设置、打开聊天模型菜单都沿用已保存顺序。未保存的输入和展开状态会保留。
19
+ - **修复测试版排序回退**,并分别显示插件前端、后端版本,方便核对更新。
20
+
21
+ 升级到本版后,需要在 **设置 → 模型分组** 更新多密钥补丁,再重启 DSH 引擎并刷新网页。
22
+
23
+ 查看 [每次更新的内容](CHANGELOG.md) · [GitHub 发布记录与安装包](https://github.com/betsunninda9-lgtm/dsh-model-groups/releases)。
24
+
7
25
  ## 安装
8
26
 
9
27
  在运行 DSH 的环境里执行(如果你使用的 DSH 配置不是 `web`,把命令中的 `web` 换成你的配置名):
@@ -12,7 +30,7 @@
12
30
  dsh plugin --profile web add dsh-model-groups@latest --registry=https://registry.npmjs.org/
13
31
  ```
14
32
 
15
- 如果 npm 尚未同步到你需要的版本,可以把 `dsh-model-groups@latest` 换成 [GitHub 0.3.8 正式版安装包](https://github.com/betsunninda9-lgtm/dsh-model-groups/releases/download/v0.3.8/dsh-model-groups-0.3.8.tgz) 的下载地址。安装后,**重启正在使用的 DSH 引擎,再刷新网页**。只想给现有模型分组?装好就可以用,不需要启用多密钥。
33
+ 如果 npm 尚未同步到你需要的版本,可以把 `dsh-model-groups@latest` 换成 [GitHub 0.3.10 正式版安装包](https://github.com/betsunninda9-lgtm/dsh-model-groups/releases/download/v0.3.10/dsh-model-groups-0.3.10.tgz) 的下载地址。安装后,**重启正在使用的 DSH 引擎,再刷新网页**。只想给现有模型分组?装好就可以用,不需要启用多密钥。
16
34
 
17
35
  ## 在对话中选模型
18
36
 
@@ -0,0 +1,34 @@
1
+ # 0.3.10 模型能力批量开关
2
+
3
+ 适用于 DSH 0.2.2-rc.1 网页模型设置,Node.js 22 或更新版本。
4
+
5
+ ## 更新
6
+
7
+ 使用 `dsh plugin --profile web add dsh-model-groups@0.3.10 --registry=https://registry.npmjs.org/` 更新原 profile(如非 web,请替换配置名);安装后重启同一个 DSH 引擎并刷新网页。插件设置页的前端、后端版本均应为 `0.3.10`。执行“更新多密钥补丁”,随后再次重启同一个引擎并刷新网页。本次设置页和后端补丁必须一起更新。
8
+
9
+ ## 设置位置
10
+
11
+ 设置 → 模型 → 供应商 → 展开密钥分组。模型列表上方只保留两个批量开关:识图、思考强度设置。原来的模型行、模型详情和单独能力控件保持原样,不显示跟随选择、能力摘要、例外计数或首次启用选择。
12
+
13
+ - 开启某项:当前组所有模型一起开启该项。
14
+ - 关闭某项:当前组所有模型一起关闭该项。
15
+ - 随后可以进入模型详情,用原来的控件单独调整。
16
+ - 新增或拉取到的新模型沿用该组最近一次批量开关。重复拉取不会覆盖已有模型的单独修改。
17
+ - 再次操作批量开关时,会重新统一修改当前所有模型,包括此前单独修改过的模型。
18
+
19
+ 两个开关彼此独立;只操作识图不会改变思考设置。所有修改沿用原来的保存按钮,未保存退出不生效。beta.1 的规则会转换为原生模型字段,保存时清理旧的跟随元数据,升级后现有实际能力保持不变。
20
+
21
+ ## 思考强度的含义
22
+
23
+ 开启表示给模型声明可选强度,不强制请求使用“高”。已有有效的自定义档位和 API 参数值在本次开启时保留;没有自定义档位时使用常用的 off、low、medium、high。中转站必须实际支持这些能力。
24
+
25
+ ## 建议实机验收
26
+
27
+ 1. 先确认每个模型的展开详情与原版相同,没有新增的跟随选择或状态行。
28
+ 2. 在分组打开识图并保存,退出再进入,所有模型开启识图;关闭并保存,所有模型关闭。
29
+ 3. 批量打开后,用原模型控件单独关闭一个模型,保存、刷新、重新拉取列表;这个单独修改应保留。
30
+ 4. 新增一个模型,核对它采用最近一次分组开关;原模型的单独修改保持不变。
31
+ 5. 再次关闭、开启分组开关,当前所有模型应再次统一;只操作识图时思考档位不变化。
32
+ 6. 保存后在聊天中选取对应模型,核对图片入口和思考选择。修改后取消编辑、原有排序及普通供应商不受影响。
33
+
34
+ 自动测试使用公开程序副本和合成配置;真实中转站、手机触摸与视觉布局仍需实机确认。
@@ -2,6 +2,18 @@
2
2
 
3
3
  日常安装和使用见 [README](../README.md)。
4
4
 
5
+ ## GitHub 自动检查
6
+
7
+ 向 `main` 提交代码或创建以 `main` 为目标的修改申请后,GitHub 的 **Actions → Automatic checks** 会自动检查:
8
+
9
+ - 已提交的网页产物是否与源码一致(先检查,再打包,避免打包自动修复后漏报)。
10
+ - 仓库现有的自动测试是否通过;完整获取 Git 历史,以运行历史补丁清单验证。
11
+ - 能否正常预览生成安装包,以及打包后网页产物是否仍与提交一致。
12
+
13
+ 使用 Node.js 22 和只读仓库权限,不发布 npm、不安装 DSH、不应用补丁。当前项目没有依赖和锁文件,因此无需安装依赖,也不使用 `npm ci`。以后新增依赖时,须提交锁文件并同步增加依赖安装步骤。
14
+
15
+ 检查详情中的红色步骤表示失败,可展开查看原因;绿色表示本次配置的检查通过。没有真实 DSH 程序文件的集成测试会明确跳过,不能视为已经通过。宿主补丁的真实升级、还原和手机窄屏触摸仍按下文单独验证。首次检查流程合并后,也可在 Actions 页点击 **Run workflow** 手动运行。
16
+
5
17
  ## 本地验证
6
18
 
7
19
  需要 Node.js 22 或更新版本。修改网页端源码后,重新生成并提交客户端产物:
@@ -28,6 +40,20 @@ node scripts/settings-models-collapse.mjs --source /path/to/pristine-client.js
28
40
  node scripts/settings-models-collapse.mjs --source /path/to/pristine-client.js --check
29
41
  ```
30
42
 
43
+ ## 触摸排序的验证
44
+
45
+ 设置页密钥分组和模型行共用 `scripts/settings-reorder.mjs` 的指针手势,补丁生成器将其嵌入宿主设置页。长按约 400 毫秒后,以蓝色插入线和「松手后放在这里」提示落点;松手只更新草稿,仍由原表单提交。分组移动时写入每组的 `sortOrder` 数值,模型沿用数组顺序。不能只依赖对象键顺序:宿主配置加载器的相等比较忽略对象键的排列,单纯重排键会被视为未变化。设置和聊天菜单按同一份已保存配置排列,不保存另一份本地排序。
46
+
47
+ 自动测试覆盖长按与防误触、松手坐标、同列表边界、边缘滚动、取消及卸载清理、编辑行身份与输入草稿、只排序的改动提示、失败重试以及选择菜单顺序。发布前仍需在真实手机浏览器核对:短按手柄不移动,手柄以外可滚动;长按拖动折叠/展开分组和模型;长列表边缘滚动;取消后原顺序不变;点击现有保存按钮后,聊天菜单及刷新后的设置顺序一致。模拟事件测试不代表实机触摸已验证。
48
+
49
+ 旧 0.3.8 与 0.3.9-beta.1 的设置补丁,以及 0.3.9-beta.1 的后端补丁,保存在测试夹具中并作为可升级、可还原的历史变体保留。完整程序生成还会验证正向和反向变换均逐字匹配,防止替换片段互相干扰。
50
+
51
+ ## 分组能力批量开关的验证
52
+
53
+ `src/capability-defaults.js` 的纯函数嵌入设置页。分组可选 `capabilityBatch` 分别记住最近一次识图、思考批量操作,只有已操作的字段才出现。批量操作直接改写当前模型的原生 `input`、`reasoningEfforts`;新增模型应用记住的字段,已有模型单独编辑、改名、重排和重新拉取均不会再次被强制覆盖。后端校验此字段但不拿它覆盖模型能力。原模型行和详情与 0.3.9 保持逐字一致。
54
+
55
+ 旧 beta.1 的 `capabilityDefaults` / `capabilityOverrides` 仍可由后端识别,设置页保存时将有效能力物化为原生字段并删除旧规则。首次打开不自动写配置;保存前后脏状态按物化结果比较。需验证两个批量字段独立、新模型/空 ID/改名、旧规则迁移后单独修改、原生思考关闭不被目录能力重新打开、实际 schema/目录/请求派发、YAML 重读和旧版补丁的精确升级还原。能力与模型列表都由原配置保存接口提交,不另存浏览器规则。实机验收见 `docs/capabilities-beta.md`。
56
+
31
57
  ## 诊断与恢复
32
58
 
33
59
  只读诊断(请替换实际安装路径):
package/lib/client.js CHANGED
@@ -87,6 +87,17 @@ function decodeKeyModelId(model) {
87
87
  } catch { return undefined; }
88
88
  }
89
89
 
90
+ /** Explicit ranks survive host equality checks, schema parsing, and reloads.
91
+ * Unranked legacy/new entries retain their existing relative order at the end.
92
+ */
93
+ function orderedKeyEntries(entries) {
94
+ const rank = entry => Number.isSafeInteger(entry?.sortOrder) && entry.sortOrder >= 0 ? entry.sortOrder : Infinity;
95
+ return Object.entries(entries ?? {}).sort(([, left], [, right]) => {
96
+ const a = rank(left), b = rank(right);
97
+ return a === b ? 0 : a < b ? -1 : 1;
98
+ });
99
+ }
100
+
90
101
  function keyEntryGroupKey(provider, entryId) {
91
102
  return `@key/${encodeURIComponent(provider)}/${encodeURIComponent(entryId)}`;
92
103
  }
@@ -119,7 +130,7 @@ function buildModelHierarchy(groups, providers, preferences, current = null) {
119
130
  }
120
131
  const byEntry = new Map();
121
132
  const configured = profile?.keyEntries && typeof profile.keyEntries === 'object' ? profile.keyEntries : {};
122
- for (const entryId of Object.keys(configured)) {
133
+ for (const [entryId] of orderedKeyEntries(configured)) {
123
134
  if (KEY_ENTRY_ID.test(entryId) && !forbiddenEntry(entryId)) byEntry.set(entryId, []);
124
135
  }
125
136
  for (const model of aliases) {
@@ -133,6 +144,17 @@ function buildModelHierarchy(groups, providers, preferences, current = null) {
133
144
  const children = [...byEntry].filter(([, models]) => models.length > 0).map(([entryId, models]) => {
134
145
  const key = keyEntryGroupKey(group.id, entryId);
135
146
  const active = root.current && selectedEntry === entryId;
147
+ // Catalog refreshes may use a different order from the saved editor rows.
148
+ // Empty lists inherit catalog order; unconfigured models stay stable last.
149
+ const configuredModels = Object.hasOwn(configured, entryId) ? configured[entryId]?.models : undefined;
150
+ if (Array.isArray(configuredModels) && configuredModels.length > 0) {
151
+ const order = new Map();
152
+ for (const model of configuredModels) {
153
+ if (typeof model?.id === 'string' && !order.has(model.id)) order.set(model.id, order.size);
154
+ }
155
+ const rank = model => order.get(decodeKeyModelId(model.id).modelId) ?? order.size;
156
+ models.sort((left, right) => rank(left) - rank(right));
157
+ }
136
158
  return { ...group, key, entryId, name: keyEntryName(providers, group.id, entryId), models, current: active,
137
159
  expanded: Object.hasOwn(preferences, key) ? preferences[key] : active };
138
160
  });
@@ -1270,7 +1292,7 @@ function installSettingsPageStyles() {
1270
1292
  document.head.appendChild(style);
1271
1293
  }
1272
1294
 
1273
- const MODEL_GROUPS_CLIENT_VERSION = "0.3.8";
1295
+ const MODEL_GROUPS_CLIENT_VERSION = "0.3.10";
1274
1296
  // Public program metadata only. Never include paths, source text, configuration,
1275
1297
  // credentials, environment variables, or arbitrary exception messages.
1276
1298
  const PATCH_IDS = ['pi-ai.patch.json', 'settings-models.patch.json', 'llm-typert.host.patch.json', 'llm-typert.remote-client.patch.json', 'api-remotes.patch.json'];
@@ -1510,7 +1532,7 @@ function ModelGroupsSettingsPage() {
1510
1532
  return h('section', { className: 'mg-maintenance', 'aria-label': '模型分组设置', 'aria-busy': state.busy },
1511
1533
  h('h2', null, '多密钥管理'),
1512
1534
  h('p', null, '在这里启用、更新或关闭多密钥。如果只想按供应商浏览模型,无需启用多密钥。'),
1513
- snapshot?.pluginVersion && h('p', { className: 'mg-maintenance-help' }, `插件 ${snapshot.pluginVersion} · DSH ${snapshot.compatibility?.hostVersion ?? '版本未确认'}`),
1535
+ h('p', { className: 'mg-maintenance-help' }, `网页插件 ${MODEL_GROUPS_CLIENT_VERSION} · 后端插件 ${snapshot?.pluginVersion ?? '检查中'} · DSH ${snapshot?.compatibility?.hostVersion ?? '版本未确认'}`),
1514
1536
  h('section', { className: 'mg-maintenance-card', 'aria-label': '当前状态' },
1515
1537
  h('div', { className: 'mg-maintenance-row' }, h('h3', null, '当前状态'), h('button', { type: 'button', disabled: state.busy, onClick: refresh }, state.busy ? '处理中…' : '刷新检查')),
1516
1538
  h('p', { role: 'status', 'aria-live': 'polite', className: 'mg-maintenance-help' }, state.busy ? '正在检查或执行维护,请勿关闭或重复操作。' : snapshot ? statusLabel : '尚无可验证状态,请刷新检查。'),