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.
- awerouter-0.2.0/LICENSE +207 -0
- awerouter-0.2.0/MANIFEST.in +5 -0
- awerouter-0.2.0/PKG-INFO +182 -0
- awerouter-0.2.0/README.md +159 -0
- awerouter-0.2.0/README_cn.md +159 -0
- awerouter-0.2.0/assets/images/wechat-pay.jpg +0 -0
- awerouter-0.2.0/docs/CHANGELOG.md +63 -0
- awerouter-0.2.0/docs/CONTRIBUTING.md +133 -0
- awerouter-0.2.0/docs/archive/PLAN.md +169 -0
- awerouter-0.2.0/docs/todo/code-quality.md +105 -0
- awerouter-0.2.0/docs/todo/l3-complexity.md +53 -0
- awerouter-0.2.0/docs/todo/l3-threshold.md +79 -0
- awerouter-0.2.0/pyproject.toml +44 -0
- awerouter-0.2.0/setup.cfg +4 -0
- awerouter-0.2.0/src/awerouter/__init__.py +1 -0
- awerouter-0.2.0/src/awerouter/cli.py +224 -0
- awerouter-0.2.0/src/awerouter/config.py +416 -0
- awerouter-0.2.0/src/awerouter/default-providers.json +9 -0
- awerouter-0.2.0/src/awerouter/default-routing.json +15 -0
- awerouter-0.2.0/src/awerouter/logging.py +216 -0
- awerouter-0.2.0/src/awerouter/router.py +137 -0
- awerouter-0.2.0/src/awerouter/server.py +397 -0
- awerouter-0.2.0/src/awerouter/types.py +70 -0
- awerouter-0.2.0/src/awerouter.egg-info/PKG-INFO +182 -0
- awerouter-0.2.0/src/awerouter.egg-info/SOURCES.txt +32 -0
- awerouter-0.2.0/src/awerouter.egg-info/dependency_links.txt +1 -0
- awerouter-0.2.0/src/awerouter.egg-info/entry_points.txt +2 -0
- awerouter-0.2.0/src/awerouter.egg-info/requires.txt +6 -0
- awerouter-0.2.0/src/awerouter.egg-info/top_level.txt +1 -0
- awerouter-0.2.0/tests/test_cli.py +148 -0
- awerouter-0.2.0/tests/test_config.py +343 -0
- awerouter-0.2.0/tests/test_logging.py +175 -0
- awerouter-0.2.0/tests/test_router.py +153 -0
- awerouter-0.2.0/tests/test_server.py +386 -0
awerouter-0.2.0/LICENSE
ADDED
|
@@ -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.
|
awerouter-0.2.0/PKG-INFO
ADDED
|
@@ -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.
|