dsh-multi-router 0.6.2 → 0.6.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/CHANGELOG.md CHANGED
@@ -2,6 +2,17 @@
2
2
 
3
3
  本项目遵循 [语义化版本](https://semver.org/lang/zh-CN/)。版本号从 `0.1` 起以功能迭代为主,接口在 `1.0` 前可能变化。
4
4
 
5
+ ## 0.6.4
6
+
7
+ - 路由编辑的上下文容量与默认思考深度合并至名称下方的「模型参数」区域;宽窗口并排、窄窗口上下排列,容量变化后仍自动核对可用思考档位。
8
+ - 更新路由设置的合成数据截图与说明。
9
+
10
+ ## 0.6.3
11
+
12
+ - 普通预配置及自定义路由的编辑弹窗新增「默认思考深度」,支持平台默认及渠道声明的关闭 / 深度档位;按当前目标、指定账号和上下文容量核对选项,仅读取本机模型缓存,不发送供应商探测请求。
13
+ - 保存校验所选档位;会话手动选择及 API `reasoning_effort` 优先于路由默认,仅向支持所选档位的账号切换。暂缺目录时保留已有默认值,不静默降档。
14
+ - 路由默认思考深度随配置导入导出迁移,覆盖保存、重载、跨设备迁移及真实 DSH 请求的回归验证。
15
+
5
16
  ## 0.6.2
6
17
 
7
18
  - 修复旧用户升级后缺少新预制路由:启用或热加载时自动补齐 Qwen3.8-Max、Qwen3.8-Flash 与 Hy4 Preview,更新后直接出现在路由列表与模型选择器,无需手动补齐或重启。
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  一个插件内置 WorkBuddy、Trae、Qoder 的认证、模型发现和聊天传输,提供三家的多账号池及集中路由。运行不依赖原来三个 DSH 插件;三家代码随安装包一起打包。基于 MIT 上游,固定版本见 `upstream-lock.json`,许可见 `THIRD_PARTY_NOTICES.md`。
8
8
 
9
- 版本:0.6.2。适配 DSH Desktop / Web 0.2.0-rc.2,Node.js ^22.19.0 或 >=24。
9
+ 版本:0.6.4。适配 DSH Desktop / Web 0.2.0-rc.2,Node.js ^22.19.0 或 >=24。
10
10
 
11
11
  ## 在 DSH 里管理
12
12
 
@@ -347,6 +347,10 @@ Free 不使用临期积分或 Token / 积分额度策略,避免将「消耗赠
347
347
 
348
348
  ## 统一路由推理模式
349
349
 
350
+ 「路由规则 → 编辑路由 → 模型参数」集中设置上下文容量与默认思考深度,宽窗口并排显示、窄窗口自动上下排列。可为普通预配置与自定义路由设置平台默认、关闭思考或具体档位。选项根据当前目标、指定账号及上下文容量,从本机目录实时核对;保存立即生效,DSH 会话的手动选择及 API 的 `reasoning_effort` 优先。没有声明可调档位时只使用平台默认,已有档位暂不可用时保留设置并显示提示,不自动降档。默认值随配置导入导出保留。
351
+
352
+ ![普通路由默认思考深度设置(合成数据,无真实账号)](docs/screenshots/route-reasoning.jpg)
353
+
350
354
  所有预配置与自定义路由保留各目标明确声明的全部档位,包括支持的 Off、Minimal、Low、Medium、High、XHigh、Max。不同渠道存在差异时使用并集,并按会话所选档位筛选实际账号 / 模型候选,不静默降级,也不因某个目标未声明档位而删掉其他目标的能力。WorkBuddy 国内 onlyReasoning 的模型仍遵守上游禁止 Off 的约束。平台默认与显式 Off 通过每次请求独立的本机 shim 头区分,头不发送到三家上游;实际请求仍使用各渠道本来的字段映射。
351
355
 
352
356
  ## 开发与验证
package/cordis.patch.yml CHANGED
@@ -1,3 +1,3 @@
1
1
  - insert:
2
- - id: llm-multi-router-v2-0.6.2
3
- name: ./lib/host-v0.6.2.js
2
+ - id: llm-multi-router-v2-0.6.4
3
+ name: ./lib/host-v0.6.4.js
package/lib/accounts.js CHANGED
@@ -4,11 +4,11 @@ import {
4
4
  localAccountKey,
5
5
  normalizeLocalAccountKey,
6
6
  readPortableCredential
7
- } from "./chunk-KGVTRS5K.js";
7
+ } from "./chunk-BVH4K6WH.js";
8
8
  import "./chunk-OK2T2UAZ.js";
