dsh-connect-modelscope-token-plan 0.6.1 → 0.6.2
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 +9 -0
- package/client.js +2 -2
- package/lib/host-config.js +44 -18
- package/lib/index.js +20 -2
- package/locale/en.json +2 -2
- package/locale/zh.json +2 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -26,6 +26,15 @@
|
|
|
26
26
|
> **发版时**:顶部开 `## [X.Y.Z] — YYYY-MM-DD`,完整流程见
|
|
27
27
|
> [RELEASING.md](RELEASING.md) §3(本文开头这段是**写作者规范**,那是**发布操作流程**)。
|
|
28
28
|
|
|
29
|
+
## [0.6.2] — 2026-10-18
|
|
30
|
+
|
|
31
|
+
**插件命名与卡片文案以「模型接入」为主题重排**
|
|
32
|
+
|
|
33
|
+
- 卡片标题与面板页头「魔搭接入 · 额度面板」改为「魔搭模型接入」(英文同步 ModelScope Model Access)。
|
|
34
|
+
- 描述改以「模型接入」为本体:一键注册魔搭语言模型与生图工具,自动填入图片识别与上下文窗口,注册即可用(均默认关);免费额度面板降为加分项,不注册也能看。
|
|
35
|
+
- provider 注册不再挂过时的「(路线图)」标签(实际已交付),改按 opt-in 如实写「均默认关」。
|
|
36
|
+
- 无行为变化:本版只改面板展示文案与注释,功能、数据与流程同 0.6.1。
|
|
37
|
+
|
|
29
38
|
## [0.6.1] — 2026-10-18
|
|
30
39
|
|
|
31
40
|
**迁移窗口期不再看不见旧数据**
|
package/client.js
CHANGED
|
@@ -136,7 +136,7 @@ var dsh_connect_modelscope_token_plan_client = (function() {
|
|
|
136
136
|
var zh, en;
|
|
137
137
|
var init_i18n = __esmMin((() => {
|
|
138
138
|
zh = {
|
|
139
|
-
"panel.title": "
|
|
139
|
+
"panel.title": "魔搭模型接入",
|
|
140
140
|
"panel.refresh": "刷新",
|
|
141
141
|
"panel.updated": "更新于 {time}",
|
|
142
142
|
"panel.loading": "加载中…",
|
|
@@ -288,7 +288,7 @@ var dsh_connect_modelscope_token_plan_client = (function() {
|
|
|
288
288
|
"composer.stale": "上次数据"
|
|
289
289
|
};
|
|
290
290
|
en = {
|
|
291
|
-
"panel.title": "ModelScope Access
|
|
291
|
+
"panel.title": "ModelScope Model Access",
|
|
292
292
|
"panel.refresh": "Refresh",
|
|
293
293
|
"panel.updated": "Updated {time}",
|
|
294
294
|
"panel.loading": "Loading…",
|
package/lib/host-config.js
CHANGED
|
@@ -181,37 +181,63 @@ function extractStructuredType(message) {
|
|
|
181
181
|
return match ? match[1] ?? null : null;
|
|
182
182
|
}
|
|
183
183
|
/**
|
|
184
|
-
* 一个被 peer 判为 QUOTA
|
|
184
|
+
* 一个被 peer 判为 QUOTA 的失败,按哪条判据路径处理——429 重分类的**可观测
|
|
185
|
+
* 理由**。纯函数,复用同一批判据(模块私有的 {@link RATE_SIGNAL} /
|
|
186
|
+
* {@link RATE_CAP_WORDING}、{@link hasHardQuotaWordingIn}、结构化 type),不
|
|
187
|
+
* 引入第二份词表;返回稳定短串(非自由文本),供面板/日志/测试引用“为什么
|
|
188
|
+
* 这么判”。
|
|
185
189
|
*
|
|
186
190
|
* 这是修正 v1(纯文本启发)漏判的核心:`{"message":"rpm exhausted",
|
|
187
191
|
* "type":"quota_exceeded_error","code":"8"}` 这种体——魔搭把**请求速率上限**
|
|
188
|
-
* 复用 `quota_exceeded_error` 这个名字,纯文本里没有
|
|
192
|
+
* 复用 `quota_exceeded_error` 这个名字,纯文本里没有 rate limit 字样,v1 的
|
|
189
193
|
* `looksLikeRateLimit` 既没命中限频信号也没命中硬额度,于是留在 QUOTA、不重试、
|
|
190
|
-
* 直接失败。本函数改读结构化 `type
|
|
191
|
-
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
194
|
-
*
|
|
195
|
-
*
|
|
196
|
-
*
|
|
197
|
-
*
|
|
194
|
+
* 直接失败。本函数改读结构化 `type` 逐层收窄,每个返回串对应一条路径:
|
|
195
|
+
*
|
|
196
|
+
* - `"not_quota_code"` 失败为空或 code≠QUOTA,纠正层不适用。
|
|
197
|
+
* - `"structured:rate_limit_type"` 结构化 type 含 rate_limit,本就是限频(防御,纠正)。
|
|
198
|
+
* - `"structured:quota_rate_cap"` 结构化 type 含 quota 且命中 {@link RATE_CAP_WORDING}
|
|
199
|
+
* (rpm/tpm/每分钟/限流…)→ 错命名,纠正。
|
|
200
|
+
* - `"structured:quota_no_cap"` 结构化 type 含 quota 且无速率信号(token/credit/
|
|
201
|
+
* balance 真耗尽)→ 保留 QUOTA。
|
|
202
|
+
* - `"structured:other_type"` 结构化 type 是其它非 quota 值 → 保守不纠正。
|
|
203
|
+
* - `"text:rate_signal"` 无结构化 type,纯文本命中 {@link RATE_SIGNAL} 且
|
|
204
|
+
* 无硬额度字样 → 纠正。
|
|
205
|
+
* - `"text:hard_quota"` 无结构化 type,命中硬额度字样 → 保留 QUOTA。
|
|
206
|
+
* - `"text:no_rate_signal"` 无结构化 type 也无限频信号 → 保留 QUOTA。
|
|
198
207
|
*
|
|
199
208
|
* 不纠正真配额耗尽:那是共享 Token Plan 池的硬耗尽,重试只会延长冷却窗口
|
|
200
209
|
* (ROADMAP §1 纪律),所以宁可快失败。
|
|
201
210
|
* @param {{code?: string, message?: string}} failure
|
|
202
|
-
* @returns {
|
|
211
|
+
* @returns {string} 标记处理路径的稳定短串。
|
|
203
212
|
*/
|
|
204
|
-
function
|
|
205
|
-
if (!failure || failure.code !== CODE.QUOTA) return
|
|
213
|
+
function reclassifyReason(failure) {
|
|
214
|
+
if (!failure || failure.code !== CODE.QUOTA) return "not_quota_code";
|
|
206
215
|
const message = typeof failure.message === "string" ? failure.message : "";
|
|
207
216
|
const type = extractStructuredType(message);
|
|
208
217
|
if (type !== null) {
|
|
209
218
|
const t = type.toLowerCase();
|
|
210
|
-
if (/rate[_\s-]?limit/.test(t)) return
|
|
211
|
-
if (/quota/.test(t)) return RATE_CAP_WORDING.test(message);
|
|
212
|
-
return
|
|
219
|
+
if (/rate[_\s-]?limit/.test(t)) return "structured:rate_limit_type";
|
|
220
|
+
if (/quota/.test(t)) return RATE_CAP_WORDING.test(message) ? "structured:quota_rate_cap" : "structured:quota_no_cap";
|
|
221
|
+
return "structured:other_type";
|
|
213
222
|
}
|
|
214
|
-
|
|
223
|
+
if (!RATE_SIGNAL.some((re) => re.test(message.toLowerCase()))) return "text:no_rate_signal";
|
|
224
|
+
return hasHardQuotaWordingIn(message) ? "text:hard_quota" : "text:rate_signal";
|
|
225
|
+
}
|
|
226
|
+
/** 判为「应纠正为 RATE_LIMIT」的理由集合——{@link shouldReclassifyQuotaToRate} 的唯一真源。 */
|
|
227
|
+
const RECLASSIFY_TRUE_REASONS = /* @__PURE__ */ new Set([
|
|
228
|
+
"structured:rate_limit_type",
|
|
229
|
+
"structured:quota_rate_cap",
|
|
230
|
+
"text:rate_signal"
|
|
231
|
+
]);
|
|
232
|
+
/**
|
|
233
|
+
* 一个被 peer 判为 QUOTA 的失败,是否其实是限频、应纠正为 RATE_LIMIT。
|
|
234
|
+
* 布尔判定单一真源:读 {@link reclassifyReason} 是否落在「应纠正」集合里,分支
|
|
235
|
+
* 逻辑不再写第二遍;可观测“为什么这么判”见 {@link reclassifyReason} 的返回串。
|
|
236
|
+
* @param {{code?: string, message?: string}} failure
|
|
237
|
+
* @returns {boolean} true 表示应纠正为 RATE_LIMIT。
|
|
238
|
+
*/
|
|
239
|
+
function shouldReclassifyQuotaToRate(failure) {
|
|
240
|
+
return RECLASSIFY_TRUE_REASONS.has(reclassifyReason(failure));
|
|
215
241
|
}
|
|
216
242
|
/**
|
|
217
243
|
* 把单个流 chunk 里的失败码在误判时纠正。
|
|
@@ -1103,7 +1129,7 @@ const inject = ["webServer"];
|
|
|
1103
1129
|
* 产物反而引入第二份真源;这里用常量、由 test/config.test.mjs 钉住与
|
|
1104
1130
|
* package.json 一致。
|
|
1105
1131
|
*/
|
|
1106
|
-
const PLUGIN_VERSION = "0.6.
|
|
1132
|
+
const PLUGIN_VERSION = "0.6.2";
|
|
1107
1133
|
/**
|
|
1108
1134
|
* 默认值。刻意不含额度常数——理由见本文件头。
|
|
1109
1135
|
* 面板头条是官方魔粒余额,次数口径的推算已在面板收敛时删除,这里若留着
|
package/lib/index.js
CHANGED
|
@@ -4762,7 +4762,7 @@ function syncSignaturesAfterPublish(state) {
|
|
|
4762
4762
|
* 最多收敛成两遍 reconcile,不会排 N 遍。调用方 await 返回的 promise,即
|
|
4763
4763
|
* 「我的触发已被消费」。
|
|
4764
4764
|
* @param {ProviderReconcilerDeps} deps - wiring 子集。
|
|
4765
|
-
* @returns {{reconcile: () => Promise<void
|
|
4765
|
+
* @returns {{reconcile: () => Promise<void>; health: () => {{ lastError: unknown }} }}
|
|
4766
4766
|
*/
|
|
4767
4767
|
function createProviderReconciler(deps) {
|
|
4768
4768
|
const { settings, providerStore, inference, publisher, usageEvents, logger } = deps;
|
|
@@ -4861,6 +4861,14 @@ function createProviderReconciler(deps) {
|
|
|
4861
4861
|
/** 有新触发待消费。闭包状态,不暴露——触发方只看得见 {@link reconcile}。 */
|
|
4862
4862
|
let dirty = false;
|
|
4863
4863
|
/**
|
|
4864
|
+
* 最近一次「未被 runOnce 自行记档」的意外反抗(executor 层面漏网的抛,如
|
|
4865
|
+
* `publisher.state` 的 getter 同步抛),下一遍成功即清空。路由侧 await 拿到
|
|
4866
|
+
* `result` 会自行 respondError;但**轮询侧 `void reconcile()` 会把它丢掉**——
|
|
4867
|
+
* 这份 lastError + 一条 warn 是「这一遍没收敛成」对操作员/测试唯一可观测的
|
|
4868
|
+
* 记档(否则链的防毒化会把它连同结果一起吞成 0 痕迹,见 {@link reconcile} 注释)。
|
|
4869
|
+
*/
|
|
4870
|
+
let lastError = null;
|
|
4871
|
+
/**
|
|
4864
4872
|
* 触发一次收敛。幂等且可重入:在途时只置 dirty,由在途那一轮替本调用消费
|
|
4865
4873
|
* (N 次连续触发最多收敛成两遍 reconcile,不会排 N 遍)。
|
|
4866
4874
|
* @returns {Promise<void>} 本触发已被消费(注册态已反映 desired)后 resolve。
|
|
@@ -4875,10 +4883,20 @@ function createProviderReconciler(deps) {
|
|
|
4875
4883
|
if (++guard > 20) break;
|
|
4876
4884
|
}
|
|
4877
4885
|
});
|
|
4886
|
+
result.then(() => {
|
|
4887
|
+
lastError = null;
|
|
4888
|
+
}, (error) => {
|
|
4889
|
+
lastError = error;
|
|
4890
|
+
logger?.warn?.(`${name}: reconcile pass failed (${redactSecrets(errMsg(error))}); state will be reconciled on the next trigger`);
|
|
4891
|
+
});
|
|
4878
4892
|
chain = result.then(() => void 0, () => void 0);
|
|
4879
4893
|
return result;
|
|
4880
4894
|
};
|
|
4881
|
-
return {
|
|
4895
|
+
return {
|
|
4896
|
+
reconcile,
|
|
4897
|
+
/** 最近一次未被记档的意外反抗(成功即清空);null = 当前健康。 */
|
|
4898
|
+
health: () => ({ lastError })
|
|
4899
|
+
};
|
|
4882
4900
|
}
|
|
4883
4901
|
|
|
4884
4902
|
//#endregion
|
package/locale/en.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
|
-
"title": "ModelScope
|
|
4
|
-
"description": "ModelScope
|
|
3
|
+
"title": "ModelScope Model Access",
|
|
4
|
+
"description": "Register ModelScope's language models and draw tool into DSH with one toggle: image-recognition capability and context windows auto-filled, usable the moment it is registered (both default off); the free-quota panel — official Magicube balance, local call counts, trends, rate-limit events — works even without registering."
|
|
5
5
|
}
|
|
6
6
|
}
|
package/locale/zh.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"meta": {
|
|
3
|
-
"title": "
|
|
4
|
-
"description": "
|
|
3
|
+
"title": "魔搭模型接入",
|
|
4
|
+
"description": "一键把魔搭(ModelScope)的语言模型与生图工具注册进 DSH:自动填入图片识别能力与上下文窗口,注册即可用(均默认关);不注册也能看免费额度面板:官方魔粒余额、本地调用计数、趋势、限流事件。"
|
|
5
5
|
}
|
|
6
6
|
}
|
package/package.json
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
"name": "dsh-connect-modelscope-token-plan",
|
|
3
3
|
"displayName": "DSH Connect ModelScope Token Plan",
|
|
4
4
|
"icon": "./icon.svg",
|
|
5
|
-
"version": "0.6.
|
|
5
|
+
"version": "0.6.2",
|
|
6
6
|
"homepage": "https://github.com/eghrhegpe/dsh-connect-modelscope-token-plan",
|
|
7
7
|
"bugs": {
|
|
8
8
|
"url": "https://github.com/eghrhegpe/dsh-connect-modelscope-token-plan/issues"
|
|
9
9
|
},
|
|
10
10
|
"author": "eghrhegpe",
|
|
11
|
-
"description": "ModelScope (modelscope.cn)
|
|
11
|
+
"description": "ModelScope (modelscope.cn) access bundle for DSH: opt-in registration of ModelScope language models and draw tool as an OpenAI-compatible provider (vision and context windows auto-filled, usable the moment registered), plus a free-quota panel with the official Magicube balance, per-model local call counts, daily trends and 429 event list — usable without registering.",
|
|
12
12
|
"keywords": [
|
|
13
13
|
"dsh-plugin",
|
|
14
14
|
"deepseek-harness",
|