dsh-auto-thinking-levels
v0.1.2
Published
DSH Host plugin: keep every llm-pi-ai provider route offering the full thinking-level set by writing the missing reasoningEfforts tables into that namespace's settings.
Maintainers
Readme
dsh-auto-thinking-levels
DeepSeek Harness (dsh) 的 Host 插件:
自动给每个提供商、每个模型补上完整的思考等级表(reasoningEfforts),不用再逐模型手写。
English TL;DR — A dsh Host plugin that keeps every
llm-pi-aiprovider route offering the full thinking-level set. It writes the missingreasoningEffortstables into that namespace's settings layer, so both the model selector and the request path agree on which levels exist. Add-only and idempotent: it never rewrites a level you already spelled, never removes an entry, and never touches a model that declaresreasoningEfforts: false.
reasoningEfforts:
{ off: null, minimal: minimal, low: low, medium: medium, high: high, xhigh: xhigh, max: max }为什么写配置层,而不是去 patch adapter
llm-pi-ai 在物化模型时读一次 reasoningEfforts,把它翻译成 pi-ai 描述符上的
thinkingLevelMap(resolveModelReasoning)。之后所有判断都只从这张 map 出发:
getSupportedThinkingLevels()决定选择器显示哪些档位;- 同一个函数守着请求路径,map 里没有的档位在发出任何 I/O 之前就以
UNSUPPORTED_REASONING_EFFORT被拒。
所以只包一层 adapter 的 resolveModel 只能改到第一个答案、改不到第二个 —— 用户会选到
一个真正发请求时被拒的档位。把表写进配置层,是让配置本身正确,两个答案都从它推导,
且任何一次重载后依然成立,不需要重新打补丁。
写入走 settings.update:候选值会先过该命名空间自己的 schema 与 assertServiceable
校验,注入不合法时什么都不落盘。
安装
dsh plugin --profile <name> add dsh-auto-thinking-levels插件自带 dsh.bundle.patch,安装后自动挂一行,不需要手改 cordis.patch.yml。
想改配置的话,在 profile 的 cordis.patch.yml 里用 id 定向 patch 覆盖那一行即可;
改完用 set_bundle 关掉再打开,这一行就会用新配置重新挂载。
配置
全部字段可选,默认值见下表。
| 字段 | 默认 | 含义 |
| --- | --- | --- |
| namespace | llm-pi-ai | 要配置的 settings 命名空间(dsh-llm-pi-ai 的 NS) |
| fill | complete | complete 把档位不足的模型补齐(已写过的档位保留你的原值);missing 只补完全没有表的模型,尊重刻意的子集 |
| levels | 七档全表 | 要写入的表;值是发给网关的线材拼写,off: null 表示「支持,但不发送该参数」 |
| providers | [] | 路由白名单,空 = 全部已注册路由 |
| enabled | true | false 时挂载但不动配置 |
行为
只做新增,绝不覆盖:
- 已有部分档位的模型(
fill: complete):补齐缺失的档位,已写过的档位保留你的原值; - 声明
reasoningEfforts: false的模型:false是「此模型不推理」的明确答复,永不填充; - 其余任何字段、任何条目都不会被删改。
因此是幂等的:第二遍扫描找不到要补的,就不写、不发事件,自然收敛。
插件会监听该命名空间的变更与 adapter 拓扑变化,所以在 GUI 新增模型,或修改 profile 的 cordis.patch.yml 后,它会自动补齐。
DSH 0.1.7 的配置通知可能从 HMR 事务内发出。插件会退出该事务的异步上下文,
再通过 settings.update 正常排队写入,避免 HMR transactions cannot be nested。
单纯加 setTimeout 或微任务不能解决此问题,因为它们会继承原事务上下文。
连续保存造成 revision 冲突时,已收到的新变更通知不会丢弃:下一遍重新读取最新配置,
不会用旧模型列表覆盖新模型。已有档位、自定义值和显式 false 仍按上述规则保留。
两种路由
- 声明了
models的路由:逐条 entry 补表。 - 没有
models的路由(catalog 路由):只能通过modelOverrides表达,因此按ctx.llm.listModels(route)实际服务的模型逐个补 —— adapter 会拒绝命名 catalog 里不存在的模型,所以这里用真实服务列表而不是去猜 catalog。 两种方式互斥:models旁边放modelOverrides会被 adapter 拒绝。
已知边界
HMR 事务兼容已在 DSH 0.1.7-rc.1 验证,目前需要访问该版本的内部
hmr.executing(AsyncLocalStorage);不是稳定公开的跨版本接口,升级 DSH 后需重新验证。
卸载后不会再提交新的补全写入;已交给 settings.update 的写入由宿主完成,插件不会在卸载时等待它,
避免和 HMR 的串行队列互相等待。
本插件只配置 llm-pi-ai 命名空间。其它 adapter 不在作用域内,且有些无法被配置:
deepseek-official(llm-deepseek adapter)拿不到七档 —— 它的档位写死在 adapter 里
(off/low/high/max),线材校验函数 reasoningEffort() 对其余值直接抛
UNSUPPORTED_REASONING_EFFORT;它的 settings schema 也只有 provider 级 reasoningEffort,
没有 per-model 表。这是官方 API 自己的线材词汇(medium 会收敛到 high),不是配置能改的。
强行为它注入 minimal/medium/xhigh 只会把请求打成必然失败。
验证
verification/probe-result.json 是一次性探针在运行中的 harness 上抓的实测结果:对每个
已注册路由/模型调用 llm.resolveModelInfo(选择器看到的档位)与
llm.resolveCallConfig(请求路径是否接受该档位)。
## provider: cpa
deepseek-v4-flash-0731 efforts: off/minimal/low/medium/high/xhigh/max rejected: none
glm-5.3-flash efforts: off/minimal/low/medium/high/xhigh/max rejected: none
deepseek-flash efforts: off/minimal/low/medium/high/xhigh/max rejected: none
gpt-6-astra efforts: off/minimal/low/medium/high/xhigh/max rejected: none
claude-fable-5-1 efforts: off/minimal/low/medium/high/xhigh/max rejected: none
glm-5.3-flashx efforts: off/minimal/low/medium/high/xhigh/max rejected: none
## provider: deepseek-official
deepseek-flash efforts: off/low/high/max rejected: minimal,medium,xhigh
deepseek-v4-pro efforts: off/low/high/max rejected: minimal,medium,xhigh(resolveModelInfo 里的 auto 由另一个插件注入,不属于本插件。)
开发
npm test # node:test,零依赖;含 HMR 事务及连续保存回归
npm run check:pack # 断言 npm publish 会打包哪些文件lib/plan.js 是纯决策逻辑(可测、无 I/O),index.js 只负责读写 settings 与事件触发。
发布
发布走 tag:
npm version patch # 0.1.1-rc.1 -> 0.1.1,或 npm version prerelease --preid rc
git push --follow-tags.github/workflows/publish.yml 会校验 tag 与 package.json 版本一致、跑测试、断言打包内容、
以 provenance 发布到 npm,并开一个 GitHub Release。只由 tag 触发,从分支上发不出任何东西。
dist-tag 由版本号推导,不写死
| package.json 版本 | dist-tag | npm i dsh-auto-thinking-levels 会拿到它吗 |
| --- | --- | --- |
| 0.1.1 | latest | 会 |
| 0.1.1-rc.1 | rc | 不会 |
| 1.0.0-beta.2 | beta | 不会 |
| 2.0.0-next | next | 不会 |
| 3.0.0-preview.1 | next(并告警) | 不会 |
这一步是必需的,不是风格问题:npm 拒绝在没有显式 --tag 时发布 prerelease
(You must specify a tag using --tag when publishing a prerelease version.),
所以 0.1.1-rc.1 走裸的 npm publish 必然失败;同时这条规则保证了 rc 永远顶不掉 latest。
推导逻辑由 test/release-dist-tag.test.js 直接抽出 workflow 里的脚本执行验证。
想先看不发,或者想确认 OIDC 登记好了没有:在 Actions 页面手动 dispatch Release,
dry_run 默认为 true —— 测试、tarball 断言、OIDC 换取都会真跑,只有 npm publish 带 --dry-run。
所以一次 dry run 就能回答"这个仓库现在能不能发布"。
注意 npm 的重复版本检查在 --dry-run 下同样生效:如果 package.json 的版本已经发布过
(比如刚发完 0.1.1,而工作区版本号还是 0.1.1),dry run 会在 Publish 步骤报
You cannot publish over the previously published versions: 0.1.1. —— 这是预期行为,
不是流水线坏了;要 dry run 一个已发布的版本,先升版本号。
发布后的回读是轮询的:npm 会对 publish 回一句 "Your package is being processed and may take
a few minutes to become available",读副本滞后(实测 0.1.1 是 131 秒)。轮询 5 分钟仍没等到时只
::warning:: 而不失败,因为发布是否成功以 Publish 步骤的输出(registry 接受 + provenance 签名)为准,
把已发布的版本标成红会误导人。
凭据:npm Trusted Publishing(OIDC),没有 secret
发布不用任何长期 token。job 里的 id-token: write 让 npm 用这次运行的 OIDC 身份换取一个
短时效发布令牌(trusted publishing),
公开仓库 + 公开包还会自动开启 provenance。仓库里不存在 NPM_TOKEN,
test/release-dist-tag.test.js 会断言工作流里不出现任何 token 注入。
代价是必须先登记一次信任关系——在 npmjs.com 的包设置里填(workflow 文件名必须仍是 publish.yml):
| 字段 | 值 |
| --- | --- |
| Organization or user | lolkda |
| Repository | dsh-auto-thinking-levels |
| Workflow filename | publish.yml |
| Environment | 留空 |
这一步必须走网页:命令行配不了。npm trust github 会被 npm 拒绝,
因为能发布包 ≠ 能改包的安全设置:
$ npm trust list dsh-auto-thinking-levels
npm error 401 Unauthorized - GET https://registry.npmjs.org/-/package/dsh-auto-thinking-levels/trust
$ npm trust github dsh-auto-thinking-levels --file publish.yml --repo lolkda/dsh-auto-thinking-levels --allow-publish
npm error 401 Unauthorized - POST https://registry.npmjs.org/-/package/dsh-auto-thinking-levels/trust(上面两条都是用一个 granular token 跑的,npm whoami 正常但改安全设置就是 401。)
没登记之前,发布会被 workflow 里那道 preflight 明确拦下,而不是给一个看不懂的 ENEEDAUTH:
::error::npm refused the OIDC token exchange (HTTP 404)
Register the trusted publisher at https://www.npmjs.com/package/dsh-auto-thinking-levels/access这道 preflight 就是 npm publish 内部会做的同一次 OIDC 换取,只是提前跑、并且自己解释失败原因;
它成功打印 npm accepted the OIDC token exchange for dsh-auto-thinking-levels 才继续发布。
License
MIT