9
9
  import "./chunk-TEZ2FMEO.js";
10
10
  import "./chunk-S5KMFAQM.js";
11
- import "./chunk-5R3IOPSF.js";
11
+ import "./chunk-PNFQDYBK.js";
12
12
  export {
13
13
  discoverLocalAccounts,
14
14
  importAccount,
@@ -1,10 +1,10 @@
1
1
  import {
2
2
  ApiService
3
- } from "./chunk-Q34JSMGN.js";
3
+ } from "./chunk-GQEKAQXE.js";
4
4
  import "./chunk-4V52V5GO.js";
5
5
  import "./chunk-DNQPZMPF.js";
6
- import "./chunk-5KYJADJ4.js";
7
- import "./chunk-5R3IOPSF.js";
6
+ import "./chunk-JJE6NRHN.js";
7
+ import "./chunk-PNFQDYBK.js";
8
8
  export {
9
9
  ApiService
10
10
  };
@@ -6,20 +6,20 @@ import {
6
6
  refreshWorkBuddyCheckinCredential,
7
7
  supportsCheckin,
8
8
  workbuddyToken
9
- } from "./chunk-74WQLVJO.js";
9
+ } from "./chunk-R3UQQMSV.js";
10
10
  import {
11
11
  discoverLocalAccounts,
12
12
  importAccount,
13
13
  localAccountKey,
14
14
  normalizeLocalAccountKey,
15
15
  readPortableCredential
16
- } from "./chunk-KGVTRS5K.js";
16
+ } from "./chunk-BVH4K6WH.js";
17
17
  import {
18
18
  modelWithCurrentPromotion
19
19
  } from "./chunk-TEZ2FMEO.js";
20
20
  import {
21
21
  ApiService
22
- } from "./chunk-Q34JSMGN.js";
22
+ } from "./chunk-GQEKAQXE.js";
23
23
  import {
24
24
  hostIsLoopback
25
25
  } from "./chunk-DNQPZMPF.js";
@@ -28,7 +28,7 @@ import {
28
28
  contextInfo,
29
29
  matchModel,
30
30
  resolveContext
31
- } from "./chunk-5KYJADJ4.js";
31
+ } from "./chunk-JJE6NRHN.js";
32
32
  import {
33
33
  PROVIDERS,
34
34
  PROVIDER_NAMES,
@@ -38,13 +38,13 @@ import {
38
38
  routeReasoning,
39
39
  validateConfig,
40
40
  writePrivateJson
41
- } from "./chunk-5R3IOPSF.js";
41
+ } from "./chunk-PNFQDYBK.js";
42
42
 
43
43
  // src/manager.mjs
44
44
  import { createHash, randomBytes as randomBytes3, timingSafeEqual } from "node:crypto";
45
45
 
46
46
  // package.json
47
- var version = "0.6.2";
47
+ var version = "0.6.4";
48
48
 
49
49
  // src/manager.mjs
50
50
  import { readFile as readFile4, mkdir as mkdir2, mkdtemp as mkdtemp2, rm as rm3 } from "node:fs/promises";
@@ -113,7 +113,7 @@ function settings(config) {
113
113
  models: Object.fromEntries(Object.entries(config.models ?? {}).map(([p, rows]) => [p, Object.fromEntries(Object.entries(rows).map(([id, pref]) => [id, pick(pref, ["name", "visible", "contextWindow", "supportsImages", "reasoningEffort"])]))])),
114
114
  accounts: config.accounts.map((a) => pick(a, ["id", "provider", "name", "enabled", "autoCheckin"])),
115
115
  routes: Object.fromEntries(Object.entries(config.routes).map(([id, r]) => [id, {
116
- ...pick(r, ["name", "visible", "strategy", "contextMode", "contextWindow", "maxTokens", "preferExpiringCredits", "expiringWithinDays", "freeOnly", "freeModels"]),
116
+ ...pick(r, ["name", "visible", "strategy", "contextMode", "contextWindow", "maxTokens", "reasoningEffort", "preferExpiringCredits", "expiringWithinDays", "freeOnly", "freeModels"]),
117
117
  targets: r.targets.map((t) => pick(t, ["provider", "account", "model", "models", "weight", "tokenBudget", "creditBudget"]))
118
118
  }]))
119
119
  };
