awerouter: Smart LLM Router
Route cheap/fast tasks to Flash, hard decisions to Pro.
Transparent same-protocol proxy that routes coding-agent requests by structural signals — no keyword guessing, no LLM classifier. Speaks Anthropic Messages, OpenAI Chat Completions, and OpenAI Responses.
English · 简体中文
Transparent proxy that splits coding-agent traffic across providers by cost and capability. Same-protocol passthrough — no translation.
Install
pip install awerouter
Quick Start
# 1. Init config (creates ~/.config/awerouter/{providers,routing}.json)
awerouter init
# 2. Interactively add a profile (writes both files, references stay consistent)
awerouter add
# or edit by hand: providers.json for keys (${ENV_VAR}), routing.json for flash/pro
# 3. Start the daemon (profile name optional when only one exists)
awerouter serve [cc-router-1] # shorthand: awerouter cc-router-1
# 4. Point CC at it — the serve banner prints both lines below
export ANTHROPIC_BASE_URL=http://127.0.0.1:20128
# aweswitch profile env: ANTHROPIC_MODEL=auto, _HAIKU_=flash, _OPUS_=pro
Config
Two files in ~/.config/awerouter/ (override with AWEROUTER_CONFIG_DIR):
providers.json — endpoints + keys, grouped by wire protocol (redacted in config show):
{
"anthropic": {
"stepfun": { "base_url": "https://api.stepfun.com/step_plan", "auth": "${STEPFUN_AUTH_TOKEN}" },
"anthropic": { "base_url": "https://api.anthropic.com", "auth": "${ANTHROPIC_KEY}" }
},
"openai-chat": {
"stepfun": { "base_url": "https://api.stepfun.com", "auth": "${STEPFUN_AUTH_TOKEN}" }
},
"openai-responses": {
"openai": { "base_url": "https://api.openai.com", "auth": "${OPENAI_API_KEY}" }
}
}
Three protocols are supported, each with its own endpoint appended to base_url:
| Protocol id | Endpoint | Typical client |
|---|---|---|
anthropic |
base_url + /v1/messages |
Claude Code (ANTHROPIC_BASE_URL) |
openai-chat |
base_url + /v1/chat/completions |
OpenAI-compatible clients; Codex with wire_api = "chat" |
openai-responses |
base_url + /v1/responses |
Codex (wire_api = "responses") |
base_url is the prefix before the endpoint path — for openai providers, drop the trailing /v1.
The auth header is auto-detected from base_url: anthropic.com → x-api-key (bare token); everyone else → Authorization (auto-prefixes Bearer ). No auth_header field needed unless the heuristic is wrong.
routing.json — strategy, no secrets (safe to commit):
{
"settings": {
"backgroundModel": "flash",
"thinkModel": "pro"
},
"cc-router-1": {
"protocol": "anthropic",
"longContextThreshold": 8000,
"destinations": {
"flash": "stepfun,step-3.7-flash",
"pro": "anthropic,claude-opus-5"
}
}
}
settings is optional (defaults: flash/pro). It defines the model ids CC sends for background (Haiku) and think (Opus) tiers. The main loop uses auto — routed by difficulty by L3. Set these in your aweswitch profile: ANTHROPIC_DEFAULT_HAIKU_MODEL=flash, ANTHROPIC_MODEL=auto, ANTHROPIC_DEFAULT_OPUS_MODEL=pro.
Keys reference ${ENV_VAR} syntax. Missing env vars die with a clear message at startup.
Profile-based routing:
routing.jsongroups configs under profile ids (like aweswitch).awerouter serve <profile>starts one; with a single profile it auto-selects.protocolmaps the profile to a providers.json group and decides which endpoint it serves — the serve banner prints the matching client env (ANTHROPIC_BASE_URLfor Claude Code,OPENAI_BASE_URL/ Codexwire_apifor the openai protocols). Note: openai clients are single-model, so L2 tier labels effectively never fire for them — openai traffic routes by L1 + L3 with a flash default.
How It Routes
Three-layer first-match-wins pipeline, evaluated per request:
| Layer | Signal | Decision |
|---|---|---|
| L1 Capability | web_search tool in body |
pro (flash can't run it) |
| L2 Tier label | model == c1/flash or c1/think |
flash / pro respectively |
| L3 Difficulty | token count > threshold, or has image | pro; else flash |
CC's /model picker sets the tier model id (c1/flash / c1/pro / c1/think). awerouter reads it and routes accordingly — no keyword parsing, no LLM classifier.
Commands
awerouter init # create default config (= config init)
awerouter add # interactively add a profile (and new providers)
awerouter list # list profiles (name, protocol, flash, pro, threshold)
awerouter show [PROFILE] # show one profile or all config (redacted)
awerouter serve [PROFILE] [--port 20128] [--host 127.0.0.1]
awerouter <PROFILE> # shorthand for serve PROFILE
awerouter config path | show | edit | init
awerouter log [--lines 20]
awerouter stats
awerouter calibrate
calibrate shows the message-token distribution of L3 traffic (the threshold-sensitive layer; messages only — system prompt and tools are excluded) and suggests candidate longContextThreshold values at p90/p95/p99. Run it after some real traffic, then edit routing.json.
Troubleshooting
CC shows 502 status code (no body) right after launch — a shell proxy (Clash etc.) is hijacking loopback traffic. Requests to 127.0.0.1:20128 go into the proxy, whose 127.0.0.1 is itself, so nothing is listening and the proxy returns an empty 502. serve prints a warning when it detects this; fix it by exempting loopback in your shell config:
export no_proxy=127.0.0.1,localhost NO_PROXY=127.0.0.1,localhost
Then open a new terminal and relaunch CC.
Development
git clone https://github.com/mugpeng/awerouter
cd awerouter
pip install -e ".[dev]"
pytest
See docs/CONTRIBUTING.md for architecture notes, config semantics, and the release process.
Support
If awerouter saves you money, consider supporting it:
- ⭐ Star the repo — it helps others find it.
- ☕ Ko-fi — buy me a coffee.
- 💬 WeChat — scan the QR code below.
awerouter is free and open source. Sponsors keep it maintained — thank you.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file awerouter-0.2.5.tar.gz.
File metadata
- Download URL: awerouter-0.2.5.tar.gz
- Upload date:
- Size: 157.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.13.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
17a0c21273a4c8bbc4bf5f1dcd6ef3c9402037f381ebe8967923f8cd2508f3d0
|
|
| MD5 |
53a209835efac0984cd1b3591e5991d1
|
|
| BLAKE2b-256 |
2ef4d3a8c331c7e8e13e7a6c48c189047f78647901a52cc893d179c8e35cdae4
|
File details
Details for the file awerouter-0.2.5-py3-none-any.whl.
File metadata
- Download URL: awerouter-0.2.5-py3-none-any.whl
- Upload date:
- Size: 26.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/7.0.0 CPython/3.13.15
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
747cf896f873f1f4a346ee479a2e1b48169096283b6c711394f5bdcc3c082516
|
|
| MD5 |
4aa150f88a1298e2daa67825937749e9
|
|
| BLAKE2b-256 |
290b734ef32c83d13cddb26c00ae745aca37c69258c0b5eada40c6933c2d5c7f
|