Repository navigation
Releases: Imzl-zl/dsh-models-router
Release list
v0.5.0
失败默认就动,截断默认续跑。
修复
- 只要失败就换候选,不再要求"这个错误码我勾过"。 此前八个错误码里有两个默认关着
(空回复、请求被拒绝),而且表外的一切错误码插件永远不接管;DSH 的llm-retry
也只重试它自己那五个码——两层都不管,一轮对话直接死在那儿。实测:网关返回
402 "Upstream request failed: Endpoint is unavailable."(报文里带
"type":"invalid_request_error"),pi-ai 适配器按文案把它归成INVALID_REQUEST,
链上明明还有候选,一次都没试。 - 新增「其他 / 未分类错误」开关(
switchOn.other):覆盖上表之外的一切码。它永远
先交给 DSH 自己的恢复插件(压缩历史、图片卸载等),它们放弃后才换候选。 - 输出被截断(达到输出 token 上限)时自动续跑:以前 DSH 直接结束本轮,只在界面上
提示你自己发"继续"。现在自动补一句"继续",同一个候选最多续
maxTokens.continues次(默认 2);续满还截断就换下一条候选接着写,不会停在一条
写不完的候选上。 - 取消(
ABORTED)与无适配器(NO_ADAPTER)仍然不切换:这两种情况没有"切换"这个
动作可言。 - 默认重试次数从 0 改成 5:原来默认"一次失败就换下一条",等于插件接管重试之后
反而比 DSH 自己更不耐心(DSH 的 provider 策略默认重试 5 次)。现在开箱行为就是你
说的那句:选中的模型先自己重试,重试还不行才按链路切,每一条候选都用自己的重试预算。
变更
- 旧的失败策略一次性升级:没有策略版本标记的配置按新默认读取。旧配置里
「空回复 / 请求被拒绝」那两个false、以及重试次数的0,都是当时的默认值、不是你的
选择,所以会被改写成新默认(全切 + 5 次重试);第一次保存后带版本标记落盘,之后你自己
取消的勾选和你填的次数(包括刻意填 0 = 失败即切)都不会再被覆盖。 - 链路页「故障转移触发策略」网格多一行「其他 / 未分类错误」;「重试与轮次」分区多一组
「输出被截断时」的开关与次数。
v0.4.0
右上角那个「更新日志」按钮改成「检查更新」。
新增
- 一个按钮回答两件事:有没有新版本,新版本改了什么。 点开先查 npm registry 上的
最新已发布版本,再拉那个版本的 GitHub Release 说明;已是最新时,下面仍然显示当前
版本的更新日志(它内嵌在产物里,离线也读得到)。 - 有新版本时给发布日期、发布页链接,并提示到「插件」页一键更新。
- 检查失败会明说原因(registry 读不到 / 说明读不到),不会假装"已是最新"。
变更
- 检查在宿主侧执行,浏览器不自己连第三方;新增
checkUpdate远程方法。 - 内嵌的版本与更新日志模块从
src/client/提到src/,宿主也能读到运行版本。
v0.3.3
行名改用你在「模型」页映射的名字,原始 id 退到 tooltip。
修复
- 链路、健康、探测下拉与 composer 的行名都写映射名。 之前一律渲染配置里的原始
provider/model:网关渠道的模型 id 常是<uuid>:<model>,三行看过去只有前缀
不同。现在名字来自 DSH 的模型目录——与「添加候选」和 DSH 自己的模型选择器同一份
来源;读不到目录或目录里没有这条路由时仍显示原始 id,不会借用别的名字。 - 原始
provider/model仍在每行 tooltip 里(候选行、健康行、探测覆盖下拉、chip
重试行、失败历史),排查时照着它就能对上日志与配置。
v0.3.2
弹层与链管理的可读性修正。
修复
- 弹层标题像标题了。 「本会话的路由」与「各路由的重试次数」改为 11px 加粗、
主文字色;之前它们和说明文字同字重,读起来只是两行正文。 - 弹层不再跟着长路由变宽。 卡片固定 280px,长
provider/model在行内省略
(悬停 title 看全名),三行输入框固定右对齐。之前不同长度的路由会把卡片撑成
不同宽度,输入框位置也跟着跳。 - 链行输入框等长。 「编辑/编辑中」固定 68px,「默认」徽标与「设为默认」按钮
共用 92px 槽位,改名输入框因此左右对齐;「添加链」改成带加号的 outline 按钮,
不再像一行普通文字。 - 会话重试覆盖上限从 9 改成 99。 与链预算共用同一个 sanity bound;
10现在
是合法值,100仍原样拒绝(不夹值)。上限只是防呆,真实重试次数仍由 provider
策略决定。
v0.3.1
文档修正,无运行时变化。0.3.0 的 README 还挂着已删除的「会话路由」页签截图,npm 页面
会因此展示一个已经不存在的入口;这一版把它换成更新日志弹窗的截图。
文档
- 截图区把「会话绑定列表」换成「更新后弹一次更新日志」。
- npm 包内 README 重新从根 README 生成,和仓库首页保持一致。
v0.3.0
更新提示与更新日志版本:DSH 的插件市场负责发现和安装新版本,插件负责在新版本
跑起来后告诉用户改了什么。
新增
- 更新后弹一次更新日志。 工作台在版本变化后(以及全新安装后)自动打开当前
版本的更新说明,页头保留「更新日志」按钮,随时可以重看。说明在构建时从本文件
生成,所以弹窗描述的永远是实际安装的产物,而不是手抄的旧文案。 - 更新仍走 DSH 原生路径。 插件不启动包管理器,也不复制安装算法:DSH Market
的更新行(npm 版本检查、一键更新、「更新了什么」)负责安装,插件只负责更新后的
那一步说明。
修复
- 点弹层外部现在会提交重试草稿。 DSH 的
Menu在pointerdown就关闭,输入框
的blur来不及跑,之前会把用户输入的重试次数丢掉;现在关闭回调先写入待提交的
按路由重试次数,宿主接受后才关闭。Escape 仍是取消路径,不写任何东西。 - README 弹层截图改为 3 倍设备像素(696×555),两列画廊不再把旧的 348×278
元素截图放大发糊。
移除
- 设置页的「会话路由」页签。 日常选链就在 composer 的 chip 里;这个页签只是
事后总览,用户明确说不会用。绑定仍按sessionId保存,chip 与路由行为不变。
只为它存在的sessionBindings/deleteSessionBinding两个 Remote 一并删除。
v0.2.1
Packaging and process only; no runtime behaviour changed. The first release
published by CI through the npm trusted publisher (OIDC) rather than a manual
npm publish.
Changed
- Releases are tag-driven. Pushing
v*.*.*runs
.github/workflows/release.yml: it refuses a tag that differs from the package
version, builds, publishes with provenance, and opens the GitHub release from
this file's matching section. - CI runs on every push to
masterand every pull request: the build, the
host type-check, and each of the four suites as its own step. - npm metadata (
keywords,homepage,bugs,author,engines) and the
README badges;dsh-models-routerreplacesdsh-model-routereverywhere,
including the repository name.
Fixed
- The client bundle no longer leaves its own
./remoteartifact as an external
requireon a clean checkout (the host face now builds first). - The legacy-config composition scenario generates its overlay at run time
instead of shipping Windows absolute paths.
v0.2.0 — named chains and per-session routing
The release that makes routing per-conversation: several named chains, a session
that can pick one, and retry counts that can be overridden per session. Published
under the name dsh-models-router (dsh-model-router on npm belongs to another
publisher).
Added
- Named chains.
chains[]+defaultChainId; a chain carries its own
candidates, switch-on rules, first-hop budget, sticky window and start
strategy. The settings page can add, rename, copy (candidates and budgets come
along), make default and remove chains; the last one cannot be removed. - Per-session chain binding. A chip in the composer tool row (after the Plan
and permission controls, before the model selector) picks the chain for the
current conversation, and a "session routing" tab lists every session so
several can be assigned without opening them. Bindings live in the
model_router_sessionsstorageDomain sidecar, keyed bysessionId; without
the storage domain they fall back to process memory and the UI says so. - Per-route retry overrides in the session popover and the session tab:
session override ?? candidate maxRetries, keyed byprovider/model, 0..9.
Empty follows the chain;0means "do not retry this route". - Batch binding read/write Remotes (
sessionBindings,
deleteSessionBinding) for the session list and its cleanup of deleted
sessions.
Changed
- Retries on a routed step are executed by this plugin. For a code listed in
switchOn, the configuredmaxRetriesis the real number, with no
provider-policy ceiling on top; the wait copies DSH's curve. Codes outside
switchOnstill go tollm-retry. - A chain's
needsgates the fallbacks; the first hop is the user's selection
and is never gated (README documents the consequence for image routing).
Fixed
- Legacy configs are actually adopted. The shape is now read from the raw
config (chains→chain→profiles→default), so a user whose config
predates the named chains keeps routing instead of silently being read as an
empty chain. - A reloading or disabled plugin no longer breaks turns. The per-agent seams
are owned by the plugin fiber and take the agent from the payload, so they are
removed on unload and replaced on reload; a closed storage domain now answers
"no binding" on reads and a plain retryable error on writes instead of leaking
DomainError('closed'), and a restart retries a transientlyalready-open
domain.
v0.1.0 — first tagged release
First tagged release. The plugin has been feature-complete since 2026-10-16; this tag publishes one verified baseline rather than a replay of the pre-release history.
What it does
Select a model in DSH's selector as usual. When it fails, the request moves along your ordered chain of physical provider/model candidates, and the conversation reports the failure code, the candidate that answered, and why the switch was allowed.
- Two-layer attempt budget. Same-route retries and backoff stay owned by DSH's
llm-retry; cross-candidate failover and the hard total cap belong to this plugin. - Pre-fire validation. Each candidate is resolved through
ctx.llm.resolveCallConfig()before any real call, so an unservable candidate is skipped rather than burned. - Conditional routing. Per-candidate
images/minContextTokensgates, plus per-codeswitchOn. - Channel health workspace. Zero-token passive latency and throughput, opt-in 1-token probes, route- and channel-scoped circuit breakers, and a failover-history drawer that rebuilds through session forks.
- Bilingual UI. Chinese and English, with an unsaved-changes bar.
Installation
Requires DSH 0.2.0-rc.2. Distributed as source from this repository — not on npm (the public registry's dsh-model-router is an unrelated package).
git clone https://github.com/Imzl-zl/dsh-model-router.git
cd dsh-model-router
npm install
npm run bundle --prefix packages/dsh-model-router
dsh plugin --profile web add packages/dsh-model-routerVerified at this tag
| Suite | Result |
|---|---|
| Composition (real headless Loader, stub adapter, durable-log assertions) | 21 scenarios, 0 failures |
| Unit (pure decision logic) | 121 passed |
| Client controller | 48 passed |
| Remote service | 14 passed |
npm run bundle (incl. tsc -p tsconfig.json) |
clean |
tsc --noEmit strict |
clean |
The DSH reference checkout was strictly read-only across the entire history (git status --porcelain → 0 lines).
Known limitations
- Agent main requests only. Compaction and session-title calls keep their own DSH configuration and are not routed.
- Tool capability is not pre-checked. DSH exposes no machine-readable "supports function calling" metadata.
- Notices are trajectory-first. Failover notices ride the durable log; this DSH build does not surface context rows in the chat conversation.
See CHANGELOG.md for the full list.