awerouter 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. awerouter-0.2.0/LICENSE +207 -0
  2. awerouter-0.2.0/MANIFEST.in +5 -0
  3. awerouter-0.2.0/PKG-INFO +182 -0
  4. awerouter-0.2.0/README.md +159 -0
  5. awerouter-0.2.0/README_cn.md +159 -0
  6. awerouter-0.2.0/assets/images/wechat-pay.jpg +0 -0
  7. awerouter-0.2.0/docs/CHANGELOG.md +63 -0
  8. awerouter-0.2.0/docs/CONTRIBUTING.md +133 -0
  9. awerouter-0.2.0/docs/archive/PLAN.md +169 -0
  10. awerouter-0.2.0/docs/todo/code-quality.md +105 -0
  11. awerouter-0.2.0/docs/todo/l3-complexity.md +53 -0
  12. awerouter-0.2.0/docs/todo/l3-threshold.md +79 -0
  13. awerouter-0.2.0/pyproject.toml +44 -0
  14. awerouter-0.2.0/setup.cfg +4 -0
  15. awerouter-0.2.0/src/awerouter/__init__.py +1 -0
  16. awerouter-0.2.0/src/awerouter/cli.py +224 -0
  17. awerouter-0.2.0/src/awerouter/config.py +416 -0
  18. awerouter-0.2.0/src/awerouter/default-providers.json +9 -0
  19. awerouter-0.2.0/src/awerouter/default-routing.json +15 -0
  20. awerouter-0.2.0/src/awerouter/logging.py +216 -0
  21. awerouter-0.2.0/src/awerouter/router.py +137 -0
  22. awerouter-0.2.0/src/awerouter/server.py +397 -0
  23. awerouter-0.2.0/src/awerouter/types.py +70 -0
  24. awerouter-0.2.0/src/awerouter.egg-info/PKG-INFO +182 -0
  25. awerouter-0.2.0/src/awerouter.egg-info/SOURCES.txt +32 -0
  26. awerouter-0.2.0/src/awerouter.egg-info/dependency_links.txt +1 -0
  27. awerouter-0.2.0/src/awerouter.egg-info/entry_points.txt +2 -0
  28. awerouter-0.2.0/src/awerouter.egg-info/requires.txt +6 -0
  29. awerouter-0.2.0/src/awerouter.egg-info/top_level.txt +1 -0
  30. awerouter-0.2.0/tests/test_cli.py +148 -0
  31. awerouter-0.2.0/tests/test_config.py +343 -0
  32. awerouter-0.2.0/tests/test_logging.py +175 -0
  33. awerouter-0.2.0/tests/test_router.py +153 -0
  34. awerouter-0.2.0/tests/test_server.py +386 -0
