Skip to main content
Glama

sunny-dispatch

Zero-dependency semantic task router. Send grunt work to free/local models, keep premium tokens for judgement.

Most LLM routers (LiteLLM, OpenRouter's own routing, gateway proxies) pick a model by name, price, or latency. None of them look at what the task is. sunny-dispatch classifies the task itself with deterministic, user-editable rules (zero tokens spent deciding) and routes it to the cheapest tier that can do it: summarizing a log goes to your local Ollama model for free; a security review goes to a strong model; everything routes BYO-key, so this project never touches your spend.

sunny "summarize this build log: <paste>"
[routed: local | qwen2.5-coder:7b | FREE/local | 2841ms]

Install

npm i -g sunny-dispatch     # or: npx sunny-dispatch --help

Requirements: Node 18+. For the free tier: Ollama with a model pulled (default qwen2.5-coder:7b). For cloud tiers: OPENROUTER_API_KEY (or point any tier at any OpenAI-compatible endpoint).

Related MCP server: ai-workers-mcp

Use as a CLI

sunny "<task>"                  # dispatch, auto-routed
sunny --tier strong "<task>"    # force a tier
sunny route "<task>"            # preview where it would go, spend nothing
sunny doctor                    # check ollama, keys, config

Result goes to stdout, routing info to stderr, so it pipes cleanly.

Use from Claude Code (MCP)

claude mcp add sunny -- npx -y sunny-dispatch mcp

Claude gets two tools: dispatch (offload a self-contained sub-task, get the result back as text) and route_preview (see the routing decision for free).

Tiers and cost

tier

default model

$/1M in

$/1M out

local

qwen2.5-coder:7b (Ollama)

0

0

cheap

deepseek/deepseek-v4-flash

0.14

0.28

strong

deepseek/deepseek-v4-pro

0.435

0.87

quality

anthropic/claude-haiku-4.5

1.00

5.00

Every model, endpoint, and price is a default, not a decision: override any of it in config. Prices drift; edit yours to match reality.

Configure

sunny.config.json in the working directory (or point SUNNY_CONFIG at a path). Everything is optional; user rules replace the default rules.

{
  "rules": [
    { "tier": "quality", "pattern": "\\b(flashcard|anki)", "reason": "study content, never downgrade" },
    { "tier": "strong",  "pattern": "\\b(architect|security|debug)", "reason": "needs reasoning" },
    { "tier": "local",   "pattern": "\\b(summar|translat|format|extract)", "reason": "mechanical" }
  ],
  "routes": {
    "local":  { "model": "llama3.2:3b" },
    "strong": { "provider": "openai", "baseUrl": "https://api.deepseek.com/chat/completions", "keyEnv": "DEEPSEEK_API_KEY", "model": "deepseek-chat", "inUsd": 0.27, "outUsd": 1.1 }
  },
  "shortTaskChars": 240,
  "shortTaskTier": "local",
  "defaultTier": "cheap"
}

Rules run in order, first match wins; unmatched short tasks go local, everything else to defaultTier. sunny route "<task>" is the fast way to check what your rules do.

Why zero dependencies

The whole thing is three small files on Node builtins: the router core, a CLI, and a hand-rolled MCP stdio server. Nothing phones home, nothing to audit beyond what fits in one sitting, npm i installs nothing else.

License

MIT © Wukoric LLC

Related MCP Connectors

Related MCP Servers