dsh-connect-modelscope-token-plan 0.1.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 +198 -0
- package/LICENSE +21 -0
- package/README.md +55 -0
- package/assets/icon-preview.png +0 -0
- package/assets/tab-account-and-usage.png +0 -0
- package/assets/tab-model-list.png +0 -0
- package/client.js +1828 -0
- package/cordis.patch.yml +53 -0
- package/icon.svg +30 -0
- package/lib/host-config-DBCiZOBW.js +900 -0
- package/lib/index.js +3181 -0
- package/lib/llm-adapter-CpcQ3gC5.js +557 -0
- package/locale/en.json +5 -0
- package/locale/zh.json +6 -0
- package/package.json +107 -0
- package/screenshots.json +4 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [Unreleased]
|
|
4
|
+
|
|
5
|
+
两轮:先是发布前的漂移收口与门禁补强,再是一轮由双路深审挖出的**数据安全与红线修复**。
|
|
6
|
+
|
|
7
|
+
### 第五轮:红线收口
|
|
8
|
+
|
|
9
|
+
- **聚合层 `soft()` 补脱敏**(红线 1 的收口漏洞):`soft()` 用裸 `errMsg(error)`,而那个
|
|
10
|
+
字符串会一路流进 `shapeWarnings` / `balance.error` / `models.error`(面板可见)与快照
|
|
11
|
+
路由的响应体。它隐式依赖「下游每个 promise 都已自行脱敏」——**契约不该靠下游的自觉**。
|
|
12
|
+
实测 `errMsg` 会把 `upstream said ms-3f2a1b8c1111222233334444555566` 原样带出去。现在收口
|
|
13
|
+
处自己脱敏(`redactSecrets` 是幂等的,重复脱敏换掉「凭据可能外泄」这个尾部风险划算)。
|
|
14
|
+
- **另两处同类出口**:`resolveSettings` 的 `configError`(进 `ok:false` 响应体)与 `index.ts`
|
|
15
|
+
的路由注销警告(**进日志**——红线 1 同时管日志与面板)。都补上脱敏。
|
|
16
|
+
- **`test/credentials.test.mjs` 加一条静态不变量**:Host 侧不允许出现「裸 `errMsg` 进入
|
|
17
|
+
wire 字段或日志」,且点名三个具体收口。判据是静态的——构造真实的泄漏路径需要上游回显,
|
|
18
|
+
而离线套件刻意不联网。已双向验证(去掉任一脱敏,对应用例变红)。
|
|
19
|
+
|
|
20
|
+
### 第四轮:资源与一致性
|
|
21
|
+
|
|
22
|
+
- **原子写补崩溃一致性与临时文件清理**(`writeStateFile`,四个 store 共同的唯一写入口):
|
|
23
|
+
- `rename` 失败时(Windows 上目标文件被占用最常见)临时文件**留在原地**——名字是
|
|
24
|
+
`<base>.<pid>.<ts>.<uuid>.tmp`,全仓原本无任何 `unlink`,于是
|
|
25
|
+
`state/<profile>/<name>/` 会慢慢堆满垃圾。现在失败时删掉自己的临时文件再重抛
|
|
26
|
+
(清理失败不掩盖原始错误)。
|
|
27
|
+
- rename 前对临时文件 `fsync`。这是**崩溃一致性**(rename 本身只保证并发原子性):
|
|
28
|
+
没有它,断电/内核崩溃时 rename 可能把一个空或半截文件立到目标路径,而
|
|
29
|
+
`parsePayload` 对损坏文件是「读作未设置」**静默降级**——用户看到「清单和历史全没了」
|
|
30
|
+
而没有任何错误。代价是真机每次几毫秒,值得:`usage.json` 每次对话都写、
|
|
31
|
+
`provider.json` 每次点开关都写,都不是低频操作。
|
|
32
|
+
- **修未消费的 response body 占住连接**:`fetchTaskTags` 在 `!response.ok` 分支直接
|
|
33
|
+
`return []`、不读也不取消 body。undici 里未消费的 body 会**占住连接直到 GC**,而目录
|
|
34
|
+
刷新对 35 个模型并发打详情端点——任何一条走该分支,连接就一直挂着,连接池被吃满后
|
|
35
|
+
后续请求开始排队。`readErrorMessage` 的 catch 分支同理(`json()` 抛在半路时 body 未被
|
|
36
|
+
消费)。现在两条路径都显式 `void response.body?.cancel()`。
|
|
37
|
+
- **删 27 个零引用样式token**(83 → 56):`poolsGrid` / `quotas` / `quotaRemaining` /
|
|
38
|
+
`statHeadline` / `details*` / `primaryHover` / `primaryBusy` 等。它们连同解释其设计
|
|
39
|
+
决策的注释一起是「上一轮删模型目录表 / 次数口径推算条」的**化石**——注释里还在描述
|
|
40
|
+
「额度双栏」「grant chip」「details 折叠区」「twin card」,读代码的人会以为这些 UI 还在。
|
|
41
|
+
已连带清理 5 处引用已删 token 的注释。
|
|
42
|
+
- **删 4 个死 helper**:`when()`(注释声称「头部更新于必须能熬过隔天」,而 `panel-page`
|
|
43
|
+
实际用 `isoTime`,该设计从未落地)、`postJsonOrThrow`、`dictKey`,以及
|
|
44
|
+
`sortCatalogIds`(featured 置顶排序随「模型目录表」退役,`roster` 现按 Host 返回顺序
|
|
45
|
+
渲染,只保留 featured 徽标——排序规则连同它的 6 条性质断言一起删,不留「有契约无
|
|
46
|
+
实现」的孤儿)。`clockLong` 经核实**有 2 处真实调用**(`format.ts:24,42`),保留。
|
|
47
|
+
- **新增 `test/state-store.test.mjs`**:`state-store.ts` 是四个 store 共同的唯一写入口,
|
|
48
|
+
此前零直接测试。
|
|
49
|
+
|
|
50
|
+
### 第三轮:状态机与配置边界
|
|
51
|
+
|
|
52
|
+
派代理专门核实三个「需要真peer 才能判定」的疑点——它在DSH 发行版里找到了
|
|
53
|
+
`@deepseek-ai/dsh-llm@0.1.7-rc.2` 的**本机真实源码**,把三个疑点从推断升级为读代码判定。
|
|
54
|
+
|
|
55
|
+
- **修 dispose × 在途 publish 竞态(僵尸 provider 泄漏)**:`publishProviderOnce` 只在
|
|
56
|
+
**入口**查一次 `queue.isDisposed()`,而注册发生在最后一个 await 之后(动态 import
|
|
57
|
+
是最宽的窗口)。teardown 落在这段窗口里时闸门不重跑,于是 `registerAdapter` 把适配器
|
|
58
|
+
注册进一个**已撤下本插件的 Host**,而 teardown 早已跑完、release 永不调用(全仓只有
|
|
59
|
+
一个 `.release()` 调用点)。泄漏的对持有 `tokenStore` 与整个 Cordis `ctx` 的闭包 →
|
|
60
|
+
**僵尸 provider**:picker 里那个 provider 还在、还能路由,而面板路由已注销,用户没有
|
|
61
|
+
界面能关掉它。
|
|
62
|
+
peer 源码证实 fiber 兜底救不了:`registerAdapter` 用的是 **llm 服务自己的**
|
|
63
|
+
`ctx.effect`(`.bind(this)` 到 llm 服务),插件卸载不会 dispose 它。
|
|
64
|
+
修法:在 `swapRegistration` 前补第二次闸门检查——从那里到 `registerAdapter` 之间
|
|
65
|
+
**没有任何 await**,单线程下无法插入 teardown,两种时序都正确。
|
|
66
|
+
- **给 provider-store 加写串行化(并发丢数据)**:这个 store 曾**完全没有写串行化**
|
|
67
|
+
(`usage-store` 有 `writeChain`,这里没有),而 `patchPayload` 是读-改-写。面板开关
|
|
68
|
+
POST 与 roster POST 是两个独立请求,各自读到同一个 `current`,后写的赢——实测用户勾了
|
|
69
|
+
模型又开了开关,两次点击只活下来一次。文件头说「两个字段必须作为一个载荷读写」防住了
|
|
70
|
+
「不同字段互相覆盖」,但缺串行化后「两次都基于同一快照」的互相覆盖照样发生。现在整段
|
|
71
|
+
读-改-写排队,`writePayload` 的版本拒绝语义一起搬进临界区。**legacy 继承回填**(读路径
|
|
72
|
+
内触发的写)也走同一条链,否则它会覆盖用户刚保存的清单,且因只发生一次而永久生效。
|
|
73
|
+
- **给三个时长配置补上界**:`cacheSeconds` / `pollSeconds` / `inferenceTimeoutMs` 的
|
|
74
|
+
`clampInt` 只传了 min(max 默认 `Infinity`)。`pollSeconds × 1000` 超过 2^31-1 ms 时
|
|
75
|
+
Node 把 `setInterval` 延时**钳到 1ms**(实测 pollSeconds=3000000 → `_idleTimeout=1`),
|
|
76
|
+
插件变成每毫秒打一次魔搭目录并打穿单飞缓存;`inferenceTimeoutMs` 同理让每次请求立刻超时。
|
|
77
|
+
现在按语义夹到轮询 1 小时 / 缓存 10 分钟 / 超时 10 分钟。
|
|
78
|
+
- **新增 `test/provider-publish.test.mjs`**:`provider-publish.ts` 此前**零测试覆盖**
|
|
79
|
+
(ROADMAP 声称 `test/provider.test.mjs` 测「publish 三条语义」,实际上没有)——零覆盖
|
|
80
|
+
正是这个 P0 能藏住的原因。新套件覆盖 dispose 闸门(含 await 窗口竞态)、publish 队列
|
|
81
|
+
串行性、回滚恢复旧对、空转守卫(指标是 `registerAdapter` 次数而非工厂次数——守卫在工厂
|
|
82
|
+
之后,被跳过的 publish 也跑过工厂)、签名的确定性。
|
|
83
|
+
- **核实并排除两条疑点**(避免误改):`{...handle}` 展开丢私有字段**不是问题**——peer
|
|
84
|
+
的 `prepareCall` 返回 plain object literal(源码 `:1827-1833`),无原型方法无私有字段;
|
|
85
|
+
`resolveRegistrationService` 漏清 `state.built` **不构成 bug**——重新注册旧适配器是安全的
|
|
86
|
+
(无 DUPLICATE_ADAPTER、A 上无注册域状态、新 release 有记录),只是语义不一致。
|
|
87
|
+
|
|
88
|
+
### 第二轮:深审挖出的真问题(每条都有复现证据,不是推断)
|
|
89
|
+
|
|
90
|
+
- **修 usage-store 版本守卫(数据静默销毁)**:守卫检查写在 `enqueue` 开头,而
|
|
91
|
+
`readOnly` 的置位发生在其后的 `cache.read()` 内部——判据比真正的探测**晚了一
|
|
92
|
+
步**。实测:磁盘预置 version 2 含 110 次真实调用,一次 `recordCall` 后 version
|
|
93
|
+
降级成 1、历史归零,而日志还在说「recording disabled to avoid clobbering」——
|
|
94
|
+
旧构建正在降级覆写新构建的数据,日志与磁盘状态互相撒谎。同一处 `readOnly` 只
|
|
95
|
+
置位不复位,于是用户按 doctor 建议删掉 usage.json 后写入永久静默丢弃、面板
|
|
96
|
+
还在显示内存残留。现在版本探测由写路径自己做(判据与写入之间不再隔 await),
|
|
97
|
+
且「版本已知」时复位闩锁。回归用例见 `test/usage-store.test.mjs` §7b/§7c(已验证
|
|
98
|
+
它们在修复前确实变红)。
|
|
99
|
+
- **修信任围栏的前缀匹配**:`hostName` 对 `Host: [::1]evil.com` 读成 `[::1]` 并
|
|
100
|
+
**通过**白名单——白名单是精确集合,前缀匹配等于把它放宽成「以成员开头」。现在
|
|
101
|
+
校验 `]` 之后只允许空或 `:port`。
|
|
102
|
+
- **修面板缺键即白屏**:`interpretSnapshot` 对 `ok:true` 的 body 是裸 cast,
|
|
103
|
+
`SNAPSHOT_REQUIRED_KEYS` 只在测试里被引用、运行时零防线。缺 `models` / `trend` /
|
|
104
|
+
`token` 任一 → TypeError炸掉整个面板(那一行在**所有 tab 上都执行**),而只有
|
|
105
|
+
`provider` 有防御。现在按 `SNAPSHOT_REQUIRED_KEYS` 校验并把缺失块补成与 Host
|
|
106
|
+
降级同构的空形状,缺失名单作为 `missingKeys` 冒到面板上(不静默)。顺带收口同
|
|
107
|
+
族裸解引用(`data?.models.sample` 的 `?.` 没到底、`TokenForm` 的 `=== null` 漏
|
|
108
|
+
`undefined`、`LocalDailyCard` 裸解构)。
|
|
109
|
+
- **修 429 分诊把限频误判成额度耗尽**:`classifyRateLimit` 曾把 `"limit reached"` /
|
|
110
|
+
`"daily"` / `"次数"` 归进 quota 桶,于是「Rate limit reached, retry after 60s」
|
|
111
|
+
被判成耗尽 → `llm-retry` 刻意排除 QUOTA → **瞬时限频快失败且不重试**;面板事件
|
|
112
|
+
流还把 rpm 限频显示成「额度耗尽」,与 `llm-error-fix` 的纠正层自相矛盾。现在判据
|
|
113
|
+
**复用** `llm-error-fix` 的 `looksLikeRateLimit` + `hasHardQuotaWordingIn`(消除
|
|
114
|
+
第二份词表),并补上 `daily|monthly … limit` 这组硬额度措辞。
|
|
115
|
+
- **修 `internal_error` 没有引导**:聚合器兜底码曾不在 `CODE` 表里,于是 Client 的
|
|
116
|
+
`GUIDANCE_BY_CODE` 映射不了它,最需要人看的内部错误反而没有引导文案,且因为不
|
|
117
|
+
在 `FORM_EXCLUDED_CODES` 里被引导去「配令牌」——方向反了。现在
|
|
118
|
+
`CODE.INTERNAL_ERROR` 正式进表,两端同一份真源。
|
|
119
|
+
- **修令牌脱敏漏紧凑写法(红线 1)**:`redactSecrets` 的 `ms-` 正则硬编码 UUID 的
|
|
120
|
+
`8-4-4-4-12` 分组形状,紧凑写法 `ms-3f2a1b8c1111222233334444555566` 整条漏网;
|
|
121
|
+
而 `ms-auth.save()` 不做形状校验,非标准形状的令牌是**可达状态**,上游一旦回显
|
|
122
|
+
即进日志与面板响应。两头都堵:脱敏闸放宽到 `ms-[0-9a-f-]{16,}`,入口新增形状
|
|
123
|
+
校验拒收遮不住的短值。
|
|
124
|
+
- **修 `forget()` 谎报删除成功(红线 1)**:曾吞掉凭据服务的 `unset` 失败,让路由回
|
|
125
|
+
`ok: true`,而令牌**仍在凭据文件里且仍被读回来**。安全动作谎报成功比失败更糟,
|
|
126
|
+
现在失败向上传播(路由已有脱敏呈现路径)。
|
|
127
|
+
- **修 `state().ephemeral` 答错问题**:wire 定义是「这个值重启会不会丢」,实现却答
|
|
128
|
+
「有没有凭据服务」——于是 env 来源的值(重启不丢)被标成 ephemeral。现在按来源
|
|
129
|
+
答:只有 `memory` 才真 ephemeral。
|
|
130
|
+
- **修 `parseRetryAfterMs` 漏英文单位**:只认中文单位,上游文案换成
|
|
131
|
+
`retry after 5 seconds` 就静默退化成「无时长」。补英文单位时还抓到一个**新引入的
|
|
132
|
+
30 倍退避差**——单位写成了非捕获组 `(?:...)`,`en[2]` 恒为 undefined,`2 minutes`
|
|
133
|
+
会被当成 2 秒。
|
|
134
|
+
- **修 HTML 响应被塞进面板**:`use-snapshot-polling` 的裸 `await response.json()`
|
|
135
|
+
在反向代理/登录墙回 HTML 时抛 SyntaxError,而 `errorText` 对 Error 返回
|
|
136
|
+
`.message`,于是那段 HTML 源码会显示在面板正文(同仓 `http.ts` 早有 `.catch`,
|
|
137
|
+
这里是漏)。
|
|
138
|
+
- 新增两个套件:`test/codes.test.mjs`(`classifyRateLimit` 此前**零测试覆盖**,所以
|
|
139
|
+
它能悄悄漂移)、`test/credentials.test.mjs`(红线 1 的专属门禁,此前也没有)。
|
|
140
|
+
后者逐档比对入口与脱敏闸**同宽**——这是防「进得来却遮不住」的关键不变量。
|
|
141
|
+
|
|
142
|
+
### 第一轮:漂移收口与门禁补强
|
|
143
|
+
|
|
144
|
+
- **修配置面漂移(会误导用户的那一处)**:`cordis.patch.yml` 仍写着「本地计数只覆盖
|
|
145
|
+
probe、不覆盖 provider 接入后的对话推理调用」——该说法在 `usage-observer` 落地时就
|
|
146
|
+
已作废,而这份文件正是操作者改配置时唯一在手边的文档。改为准确描述:计数经流出口
|
|
147
|
+
观察器覆盖**全部 DSH 魔搭调用**,并写明已知偏差方向是**少记**(peer 的重试在同一条
|
|
148
|
+
流内部重发)。
|
|
149
|
+
- **新增产物新鲜度门禁 `test/build-gate.mjs`**:`lib/` 与根 `client.js` 是故意入库的
|
|
150
|
+
产物(`github:` 安装源不跑 prepack),此前 `.gitignore` 里「后续接入」的 build-gate
|
|
151
|
+
并不存在——**改了`src/` 忘build,市场装到旧代码,而工作树干净、测试与typecheck 全绿,
|
|
152
|
+
没有任何东西会红**。判据是**内容哈希不是 mtime**(clone / 解压都会重写时间戳):重新
|
|
153
|
+
构建到系统临时目录,与入库产物逐字节比对,且**只读工作树**。已挂进 `npm test` 尾部。
|
|
154
|
+
- **删死配置 `dailyQuotaTotal` / `dailyQuotaPerModel`**:面板早已移除次数口径的推算条,
|
|
155
|
+
「M4 阈值提醒预留」的前提(M4)已落地且阈值提醒不存在。客户端对两者零消费,故连同
|
|
156
|
+
wire 的 `quota.daily.limit` / `remainingComputed` / `quota.perModelLimit` 一并收窄为
|
|
157
|
+
`quota.daily.usedLocal` 单字段。`test/config.test.mjs` 显式钉死「它们不回来」。
|
|
158
|
+
- **清悬空引用**:源码里 20+ 处引用本仓库不存在的 `PITFALLS §NN` / `docs/IMPROVEMENTS.md`、
|
|
159
|
+
以及一个本仓库没有的 Raccoon 插件的 bug 史。**保留知识、删掉死指针**——坑是什么仍写
|
|
160
|
+
在注释里,只是不再指向查不到的编号。
|
|
161
|
+
- **新增 CI**(`.github/workflows/gate.yml`):typecheck + 20 套件离线门禁。CI 是唯一能
|
|
162
|
+
抓到「提交了忘 build」的地方。`on` 加了引号(裸 `on` 被 YAML 1.1 解析成布尔 true,
|
|
163
|
+
工作流不报错但永不触发)。
|
|
164
|
+
|
|
165
|
+
## [0.1.0] — 2026-10-05
|
|
166
|
+
|
|
167
|
+
**首个发布版。**把魔搭社区(modelscope.cn)API-Inference 免费额度的本地用量面板接入
|
|
168
|
+
DeepSeek Harness 的 Plugins 页,并可选把魔搭注册为 DSH 的 LLM provider(id
|
|
169
|
+
`modelscope-token-plan`,直连 apiBase,OpenAI 兼容)——注册后魔搭模型进入 DSH 模型选择器,
|
|
170
|
+
可直接对话调用。
|
|
171
|
+
|
|
172
|
+
- **面板**(Plugins 页插件卡,三个 tab:额度 / 模型 / 接入):头条是官方魔粒余额(真实值),
|
|
173
|
+
下面接本地调用分布与趋势、429 事件流。zh+en 字典,Host 下发 cadence 的快照轮询(隐藏页
|
|
174
|
+
暂停、失败退避、generation guard)。
|
|
175
|
+
- **provider 接入**:模型 tab 顶部「接入为 DSH 模型」开关 + roster 勾选,默认关(opt-in)。
|
|
176
|
+
目录→pi-ai descriptor 映射、发布状态机(队列化 / `disposed` 闸 / `registerPair` 单点 +
|
|
177
|
+
rollback 恢复旧对)、面板开关持久层、429 自愈(QUOTA 快速失败 vs RATE_LIMIT 退避 +
|
|
178
|
+
peer 误判纠正层)。设计契约见 [docs/PROVIDER-M4.md](docs/PROVIDER-M4.md)。
|
|
179
|
+
- **本地计数接上真实对话**:新增 peer-free 的 `usage-observer` 观察 harness 流出口,一次遍历
|
|
180
|
+
同时拿到 token 与失败分类。**不重复造第二本账**——成功的 token 总量交给DSH 全局账本,本
|
|
181
|
+
插件只生产它答不了的两件:经本插件的分布/趋势、429/错误事件流。
|
|
182
|
+
- **能力分类器 `resolveModelCapability`**:二值「是不是 vision」升级为七档能力路由。判定走
|
|
183
|
+
详情端点 `Tasks[].Name` 精确任务标签,策展清单 `KNOWN_VISION_IDS`(14 条实测吃图模型)兜底,
|
|
184
|
+
全不认识则 `unknown`——**绝不猜**。
|
|
185
|
+
- **模型目录**:featured owner 置顶排序、逐行外链魔搭详情页(人工核对魔粒单价,非数据源)、
|
|
186
|
+
推荐 pill 取代整行染色高亮。删去与roster 并排重复的「模型目录」表与逐行「试调」按钮。
|
|
187
|
+
- **诊断工具** `tools/doctor.mjs`:只读盘点各 profile 的 usage.json 与 env 令牌在场性,绝不
|
|
188
|
+
写盘、绝不打印令牌值。抓「恒为 0 / 恒为空」这类不会报错的状态。
|
|
189
|
+
- **发版链路**:截图接入 `screenshots.json` 清单 + `files` 白名单;新增 `test/release.test.mjs`
|
|
190
|
+
(SCREENSHOTS / SHIPPED 两道门禁,防止市场取图裂图)。
|
|
191
|
+
- **测试**:15 套件离线门禁(wire / config / usage-store / routes / provider / inference-vision
|
|
192
|
+
/ provider-routes / panel / doctor / catalog / switch-precedence / retry / error-fix /
|
|
193
|
+
usage-observer / release),strict typecheck 与 build 全绿。真机冒烟:魔粒余额 143、模型目录
|
|
194
|
+
35、零漂移警告。
|
|
195
|
+
|
|
196
|
+
> **实施过程(病灶、取证、判据、「看起来能省但不能省」的取舍)记在
|
|
197
|
+
> [docs/IMPLEMENTATION.md](docs/IMPLEMENTATION.md)** —— 那几轮里有不少踩过的坑,照着做能
|
|
198
|
+
> 绕开,不知道就再踩一遍。
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 dsh-connect-modelscope-token-plan contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# dsh-connect-modelscope-token-plan
|
|
2
|
+
|
|
3
|
+
把魔搭社区(modelscope.cn)**API-Inference 免费额度**的本地用量面板接入 DeepSeek Harness 的 Plugins 页(插件卡内联,三个 tab:额度 / 模型 / 接入),并可选把魔搭注册成 **DSH 的 LLM provider**(id `modelscope-token-plan`,直连 `apiBase`,OpenAI 兼容)——注册后魔搭模型进入 DSH 模型选择器,可直接对话调用。见 [docs/PROVIDER-M4.md](docs/PROVIDER-M4.md)。
|
|
4
|
+
|
|
5
|
+
姊妹插件:`dsh-connect-sensenova-token-plan`、`dsh-connect-agnes-token-plan`(同族结构,受控复制)。
|
|
6
|
+
|
|
7
|
+
**状态:M4 已落地(provider 注册 + 面板「接入为 DSH 模型」),离线测试与 typecheck/build 全绿。**
|
|
8
|
+
|
|
9
|
+
## 面板长什么样
|
|
10
|
+
|
|
11
|
+
三个 tab:**额度 / 模型 / 接入**。
|
|
12
|
+
|
|
13
|
+
**「额度」tab**——头条是官方魔粒余额(真实值),下面接本地调用分布:
|
|
14
|
+
|
|
15
|
+

