dsh-modelscope-pool 0.10.0 → 0.10.1

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/README.md CHANGED
@@ -1,5 +1,6 @@
1
1
  # dsh-modelscope-pool
2
2
 
3
+ [![npm](https://img.shields.io/npm/v/dsh-modelscope-pool.svg)](https://www.npmjs.com/package/dsh-modelscope-pool)
3
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
4
5
  [![dsh](https://img.shields.io/badge/dsh-%3E%3D0.2.1--rc.2-4D6BFE.svg)](https://github.com/deepseek-ai/deepseek-harness)
5
6
  [![Node](https://img.shields.io/badge/node-%3E%3D20-5FA04E.svg)](https://nodejs.org/)
@@ -117,29 +118,53 @@
117
118
  - Node.js ≥ 20
118
119
  - 一个魔搭账号和它的 API Key(`ms-` 开头),在 <https://modelscope.cn/my/myaccesstoken> 或 <https://modelscope.ai> 生成
119
120
 
120
- ### 作为 dsh-go 原生插件安装
121
+ ### 方式一:从 npm 安装(推荐)
121
122
 
122
123
  ```bash
123
- # 1. 把仓库放进 dsh-go 的本地插件目录
124
- # 目录名必须叫 dsh-modelscope-pool(与 package.json 的 name 一致)
124
+ dsh plugin --profile web add dsh-modelscope-pool@0.10.0
125
+ ```
126
+
127
+ 一条命令搞定。装好后重启 App(或让插件重新加载),网关会在 `127.0.0.1:8899` 起来。
128
+
129
+ 升级就是换版本号重装:
130
+
131
+ ```bash
132
+ dsh plugin --profile web add dsh-modelscope-pool@<新版本>
133
+ ```
134
+
135
+ > 建议写死精确版本。npm 上其他包常见的 `latest`、`^` 范围在这里没有好处——插件会监听端口、读写本机文件,版本变化值得你自己看一眼。
136
+
137
+ 只想要包本身、不需要 dsh 帮你装:
138
+
139
+ ```bash
140
+ npm i dsh-modelscope-pool
141
+ ```
142
+
143
+ > 每个版本的改动见 [Releases](https://github.com/Yizutt/dsh-modelscope-pool/releases)。
144
+
145
+ ### 方式二:从源码安装(要改代码时用)
146
+
147
+ ```bash
148
+ # 目录名要和 package.json 的 name 一致
125
149
  mkdir -p "$DSH_HOME/dsh-go/plugins"
126
150
  cd "$DSH_HOME/dsh-go/plugins"
127
151
  git clone https://github.com/Yizutt/dsh-modelscope-pool.git dsh-modelscope-pool
128
152
 
129
- # 2. 装进当前 profile
130
153
  dsh plugin --profile web add "$DSH_HOME/dsh-go/plugins/dsh-modelscope-pool"
131
154
  ```
132
155
 
133
- 装好后重启 App,或让插件重新加载。网关会在 `127.0.0.1:8899` 起来。
156
+ 装的是指向这个目录的链接,所以**目录不能删**。改完 `lib/` 里的代码需要在 App 的「引擎」页重启引擎才生效。
134
157
 
135
- ### 只想用网关、不装 App
158
+ ### 方式三:只挂到 Harness(网页版 / 桌面版)
136
159
 
137
- 插件本身就是一个 Cordis bundle(`package.json` 里的 `dsh.bundle.patch` 指向 `cordis.patch.yml`),所以也可以直接挂到 Harness 的 profile 里:
160
+ 插件本身就是一个 Cordis bundle(`package.json` 里的 `dsh.bundle.patch` 指向 `cordis.patch.yml`),所以不装 dsh-go 也能用:
138
161
 
139
162
  ```bash
140
163
  dsh plugin --profile web add /path/to/dsh-modelscope-pool
141
164
  ```
142
165
 
166
+ 网页版的管理页在 `http://127.0.0.1:3080/modelscope-pool`,见[网页版与桌面版](#网页版与桌面版)。
167
+
143
168
  ---
144
169
 
145
170
  ## 配置
@@ -308,6 +333,9 @@ dsh-go 里更进一步:这个插件会把自己注册成一个叫 **魔搭 Key
308
333
  **Key 一直不自动恢复?**
309
334
  看看是不是被「手动禁用」了(`manual` 标记)。手动禁用不会被自动恢复流程放开,需要点「恢复」。
310
335
 
336
+ **点「刷新额度」提示「魔搭 Key 池没有响应」?**
337
+ App 要求点按在 3 秒内返回,而刷新额度要联网逐个查所有 Key 的额度,早就超过 3 秒了。0.10.1 起这几个慢操作(刷新额度 / 测试全部 / 重新拉取模型 / 添加 Key)改成先回一个「正在刷新额度」的等待态,结果异步回来,最多 60 秒。如果你还在 0.10.0 或更早,升级即可。
338
+
311
339
  **端口被占用?**
312
340
  改 `config.json` 的 `port`,同时把客户端和供应商配置里的地址一起改掉。
313
341
 
package/lib/i18n.js CHANGED
@@ -129,6 +129,12 @@ const S = {
129
129
  trailingCurrent: ['当前', 'Current'],
130
130
  trailingUse: ['使用', 'Use'],
131
131
 
132
+ // 慢操作:点按后先回 pending,App 会显示等待
133
+ pendingRefresh: ['正在刷新额度', 'Refreshing quota'],
134
+ pendingTest: ['正在测试全部', 'Testing all'],
135
+ pendingModels: ['正在拉取模型', 'Loading models'],
136
+ pendingAdd: ['正在校验 Key', 'Checking keys'],
137
+
132
138
  // 操作反馈
133
139
  toastQuotaUpdated: ['额度已更新', 'Quota refreshed'],
134
140
  toastModelSet: ['默认模型:{model}', 'Default model: {model}'],
package/lib/index.js CHANGED
@@ -728,11 +728,26 @@ const modelRowOf = (entry, current, t) => {
728
728
  }
729
729
  }
730
730
 
731
+ // 点按必须在 3 秒内返回,否则 App 会提示「没有响应」。
732
+ // 刷新额度 / 测试全部 / 拉模型 / 校验 Key 都要联网,所以先回 pending,结果走延后效果。
733
+ const defer = (label, work) => ({
734
+ pending: { label, timeoutMs: 60_000 },
735
+ result: async () => {
736
+ const value = await work()
737
+ refreshPanel()
738
+ refreshStrip()
739
+ return value
740
+ },
741
+ })
742
+
731
743
  const panelActions = {
732
- async refresh(scope, event) {
744
+ refresh(scope, event) {
733
745
  const t = makeT(event?.locale)
734
- await Promise.all([refreshQuota(true), refreshCatalog(true)])
735
- return { toast: t('toastQuotaUpdated'), refresh: true }
746
+ return defer(t('pendingRefresh'), async () => {
747
+ await refreshQuota(true)
748
+ await refreshCatalog(true)
749
+ return { toast: t('toastQuotaUpdated') }
750
+ })
736
751
  },
737
752
  async 'pick-model'(scope, event) {
738
753
  const t = makeT(event?.locale)
@@ -742,12 +757,13 @@ const modelRowOf = (entry, current, t) => {
742
757
  ? { toast: t('toastModelSet', { model }), refresh: true }
743
758
  : { toast: t('toastModelBad') }
744
759
  },
745
- async 'test-all'(scope, event) {
760
+ 'test-all'(scope, event) {
746
761
  const t = makeT(event?.locale)
747
762
  const keys = pool ? [...pool.keys] : []
748
763
  if (keys.length === 0) return { toast: t('toastNoKeys') }
749
764
  // 注意不能用 /v1/models 验 Key:那个接口是公开的,什么令牌都返回 200。
750
765
  // 真正会校验令牌的是额度接口,而且查额度不扣魔粒。
766
+ return defer(t('pendingTest'), async () => {
751
767
  const rows = await quota.refresh(
752
768
  keys.map((k) => ({ id: k.id, key: k.key, region: k.region })),
753
769
  { force: true },
@@ -774,6 +790,7 @@ const modelRowOf = (entry, current, t) => {
774
790
  if (short > 0) parts.push(t('partShort', { n: short }))
775
791
  if (invalid > 0) parts.push(t('partInvalid', { n: invalid }))
776
792
  return { toast: t('toastTestDone', { parts: parts.join(' · ') }) }
793
+ })
777
794
  },
778
795
  'restore-all': async (scope, event) => {
779
796
  const t = makeT(event?.locale)
@@ -853,20 +870,24 @@ const modelRowOf = (entry, current, t) => {
853
870
  })
854
871
  return { toast: t('toastDeleted', { id }), refresh: true }
855
872
  },
856
- 'add-form': async (scope, event) => {
873
+ 'add-form': (scope, event) => {
874
+ const t = makeT(event?.locale)
857
875
  const value = event?.value
858
876
  const raw = typeof value === 'string' ? value : value?.newKey ?? value?.value ?? ''
859
- const result = await addKey(raw, event?.locale)
860
- return { ...result, refresh: true }
877
+ return defer(t('pendingAdd'), async () => {
878
+ const result = await addKey(raw, event?.locale)
879
+ return { toast: result?.toast ?? t('toastNoNew') }
880
+ })
861
881
  },
862
882
  'key-back'() {
863
883
  return { open: 'panel:pool' }
864
884
  },
865
- async 'refresh-models'(scope, event) {
885
+ 'refresh-models'(scope, event) {
866
886
  const t = makeT(event?.locale)
867
- await refreshCatalog(true)
868
- refreshPanel()
869
- return { toast: t('toastModelsReloaded'), refresh: true }
887
+ return defer(t('pendingModels'), async () => {
888
+ await refreshCatalog(true)
889
+ return { toast: t('toastModelsReloaded') }
890
+ })
870
891
  },
871
892
  }
872
893
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-modelscope-pool",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "description": "魔搭多 Key 聚合网关:无感路由(模型不变,Key 遇限流/额度不足/无权限/不存在自动换下一个);自动识别国际版/国内版令牌;单个 Key 可禁用/停用/恢复/删除/查看;全部密钥分页浏览;模型实际拉取国际版+国内版合并成简化名、按站点分组;额度低于 2 停用到次日 00:01、多 Key 轮换、限流临时失败冷却 5 分钟,并注册为 DSH 模型供应商",
5
5
  "license": "MIT",
6
6
  "author": "Yizutt",