tokenrace 1.0.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.
- tokenrace-1.0.0/.gitignore +18 -0
- tokenrace-1.0.0/.streamlit/config.toml +11 -0
- tokenrace-1.0.0/CHANGELOG.md +18 -0
- tokenrace-1.0.0/LICENSE +21 -0
- tokenrace-1.0.0/PKG-INFO +203 -0
- tokenrace-1.0.0/README.it.md +185 -0
- tokenrace-1.0.0/README.md +185 -0
- tokenrace-1.0.0/app.py +414 -0
- tokenrace-1.0.0/assets/logo/tokenrace-favicon.png +0 -0
- tokenrace-1.0.0/assets/logo/tokenrace-favicon.svg +14 -0
- tokenrace-1.0.0/assets/logo/tokenrace-icon.png +0 -0
- tokenrace-1.0.0/assets/logo/tokenrace-icon.svg +47 -0
- tokenrace-1.0.0/assets/logo/tokenrace-mascot.png +0 -0
- tokenrace-1.0.0/assets/logo/tokenrace-mascot.svg +57 -0
- tokenrace-1.0.0/assets/style.css +37 -0
- tokenrace-1.0.0/config/defaults.toml +27 -0
- tokenrace-1.0.0/config/prompts.toml +39 -0
- tokenrace-1.0.0/config/servers.toml +117 -0
- tokenrace-1.0.0/locales/en.toml +194 -0
- tokenrace-1.0.0/locales/it.toml +194 -0
- tokenrace-1.0.0/pyproject.toml +55 -0
- tokenrace-1.0.0/requirements.txt +4 -0
- tokenrace-1.0.0/tokenrace/__init__.py +3 -0
- tokenrace-1.0.0/tokenrace/bench.py +238 -0
- tokenrace-1.0.0/tokenrace/cli.py +28 -0
- tokenrace-1.0.0/tokenrace/config.py +49 -0
- tokenrace-1.0.0/tokenrace/i18n.py +42 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Ambiente virtuale e cache Python
|
|
2
|
+
.venv/
|
|
3
|
+
venv/
|
|
4
|
+
__pycache__/
|
|
5
|
+
*.pyc
|
|
6
|
+
|
|
7
|
+
# Pacchetti generati da uv build
|
|
8
|
+
dist/
|
|
9
|
+
|
|
10
|
+
# Risultati esportati
|
|
11
|
+
*.csv
|
|
12
|
+
|
|
13
|
+
# Configurazioni locali di editor e strumenti
|
|
14
|
+
.claude/
|
|
15
|
+
.vscode/
|
|
16
|
+
.idea/
|
|
17
|
+
.DS_Store
|
|
18
|
+
.streamlit/secrets.toml
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Nessuna statistica d'uso inviata a Streamlit: l'app resta tutta locale.
|
|
2
|
+
[browser]
|
|
3
|
+
gatherUsageStats = false
|
|
4
|
+
|
|
5
|
+
[client]
|
|
6
|
+
# Nasconde il pulsante "Deploy" e lascia solo il menu essenziale.
|
|
7
|
+
toolbarMode = "minimal"
|
|
8
|
+
|
|
9
|
+
[theme]
|
|
10
|
+
primaryColor = "#FF5A1F"
|
|
11
|
+
baseRadius = "0.6rem"
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0 — 2026-11-10
|
|
4
|
+
|
|
5
|
+
First public release.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Install and run with `uvx tokenrace`, with `--port` and `--lang` options.
|
|
10
|
+
- Speed test for local OpenAI-compatible LLM inference servers, with the same client-side measuring code for all: TTFT, generation and effective tokens per second, total time, input and output tokens, estimated prompt speed, system RAM.
|
|
11
|
+
- 15 preconfigured servers, including Ollama, LM Studio, Rapid-MLX, oMLX, llama.cpp and vLLM, with editable URL and optional API key.
|
|
12
|
+
- Three preset tests: Short, Long (≈8,000 tokens of context) and Cache.
|
|
13
|
+
- Reasoning and answer tokens shown separately, and a "No reasoning" switch with the right parameter for each server.
|
|
14
|
+
- Streaming response and live metrics, with the status of each run.
|
|
15
|
+
- Session summary with median, min and max (cold run excluded from the median), chart per server and model, CSV export.
|
|
16
|
+
- English and Italian interface; servers, defaults, prompts and texts in editable TOML files.
|
|
17
|
+
- TokenRace logo and mascot.
|
|
18
|
+
- Form to share results in GitHub Discussions.
|
tokenrace-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ernext
|
|
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.
|
tokenrace-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: tokenrace
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: LLM inference speed test: compare local inference servers on your own hardware, with the same measuring code for all.
|
|
5
|
+
Project-URL: Homepage, https://github.com/ernext/tokenrace
|
|
6
|
+
Project-URL: Repository, https://github.com/ernext/tokenrace
|
|
7
|
+
Project-URL: Issues, https://github.com/ernext/tokenrace/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/ernext/tokenrace/blob/main/CHANGELOG.md
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: benchmark,inference,llm,lm-studio,local-llm,mlx,ollama,speed-test,streamlit
|
|
12
|
+
Requires-Python: >=3.11
|
|
13
|
+
Requires-Dist: pandas==3.1.0
|
|
14
|
+
Requires-Dist: psutil==7.2.2
|
|
15
|
+
Requires-Dist: requests==2.34.2
|
|
16
|
+
Requires-Dist: streamlit==1.66.1
|
|
17
|
+
Description-Content-Type: text/markdown
|
|
18
|
+
|
|
19
|
+
<p align="center"><img src="https://raw.githubusercontent.com/ernext/tokenrace/main/assets/logo/tokenrace-mascot.png" alt="TokenRace mascot: a running token" width="190"></p>
|
|
20
|
+
|
|
21
|
+
# TokenRace — Local LLM Speed Test
|
|
22
|
+
|
|
23
|
+
[](https://www.python.org)
|
|
24
|
+
[](https://streamlit.io)
|
|
25
|
+
[](LICENSE)
|
|
26
|
+
[](#supported-servers)
|
|
27
|
+
[](#languages)
|
|
28
|
+
|
|
29
|
+
**English** · [Italiano](README.it.md)
|
|
30
|
+
|
|
31
|
+
An **LLM inference speed test**: compare local inference servers (Ollama, LM Studio, Rapid-MLX, oMLX, llama.cpp, vLLM and more) on your own hardware, with **the exact same measuring code** for all of them.
|
|
32
|
+
|
|
33
|
+
Speed depends on the combination of model, inference server and hardware: there is no universal winner, and numbers measured elsewhere, with other rules, rarely apply to your machine. **Measure first, then choose.**
|
|
34
|
+
|
|
35
|
+
A small [Streamlit](https://streamlit.io) app: pick a server and a model, start the test and watch TTFT, tokens per second, total time and RAM live, with a summary, a chart and CSV export.
|
|
36
|
+
|
|
37
|
+
<p align="center">
|
|
38
|
+
<img src="https://raw.githubusercontent.com/ernext/tokenrace/main/docs/tokenrace-en.png" alt="TokenRace: LFM3 1.7B at 8-bit on Rapid-MLX, oMLX and Ollama, with streaming response, live metrics and summary" width="900">
|
|
39
|
+
</p>
|
|
40
|
+
<p align="center"><sub>LFM3 1.7B at 8-bit on Rapid-MLX and oMLX (same MLX weights) and on Ollama (mxfp8), "Short" prompt, no reasoning, 3 runs on one Mac. A single example, not a ranking.</sub></p>
|
|
41
|
+
|
|
42
|
+
## Features
|
|
43
|
+
|
|
44
|
+
- Same method for every server: streaming `POST /chat/completions`, timed on the client with `time.perf_counter()`, never with the servers' own stats.
|
|
45
|
+
- Three preset tests: **Short** (long answer), **Long** (≈8,000 tokens of context) and **Cache** (identical prompt on every run).
|
|
46
|
+
- Streaming response and live metrics.
|
|
47
|
+
- Reasoning models timed correctly, with reasoning and answer tokens shown separately, plus a **No reasoning** switch.
|
|
48
|
+
- First run marked "cold" and excluded from the median.
|
|
49
|
+
- Summary with median, min and max, a chart per server and model, CSV export.
|
|
50
|
+
- English and Italian interface; servers, defaults and texts in easy-to-edit TOML files.
|
|
51
|
+
- Fully local: no database, accounts or telemetry.
|
|
52
|
+
|
|
53
|
+
## Example results
|
|
54
|
+
|
|
55
|
+
LFM3 1.7B at 8-bit on an Apple M5 Pro (24 GB), "Short" prompt, 512 tokens, median of 3 warm runs. Effective speed in tok/s.
|
|
56
|
+
|
|
57
|
+
| Server | IT, reasoning on | IT, no reasoning | EN, reasoning on | EN, no reasoning |
|
|
58
|
+
|---|---:|---:|---:|---:|
|
|
59
|
+
| oMLX | **131.8** | **129.1** | 123.1 | **117.2** |
|
|
60
|
+
| Rapid-MLX | 128.8 | 126.8 | **127.0** | 103.2 |
|
|
61
|
+
| Ollama (mxfp8) | 118.0 | 113.9 | 101.1 | 104.4 |
|
|
62
|
+
|
|
63
|
+
- oMLX and Rapid-MLX use the same MLX weights (`LiquidAI/LFM3-1.7B-MLX-8bit`).
|
|
64
|
+
- With reasoning on, oMLX and Ollama spend all 512 tokens reasoning, while Rapid-MLX does not reason with this model: those columns are not a like-for-like comparison.
|
|
65
|
+
- The same test can vary by 10–20% from one minute to the next on the same Mac: treat small gaps as a tie and run your own tests.
|
|
66
|
+
|
|
67
|
+
Got different numbers on your hardware? **[Share your results](https://github.com/ernext/tokenrace/discussions/categories/share-your-results)**.
|
|
68
|
+
|
|
69
|
+
## Requirements
|
|
70
|
+
|
|
71
|
+
- Python 3.11 or newer
|
|
72
|
+
- At least one local OpenAI-compatible LLM server, already running
|
|
73
|
+
|
|
74
|
+
Built for Apple Silicon Macs, works on Linux and Windows too.
|
|
75
|
+
|
|
76
|
+
## Install
|
|
77
|
+
|
|
78
|
+
The quickest way needs only [uv](https://docs.astral.sh/uv/getting-started/installation/) (macOS/Linux: `curl -LsSf https://astral.sh/uv/install.sh | sh`):
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
uvx tokenrace
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
The app opens at http://localhost:8501. Options: `uvx tokenrace --port 8600 --lang it`.
|
|
85
|
+
|
|
86
|
+
Alternatively, from the source code:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
git clone https://github.com/ernext/tokenrace.git
|
|
90
|
+
cd tokenrace
|
|
91
|
+
python3 -m venv .venv
|
|
92
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
93
|
+
pip install -r requirements.txt
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Run
|
|
97
|
+
|
|
98
|
+
If you installed from the source code:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
streamlit run app.py
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Then open http://localhost:8501. The interface opens in English; switch language with the selector at the top right, or open http://localhost:8501/?lang=it.
|
|
105
|
+
|
|
106
|
+
## Supported servers
|
|
107
|
+
|
|
108
|
+
TokenRace never installs or starts servers: start them yourself. They all speak the same OpenAI-compatible API, and the app shows the start command of the selected server.
|
|
109
|
+
|
|
110
|
+
If your server is not listed but is OpenAI-compatible, pick any entry and change the base URL, or add it to `config/servers.toml` (see below).
|
|
111
|
+
|
|
112
|
+
| Server | Default URL | Start | Notes |
|
|
113
|
+
|---|---|---|---|
|
|
114
|
+
| <img src="https://github.com/ollama.png?size=40" width="20" alt=""> [Ollama](https://ollama.com) | `http://localhost:11434/v1` | `ollama serve` | Mac, Linux, Windows. |
|
|
115
|
+
| <img src="https://github.com/lmstudio-ai.png?size=40" width="20" alt=""> [LM Studio](https://lmstudio.ai) | `http://localhost:1234/v1` | `lms server start && lms load <model>` | Mac, Linux, Windows. |
|
|
116
|
+
| <img src="https://raw.githubusercontent.com/raullenchai/Rapid-MLX/e2114cf9e7c16dfa3c668832ea6780c02779ca89/docs/assets/logo.png" width="20" alt=""> [Rapid-MLX](https://github.com/raullenchai/Rapid-MLX) | `http://localhost:7659/v1` | `rapid-mlx serve <model> --port 8000` | Mac only. The Rapid-MLX Desktop app uses port 7659 and requires the API key; the CLI uses 8000. |
|
|
117
|
+
| <img src="https://raw.githubusercontent.com/jundot/omlx/2165e75726ba83d9651dbaced61c97728da77bba/apps/omlx-web/omlx_web/static/favicon.svg" width="20" alt=""> [oMLX](https://github.com/jundot/omlx) | `http://localhost:8000/v1` | — | Mac only. Start it from the oMLX app: it uses port 8000 and requires the API key set in the app. |
|
|
118
|
+
| <img src="https://github.com/ml-explore.png?size=40" width="20" alt=""> [mlx_lm.server](https://github.com/ml-explore/mlx-lm) | `http://localhost:8080/v1` | `mlx_lm.server --model <model> --port 8080` | Mac only. The reference MLX server, with no extra optimizations. |
|
|
119
|
+
| <img src="https://github.com/ggml-org.png?size=40" width="20" alt=""> [llama.cpp (llama-server)](https://github.com/ggml-org/llama.cpp) | `http://localhost:8080/v1` | `llama-server -m <file.gguf> --port 8080` | Mac, Linux, Windows. The engine used by Ollama and LM Studio. |
|
|
120
|
+
| <img src="https://github.com/vllm-project.png?size=40" width="20" alt=""> [vLLM](https://github.com/vllm-project/vllm) | `http://localhost:8000/v1` | `vllm serve <model>` | Linux with NVIDIA/AMD GPU. The most widely used in production. |
|
|
121
|
+
| <img src="https://github.com/sgl-project.png?size=40" width="20" alt=""> [SGLang](https://github.com/sgl-project/sglang) | `http://localhost:30000/v1` | `python -m sglang.launch_server --model-path <model>` | Linux with NVIDIA/AMD GPU. |
|
|
122
|
+
| <img src="https://github.com/lemonade-sdk.png?size=40" width="20" alt=""> [Lemonade](https://github.com/lemonade-sdk/lemonade) | `http://localhost:13305/api/v1` | `lemonade-server serve` | Windows/Linux, optimized for AMD Ryzen AI. Older versions use port 8000. |
|
|
123
|
+
| <img src="https://github.com/docker.png?size=40" width="20" alt=""> [Docker Model Runner](https://docs.docker.com/ai/model-runner/) | `http://localhost:12434/engines/v1` | `docker desktop enable model-runner --tcp 12434` | Mac, Linux, Windows. Host-side TCP access must be enabled. |
|
|
124
|
+
| <img src="https://github.com/unslothai.png?size=40" width="20" alt=""> [Unsloth Studio](https://github.com/unslothai/unsloth) | `http://localhost:8888/v1` | `unsloth run --model <model>` | Port 8888 comes from examples: «unsloth run» prints the real one at startup. Needs the sk-unsloth-… API key (Studio → Settings → API). |
|
|
125
|
+
| <img src="https://github.com/go-skynet.png?size=40" width="20" alt=""> [LocalAI](https://github.com/mudler/LocalAI) | `http://localhost:8080/v1` | `local-ai run <model>` | Mac, Linux, Windows. |
|
|
126
|
+
| <img src="https://github.com/janhq.png?size=40" width="20" alt=""> [Jan](https://github.com/janhq/jan) | `http://localhost:1337/v1` | — | Mac, Linux, Windows. In Jan: Settings → Local API Server → Start. Requires the API key set in Jan. |
|
|
127
|
+
| <img src="https://raw.githubusercontent.com/LostRuins/koboldcpp/6d2fedf8624e5f05926af3191f9efdcb13c4133c/niko.ico" width="20" alt=""> [KoboldCpp](https://github.com/LostRuins/koboldcpp) | `http://localhost:5001/v1` | `koboldcpp --model <file.gguf>` | Mac, Linux, Windows. |
|
|
128
|
+
| <img src="https://raw.githubusercontent.com/oobabooga/textgen/c93f8871239550de2ccfe1e95d469aa82616f07e/css/icon.png" width="20" alt=""> [text-generation-webui](https://github.com/oobabooga/textgen) | `http://localhost:5000/v1` | — | Mac, Linux, Windows. Start it with --api. On Mac port 5000 is taken by AirPlay Receiver: use --api-port 5005 and change the URL. |
|
|
129
|
+
|
|
130
|
+
Notes:
|
|
131
|
+
- Several servers share port 8000 or 8080: run one at a time, or change the port with `--port` and update the URL in the app.
|
|
132
|
+
- The **API key** field is optional: it is needed for Rapid-MLX Desktop, oMLX, Jan, Unsloth Studio or vLLM started with `--api-key`. It is never saved or exported.
|
|
133
|
+
|
|
134
|
+
## Metrics
|
|
135
|
+
|
|
136
|
+
- **TTFT**: time from sending the request to the first non-empty chunk (content or reasoning).
|
|
137
|
+
- **Generation (tok/s)**: (output tokens − 1) / (last token − first token).
|
|
138
|
+
- **Effective speed (tok/s)**: output tokens / (last token − request sent).
|
|
139
|
+
- **Prompt processing (estimate)**: input tokens / TTFT.
|
|
140
|
+
- **RAM**: memory in use on the whole system, sampled every 0.5 s (value before the test and peak).
|
|
141
|
+
|
|
142
|
+
Tokens come from the `usage` field. If the server does not send it, chunks are counted and an "estimated count" warning appears.
|
|
143
|
+
|
|
144
|
+
## Presets
|
|
145
|
+
|
|
146
|
+
- **Short**: short prompt, long answer.
|
|
147
|
+
- **Long**: about 8,000 tokens of context, short answer.
|
|
148
|
+
- **Cache**: the same long prompt, identical on every run.
|
|
149
|
+
|
|
150
|
+
In Short and Long every run starts with a unique ID, so prompt caching does not skew the result. Prompts exist in both languages: compare only runs made with the same prompt language (the CSV records it).
|
|
151
|
+
|
|
152
|
+
The **No reasoning** switch sends a different parameter depending on the server (✅ = tested):
|
|
153
|
+
- Ollama: `reasoning_effort: "none"` ✅
|
|
154
|
+
- LM Studio: `reasoning_effort: "none"` plus `chat_template_kwargs` ✅
|
|
155
|
+
- oMLX: `chat_template_kwargs: {"enable_thinking": false}` ✅
|
|
156
|
+
- text-generation-webui: `enable_thinking: false`
|
|
157
|
+
- all others (llama.cpp, MLX, vLLM, SGLang…): `chat_template_kwargs: {"enable_thinking": false}`
|
|
158
|
+
|
|
159
|
+
Some models reason anyway: the app shows a warning in that case.
|
|
160
|
+
|
|
161
|
+
## Project structure
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
app.py Streamlit interface
|
|
165
|
+
tokenrace/
|
|
166
|
+
bench.py measuring logic (streaming, timing, RAM, token counts), no Streamlit
|
|
167
|
+
cli.py the `tokenrace` command
|
|
168
|
+
config.py reads the TOML files
|
|
169
|
+
i18n.py translations
|
|
170
|
+
config/
|
|
171
|
+
servers.toml preconfigured servers
|
|
172
|
+
defaults.toml default parameters, timeouts, languages, colours
|
|
173
|
+
prompts.toml preset prompt texts, per language
|
|
174
|
+
locales/
|
|
175
|
+
en.toml, it.toml interface texts
|
|
176
|
+
assets/style.css visual style
|
|
177
|
+
assets/logo/ mascot, icon and favicon (SVG + PNG)
|
|
178
|
+
tools/make_logo.py regenerates the logo: python tools/make_logo.py --png
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Add a server**: add a `[[server]]` block to `config/servers.toml` and its note under `[server_notes]` in each `locales/*.toml` file.
|
|
182
|
+
|
|
183
|
+
<a id="languages"></a>**Add a language**: copy `locales/en.toml` to `locales/<code>.toml`, translate it, add the prompt texts in `config/prompts.toml` and the code to `languages` in `config/defaults.toml`.
|
|
184
|
+
|
|
185
|
+
## Limitations
|
|
186
|
+
|
|
187
|
+
- RAM is the whole system's, not just the server's.
|
|
188
|
+
- It measures speed, not the quality of the answers.
|
|
189
|
+
- Prompt processing speed is an estimate (input tokens / TTFT) and includes network latency and any model loading.
|
|
190
|
+
- Default ports for Unsloth Studio and text-generation-webui come from examples in their docs: check them when you start the server.
|
|
191
|
+
|
|
192
|
+
## Contributing
|
|
193
|
+
|
|
194
|
+
Issues and pull requests are welcome, especially:
|
|
195
|
+
- confirmations or fixes for ports and "no reasoning" parameters of servers not yet tested;
|
|
196
|
+
- new OpenAI-compatible servers;
|
|
197
|
+
- new translations.
|
|
198
|
+
|
|
199
|
+
If TokenRace is useful to you, a ⭐ on GitHub is always appreciated.
|
|
200
|
+
|
|
201
|
+
## License
|
|
202
|
+
|
|
203
|
+
[MIT](LICENSE). Server logos belong to their respective projects and are shown only to identify them.
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
<p align="center"><img src="assets/logo/tokenrace-mascot.svg" alt="Mascotte di TokenRace: un gettone che corre" width="190"></p>
|
|
2
|
+
|
|
3
|
+
# TokenRace — Local LLM Speed Test
|
|
4
|
+
|
|
5
|
+
[](https://www.python.org)
|
|
6
|
+
[](https://streamlit.io)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](#server-supportati)
|
|
9
|
+
[](#lingue)
|
|
10
|
+
|
|
11
|
+
[English](README.md) · **Italiano**
|
|
12
|
+
|
|
13
|
+
Un **LLM inference speed test**: confronta gli inference server locali (Ollama, LM Studio, Rapid-MLX, oMLX, llama.cpp, vLLM e altri) sul tuo hardware, con **lo stesso identico codice di misura** per tutti.
|
|
14
|
+
|
|
15
|
+
La velocità dipende dalla combinazione di modello, inference server e hardware: non c'è un vincitore universale, e i numeri misurati altrove, con altre regole, raramente valgono per la tua macchina. **Prima misuri, poi scegli.**
|
|
16
|
+
|
|
17
|
+
Una piccola app [Streamlit](https://streamlit.io): scegli server e modello, avvii il test e vedi in tempo reale TTFT, token al secondo, tempo totale e RAM, con riepilogo, grafico ed export CSV.
|
|
18
|
+
|
|
19
|
+
<p align="center">
|
|
20
|
+
<img src="docs/tokenrace-it.png" alt="TokenRace: LFM3 1.7B a 8 bit su Rapid-MLX, oMLX e Ollama, con risposta in streaming, misure e riepilogo" width="900">
|
|
21
|
+
</p>
|
|
22
|
+
<p align="center"><sub>LFM3 1.7B a 8 bit su Rapid-MLX e oMLX (stessi pesi MLX) e su Ollama (mxfp8), prompt "Breve", senza ragionamento, 3 ripetizioni su un Mac. Un singolo esempio, non una classifica.</sub></p>
|
|
23
|
+
|
|
24
|
+
## Caratteristiche
|
|
25
|
+
|
|
26
|
+
- Stesso metodo per tutti i server: `POST /chat/completions` in streaming, cronometrato lato client con `time.perf_counter()`, mai con le statistiche dei server.
|
|
27
|
+
- Tre prove pronte: **Breve** (risposta lunga), **Lungo** (≈8.000 token di contesto) e **Cache** (prompt identico a ogni ripetizione).
|
|
28
|
+
- Risposta in streaming e misure in tempo reale.
|
|
29
|
+
- Modelli con ragionamento cronometrati correttamente, con token di ragionamento e di risposta separati, e interruttore **Senza ragionamento**.
|
|
30
|
+
- Prima ripetizione segnata "a freddo" ed esclusa dalla mediana.
|
|
31
|
+
- Riepilogo con mediana, minimo e massimo, grafico per server e modello ed export CSV.
|
|
32
|
+
- Interfaccia in inglese e italiano; server, valori predefiniti e testi in file TOML facili da modificare.
|
|
33
|
+
- Tutto locale: niente database, account o telemetria.
|
|
34
|
+
|
|
35
|
+
## Risultati di esempio
|
|
36
|
+
|
|
37
|
+
LFM3 1.7B a 8 bit su Apple M5 Pro (24 GB), prompt "Breve", 512 token, mediana di 3 ripetizioni a caldo. Velocità effettiva in tok/s.
|
|
38
|
+
|
|
39
|
+
| Server | IT, ragionamento attivo | IT, senza ragionamento | EN, ragionamento attivo | EN, senza ragionamento |
|
|
40
|
+
|---|---:|---:|---:|---:|
|
|
41
|
+
| oMLX | **131,8** | **129,1** | 123,1 | **117,2** |
|
|
42
|
+
| Rapid-MLX | 128,8 | 126,8 | **127,0** | 103,2 |
|
|
43
|
+
| Ollama (mxfp8) | 118,0 | 113,9 | 101,1 | 104,4 |
|
|
44
|
+
|
|
45
|
+
- oMLX e Rapid-MLX usano gli stessi pesi MLX (`LiquidAI/LFM3-1.7B-MLX-8bit`).
|
|
46
|
+
- Con il ragionamento attivo oMLX e Ollama passano tutti i 512 token a ragionare, mentre Rapid-MLX con questo modello non ragiona: quelle colonne non sono un confronto alla pari.
|
|
47
|
+
- Lo stesso test può variare del 10–20% da un minuto all'altro sullo stesso Mac: considera pari le differenze piccole e fai le tue prove.
|
|
48
|
+
|
|
49
|
+
Hai ottenuto numeri diversi sul tuo hardware? **[Share your results](https://github.com/ernext/tokenrace/discussions/categories/share-your-results)**.
|
|
50
|
+
|
|
51
|
+
## Requisiti
|
|
52
|
+
|
|
53
|
+
- Python 3.11 o superiore
|
|
54
|
+
- Almeno un server LLM locale compatibile OpenAI, già avviato
|
|
55
|
+
|
|
56
|
+
Pensata per Mac con Apple Silicon, funziona anche su Linux e Windows.
|
|
57
|
+
|
|
58
|
+
## Installazione
|
|
59
|
+
|
|
60
|
+
Il modo più rapido richiede solo [uv](https://docs.astral.sh/uv/getting-started/installation/) (macOS/Linux: `curl -LsSf https://astral.sh/uv/install.sh | sh`):
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
uvx tokenrace
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
L'app si apre su http://localhost:8501. Opzioni: `uvx tokenrace --port 8600 --lang it`.
|
|
67
|
+
|
|
68
|
+
In alternativa, dal codice sorgente:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
git clone https://github.com/ernext/tokenrace.git
|
|
72
|
+
cd tokenrace
|
|
73
|
+
python3 -m venv .venv
|
|
74
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
75
|
+
pip install -r requirements.txt
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Avvio
|
|
79
|
+
|
|
80
|
+
Se hai installato dal codice sorgente:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
streamlit run app.py
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Poi apri http://localhost:8501. L'interfaccia si apre in inglese: cambia lingua con il selettore in alto a destra, oppure apri direttamente http://localhost:8501/?lang=it.
|
|
87
|
+
|
|
88
|
+
## Server supportati
|
|
89
|
+
|
|
90
|
+
TokenRace non installa né avvia i server: vanno avviati a parte. Tutti parlano la stessa API compatibile OpenAI, e l'app mostra il comando di avvio del server scelto.
|
|
91
|
+
|
|
92
|
+
Se il tuo server non è in elenco ma è compatibile OpenAI, scegline uno qualsiasi e cambia l'URL base, oppure aggiungilo in `config/servers.toml` (vedi sotto).
|
|
93
|
+
|
|
94
|
+
| Server | URL predefinito | Avvio | Note |
|
|
95
|
+
|---|---|---|---|
|
|
96
|
+
| <img src="https://github.com/ollama.png?size=40" width="20" alt=""> [Ollama](https://ollama.com) | `http://localhost:11434/v1` | `ollama serve` | Mac, Linux, Windows. |
|
|
97
|
+
| <img src="https://github.com/lmstudio-ai.png?size=40" width="20" alt=""> [LM Studio](https://lmstudio.ai) | `http://localhost:1234/v1` | `lms server start && lms load <model>` | Mac, Linux, Windows. |
|
|
98
|
+
| <img src="https://raw.githubusercontent.com/raullenchai/Rapid-MLX/e2114cf9e7c16dfa3c668832ea6780c02779ca89/docs/assets/logo.png" width="20" alt=""> [Rapid-MLX](https://github.com/raullenchai/Rapid-MLX) | `http://localhost:7659/v1` | `rapid-mlx serve <model> --port 8000` | Solo Mac. L'app Rapid-MLX Desktop usa la porta 7659 e richiede la API key; la CLI usa la 8000. |
|
|
99
|
+
| <img src="https://raw.githubusercontent.com/jundot/omlx/2165e75726ba83d9651dbaced61c97728da77bba/apps/omlx-web/omlx_web/static/favicon.svg" width="20" alt=""> [oMLX](https://github.com/jundot/omlx) | `http://localhost:8000/v1` | — | Solo Mac. Avvialo dall'app oMLX: usa la porta 8000 e richiede la API key impostata nell'app. |
|
|
100
|
+
| <img src="https://github.com/ml-explore.png?size=40" width="20" alt=""> [mlx_lm.server](https://github.com/ml-explore/mlx-lm) | `http://localhost:8080/v1` | `mlx_lm.server --model <model> --port 8080` | Solo Mac. Server di riferimento MLX, senza ottimizzazioni extra. |
|
|
101
|
+
| <img src="https://github.com/ggml-org.png?size=40" width="20" alt=""> [llama.cpp (llama-server)](https://github.com/ggml-org/llama.cpp) | `http://localhost:8080/v1` | `llama-server -m <file.gguf> --port 8080` | Mac, Linux, Windows. Il motore usato da Ollama e LM Studio. |
|
|
102
|
+
| <img src="https://github.com/vllm-project.png?size=40" width="20" alt=""> [vLLM](https://github.com/vllm-project/vllm) | `http://localhost:8000/v1` | `vllm serve <model>` | Linux con GPU NVIDIA/AMD. Il più usato in produzione. |
|
|
103
|
+
| <img src="https://github.com/sgl-project.png?size=40" width="20" alt=""> [SGLang](https://github.com/sgl-project/sglang) | `http://localhost:30000/v1` | `python -m sglang.launch_server --model-path <model>` | Linux con GPU NVIDIA/AMD. |
|
|
104
|
+
| <img src="https://github.com/lemonade-sdk.png?size=40" width="20" alt=""> [Lemonade](https://github.com/lemonade-sdk/lemonade) | `http://localhost:13305/api/v1` | `lemonade-server serve` | Windows/Linux, ottimizzato per AMD Ryzen AI. Le versioni vecchie usano la porta 8000. |
|
|
105
|
+
| <img src="https://github.com/docker.png?size=40" width="20" alt=""> [Docker Model Runner](https://docs.docker.com/ai/model-runner/) | `http://localhost:12434/engines/v1` | `docker desktop enable model-runner --tcp 12434` | Mac, Linux, Windows. Serve l'accesso TCP dall'host attivo. |
|
|
106
|
+
| <img src="https://github.com/unslothai.png?size=40" width="20" alt=""> [Unsloth Studio](https://github.com/unslothai/unsloth) | `http://localhost:8888/v1` | `unsloth run --model <model>` | Porta 8888 presa dagli esempi: quella vera la stampa «unsloth run» all'avvio. Serve la API key sk-unsloth-… (Studio → Impostazioni → API). |
|
|
107
|
+
| <img src="https://github.com/go-skynet.png?size=40" width="20" alt=""> [LocalAI](https://github.com/mudler/LocalAI) | `http://localhost:8080/v1` | `local-ai run <model>` | Mac, Linux, Windows. |
|
|
108
|
+
| <img src="https://github.com/janhq.png?size=40" width="20" alt=""> [Jan](https://github.com/janhq/jan) | `http://localhost:1337/v1` | — | Mac, Linux, Windows. In Jan: Impostazioni → Local API Server → Avvia. Richiede la API key impostata in Jan. |
|
|
109
|
+
| <img src="https://raw.githubusercontent.com/LostRuins/koboldcpp/6d2fedf8624e5f05926af3191f9efdcb13c4133c/niko.ico" width="20" alt=""> [KoboldCpp](https://github.com/LostRuins/koboldcpp) | `http://localhost:5001/v1` | `koboldcpp --model <file.gguf>` | Mac, Linux, Windows. |
|
|
110
|
+
| <img src="https://raw.githubusercontent.com/oobabooga/textgen/c93f8871239550de2ccfe1e95d469aa82616f07e/css/icon.png" width="20" alt=""> [text-generation-webui](https://github.com/oobabooga/textgen) | `http://localhost:5000/v1` | — | Mac, Linux, Windows. Avvialo con --api. Su Mac la porta 5000 è occupata dal Ricevitore AirPlay: usa --api-port 5005 e cambia l'URL. |
|
|
111
|
+
|
|
112
|
+
Note:
|
|
113
|
+
- Diversi server condividono la porta 8000 o 8080: avviane uno alla volta, oppure cambia porta con `--port` e aggiorna l'URL nell'app.
|
|
114
|
+
- Il campo **API key** è facoltativo: serve per Rapid-MLX Desktop, oMLX, Jan, Unsloth Studio o vLLM avviato con `--api-key`. Non viene mai salvato né esportato.
|
|
115
|
+
|
|
116
|
+
## Misure
|
|
117
|
+
|
|
118
|
+
- **TTFT**: tempo dall'invio della richiesta al primo chunk non vuoto (contenuto o ragionamento).
|
|
119
|
+
- **Generazione (tok/s)**: (token output − 1) / (ultimo token − primo token).
|
|
120
|
+
- **Velocità effettiva (tok/s)**: token output / (ultimo token − invio richiesta).
|
|
121
|
+
- **Elaborazione prompt (stima)**: token input / TTFT.
|
|
122
|
+
- **RAM**: memoria in uso dell'intero sistema, campionata ogni 0,5 s (valore prima del test e picco).
|
|
123
|
+
|
|
124
|
+
I token arrivano dal campo `usage`. Se il server non lo fornisce, vengono contati i chunk e compare l'avviso "conteggio stimato".
|
|
125
|
+
|
|
126
|
+
## Prompt pronti
|
|
127
|
+
|
|
128
|
+
- **Breve**: prompt corto, risposta lunga.
|
|
129
|
+
- **Lungo**: circa 8.000 token di contesto, risposta breve.
|
|
130
|
+
- **Cache**: lo stesso prompt lungo, identico a ogni ripetizione.
|
|
131
|
+
|
|
132
|
+
In Breve e Lungo ogni ripetizione inizia con un ID univoco, così la cache del prompt non altera il risultato. I prompt esistono in entrambe le lingue: confronta solo prove fatte con la stessa lingua del prompt (il CSV la registra).
|
|
133
|
+
|
|
134
|
+
L'interruttore **Senza ragionamento** invia un parametro diverso a seconda del server (✅ = provato):
|
|
135
|
+
- Ollama: `reasoning_effort: "none"` ✅
|
|
136
|
+
- LM Studio: `reasoning_effort: "none"` più `chat_template_kwargs` ✅
|
|
137
|
+
- oMLX: `chat_template_kwargs: {"enable_thinking": false}` ✅
|
|
138
|
+
- text-generation-webui: `enable_thinking: false`
|
|
139
|
+
- tutti gli altri (llama.cpp, MLX, vLLM, SGLang…): `chat_template_kwargs: {"enable_thinking": false}`
|
|
140
|
+
|
|
141
|
+
Alcuni modelli ragionano comunque: in quel caso l'app mostra un avviso.
|
|
142
|
+
|
|
143
|
+
## Struttura del progetto
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
app.py interfaccia Streamlit
|
|
147
|
+
tokenrace/
|
|
148
|
+
bench.py logica di misura (streaming, cronometro, RAM, token), senza Streamlit
|
|
149
|
+
cli.py il comando `tokenrace`
|
|
150
|
+
config.py lettura dei file TOML
|
|
151
|
+
i18n.py traduzioni
|
|
152
|
+
config/
|
|
153
|
+
servers.toml server preconfigurati
|
|
154
|
+
defaults.toml parametri predefiniti, timeout, lingue, colori
|
|
155
|
+
prompts.toml testi dei prompt pronti, per lingua
|
|
156
|
+
locales/
|
|
157
|
+
en.toml, it.toml testi dell'interfaccia
|
|
158
|
+
assets/style.css stile grafico
|
|
159
|
+
assets/logo/ mascotte, icona e favicon (SVG + PNG)
|
|
160
|
+
tools/make_logo.py rigenera il logo: python tools/make_logo.py --png
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Aggiungere un server**: un blocco `[[server]]` in `config/servers.toml` e la sua nota sotto `[server_notes]` in ogni file `locales/*.toml`.
|
|
164
|
+
|
|
165
|
+
<a id="lingue"></a>**Aggiungere una lingua**: copia `locales/en.toml` come `locales/<codice>.toml`, traducilo, aggiungi i testi dei prompt in `config/prompts.toml` e il codice in `languages` dentro `config/defaults.toml`.
|
|
166
|
+
|
|
167
|
+
## Limiti
|
|
168
|
+
|
|
169
|
+
- La RAM è quella dell'intero sistema, non del solo server.
|
|
170
|
+
- Misura la velocità, non la qualità delle risposte.
|
|
171
|
+
- La velocità di elaborazione del prompt è una stima (token input / TTFT) e include la latenza di rete e l'eventuale caricamento del modello.
|
|
172
|
+
- Le porte predefinite di Unsloth Studio e text-generation-webui vengono da esempi nella documentazione: verificale all'avvio del server.
|
|
173
|
+
|
|
174
|
+
## Contribuire
|
|
175
|
+
|
|
176
|
+
Segnalazioni e pull request sono benvenute, in particolare:
|
|
177
|
+
- conferme o correzioni su porte e parametri "senza ragionamento" dei server non ancora provati;
|
|
178
|
+
- nuovi server compatibili OpenAI;
|
|
179
|
+
- nuove traduzioni.
|
|
180
|
+
|
|
181
|
+
Se TokenRace ti è utile, una ⭐ su GitHub fa sempre piacere.
|
|
182
|
+
|
|
183
|
+
## Licenza
|
|
184
|
+
|
|
185
|
+
[MIT](LICENSE). I loghi dei server appartengono ai rispettivi progetti e sono usati solo per identificarli.
|