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.
Files changed (31) hide show
  1. jev_model_router-0.1.0/LICENSE +21 -0
  2. jev_model_router-0.1.0/PKG-INFO +255 -0
  3. jev_model_router-0.1.0/README.md +225 -0
  4. jev_model_router-0.1.0/pyproject.toml +53 -0
  5. jev_model_router-0.1.0/setup.cfg +4 -0
  6. jev_model_router-0.1.0/src/jev_model_router.egg-info/PKG-INFO +255 -0
  7. jev_model_router-0.1.0/src/jev_model_router.egg-info/SOURCES.txt +29 -0
  8. jev_model_router-0.1.0/src/jev_model_router.egg-info/dependency_links.txt +1 -0
  9. jev_model_router-0.1.0/src/jev_model_router.egg-info/entry_points.txt +4 -0
  10. jev_model_router-0.1.0/src/jev_model_router.egg-info/requires.txt +6 -0
  11. jev_model_router-0.1.0/src/jev_model_router.egg-info/top_level.txt +1 -0
  12. jev_model_router-0.1.0/src/jev_router_live/__init__.py +14 -0
  13. jev_model_router-0.1.0/src/jev_router_live/bin/__init__.py +0 -0
  14. jev_model_router-0.1.0/src/jev_router_live/bin/jev_claude.py +131 -0
  15. jev_model_router-0.1.0/src/jev_router_live/bin/jev_codex.py +89 -0
  16. jev_model_router-0.1.0/src/jev_router_live/bin/jev_explain.py +18 -0
  17. jev_model_router-0.1.0/src/jev_router_live/bin/jev_statusline.py +47 -0
  18. jev_model_router-0.1.0/src/jev_router_live/codex_proxy.py +354 -0
  19. jev_model_router-0.1.0/src/jev_router_live/config.py +221 -0
  20. jev_model_router-0.1.0/src/jev_router_live/env_file.py +29 -0
  21. jev_model_router-0.1.0/src/jev_router_live/explain.py +84 -0
  22. jev_model_router-0.1.0/src/jev_router_live/log.py +36 -0
  23. jev_model_router-0.1.0/src/jev_router_live/policy.py +77 -0
  24. jev_model_router-0.1.0/src/jev_router_live/proxy.py +594 -0
  25. jev_model_router-0.1.0/src/jev_router_live/router.py +17 -0
  26. jev_model_router-0.1.0/src/jev_router_live/sdk_router.py +34 -0
  27. jev_model_router-0.1.0/src/jev_router_live/settings.py +42 -0
  28. jev_model_router-0.1.0/src/jev_router_live/skills/codex/jev-explain/SKILL.md +7 -0
  29. jev_model_router-0.1.0/src/jev_router_live/status.py +110 -0
  30. jev_model_router-0.1.0/src/jev_router_live/stdlib_router.py +136 -0
  31. 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
+ ![A design question routed to Opus](https://raw.githubusercontent.com/himanshu231204/jev_model_routers/main/assets/images/jev-claude-04-hard-turn-opus.png)
98
+
99
+ **Picking a model yourself pauses routing** — the status line shows `⏸ manual`:
100
+
101
+ ![Manual model selection bypasses Jev](https://raw.githubusercontent.com/himanshu231204/jev_model_routers/main/assets/images/jev-claude-05-manual-model.png)
102
+
103
+ **`jev-explain` shows why** a turn got its model:
104
+
105
+ ![jev-explain report](https://raw.githubusercontent.com/himanshu231204/jev_model_routers/main/assets/images/jev-claude-06-explain.png)
106
+
107
+ **One safe log line per turn** — no prompt, no keys:
108
+
109
+ ![Decision log](https://raw.githubusercontent.com/himanshu231204/jev_model_routers/main/assets/images/jev-claude-07-decision-log.png)
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
+ ![A design question routed to Opus](https://raw.githubusercontent.com/himanshu231204/jev_model_routers/main/assets/images/jev-claude-04-hard-turn-opus.png)
68
+
69
+ **Picking a model yourself pauses routing** — the status line shows `⏸ manual`:
70
+
71
+ ![Manual model selection bypasses Jev](https://raw.githubusercontent.com/himanshu231204/jev_model_routers/main/assets/images/jev-claude-05-manual-model.png)
72
+
73
+ **`jev-explain` shows why** a turn got its model:
74
+
75
+ ![jev-explain report](https://raw.githubusercontent.com/himanshu231204/jev_model_routers/main/assets/images/jev-claude-06-explain.png)
76
+
77
+ **One safe log line per turn** — no prompt, no keys:
78
+
79
+ ![Decision log](https://raw.githubusercontent.com/himanshu231204/jev_model_routers/main/assets/images/jev-claude-07-decision-log.png)
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"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+