@zhz8888/dsh-radeon-cloud-patcher 1.1.0 → 1.2.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/README.md +310 -76
- package/client/client.js +62 -0
- package/cordis.plugin.patch.yml +179 -13
- package/package.json +18 -9
- package/scripts/apply-to-profile.mjs +45 -0
- package/scripts/sync-bundle-patch.mjs +98 -0
- package/src/index.js +198 -35
- package/src/ownership.js +148 -0
package/README.md
CHANGED
|
@@ -2,12 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
把 [AMD Radeon Cloud](https://amd-aim.github.io/radeon-cloud-docs/) 接入 DeepSeek Harness 的模型选择器,**完整支持模型思考(reasoning)功能**。
|
|
4
4
|
|
|
5
|
-
包名 `@zhz8888/dsh-radeon-cloud-patcher
|
|
6
|
-
`dsh-llm-pi-ai`(通用 OpenAI 兼容 provider)承载——**不修改 DSH
|
|
5
|
+
包名 `@zhz8888/dsh-radeon-cloud-patcher`。provider 定义由插件**自带的补丁层**声明,
|
|
6
|
+
由 DSH 自带的 `dsh-llm-pi-ai`(通用 OpenAI 兼容 provider)承载——**不修改 DSH 本体,
|
|
7
|
+
不需要自研适配器,也不往你的 profile 里写任何东西**。
|
|
7
8
|
|
|
8
|
-
> 之所以叫 patcher
|
|
9
|
-
>
|
|
10
|
-
>
|
|
9
|
+
> 之所以叫 patcher,是因为它靠**补丁层**交付:定义写在插件自己的
|
|
10
|
+
> `cordis.plugin.patch.yml` 里。这样做的直接后果有三个,也正是这个设计的目的——
|
|
11
|
+
>
|
|
12
|
+
> | 现象 | 原因 |
|
|
13
|
+
> | --- | --- |
|
|
14
|
+
> | 设置页**没有**「删除」按钮 | DSH 只在「该 provider 路径存在于用户层、且不存在于 base 层」时渲染它;定义在插件的 bundle 层,base 层永远有它 |
|
|
15
|
+
> | 卸载插件 = 删除供应商 | 定义只活在插件里,profile 中不留副本 |
|
|
16
|
+
> | 想删供应商只能卸载插件 | 同上;设置页里也点不到删除 |
|
|
11
17
|
|
|
12
18
|

|
|
13
19
|

|
|
@@ -15,11 +21,49 @@
|
|
|
15
21
|
|
|
16
22
|
---
|
|
17
23
|
|
|
24
|
+
## 快速上手
|
|
25
|
+
|
|
26
|
+
装好 DSH、手上有 Radeon Cloud 的 API 密钥(`rc-…`),三步就能用起来,
|
|
27
|
+
**不需要编辑任何配置文件**:
|
|
28
|
+
|
|
29
|
+
1. **装插件** —— 桌面版在左侧「插件」页里添加 `@zhz8888/dsh-radeon-cloud-patcher`;
|
|
30
|
+
命令行执行
|
|
31
|
+
`dsh plugin --profile <profile> add @zhz8888/dsh-radeon-cloud-patcher`。
|
|
32
|
+
装不上(pnpm 报 404)就改用[安装](#安装)里的方式三,从源码装同一个包。
|
|
33
|
+
2. **重启 DSH** —— 定义写在补丁层的 config 行里,而插件市场的**热挂载只支持纯 insert 行**,
|
|
34
|
+
因此安装后市场会提示「重启后生效」,这一步不能省。重启后 provider 就位,没有其它后置步骤。
|
|
35
|
+
3. **填密钥** —— 左下角「账号菜单 → 设置 → 模型」,找到 **Radeon Cloud** 一行,
|
|
36
|
+
点「编辑」,在「API 密钥」里粘贴密钥,点「保存」。
|
|
37
|
+
|
|
38
|
+
装好之后:那一行右侧出现**绿点**(悬停显示「API 密钥已配置」);回到对话,点输入框下方的
|
|
39
|
+
模型选择器,Radeon 的 8 个模型会出现在列表里;选中模型后再选「推理等级」就是思考档位。
|
|
40
|
+
|
|
41
|
+
**怎么确认真的装好了** —— 三个现象同时成立,说明插件与它的补丁层都到位:
|
|
42
|
+
|
|
43
|
+
| 现象 | 在哪看 |
|
|
44
|
+
| --- | --- |
|
|
45
|
+
| 模型选择器里出现 Radeon Cloud 的模型 | 输入框下方的模型选择器 |
|
|
46
|
+
| 那一行**没有**「删除」按钮 | 设置 → 模型 → Radeon Cloud 行 |
|
|
47
|
+
| 那一行**不带**「自定义」标签 | 同上 |
|
|
48
|
+
|
|
49
|
+
**没生效怎么办** —— 插件不会静默消失:定义失效时它**直接让启动失败并写明原因**
|
|
50
|
+
(profile 里已有同 id 定义、profile 的 `llm-pi-ai` 配置把插件层整份盖掉……),
|
|
51
|
+
逐条处置见[归属与删除](#归属与删除)。想手工核验真正生效的配置,用
|
|
52
|
+
`dsh --profile <profile> --dump-config`(`desktop` profile 例外,它由桌面应用独占管理)。
|
|
53
|
+
|
|
54
|
+
> 更细的安装方式(npm 源、镜像、GitHub、本地 tarball、手工清单)见[安装](#安装);
|
|
55
|
+
> 密钥、模型、档位、图片输入与升级卸载见[使用](#使用)。
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
18
59
|
## 目录
|
|
19
60
|
|
|
61
|
+
- [快速上手](#快速上手)
|
|
20
62
|
- [功能](#功能)
|
|
21
63
|
- [环境要求](#环境要求)
|
|
22
|
-
- [
|
|
64
|
+
- [安装](#安装)
|
|
65
|
+
- [使用](#使用)
|
|
66
|
+
- [归属与删除](#归属与删除)
|
|
23
67
|
- [项目结构](#项目结构)
|
|
24
68
|
- [常用命令](#常用命令)
|
|
25
69
|
- [可用模型](#可用模型)
|
|
@@ -76,8 +120,8 @@ Radeon 的响应体与流式分片都存在一个容易致错的形态:思考
|
|
|
76
120
|
|
|
77
121
|
| 项 | 要求 | 说明 |
|
|
78
122
|
| --- | --- | --- |
|
|
79
|
-
| DSH |
|
|
80
|
-
| Node.js |
|
|
123
|
+
| DSH | `>=0.2.0-rc.1`(含预发布,无上界) | 由 `peerDependencies` 声明范围,低于下界会被安装器拒绝;上界不写,DSH 升大版本不需要插件跟着重发(见[版本闸门](#版本闸门)) |
|
|
124
|
+
| Node.js | `>=22` | 与 `package.json` 的 `engines.node` 一致;仅运行 `scripts/` 与 `test/` 下的脚本时需要 |
|
|
81
125
|
| bash | 任意 | 仅 `scripts/radeon-api.sh` 需要 |
|
|
82
126
|
| 操作系统 | macOS | `scripts/validate-config.mjs` 按 DSH 桌面版应用包的绝对路径加载校验用的 schema,路径为 `/Applications/DSH Desktop.app/Contents/Resources/app/node_modules`。其余脚本与系统无关 |
|
|
83
127
|
|
|
@@ -85,88 +129,190 @@ Radeon 的响应体与流式分片都存在一个容易致错的形态:思考
|
|
|
85
129
|
|
|
86
130
|
---
|
|
87
131
|
|
|
88
|
-
##
|
|
132
|
+
## 安装
|
|
133
|
+
|
|
134
|
+
四种方式,任选其一。**装完都要重启 DSH**(原因见[快速上手](#快速上手)第 2 步):
|
|
135
|
+
定义由插件自带的补丁层声明,不写入你的 profile,重启后即生效,没有其它后置步骤。
|
|
89
136
|
|
|
90
|
-
###
|
|
137
|
+
### 方式一:DSH 插件管理器(推荐)
|
|
91
138
|
|
|
92
|
-
|
|
139
|
+
桌面版:应用左侧「插件」页 → 「添加插件」→ 输入包名或本地目录路径。
|
|
140
|
+
|
|
141
|
+
命令行:`dsh plugin --profile <profile> …` 会把参数**原样转发**给 profile 目录里的 pnpm,
|
|
142
|
+
因此 pnpm 支持的写法它都支持:
|
|
93
143
|
|
|
94
144
|
```bash
|
|
95
145
|
dsh plugin --profile <profile> add @zhz8888/dsh-radeon-cloud-patcher
|
|
146
|
+
dsh plugin --profile <profile> remove @zhz8888/dsh-radeon-cloud-patcher # 卸载
|
|
96
147
|
```
|
|
97
148
|
|
|
98
|
-
|
|
99
|
-
|
|
149
|
+
装完 `dsh.profile.bundles` 会自动多出本包名——插件管理器按各包的 `dsh.bundle` 声明维护它,
|
|
150
|
+
不必手改 profile 清单。
|
|
100
151
|
|
|
101
|
-
>
|
|
102
|
-
>
|
|
103
|
-
>
|
|
104
|
-
>
|
|
152
|
+
> **桌面版的 `desktop` profile 由应用独占管理**:在普通终端里执行
|
|
153
|
+
> `dsh plugin --profile desktop …` 会被拒绝(`profile "desktop" is managed exclusively by
|
|
154
|
+
> the Electron application`)。两条正路:用应用内的插件页安装,或在应用右上角
|
|
155
|
+
> 「打开 DSH 终端」里执行同样的命令。`web` / `tui` / `headless` 等 profile 不受此限。
|
|
105
156
|
>
|
|
106
|
-
>
|
|
107
|
-
> 是为了同时满足 DSH 的闸门判定与 npm/pnpm 的 peer 解析:node-semver 只有当范围里
|
|
108
|
-
> 某个比较符与该版本的 `major.minor.patch` 元组完全一致、且自身也带预发布标签时,
|
|
109
|
-
> 才会放行预发布版本。单写 `>=0.2.0-rc.1 <0.3.0-0` 会把 `0.2.1-alpha.1` 这类
|
|
110
|
-
> **不同元组**的预发布静默排除。
|
|
157
|
+
> 若你的源上还没有这个包(pnpm 报 404),用方式三装同一个包。
|
|
111
158
|
|
|
112
|
-
###
|
|
159
|
+
### 方式二:从 npm 源安装
|
|
113
160
|
|
|
114
|
-
|
|
115
|
-
由 `llm-pi-ai` 承载。这一步是**按键合并**,不是整体替换:
|
|
161
|
+
方式一走的就是 npm registry;换源只需给 pnpm 传参,或写进 profile 自己的 `.npmrc`:
|
|
116
162
|
|
|
117
163
|
```bash
|
|
118
|
-
#
|
|
119
|
-
|
|
164
|
+
# 单次指定镜像源
|
|
165
|
+
dsh plugin --profile <profile> add @zhz8888/dsh-radeon-cloud-patcher \
|
|
166
|
+
--registry https://registry.npmmirror.com
|
|
120
167
|
|
|
121
|
-
#
|
|
122
|
-
|
|
168
|
+
# 或给这个 profile 固定源(.npmrc 是 pnpm 自己的配置文件)
|
|
169
|
+
printf 'registry=https://registry.npmmirror.com\n' >> ~/.dsh/profiles/<profile>/.npmrc
|
|
170
|
+
|
|
171
|
+
# 指定版本或范围(pnpm 语法)
|
|
172
|
+
dsh plugin --profile <profile> add @zhz8888/dsh-radeon-cloud-patcher@<版本>
|
|
123
173
|
```
|
|
124
174
|
|
|
125
|
-
|
|
175
|
+
> 本包的 tarball 里只有 JSON / YAML / JS,**没有安装期构建脚本**,
|
|
176
|
+
> 因此换源、离线镜像、内网私服都不会碰到 pnpm 的 `allowBuilds` 授权那一步。
|
|
126
177
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
178
|
+
### 方式三:从源码 / GitHub 安装
|
|
179
|
+
|
|
180
|
+
三种源码形态都直接可用,原因同上:本项目零运行时依赖、没有构建步骤。
|
|
181
|
+
|
|
182
|
+
```bash
|
|
183
|
+
git clone https://github.com/zhz8888/dsh-radeon-cloud-patcher.git
|
|
184
|
+
|
|
185
|
+
# ① 本地目录:pnpm 做 link,仓库里改了代码重启 DSH 即生效(开发本插件时最省事)
|
|
186
|
+
dsh plugin --profile <profile> add /绝对路径/dsh-radeon-cloud-patcher
|
|
187
|
+
|
|
188
|
+
# ② git 依赖:直接装公开仓库
|
|
189
|
+
dsh plugin --profile <profile> add github:zhz8888/dsh-radeon-cloud-patcher
|
|
190
|
+
|
|
191
|
+
# ③ 本地 tarball:适合内网分发(npm pack 在仓库目录里执行)
|
|
192
|
+
npm pack
|
|
193
|
+
dsh plugin --profile <profile> add ./zhz8888-dsh-radeon-cloud-patcher-<版本>.tgz
|
|
132
194
|
```
|
|
133
195
|
|
|
134
|
-
>
|
|
135
|
-
>
|
|
136
|
-
>
|
|
137
|
-
|
|
196
|
+
> DSH 对「装 git 依赖」有一句提醒:带 `prepare` 脚本的插件会在安装期构建,
|
|
197
|
+
> 而 pnpm 默认拦住构建脚本,需要先在 profile 的 `pnpm-workspace.yaml` 里追加
|
|
198
|
+
> `allowBuilds` 才能装上。**本插件不属于这种情况**——它没有任何安装期脚本。
|
|
199
|
+
|
|
200
|
+
### 方式四:手工改 profile 清单
|
|
201
|
+
|
|
202
|
+
不用插件管理器时:
|
|
203
|
+
|
|
204
|
+
1. 在 profile 的 `package.json` 里把本包加进 `dependencies`;
|
|
205
|
+
2. 把包名追加进 `dsh.profile.bundles`(顺序即补丁层顺序,放在 `@deepseek-ai/dsh-base`
|
|
206
|
+
与 `@deepseek-ai/dsh-web-app` 之后即可);
|
|
207
|
+
3. 执行 `dsh plugin --profile <profile> install` 安装依赖。
|
|
208
|
+
|
|
209
|
+
### 版本闸门
|
|
210
|
+
|
|
211
|
+
本包的 DSH 兼容范围是 **`>=0.2.0-rc.1`——只有下界,没有上界**,
|
|
212
|
+
三处声明逐字一致(`peerDependencies`、`engines.dsh`、`dsh.compatibility.dsh`,
|
|
213
|
+
由 `pnpm test:manifest` 断言):
|
|
214
|
+
|
|
215
|
+
- DSH 安装器读的是 `peerDependencies`:运行时版本低于下界 → **直接拒绝安装**,
|
|
216
|
+
不会出现「装上了但思考功能失效」;
|
|
217
|
+
- **上界故意不写**:写死上界意味着 DSH 每升一个大版本都要重发一版插件去放宽范围,
|
|
218
|
+
代价大于收益。换来的代价是失去「未来版本被挡住」这层保护——改由插件自己的
|
|
219
|
+
响亮失败兜底(定义校验、id 归属判定、启动收尾核验路由是否注册,
|
|
220
|
+
见[归属与删除](#归属与删除));
|
|
221
|
+
- `@deepseek-ai/cordis` 同理放宽为 `>=4.0.2`:它**不参与**安装闸门(判定只看
|
|
222
|
+
`@deepseek-ai/dsh*` 开头的包),但它是 pnpm 的 peer,写死上界同样会在它升大版本时
|
|
223
|
+
把安装卡住。
|
|
224
|
+
|
|
225
|
+
> **预发布的一个细节**:node-semver 规定,带预发布标签的版本只有在范围里存在
|
|
226
|
+
> 「元组相同、且自身也带预发布标签」的比较符时才被放行。
|
|
227
|
+
> `>=0.2.0-rc.1` 因此只覆盖 `0.2.0` 这一元组的预发布(`0.2.0-rc.2` ✓);
|
|
228
|
+
> `0.2.1-alpha.1`、`0.3.0-beta.1` 这类**新元组的预发布**在 npm/pnpm 的默认规则下
|
|
229
|
+
> 不被放行(DSH 自己的闸门带 `includePrerelease`,会放行——两者判定不一致时,
|
|
230
|
+
> 失败的是 pnpm 那一步)。真遇到时二选一:
|
|
231
|
+
>
|
|
232
|
+
> ```bash
|
|
233
|
+
> # ① 给该元组补一条分支,写回范围(例如到 0.2.1 的预发布)
|
|
234
|
+
> # ">=0.2.0-rc.1 <0.2.1-0 || >=0.2.1-0"
|
|
235
|
+
>
|
|
236
|
+
> # ② 或让 pnpm 放宽 peer 校验(参数照样原样转发)
|
|
237
|
+
> dsh plugin --profile <profile> add @zhz8888/dsh-radeon-cloud-patcher \
|
|
238
|
+
> --config.strict-peer-dependencies=false
|
|
239
|
+
> ```
|
|
240
|
+
>
|
|
241
|
+
> 稳定版(`0.2.0`、`0.2.1`、`0.3.0`、`1.0.0`…)不受这个细节影响,一律放行。
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## 使用
|
|
138
246
|
|
|
139
|
-
###
|
|
247
|
+
### 1. 配置 API 密钥
|
|
140
248
|
|
|
141
|
-
|
|
249
|
+
密钥从 [AMD Radeon Cloud 文档](https://amd-aim.github.io/radeon-cloud-docs/) 里的入口申请
|
|
250
|
+
(本项目只覆盖 Public Free Model APIs 的共享端点)。拿到 `rc-…` 之后二选一:
|
|
142
251
|
|
|
143
|
-
|
|
252
|
+
**方式一:设置页(推荐给日常使用)**
|
|
144
253
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
254
|
+
左下角「账号菜单 → 设置 → 模型」→ Radeon Cloud 行 →「编辑」→ 在「API 密钥」里粘贴
|
|
255
|
+
`rc-…` →「保存」。保存成功后该行右侧出现**绿点**(悬停显示「API 密钥已配置」;
|
|
256
|
+
没配密钥时是红点,提示「API 密钥缺失」)。
|
|
257
|
+
|
|
258
|
+
密钥写进本机凭据存储 `~/.dsh/.credentials.yaml` 的 `RADEON_CLOUD_API_KEY` 条目,
|
|
259
|
+
明文不落配置文件、界面不回显;要换密钥就再次「编辑」并输入新值
|
|
260
|
+
(输入框会提示「已配置——输入新值可替换」)。`scripts/` 下的脚本读的是同一个条目,
|
|
261
|
+
因此在设置页填过一次,脚本也就不用再配了。
|
|
262
|
+
|
|
263
|
+
**方式二:环境变量(不落盘,适合临时试用与 CI)**
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
export RADEON_CLOUD_API_KEY=rc-你的密钥
|
|
267
|
+
```
|
|
148
268
|
|
|
149
|
-
|
|
269
|
+
凭据服务按「**继承的进程环境** → 插件写入的凭据存储 → 当前目录 `.env` → `$DSH_HOME/.env`」
|
|
270
|
+
依次查找,环境变量优先级最高,一次性的写法也有效:
|
|
150
271
|
|
|
151
272
|
```bash
|
|
152
|
-
|
|
273
|
+
RADEON_CLOUD_API_KEY=rc-你的密钥 dsh --profile <profile>
|
|
153
274
|
```
|
|
154
275
|
|
|
155
|
-
|
|
276
|
+
> 注意名字:**插件运行时**读的是 `RADEON_CLOUD_API_KEY`(即 provider 定义里的
|
|
277
|
+
> `apiKeyEnv`),而**仓库脚本**用的是 `RADEON_API_KEY`。两者互不影响,
|
|
278
|
+
> 脚本那一套见下面的表格。
|
|
279
|
+
|
|
280
|
+
### 2. 选模型与思考档位
|
|
281
|
+
|
|
282
|
+
对话输入框下方点模型选择器 → 选 Radeon Cloud 的模型 → 再选「推理等级」,
|
|
283
|
+
那就是思考档位。档位按模型逐个声明(见[可用模型](#可用模型)):
|
|
284
|
+
GLM 与 Qwen 系不接受 `none`,因此没有关闭档位;`MiniCPM5-2B` 不是推理模型,
|
|
285
|
+
不会出现档位下拉。选了服务端不接受的档位是**硬失败**(400 / 422),不会静默降级。
|
|
286
|
+
|
|
287
|
+
### 3. 图片输入
|
|
288
|
+
|
|
289
|
+
支持图片:DeepSeek-V4.1-Flash、DeepSeek-V4-Flash-Vision-Exp、MiMo-V2.6-Flash、
|
|
290
|
+
Qwen3.8-27B、Qwen3.8-Flash-Next。只收文本:DeepSeek-V4-Flash、GLM-5.3-Flash、MiniCPM5-2B。
|
|
291
|
+
|
|
292
|
+
### 4. 升级与卸载
|
|
156
293
|
|
|
157
294
|
```bash
|
|
158
|
-
|
|
295
|
+
dsh plugin --profile <profile> add @zhz8888/dsh-radeon-cloud-patcher@latest # 升级
|
|
296
|
+
dsh plugin --profile <profile> remove @zhz8888/dsh-radeon-cloud-patcher # 卸载
|
|
159
297
|
```
|
|
160
298
|
|
|
161
|
-
|
|
162
|
-
|
|
299
|
+
升级不需要额外动作:定义由插件层声明,装上新版本重启即生效
|
|
300
|
+
(在市场里升级同样会提示「重启后生效」,原因与安装一致)。
|
|
301
|
+
**卸载即删除供应商**——插件层消失,provider 与设置页那一行一起消失。
|
|
302
|
+
|
|
303
|
+
> 例外:如果你曾用 `pnpm install:profile` 把定义合并进 profile(见[归属与删除](#归属与删除)),
|
|
304
|
+
> profile 里那份副本会**盖住**插件层的新定义——升级后要么重跑一次
|
|
305
|
+
> `pnpm install:profile`,要么删掉那份副本,让插件独占管理。
|
|
306
|
+
|
|
307
|
+
### 5. 仓库脚本用的环境变量
|
|
163
308
|
|
|
164
|
-
|
|
309
|
+
下面这些只影响仓库里的 `scripts/`(`radeon-api.sh`、`probe-efforts.mjs`),
|
|
310
|
+
与插件运行时无关:
|
|
165
311
|
|
|
166
312
|
| 变量 | 作用 | 默认值 |
|
|
167
313
|
| --- | --- | --- |
|
|
168
|
-
| `RADEON_API_KEY` |
|
|
169
|
-
| `RADEON_KEY_REF` |
|
|
314
|
+
| `RADEON_API_KEY` | 脚本用的密钥字面值,优先级高于凭据文件 | 无 |
|
|
315
|
+
| `RADEON_KEY_REF` | 脚本从凭据文件读取时用的条目名 | `RADEON_CLOUD_API_KEY` |
|
|
170
316
|
| `RADEON_BASE` | 端点基础 URL,可指向独占端点 | `https://developer.amd.com.cn/radeon/api/v1` |
|
|
171
317
|
| `DSH_HOME` | DSH 数据目录,凭据文件据此定位 | `~/.dsh` |
|
|
172
318
|
|
|
@@ -175,28 +321,79 @@ RADEON_API_KEY=rc-你的密钥 ./scripts/radeon-api.sh GET /models
|
|
|
175
321
|
|
|
176
322
|
---
|
|
177
323
|
|
|
324
|
+
## 归属与删除
|
|
325
|
+
|
|
326
|
+
本插件要求 `radeon-cloud` 这个 id **由它独占**,因此:
|
|
327
|
+
|
|
328
|
+
- 设置页那一行**不会出现「删除」按钮**。DSH 的判据是「该 provider 路径存在于用户层、
|
|
329
|
+
且不存在于 base 层」——定义写在插件的 bundle 层,base 层永远有它,按钮因此永不渲染;
|
|
330
|
+
- **删除供应商 = 卸载插件**。卸载后插件层消失,provider 随之消失。
|
|
331
|
+
若你曾用 `pnpm install:profile` 把定义合并进 profile,profile 里会留下一份副本
|
|
332
|
+
(那份副本的所有权归你,卸载后设置页里也会重新出现删除按钮,可自行清理);
|
|
333
|
+
- **「自定义」标签**:DSH 对**任何不在内置模型目录里的 provider** 硬编码了这个标记
|
|
334
|
+
(`dsh-llm-pi-ai` 的 `declared: !catalog.has(provider)`),配置层面无从去掉。
|
|
335
|
+
本插件随包带了一个极小的客户端补丁(`client/client.js`),用一条 CSS 规则把
|
|
336
|
+
**本 provider 那一行**的标签隐藏掉:
|
|
337
|
+
|
|
338
|
+
```css
|
|
339
|
+
li:has(button[aria-label*="(radeon-cloud)"]) span[class*="rowTag"] { display: none }
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
代价说清楚:它依赖 DSH 的 DOM 结构与类名片段,DSH 改版后可能静默失效——
|
|
343
|
+
标签重新出现,功能不受影响。
|
|
344
|
+
|
|
345
|
+
### 定义归谁管:启动时的判定
|
|
346
|
+
|
|
347
|
+
DSH 的非 insert 补丁是**整体替换**:`llm-pi-ai` 的 config 只要在 profile 里出现过,
|
|
348
|
+
就会整份盖掉插件层的定义。插件启动时静态读 profile / home / 命令行 overlay 三层,
|
|
349
|
+
判定这个 id 现在由谁说了算:
|
|
350
|
+
|
|
351
|
+
| 情况 | 结果 |
|
|
352
|
+
| --- | --- |
|
|
353
|
+
| 没人碰过 `llm-pi-ai` 的 config | 插件层的定义生效,正常启动 |
|
|
354
|
+
| profile 里有一份**与本插件一致**的同 id 声明 | 放行,并提示它会在卸载后残留(通常是你在设置页保存过一次、被物化出的副本) |
|
|
355
|
+
| profile 里有一份**与之不一致**的同 id 声明 | **启动失败**:报「provider id 冲突」,并指名第一处差异字段 |
|
|
356
|
+
| profile 给 `llm-pi-ai` 写了 config 却没有本 provider | **启动失败**:报「定义未生效」,并打印可直接执行的合并命令 |
|
|
357
|
+
|
|
358
|
+
后两种是**失败 + 原因**,而不是静默接管或静默消失——这是本项目的取舍:id 冲突要响亮。
|
|
359
|
+
处置方式(按键合并、`--force`、输出示例)见[已知限制](#6-与用户自有-provider-共存)。
|
|
360
|
+
|
|
361
|
+
> 想彻底消除标签、不留 DOM 依赖,只能让插件自己注册 provider 路由,也就意味着自己实现
|
|
362
|
+
> 传输、思考解析与用量计量。本项目定位是「薄壳 + 由 DSH 自带的 pi-ai 承载」,
|
|
363
|
+
> 因此选择了 CSS 隐藏这条路。
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
178
367
|
## 项目结构
|
|
179
368
|
|
|
180
369
|
```
|
|
181
370
|
dsh-radeon-cloud-patcher/
|
|
182
371
|
├── README.md 本文件
|
|
372
|
+
├── AGENTS.md 智能体与协作者指引
|
|
183
373
|
├── LICENSE MIT 许可证
|
|
184
|
-
├── package.json
|
|
185
|
-
├── cordis.plugin.patch.yml bundle
|
|
374
|
+
├── package.json 插件包清单(版本闸门、导出、客户端声明、脚本)
|
|
375
|
+
├── cordis.plugin.patch.yml bundle 补丁:provider 定义 + 本插件行
|
|
376
|
+
├── pnpm-workspace.yaml 本仓库的安装期构建授权(不影响用户安装)
|
|
377
|
+
├── pnpm-lock.yaml 依赖锁文件
|
|
186
378
|
├── .gitignore
|
|
187
379
|
│
|
|
188
380
|
├── provider/ 配置本体
|
|
189
381
|
│ └── radeon-cloud.yml provider 定义的唯一真源
|
|
190
382
|
│
|
|
383
|
+
├── client/ 客户端补丁
|
|
384
|
+
│ └── client.js 隐藏本行的「自定义」标签(DSH 客户端模块格式,非 ESM)
|
|
385
|
+
│
|
|
191
386
|
├── src/ 插件实现(薄壳,不含 provider 逻辑)
|
|
192
|
-
│ ├── index.js
|
|
193
|
-
│ ├──
|
|
387
|
+
│ ├── index.js 插件入口:启动校验、所有权判定、收尾核验
|
|
388
|
+
│ ├── ownership.js 所有权判定的纯函数(四种结局与差异定位)
|
|
389
|
+
│ ├── merge.js 键级合并的纯函数(补救路径使用)
|
|
194
390
|
│ ├── patch-text.js 文本级合并,保留用户注释与格式
|
|
195
391
|
│ ├── validate.js 定义的结构校验
|
|
196
392
|
│ └── yaml.js 定位 YAML 解析器
|
|
197
393
|
│
|
|
198
394
|
├── scripts/ 运维与校验工具
|
|
199
|
-
│ ├──
|
|
395
|
+
│ ├── sync-bundle-patch.mjs 把定义真源渲染进补丁的生成区块
|
|
396
|
+
│ ├── apply-to-profile.mjs 补救路径:按键合并进 profile(冲突时拒绝覆盖)
|
|
200
397
|
│ ├── validate-config.mjs 用 llm-pi-ai 真实 schema 校验定义
|
|
201
398
|
│ ├── probe-efforts.mjs 实测逐模型 reasoning_effort 档位表
|
|
202
399
|
│ └── radeon-api.sh API 调用助手(密钥取自环境变量或凭据存储)
|
|
@@ -204,6 +401,10 @@ dsh-radeon-cloud-patcher/
|
|
|
204
401
|
├── test/ 离线测试
|
|
205
402
|
│ ├── merge.test.mjs 合并语义与幂等性
|
|
206
403
|
│ ├── patch-text.test.mjs 不破坏用户 provider 与注释
|
|
404
|
+
│ ├── ownership.test.mjs 四种所有权结局与差异定位
|
|
405
|
+
│ ├── apply-guard.test.mjs 启动看护:放行、或失败并说明原因
|
|
406
|
+
│ ├── patch-sync.test.mjs 补丁与定义真源一致、结构符合「无删除按钮」
|
|
407
|
+
│ ├── client-patch.test.mjs 客户端补丁注入的规则
|
|
207
408
|
│ ├── verify-reasoning.mjs 思考文本取回的回归测试
|
|
208
409
|
│ └── fixtures/
|
|
209
410
|
│ └── stream-reasoning.sse 实测抓取的真实流
|
|
@@ -213,28 +414,35 @@ dsh-radeon-cloud-patcher/
|
|
|
213
414
|
├── feasibility-assessment.md 可行性评估与线格式差异矩阵
|
|
214
415
|
├── implementation-plan.md 实施计划与检查点结论
|
|
215
416
|
├── live-verification.md 真机联调实测记录
|
|
216
|
-
|
|
417
|
+
├── reference-commandcode-provider.md 第三方 provider 插件参考实现分析
|
|
418
|
+
└── market-submission.md 插件市场投稿说明
|
|
217
419
|
```
|
|
218
420
|
|
|
219
|
-
> 插件壳只做四件事:**分发**(npm
|
|
220
|
-
>
|
|
221
|
-
>
|
|
222
|
-
> 实际的传输、思考字段解析、多轮回传、用量计量仍全部由 DSH 自带的
|
|
223
|
-
> `llm-pi-ai` 承担。
|
|
421
|
+
> 插件壳只做四件事:**分发**(npm 包,可被插件市场安装)、**声明**(provider 定义写在
|
|
422
|
+
> 自带的补丁层里,profile 只读不改)、**版本闸门**(版本不匹配直接拒装)、
|
|
423
|
+
> **看护**(启动时校验定义、判定 id 归属,不该继续就让插件启动失败并说明原因)。
|
|
424
|
+
> 实际的传输、思考字段解析、多轮回传、用量计量仍全部由 DSH 自带的 `llm-pi-ai` 承担。
|
|
224
425
|
|
|
225
426
|
---
|
|
226
427
|
|
|
227
428
|
## 常用命令
|
|
228
429
|
|
|
229
430
|
```bash
|
|
230
|
-
#
|
|
431
|
+
# 全量测试(合并语义、所有权判定、启动看护、补丁一致性、思考字段取回)
|
|
231
432
|
pnpm test
|
|
232
433
|
|
|
434
|
+
# 只跑启动看护(四种所有权结局)与补丁一致性
|
|
435
|
+
pnpm test:guard
|
|
436
|
+
pnpm test:patch
|
|
437
|
+
|
|
233
438
|
# 校验 provider 定义:结构校验 + llm-pi-ai 真实 schema 校验
|
|
234
439
|
# 这是判断「字段在当前 DSH 版本里是否仍然有效」的那道校验,建议升级 DSH 后跑一次
|
|
235
440
|
pnpm validate
|
|
236
441
|
|
|
237
|
-
#
|
|
442
|
+
# 改完 provider/radeon-cloud.yml 后,把定义同步进补丁的生成区块
|
|
443
|
+
pnpm sync:patch
|
|
444
|
+
|
|
445
|
+
# 补救路径:把定义按键合并进 profile(冲突时拒绝覆盖,--force 才覆盖)
|
|
238
446
|
pnpm install:profile:dry
|
|
239
447
|
|
|
240
448
|
# 重新实测某个模型支持的思考档位
|
|
@@ -311,25 +519,51 @@ DSH 将其归为可重试的 `SERVER`。偶发,重试即可。
|
|
|
311
519
|
|
|
312
520
|
### 6. 与用户自有 provider 共存
|
|
313
521
|
|
|
314
|
-
provider
|
|
315
|
-
|
|
522
|
+
provider 定义由插件的 bundle 层声明,插件**只读 profile、不写 profile**。
|
|
523
|
+
DSH 的非 insert 补丁是整体替换,因此你自己写的 `llm-pi-ai` config 会整份盖掉插件层的定义:
|
|
524
|
+
插件启动时会判定这件事,响亮失败并给出处置,而不是让 provider 无声消失
|
|
525
|
+
(判据见[归属与删除](#归属与删除))。
|
|
526
|
+
|
|
527
|
+
想与自有 provider 共存,就用补救命令做按键合并:
|
|
528
|
+
|
|
529
|
+
```bash
|
|
530
|
+
# 在插件仓库里
|
|
531
|
+
pnpm install:profile:dry # 演练:打印改动与被保留的其它 provider
|
|
532
|
+
pnpm install:profile # 写入(自动备份,断言其余条目与同级键逐字未变)
|
|
533
|
+
|
|
534
|
+
# 只装了插件、没有克隆仓库时,脚本在包里;启动失败信息会直接打印完整命令
|
|
535
|
+
node ~/.dsh/profiles/<profile>/node_modules/@zhz8888/dsh-radeon-cloud-patcher/scripts/apply-to-profile.mjs \
|
|
536
|
+
~/.dsh/profiles/<profile>/cordis.patch.yml
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
输出示例:
|
|
540
|
+
|
|
541
|
+
```
|
|
542
|
+
【写入】 ~/.dsh/profiles/desktop/cordis.patch.yml
|
|
543
|
+
新增 providers.radeon-cloud(8 个模型)
|
|
544
|
+
保留的其它 provider:my-corp-proxy, another-one
|
|
545
|
+
顶层条目 12 个,增减 0 个
|
|
546
|
+
```
|
|
316
547
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
`llm-pi-ai`
|
|
548
|
+
合并粒度是键:只新增或替换 `providers.radeon-cloud` 这一个键,你添加的其它 provider、
|
|
549
|
+
你写的注释、缩进风格与键序都原样保留(文本级编辑,不重新序列化你的文件)。
|
|
550
|
+
写入前会断言:除 `radeon-cloud` 外的同级 provider 键集合一致、`llm-pi-ai` 之外的顶层条目
|
|
551
|
+
逐字符未变,任一不成立即中止且不落盘;已存在不一致的同 id 定义时**拒绝覆盖**并打印第一处
|
|
552
|
+
差异,确认要覆盖再加 `--force`。
|
|
320
553
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
请只改你需要的那个字段,不要整块替换 `config`。
|
|
554
|
+
另有一处要注意:**不要另写一条 `llm-pi-ai` 的非 insert 补丁**去改该行的其它字段——
|
|
555
|
+
那会整体替换这一行,把 `providers` 一并带走。要改就只改你需要的那一个键。
|
|
324
556
|
|
|
325
557
|
---
|
|
326
558
|
|
|
327
559
|
## 兼容性
|
|
328
560
|
|
|
329
|
-
- 目标 DSH
|
|
561
|
+
- 目标 DSH 版本:`>=0.2.0-rc.1`,**无上界**(三处声明一致,见[版本闸门](#版本闸门))
|
|
330
562
|
- 模型目录与档位表采集日期:**2026-10-04**
|
|
331
563
|
- 收录的模型当前均为 `stability: experimental`
|
|
332
564
|
- 仅覆盖 Public Free Model APIs(共享端点);独占端点的基础 URL 每次实例重启都会变化,不在本项目范围内
|
|
565
|
+
- 客户端补丁依赖 DSH 模型设置页的 DOM 结构(`aria-label` 中的 provider 路由名 + `rowTag` 类名片段)。
|
|
566
|
+
DSH 改版后它会静默失效——「自定义」标签重新出现,功能不受影响
|
|
333
567
|
|
|
334
568
|
---
|
|
335
569
|
|
package/client/client.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 客户端补丁:把「设置 → 模型」里本插件那一行的「自定义」标签藏掉。
|
|
3
|
+
*
|
|
4
|
+
* 为什么需要它:dsh-llm-pi-ai 对「不在内置模型目录中的 provider」硬编码
|
|
5
|
+
* `declared: !catalog.has(provider)`,而 dsh-client-ui-settings-models 只看到这个
|
|
6
|
+
* 标记就渲染「自定义」标签。本插件的 provider 必然落在这一支,配置层面无从去掉。
|
|
7
|
+
*
|
|
8
|
+
* 做法是纯 CSS,不碰 DSH 的任何模块、不做 DOM 写入:
|
|
9
|
+
*
|
|
10
|
+
* li:has(button[aria-label*="(radeon-cloud)"]) span[class*="rowTag"] { display: none }
|
|
11
|
+
*
|
|
12
|
+
* 判据取自 DSH 自己拼的 aria-label(`编辑 Radeon Cloud (radeon-cloud)`)——它与
|
|
13
|
+
* 界面语言无关,只随 provider 路由名变化;`rowTag` 是 CSS Modules 源类名,会以
|
|
14
|
+
* 片段形式保留在哈希类名里。
|
|
15
|
+
*
|
|
16
|
+
* 代价说清楚:这依赖 DSH 的 DOM 结构与类名片段。DSH 改版后它会静默失效——
|
|
17
|
+
* 标签重新出现,但功能不受影响;本行「删除」按钮则根本不由本补丁负责,
|
|
18
|
+
* 它由 provider 定义落在补丁层这件事本身永久消除(见 cordis.plugin.patch.yml)。
|
|
19
|
+
*
|
|
20
|
+
* 文件格式注意:本文件不是 ESM,而是 DSH 客户端模块的包装格式
|
|
21
|
+
* (window.__ModuleLoader__.load),因此不要把它 import 进任何 Node 代码。
|
|
22
|
+
* test/client-patch.test.mjs 用 node:vm 在桩环境里跑它。
|
|
23
|
+
*/
|
|
24
|
+
window.__ModuleLoader__.load({
|
|
25
|
+
// 必须等于 package.json 的 name —— DSH 按包名给客户端模块建表,写错会「加载了却没注册」。
|
|
26
|
+
id: '@zhz8888/dsh-radeon-cloud-patcher',
|
|
27
|
+
factory: (require) => {
|
|
28
|
+
var module = { exports: {} }
|
|
29
|
+
var exports = module.exports
|
|
30
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' })
|
|
31
|
+
|
|
32
|
+
/** 本插件负责的 provider 路由名。 */
|
|
33
|
+
var PROVIDER = 'radeon-cloud'
|
|
34
|
+
/** 注入的样式元素 id,便于重复加载时复用而非叠加。 */
|
|
35
|
+
var STYLE_ID = 'dsh-radeon-cloud-hide-custom-tag'
|
|
36
|
+
/** 隐藏标签的规则;只匹配带本 provider 路由名的行。 */
|
|
37
|
+
var CSS = 'li:has(button[aria-label*="(' + PROVIDER + ')"]) span[class*="rowTag"]{display:none !important}'
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* 注入样式表。用 ctx.effect 登记,插件卸载时一并移除。
|
|
41
|
+
*
|
|
42
|
+
* @param ctx 客户端插件上下文
|
|
43
|
+
*/
|
|
44
|
+
function apply(ctx) {
|
|
45
|
+
if (typeof document === 'undefined') return
|
|
46
|
+
ctx.effect(function () {
|
|
47
|
+
var existing = document.getElementById(STYLE_ID)
|
|
48
|
+
var style = existing === null ? document.createElement('style') : existing
|
|
49
|
+
style.id = STYLE_ID
|
|
50
|
+
style.textContent = CSS
|
|
51
|
+
if (existing === null) document.head.append(style)
|
|
52
|
+
return function () {
|
|
53
|
+
style.remove()
|
|
54
|
+
}
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
exports.name = 'radeon-cloud-client'
|
|
59
|
+
exports.apply = apply
|
|
60
|
+
return module.exports
|
|
61
|
+
},
|
|
62
|
+
})
|