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 +10 -0
- shunt-0.0.1/LICENSE +21 -0
- shunt-0.0.1/PKG-INFO +47 -0
- shunt-0.0.1/README.md +30 -0
- shunt-0.0.1/alias/shunt-llm/README.md +9 -0
- shunt-0.0.1/alias/shunt-llm/pyproject.toml +19 -0
- shunt-0.0.1/alias/shunt-llm/src/shunt_llm/__init__.py +5 -0
- shunt-0.0.1/pyproject.toml +26 -0
- shunt-0.0.1/src/shunt/__init__.py +10 -0
- shunt-0.0.1/src/shunt/router.py +48 -0
- shunt-0.0.1/website/index.html +258 -0
shunt-0.0.1/.gitignore
ADDED
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,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,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 && 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>
|