dsh-model-priority
自定义模型 / 提供方的排列顺序。宿主的模型选择列表按适配器注册顺序排,常用的模型 常常排在后面;这个插件让你在模型设置页里拖一下,把它顶到前面去。
长什么样
设置 → 模型页里,每张提供方卡片下面多一块「模型顺序」区(走上游槽位
settings.models.provider-card,不用另开页面,也不需要装 dsh-better-sidebar)。
默认折叠,展开后是分组后的模型列表,按住行拖动排序:点「保存」写盘,
点「恢复默认」回到宿主原顺序。
它怎么生效
- 顺序存在
~/.dsh/model-order.json,纯数据,可以手改,删掉就回默认。 - 服务端半边在
ctx.llm的实例属性上包一层,让这三个方法按这份顺序返回:listProviders()、listConfigurableProviders()、listModels(provider)。 排序是稳定排序:没被点过名的条目保持原来的相对位置,所以只调两个不会搅乱别的。 - 为什么是替换实例属性:宿主的远端分发拿的是
Reflect.get(活实例, 'listProviders')(dsh-api-gateway的prepareInvocation),own property 优先,所以这一处改动 同时覆盖直调方与远端两条路。模型选择对话框走的session/modelCatalog链内部 同步调ctx.llm.listProviders()、异步调ctx.llm.listModels(),都在这条路上。 - 三个方法各有一处必须守住的契约(写错了是直接报错,不是静默降级):
listProviders/listConfigurableProviders的替换必须是同步函数。 宿主buildModelCatalog同步调用它,返回 Promise 会在紧接着的providers.map(...)上炸掉,模型选择对话框直接打不开。- 形参个数跟原方法一致(远端按签名校验):
listProviders()收 0 个,listModels(provider)收 1 个。 - 只重排,条目本身一个字段都不动。
- 浏览器侧走本包自己的路由,不碰宿主的 Remote 通道(其余几条见下面「附带的轮换代理」):
GET /dsh-model-priority/state.json—— 目录快照 + 当前顺序 + 挂钩状态GET /dsh-model-priority/provider-models?route=<route>—— 某路由当前的模型 id 顺序GET /dsh-model-priority/suggest?route=<route>&mode=capability|cheap|quota—— 建议顺序PUT /dsh-model-priority/order.json—— 保存顺序文件({"reset":true}清空)POST /dsh-model-priority/settings-order—— 按给定顺序重排settings.yaml里该提供方的models
这些路由注册在 ctx.webServer 上,是裸路由:宿主的鉴权只覆盖 client-connection
注册的通道与首页。所以本包自己判来源(Host 必须是 loopback 或宿主登记的权威、
sec-fetch-site 不是 cross-site、有 Origin 时主机名要与 Host 一致),
浏览器里任意站点的跨域请求一律 403,响应也不再带 Access-Control-Allow-Origin。
本机 shell 里 curl http://127.0.0.1:3080/dsh-model-priority/state.json 照常能用。
它会动你的哪些文件
| 文件 | 什么时候 | 怎么收拾 |
|---|---|---|
~/.dsh/model-order.json |
每次点「保存」 | 删掉就回默认 |
~/.dsh/settings.yaml |
点「保存」并同步宿主时 | 写之前先备份成 settings.yaml.bak-<时间戳>;只重排对应提供方 models 数组的顺序,其它键一个都不动 |
~/.dsh/model-priority-proxy.json |
只有启用下面那个轮换代理才会创建 | 删掉即停用 |
为什么连 settings.yaml 也要写:宿主是拿 settings 里 models 数组的顺序来渲染模型列表的(设置页与选择器都看它)。
只在读的时候排序,会出现「设置页一个顺序、模型选择器另一个顺序」,两套顺序来源必然打架。所以写盘这一边是刻意的 ——
也正因为如此,写之前必须备份。
装 / 卸
dsh plugin --profile web add "<本仓目录,或 git 地址>"
dsh plugin --profile web remove dsh-model-priority
面板挂在上游的 settings.models.provider-card 席位上(2026-09-10 改版时删掉了早期的
侧边栏 tab),所以不需要装 dsh-better-sidebar:宿主有模型设置页就能用;
拿不到 slots 服务时面板不挂载,那几条 HTTP 路由照常可用。
装完要重启 dsh web:服务端半边(注册路由、挂钩)与浏览器侧半边(进 boot 图的 combo) 都是启动时装配的,只有重启才生效。顺序文件是每次调用现读的,改它不用重启。
自检
npm test
三个脚本,都不启动 dsh、不碰 3080 端口:
test/selftest.mjs—— 数据层:顺序文件的读写与清洗、稳定排序的边界 (名单里有不存在的 id、缺 id、空名单、原数组不被就地改动)。test/server-routes.mjs—— 服务端半边端到端:拿假 ctx(假webServer+ 假llm) 把八条路由真跑一遍,验状态码、排序真的作用到listProviders/listModels/listConfigurableProviders、坏 body 回 400、不支持的方法回 405、 同步契约、重复 apply 不套第二层挂钩,以及信任栅栏的那五条 (跨站 403、DNS 重绑定 403、宿主登记的权威放行、响应不带跨域头、proxy-status不含 token)。test/client-smoke.mjs—— 浏览器侧半边:用假window.__ModuleLoader__与假 react 把 bundle 跑一遍,验模块 id、只 require react、注册的描述符字段、icon 是内联 svg。
边界
- 只在
ctx.llm真的暴露上述方法的宿主版本上生效;挂不上时state.json的hook.ok会是false并带上原因,页面顶部会把原因显示出来,顺序文件仍然可编辑。 - 不改宿主任何一行代码;但会按你的操作重排
~/.dsh/settings.yaml里对应提供方的models数组(写前自动备份,见上)。卸载后model-order.json与代理配置留在~/.dsh下,手动删。 - 靠改
settings.yaml里 provider 的键顺序来排序不可靠:宿主的registrationFacts会对 provider 做 sort 来判断"是否变化",单纯调键序不触发 replace,运行中不会立刻生效,而且被 replace 的那批路由会被挪到 Map 末尾。 要固定顺序就用这个插件。 - 某个适配器不支持列举模型时,那个提供方下面是空的,这是适配器的能力边界, 不是排序没生效。
它不管什么
- 只管「列表顺序」:不做请求路由、不做故障转移、不做用量统计。那三件事各有人在管
(路由/故障转移看
dsh-model-router一类,用量统计看dsh-provider-usage一类), 塞进一个插件里最容易互相打架。 - 上游没有「调整模型顺序」的入口(2026-09-10 提过一条 discussion,至今零回复)。这个插件补的就是那一格。
附带的轮换代理(默认关,未上页面)
包内另有一个小的本机转发层:把某个 provider 的 baseURL 指到本插件的 rotate 端点,
它会在 .credentials.yaml 里的多把 key 之间依次试(429 / 额度尽那类响应才换)。
- 默认不开,也没有页面入口,只能用 HTTP 接口逐个 route 启用;
- 路径里带一个 32 字节随机 token(存在
~/.dsh/model-priority-proxy.json),不知道 token 一律 403 —— 插件路由不走浏览器鉴权,不设这道门等于本机任何进程都能拿你的密钥刷额度; - 想要的是「成熟的换 key / 故障转移」,建议先跟
dsh-model-router那类插件比过再决定用哪个。 - 状态口
GET /dsh-model-priority/proxy-status只报密钥数量、冷却与配置文件位置, 不回显 token(要手工配 baseURL 就自己看~/.dsh/model-priority-proxy.json)。
No comments yet. Be the first to write one.