Role-based LLM retry & fallback strategies.
Install
# from GitHub (first run asks for allowBuilds approval — follow the hint, retry)
dsh plugin --profile web add github:btspoony/dsh-llm-fallbacks
GitHub-sourced plugins run build scripts on your machine at install time. Only install sources you trust, and pin a commit (github:owner/repo#sha).
README
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.
Install with a single command (see Install):
dsh plugin --profile web add dsh-llm-fallbacks # pin a version with @<version>
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: block 1
rootChain— the root agent's single fallback chain (empty = root does not fall back); block 2 declared roles —roles.listrole entities (id/label/description/chain/fallback) thatroles.rulesreference by id (or the built-ininherit); no rule match →inherit→rootChain. - Entry syntax: chain entries are
provider/model(exact switch) orprovider/*(keep the failed model id, switch provider only) — the old chain-key namespace (provider/model keys, role-name keys) is gone. - Cooldown and revert: models that were switched away from / failed are not re-selected during the cooldown period;
revertPolicy: cooldown-expiryautomatically returns to the primary model when the cooldown expires, whileneverdoes not return within the session. - Visible behavior: every switch appends a persisted session event
fallbacks/switch(from/to/role/reason), alongside info-level logs (candidate attempt order and skip reasons) and the read-only status block on the Settings → 插件配置 → Fallbacks card — no silent model switching. - Safety valves: switching stops and the original error semantics are kept once
maxSwitchesPerStepis exceeded for a step, preventing chain loops from amplifying latency;mode: 'always'providers additionally have a retry cap (alwaysModeRetryCap). - No-config no-op:
enableddefaults to off (false); with norootChain/role chains, unmatched trigger codes, or unresolved roles the plugin is a complete no-op — identical to not being installed, and no events are emitted.
Install
One-line install
dsh plugin --profile web add dsh-llm-fallbacks # pin a version with @<version>
A registry install fetches the built package (dist/) — nothing is built on the target machine. The plugin is mount-only: it never modifies the dsh source tree, and no patch / postinstall step exists — dsh upgrades never require re-patching. Versioning follows npm dist-tags (latest by default); pin an exact version with dsh plugin --profile web add dsh-llm-fallbacks@<version>.
Release status: published as
dsh-llm-fallbacks@0.1.0-alpha.2(latest). The first publish used a one-timeNODE_AUTH_TOKENbootstrap secret; Trusted Publishing is configured afterwards for tokenless releases.
Registry package (npm / pnpm)
npm install dsh-llm-fallbacks # or: pnpm add dsh-llm-fallbacks
Release process → docs/release.md.
Git install (works today)
dsh plugin --profile web add github:omdsh-dev/dsh-llm-fallbacks # pin a commit with #<sha>
A git install fetches sources, not built artifacts — the bundle builds itself on install (prepare self-build), so the target machine needs node + pnpm. pnpm ≥ 10 blocks a git dependency's prepare by default (ERR_PNPM_GIT_DEP_PREPARE_NOT_ALLOWED); allow the build in the profile's pnpm-workspace.yaml and re-run the add (details in docs/install.md).
Local directory install (recommended for development / verification)
# 1) Build in the plugin repo (the prepare self-build runs the pnpm toolchain: tsdown + tsc, no bun)
pnpm install
# 2) Add to the target profile (example: web)
dsh plugin --profile web add .
Development prerequisite: type-checking and tests resolve the real
@deepseek-ai/*packages (peer deps, host-provided at runtime) from the npm registry at0.1.0-rc.6—autoInstallPeers+ the registry auth token in the user-level~/.npmrc(pnpm 11 no longer expands${NPM_TOKEN}from a project.npmrc), no local link farm.
Both methods, uninstall, and
--dump-configverification — including the bundle-layer ordering requirements — are covered in docs/install.md.
Quick start
Minimal configuration
Add a fallbacks: section to the dsh settings document (default $DSH_HOME/settings.yaml):
fallbacks:
enabled: true # feature switch; defaults to false — set explicitly to enable
rootChain: # block 1: root agent's chain; tried in order after the primary model fails
- anthropic/claude-3-5-sonnet
- openai/*
roles: # block 2: declare roles first, then let rules reference them
list:
- id: reviewer # role entity: unique id (/^[a-z0-9-]{1,32}$/); "inherit" is reserved
label: Reviewer
description: Code-review subagents
chain:
- openai/gpt-4o-mini
fallback: inherit-root # default: role chain, then append rootChain
rules:
- origin: subagent # all subagents → reviewer role (own chain + inherited root)
role: reviewer
Roles are declared entities: roles.list holds role cards (id/label/description + chain/fallback), and roles.rules match origin/provider/model in order to a declared role id or the built-in inherit (first match wins) — no rule match → inherit → rootChain. A declared role is only ever hit when a rule references it. A role without a chain is meaningless — the settings card blocks saving it and the host warns at startup; give every declared role a chain, or point its rules at the built-in inherit. (A hand-written YAML role with a missing/empty chain is not fatal: with fallback: inherit-root (the default) an empty chain still falls back to rootChain; with fallback: none an empty chain leaves no candidates and the request passes through untouched — either way it is only a startup warn.) The legacy chain-key namespace and role-default field are gone (migration table: docs/configuration.md).
Save and restart the web session for the changes to take effect. The feature switch fallbacks.enabled defaults to off (false) — the plugin only engages once it is turned on; triggerCodes defaults to AUTH / QUOTA / RATE_LIMIT; and with no rootChain/role chains configured the behavior is identical to not having the plugin installed. More examples (role entities, fallback strategies, rules referencing inherit) → docs/configuration.md.
Upgrade note (behavior change): an existing
fallbacks:section without an explicitenabledkey now resolves tofalseafter upgrading — addenabled: trueto keep the plugin active.
/fallbacks command (in-session diagnostics)
Type /fallbacks in any session to inspect this session's fallback state — no need to open the settings page:
- Session origin (
root/subagent) and the resolved role (theroleof the first matchingroles.rulesentry, otherwise the built-ininherit); - the resolved chain for that role (the role's own chain entries, annotated
(inherit-root)whenrootChainis appended —rootChainentries render in full only when the role has no own chain;fallback: nonewith an empty own chain, or no chain at all, →not configured); - the recent switches (
fallbacks/switchevents, newest first, up to 5): from/to provider/model, role, reason; - the cooldown status: which
provider/modelkeys are currently suppressed and until when.
The command is read-only — it never mutates fallback state (no cooldown reset, no pending-switch writes). It registers through a conditional commands child, so it appears only when the host composes the slash-command registry — with no registry the command is silently unavailable (no top-level inject pollution). Output is zh by default (the host carries no per-session locale signal); the en dictionary lives in the same copy table.
Mount-only (no dsh modification)
The plugin installs as a pure mount — it never modifies the dsh source tree:
- Install = bundle insert + client inject + own gateway:
bundle/cordis.patch.ymlinserts the plugin row over the profile bundle stack,dsh.client.injectmounts the Fallbacks card on the Settings → 插件配置 page, and settings read/write/reset go through the plugin's own gateway channel (/api/fallbacks/get|set|reset). - No patches, no auto-apply step: there are no dsh-body patch files and no install lifecycle step that applies one. A one-line registry install works as-is.
- dsh upgrades never require re-patching: a dsh upgrade that resets the source tree changes nothing for this plugin — it keeps working without any re-apply step.
- Stale leftover patches are harmless: the plugin never depends on a patch export (role resolution is rules-only; the model-selection marker coordination was removed), so a previously patched dsh tree can be left as-is or manually reverted — neither is required.
Developer consumption
Beyond the dsh plugin mount, dsh-llm-fallbacks exposes a programmable consumer surface — both faces share the same function implementations (single point of truth, no copied logic):
- Library API: import the runtime functions and types from the package root —
import { resolveRole, resolveChain, validateFallbacksConfig } from 'dsh-llm-fallbacks'. - Named cordis service: while the plugin is applied,
ctx.get('llm-fallbacks')returns a pure-function service ({ name, version, resolveRole, resolveChain, validateFallbacksConfig, detectLegacyKeys }); after the plugin is disposed it isundefined. Runtime state stays observable viafallbacks/switchevents, not through the service.
Full contract (export inventory, minimal examples, lifecycle, typing) → docs/consumer-api.md.
Documentation
| Doc | Content |
|---|---|
| docs/install.md | profile install / registry install / uninstall / --dump-config verification |
| docs/release.md | release process: Trusted Publishing setup, Release prep SOP, fragment format, rollback |
| docs/configuration.md | full fallbacks namespace reference, selector syntax, example YAML, plugin-config card usage, behavior notes |
| docs/consumer-api.md | developer consumption contract: export inventory, minimal examples, lifecycle, typing |
| 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.
Links
More in this category
franksong2702/dsh-codex-connect★ 5
Connect ChatGPT OAuth and OpenAI Codex models to DeepSeek Harness, with opt-in search and image tools.
omdsh-dev/Qwen-MM-Plugins★ 4
Qwen multi-modal plugin support.
dylan121322/llm-adaptive★ 2
Adaptive model routing: per-request complexity classification with automatic provider routing.
kam74515-boop/dsh-everything-oauth★ 1
Import local Codex, Grok, Claude, OpenCode, and CC Switch logins into DSH; pick sources and enable models in Settings.
suntianc/dsh-codex-auth★ 1
Reuses the Codex CLI ChatGPT login as an `openai-codex` LLM route and adds GPT Auth controls to DSH Web settings.
feibi-mochi/deepseek-harness-wallet★ 1
Multi-provider wallet chip: official DeepSeek balance, per-session cost & tokens, third-party token totals, recharge shortcut, low-balance alerts.