当 LLM 请求持续失败(鉴权、配额、限流)时自动沿降级链切换 provider/model,让 DSH 代理任务不被模型问题中断。
- 语言
- TypeScript
- License
- MIT
- 分支
- main
安装
$ dsh plugin --profile web add dsh-llm-fallbacks在终端中运行以上命令,通过 dsh CLI 安装此插件。可在右上角切换 Profile。 第一次用 dsh?看这篇新手教程
对话式安装
帮我安装 DeepSeek Harness 插件 omdsh-dev/dsh-llm-fallbacks:先查看仓库 https://github.com/omdsh-dev/dsh-llm-fallbacks 确认安全性,然后执行安装命令并验证插件加载成功。
把这段指令粘贴给 DSH Web GUI 里的助手,由它代你完成安装与验证。
一句话定位
当 DSH 代理的 LLM 请求反复失败(鉴权错误、配额耗尽、429 限流)时,这个插件会自动沿降级链切换到下一个可用的 provider/model,让当前步骤直接在新模型上继续,不让模型问题打断整个任务;web 端与 dsh-tui 都共用同一套配置。
核心能力
- 自动故障降级:在
agent/request-error水龙头里监听到配置的失败码时,按角色解析出的降级链依次往下走,挑出当前可用且不在冷却里的下一个模型,自行恢复(不再走宿主默认重试) - 时间槽路由:按
fallbacks.tz(默认 Asia/Shanghai)的时钟窗口切换"有效的全天链",高峰低谷用不同模型,跨过窗口后下一次根请求自动换链(不算失败切换、不占冷却) - 虚拟 Auto 模型:插件启用后向宿主 LLM 适配器目录注入一行
FallbacksChain / Auto,把它当主模型选用就把整条当前有效降级链作为根代理的主模型 - 三阶段角色解析:子代理首请求依次按 显式
agentPreset→ 声明规则 → 可选的 LLM 自动匹配 解析角色,并把解析到的角色链头模型注入首请求 - 冷却与回切:被切走或失败的模型在
cooldownMs内不再被选中,到期后依据revertPolicy自动回到主模型;每步还有maxSwitchesPerStep安全阀防止链无限走 - 零配置即空操作:
enabled: false且无任何 chain 时插件是完全 no-op,不会做任何拦截和切换
技术实现
- 语言: TypeScript(host 端 ESM,client 端 React + TSX)
- 关键依赖:
@deepseek-ai/cordis(cordis 4 插件框架)/@deepseek-ai/schemastery(设置 schema 与默认值)/@deepseek-ai/dsh-settings(installSettingsSection注册设置命名空间)/react(web 端 FallbacksCard 与 ConversationFallbackSwitch) - 架构模式: 纯 mount 插件——通过
bundle/cordis.patch.yml在 profile 的 bundle 栈里插一行(必须在 dsh-base 的 llm-retry 之后注册,这样插件的agent/request-error监听器在 llm-retry 重试预算耗尽后才接管);host 端apply()在agent/request-error与agent/request两个水龙头接链决策,client 端通过dsh.client.inject把设置卡片注入 web 设置侧栏;配置读写走插件自己的/api/fallbacks/get|set|resetRPC 通道 - 入口文件:
src/index.ts(host 端:apply(ctx, config)入口、FallbacksService注册、gateway 注册、commands 注册)/src/client/index.ts(client 端:设置卡片FallbacksCard、会话切换徽章ConversationFallbackSwitch)
适用场景
适合同时使用多家 provider 的 DSH 用户:单一 provider 偶发鉴权、配额、限流是长任务最常见的失败原因,启用后可以让代理在主模型短暂异常时静默切到备用模型继续工作;按时段切换有效链的特性也适合把便宜的模型放在高峰、旗舰模型放在低峰。需要注意的是,rootChain 末尾必须是 deepseek-official/deepseek-v4-flash 或 deepseek-official/deepseek-v4-pro 之一作为最后兜底,不支持其它模型作为整链终点。
前置依赖与兼容性
| 依赖 | 最低版本 | 说明 |
|---|---|---|
| DSH | ^0.1.1-rc.1 | peerDependencies 声明的 @deepseek-ai/dsh-* 系列;运行时由 dsh 宿主以 bundle 形式提供,本机 npm 注册表按 autoInstallPeers 拉取(README 徽标 DSH-0.1.1--rc.1) |
| Node.js | >= 22 | package.json 的 engines.node;构建脚本需要 pnpm ≥ 10(仅本地目录安装需要) |
| 平台 | 跨平台 | 纯 TypeScript,无 OS / CPU 限制字段 |
| 原生模块 | 无 | 无 node-gyp 原生依赖,未使用 node:sqlite 等内置原生 API |
安装方式
dsh plugin --profile web add github:omdsh-dev/dsh-llm-fallbacks
配置项
所有配置都在 DSH 共享设置文档的 fallbacks: 命名空间下,启用即代表放弃 dsh 的内置降级兜底:
| 配置 | 类型 | 说明 | 默认值 |
|---|---|---|---|
enabled | 布尔 | 总开关。false 时插件不拦截任何请求,行为与未安装完全一致 | false |
triggerCodes | 字符串数组 | 触发降级决策的失败码(AUTH / QUOTA / RATE_LIMIT),代码不在此列表的请求会被原样放行 | ['AUTH', 'QUOTA', 'RATE_LIMIT'] |
rootChain | 字符串数组 | 主代理的全天降级链;最后一项必须恰好是 deepseek-official/deepseek-v4-flash 或 deepseek-official/deepseek-v4-pro 二选一(兜底模型),前面才是按顺序走的降级模型 | [] |
timeSlots | 数组 | 时段行;预设行(kind: preset)使用冻结窗口(梁文峰 / 梁文谷 / GLM峰 / GLM谷,固定 UTC+8 不可调),自定义行(kind: custom)可设 start / end / days;首个窗口命中当前时刻的行作为下一次根请求的有效链 | [] |
roles.list | 数组 | 声明的角色集合;每项 id(唯一小写字母数字短横线 ≤32 字符,inherit 是保留字不能用作 id) + persona(人格描述,自由文本)+ 可选 chain(专用降级链)+ 可选 fallback('inherit-root' / 'none') | [] |
roles.rules | 数组 | 子代理规则:按 provider / model 模式匹配首个命中规则的子代理,把它路由到 roles.list 里的某个角色或内置的 inherit。root 永远不匹配规则,直接走 rootChain | [] |
cooldownMs | 数字(毫秒) | 失败/被切走的模型在多少毫秒内不被重新选中;过期之后依据 revertPolicy 处理 | 300000(5 分钟) |
revertPolicy | 枚举 | 冷却到期后的回切策略:'cooldown-expiry' 到期回到主模型,'never' 本会话一直停在最后一个 fallback | 'cooldown-expiry' |
maxSwitchesPerStep | 数字 | 单步内允许的最大降级切换次数;超过后停止切换,保留原始错误语义,防止降级链放大延迟 | 8 |
alwaysModeRetryCap | 数字 | 当 provider 是 retryPolicy.mode: 'always' 时,多少次重试后强制切走;0 表示禁用 | 5 |
presets | 枚举 | 是否在 apply 时自动声明 7 个内置预设角色(task / sonic / scout / designer / librarian / reviewer / security-reviewer) | 'bundled' |
roleAutoMatch | 布尔 | 子代理的角色三阶段解析里是否启用第三阶段 LLM 自动匹配;false 时只剩显式 agentPreset + 规则两条路径 | true |
tz | 字符串 IANA 时区 | 时段行匹配使用的时区;只要存在任意预设时段行就锁定为 Asia/Shanghai | 'Asia/Shanghai' |
常见问题
Q: 装上之后代理行为没任何变化,是没生效吗?
A: 不是,是配置处于"零配置 no-op"状态。默认 enabled: false 且 rootChain 是空数组,插件会主动跳过对所有请求的干预。需要在 Fallbacks 设置卡或 YAML 里把 enabled 设为 true,再加上一条至少含 V4 官方模型作为末项的 rootChain。
Q: 配置改完之后什么时候生效?
A: 保存即生效。host 端通过 settings 的 onChange 钩子读取最新配置,热更新角色 id 集合、maxSwitchesPerStep 等所有设置;不需要重启 dsh 也不需要新建会话。
Q: rootChain 末尾必须用某个特定模型,是不是太死板?
A: 是故意设计的:rootChain 末尾是整个降级链的最终兜底,插件把它和虚拟 Auto 行绑定,期望它指向能兜住所有场景的官方旗舰/闪速模型。其它模型作为末项启动时只会告警、走老版本兼容路径,但 web 设置卡和 gateway 会拒绝保存带非官方末项的 chain。
Q: 模型选择器里那个新出来的 Auto 怎么用?
A: 它是注册到宿主 LLM 适配器目录的虚拟行(provider 是 FallbacksChain,模型 id 是 Auto),开启插件即出现。选中后根代理的主模型就用 rootChain 当前有效窗口里的第一项;选真实模型则保持"主模型 + 失败后才降级"的传统行为。Auto 出现的条件只有 enabled: true,无论 chain 是否已配置都会显示,chain 不合规时仅拒绝接管、不会隐藏。
Q: 子代理会话也要配置降级链吗?
A: 不强制,但建议:未匹配任何规则的子代理默认走内置的 inherit 角色,等同于直接走 rootChain;想给特定子代理用特定链就在 roles.list 声明一个角色(含 chain),再用 roles.rules 按 provider / model 模式把它路由过去。LLM 自动匹配默认开启,配 roles.list 之后会自动为子代理挑最合适的角色。
Q: 怎么关掉某一类失败不触发降级?
A: 把对应的失败码从 triggerCodes 移除。默认只有 AUTH / QUOTA / RATE_LIMIT 三个,5xx 类的可重试错误由 dsh 自带的 llm-retry 先吃回退预算,超出后才进入这条降级链,无需额外声明。
Q: dsh-tui 终端用户怎么编辑配置?
A: 在 /settings 屏幕里找 fallbacks 段(需要 dsh-tui ≥ v0.8.5)。布尔(enabled、roleAutoMatch)渲染为开关,单选(presets、revertPolicy)渲染为选择器,数字渲染为数字输入,复杂结构(rootChain、timeSlots、roles.list、roles.rules)是 JSON 文本框。非法草稿(坏 JSON、不合规 chain、坏时段行)会阻止保存,不会写坏配置。
Q: 升级到新版会不会丢配置?
A: 不会,配置写在 DSH 共享设置文档里,插件只读写自己注册的 fallbacks 命名空间,不动其它字段。注意:升级后如果你的旧配置没有显式 enabled 字段,schema 解析默认到 false,插件会突然变成 no-op,请主动加上 enabled: true。
上手难度
进阶 — 需要理解降级链、cooldown、角色匹配等概念,并按要求写一段合理的 fallbacks: YAML(或用 web 卡片点点鼠标);只想"装上就有用"还不够,必须先配置至少一条 chain 才会从 no-op 模式脱离。
已知问题与限制
- 从
<0.2.2版本升级上来的会话可能无法加载:旧版本会持久化写fallbacks/switch会话事件,新版 dsh 的会话读取模块识别不了(插件和宿主解析的是不同模块实例),新插件已停止写入此类事件,但历史会话需要运行pnpm repair:fallbacks-switch-logs -- --apply --backup(先停 dsh)把老事件标记为可忽略,会保留一份.bak备份 rootChain末尾必须是官方 V4 模型(deepseek-official/deepseek-v4-flash或deepseek-official/deepseek-v4-pro二选一),web 设置卡和 gateway 在保存时会拒收任何其它尾节点;想保留非官方末项只能在 YAML 里手写,startup 会有一次告警、运行时会按老版本兼容走完降级链路- 只要存在任意
kind: preset时段行,tz就被锁定为Asia/Shanghai,预设时段窗口是写死的 UTC+8 常量,无法在 UI 里调整;自定义时段行不受此限制 - 单步内的降级切换不能突破
maxSwitchesPerStep,超过后停止切换并保留原始错误语义;这是有意的安全阀,避免降级链死循环放大延迟 - 没有
rootMode这类配置字段:根请求到底走"chain 为主模型"还是"主模型失败再 chain"两种模式,由模型选择器里选了Auto还是真实模型决定,调用方不要在 YAML 里找这种配置 roleAutoMatch默认开启会让插件在子代理首请求时多发一次受限 LLM 调用(超时 5 秒、32 token 上限;src/automatch.ts:52-55)来挑选角色,断网/超时/没声明角色都会自动回退到inherit,不会抛错roles.rules不匹配根代理会话(PR #62 反馈):根请求只能走rootChain;如要给根代理配特定链,只能改rootChain本身
Automatic provider/model fallback chains for dsh (DeepSeek Harness): when an agent's LLM requests keep failing — retries exhausted, auth errors, quota exceeded, rate limiting (429) — the plugin switches provider/model along the fallback chain for the current role, and the current step/turn continues on the target model: tasks are not interrupted by model problems.
Works in both dsh front ends: the web profile (Settings → Plugins → Fallbacks card) and the dsh-tui terminal profile (/fallbacks session diagnostics, /fallbacks config readback, and the /settings fallbacks section for editing).
Time slots
Time slots rotate the effective root chain by wall-clock windows: each slot row carries its own fallback chain, and the first row whose window contains the current moment replaces the all-day chain for the next root request — the all-day chain stays as the last resort when no slot matches. Peak and valley windows can therefore use different chains while the failure walk (fallback switch) remains untouched.

Four frozen UTC+8 presets (windows are code constants; preset rows lock tz to Asia/Shanghai):
| Preset | Window |
|---|---|
liang-peak | 09:00–12:00 and 14:00–18:00, every day |
liang-valley | every other UTC+8 time (complement of Liang Peak) |
glm-peak | Monday–Friday 14:00–18:00 |
glm-valley | every other time (complement of GLM Peak) |
GLM Peak and GLM Valley are offered in the card picker only when zai-coding-cn is configured.
The first extra row whose window contains the current moment (in fallbacks.tz, default Asia/Shanghai) wins; no match → the all-day rootChain, whose tail (Default model) must be exactly one official V4 model — deepseek-official/deepseek-v4-flash XOR deepseek-official/deepseek-v4-pro. Slot rotation is a routing seed, not a failure decision: it applies on the next root request, consumes no cooldown, and is logged as a time-slot switch — failure walks keep fallback switch. Full semantics → Time-slot presets and docs/configuration.md.
Quick start
Install
dsh plugin --profile web add dsh-llm-fallbacks # web profile (Settings → Fallbacks card)
dsh plugin --profile dsh-tui add dsh-llm-fallbacks # dsh-tui terminal profile
Same plugin, either front end — the only difference is the --profile flag. Pin a version with @<version>. A registry install fetches the built package (dist/), nothing builds on the target machine. Registry / git / local-directory variants, uninstall, and --dump-config verification → docs/install.md.
Repair existing sessions (versions before 0.2.2)
Versions before 0.2.2 wrote durable fallbacks/switch session events that newer dsh releases refuse to load (issue #52 — the apply()-time event-type registration is ineffective because plugin and host resolve different module instances). If existing sessions fail to open after an upgrade, clone this repository and repair the logs (stop dsh first):
git clone https://github.com/omdsh-dev/dsh-llm-fallbacks.git
cd dsh-llm-fallbacks
pnpm install
pnpm repair:fallbacks-switch-logs -- --dry-run # preview which sessions would change
pnpm repair:fallbacks-switch-logs -- --apply --backup # mark legacy events ignorable
The script scans ~/.dsh/sessions by default (override with --root <dir>), marks legacy fallbacks/switch events ignorable: true so the host read path accepts the session again, and keeps a <file>.bak per repaired log. --apply requires --backup and must run with dsh stopped. From 0.2.2 on, the plugin stops writing durable switch events, so no new sessions need repair.
Configuration surfaces
The plugin's settings live in a shared fallbacks: namespace, editable from three surfaces:
| Surface | What it is | Notes |
|---|---|---|
| Web settings card | Settings → Plugins → Fallbacks | Full GUI editor for the fallbacks: namespace; writes the shared settings document |
$DSH_HOME/settings.yaml | fallbacks: section in the dsh settings document | The shared source of truth — the same file the web card writes; readable and editable everywhere, including scripted setups |
TUI /settings | fallbacks section in the dsh-tui settings screen | dsh-tui ≥ v0.8.5; native fields for simple keys, JSON text fields for complex structures (see dsh-tui profile (terminal)) |
Pick the surface that matches your front end: web users get the card, terminal users get /settings, and the YAML file works everywhere. (/fallbacks and /fallbacks config are diagnostics — read-only views, not edit surfaces.)
Minimal configuration
Add a fallbacks: section to the shared settings document ($DSH_HOME/settings.yaml — see Configuration surfaces):
fallbacks:
enabled: true # feature switch — defaults to off (plugin is a no-op otherwise)
rootChain: # all-day chain: leading entries = fallback walk, last = Default model (official V4)
- anthropic/claude-3-5-sonnet # walked first
- deepseek-official/deepseek-v4-flash # last resort (Flash or Pro)
timeSlots: # optional: rotate the effective root chain by wall-clock windows
- kind: preset # frozen UTC+8 window; only the chain is editable
preset: liang-peak # 09:00–12:00 and 14:00–18:00, every day
chain:
- anthropic/claude-3-5-sonnet
- kind: custom # custom window (may wrap midnight)
name: evening # optional display name
start: '22:00'
end: '02:00'
days: [1, 5] # optional; omitted/empty = every day (0=Sunday…6=Saturday)
chain:
- openai/gpt-4o
roles: # optional: declare role entities, then reference them from rules
list:
- id: reviewer # unique id; "inherit" is reserved
persona: Code-review subagents
chain:
- openai/gpt-4o-mini
fallback: inherit-root # role chain first, then the inherited rootChain
rules: # subagent-only: rules never match root requests
- role: reviewer # all subagents → the reviewer role
Build the section up in four steps:
1. Enable the plugin. enabled: true turns the fallback engine on. It defaults to off — with no chains configured the plugin is a complete no-op.
2. Set the all-day rootChain. Leading entries are the fallback chain, walked first when a request fails; the last entry is the Default model.
Conformance: the last entry must be exactly one official V4 model —
deepseek-official/deepseek-v4-flashXORdeepseek-official/deepseek-v4-pro. The settings card and gateway reject any other tail on save; a legacy non-official tail warns at startup and keeps working as a fallback-only walk, but cannot be saved as-is.
3. Add timeSlots (optional). Rows rotate the effective root chain by wall-clock windows. Preset rows use frozen UTC+8 windows (only their chain is editable; while a preset row exists, tz locks to Asia/Shanghai); custom rows take start/end (may wrap midnight) and an optional days list. The first row whose window contains the current moment wins; no match → the all-day rootChain. Rotation is a routing seed — it applies on the next root request and consumes no cooldown (see Time slots).
4. Add roles (optional). Declare role entities in roles.list (id, persona, chain, optional fallback policy), then map subagents to them with roles.rules. Rules never match root requests — with no rule match (or on a root request) the built-in inherit role applies and appends the rootChain.
Full reference (role entities, fallback strategies, rules, selectors, preset roles, time-slot presets) → docs/configuration.md.
Upgrade note (behavior change): an existing
fallbacks:section without an explicitenabledkey resolves tofalseafter upgrading — addenabled: trueto keep the plugin active.
Verify
Save the config and restart the session, then type /fallbacks — the read-only in-session diagnostics (origin, resolved role, chain, recent fallback switches, cooldown status). In a dsh-tui profile, /fallbacks config reads back the composed configuration; see dsh-tui profile (terminal).
Features
- Automatic fallback for root and subagents: any agent switches down the chain to the next available provider/model on model failure — no manual model switching.
- Two-block config:
rootChainfor the root agent; declared role entities (roles.list) referenced byroles.rules(or the built-ininherit). - Chain as root primary from the picker: when
enabledis on, the host model picker (web and TUI alike) shows a virtualFallbacksChain/Autorow — selecting it uses the configured chain as the root primary (a conforming all-day head is required for the override to succeed); selecting a real model keeps fallback-only (see FallbacksChain in the model picker). - Time slots: optional
fallbacks.timeSlotsrows rotate the effective root chain by wall-clock windows in the config-leveltztimezone (defaultAsia/Shanghai) — four frozen UTC+8 presets (liang-peak/liang-valley/glm-peak/glm-valley, windows are code constants, models-only edits) or customstart/end/dayswindows. The first matching row wins; the all-day row is always last. A slot change applies on the next root request and is logged as a time-slot switch — a routing seed, never a failure decision: it consumes no cooldown and does not count againstmaxSwitchesPerStep. Failure walks keep the fallback switch copy (see Time-slot presets). - Dispatch-time role resolution: on a subagent's first request its role is resolved in three stages — explicit (
agentPresetmatches a declared role id) → deterministic rules (unchanged) → LLM auto-match from the declared role taxonomy (fallbacks.roleAutoMatch, defaulttrue). The resolved role's chain-head model is injected into the first request and recorded via an explicitrole → modellog line (no durablefallbacks/switchevent is written — issue #52 stop-write); setroleAutoMatch: falseto disable the LLM auto-match stage (the explicitagentPresetstage still applies — with no explicit role this reproduces the previous rules-only behavior). The settings card always renders an Enable role auto-match switch (defaulttrue) to toggle it — the schema default applies even to legacy configs that never declared the key. - Cooldown and revert: failed / switched-away models are not re-selected during cooldown;
revertPolicy: cooldown-expiryreturns to the primary model automatically. - Visible behavior: every switch is recorded in an info-level log line (from/to/role/reason) — no silent model switching. The plugin deliberately writes no durable
fallbacks/switchsession events (issue #52: the apply()-time event-type registration was proven ineffective, and a session containing the event refused to load after a dsh restart). Sessions written by older plugin versions that contain such events are repaired byscripts/repair-fallbacks-switch-logs.ts, which marks legacy events ignorable so affected sessions load again. - Safety valves:
maxSwitchesPerStepcaps switches per step andalwaysModeRetryCapcaps always-mode retries — chain loops cannot amplify latency. - No-config no-op: with no chains configured the plugin behaves exactly like not being installed (
enabledis off by default — see Minimal configuration).
dsh-tui profile (terminal)
In a dsh-tui profile the plugin has three operator surfaces, with a strict duty split:
/fallbacks— what happened this session: origin, resolved role, effective chain, recent fallback switches, cooldown status. Read-only./fallbacks config— what is configured: composed-config readback (trigger codes, root chain, time slots, timezone, roles, role rules, cooldown, revert policy, safety valves, presets, role auto-match). Read-only apart from the one action command/fallbacks config revert-seed <role-id>, which restores a seeded role's persona to its declared seed default (a web-card action the settings seam cannot express)./settings— the edit surface. The plugin registers a fallbacks section with full parity to the web settings card: booleans (enabled,roleAutoMatch) render as toggles, selects (presets,revertPolicy) as pickers, and numbers (cooldownMs,maxSwitchesPerStep,alwaysModeRetryCap) as numeric inputs; complex structures (rootChain,timeSlots,roles.list,roles.rules) are JSON text fields andtriggerCodesa comma-separated text field. Invalid drafts (bad JSON, non-conforming chains, malformed time-slot rows) block the save — the section never corrupts the config.
Requirements: the /settings fallbacks section needs dsh-tui ≥ v0.8.5 (commit c51661f or later on main; the settings seam shipped in v0.8.0, the groups shape + validation in v0.8.5). On an older dsh-tui the section is absent, and file editing remains the only TUI edit surface.
File editing still works everywhere: the shared $DSH_HOME/settings.yaml (fallbacks: section — the same file the web card writes) for global settings, or the profile patch ~/.dsh/profiles/dsh-tui/cordis.patch.yml (config: overrides on the plugin row) for dsh-tui-specific values. A patch row replaces the targeted row's whole config — restate every field you want to keep (schema defaults fill the rest).
FallbacksChain in the model picker
When enabled: true, the plugin registers a virtual provider, FallbacksChain, with a single catalog row: Auto. The web profile and dsh-tui both see the row: they share the same adapter catalog, so the row needs no settings-page wiring or host patch (it is independent of the /settings fallbacks section, which edits configuration rather than the picker catalog). The row is visible whenever the plugin is enabled — a legacy or empty all-day chain does NOT hide it (the override just refuses to fire).
Selecting FallbacksChain / Auto uses the configured chain as the root primary: root requests route to the effective chain's first exact provider/model at request time, and the fallback engine degrades from that head as usual. Selecting any real catalog model keeps the v0.2.2 fallback-only behavior — the session model is primary and the chain engages only after it fails.
There is no rootMode switch — no config key, YAML field, settings toggle, or gateway flag. The mode is the session's {provider, model} selection itself: FallbacksChain = chain primary; any real model = fallback-only.
Notes:
- Picker label: the row's catalog
name(what the composer trigger shows) is live —Auto: DeepSeek V4 Flash[Liang Peak]/Auto: DeepSeek V4 Flash[all-day](catalog display name, not the model id); the id staysAuto. BareAutoif the all-day tail is not conforming. Refresh by reopening the picker. - Root only: the row is about the root agent. Subagent role resolution and injection are unchanged; a subagent session that inherits the selection still routes through the chain head — the virtual row is a thin delegate, never a second routing engine.
- Conformance gate on the tail: a successful override/delegate requires the all-day chain to be tail-conforming — its last entry must be exactly one official V4 model (
deepseek-official/deepseek-v4-flashordeepseek-official/deepseek-v4-pro, the card's Default model panel); leading entries (Default fallback chain) are walked first. Disabling the plugin hides the row again (slot-row/chain edits never churn registration). - Stale selection: if the row disappears (plugin disabled) while
FallbacksChain / Autois selected, the session keeps showing it as the current model withroutable: false— pick a real model from the catalog to continue (host-native catalog semantics). - Capabilities follow the head: the row's model metadata (context window, modalities, reasoning) mirrors the current effective head; retry attribution follows the permissive default — retries/failures are accounted to the real head pair, not to the
FallbacksChainprovider. Full semantics → docs/configuration.md.
Time-slot presets
Time slots are introduced in the featured overview above; this section is the reference. Time-slot rows rotate the effective root chain by wall-clock windows — useful for peak/valley pricing without confusing wall-clock rotation with failure fallback. The copy split is strict: slot rotation logs and UI say time-slot switch; the failure walk keeps fallback switch; the conversation notice Model downgraded stays on the failure path only.
- Match order: at every root request, the first extra row whose window contains the current moment (in
fallbacks.tz, defaultAsia/Shanghai/ UTC+8) wins — that row's chain replaces the all-day chain. No row matches → the all-dayrootChainis used. The all-day row is always last and required: its last entry must be exactly one official V4 model (Flash XOR Pro; leading Default fallback chain entries are walked first). - Presets (frozen, not user-editable):
liang-peak= 09:00–12:00 and 14:00–18:00 every day;liang-valley= every other UTC+8 time;glm-peak= Monday–Friday 14:00–18:00;glm-valley= every other time. One preset id = one row; the card picker never offers a duplicate. - Custom rows:
start/end(HH:mm, may wrap midnight) + optionaldays(0=Sunday…6=Saturday; omitted/empty = every day) + models. - Next-request apply: a slot boundary crossing never preempts an in-flight step — the new row takes effect on the next root request. Rotation is mount-only: info log + card/
/fallbacksstatus line, no durable switch event. - Settings card: the Main agent section groups Time slots (extra rows — add preset / add custom / remove / reorder by buttons or drag; preset rows show a read-only window summary and edit models only; custom rows carry an editable name; the timezone picker lives here and locks to Asia/Shanghai while any preset row exists, since preset windows are frozen UTC+8 constants), Default fallback chain (walked first when no slot matches) and Default model (the official V4 Flash | Pro last-resort fallback). Rows are collapsible to name + first model. There is no
timeSlots.enabledmaster switch (adding a row is the opt-in) and norootModecontrol.
Preset roles
The plugin ships 7 bundled generic subagent roles out of the box — designer / librarian / reviewer / scout / security-reviewer / sonic / task — declared automatically on apply as seeded roles.list rows ({ id, persona }): idempotent, and never overwriting an operator persona. They appear in the Settings card (seed badge, id immutable) and in the /fallbacks config role summary, ready for roles.rules to reference.
- Switch:
fallbacks.presets—'bundled'(default) declares the preset roles on apply;'none'disables the automatic declaration (already-materialized rows stay). - Full semantics (upgrade behavior, conflict handling, library reuse of
presetRoles) → docs/configuration.md.
Mount-only (no dsh modification)
The plugin installs as a pure mount: bundle insert + client inject + its own gateway channel (/api/fallbacks/get|set|reset) — no dsh patches, no postinstall step, and dsh upgrades never require re-patching. Stale leftover patches from an older patched install are harmless.
Documentation
| Doc | Content |
|---|---|
| docs/install.md | profile install (web + dsh-tui) / registry / git / local variants / uninstall / --dump-config verification |
| docs/configuration.md | full fallbacks namespace reference, selector syntax, example YAML, plugin-config card usage, TUI readback, behavior notes, preset roles |
| docs/consumer-api.md | developer consumption contract: library API + named llm-fallbacks service + role seeds, export inventory, lifecycle, typing |
| docs/release.md | release process: Trusted Publishing setup, Release prep SOP, fragment format, rollback |
| docs/verification.md | verification records (test matrix, bundle layer order, runtime contracts, QA gate script) |
License
Released under the MIT License — see LICENSE. The LICENSE file is authoritative for copyright and license terms.
查看使用指南 →
该插件的安装步骤、关键要点、FAQ 与兼容性说明(基于已收录字段派生)。
收录徽章
[](https://deepseek-plugin.org/plugins/omdsh-dev/dsh-llm-fallbacks)把这段 markdown 粘贴到你的 GitHub README,链接回本插件详情页。徽章只声明已被本站收录,不代表安全认证。