@@ -1376,8 +1376,8 @@ var Manager = class {
1376
1376
  modelVisible(provider, id) {
1377
1377
  return this.config.models[provider]?.[id]?.visible ?? this.config.modelVisibility[provider] ?? false;
1378
1378
  }
1379
- routeModels() {
1380
- return Object.entries(this.config.routes).sort(([, a], [, b]) => Number(!!b.freeOnly) - Number(!!a.freeOnly)).map(([id, row]) => {
1379
+ routeModels(routes = this.config.routes) {
1380
+ return Object.entries(routes).sort(([, a], [, b]) => Number(!!b.freeOnly) - Number(!!a.freeOnly)).map(([id, row]) => {
1381
1381
  const effectiveTargets = row.freeOnly ? this.freeRoutes(id, row).flatMap((r) => r.targets) : row.targets;
1382
1382
  const sources = effectiveTargets.flatMap((target) => this.config.accounts.filter((a) => a.enabled !== false && a.provider === target.provider && (!target.account || target.account === a.id)).flatMap((a) => {
1383
1383
  const catalog = this.catalogs.get(a.id)?.models ?? [];
@@ -1489,6 +1489,17 @@ var Manager = class {
1489
1489
  }
1490
1490
  async action(input) {
1491
1491
  if (!input || typeof input !== "object" || Array.isArray(input)) bad("\u8BF7\u6C42\u683C\u5F0F\u65E0\u6548");
1492
+ if (input.action === "route.preview") {
1493
+ if (input.route?.freeOnly) bad("\u514D\u8D39\u6A21\u578B\u8BF7\u9010\u9879\u8BBE\u7F6E\u63A8\u7406\u7B49\u7EA7");
1494
+ let config;
1495
+ try {
1496
+ config = validateConfig({ ...this.raw, routes: { [input.id || "preview"]: input.route } }, this.file);
1497
+ } catch {
1498
+ bad("\u8BF7\u5148\u9009\u62E9\u6709\u6548\u7684\u76EE\u6807\u6A21\u578B\u4E0E\u4E0A\u4E0B\u6587\u5BB9\u91CF");
1499
+ }
1500
+ const { reasoning, contextKnown } = this.routeModels(config.routes)[0];
1501
+ return { routePreview: { reasoning, contextKnown } };
1502
+ }
1492
1503
  if (input.action === "model.test") {
1493
1504
  const model = this.providerModels(input.provider).find((m) => m.id === input.id);
1494
1505
  if (!model) bad("\u8BF7\u5148\u5237\u65B0\u5E76\u9009\u62E9\u5DF2\u53D1\u73B0\u7684\u6A21\u578B");
@@ -1911,6 +1922,16 @@ var Manager = class {
1911
1922
  } catch (error) {
1912
1923
  bad(error.message);
1913
1924
  }
1925
+ if (input.action === "route.save" && input.route.reasoningEffort != null) {
1926
+ const previous = this.config.routes[input.previousId], route = config.routes[input.id];
1927
+ const bindings = (r) => JSON.stringify(r.targets.map((t) => ({
1928
+ provider: t.provider,
1929
+ account: t.account,
1930
+ models: t.models ?? (t.model ? [t.model] : [])
1931
+ })));
1932
+ const unchanged = previous?.reasoningEffort === route.reasoningEffort && bindings(previous) === bindings(route) && previous.contextMode === route.contextMode && (route.contextMode === "maximum" || previous.contextWindow === route.contextWindow);
1933
+ if (!unchanged && !this.routeModels({ [input.id]: route })[0].reasoning?.supportedEfforts.includes(route.reasoningEffort)) bad("\u76EE\u6807\u6A21\u578B\u5728\u5F53\u524D\u4E0A\u4E0B\u6587\u5BB9\u91CF\u4E0B\u4E0D\u652F\u6301\u6240\u9009\u601D\u8003\u6DF1\u5EA6\uFF0C\u8BF7\u91CD\u65B0\u9009\u62E9");
1934
+ }
1914
1935
  await writePrivateJson(this.file, raw);
1915
1936
  staging = void 0;
1916
1937
  this.raw = raw;
@@ -2027,4 +2048,4 @@ export {
2027
2048
  Manager,
2028
2049
  managementHandler
2029
2050
  };
2030
- //# sourceMappingURL=chunk-4EO5V6FW.js.map
2051
+ //# sourceMappingURL=chunk-3U6KCQEP.js.map