|
|
16
|
+
|
|
17
|
+
**「模型」tab**——一行一个魔搭模型,可勾选启用;开关打开即把魔搭注册为 DSH provider:
|
|
18
|
+
|
|
19
|
+

|
|
20
|
+
|
|
21
|
+
## 三条事实(写代码前先认清)
|
|
22
|
+
|
|
23
|
+
1. **官方有「魔粒」余额端点**:`GET {siteBase}/openapi/v1/magicubes/balance`(Bearer 访问令牌,匿名 401),返回 `{success, data:{total_balance, available_balance, frozen_amount}}`(2026-10-04 实测,见 [docs/SPIKE.md](docs/SPIKE.md))。推理响应本身仍不带任何额度头。
|
|
24
|
+
2. **本地计数是辅助口径**:面板头条是官方魔粒余额;本地计数只回答官方余额答不了的问题——按模型分布、本地趋势、429 事件流(只统计经本插件的调用,直连魔搭的其它客户端不计入)。次数口径的「推算剩余/参考上限」已从面板移除(官方改魔粒计费后两个单位并排是误导),相应的 `dailyQuotaTotal` / `dailyQuotaPerModel` 两个社区快照常数也从配置里一并删除了——面板不消费它们,留着只会让人以为能算「还剩几次」。
|
|
25
|
+
3. **凭据只有一把静态钥匙**:魔搭访问令牌(个人中心生成,形如 `ms-…`),无 OIDC、无密码、无 refresh。默认读 DSH 凭据服务已有的 `MODELSCOPE_API_KEY` 引用,`MODELSCOPE_API_KEY` 环境变量作回退;令牌永不入日志、永不进插件目录。
|
|
26
|
+
|
|
27
|
+
## 免费额度怎么算(2026-10 快照,以官方为准)
|
|
28
|
+
|
|
29
|
+
- 每日免费调用次数按账户计(当前约 2000 次/天,需绑定阿里云 + 实名;规则多次调整过);
|
|
30
|
+
- 单模型另有每日上限(社区数据约 500 次/天)与每分钟限频;
|
|
31
|
+
- 额度按天重置;耗尽返回 429。
|
|
32
|
+
- `/v1/models` 免认证可读,**不消耗额度**。
|
|
33
|
+
|
|
34
|
+
## 文档地图
|
|
35
|
+
|
|
36
|
+
| 何时 | 查 |
|
|
37
|
+
|---|---|
|
|
38
|
+
| 「上游到底有没有额度信号」 | [docs/SPIKE.md](docs/SPIKE.md)(实测记录 + 结论) |
|
|
39
|
+
| provider 怎么接入(写代码前) | [docs/PROVIDER-M4.md](docs/PROVIDER-M4.md)(实现契约) |
|
|
40
|
+
| **这版是怎么做出来的、踩过什么坑** | [docs/IMPLEMENTATION.md](docs/IMPLEMENTATION.md)(实施过程档案) |
|
|
41
|
+
| 上游端点全景 / 第三方方案对比 | [docs/REFERENCES.md](docs/REFERENCES.md)、[docs/REFERENCE-modelsdev.md](docs/REFERENCE-modelsdev.md) |
|
|
42
|
+
| v1 要做什么、做到哪了 | [docs/ROADMAP.md](docs/ROADMAP.md) |
|
|
43
|
+
| Host/Client 两半怎么分 | sensenova 仓库 `docs/ARCHITECTURE.md`(本仓库按同构落地后再补自己的) |
|
|
44
|
+
|
|
45
|
+
## 诚实声明
|
|
46
|
+
|
|
47
|
+
- 面板头条是**官方魔粒余额**(真实值);本地计数只统计**经本插件的调用**——它回答的是分布/趋势/429 事件流,不是余额,直连魔搭的其它客户端不计入。注册 provider 之后,DSH 的全部魔搭调用都经本插件,所以本地计数**覆盖全部 DSH 魔搭调用**。次数口径的「推算剩余」已从面板移除(官方改魔粒计费后两单位并排是误导),相应地`quota.daily` 只剩 `usedLocal` 一个数——没有 `limit`,也没有推算出的「剩余」。
|
|
48
|
+
- **本地计数的口径与 harness 全局账本不同,且刻意不合并**。DSH 自己有 `@linxin666/dsh-usage`,按 `days→provider→model` 记成功调用的 token;它只订阅 session 的 `assistant/message`,**失败调用一次都不记**。所以本插件只做两件全局账本答不了的事:①「经本插件的调用分布与趋势」;②**429/错误事件流**——这是本插件的独家数据(官方只给总额度余额,不给「哪个模型刚才在限频」)。成功的 token 总量不在此重复记账(详见 `src/host/usage-observer.ts` 文件头)。
|
|
49
|
+
- 429/错误事件的分类读的是 **`llm-error-fix` 重分类之后**的 code:被误判成 QUOTA 的 rpm 限频在事件流里记作 `rate_limit`,与退避策略说同一件事。观察层套在重分类层**之外**,顺序不可反。
|
|
50
|
+
- **计数口径**:每次流结束记一次 call,**含失败、含用户中断**(魔搭按次数计费,限频掉的请求同样是一次调用);token 数上游没给时是 `null` 而非 0。已知偏差方向是**少记**:peer 的重试在同一条流内部重发,观察器只看到一条流——宁可少记也不虚增。
|
|
51
|
+
- **provider 默认关**(opt-in,与姊妹插件一致):面板「模型」tab 的「接入为 DSH 模型」开关翻转即生效,面板保存值优先于 `cordis.patch.yml` 的 `registerProvider` 默认。
|
|
52
|
+
- **已知限制:`reasoning` 恒为 false**。魔搭 API-Inference 是多模型代理,是否吃 `reasoning_effort` 取决于背后那个模型;本插件无法离线得知每个 id 的档位表,保守默认不发该参数(模型用自己的默认),也不提供思考强度选择器。「宁可不选,不可错发」——发错档位会整条请求 400。见 [docs/PROVIDER-M4.md](docs/PROVIDER-M4.md) §4。
|
|
53
|
+
- 额度常数来自社区公开信息,非官方数据;官方调整后需要手动更新配置。
|
|
54
|
+
- 429 响应形状未实测(不值得烧额度去触发),解析按 OpenAI 惯例兼容,漂移时面板会给 `shapeWarnings`。
|
|
55
|
+
- 面板「模型」tab 的每个模型都外链到其魔搭详情页(`https://www.modelscope.cn/models/{owner}/{model}`):魔粒单价只在网页展示、不在官方 API,所以这只是一个**人工核对用的跳转,不是数据源**(「为什么没有按模型消耗 API」见 [docs/REFERENCES.md](docs/REFERENCES.md))。
|
|
Binary file
|
|
Binary file
|
|
Binary file
|