@@ -0,0 +1,207 @@
1
+ Mozilla Public License Version 2.0
2
+
3
+ 1. Definitions
4
+ --------------
5
+
6
+ 1.1. "Contributor"
7
+ means each individual or legal entity that creates, contributes to
8
+ the creation of, or owns Covered Software.
9
+
10
+ 1.2. "Contributor Version"
11
+ means the combination of the Contributions of others (if any) used
12
+ by a Contributor and that particular Contributor's Contribution.
13
+
14
+ 1.3. "Contribution"
15
+ means Covered Software of a particular Contributor.
16
+
17
+ 1.4. "Covered Software"
18
+ means Source Code Form to which the initial Contributor has attached
19
+ the notice in Exhibit A, the Executable Form of such Source Code
20
+ Form, and Modifications of such Source Code Form, in each case
21
+ including portions thereof.
22
+
23
+ 1.5. "Incompatible With Secondary Licenses"
24
+ means that the work at issue cannot be redistributed under the terms
25
+ of this License without violating, conflicting with, or otherwise
26
+ being incompatible with the terms of the Secondary License.
27
+
28
+ 1.6. "Larger Work"
29
+ means a work that combines Covered Software with other material, in
30
+ a separate file or files, that is not Covered Software.
31
+
32
+ 1.7. "License"
33
+ means this document.
34
+
35
+ 1.8. "Licensable"
36
+ means having the right to grant, to the maximum extent possible,
37
+ whether at the time of the initial grant or subsequently, any and
38
+ all of the rights conveyed by this License.
39
+
40
+ 1.9. "Modifications"
41
+ means any of the following:
42
+
43
+ (a) any addition to or deletion from the substance or structure of
44
+ the Covered Software; or
45
+
46
+ (b) any new Source Code Form to which the initial Contributor has
47
+ attached the notice in Exhibit A and which constitutes Covered
48
+ Software.
49
+
50
+ 1.10. "Patent Claims" of a Contributor
51
+ means any patent claim, including without limitation, method,
52
+ process, and apparatus claims, in any patent Licensable by such
53
+ Contributor that would be infringed, but for the grant of the
54
+ License, by the making, using, selling, offering for sale, having
55
+ made, import, or transfer of either its Contributions or its
56
+ Contributor Version.
57
+
58
+ 1.11. "Secondary License"
59
+ means either the GNU General Public License, Version 2.0, or any
60
+ later version of such license published by the Free Software
61
+ Foundation; or the GNU Lesser General Public License, Version 2.1,
62
+ or any later version of such license published by the Free Software
63
+ Foundation; or the GNU Affero General Public License, Version 3.0,
64
+ or any later version of such license published by the Free Software
65
+ Foundation.
66
+
67
+ 1.12. "Source Code Form"
68
+ means the form of software preferred for making modifications.
69
+
70
+ 1.13. "You" (or "Your")
71
+ means an individual or a legal entity exercising rights under this
72
+ License.
73
+
74
+ 2. License Grants and Conditions
75
+ --------------------------------
76
+
77
+ 2.1. Grants
78
+
79
+ Each Contributor hereby grants You a world-wide, royalty-free,
80
+ non-exclusive license, subject to third-party intellectual property
81
+ claims, under intellectual property rights (other than patent or
82
+ trademark) Licensable by such Contributor to use, reproduce, make
83
+ available, modify, display, perform, distribute, and otherwise
84
+ exploit its Contributions, either on an unmodified basis, with
85
+ Modifications, or as part of a Larger Work; and under Patent Claims
86
+ of such Contributor to make, use, sell, offer for sale, have made,
87
+ import, and otherwise transfer either its Contributions or its
88
+ Contributor Version.
89
+
90
+ 2.2. Effective Date
91
+
92
+ The licenses granted in Section 2.1 with respect to any Contribution
93
+ become effective for each Contribution on the date the Contributor
94
+ first distributes such Contribution.
95
+
96
+ 2.3. Limitations on Grant Scope
97
+
98
+ Nothing in this License shall be construed as excluding or limiting
99
+ any implied license or other defense to infringement that may
100
+ otherwise be available to You under applicable patent law.
101
+
102
+ 2.4. Subsequent Licenses
103
+
104
+ No Contributor makes additional grants as a result of Your choice to
105
+ distribute the Covered Software under a subsequent version of this
106
+ License or under the terms of a Secondary License (if permitted
107
+ under the terms of this License).
108
+
109
+ 2.5. Representation
110
+
111
+ Each Contributor represents that the Contributor believes its
112
+ Contributions are its original creation(s) or it has sufficient
113
+ rights to grant the rights to its Contributions conveyed by this
114
+ License.
115
+
116
+ 3. Obligations
117
+ --------------
118
+
119
+ 3.1. Distribution of Source Code Form
120
+
121
+ All distribution of Covered Software in Source Code Form, including
122
+ any Modifications that You create or to which You contribute, must
123
+ be under the terms of this License. You must inform recipients that
124
+ the Source Code Form of the Covered Software is subject to the terms
125
+ of this License, and how they can obtain a copy of this License.
126
+
127
+ 3.2. Distribution of Executable Form
128
+
129
+ If You distribute Covered Software in Executable Form then:
130
+
131
+ (a) such Covered Software must also be made available in Source Code
132
+ Form; and
133
+
134
+ (b) You may distribute such Executable Form under the terms of this
135
+ License; and
136
+
137
+ (c) You must include a copy of this License with every copy of the
138
+ Covered Software in Executable Form You distribute; and
139
+
140
+ (d) You may not prevent recipients from copying, modifying, or
141
+ further distributing the Covered Software in Source Code Form.
142
+
143
+ 3.3. Distribution of a Larger Work
144
+
145
+ You may create and distribute a Larger Work under terms of Your
146
+ choice, provided that You also comply with the requirements of this
147
+ License for the Covered Software.
148
+
149
+ 3.4. Notices
150
+
151
+ You may not remove or alter the substance of any license notices
152
+ (including copyright notices, patent notices, disclaimers of
153
+ warranty, or limitations of liability) contained within the Source
154
+ Code Form of the Covered Software.
155
+
156
+ 3.5. Application of Additional Terms
157
+
158
+ You may not offer or impose any additional or different terms on any
159
+ portion of the Covered Software that would alter or restrict the
160
+ applicable version of this License or the recipients' rights
161
+ hereunder.
162
+
163
+ 4. Inability to Comply Due to Statute or Regulation
164
+ ---------------------------------------------------
165
+
166
+ If it is impossible for You to comply with any of the terms of this
167
+ License with respect to some or all of the Covered Software due to
168
+ statute, judicial order, or regulation then You must: (a) comply with
169
+ the terms of this License to the maximum extent possible; and (b)
170
+ describe the limitations and the code they affect.
171
+
172
+ 5. Termination
173
+ --------------
174
+
175
+ 5.1. The rights granted under this License will terminate automatically
176
+ if You fail to comply with any of its terms. However, if You become
177
+ compliant, then the rights granted under this License are
178
+ reinstated.
179
+
180
+ 5.2. If You initiate litigation against any entity by asserting a patent
181
+ infringement claim (excluding declaratory judgment actions,
182
+ counter-claims, and cross-claims) alleging that a Contributor
183
+ Version directly or indirectly infringes any patent, then the rights
184
+ granted to You by any and all Contributors for the Covered Software
185
+ under Sections 2.1 and 2.2 shall terminate.
186
+
187
+ 6. Disclaimer of Warranty
188
+ -------------------------
189
+
190
+ Covered Software is provided under this License on an "as is" basis,
191
+ without warranty of any kind, either express or implied.
192
+
193
+ 7. Limitation of Liability
194
+ ---------------------------
195
+
196
+ To the extent permitted by applicable law, in no event shall any
197
+ Contributor be liable to You for any indirect, special, incidental, or
198
+ consequential damages.
199
+
200
+ 8. Miscellaneous
201
+ ----------------
202
+
203
+ 8.1. This License represents the complete agreement concerning subject
204
+ matter hereof.
205
+
206
+ 8.2. If any provision of this License is held to be unenforceable, it
207
+ shall be reformed only to the minimum extent necessary.
@@ -0,0 +1,5 @@
1
+ include LICENSE
2
+ include README.md
3
+ include README_cn.md
4
+ recursive-include assets *.jpg *.png
5
+ recursive-include docs *.md
@@ -0,0 +1,182 @@
1
+ Metadata-Version: 2.4
2
+ Name: awerouter
3
+ Version: 0.2.0
4
+ Summary: Smart LLM router: fast cheap tasks to flash, hard decisions to pro.
5
+ Author: Peng
6
+ License: MPL-2.0
7
+ Keywords: ai,llm,router,claude,proxy,claude-code
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Topic :: Utilities
14
+ Requires-Python: >=3.9
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: click>=8.1
18
+ Requires-Dist: aiohttp>=3.9
19
+ Provides-Extra: dev
20
+ Requires-Dist: pytest>=8; extra == "dev"
21
+ Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
22
+ Dynamic: license-file
23
+
24
+ <div align="center">
25
+ <h1>awerouter: Smart LLM Router</h1>
26
+ <p><strong>Route cheap/fast tasks to Flash, hard decisions to Pro.</strong></p>
27
+ <p>Transparent Anthropic proxy that routes Claude Code requests by structural signals — no keyword guessing, no LLM classifier.</p>
28
+ <p>
29
+ <strong>English</strong> ·
30
+ <a href="./README_cn.md">简体中文</a>
31
+ </p>
32
+ <p>
33
+ <img src="https://img.shields.io/pypi/v/awerouter?style=flat-square&color=7C3AED" alt="Version">
34
+ <img src="https://img.shields.io/badge/python-%E2%89%A53.9-0EA5E9?style=flat-square" alt="Python">
35
+ <img src="https://img.shields.io/badge/license-MPL--2.0-22C55E?style=flat-square" alt="License">
36
+ </p>
37
+ <p>
38
+ <img src="https://img.shields.io/badge/status-alpha-c96a3d?style=flat-square" alt="Status">
39
+ <img src="https://img.shields.io/badge/install-pip-22C55E?style=flat-square" alt="pip">
40
+ <img src="https://img.shields.io/badge/platform-terminal-334155?style=flat-square" alt="Platform">
41
+ <img src="https://img.shields.io/pypi/dm/awerouter?style=flat-square" alt="Downloads">
42
+ <img src="https://img.shields.io/github/stars/mugpeng/awerouter?style=flat-square" alt="Stars">
43
+ </p>
44
+ <p>
45
+ <a href="https://ko-fi.com/mugpeng"><img src="https://img.shields.io/badge/Ko--fi-Buy%20me%20a%20coffee-FF5E5B?style=flat-square&logo=ko-fi&logoColor=white" alt="Ko-fi"></a>
46
+ </p>
47
+ </div>
48
+
49
+ > Transparent proxy that splits Claude Code traffic across providers by cost and capability.
50
+
51
+ ## Install
52
+
53
+ ```bash
54
+ pip install awerouter
55
+ ```
56
+
57
+ ## Quick Start
58
+
59
+ ```bash
60
+ # 1. Init config (creates ~/.config/awerouter/{providers,routing}.json)
61
+ awerouter init
62
+
63
+ # 2. Interactively add a profile (writes both files, references stay consistent)
64
+ awerouter add
65
+ # or edit by hand: providers.json for keys (${ENV_VAR}), routing.json for flash/pro
66
+
67
+ # 3. Start the daemon (profile name optional when only one exists)
68
+ awerouter serve [cc-router-1] # shorthand: awerouter cc-router-1
69
+
70
+ # 4. Point CC at it — the serve banner prints both lines below
71
+ export ANTHROPIC_BASE_URL=http://127.0.0.1:20128
72
+ # aweswitch profile env: ANTHROPIC_MODEL=auto, _HAIKU_=flash, _OPUS_=pro
73
+ ```
74
+
75
+ ## Config
76
+
77
+ Two files in `~/.config/awerouter/` (override with `AWEROUTER_CONFIG_DIR`):
78
+
79
+ **providers.json** — endpoints + keys, grouped by agent (redacted in `config show`):
80
+
81
+ ```json
82
+ {
83
+ "claude": {
84
+ "stepfun": { "base_url": "https://api.stepfun.com/step_plan", "auth": "${STEPFUN_AUTH_TOKEN}" },
85
+ "anthropic": { "base_url": "https://api.anthropic.com", "auth": "${ANTHROPIC_KEY}" }
86
+ },
87
+ "codex": {
88
+ "stepfun": { "base_url": "https://api.stepfun.com/v1", "auth": "${STEPFUN_AUTH_TOKEN}" }
89
+ }
90
+ }
91
+ ```
92
+
93
+ 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.
94
+
95
+ **routing.json** — strategy, no secrets (safe to commit):
96
+
97
+ ```json
98
+ {
99
+ "settings": {
100
+ "backgroundModel": "flash",
101
+ "thinkModel": "pro"
102
+ },
103
+ "cc-router-1": {
104
+ "agent": "claude",
105
+ "longContextThreshold": 8000,
106
+ "destinations": {
107
+ "flash": "stepfun,step-3.7-flash",
108
+ "pro": "anthropic,claude-opus-5"
109
+ }
110
+ }
111
+ }
112
+ ```
113
+
114
+ `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`.
115
+
116
+ Keys reference `${ENV_VAR}` syntax. Missing env vars die with a clear message at startup.
117
+
118
+ > **Profile-based routing:** `routing.json` groups configs under profile ids (like aweswitch). `awerouter serve <profile>` starts one; with a single profile it auto-selects. `agent` maps the profile to a providers.json group.
119
+
120
+ ## How It Routes
121
+
122
+ Three-layer first-match-wins pipeline, evaluated per request:
123
+
124
+ | Layer | Signal | Decision |
125
+ |-------|--------|----------|
126
+ | L1 Capability | `web_search` tool in body | **pro** (flash can't run it) |
127
+ | L2 Tier label | `model == c1/flash` or `c1/think` | flash / pro respectively |
128
+ | L3 Difficulty | token count > threshold, or has image | **pro**; else **flash** |
129
+
130
+ 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.
131
+
132
+ ## Commands
133
+
134
+ ```bash
135
+ awerouter init # create default config (= config init)
136
+ awerouter add # interactively add a profile (and new providers)
137
+ awerouter list # list profiles (name, agent, flash, pro, threshold)
138
+ awerouter show [PROFILE] # show one profile or all config (redacted)
139
+ awerouter serve [PROFILE] [--port 20128] [--host 127.0.0.1]
140
+ awerouter <PROFILE> # shorthand for serve PROFILE
141
+ awerouter config path | show | edit | init
142
+ awerouter log [--lines 20]
143
+ awerouter stats
144
+ awerouter calibrate
145
+ ```
146
+
147
+ `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`.
148
+
149
+ ## Troubleshooting
150
+
151
+ **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:
152
+
153
+ ```bash
154
+ export no_proxy=127.0.0.1,localhost NO_PROXY=127.0.0.1,localhost
155
+ ```
156
+
157
+ Then open a new terminal and relaunch CC.
158
+
159
+ ## Development
160
+
161
+ ```bash
162
+ git clone https://github.com/mugpeng/awerouter
163
+ cd awerouter
164
+ pip install -e ".[dev]"
165
+ pytest
166
+ ```
167
+
168
+ See [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md) for architecture notes, config semantics, and the release process.
169
+
170
+ ## Support
171
+
172
+ If awerouter saves you money, consider supporting it:
173
+
174
+ - ⭐ Star the repo — it helps others find it.
175
+ - ☕ [Ko-fi](https://ko-fi.com/mugpeng) — buy me a coffee.
176
+ - 💬 WeChat — scan the QR code below.
177
+
178
+ <p align="center">
179
+ <img src="assets/images/wechat-pay.jpg" alt="WeChat Pay" width="240">
180
+ </p>
181
+
182
+ > awerouter is free and open source. Sponsors keep it maintained — thank you.
@@ -0,0 +1,159 @@
1
+ <div align="center">
2
+ <h1>awerouter: Smart LLM Router</h1>
3
+ <p><strong>Route cheap/fast tasks to Flash, hard decisions to Pro.</strong></p>
4
+ <p>Transparent Anthropic proxy that routes Claude Code requests by structural signals — no keyword guessing, no LLM classifier.</p>
5
+ <p>
6
+ <strong>English</strong> ·
7
+ <a href="./README_cn.md">简体中文</a>
8
+ </p>
9
+ <p>
10
+ <img src="https://img.shields.io/pypi/v/awerouter?style=flat-square&color=7C3AED" alt="Version">
11
+ <img src="https://img.shields.io/badge/python-%E2%89%A53.9-0EA5E9?style=flat-square" alt="Python">
12
+ <img src="https://img.shields.io/badge/license-MPL--2.0-22C55E?style=flat-square" alt="License">
13
+ </p>
14
+ <p>
15
+ <img src="https://img.shields.io/badge/status-alpha-c96a3d?style=flat-square" alt="Status">
16
+ <img src="https://img.shields.io/badge/install-pip-22C55E?style=flat-square" alt="pip">
17
+ <img src="https://img.shields.io/badge/platform-terminal-334155?style=flat-square" alt="Platform">
18
+ <img src="https://img.shields.io/pypi/dm/awerouter?style=flat-square" alt="Downloads">
19
+ <img src="https://img.shields.io/github/stars/mugpeng/awerouter?style=flat-square" alt="Stars">
20
+ </p>
21
+ <p>
22
+ <a href="https://ko-fi.com/mugpeng"><img src="https://img.shields.io/badge/Ko--fi-Buy%20me%20a%20coffee-FF5E5B?style=flat-square&logo=ko-fi&logoColor=white" alt="Ko-fi"></a>
23
+ </p>
24
+ </div>
25
+
26
+ > Transparent proxy that splits Claude Code traffic across providers by cost and capability.
27
+
28
+ ## Install
29
+
30
+ ```bash
31
+ pip install awerouter
32
+ ```
33
+
34
+ ## Quick Start
35
+
36
+ ```bash
37
+ # 1. Init config (creates ~/.config/awerouter/{providers,routing}.json)
38
+ awerouter init
39
+
40
+ # 2. Interactively add a profile (writes both files, references stay consistent)
41
+ awerouter add
42
+ # or edit by hand: providers.json for keys (${ENV_VAR}), routing.json for flash/pro
43
+
44
+ # 3. Start the daemon (profile name optional when only one exists)
45
+ awerouter serve [cc-router-1] # shorthand: awerouter cc-router-1
46
+
47
+ # 4. Point CC at it — the serve banner prints both lines below
48
+ export ANTHROPIC_BASE_URL=http://127.0.0.1:20128
49
+ # aweswitch profile env: ANTHROPIC_MODEL=auto, _HAIKU_=flash, _OPUS_=pro
50
+ ```
51
+
52
+ ## Config
53
+
54
+ Two files in `~/.config/awerouter/` (override with `AWEROUTER_CONFIG_DIR`):
55
+
56
+ **providers.json** — endpoints + keys, grouped by agent (redacted in `config show`):
57
+
58
+ ```json
59
+ {
60
+ "claude": {
61
+ "stepfun": { "base_url": "https://api.stepfun.com/step_plan", "auth": "${STEPFUN_AUTH_TOKEN}" },
62
+ "anthropic": { "base_url": "https://api.anthropic.com", "auth": "${ANTHROPIC_KEY}" }
63
+ },
64
+ "codex": {
65
+ "stepfun": { "base_url": "https://api.stepfun.com/v1", "auth": "${STEPFUN_AUTH_TOKEN}" }
66
+ }
67
+ }
68
+ ```
69
+
70
+ 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.
71
+
72
+ **routing.json** — strategy, no secrets (safe to commit):
73
+
74
+ ```json
75
+ {
76
+ "settings": {
77
+ "backgroundModel": "flash",
78
+ "thinkModel": "pro"
79
+ },
80
+ "cc-router-1": {
81
+ "agent": "claude",
82
+ "longContextThreshold": 8000,
83
+ "destinations": {
84
+ "flash": "stepfun,step-3.7-flash",
85
+ "pro": "anthropic,claude-opus-5"
86
+ }
87
+ }
88
+ }
89
+ ```
90
+
91
+ `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`.
92
+
93
+ Keys reference `${ENV_VAR}` syntax. Missing env vars die with a clear message at startup.
94
+
95
+ > **Profile-based routing:** `routing.json` groups configs under profile ids (like aweswitch). `awerouter serve <profile>` starts one; with a single profile it auto-selects. `agent` maps the profile to a providers.json group.
96
+
97
+ ## How It Routes
98
+
99
+ Three-layer first-match-wins pipeline, evaluated per request:
100
+
101
+ | Layer | Signal | Decision |
102
+ |-------|--------|----------|
103
+ | L1 Capability | `web_search` tool in body | **pro** (flash can't run it) |
104
+ | L2 Tier label | `model == c1/flash` or `c1/think` | flash / pro respectively |
105
+ | L3 Difficulty | token count > threshold, or has image | **pro**; else **flash** |
106
+
107
+ 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.
108
+
109
+ ## Commands
110
+
111
+ ```bash
112
+ awerouter init # create default config (= config init)
113
+ awerouter add # interactively add a profile (and new providers)
114
+ awerouter list # list profiles (name, agent, flash, pro, threshold)
115
+ awerouter show [PROFILE] # show one profile or all config (redacted)
116
+ awerouter serve [PROFILE] [--port 20128] [--host 127.0.0.1]
117
+ awerouter <PROFILE> # shorthand for serve PROFILE
118
+ awerouter config path | show | edit | init
119
+ awerouter log [--lines 20]
120
+ awerouter stats
121
+ awerouter calibrate
122
+ ```
123
+
124
+ `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`.
125
+
126
+ ## Troubleshooting
127
+
128
+ **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:
129
+
130
+ ```bash
131
+ export no_proxy=127.0.0.1,localhost NO_PROXY=127.0.0.1,localhost
132
+ ```
133
+
134
+ Then open a new terminal and relaunch CC.
135
+
136
+ ## Development
137
+
138
+ ```bash
139
+ git clone https://github.com/mugpeng/awerouter
140
+ cd awerouter
141
+ pip install -e ".[dev]"
142
+ pytest
143
+ ```
144
+
145
+ See [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md) for architecture notes, config semantics, and the release process.
146
+
147
+ ## Support
148
+
149
+ If awerouter saves you money, consider supporting it:
150
+
151
+ - ⭐ Star the repo — it helps others find it.
152
+ - ☕ [Ko-fi](https://ko-fi.com/mugpeng) — buy me a coffee.
153
+ - 💬 WeChat — scan the QR code below.
154
+
155
+ <p align="center">
156
+ <img src="assets/images/wechat-pay.jpg" alt="WeChat Pay" width="240">
157
+ </p>
158
+
159
+ > awerouter is free and open source. Sponsors keep it maintained — thank you.