dsh-smart-router · Smart model routing for DeepSeek Harness
English · 简体中文
  
Why this exists
In August 2026, DeepSeek raised prices across the V4 family and introduced peak/off-peak pricing. Flash output tokens jumped from ¥2 to ¥4.5 (¥9 at peak); Pro went up 3–5×. My monthly bill went from ¥120 to ¥700+.
I tried everything. OpenCode Go's "affordable subscription" turned out to be the same official price with a monthly cap. MiMo-V2.5 was cheap but too weak for real coding. GLM-5.1 and Kimi K3 cost more than Flash. Subscription plans didn't help either — Xiaomi's Token Plan Pro covered my usage but couldn't match the quality; Zhipu's Coding Plan was too tight; Google AI Pro and ChatGPT subscriptions don't offer API access.
No single option could deliver both "no quality loss" and "affordable price."
887M tokens/month, 80% cached input — that was my usage profile with DS Flash. The price hike hit output tokens hardest, and most of my output was thinking tokens I couldn't just turn off.
Then it clicked: instead of finding one cheaper model to replace everything, let the same system call different models for different tasks. Simple file operations, formatting, and Q&A go to the cheap model; real reasoning and complex coding stay on Flash. Distribute on demand, not one-size-fits-all.
That's how dsh-smart-router was born. It's not an architectural breakthrough — just a practical tool born from sticker shock, routing each request to the best-fit model so you can find your own balance between capability and cost.
> If AI API bills have ever stung you, hopefully this small tool saves you some money.
What it solves
- Performance tiering: hard tasks (architecture, refactoring, tricky debugging) go to a strong model; easy tasks (small talk, wrap-ups, tiny edits) go to a cheap fast model; everything else hits the middle tier — like cc-switch's per-tier model slots for Claude Code, but for DSH.
- Vision built in: many users add a separate vision plugin; this router integrates vision routing into itself. Requests with images → vision tier automatically; plain text → difficulty tiers. Out of the box it ships a free anonymous vision model (OVHcloud Qwen2.5-VL-72B-Instruct, no API key), replaceable with any vision-capable model you configured.
- Cache friendly: pick all three tiers from the same vendor family (e.g. pro and flash editions of one series) for higher prefix-cache hit rates and lower cost (hint shown in the settings card).
Install
dsh plugin --profile web add dsh-smart-routerRestart dsh web, then:
1. Open Settings → Smart Router: pick a model for each of the four rows (hard / normal / easy / vision). Dropdowns list the models you already configured; the vision row only lists models that declare image input. 2. Back in chat, open the model picker and choose Smart Router (auto route). 3. Chat normally — every request is classified and routed automatically.
> Zero configuration also works: requests fall back to your default model (fail-open, never silently broken).
How it works
You pick the virtual model "smart" (declares text+image, passes DSH image admission)
│
▼
SmartRouterAdapter.stream(request)
│
├─ has image? ──────→ vision tier (free anonymous OVH by default) ──┐
│ │
└─ else: classify ──→ hard → hard tier │
(heuristic by → normal → normal tier │
default / LLM) → easy → easy tier │
│ │
▼ ▼
ctx.llm.prepareCall({provider, model}).stream(request) ← passthrough stream
│
└─ missing tier → hard→normal→easy→default ladder; error only when all fail- Routing happens at the LLM adapter layer (architecture from llm-adaptive / dsh-vision-mix): a virtual provider is registered;
stream()decides per request and delegates viaprepareCall().stream()— no host patches, no conflicts with other vision plugins. - Classifier modes: heuristic (default; zero cost — keywords + code volume + file references) or LLM (more accurate; one small call on the easy-tier model, 120s cache).
- Image admission: the virtual model declares
inputModalities: ['text', 'image'], so DSH's preflight (MODEL_DOES_NOT_SUPPORT_IMAGES) lets images through — no host patching (compare dsh-easyvision's patch approach).
Settings (Settings → Smart Router)
| Setting | Meaning |
|---|---|
| Enable routing | Off = requests go to the session default model unchanged |
| Classifier | heuristic (default) / llm |
| Hard / Normal / Easy | { provider, model, effort } per tier; empty = unconfigured (ladder fallback) |
| Vision | Default ovh-vision / Qwen2.5-VL-72B-Instruct (free, anonymous); replace with any configured vision model |
| Default fallback | Empty = the session default model |
| LLM classifier | Optional; defaults to the easy-tier model |
Reasoning effort two-tier hierarchy: The reasoning effort in the chat input model selector (Off / High) is the master switch:
- Off: reasoning disabled for all tiers (no extended thinking)
- High: each tier uses the effort configured above (e.g. hardEffort=high, normalEffort=max, etc.)
A hint explaining this is shown in the tier models section of the settings page.
Manual config (~/.dsh/profiles/web/settings.yaml):
smart-router:
enabled: true
classifier: heuristic # heuristic | llm
hardProvider: deepseek-official
hardModel: deepseek-v4-pro
normalProvider: deepseek-official
normalModel: deepseek-chat
easyProvider: deepseek-official
easyModel: deepseek-flash
visionProvider: zhipu-vision # free quota; add GLM_API_KEY under Settings → Models
visionModel: glm-4v-flash
visionFallbacks: []
fallbackProvider: ''
fallbackModel: ''On install the plugin idempotently seeds two free vision routes into llm-pi-ai (missing keys only, your config is never touched):
| Route | Model | Cost |
|---|---|---|
ovh-vision | OVHcloud Qwen2.5-VL-72B-Instruct (anonymous endpoint) | Free, no key (~2 req/min/IP) |
zhipu-vision | Zhipu GLM-4V-Flash | Free quota; set GLM_API_KEY in Settings → Models |
> The vision default works out of the box; for more stable/stronger vision, point the vision row at any vision model you configured.
Coexistence with other vision plugins
The router only participates when the session model is smart; it never takes over existing provider routes and never touches the host, so modlens, dsh-vision-router and friends can stay installed side by side.
Development
npm test # node --test tests/ (80 cases: classifier / chain / vision sidecar / config API / seed idempotency / schema)Local install for debugging:
dsh plugin --profile web add C:\path\to\dsh-smart-router
dsh --profile web --dump-config | grep smart-routerAcknowledgements (References)
This plugin directly references the following open-source projects and DSH internals:
| Project | What was referenced |
|---|---|
| farion1231/cc-switch | The per-tier model slot product shape (Claude Code's main/fast/thinking/vision tiers) |
| dylan121322/llm-adaptive | Adapter-level routing: registerAdapter + prepareCall().stream() passthrough; LLM classifier rubric |
| BruceLanLan/dsh-tier-router | Tier config schema shape and fallback/escalation trade-offs |
| haiziyao/dsh-vision-mix | Declaring inputModalities: ['text','image'] at the adapter to pass DSH image admission (zero host patches) |
| liustack/modlens | Structured vision evidence: summary/OCR/layout/semantics/visual/uncertainty template and the "vision parsing engine" prompt (source-level reference) |
| gloryxpnv/dsh-tool-vision | Same structured JSON evidence template; the vision-bridge image→text replacement and fail-open pattern |
| ysr666/dsh-vision-router | Free vision chain: anonymous OVHcloud endpoint (no key); image-admission analysis; per-image caching |
| s3yf1337/dsh-easyvision | Reading model vision capability via resolveModelInfo().inputModalities |
| akqwpeter-prog/dsh-media-skills | Idempotent free-vision route seeding into llm-pi-ai (zhipu-vision) |
| DeepSeek Harness internals | dsh-llm (LlmAdapter / llm service / prepareCall contract), dsh-settings (installSettingsSection), dsh-agent-loop (agent/request waterfall), dsh-client-modules (client bundle contract) |
License
MIT