jev-model-router 0.1.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.
- jev_model_router-0.1.0/LICENSE +21 -0
- jev_model_router-0.1.0/PKG-INFO +255 -0
- jev_model_router-0.1.0/README.md +225 -0
- jev_model_router-0.1.0/pyproject.toml +53 -0
- jev_model_router-0.1.0/setup.cfg +4 -0
- jev_model_router-0.1.0/src/jev_model_router.egg-info/PKG-INFO +255 -0
- jev_model_router-0.1.0/src/jev_model_router.egg-info/SOURCES.txt +29 -0
- jev_model_router-0.1.0/src/jev_model_router.egg-info/dependency_links.txt +1 -0
- jev_model_router-0.1.0/src/jev_model_router.egg-info/entry_points.txt +4 -0
- jev_model_router-0.1.0/src/jev_model_router.egg-info/requires.txt +6 -0
- jev_model_router-0.1.0/src/jev_model_router.egg-info/top_level.txt +1 -0
- jev_model_router-0.1.0/src/jev_router_live/__init__.py +14 -0
- jev_model_router-0.1.0/src/jev_router_live/bin/__init__.py +0 -0
- jev_model_router-0.1.0/src/jev_router_live/bin/jev_claude.py +131 -0
- jev_model_router-0.1.0/src/jev_router_live/bin/jev_codex.py +89 -0
- jev_model_router-0.1.0/src/jev_router_live/bin/jev_explain.py +18 -0
- jev_model_router-0.1.0/src/jev_router_live/bin/jev_statusline.py +47 -0
- jev_model_router-0.1.0/src/jev_router_live/codex_proxy.py +354 -0
- jev_model_router-0.1.0/src/jev_router_live/config.py +221 -0
- jev_model_router-0.1.0/src/jev_router_live/env_file.py +29 -0
- jev_model_router-0.1.0/src/jev_router_live/explain.py +84 -0
- jev_model_router-0.1.0/src/jev_router_live/log.py +36 -0
- jev_model_router-0.1.0/src/jev_router_live/policy.py +77 -0
- jev_model_router-0.1.0/src/jev_router_live/proxy.py +594 -0
- jev_model_router-0.1.0/src/jev_router_live/router.py +17 -0
- jev_model_router-0.1.0/src/jev_router_live/sdk_router.py +34 -0
- jev_model_router-0.1.0/src/jev_router_live/settings.py +42 -0
- jev_model_router-0.1.0/src/jev_router_live/skills/codex/jev-explain/SKILL.md +7 -0
- jev_model_router-0.1.0/src/jev_router_live/status.py +110 -0
- jev_model_router-0.1.0/src/jev_router_live/stdlib_router.py +136 -0
- jev_model_router-0.1.0/src/jev_router_live/version.py +1 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Himanshu Kumar
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jev-model-router
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Per-turn model routing for Claude Code, decided by TypeSafe's Jev.
|
|
5
|
+
Author: Himanshu Kumar
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/himanshu231204/jev_model_routers
|
|
8
|
+
Project-URL: Documentation, https://github.com/himanshu231204/jev_model_routers/blob/main/docs/quickstart.md
|
|
9
|
+
Project-URL: Issues, https://github.com/himanshu231204/jev_model_routers/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/himanshu231204/jev_model_routers/blob/main/CHANGELOG.md
|
|
11
|
+
Keywords: claude,claude-code,codex,model-routing,llm,jev,typesafe,proxy
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Provides-Extra: test
|
|
26
|
+
Requires-Dist: pytest; extra == "test"
|
|
27
|
+
Provides-Extra: typesafe
|
|
28
|
+
Requires-Dist: typesafe-sdk; extra == "typesafe"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
<p align="center">
|
|
32
|
+
<img src="https://raw.githubusercontent.com/himanshu231204/jev_model_routers/main/assets/banner.svg" alt="JEV Router — the right Claude model for every turn" width="100%">
|
|
33
|
+
</p>
|
|
34
|
+
|
|
35
|
+
<p align="center">
|
|
36
|
+
<a href="https://github.com/himanshu231204/jev_model_routers/actions/workflows/ci.yml"><img src="https://github.com/himanshu231204/jev_model_routers/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
37
|
+
<a href="https://pypi.org/project/jev-model-router/"><img src="https://img.shields.io/pypi/v/jev-model-router?color=d97757" alt="PyPI"></a>
|
|
38
|
+
<a href="https://github.com/himanshu231204/jev_model_routers/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="MIT License"></a>
|
|
39
|
+
<img src="https://img.shields.io/badge/python-3.11%20%7C%203.12-blue" alt="Python 3.11 | 3.12">
|
|
40
|
+
<img src="https://img.shields.io/badge/dependencies-none-brightgreen" alt="Zero dependencies">
|
|
41
|
+
<img src="https://img.shields.io/badge/Claude%20Code-supported-d97757" alt="Claude Code supported">
|
|
42
|
+
<img src="https://img.shields.io/badge/status-beta-orange" alt="Status: beta">
|
|
43
|
+
</p>
|
|
44
|
+
|
|
45
|
+
<p align="center">
|
|
46
|
+
<a href="#-quick-start">Quick start</a> ·
|
|
47
|
+
<a href="#-how-it-works">How it works</a> ·
|
|
48
|
+
<a href="https://github.com/himanshu231204/jev_model_routers/blob/main/docs/quickstart.md">User guide</a> ·
|
|
49
|
+
<a href="https://github.com/himanshu231204/jev_model_routers/blob/main/ARCHITECTURE.md">Architecture</a> ·
|
|
50
|
+
<a href="https://github.com/himanshu231204/jev_model_routers/blob/main/CONTRIBUTING.md">Contributing</a>
|
|
51
|
+
</p>
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
**JEV Router** picks the Claude model for **each new turn** of your Claude Code session. Renaming
|
|
56
|
+
a variable doesn't need Opus; debugging a race condition does. JEV Router asks
|
|
57
|
+
[TypeSafe's Jev](https://docs.typesafe.ai/introduction/coding-agents) — a fast decision model —
|
|
58
|
+
which of your models fits the turn, then runs it there. Claude Code keeps working exactly as
|
|
59
|
+
usual: same UI, tools, permissions, sessions and streaming.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pip install jev-model-router
|
|
63
|
+
jev-claude # that's it — Claude Code, with per-turn routing
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## ✨ Features
|
|
67
|
+
|
|
68
|
+
- **Per-turn routing** — every new message is judged on its own, so a session can move from
|
|
69
|
+
Haiku to Opus and back as the work changes.
|
|
70
|
+
- **Native Claude Code** — a local proxy in front of Claude Code's API; nothing to configure in
|
|
71
|
+
Claude Code, no fork, no plugin. `/resume`, permissions, tools and streaming are untouched.
|
|
72
|
+
- **You stay in control** — pick a model in `/model` and routing steps aside; pick
|
|
73
|
+
**JEV Router** again to resume. Say "use opus" in a prompt to force it for one turn.
|
|
74
|
+
- **Tool loops stay on one model** — Jev is asked once per turn; every file read, edit and
|
|
75
|
+
command in that turn reuses the chosen model.
|
|
76
|
+
- **Fails open** — no key, a Jev timeout or an error never blocks you; Claude Code carries on
|
|
77
|
+
with a safe model within ~3 seconds.
|
|
78
|
+
- **Explainable** — a status-line readout of every decision, `jev-explain` for the full
|
|
79
|
+
reasoning, and a one-line log per turn.
|
|
80
|
+
- **Private by design** — your key and prompts are never logged; local files are owner-only.
|
|
81
|
+
- **Zero dependencies** — pure Python standard library.
|
|
82
|
+
|
|
83
|
+
## 🎬 Demo
|
|
84
|
+
|
|
85
|
+
<p align="center">
|
|
86
|
+
<img src="https://raw.githubusercontent.com/himanshu231204/jev_model_routers/main/assets/images/jev-claude-02-model-picker.png" alt="The /model picker with the JEV Router row" width="49%">
|
|
87
|
+
<img src="https://raw.githubusercontent.com/himanshu231204/jev_model_routers/main/assets/images/jev-claude-03-trivial-turn-haiku.png" alt="A rename task routed to Haiku" width="49%">
|
|
88
|
+
</p>
|
|
89
|
+
<p align="center"><sub><b>Left:</b> "JEV Router" in Claude Code's own <code>/model</code> picker. <b>Right:</b> a rename routed to Haiku — the status line shows the model Jev picked.</sub></p>
|
|
90
|
+
|
|
91
|
+
<details>
|
|
92
|
+
<summary><b>More screenshots</b> — hard turn on Opus, manual override, <code>jev-explain</code>, decision log</summary>
|
|
93
|
+
<br>
|
|
94
|
+
|
|
95
|
+
**A harder turn moves up to Opus** — Jev is asked again on the next message:
|
|
96
|
+
|
|
97
|
+

|
|
98
|
+
|
|
99
|
+
**Picking a model yourself pauses routing** — the status line shows `⏸ manual`:
|
|
100
|
+
|
|
101
|
+

|
|
102
|
+
|
|
103
|
+
**`jev-explain` shows why** a turn got its model:
|
|
104
|
+
|
|
105
|
+

|
|
106
|
+
|
|
107
|
+
**One safe log line per turn** — no prompt, no keys:
|
|
108
|
+
|
|
109
|
+

|
|
110
|
+
|
|
111
|
+
<sub>Captured from a real Claude Code 2.1.282 session through <code>jev-claude</code>; Jev's answers came from the local stand-in <a href="https://github.com/himanshu231204/jev_model_routers/blob/main/scripts/fake_jev.py"><code>scripts/fake_jev.py</code></a>.</sub>
|
|
112
|
+
</details>
|
|
113
|
+
|
|
114
|
+
## 🚀 Quick start
|
|
115
|
+
|
|
116
|
+
**Requirements:** Python 3.11+, [Claude Code](https://code.claude.com/docs/en/setup) installed and
|
|
117
|
+
logged in (a claude.ai subscription or an API key — no extra Anthropic key needed), and a
|
|
118
|
+
[TypeSafe API key](https://console.typesafe.ai/keys).
|
|
119
|
+
|
|
120
|
+
**1. Install**
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
pip install jev-model-router
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
<sub>Latest development version: <code>pip install git+https://github.com/himanshu231204/jev_model_routers</code></sub>
|
|
127
|
+
|
|
128
|
+
**2. Add your TypeSafe key** — `TYPESAFE_API_KEY`, the name TypeSafe's docs and SDK use:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
echo "TYPESAFE_API_KEY=your_key" > ~/.jev-router.env # or: export TYPESAFE_API_KEY=...
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
<sub>Windows PowerShell: <code>Set-Content "$HOME\.jev-router.env" "TYPESAFE_API_KEY=your_key"</code></sub>
|
|
135
|
+
|
|
136
|
+
**3. Run**
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
jev-claude # interactive, routing each new turn
|
|
140
|
+
jev-claude -p "fix the failing test" # every Claude Code argument is passed through
|
|
141
|
+
jev-claude --resume # sessions work as usual
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The session starts on **JEV Router**. Watch the status line — e.g.
|
|
145
|
+
`claude-haiku-4-5-20251001 (p=0.97)` — to see which model each turn got. The full guide, with
|
|
146
|
+
troubleshooting, is in [`docs/quickstart.md`](https://github.com/himanshu231204/jev_model_routers/blob/main/docs/quickstart.md).
|
|
147
|
+
|
|
148
|
+
## 🧠 How it works
|
|
149
|
+
|
|
150
|
+
```mermaid
|
|
151
|
+
flowchart LR
|
|
152
|
+
A["Claude Code"] -->|"model = jev-router"| B["JEV Router<br/>(local proxy)"]
|
|
153
|
+
B -->|"new turn: ask Jev"| J["Jev<br/>(TypeSafe System One)"]
|
|
154
|
+
J -->|"best model + confidence"| B
|
|
155
|
+
B -->|"model = claude-haiku / sonnet / opus"| C["Anthropic API"]
|
|
156
|
+
C -->|"streamed back, unchanged"| B --> A
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
1. `jev-claude` starts Claude Code with a local proxy as its API endpoint and a **JEV Router**
|
|
160
|
+
entry in `/model`.
|
|
161
|
+
2. When you send a message, the proxy asks Jev which of your account's models — newest Haiku,
|
|
162
|
+
Sonnet or Opus — can handle it, with a confidence score.
|
|
163
|
+
3. A small local **policy** makes the final call: explicit choices win, low confidence never
|
|
164
|
+
downgrades, and a long conversation isn't downgraded (switching would re-read it all).
|
|
165
|
+
4. Only the request's model is rewritten; Claude's response streams straight back.
|
|
166
|
+
5. Tool calls, retries and Claude Code's background requests in the same turn reuse that model.
|
|
167
|
+
|
|
168
|
+
Jev doesn't write code — Claude does. Jev answers one quick structured question per turn, the
|
|
169
|
+
kind of *"route this to one of a fixed set of destinations, and know how confident you are"*
|
|
170
|
+
decision it is built for. Design details: [`ARCHITECTURE.md`](https://github.com/himanshu231204/jev_model_routers/blob/main/ARCHITECTURE.md).
|
|
171
|
+
|
|
172
|
+
## ⚙️ Configuration
|
|
173
|
+
|
|
174
|
+
Everything works with just the key. Optional environment variables:
|
|
175
|
+
|
|
176
|
+
| Variable | Effect |
|
|
177
|
+
| --- | --- |
|
|
178
|
+
| `TYPESAFE_API_KEY` | TypeSafe key for Jev. Required for routing; without it `jev-claude` runs plain Claude Code. |
|
|
179
|
+
| `JEV_ALLOW_FABLE=1` | Also offer Fable (bills extra usage credits). |
|
|
180
|
+
| `JEV_NO_STATUSLINE=1` | Don't add the routing status line (your own status line is never overridden). |
|
|
181
|
+
| `JEV_DEBUG=1` | Request-level tracing in `~/.jev-claude.log`, including prompt excerpts. |
|
|
182
|
+
| `JEV_CLIENT=sdk` | Call Jev through the official `typesafe-sdk` (`pip install "jev-model-router[typesafe]"`). |
|
|
183
|
+
|
|
184
|
+
Routing thresholds (confidence floor, cache protection, timeouts) live in one file:
|
|
185
|
+
[`src/jev_router_live/config.py`](https://github.com/himanshu231204/jev_model_routers/blob/main/src/jev_router_live/config.py).
|
|
186
|
+
|
|
187
|
+
## 🧰 Commands
|
|
188
|
+
|
|
189
|
+
| Command | What it does |
|
|
190
|
+
| --- | --- |
|
|
191
|
+
| `jev-claude [claude args…]` | Run Claude Code with per-turn routing. |
|
|
192
|
+
| `jev-codex [codex args…]` | Run OpenAI Codex with per-turn routing *(experimental)*. |
|
|
193
|
+
| `jev-explain <session-id>` | Show why the last turn got its model. |
|
|
194
|
+
|
|
195
|
+
## ❓ FAQ
|
|
196
|
+
|
|
197
|
+
<details>
|
|
198
|
+
<summary><b>Does it replace Claude?</b></summary>
|
|
199
|
+
|
|
200
|
+
No. Claude still does all the work. Jev only chooses which Claude model handles each turn.
|
|
201
|
+
</details>
|
|
202
|
+
|
|
203
|
+
<details>
|
|
204
|
+
<summary><b>What happens if Jev is down or my key is wrong?</b></summary>
|
|
205
|
+
|
|
206
|
+
Routing fails open: the turn runs on a safe model (Opus on the first turn, otherwise the model
|
|
207
|
+
already in use) within about three seconds, and the reason is logged. Claude Code never stops.
|
|
208
|
+
</details>
|
|
209
|
+
|
|
210
|
+
<details>
|
|
211
|
+
<summary><b>What does Jev see?</b></summary>
|
|
212
|
+
|
|
213
|
+
The prompt of each new turn (with Claude Code's injected system context removed), the current
|
|
214
|
+
model, a rough context size, and the list of models to choose from. Not your files, tool output
|
|
215
|
+
or conversation history.
|
|
216
|
+
</details>
|
|
217
|
+
|
|
218
|
+
<details>
|
|
219
|
+
<summary><b>Will it change my Claude Code settings?</b></summary>
|
|
220
|
+
|
|
221
|
+
No. "JEV Router" is selected for the session only and never saved as your default; if Claude
|
|
222
|
+
Code persists it anyway, `jev-claude` restores your previous default on exit. Plain `claude` is
|
|
223
|
+
unaffected.
|
|
224
|
+
</details>
|
|
225
|
+
|
|
226
|
+
<details>
|
|
227
|
+
<summary><b>Claude Code prints <code>[claude-code:unrecognized_model] {"model":"jev-router"}</code> at startup.</b></summary>
|
|
228
|
+
|
|
229
|
+
Harmless — it's Claude Code noting the extra "JEV Router" entry. Requests are still routed.
|
|
230
|
+
</details>
|
|
231
|
+
|
|
232
|
+
## 📍 Project status
|
|
233
|
+
|
|
234
|
+
**Beta.** Verified end to end against Claude Code 2.1.282 (interactive and `-p`): the picker entry,
|
|
235
|
+
per-turn routing, tool-loop pinning, manual override, fail-open and streaming, with 94 automated
|
|
236
|
+
tests on Python 3.11 and 3.12. OpenAI Codex support (`jev-codex`) is experimental. Known
|
|
237
|
+
limitations are listed in [`ARCHITECTURE.md`](https://github.com/himanshu231204/jev_model_routers/blob/main/ARCHITECTURE.md#15-known-limitations).
|
|
238
|
+
|
|
239
|
+
## 🤝 Contributing
|
|
240
|
+
|
|
241
|
+
Contributions are welcome — see [`CONTRIBUTING.md`](https://github.com/himanshu231204/jev_model_routers/blob/main/CONTRIBUTING.md) for setup and conventions and
|
|
242
|
+
[`AGENTS.md`](https://github.com/himanshu231204/jev_model_routers/blob/main/AGENTS.md) for the invariants a change must keep. Please report security issues
|
|
243
|
+
privately as described in [`SECURITY.md`](https://github.com/himanshu231204/jev_model_routers/blob/main/SECURITY.md).
|
|
244
|
+
|
|
245
|
+
```bash
|
|
246
|
+
git clone https://github.com/himanshu231204/jev_model_routers.git && cd jev_model_routers
|
|
247
|
+
pip install -e ".[test]"
|
|
248
|
+
python -m pytest -q
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## 📄 License
|
|
252
|
+
|
|
253
|
+
[MIT](https://github.com/himanshu231204/jev_model_routers/blob/main/LICENSE) © 2026 Himanshu Kumar
|
|
254
|
+
|
|
255
|
+
<sub>JEV Router is an independent open-source project and is not affiliated with Anthropic or TypeSafe. Claude and Claude Code are products of Anthropic; Jev is a model by TypeSafe.</sub>
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="https://raw.githubusercontent.com/himanshu231204/jev_model_routers/main/assets/banner.svg" alt="JEV Router — the right Claude model for every turn" width="100%">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<a href="https://github.com/himanshu231204/jev_model_routers/actions/workflows/ci.yml"><img src="https://github.com/himanshu231204/jev_model_routers/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
|
|
7
|
+
<a href="https://pypi.org/project/jev-model-router/"><img src="https://img.shields.io/pypi/v/jev-model-router?color=d97757" alt="PyPI"></a>
|
|
8
|
+
<a href="https://github.com/himanshu231204/jev_model_routers/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="MIT License"></a>
|
|
9
|
+
<img src="https://img.shields.io/badge/python-3.11%20%7C%203.12-blue" alt="Python 3.11 | 3.12">
|
|
10
|
+
<img src="https://img.shields.io/badge/dependencies-none-brightgreen" alt="Zero dependencies">
|
|
11
|
+
<img src="https://img.shields.io/badge/Claude%20Code-supported-d97757" alt="Claude Code supported">
|
|
12
|
+
<img src="https://img.shields.io/badge/status-beta-orange" alt="Status: beta">
|
|
13
|
+
</p>
|
|
14
|
+
|
|
15
|
+
<p align="center">
|
|
16
|
+
<a href="#-quick-start">Quick start</a> ·
|
|
17
|
+
<a href="#-how-it-works">How it works</a> ·
|
|
18
|
+
<a href="https://github.com/himanshu231204/jev_model_routers/blob/main/docs/quickstart.md">User guide</a> ·
|
|
19
|
+
<a href="https://github.com/himanshu231204/jev_model_routers/blob/main/ARCHITECTURE.md">Architecture</a> ·
|
|
20
|
+
<a href="https://github.com/himanshu231204/jev_model_routers/blob/main/CONTRIBUTING.md">Contributing</a>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
**JEV Router** picks the Claude model for **each new turn** of your Claude Code session. Renaming
|
|
26
|
+
a variable doesn't need Opus; debugging a race condition does. JEV Router asks
|
|
27
|
+
[TypeSafe's Jev](https://docs.typesafe.ai/introduction/coding-agents) — a fast decision model —
|
|
28
|
+
which of your models fits the turn, then runs it there. Claude Code keeps working exactly as
|
|
29
|
+
usual: same UI, tools, permissions, sessions and streaming.
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install jev-model-router
|
|
33
|
+
jev-claude # that's it — Claude Code, with per-turn routing
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## ✨ Features
|
|
37
|
+
|
|
38
|
+
- **Per-turn routing** — every new message is judged on its own, so a session can move from
|
|
39
|
+
Haiku to Opus and back as the work changes.
|
|
40
|
+
- **Native Claude Code** — a local proxy in front of Claude Code's API; nothing to configure in
|
|
41
|
+
Claude Code, no fork, no plugin. `/resume`, permissions, tools and streaming are untouched.
|
|
42
|
+
- **You stay in control** — pick a model in `/model` and routing steps aside; pick
|
|
43
|
+
**JEV Router** again to resume. Say "use opus" in a prompt to force it for one turn.
|
|
44
|
+
- **Tool loops stay on one model** — Jev is asked once per turn; every file read, edit and
|
|
45
|
+
command in that turn reuses the chosen model.
|
|
46
|
+
- **Fails open** — no key, a Jev timeout or an error never blocks you; Claude Code carries on
|
|
47
|
+
with a safe model within ~3 seconds.
|
|
48
|
+
- **Explainable** — a status-line readout of every decision, `jev-explain` for the full
|
|
49
|
+
reasoning, and a one-line log per turn.
|
|
50
|
+
- **Private by design** — your key and prompts are never logged; local files are owner-only.
|
|
51
|
+
- **Zero dependencies** — pure Python standard library.
|
|
52
|
+
|
|
53
|
+
## 🎬 Demo
|
|
54
|
+
|
|
55
|
+
<p align="center">
|
|
56
|
+
<img src="https://raw.githubusercontent.com/himanshu231204/jev_model_routers/main/assets/images/jev-claude-02-model-picker.png" alt="The /model picker with the JEV Router row" width="49%">
|
|
57
|
+
<img src="https://raw.githubusercontent.com/himanshu231204/jev_model_routers/main/assets/images/jev-claude-03-trivial-turn-haiku.png" alt="A rename task routed to Haiku" width="49%">
|
|
58
|
+
</p>
|
|
59
|
+
<p align="center"><sub><b>Left:</b> "JEV Router" in Claude Code's own <code>/model</code> picker. <b>Right:</b> a rename routed to Haiku — the status line shows the model Jev picked.</sub></p>
|
|
60
|
+
|
|
61
|
+
<details>
|
|
62
|
+
<summary><b>More screenshots</b> — hard turn on Opus, manual override, <code>jev-explain</code>, decision log</summary>
|
|
63
|
+
<br>
|
|
64
|
+
|
|
65
|
+
**A harder turn moves up to Opus** — Jev is asked again on the next message:
|
|
66
|
+
|
|
67
|
+

|
|
68
|
+
|
|
69
|
+
**Picking a model yourself pauses routing** — the status line shows `⏸ manual`:
|
|
70
|
+
|
|
71
|
+

|
|
72
|
+
|
|
73
|
+
**`jev-explain` shows why** a turn got its model:
|
|
74
|
+
|
|
75
|
+

|
|
76
|
+
|
|
77
|
+
**One safe log line per turn** — no prompt, no keys:
|
|
78
|
+
|
|
79
|
+

|
|
80
|
+
|
|
81
|
+
<sub>Captured from a real Claude Code 2.1.282 session through <code>jev-claude</code>; Jev's answers came from the local stand-in <a href="https://github.com/himanshu231204/jev_model_routers/blob/main/scripts/fake_jev.py"><code>scripts/fake_jev.py</code></a>.</sub>
|
|
82
|
+
</details>
|
|
83
|
+
|
|
84
|
+
## 🚀 Quick start
|
|
85
|
+
|
|
86
|
+
**Requirements:** Python 3.11+, [Claude Code](https://code.claude.com/docs/en/setup) installed and
|
|
87
|
+
logged in (a claude.ai subscription or an API key — no extra Anthropic key needed), and a
|
|
88
|
+
[TypeSafe API key](https://console.typesafe.ai/keys).
|
|
89
|
+
|
|
90
|
+
**1. Install**
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
pip install jev-model-router
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
<sub>Latest development version: <code>pip install git+https://github.com/himanshu231204/jev_model_routers</code></sub>
|
|
97
|
+
|
|
98
|
+
**2. Add your TypeSafe key** — `TYPESAFE_API_KEY`, the name TypeSafe's docs and SDK use:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
echo "TYPESAFE_API_KEY=your_key" > ~/.jev-router.env # or: export TYPESAFE_API_KEY=...
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
<sub>Windows PowerShell: <code>Set-Content "$HOME\.jev-router.env" "TYPESAFE_API_KEY=your_key"</code></sub>
|
|
105
|
+
|
|
106
|
+
**3. Run**
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
jev-claude # interactive, routing each new turn
|
|
110
|
+
jev-claude -p "fix the failing test" # every Claude Code argument is passed through
|
|
111
|
+
jev-claude --resume # sessions work as usual
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The session starts on **JEV Router**. Watch the status line — e.g.
|
|
115
|
+
`claude-haiku-4-5-20251001 (p=0.97)` — to see which model each turn got. The full guide, with
|
|
116
|
+
troubleshooting, is in [`docs/quickstart.md`](https://github.com/himanshu231204/jev_model_routers/blob/main/docs/quickstart.md).
|
|
117
|
+
|
|
118
|
+
## 🧠 How it works
|
|
119
|
+
|
|
120
|
+
```mermaid
|
|
121
|
+
flowchart LR
|
|
122
|
+
A["Claude Code"] -->|"model = jev-router"| B["JEV Router<br/>(local proxy)"]
|
|
123
|
+
B -->|"new turn: ask Jev"| J["Jev<br/>(TypeSafe System One)"]
|
|
124
|
+
J -->|"best model + confidence"| B
|
|
125
|
+
B -->|"model = claude-haiku / sonnet / opus"| C["Anthropic API"]
|
|
126
|
+
C -->|"streamed back, unchanged"| B --> A
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
1. `jev-claude` starts Claude Code with a local proxy as its API endpoint and a **JEV Router**
|
|
130
|
+
entry in `/model`.
|
|
131
|
+
2. When you send a message, the proxy asks Jev which of your account's models — newest Haiku,
|
|
132
|
+
Sonnet or Opus — can handle it, with a confidence score.
|
|
133
|
+
3. A small local **policy** makes the final call: explicit choices win, low confidence never
|
|
134
|
+
downgrades, and a long conversation isn't downgraded (switching would re-read it all).
|
|
135
|
+
4. Only the request's model is rewritten; Claude's response streams straight back.
|
|
136
|
+
5. Tool calls, retries and Claude Code's background requests in the same turn reuse that model.
|
|
137
|
+
|
|
138
|
+
Jev doesn't write code — Claude does. Jev answers one quick structured question per turn, the
|
|
139
|
+
kind of *"route this to one of a fixed set of destinations, and know how confident you are"*
|
|
140
|
+
decision it is built for. Design details: [`ARCHITECTURE.md`](https://github.com/himanshu231204/jev_model_routers/blob/main/ARCHITECTURE.md).
|
|
141
|
+
|
|
142
|
+
## ⚙️ Configuration
|
|
143
|
+
|
|
144
|
+
Everything works with just the key. Optional environment variables:
|
|
145
|
+
|
|
146
|
+
| Variable | Effect |
|
|
147
|
+
| --- | --- |
|
|
148
|
+
| `TYPESAFE_API_KEY` | TypeSafe key for Jev. Required for routing; without it `jev-claude` runs plain Claude Code. |
|
|
149
|
+
| `JEV_ALLOW_FABLE=1` | Also offer Fable (bills extra usage credits). |
|
|
150
|
+
| `JEV_NO_STATUSLINE=1` | Don't add the routing status line (your own status line is never overridden). |
|
|
151
|
+
| `JEV_DEBUG=1` | Request-level tracing in `~/.jev-claude.log`, including prompt excerpts. |
|
|
152
|
+
| `JEV_CLIENT=sdk` | Call Jev through the official `typesafe-sdk` (`pip install "jev-model-router[typesafe]"`). |
|
|
153
|
+
|
|
154
|
+
Routing thresholds (confidence floor, cache protection, timeouts) live in one file:
|
|
155
|
+
[`src/jev_router_live/config.py`](https://github.com/himanshu231204/jev_model_routers/blob/main/src/jev_router_live/config.py).
|
|
156
|
+
|
|
157
|
+
## 🧰 Commands
|
|
158
|
+
|
|
159
|
+
| Command | What it does |
|
|
160
|
+
| --- | --- |
|
|
161
|
+
| `jev-claude [claude args…]` | Run Claude Code with per-turn routing. |
|
|
162
|
+
| `jev-codex [codex args…]` | Run OpenAI Codex with per-turn routing *(experimental)*. |
|
|
163
|
+
| `jev-explain <session-id>` | Show why the last turn got its model. |
|
|
164
|
+
|
|
165
|
+
## ❓ FAQ
|
|
166
|
+
|
|
167
|
+
<details>
|
|
168
|
+
<summary><b>Does it replace Claude?</b></summary>
|
|
169
|
+
|
|
170
|
+
No. Claude still does all the work. Jev only chooses which Claude model handles each turn.
|
|
171
|
+
</details>
|
|
172
|
+
|
|
173
|
+
<details>
|
|
174
|
+
<summary><b>What happens if Jev is down or my key is wrong?</b></summary>
|
|
175
|
+
|
|
176
|
+
Routing fails open: the turn runs on a safe model (Opus on the first turn, otherwise the model
|
|
177
|
+
already in use) within about three seconds, and the reason is logged. Claude Code never stops.
|
|
178
|
+
</details>
|
|
179
|
+
|
|
180
|
+
<details>
|
|
181
|
+
<summary><b>What does Jev see?</b></summary>
|
|
182
|
+
|
|
183
|
+
The prompt of each new turn (with Claude Code's injected system context removed), the current
|
|
184
|
+
model, a rough context size, and the list of models to choose from. Not your files, tool output
|
|
185
|
+
or conversation history.
|
|
186
|
+
</details>
|
|
187
|
+
|
|
188
|
+
<details>
|
|
189
|
+
<summary><b>Will it change my Claude Code settings?</b></summary>
|
|
190
|
+
|
|
191
|
+
No. "JEV Router" is selected for the session only and never saved as your default; if Claude
|
|
192
|
+
Code persists it anyway, `jev-claude` restores your previous default on exit. Plain `claude` is
|
|
193
|
+
unaffected.
|
|
194
|
+
</details>
|
|
195
|
+
|
|
196
|
+
<details>
|
|
197
|
+
<summary><b>Claude Code prints <code>[claude-code:unrecognized_model] {"model":"jev-router"}</code> at startup.</b></summary>
|
|
198
|
+
|
|
199
|
+
Harmless — it's Claude Code noting the extra "JEV Router" entry. Requests are still routed.
|
|
200
|
+
</details>
|
|
201
|
+
|
|
202
|
+
## 📍 Project status
|
|
203
|
+
|
|
204
|
+
**Beta.** Verified end to end against Claude Code 2.1.282 (interactive and `-p`): the picker entry,
|
|
205
|
+
per-turn routing, tool-loop pinning, manual override, fail-open and streaming, with 94 automated
|
|
206
|
+
tests on Python 3.11 and 3.12. OpenAI Codex support (`jev-codex`) is experimental. Known
|
|
207
|
+
limitations are listed in [`ARCHITECTURE.md`](https://github.com/himanshu231204/jev_model_routers/blob/main/ARCHITECTURE.md#15-known-limitations).
|
|
208
|
+
|
|
209
|
+
## 🤝 Contributing
|
|
210
|
+
|
|
211
|
+
Contributions are welcome — see [`CONTRIBUTING.md`](https://github.com/himanshu231204/jev_model_routers/blob/main/CONTRIBUTING.md) for setup and conventions and
|
|
212
|
+
[`AGENTS.md`](https://github.com/himanshu231204/jev_model_routers/blob/main/AGENTS.md) for the invariants a change must keep. Please report security issues
|
|
213
|
+
privately as described in [`SECURITY.md`](https://github.com/himanshu231204/jev_model_routers/blob/main/SECURITY.md).
|
|
214
|
+
|
|
215
|
+
```bash
|
|
216
|
+
git clone https://github.com/himanshu231204/jev_model_routers.git && cd jev_model_routers
|
|
217
|
+
pip install -e ".[test]"
|
|
218
|
+
python -m pytest -q
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## 📄 License
|
|
222
|
+
|
|
223
|
+
[MIT](https://github.com/himanshu231204/jev_model_routers/blob/main/LICENSE) © 2026 Himanshu Kumar
|
|
224
|
+
|
|
225
|
+
<sub>JEV Router is an independent open-source project and is not affiliated with Anthropic or TypeSafe. Claude and Claude Code are products of Anthropic; Jev is a model by TypeSafe.</sub>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "jev-model-router"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Per-turn model routing for Claude Code, decided by TypeSafe's Jev."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.11"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [{ name = "Himanshu Kumar" }]
|
|
13
|
+
keywords = ["claude", "claude-code", "codex", "model-routing", "llm", "jev", "typesafe", "proxy"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Environment :: Console",
|
|
17
|
+
"Intended Audience :: Developers",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Operating System :: OS Independent",
|
|
20
|
+
"Programming Language :: Python :: 3",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
"Topic :: Software Development",
|
|
24
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
25
|
+
]
|
|
26
|
+
dependencies = []
|
|
27
|
+
|
|
28
|
+
[project.urls]
|
|
29
|
+
Homepage = "https://github.com/himanshu231204/jev_model_routers"
|
|
30
|
+
Documentation = "https://github.com/himanshu231204/jev_model_routers/blob/main/docs/quickstart.md"
|
|
31
|
+
Issues = "https://github.com/himanshu231204/jev_model_routers/issues"
|
|
32
|
+
Changelog = "https://github.com/himanshu231204/jev_model_routers/blob/main/CHANGELOG.md"
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
test = ["pytest"]
|
|
36
|
+
typesafe = ["typesafe-sdk"]
|
|
37
|
+
|
|
38
|
+
[project.scripts]
|
|
39
|
+
jev-claude = "jev_router_live.bin.jev_claude:main"
|
|
40
|
+
jev-codex = "jev_router_live.bin.jev_codex:main"
|
|
41
|
+
jev-explain = "jev_router_live.bin.jev_explain:main"
|
|
42
|
+
|
|
43
|
+
[tool.setuptools.dynamic]
|
|
44
|
+
version = { attr = "jev_router_live.version.__version__" }
|
|
45
|
+
|
|
46
|
+
[tool.setuptools.packages.find]
|
|
47
|
+
where = ["src"]
|
|
48
|
+
|
|
49
|
+
[tool.setuptools.package-data]
|
|
50
|
+
jev_router_live = ["skills/*/*/SKILL.md"]
|
|
51
|
+
|
|
52
|
+
[tool.pytest.ini_options]
|
|
53
|
+
testpaths = ["tests"]
|