shunt 0.0.1__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.
shunt-0.0.1/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ dist/
4
+ build/
5
+ *.egg-info/
6
+ .venv/
7
+ venv/
8
+ .env
9
+ *.sqlite3
10
+ .pytest_cache/
shunt-0.0.1/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 PierrunoYT
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.
shunt-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,47 @@
1
+ Metadata-Version: 2.4
2
+ Name: shunt
3
+ Version: 0.0.1
4
+ Summary: Routes LLM requests between cheap open-source and premium models based on task difficulty
5
+ Project-URL: Homepage, https://github.com/PierrunoYT/shunt.sh
6
+ Author: PierrunoYT
7
+ License: MIT
8
+ License-File: LICENSE
9
+ Keywords: cost-optimization,llm,openai,openrouter,router
10
+ Classifier: Development Status :: 1 - Planning
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Topic :: Software Development :: Libraries
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+
18
+ # shunt
19
+
20
+ **Early development — APIs will change.**
21
+
22
+ Shunt is a small, self-hosted router with an OpenAI-compatible API. It classifies
23
+ every request — coding, reasoning, light chat — and forwards it to the cheapest
24
+ open-source model that can handle it.
25
+
26
+ ```python
27
+ from shunt import Router
28
+
29
+ router = Router()
30
+ track = router.classify("Refactor this function to be async")
31
+ # -> "coding"
32
+ ```
33
+
34
+ Planned for upcoming releases:
35
+
36
+ - `shunt serve` — local OpenAI-compatible proxy server
37
+ - Ordered fallback chains per track ("light", "general", "coding", "coding_heavy", "reasoning")
38
+ - YAML configuration and a local SQLite cost ledger
39
+
40
+ ## Status
41
+
42
+ `v0.0.1` is a name-holding stub for the project under active development.
43
+ Follow progress at the project repository.
44
+
45
+ ## License
46
+
47
+ MIT
shunt-0.0.1/README.md ADDED
@@ -0,0 +1,30 @@
1
+ # shunt
2
+
3
+ **Early development — APIs will change.**
4
+
5
+ Shunt is a small, self-hosted router with an OpenAI-compatible API. It classifies
6
+ every request — coding, reasoning, light chat — and forwards it to the cheapest
7
+ open-source model that can handle it.
8
+
9
+ ```python
10
+ from shunt import Router
11
+
12
+ router = Router()
13
+ track = router.classify("Refactor this function to be async")
14
+ # -> "coding"
15
+ ```
16
+
17
+ Planned for upcoming releases:
18
+
19
+ - `shunt serve` — local OpenAI-compatible proxy server
20
+ - Ordered fallback chains per track ("light", "general", "coding", "coding_heavy", "reasoning")
21
+ - YAML configuration and a local SQLite cost ledger
22
+
23
+ ## Status
24
+
25
+ `v0.0.1` is a name-holding stub for the project under active development.
26
+ Follow progress at the project repository.
27
+
28
+ ## License
29
+
30
+ MIT
@@ -0,0 +1,9 @@
1
+ # shunt-llm
2
+
3
+ Alias package. Install [`shunt`](https://pypi.org/project/shunt/) instead:
4
+
5
+ ```bash
6
+ pip install shunt
7
+ ```
8
+
9
+ Installing `shunt-llm` pulls in `shunt` and re-exports its API.
@@ -0,0 +1,19 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "shunt-llm"
7
+ version = "0.0.1"
8
+ description = "Alias for the 'shunt' package — routes LLM requests between cheap and premium models"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "PierrunoYT" }]
13
+ dependencies = ["shunt"]
14
+
15
+ [project.urls]
16
+ Homepage = "https://github.com/PierrunoYT/shunt.sh"
17
+
18
+ [tool.hatch.build.targets.wheel]
19
+ packages = ["src/shunt_llm"]
@@ -0,0 +1,5 @@
1
+ """Alias for the ``shunt`` package."""
2
+
3
+ from shunt import Router, Track, __version__
4
+
5
+ __all__ = ["Router", "Track", "__version__"]
@@ -0,0 +1,26 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "shunt"
7
+ version = "0.0.1"
8
+ description = "Routes LLM requests between cheap open-source and premium models based on task difficulty"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "MIT" }
12
+ authors = [{ name = "PierrunoYT" }]
13
+ keywords = ["llm", "router", "openai", "openrouter", "cost-optimization"]
14
+ classifiers = [
15
+ "Development Status :: 1 - Planning",
16
+ "Intended Audience :: Developers",
17
+ "License :: OSI Approved :: MIT License",
18
+ "Programming Language :: Python :: 3",
19
+ "Topic :: Software Development :: Libraries",
20
+ ]
21
+
22
+ [project.urls]
23
+ Homepage = "https://github.com/PierrunoYT/shunt.sh"
24
+
25
+ [tool.hatch.build.targets.wheel]
26
+ packages = ["src/shunt"]
@@ -0,0 +1,10 @@
1
+ """shunt: route LLM requests to the cheapest capable model.
2
+
3
+ Early development. The public API consists of :class:`Router`, whose
4
+ interface is a preview of the real implementation.
5
+ """
6
+
7
+ from shunt.router import Router, Track
8
+
9
+ __all__ = ["Router", "Track", "__version__"]
10
+ __version__ = "0.0.1"
@@ -0,0 +1,48 @@
1
+ """Skeleton router. The heuristics here are intentionally minimal; the real
2
+ classifier and provider fallback chains land in 0.1.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import re
8
+ from enum import Enum
9
+
10
+
11
+ class Track(str, Enum):
12
+ """A routing track: an ordered chain of models suited to a task class."""
13
+
14
+ LIGHT = "light"
15
+ GENERAL = "general"
16
+ CODING = "coding"
17
+ CODING_HEAVY = "coding_heavy"
18
+ REASONING = "reasoning"
19
+
20
+
21
+ _CODE_HINTS = re.compile(
22
+ r"\b(refactor|debug|function|class|bug|compile|stack ?trace|regex|sql|"
23
+ r"python|javascript|typescript|rust|api|unit test)\b|```",
24
+ re.IGNORECASE,
25
+ )
26
+ _REASONING_HINTS = re.compile(
27
+ r"\b(prove|theorem|step[- ]by[- ]step|logic|math|equation|derive|optimi[sz]e)\b",
28
+ re.IGNORECASE,
29
+ )
30
+
31
+
32
+ class Router:
33
+ """Classifies prompts onto tracks. Stub implementation.
34
+
35
+ The 0.1 release will add provider configuration, fallback chains, and an
36
+ OpenAI-compatible ``serve`` mode. The :meth:`classify` signature is stable.
37
+ """
38
+
39
+ def classify(self, prompt: str) -> str:
40
+ """Return the track name for *prompt* using cheap keyword heuristics."""
41
+ if len(prompt) < 40 and not _CODE_HINTS.search(prompt):
42
+ return Track.LIGHT.value
43
+ if _CODE_HINTS.search(prompt):
44
+ heavy = len(prompt) > 2000 or "refactor" in prompt.lower()
45
+ return (Track.CODING_HEAVY if heavy else Track.CODING).value
46
+ if _REASONING_HINTS.search(prompt):
47
+ return Track.REASONING.value
48
+ return Track.GENERAL.value
@@ -0,0 +1,258 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>Shunt — route prompts to the cheapest capable model</title>
7
+ <meta name="description" content="Shunt is a self-hosted, OpenAI-compatible router that sends every request to the cheapest open-source model that can handle it." />
8
+ <style>
9
+ :root {
10
+ --paper: #fcfbf8;
11
+ --ink: #1a1a18;
12
+ --faint: #6b6b65;
13
+ --line: #d9d7d0;
14
+ --accent: #c2410c;
15
+ --code-bg: #f3f1ea;
16
+ --max: 880px;
17
+ }
18
+
19
+ * { margin: 0; padding: 0; box-sizing: border-box; }
20
+
21
+ body {
22
+ background: var(--paper);
23
+ color: var(--ink);
24
+ font-family: Georgia, "Times New Roman", serif;
25
+ font-size: 17px;
26
+ line-height: 1.65;
27
+ }
28
+
29
+ code, pre, .mono {
30
+ font-family: ui-monospace, "SF Mono", Consolas, "Cascadia Mono", monospace;
31
+ font-size: 0.86em;
32
+ }
33
+
34
+ a { color: var(--accent); text-decoration: none; }
35
+ a:hover { text-decoration: underline; }
36
+
37
+ .wrap { max-width: var(--max); margin: 0 auto; padding: 0 24px; }
38
+
39
+ /* Header */
40
+ header {
41
+ border-bottom: 1px solid var(--line);
42
+ padding: 18px 0;
43
+ }
44
+ .header-row {
45
+ display: flex; align-items: baseline; justify-content: space-between;
46
+ flex-wrap: wrap; gap: 12px;
47
+ }
48
+ .name {
49
+ font-family: ui-monospace, "SF Mono", Consolas, monospace;
50
+ font-size: 1.05rem; font-weight: 700; letter-spacing: 0.01em;
51
+ color: var(--ink);
52
+ }
53
+ .name:hover { text-decoration: none; }
54
+ nav a {
55
+ color: var(--faint); margin-left: 22px;
56
+ font-family: ui-monospace, "SF Mono", Consolas, monospace;
57
+ font-size: 0.82rem;
58
+ }
59
+ nav a:hover { color: var(--ink); text-decoration: none; }
60
+
61
+ /* Hero */
62
+ .hero { padding: 72px 0 56px; border-bottom: 1px solid var(--line); }
63
+ h1 {
64
+ font-size: clamp(1.9rem, 4.5vw, 2.7rem);
65
+ font-weight: 400;
66
+ line-height: 1.2;
67
+ letter-spacing: -0.01em;
68
+ max-width: 21em;
69
+ }
70
+ .hero .sub {
71
+ margin-top: 20px; max-width: 38em; color: var(--faint);
72
+ }
73
+ .hero .install {
74
+ margin-top: 32px;
75
+ display: inline-block;
76
+ background: var(--code-bg);
77
+ border: 1px solid var(--line);
78
+ padding: 10px 16px;
79
+ }
80
+ .hero .install code { font-size: 0.92rem; }
81
+ .hero .links { margin-top: 16px; font-size: 0.95rem; }
82
+ .hero .links a + a { margin-left: 18px; }
83
+
84
+ /* Sections */
85
+ section { padding: 52px 0; border-bottom: 1px solid var(--line); }
86
+ h2 {
87
+ font-size: 1.05rem;
88
+ font-family: ui-monospace, "SF Mono", Consolas, monospace;
89
+ font-weight: 700;
90
+ text-transform: uppercase;
91
+ letter-spacing: 0.08em;
92
+ color: var(--faint);
93
+ margin-bottom: 26px;
94
+ }
95
+ section p { max-width: 42em; }
96
+ section p + p { margin-top: 14px; }
97
+
98
+ /* How it works list */
99
+ ol.steps {
100
+ counter-reset: step;
101
+ list-style: none;
102
+ max-width: 42em;
103
+ }
104
+ ol.steps li {
105
+ counter-increment: step;
106
+ padding: 16px 0 16px 56px;
107
+ position: relative;
108
+ }
109
+ ol.steps li + li { border-top: 1px solid var(--line); }
110
+ ol.steps li::before {
111
+ content: counter(step, decimal-leading-zero);
112
+ position: absolute; left: 0; top: 18px;
113
+ font-family: ui-monospace, "SF Mono", Consolas, monospace;
114
+ font-size: 0.8rem; color: var(--accent);
115
+ }
116
+ ol.steps strong { font-weight: 700; }
117
+
118
+ /* Code block */
119
+ pre {
120
+ background: var(--code-bg);
121
+ border: 1px solid var(--line);
122
+ padding: 18px 20px;
123
+ overflow-x: auto;
124
+ line-height: 1.6;
125
+ margin-top: 22px;
126
+ max-width: 42em;
127
+ }
128
+ pre .c { color: var(--faint); }
129
+ pre .s { color: var(--accent); }
130
+
131
+ /* Table */
132
+ table {
133
+ width: 100%;
134
+ border-collapse: collapse;
135
+ font-size: 0.95rem;
136
+ }
137
+ th, td { text-align: left; padding: 10px 14px 10px 0; vertical-align: top; }
138
+ th {
139
+ font-family: ui-monospace, "SF Mono", Consolas, monospace;
140
+ font-size: 0.75rem; font-weight: 700;
141
+ text-transform: uppercase; letter-spacing: 0.07em;
142
+ color: var(--faint);
143
+ border-bottom: 1px solid var(--ink);
144
+ }
145
+ td { border-bottom: 1px solid var(--line); }
146
+ td.mono { white-space: nowrap; }
147
+ .num { font-variant-numeric: tabular-nums; }
148
+
149
+ /* Footer */
150
+ footer { padding: 32px 0 48px; color: var(--faint); font-size: 0.9rem; }
151
+ footer .wrap { display: flex; justify-content: space-between; flex-wrap: wrap; gap: 10px; }
152
+
153
+ @media (max-width: 560px) {
154
+ nav a { margin-left: 14px; }
155
+ ol.steps li { padding-left: 40px; }
156
+ }
157
+ </style>
158
+ </head>
159
+ <body>
160
+
161
+ <header>
162
+ <div class="wrap header-row">
163
+ <a class="name" href="#">shunt</a>
164
+ <nav>
165
+ <a href="#how">how it works</a>
166
+ <a href="#routes">routes</a>
167
+ <a href="#start">install</a>
168
+ <a href="#">github</a>
169
+ </nav>
170
+ </div>
171
+ </header>
172
+
173
+ <div class="hero">
174
+ <div class="wrap">
175
+ <h1>Most prompts don't need a frontier model. Shunt sends each one to the cheapest model that can handle it.</h1>
176
+ <p class="sub">
177
+ Shunt is a small, self-hosted router with an OpenAI-compatible API. It classifies
178
+ every request — coding, reasoning, light chat — and forwards it to an open-source
179
+ model priced for the job, from $0.01 per million tokens.
180
+ </p>
181
+ <div class="install"><code>pip install shunt &amp;&amp; shunt serve</code></div>
182
+ <div class="links">
183
+ <a href="#start">Quickstart</a>
184
+ <a href="#">Documentation</a>
185
+ <a href="#">Source</a>
186
+ </div>
187
+ </div>
188
+ </div>
189
+
190
+ <section id="how">
191
+ <div class="wrap">
192
+ <h2>How it works</h2>
193
+ <ol class="steps">
194
+ <li><strong>Point your client at Shunt.</strong> It speaks the OpenAI API, so existing SDKs, agents, and tools work unchanged — you only swap the base URL.</li>
195
+ <li><strong>Each prompt is classified.</strong> A fast local heuristic (with an optional small-model fallback) tags the request: coding, heavy reasoning, general chat, or trivial.</li>
196
+ <li><strong>The request is switched to a track.</strong> Each track is an ordered chain of open-source models, cheapest capable first. Provider down or rate-limited? Shunt falls through to the next model without surfacing an error.</li>
197
+ <li><strong>Spend is recorded.</strong> A local SQLite ledger tracks tokens and cost per route, and what the same traffic would have cost on a frontier model.</li>
198
+ </ol>
199
+ <pre><span class="c"># your code, unchanged except the base URL</span>
200
+ client = OpenAI(base_url=<span class="s">"http://localhost:8484/v1"</span>)
201
+
202
+ client.chat.completions.create(
203
+ model=<span class="s">"shunt/auto"</span>,
204
+ messages=[{<span class="s">"role"</span>: <span class="s">"user"</span>, <span class="s">"content"</span>: <span class="s">"Refactor this function..."</span>}],
205
+ )</pre>
206
+ </div>
207
+ </section>
208
+
209
+ <section id="routes">
210
+ <div class="wrap">
211
+ <h2>Default routes</h2>
212
+ <p>Every track is plain YAML in <code>shunt.yaml</code>. Reorder models, change prices, add providers — Shunt ships with sensible defaults and gets out of the way.</p>
213
+ <br />
214
+ <table>
215
+ <thead>
216
+ <tr><th>Track</th><th>Used for</th><th>First choice</th><th class="num">Output, $/M</th></tr>
217
+ </thead>
218
+ <tbody>
219
+ <tr><td class="mono"><code>light</code></td><td>Classification, extraction, short replies</td><td>Qwen3 8B</td><td class="num">0.01</td></tr>
220
+ <tr><td class="mono"><code>general</code></td><td>Standard chat and writing</td><td>DeepSeek V4 Flash</td><td class="num">0.20</td></tr>
221
+ <tr><td class="mono"><code>coding</code></td><td>Everyday code generation and edits</td><td>Qwen3 Coder 30B</td><td class="num">0.27</td></tr>
222
+ <tr><td class="mono"><code>coding_heavy</code></td><td>Large refactors, difficult debugging</td><td>DeepSeek V4 Pro</td><td class="num">0.87</td></tr>
223
+ <tr><td class="mono"><code>reasoning</code></td><td>Math, logic, multi-step planning</td><td>Kimi K2.5</td><td class="num">1.90</td></tr>
224
+ </tbody>
225
+ </table>
226
+ </div>
227
+ </section>
228
+
229
+ <section>
230
+ <div class="wrap">
231
+ <h2>Principles</h2>
232
+ <p><strong>Self-hosted by default.</strong> Shunt runs on your machine with your provider keys. Prompts never pass through infrastructure you didn't choose.</p>
233
+ <p><strong>Boring and inspectable.</strong> One Python process, one YAML config, one SQLite file. No dashboard SaaS, no telemetry, no account.</p>
234
+ <p><strong>Cheap means cheap.</strong> Routing overhead is a few milliseconds and the classifier never calls a paid model unless you opt in.</p>
235
+ </div>
236
+ </section>
237
+
238
+ <section id="start">
239
+ <div class="wrap">
240
+ <h2>Quickstart</h2>
241
+ <pre>$ pip install shunt
242
+ $ export OPENROUTER_API_KEY=sk-or-...
243
+ $ shunt serve
244
+ <span class="c">listening on http://localhost:8484/v1
245
+ 5 tracks loaded from shunt.yaml</span></pre>
246
+ <p>Then set <code>base_url</code> in any OpenAI client and use <code>model="shunt/auto"</code>. That's the whole integration.</p>
247
+ </div>
248
+ </section>
249
+
250
+ <footer>
251
+ <div class="wrap">
252
+ <span>Shunt — MIT licensed.</span>
253
+ <span><a href="#">GitHub</a> · <a href="#">Docs</a> · <a href="#">Changelog</a></span>
254
+ </div>
255
+ </footer>
256
+
257
+ </body>
258
+ </html>