@dsh-plugins/dsh-llm-hub 0.7.3 → 1.3.0

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,98 @@
2
2
 
3
3
  本项目遵循 [Semantic Versioning](https://semver.org/lang/zh-CN/)。
4
4
 
5
+ ## [1.3.0] - 2026-09-17
6
+
7
+ ### 新增
8
+
9
+ - **多账号 key 轮换**(`settings.dsh-llm-hub.keyPool`):
10
+ 同一 provider 配多把 key,每次解析连接时按 round-robin 选下一个。
11
+ 配置形如:
12
+ ```yaml
13
+ dsh-llm-hub:
14
+ keyPool:
15
+ deepseek-official:
16
+ - name: primary
17
+ env: DEEPSEEK_API_KEY_1
18
+ - name: backup
19
+ env: DEEPSEEK_API_KEY_2
20
+ ```
21
+ 客户端在「轮换」卡(settings → 模型 → 页脚 order 73)显示每个 provider
22
+ 当前轮到的 key(仅 name + env 名,**不**暴露 apiKey 实际值)。
23
+ - 向后兼容:未配 keyPool 时走旧的 apiKeyEnv 单 key 路径,行为完全不变
24
+ - 显式覆盖(`request.apiKey`)绕过轮换,不污染索引
25
+ - 失败 key **不**自动跳过 —— 用户从 debug 卡能看到「上次失败的是 X」,
26
+ 下次请求自然轮换过去;自动跳过失败 key 等收集真实失败场景再加
27
+ - 新增 debug 路由 `GET /api/dsh-llm-hub/keypool/status`,让 client 知道当前
28
+ 状态
29
+
30
+ ## [1.2.0] - 2026-09-17
31
+
32
+ ### 新增
33
+
34
+ - **模型别名**(`settings.dsh-llm-hub.aliases`):把长 model id 翻成短显示名,
35
+ 典型场景是 `deepseek-reasoner` → `推理`、`claude-3.5-sonnet` → `Sonnet`。
36
+ 客户端在「拉取目录」picker 里渲染成「短名 (model.id)」(完整 id 永远在 title
37
+ 里 —— 复制 id 粘配置时不能是「推理」)。
38
+ - key 形态:「provider/model」;两端都必须匹配 provider/model 的合法字符集
39
+ (provider 严格,model 允许 `.` `+`,适配 DSH 真实 id)
40
+ - 空值 / 纯空白 / 非字符串值全部过滤,避免「短名 = 」这种占位塞进 UI
41
+ - 启动时拉一次,所有 picker 行共用一份别名表;卡 mount 后改 settings 不立刻
42
+ 反映(picker 在 catalog 拉完后渲染一次就 freeze)—— 重拉一次目录即可
43
+ - host 端只读 `GET /api/dsh-llm-hub/aliases`,纯读无需写
44
+
45
+ ## [1.1.0] - 2026-09-17
46
+
47
+ ### 新增
48
+
49
+ - **智能路由建议**(settings → 模型 → 页脚 order 75):用户声明
50
+ `routing.primary` + `routing.fallbacks[]`,host 半按当前可用性判定给
51
+ 「该用哪个 + 原因」。
52
+
53
+ ### 变更
54
+
55
+ - **P1-5(原方案)从「自动切换」改成「建议」** —— DSH 的
56
+ `conversation.input.model` 是 single + user-controlled slot(`replaceRisk:
57
+ shadows-shipped-ui`,README 已钉死),宿主没暴露 setter;强行覆盖会撞红线。
58
+ 新方案是「提示 + 引导」,由用户在 UI 里手动切 —— 这才是 DSH 设计边界内的姿势。
59
+
60
+ ## [1.0.0] - 2026-09-17
61
+
62
+ 从「DSH 模型页补丁」升级到「模型管理层」:插件有了独立的存储与可见的
63
+ 健康维度,外部只看到余额和下拉的旧印象不再成立。
64
+
65
+ ### 新增
66
+
67
+ - **本月用量卡**(settings → 模型 → 页脚 order 80):
68
+ host 半从 `llm/stream` 的 `usage` chunk 捕获每次模型调用的 in/out/cache tokens,
69
+ 写进 storageDomain 的 per-record table;client 半按本地日历月聚合,显示
70
+ 「N 次调用 / 输入 X tokens / 输出 Y / 缓存 Z / 合计」+ 前 5 个最常用模型。
71
+ 按钮:「导出 CSV」(复制到剪贴板,用户自己对照官方价目表定价)、
72
+ 「清空记录」(POST + confirm 两段式,避免误删)。
73
+ 不算钱:开源 DSH 没收 chat 文本定价接口,每个 provider 写硬编码价目表
74
+ 既过时又快塌。
75
+
76
+ - **余额预警**(DeepSeek cash + pi-ai plan/quota):
77
+ 阈值在 `settings.dsh-llm-hub.warning`(`cashCNY` 默认 10 元、`planPercent`
78
+ 默认 10%)。新加 `/api/dsh-llm-hub/warning/check` 路由,客户端把当前余额信封
79
+ POST 进去,host 半返回 `{ level: 'low'|null, text }`。阈值数字不进响应
80
+ (服务端策略,不让前端能关掉)。
81
+ 余额数字低于阈值时变红 + ⚠️ chip(鼠标悬停看具体提示)。
82
+
83
+ - **Provider 健康看板**(settings → 模型 → 页脚 order 70):
84
+ 新加 `/api/dsh-llm-hub/health` 路由,返回 availability 全字段 + latencyMs
85
+ / 上游 HTTP 状态码。每行 provider 名称 + 状态 chip(available/unavailable/
86
+ unknown 三态配色)+ 延迟 + HTTP + 原因(截断 + title 出全文)+ 探测时间。
87
+ 「重新探测」按钮复用 `availability.recheck`,与下拉重探同一条底层刷新。
88
+
89
+ ### 内部
90
+
91
+ - `probeDeepseek` / `probePiai` 给每次返回加 `latencyMs` / `status`(之前
92
+ latency 没存过,HTTP 状态码在 listing 里就丢)
93
+ - storageDomain 用**局部注入**,缺席时本插件其余功能(余额 / 可用性 /
94
+ harness)照常工作 —— 没有用量统计只是少一张卡,不会让整个 boot 失败
95
+ - `apply` 函数 `inject` 仍是 `['llm', 'webServer']`;新增功能全部走局部注入
96
+
5
97
  ## [0.7.3] - 2026-09-17
6
98
 
7
99
  ### 新增
@@ -25,6 +117,29 @@
25
117
  那个,顺便补了「footer 是 list slot,两个条目都必须带 id 与 order」的断言
26
118
  (漏 id 会被宿主静默丢弃:接口通、组件在、页面上什么都没有)。
27
119
 
120
+ ## [0.7.2] - 2026-09-17
121
+
122
+ ### 文档
123
+
124
+ - README 首屏补一张实拍截图,去掉「用法」里的重复段落(`a67cb7e`)。
125
+
126
+ ## [0.7.1] - 2026-09-17
127
+
128
+ > **本节为占位说明。** 0.7.1 内容等同 0.7.0,无独立 commit / tag,详见下方 0.7.0 节。
129
+
130
+ ### 发布修正
131
+
132
+ - 与 0.7.0 内容等同的一次重发。0.7.0 是在包名迁移(`@webkubor/` → `@dsh-plugins/`)
133
+ 过程中、工作区处于混合状态时发出的,0.7.1 把注册表上的版本对齐到迁移后的状态。
134
+
135
+ 本条按 git 证据如实记录:0.7.1 **没有对应的 commit**(版本从 `8ae4f7b` 的 0.7.0
136
+ 直接跳到 `a67cb7e` 的 0.7.2),也没有 tag,所以这里不罗列具体改动 —— 编一份看起来
137
+ 完整的清单,比留一句「查不到」更坏。
138
+
139
+ 补这两节的原因是发布工作流的 Release 步骤要从 CHANGELOG 取对应小节,取不到就
140
+ `process.exit(1)`:缺小节意味着 `gh workflow run publish.yml -f tag=v0.7.1`
141
+ 这类补发会直接失败。
142
+
28
143
  ## [0.7.0] - 2026-09-17
29
144
 
30
145
  ### 新增
package/README.md CHANGED
@@ -3,9 +3,17 @@
3
3
  </p>
4
4
 
5
5
  <p align="center">
6
- <a href="https://www.npmjs.com/package/@dsh-plugins/dsh-llm-hub"><img src="https://img.shields.io/npm/v/@dsh-plugins/dsh-llm-hub?style=flat-square&color=4C7EF3&label=npm" alt="npm" /></a>
7
- <img src="https://img.shields.io/badge/deps-0-5A9E6F?style=flat-square" alt="zero deps" />
6
+ <a href="https://www.npmjs.com/package/@dsh-plugins/dsh-llm-hub"><img src="https://img.shields.io/npm/v/%40dsh-plugins%2Fdsh-llm-hub?style=flat-square&color=4C7EF3&logo=npm&label=npm" alt="npm" /></a>
7
+ <a href="https://www.npmjs.com/package/@dsh-plugins/dsh-llm-hub"><img src="https://img.shields.io/npm/dm/%40dsh-plugins%2Fdsh-llm-hub?style=flat-square&color=6d7f9c&label=downloads" alt="downloads" /></a>
8
+ <img src="https://img.shields.io/badge/DSH-%E2%89%A50.1.1--rc.2-4d6bfe?style=flat-square" alt="DSH" />
9
+ <img src="https://img.shields.io/badge/runtime_deps-0-5A9E6F?style=flat-square" alt="zero deps" />
8
10
  <img src="https://img.shields.io/badge/license-MIT-777?style=flat-square" alt="MIT" />
11
+ </p>
12
+
13
+ <p align="center">
14
+ <a href="https://github.com/deepseek-ai/deepseek-harness"><img src="https://img.shields.io/badge/DeepSeek_Harness-Plugin-4d6bfe?style=flat-square" alt="DSH Plugin" /></a>
15
+ <a href="https://github.com/topics/dsh-plugin"><img src="https://img.shields.io/badge/topic-dsh--plugin-4d6bfe?style=flat-square" alt="dsh-plugin" /></a>
16
+ <a href="https://github.com/topics/llm-gateway"><img src="https://img.shields.io/badge/topic-llm--gateway-4C7EF3?style=flat-square" alt="llm-gateway" /></a>
9
17
  &nbsp;·&nbsp; <a href="README.en.md">English</a> · <a href="CHANGELOG.md">更新日志</a>
10
18
  </p>
11
19
 
@@ -14,10 +22,13 @@ DSH 的模型页上,官方适配器有一半事情没做。这个插件把它
14
22
  | | 官方适配器 | dsh-llm-hub |
15
23
  |---|---|---|
16
24
  | DeepSeek 有哪些模型 | 看不到 | **一键拉取在售列表** |
17
- | 账户还剩多少钱 | 看不到 | **卡片下常驻余额** |
18
- | 网关通不通、多快 | 按钮点了没反应 | **实测延迟与状态** |
25
+ | 账户还剩多少钱 | 看不到 | **卡片下常驻余额 + 阈值预警** |
26
+ | 网关通不通、多快 | 按钮点了没反应 | **实测延迟与状态 + 健康看板** |
19
27
  | 网关上有多少模型 | 看不到 | **实测 71 个**(手填只有 11) |
20
28
  | 为什么探测不了 | 无提示 | **写明「没配 baseURL」** |
29
+ | 本月花了多少 token | 没有 | **用量统计 + 前 5 个最常用模型 + CSV 导出** |
30
+ | 不可用的 provider | 仍在下拉里 | **下拉里静默摘掉,卡片上标红 + 原因** |
31
+ | 外部 agent CLI(codex/claude/agy) | 装不装无感 | **装了就出现子代理工具** |
21
32
 
22
33
  <p align="center">
23
34
  <img src="https://img.webkubor.online/oss/dsh-llm-hub/v060/models-piai-cards.png" alt="装上之后的模型页:每张 provider 卡片下方多出一行 —— 协议、接入地址、已配模型数、余额,右侧是探测与拉取目录" width="100%" />
@@ -157,6 +168,176 @@ key 过期、余额耗尽、网关 401,都会照常出现在那里,点了才
157
168
  「隐藏即永久」。同理,cordis 服务的方法**不能**用 `!==` 校验是否替换成功
158
169
  (traceable 代理每次访问都是新对象),要看属性描述符。
159
170
 
171
+ ## 本月用量(1.0.0 起)
172
+
173
+ `llm/stream` 的 `usage` chunk 在 finish 之前到达;本插件观察流时把每次
174
+ in / out / cacheRead / cacheWrite tokens 累加,finish 时一次性写入
175
+ storageDomain 的 per-record table。**不算钱** —— 开源 DSH 的 llm 服务只暴露
176
+ `imageRequestPricing`,没有 chat 文本定价接口;给每个 provider 写硬编码价目表
177
+ 既过时又快塌。把 token 用量留给用户自己对照官方价目表定价。
178
+
179
+ 设置 → 模型 → 页脚 order 80 处渲染成一张「本月用量」卡:
180
+ 输入 / 输出 / 缓存 / 合计 tokens + 调用次数 + 前 5 个最常用模型。
181
+
182
+ - **导出 CSV**:按钮把全部记录写到剪贴板(id / provider / model / at / 各 token /
183
+ finished),文件命名 `dsh-llm-hub-usage-YYYY-MM-DD.csv`。
184
+ - **清空记录**:POST + `window.confirm` 两段式,避免被预取或前进后退误删。
185
+
186
+ storageDomain 用**局部注入** —— 缺席时本插件其余功能(余额 / 可用性 /
187
+ harness)照常工作,没有用量统计只是少一张卡,不会让整个 boot 失败。
188
+
189
+ ## 余额预警(1.0.0 起)
190
+
191
+ 阈值在 `settings.dsh-llm-hub.warning`:
192
+
193
+ ```yaml
194
+ dsh-llm-hub:
195
+ warning:
196
+ cashCNY: 10 # DeepSeek / moonshot / stepfun 等 cash 类,CNY 余额下限,默认 10 元
197
+ planPercent: 10 # minimax / zhipu 等 plan 类,「余量」下限,默认 10%
198
+ ```
199
+
200
+ 新加 `/api/dsh-llm-hub/warning/check`:客户端把当前余额信封 POST 进来,host 半
201
+ 按阈值判定,返回 `{ level: 'low'|null, text }`。**阈值数字不进响应** ——
202
+ 这是服务端策略,不让前端能关掉;也不暴露 USD/USDT 之外的汇率换算细节。
203
+
204
+ 余额数字低于阈值时变红 + 一枚 ⚠️ chip(鼠标悬停看具体提示):
205
+ 「DeepSeek 余额 ¥8.50 低于 10,充值一下」或「minimax 5h 余 8% 低于 10%」。
206
+
207
+ ## Provider 健康看板(1.0.0 起)
208
+
209
+ 设置 → 模型 → 页脚 order 70 处渲染成一张表,每行一个 provider:
210
+
211
+ | 列 | 含义 |
212
+ |---|---|
213
+ | 名称 | provider id + displayName |
214
+ | 状态 chip | `available`(绿)/ `unavailable`(红)/ `unknown`(灰) |
215
+ | 延迟 | 本次探测 `latencyMs`,探测没成则显示 `—` |
216
+ | HTTP | 上游状态码(401 / 402 / 5xx 一眼可分) |
217
+ | 原因 | 不可用/未知时给一句人话提示(截断 + title 出全文) |
218
+ | 探测时间 | 本地时间 `HH:MM:SS`,方便判断缓存新鲜度 |
219
+
220
+ 数据来源 `/api/dsh-llm-hub/health`:复用 availability 的 verdict,附加
221
+ `latencyMs` 与 `status` 字段。`ensureAvailabilityFresh()` 触发后台重探
222
+ (5 分钟 TTL 过期),不阻塞响应。
223
+
224
+ 「重新探测」按钮复用 `availability.recheck`,与下拉重探同一条底层刷新
225
+ 路径 —— 不会双探。
226
+
227
+ ## 智能路由建议(1.1.0 起;原 P1-5 改方案)
228
+
229
+ DSH 的 `conversation.input.model` 是 **single + user-controlled** slot
230
+ (`replaceRisk: shadows-shipped-ui`,README 已钉死),没有供插件改值的 setter。
231
+ **所以本插件不做自动切换**,只给建议 —— 让你在 Settings 页一眼看到
232
+ 「如果现在要切,应该选哪个」。
233
+
234
+ 设置 → 模型 → 页脚 order 75 处的「当前路由」卡:显示当前激活的 (provider, model)
235
+ + 状态 chip、配置里的 primary 与 fallbacks[] 各自的可用状态。当 current 不可用时
236
+ 卡片底部高亮「建议切换到 X」;全不可用时给一句人话 reason。
237
+
238
+ 配置(settings.dsh-llm-hub.routing):
239
+
240
+ ```yaml
241
+ dsh-llm-hub:
242
+ warning:
243
+ cashCNY: 10
244
+ planPercent: 10
245
+ routing:
246
+ primary: minimax/abab5.5-chat
247
+ fallbacks:
248
+ - deepseek-official/deepseek-chat
249
+ - modelgo/gpt-4o
250
+ ```
251
+
252
+ 请求体(POST `/api/dsh-llm-hub/routing/resolve`):
253
+
254
+ ```json
255
+ { "active": "modelgo/gpt-4o" }
256
+ ```
257
+
258
+ 返回:
259
+
260
+ ```json
261
+ {
262
+ "ok": true,
263
+ "active": { "provider": "modelgo", "model": "gpt-4o", "state": "available" },
264
+ "candidates": [
265
+ { "provider": "minimax", "model": "abab5.5-chat", "state": "available" },
266
+ { "provider": "deepseek-official", "model": "deepseek-chat", "state": "unavailable" },
267
+ { "provider": "modelgo", "model": "gpt-4o", "state": "available" }
268
+ ],
269
+ "recommendation": { "provider": "minimax", "model": "abab5.5-chat" },
270
+ "reason": null
271
+ }
272
+ ```
273
+
274
+ 判定语义:按 primary → fallbacks[] 顺序找第一个 `state === 'available'` 的
275
+ 作为 recommendation;全不可用返 null + reason;未配置 routing 返 null +
276
+ 「尚未在 settings.dsh-llm-hub.routing 配置主力模型 / fallbacks」。
277
+
278
+ 为什么不做自动切换:DSH 的 composer 下拉是 session-scope 的 single slot,宿主
279
+ 没暴露 setter;强行模拟键盘事件去点下拉既脆又破可访问性。「提示」+「一键跳」
280
+ 的姿势比「替你点」更尊重用户当前的下一步动作。
281
+
282
+ ## 模型别名(1.2.0 起)
283
+
284
+ 把长 model id 翻成短显示名 —— `deepseek-reasoner` 渲染成 `推理 (deepseek-reasoner)`、
285
+ `claude-3.5-sonnet` 渲染成 `Sonnet (claude-3.5-sonnet)`。完整 id 永远在 title 里
286
+ (复制 id 粘到 settings.yaml 时不能是「推理」),但日常切模型眼睛扫得快。
287
+
288
+ 配置(`settings.dsh-llm-hub.aliases`):
289
+
290
+ ```yaml
291
+ dsh-llm-hub:
292
+ aliases:
293
+ 'deepseek-official/deepseek-reasoner': '推理'
294
+ 'modelgo/claude-3.5-sonnet': 'Sonnet'
295
+ 'modelgo/gpt-4o': 'GPT-4o'
296
+ ```
297
+
298
+ key 形态:`provider/model`。provider 严格(要去 settings 段查),model 允许
299
+ `.`/`+`(DSH 真实 id 有 `claude-3.5-sonnet`、`deepseek-reasoner` 这种)。
300
+
301
+ 值是空字符串 / 纯空白 / 非字符串 全部过滤;不会让「短名 = 」这种占位塞进 UI。
302
+
303
+ 未配置 aliases 段时 UI 退回只显示 `model.id`,与没装本插件时一致。
304
+
305
+ ## 多账号 key 轮换(1.3.0 起)
306
+
307
+ 同 provider 配多把 key,每次解析连接时按 round-robin 选下一把(避免单把 key
308
+ 撞 rate limit / 配额上限)。**不**自动跳过失败 key —— 用户从 debug 卡看到「上次
309
+ 失败用的是 X」后,下次请求自然轮换过去;自动跳过失败 key 等 1.4 收集真实失败场景再加。
310
+
311
+ 配置(`settings.dsh-llm-hub.keyPool`):
312
+
313
+ ```yaml
314
+ dsh-llm-hub:
315
+ keyPool:
316
+ deepseek-official:
317
+ - name: primary
318
+ env: DEEPSEEK_API_KEY_1
319
+ - name: backup
320
+ env: DEEPSEEK_API_KEY_2
321
+ - name: rotated
322
+ env: DEEPSEEK_API_KEY_3
323
+ modelgo:
324
+ - name: '轮换 A'
325
+ env: MODELGO_KEY_1
326
+ - name: '轮换 B'
327
+ env: MODELGO_KEY_2
328
+ ```
329
+
330
+ 每条 entry 是 `{ name, env }`:name 是人话(debug 用),env 是凭据引用的环境变量名。
331
+ host 半直接 `process.env[ref]` 解出来 —— 不走 credentials 服务,因为这些 key
332
+ 多半不会进凭据库(轮换场景通常是测试 / 临时 key,手贴环境变量更简单)。
333
+
334
+ 向后兼容:未配 keyPool 段时走旧的 `apiKeyEnv` 单 key 路径,行为完全不变。显式
335
+ 覆盖(`request.apiKey`)绕过轮换直接用它,索引不动。
336
+
337
+ 设置 → 模型 → 页脚 order 73 处的「多账号 key 轮换」卡:每行一个 provider +
338
+ 「N keys」元信息 + 当前轮到的 key 名(带 `#M / N · HH:MM:SS」的小标,鼠标悬停看 env 名)。
339
+ **不**显示 apiKey 实际值。
340
+
160
341
  ## 外部 harness 子代理(装了才出现)
161
342
 
162
343
  把**本机已经装好**的外部 agent CLI 注册成 DSH 的子代理提供方,会话里就能把一段独立