@neptune810/dsh-model-router
Keeps a pool of models and routes every agent step to one of them with its reasoning effort set — configured by clicking in the composer, no JSON needed. The router takes over the composer's model cell, so one control shows the session's model and reasoning effort alongside the router's own settings
Install
dsh plugin --profile web add @neptune810/dsh-model-router
Sandbox test results
| Stage | Result |
|---|---|
| 1 install | ✗ |
| 2 list | — |
| 3 mount | — |
| 4 boot | — |
dsh: initialized profile web at /home/node/.dsh/profiles/webFour stages run in an isolated sandbox: install (network on) → list → mount → boot (last three with no network).
README
dsh-model-router
[English](README.md) | 中文
一个 DeepSeek Harness 插件:维护一组模型池,
把每一步路由到其中之一并设定该步的思考等级。思考始终开启——DeepSeek 会拒绝历史中含有
「关闭思考时产生的工具调用」的思考请求。
路由表
步骤类别 · 判定依据 · 思考等级 · 底层标量
trivial · 短步骤里的明确廉价意图(翻译、改名、格式化) · low · ~50
standard · 普通短请求,无工程线索 · low · ~50
engineering · 工程线索 / 代码·diff·XML 结构 / 工具循环 · high · ~75
hard · 密集工程简报,或任务内挣来的失败证据 · high(allowMax 时为 max) · ~75 / 100
high 是默认上限。V4.1-Flash 底层是一个 1–100 的标量,对外只暴露三个预设名;high 约等于 75,
正是公开曲线仍然陡峭的位置。再往上拉,输出 token 多花约 1.6–1.8 倍,换来的提升却很边际。
v0.15.0 起 routes.<class>.effort 可以写任意一档九词词表(off、none、minimal、low、medium、high、xhigh、max、ultra);目标模型不认识某个词时取最近的一档,等距时向上取(质量优先)。off 与 max 类词仍受规则 5 与 allowMax 两道闸门约束。
授权模式、模型池与任务预设(v0.5)
control 决定插件能改什么,而且手调只在拥有它的那个模式里算数:改思考等级会让
full/effort 让位,但不影响 model;换模型会让 full/model 让位,但不影响 effort。
让位后插件一直不接管,直到你发下一条命令,或在输入栏控件上点「重新接管」。
模式 · 控制范围
full 模型+思考(Model+Effort) · 模型 + 思考等级
effort 思考 · 只作用于你在面板里选中的那一个模型,只调它的思考等级
model 模型 · 只调模型,思考等级听你的
思考 模式下,面板会先要求你选中唯一一个模型(POST /model-router/effort-model,/state
用 effortModel / effectiveEffortModel 汇报)。没选之前插件完全不接管——既不换模型也不改思考
等级。绑定之后,手动换模型不会让它退出(它只管绑定的那个模型),手动改思考等级才会。
客户端半边以优先级 -1 注册进官方 conversation.input.model 座位,接管 composer 的模型格;它同时
承载官方「本会话模型 + 推理等级」两段和路由器的模式切换、重新接管、任务类型选择与任务预设:模型池已合并进同一份模型列表——每行右侧的
「模型池」胶囊点一下把这个模型加进 / 移出池子,进了池子的行会在下面展开定位 / 价格 / 视觉,池里多出来的模型归到「其他模型」一组,菜单因此短了一半。列表按 provider 分组,每组标题右侧有「全加入 / 全移除」,成本还能用内置规则或 OpenRouter 快照自动填。
所有写操作都走同源 /model-router/* 路由。
模型池:只有池里的模型会被选中。
pool:
- { id: deepseek-official/deepseek-flash, cost: 1, tier: cheap }
- { id: deepseek-official/deepseek-v4-pro, cost: 8, tier: strong, maxPerTask: 3 }
- { id: deepseek-official/deepseek-v4-flash-vision-exp, cost: 1, tags: [vision] }
- { id: vendor-x/writer-pro, cost: 20, weights: { 小说续写: 95 } }
tier: strong 是困难步骤与证据升级时优先的档位;cost 参与 scoring.costPenalty 扣分;
maxPerTask 限制单个任务能用这个模型几步(可写在条目上,也可在配置层写
maxPerTask.strong 作为所有强档条目的默认上限;两者都不写就是不限)。一旦池中有模型带
vision 标签,带图步骤只在它们之间选。
任务预设:自定义任务类型 + 规则 + 每模型权重。
presets:
小说续写:
match: ["续写", "小说", "文风", "/chapter\\s+\\d+/i"]
weights: { "vendor-x/writer-pro": 95, "deepseek-official/deepseek-v4-pro": 60 }
代码重构:
match: ["重构", "refactor", "架构"]
weights: { "deepseek-official/deepseek-v4-pro": 90 }
在控件里手动选定的任务类型优先于规则;没选才走关键词规则(classifier: rules)。第三方模型用
自己的 effort 词表:插件向 provider 查询它支持哪些档位,取最接近的,模型完全不支持思考时就不发
effort 字段。
默认不透明(主题的菜单色本身是半透明,会把背后的聊天内容透上来):
控件就是 composer 的模型格(v0.11.0)
路由器的控件以优先级 -1 注册进官方 conversation.input.model 座位,低于官方 ModelSelect
(优先级 0,来自 @deepseek-ai/dsh-client-ui-model-selection)。槽位引擎渲染优先级最低的那个,所以这个
合并控件接管了 composer 的模型格,输入栏只会出现一个控件,而不是在官方选择器旁边再放一个 chip。
这一个控件同时保留两半。官方那一半是 本会话模型 / Session model 与 推理等级 / Reasoning effort,由真正的
ModelDirectory 驱动(props.directory.store 订阅、props.load()、`props.select({ provider, model,
reasoningEffort })`):provider/模型分组、当前行的对勾、待选行的转圈、供应商默认行、目录加载 / 出错 /
重试,以及官方「模型 · 等级」的触发器文案。下面才是路由器自己的分组:接管状态、控制范围(模型+思考 /
思考 / 模型)、任务类型,然后是模型列表本身——模型池折进模型行:每个会话模型行右侧带一个 模型池 /
Model pool 小胶囊,点它把该模型加入或移出池子,入池的行会在下面展开 定位 / 价格 / 视觉,目录里没有对应
provider 分组的池条目会和「手动填模型 ID」一起归到 其他模型 / Other models。模型行按 provider 分组,每组标题右侧是 全加入 / Add all 与 全移除 / Remove all;面板头部有 自动定价 / Auto price 与 联网定价 / Fetch prices 两个按钮,用来给没手标价格的池条目填 便宜 / 中 / 贵。再往下是 任务预设 与 更多。
所有改动都通过同源 /model-router/* 路由发给宿主;对话区里每次调用的路由行不变。
旧的 conversation.input.right chip(model-router:composer-control,order 20)仍然注册着,但它会
自我收回——座位注册一落地就把自己 dispose 掉——所以正常宿主渲染出的 .right 一列是空的,永远只会看到
合并后的那个控件。只有拒绝或重命名该座位的宿主,才会保留 chip 作为回退。
0.10.0 第一次尝试这么做,0.10.1 已回滚:现场那个座位整格空白,而且一次重启直接启动失败。根因是 cordis
解析 remote.session 这类嵌套服务时,会从调用方所在的 fiber 往上走,所以客户端插件自己必须先在插件级声明
sessions、remote 与 remote.session,之后方法才能碰 ModelDirectory.directoryFor()。插件级 inject 现在
是 ["slots", "uiConversation", "sessions", "remote", "remote.session"]。
限制。 这是注册进官方座位,不是给官方组件打补丁:官方弹层的内部实现(portal 菜单、搜索框、键盘导航、
模块 CSS)没有被复制,只复刻了它的选择语义。如果某个 DSH 不再声明 conversation.input.model,座位注册会静默
失败,小号的回退 chip 接管。插件源码改动仍然需要完整重启 dsh web / 桌面端。
新对话继承上一次的方案并确认一次(v0.13.0)
以前每次启动 DSH 面板都会回到默认值,每个新对话也各按各的来。现在宿主会记住你最后一次设定的方案——控制
范围、绑定的思考模型、钉住的任务类型——存在 <profile>/.model-router/state.json 的 last 里;自己还没做
过选择的对话就沿用它。选择仍然是分对话的:某个对话一旦自己配过,就以它自己的 sessions 条目为准,对话之间
互不影响。
继承来的方案不是你在这个对话里做的决定,所以面板顶部会先出现一条警示色高亮条:有可继承的东西时是
「已沿用上一次的选择」,什么都没有时是「新对话:先确认这次的路由方案」。条上列出生效的 范围 / 思考模型 /
任务类型 / 模型池,以及一个「确认」按钮(POST /model-router/confirm { sessionId, plan });没确认之前
composer 触发器上会有一个脉冲的黄点。确认会把这个方案键记到该会话上——/state 回报 plan、planKey
与 planConfirmed——此后本对话不再出现高亮条和黄点;改动控制范围、绑定模型、任务类型或模型池都会产生不同
的键,于是再确认一次。
多品牌:分组、自动定价与跨品牌确认(v0.14.0)
以前池子一行一个模型平铺,换第二个品牌要点很多次。现在模型列表按 provider 分组:官方目录的每个分组、
以及「其他模型」里每个品牌,标题右侧都有「全加入 / Add all」和「全移除 / Remove all」,一次把整个品牌的
模型放进 / 拿出池子。
成本不再必须手标。内置的相对价格分类器(lib/pricing.js,纯函数、无依赖)按模型名给出 便宜 / 中 / 贵
(cost 1 / 4 / 8),覆盖常见的 DeepSeek、OpenAI、Anthropic、Google、Qwen、Llama 家族;「自动定价」用它
加上已存的 OpenRouter 快照,联网定价先拉一次 OpenRouter 快照(20 秒超时)再定价。每条池条目的
costSource 记录来源(builtin / openrouter / manual),你手标过的那条永远不被覆盖。
跨品牌切换本身也变成一个决定。跨品牌 / Cross-brand 有三个取值:允许 / 先问 / 禁止(默认先问)。选「先问」
时,如果下一步想用的模型属于另一个品牌,宿主先留在当前品牌,把候选放进 state.cross.proposal,面板顶部
弹出提议条,三个按钮分别对应 只切一次 / 本会话允许 / 不再跨品牌。池子里只有另一个品牌的模型时照常路由(守卫
只在存在同品牌备选时才拦),迟滞记忆也不会再把被拦下的模型放回来。
对应路由:POST /model-router/cross { sessionId, action }(action 取 allow / once / never)、
POST /model-router/pool/auto-price { fetch }、POST /model-router/prices;/state 增加 cross 与
prices。
全部点一点就能配(v0.6.2)
输入栏那个控件就是完整的配置入口,不需要写 JSON/YAML:
区块 · 操作
模式 · 点「模型+思考 / 思考 / 模型」;「思考」下先选中唯一一个模型;手调后会出现「重新接管」按钮
任务类型 · 选「自动(按关键词规则)」或某个预设
模型池 · 从实时模型目录里勾选,再用下拉设档位 / 成本 / 视觉
任务预设 · 点「+ 新建预设」→ 点常用词包(写作/代码/翻译/分析)或输入一个词 →
…(truncated)
Rendered from the README published on npm. Formatted only, not edited.
Versions
Published on npm::35,最后更新 2026-11-05