tuieval 0.2.0.dev14__tar.gz → 0.2.0.dev28__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.
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/CHANGELOG.md +11 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/PKG-INFO +18 -7
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/README.md +17 -6
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/docs/models.md +130 -3
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/docs/writing-packs.md +6 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/_version.py +2 -2
- tuieval-0.2.0.dev28/src/tuieval/builtin.toml +83 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/cli.py +21 -13
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/compare.py +17 -3
- tuieval-0.2.0.dev28/src/tuieval/doctor.py +131 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/engine.py +379 -50
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/graders/code.py +1 -0
- tuieval-0.2.0.dev28/src/tuieval/onboard.py +132 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/packs.py +8 -2
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/run_evals.py +81 -9
- tuieval-0.2.0.dev28/src/tuieval/scaffold.py +243 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/selftest.py +11 -7
- tuieval-0.2.0.dev28/src/tuieval/templates/models.toml +23 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/tui.py +316 -38
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/verdict.py +57 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/tests/mock_server.py +5 -4
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/tests/test_tuieval.py +368 -2
- tuieval-0.2.0.dev14/src/tuieval/scaffold.py +0 -106
- tuieval-0.2.0.dev14/src/tuieval/templates/models.toml +0 -142
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/.gitignore +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/LICENSE +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/RELEASING.md +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/docs/images/brand/README.md +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/docs/images/brand/favicon.ico +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/docs/images/brand/readme-header.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/docs/images/brand/social-preview.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/docs/images/brand/tuieval-icon-1024.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/docs/images/brand/tuieval-icon-128.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/docs/images/brand/tuieval-icon-16.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/docs/images/brand/tuieval-icon-16.svg +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/docs/images/brand/tuieval-icon-256.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/docs/images/brand/tuieval-icon-32.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/docs/images/brand/tuieval-icon-48.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/docs/images/brand/tuieval-icon-512.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/docs/images/brand/tuieval-icon-64.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/docs/images/brand/tuieval-icon-animated.svg +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/docs/images/brand/tuieval-icon.svg +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/docs/images/tui-setup.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/pyproject.toml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/__init__.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/__main__.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/client.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/export.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/graders/__init__.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/graders/answer.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/graders/rag.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/graders/reply.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/graders/tool_call.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/machines.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/profiles.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/remove.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/templates/packs/answer/pack.toml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/templates/packs/answer/system.txt +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/templates/packs/answer/tests.yaml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/templates/packs/code/pack.toml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/templates/packs/code/system.txt +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/templates/packs/code/tests.yaml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/templates/packs/rag/pack.toml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/templates/packs/rag/system.txt +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/templates/packs/rag/tests.yaml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/templates/packs/reply/pack.toml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/templates/packs/reply/system.txt +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/templates/packs/reply/tests.yaml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/templates/packs/tool_call/pack.toml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/templates/packs/tool_call/system.txt +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/templates/packs/tool_call/tests.yaml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/templates/packs/tool_call/tools.yaml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/tune.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/watch_proxy.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/workspace.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev28}/src/tuieval/yamlout.py +0 -0
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- Run several models at a time: `parallel_models` per machine, **Models at a time** in the TUI, or `tuieval run --parallel N` (default 1). Models wait for memory to fit, and answers note what ran alongside them.
|
|
6
|
+
- A new workspace's `models.toml` is 23 lines: the llama, local and OpenRouter servers and the defaults are built in (docs/models.md lists them in full; a server defined in `models.toml` replaces the built-in one with that name). Existing workspaces keep their settings; built-in servers they don't define become available, and adding a model still prefers the workspace's own servers.
|
|
7
|
+
- Less to take in at first: until something has run, the setup screen leaves out context sizes, tuning state and the Tune key; Presets and Hide are off the footer (`?` lists every key); `tuieval help` groups commands into Start here, Day to day and Advanced.
|
|
8
|
+
- Results lead with a plain summary per model ("ready for Support; not decided yet for Coding → run Certify to finish"), in the TUI, `tuieval verdict` (with the command to run next) and the report; the statistics follow.
|
|
9
|
+
- First packs without hand-written YAML: `tuieval new-pack <name> --from questions.csv` (question and answer columns) and `--about "<topic>"` (writes a prompt for a strong model to draft the tests). A test can take its prompt from a file (`input_file`). `tuieval selftest` errors say how to fix them.
|
|
10
|
+
- `tuieval init` in a terminal is a guided setup: it finds models on servers already running (LM Studio, Ollama, llama-server, vLLM), adds the ones you pick, creates a starter pack and runs a Smoke check (`--yes` for no questions).
|
|
11
|
+
- `tuieval add` without a model lists the models on running servers to pick from; `tuieval add <id>` finds the server that has it and adds that server to models.toml. The TUI's add dialog lists them too, with rarely needed options folded away.
|
|
12
|
+
- `?` on any TUI screen explains what it's for, its keys, and the words tuieval uses (tiers, verdicts, critical failures, gates).
|
|
13
|
+
- The setup screen is ready on first open: the only pack and model are ticked, the tier is Smoke until something has run, and ticked boxes are clearly marked (unticked ones are empty).
|
|
14
|
+
- First runs fail fast with the fix instead of hanging: a server that isn't running fails in seconds (it used to wait up to 20 minutes), a missing `llama-server` says how to install it, and `tuieval run` with no models says how to add one.
|
|
15
|
+
- `tuieval doctor` checks servers, models, packs and API keys, and says what to fix.
|
|
5
16
|
- Run only some tests of a pack: `e` in the TUI, or `tuieval run --tests`. Runs add up; `--force` reruns just the picked tests. Presets keep the pick.
|
|
6
17
|
- `tuieval tune` scores options on projected full-length answers (`[tune] answer_tokens`, default 1024), so generation speed counts as in real runs.
|
|
7
18
|
- `tuieval tune` reports an option's memory cost instead of rejecting it; it rejects only on critical memory pressure or over `[tune] swap_limit_mb`.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: tuieval
|
|
3
|
-
Version: 0.2.0.
|
|
3
|
+
Version: 0.2.0.dev28
|
|
4
4
|
Summary: Evaluate local and frontier LLMs on your own questions: accuracy, speed, tokens and PASS/FAIL verdicts, in the terminal.
|
|
5
5
|
Project-URL: Homepage, https://github.com/ashe-wb/tuieval
|
|
6
6
|
Project-URL: Issues, https://github.com/ashe-wb/tuieval/issues
|
|
@@ -43,12 +43,21 @@ Then you get an answer to the questions that matter:
|
|
|
43
43
|
|
|
44
44
|
```bash
|
|
45
45
|
pipx install tuieval # or: uv tool install tuieval, or pip install tuieval (Python 3.11+)
|
|
46
|
-
tuieval init my-evals
|
|
47
|
-
|
|
48
|
-
tuieval
|
|
49
|
-
tuieval # open the TUI
|
|
46
|
+
tuieval init my-evals # guided: finds models already running (LM Studio, Ollama, vLLM…),
|
|
47
|
+
# adds a starter pack and runs a 3-question check
|
|
48
|
+
cd my-evals && tuieval # open the TUI (? explains any screen)
|
|
50
49
|
```
|
|
51
50
|
|
|
51
|
+
Whatever runs your models, the first steps are short:
|
|
52
|
+
|
|
53
|
+
| You have | Do this |
|
|
54
|
+
|---|---|
|
|
55
|
+
| **LM Studio, Ollama or vLLM** | Start its server and load a model, then `tuieval init my-evals`: it finds the model. Later: `tuieval add` lists new ones. |
|
|
56
|
+
| **GGUF files** | Install llama.cpp (`brew install llama.cpp`), then `tuieval add ~/models/Some-Model-Q4_K_M.gguf`. tuieval starts `llama-server` for it, sized to your machine. |
|
|
57
|
+
| **An OpenRouter key** | `export OPENROUTER_API_KEY=…`, then `tuieval run --tier smoke --only openrouter:<model id>`. |
|
|
58
|
+
|
|
59
|
+
Stuck? `tuieval doctor` checks servers, models, packs and keys, and says what to fix.
|
|
60
|
+
|
|
52
61
|
`pip install tuieval` gets the latest release. Every change on `main` is also published as a dev build (`X.Y.0.devN`); get it with `pipx install --pip-args=--pre tuieval` or `pip install --pre tuieval`.
|
|
53
62
|
|
|
54
63
|
Once you've added your own packs and models, the setup screen looks like this (packs on the left, models with a verdict code per use case on the right, the highlighted model's details below):
|
|
@@ -117,7 +126,8 @@ tuieval uses the current folder, or `--workspace DIR` / `TUIEVAL_HOME`. Keep it
|
|
|
117
126
|
```bash
|
|
118
127
|
tuieval add ~/models/Some-Model-Q4_K_M.gguf --tags 9b,dense,q4 # llama.cpp (llama-server on PATH)
|
|
119
128
|
tuieval add ~/models/VL-Q4.gguf --mmproj ~/models/VL-mmproj.gguf # vision
|
|
120
|
-
tuieval add
|
|
129
|
+
tuieval add # pick from the models on running servers
|
|
130
|
+
tuieval add qwen3:8b # finds the running server that has it
|
|
121
131
|
tuieval run --tier smoke --only openrouter:qwen/qwen3-32b # any OpenRouter model, no config needed
|
|
122
132
|
```
|
|
123
133
|
|
|
@@ -130,7 +140,7 @@ See **[docs/models.md](docs/models.md)** for servers, OpenRouter endpoint pinnin
|
|
|
130
140
|
3. **Watch the run:** progress with ETA, live score, tok/s, TTFT and memory, the current test with reasoning and answer side by side, recent results with the grader's reason. `k` skips a model, `c` cancels (finished work is kept and resumes next time). Runs started while one is going are queued.
|
|
131
141
|
4. **Press `r` for results:** production readiness, verdict history, scorecard, speed & tokens (★ marks models nothing beats on both accuracy and time), per question, is the difference real?, tests that separate models, by difficulty, failures, and test quality.
|
|
132
142
|
|
|
133
|
-
Other keys: `t` tunes the ticked models' speed flags, `a` adds a model, `m` scans for GGUFs, `p` saves or loads a preset, `x` hides a model.
|
|
143
|
+
Other keys: `t` tunes the ticked models' speed flags, `a` adds a model, `m` scans for GGUFs, `p` saves or loads a preset, `x` hides a model. `?` explains any screen.
|
|
134
144
|
|
|
135
145
|
## Command line
|
|
136
146
|
|
|
@@ -139,6 +149,7 @@ tuieval run # Screen every model on every pack
|
|
|
139
149
|
tuieval run --tier certify --only a,b --packs support-bot,coding
|
|
140
150
|
tuieval run --packs coding --tests parse-dates,fix-bug # only these tests (or --tests coding:parse-dates)
|
|
141
151
|
tuieval run --dry-run # the plan and server commands
|
|
152
|
+
tuieval doctor # check servers, models, packs and keys, and what to fix
|
|
142
153
|
tuieval verdict # PASS / FAIL / INCONCLUSIVE per model and use case
|
|
143
154
|
tuieval report # the same with evidence, as a markdown file
|
|
144
155
|
tuieval history # every verdict change over time
|
|
@@ -17,12 +17,21 @@ Then you get an answer to the questions that matter:
|
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
19
|
pipx install tuieval # or: uv tool install tuieval, or pip install tuieval (Python 3.11+)
|
|
20
|
-
tuieval init my-evals
|
|
21
|
-
|
|
22
|
-
tuieval
|
|
23
|
-
tuieval # open the TUI
|
|
20
|
+
tuieval init my-evals # guided: finds models already running (LM Studio, Ollama, vLLM…),
|
|
21
|
+
# adds a starter pack and runs a 3-question check
|
|
22
|
+
cd my-evals && tuieval # open the TUI (? explains any screen)
|
|
24
23
|
```
|
|
25
24
|
|
|
25
|
+
Whatever runs your models, the first steps are short:
|
|
26
|
+
|
|
27
|
+
| You have | Do this |
|
|
28
|
+
|---|---|
|
|
29
|
+
| **LM Studio, Ollama or vLLM** | Start its server and load a model, then `tuieval init my-evals`: it finds the model. Later: `tuieval add` lists new ones. |
|
|
30
|
+
| **GGUF files** | Install llama.cpp (`brew install llama.cpp`), then `tuieval add ~/models/Some-Model-Q4_K_M.gguf`. tuieval starts `llama-server` for it, sized to your machine. |
|
|
31
|
+
| **An OpenRouter key** | `export OPENROUTER_API_KEY=…`, then `tuieval run --tier smoke --only openrouter:<model id>`. |
|
|
32
|
+
|
|
33
|
+
Stuck? `tuieval doctor` checks servers, models, packs and keys, and says what to fix.
|
|
34
|
+
|
|
26
35
|
`pip install tuieval` gets the latest release. Every change on `main` is also published as a dev build (`X.Y.0.devN`); get it with `pipx install --pip-args=--pre tuieval` or `pip install --pre tuieval`.
|
|
27
36
|
|
|
28
37
|
Once you've added your own packs and models, the setup screen looks like this (packs on the left, models with a verdict code per use case on the right, the highlighted model's details below):
|
|
@@ -91,7 +100,8 @@ tuieval uses the current folder, or `--workspace DIR` / `TUIEVAL_HOME`. Keep it
|
|
|
91
100
|
```bash
|
|
92
101
|
tuieval add ~/models/Some-Model-Q4_K_M.gguf --tags 9b,dense,q4 # llama.cpp (llama-server on PATH)
|
|
93
102
|
tuieval add ~/models/VL-Q4.gguf --mmproj ~/models/VL-mmproj.gguf # vision
|
|
94
|
-
tuieval add
|
|
103
|
+
tuieval add # pick from the models on running servers
|
|
104
|
+
tuieval add qwen3:8b # finds the running server that has it
|
|
95
105
|
tuieval run --tier smoke --only openrouter:qwen/qwen3-32b # any OpenRouter model, no config needed
|
|
96
106
|
```
|
|
97
107
|
|
|
@@ -104,7 +114,7 @@ See **[docs/models.md](docs/models.md)** for servers, OpenRouter endpoint pinnin
|
|
|
104
114
|
3. **Watch the run:** progress with ETA, live score, tok/s, TTFT and memory, the current test with reasoning and answer side by side, recent results with the grader's reason. `k` skips a model, `c` cancels (finished work is kept and resumes next time). Runs started while one is going are queued.
|
|
105
115
|
4. **Press `r` for results:** production readiness, verdict history, scorecard, speed & tokens (★ marks models nothing beats on both accuracy and time), per question, is the difference real?, tests that separate models, by difficulty, failures, and test quality.
|
|
106
116
|
|
|
107
|
-
Other keys: `t` tunes the ticked models' speed flags, `a` adds a model, `m` scans for GGUFs, `p` saves or loads a preset, `x` hides a model.
|
|
117
|
+
Other keys: `t` tunes the ticked models' speed flags, `a` adds a model, `m` scans for GGUFs, `p` saves or loads a preset, `x` hides a model. `?` explains any screen.
|
|
108
118
|
|
|
109
119
|
## Command line
|
|
110
120
|
|
|
@@ -113,6 +123,7 @@ tuieval run # Screen every model on every pack
|
|
|
113
123
|
tuieval run --tier certify --only a,b --packs support-bot,coding
|
|
114
124
|
tuieval run --packs coding --tests parse-dates,fix-bug # only these tests (or --tests coding:parse-dates)
|
|
115
125
|
tuieval run --dry-run # the plan and server commands
|
|
126
|
+
tuieval doctor # check servers, models, packs and keys, and what to fix
|
|
116
127
|
tuieval verdict # PASS / FAIL / INCONCLUSIVE per model and use case
|
|
117
128
|
tuieval report # the same with evidence, as a markdown file
|
|
118
129
|
tuieval history # every verdict change over time
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Models, servers and machines
|
|
2
2
|
|
|
3
|
-
Everything about models lives in your workspace's `models.toml`. `tuieval init` writes one
|
|
3
|
+
Everything about models lives in your workspace's `models.toml`. `tuieval init` writes a short one; three servers are built in and ready to use (their full definitions are at the end of this page, under [Built-in servers and defaults](#built-in-servers-and-defaults)):
|
|
4
4
|
|
|
5
5
|
| server | what it is |
|
|
6
6
|
|---|---|
|
|
@@ -14,7 +14,8 @@ Everything about models lives in your workspace's `models.toml`. `tuieval init`
|
|
|
14
14
|
tuieval add ~/models/Some-Model-Q4_K_M.gguf --tags 9b,dense,q4 # GGUF -> llama
|
|
15
15
|
tuieval add ~/models/Some-Model-Q4_K_M.gguf --no-think # same model, thinking off (label …-nothink)
|
|
16
16
|
tuieval add ~/models/VL-Q4.gguf --mmproj ~/models/VL-mmproj.gguf # vision model
|
|
17
|
-
tuieval add
|
|
17
|
+
tuieval add # pick from the models on servers already running
|
|
18
|
+
tuieval add qwen3:8b # finds the running server that has it
|
|
18
19
|
tuieval scan --add # every new GGUF under model_dirs
|
|
19
20
|
tuieval list # models; hidden ones listed separately
|
|
20
21
|
tuieval remove --hidden # clean up every hidden model
|
|
@@ -70,7 +71,18 @@ The best server flags differ per model and per machine, so tuieval splits them b
|
|
|
70
71
|
|
|
71
72
|
- **Machine id** is detected automatically (e.g. `m3max-64gb`, `m2-16gb`; set `EVALS_MACHINE` to rename it). Every result records the machine, the server version and the exact speed flags used. `tuieval machines` lists this machine and every other machine that has run the evals (they record themselves in `tuning/`).
|
|
72
73
|
- **Fit check (Apple Silicon, llama):** before starting a GGUF, tuieval reads its header (layers, KV heads, hybrid attention layers) and picks the largest context that fits this machine's GPU memory, capped at `max_ctx`. A model that can't fit at 8k context is skipped with *doesn't fit on <machine>* instead of swapping. Packs that need more context than fits are skipped. It never quantizes the KV cache on its own, since that changes answers. Servers that size their own memory (their `cmd` doesn't take `{ctx}`) are left alone: their context is the model's `max_context`; `fit_check` on the server changes that.
|
|
73
|
-
- **Per-machine settings** go under `[machines.<id>]`: `memory_headroom_gb` (GPU memory kept free for macOS, default 4)
|
|
74
|
+
- **Per-machine settings** go under `[machines.<id>]`: `memory_headroom_gb` (GPU memory kept free for macOS, default 4), `gpu_residency_gb` (see the stall guard) and `parallel_models` (see below).
|
|
75
|
+
|
|
76
|
+
## Several models at a time
|
|
77
|
+
|
|
78
|
+
By default a run serves one model at a time. On a machine with room for more, set `parallel_models = 2` (or more) under `[machines.<id>]`, type a number in **Models at a time** on the TUI's setup screen (blank uses the machine's setting), or pass `tuieval run --parallel N`.
|
|
79
|
+
|
|
80
|
+
- Models start in queue order, each in its own lane. The next one waits while its fit-check memory estimate wouldn't fit next to the ones running (without one, e.g. a server that sizes its own memory, the setting decides). Contexts are never shrunk to make room.
|
|
81
|
+
- A second model on the same server gets a free port, so a server's `cmd` must take `{port}`.
|
|
82
|
+
- A server with `before_start` (which may stop other servers) always runs on its own. Tuning is always one model at a time.
|
|
83
|
+
- Answers are judged exactly as in a run of one model. Each answer records the models served while it ran (`ran_alongside`), and a load time measured next to others is marked (`loaded_alongside`), since models side by side share the machine's GPU and memory bandwidth. The speed table and the answer view show it.
|
|
84
|
+
- In the TUI, one model's answers stream at a time: `v` switches to the next, `k` skips the one streaming. `tuieval run` prints one line per answer instead of streaming.
|
|
85
|
+
- Only one run uses the machine at a time across tuieval windows; models at a time applies within a run.
|
|
74
86
|
|
|
75
87
|
## Tuning
|
|
76
88
|
|
|
@@ -142,3 +154,118 @@ On Apple Silicon Macs, once the system's GPU allocations pass about half of RAM,
|
|
|
142
154
|
| `api_key_env` | environment variable holding the API key |
|
|
143
155
|
| `thinking_param` | `reasoning` to send `[sampling] enable_thinking` as OpenRouter's `reasoning.enabled` |
|
|
144
156
|
| `headers` | extra HTTP headers |
|
|
157
|
+
|
|
158
|
+
## Built-in servers and defaults
|
|
159
|
+
|
|
160
|
+
A workspace's `models.toml` only needs what differs from these. `[defaults]` fill in key by key. `[sampling]` applies only when `models.toml` has no `[sampling]` section at all, because sampling is part of every result's fingerprint. A server you define replaces the built-in one with the same name; to change one setting, copy the whole block into `models.toml` and edit it. `tuieval add` adds a `[servers.<name>]` block with just a `url` when it finds a model on a server that's already running.
|
|
161
|
+
|
|
162
|
+
```toml
|
|
163
|
+
[defaults] fill in key by key; [sampling] only when models.toml has no [sampling] at all (it's part of
|
|
164
|
+
# every result's fingerprint); a server here is replaced whole by one of the same name in models.toml.
|
|
165
|
+
|
|
166
|
+
[defaults]
|
|
167
|
+
repeat = 3
|
|
168
|
+
request_timeout_ms = 1800000 # per request; long thinking runs need it
|
|
169
|
+
ready_timeout_s = 1200 # how long a model may take to load before giving up
|
|
170
|
+
model_dirs = ["~/models"] # where "Scan for models" looks for new GGUFs
|
|
171
|
+
|
|
172
|
+
# Sent with every request. A model may override it (see `thinking` / `sampling` below); results
|
|
173
|
+
# record the settings each model actually ran with, and the scorecard shows any difference.
|
|
174
|
+
[sampling]
|
|
175
|
+
temperature = 0.7
|
|
176
|
+
top_p = 0.95
|
|
177
|
+
top_k = 20
|
|
178
|
+
min_p = 0.0
|
|
179
|
+
max_tokens = 16384
|
|
180
|
+
enable_thinking = true # test every model in the SAME mode
|
|
181
|
+
|
|
182
|
+
# llama.cpp's server (https://github.com/ggml-org/llama.cpp), started for each GGUF model.
|
|
183
|
+
[servers.llama]
|
|
184
|
+
# Output-affecting flags only. Anything here must be identical wherever results are compared,
|
|
185
|
+
# so it's part of each result's fingerprint. Speed-only flags live in `perf` and `tune` below.
|
|
186
|
+
cmd = [
|
|
187
|
+
"llama-server",
|
|
188
|
+
"-m", "{model}",
|
|
189
|
+
"--alias", "{served_name}",
|
|
190
|
+
"--host", "127.0.0.1", "--port", "{port}",
|
|
191
|
+
"--jinja",
|
|
192
|
+
"--metrics",
|
|
193
|
+
"-np", "1",
|
|
194
|
+
"-ctk", "{kv_type}", "-ctv", "{kv_type}",
|
|
195
|
+
"-c", "{ctx}",
|
|
196
|
+
"--reasoning-format", "auto",
|
|
197
|
+
]
|
|
198
|
+
port = 8080
|
|
199
|
+
model_is_path = true # check the GGUF exists and fits before starting
|
|
200
|
+
vision_args = ["--mmproj", "{mmproj}"] # appended only for models that set mmproj
|
|
201
|
+
kv_type = "f16" # a model's own `kv_type` (e.g. "q8_0") makes it a different model entry
|
|
202
|
+
max_ctx = 65536 # upper bound; the fit check lowers it per machine (see machines.py)
|
|
203
|
+
version_cmd = ["llama-server", "--version"] # recorded with every result
|
|
204
|
+
# Added to every request. cache_prompt = false: each answer is computed from scratch, so a repeat of
|
|
205
|
+
# the same question reuses nothing from the last time it was asked (and its TTFT is honest).
|
|
206
|
+
# Answers record any prompt tokens the server reused.
|
|
207
|
+
request = { cache_prompt = false }
|
|
208
|
+
|
|
209
|
+
# Speed-only flags, always applied (not tuned).
|
|
210
|
+
perf = ["-ngl", "99"]
|
|
211
|
+
|
|
212
|
+
# Speed-only knobs `tuieval tune` tries, one at a time from the best so far. The first option of
|
|
213
|
+
# each knob is the default for models not tuned on this machine yet. Placeholders: {p} P-cores,
|
|
214
|
+
# {p_minus_2}, {all} all cores. Options that stop the server from starting are skipped.
|
|
215
|
+
# Check the flags against `llama-server --help` for your build.
|
|
216
|
+
[servers.llama.tune]
|
|
217
|
+
threads = [["-t", "{p}", "-tb", "{p}"], ["-t", "{p_minus_2}", "-tb", "{p}"], ["-t", "{all}", "-tb", "{all}"]]
|
|
218
|
+
ubatch = [["-ub", "512"], ["-ub", "256"], ["-ub", "1024"]]
|
|
219
|
+
batch = [["-b", "2048"], ["-b", "4096"]]
|
|
220
|
+
flash_attn = [["-fa", "on"], ["-fa", "off"]]
|
|
221
|
+
|
|
222
|
+
# A server that's already running (LM Studio, Ollama, vLLM, a remote box): give its URL and no cmd.
|
|
223
|
+
# Its models are added with `tuieval add <model id> --server local`.
|
|
224
|
+
[servers.local]
|
|
225
|
+
url = "http://127.0.0.1:1234" # LM Studio's default; Ollama is http://127.0.0.1:11434, vLLM :8000
|
|
226
|
+
|
|
227
|
+
# OpenRouter: ANY model it serves, with no [[models]] entry. Name it when you run:
|
|
228
|
+
# tuieval run --tier smoke --only openrouter:qwen/qwen3-235b-a22b-2507
|
|
229
|
+
# TUI: tick "+ openrouter: any model" in the model list, or press "a" and type openrouter:<model id>
|
|
230
|
+
# Results go to results/or-<id>/ and the model stays listed once it has results. A plain
|
|
231
|
+
# `tuieval run` never includes OpenRouter models (they cost money); name them.
|
|
232
|
+
# Needs OPENROUTER_API_KEY in the environment of the shell that starts tuieval.
|
|
233
|
+
# pin_endpoint: every answer of a model comes from one provider endpoint (the closest to the released
|
|
234
|
+
# weights), with no fallback, since providers run different quantizations.
|
|
235
|
+
# To keep providers that store or train on prompts (and could learn your tests) out entirely, add
|
|
236
|
+
# request = { provider = { data_collection = "deny" } }
|
|
237
|
+
[servers.openrouter]
|
|
238
|
+
url = "https://openrouter.ai/api/v1"
|
|
239
|
+
any_model = true
|
|
240
|
+
label_prefix = "or"
|
|
241
|
+
pin_endpoint = true
|
|
242
|
+
api_key_env = "OPENROUTER_API_KEY"
|
|
243
|
+
thinking_param = "reasoning" # [sampling] enable_thinking -> OpenRouter's reasoning.enabled
|
|
244
|
+
headers = { "X-Title" = "tuieval" }
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
`[defaults] detect_ports` (default `[1234, 11434, 8080, 8000]`, the usual LM Studio, Ollama, llama-server and vLLM ports) is where `tuieval add`, `tuieval init` and `tuieval doctor` look for servers that are already running. `TUIEVAL_DETECT_PORTS` (comma-separated; empty means none) overrides it.
|
|
248
|
+
|
|
249
|
+
Optional sections:
|
|
250
|
+
|
|
251
|
+
```toml
|
|
252
|
+
# Optional tuner settings.
|
|
253
|
+
# [tune]
|
|
254
|
+
# guard_similarity = 0.6 # greedy answers under a candidate flag must stay this similar to the defaults'
|
|
255
|
+
# request_timeout_s = 600 # a tuning request that takes longer fails that candidate
|
|
256
|
+
# warm_start = true # start from a tuned model with the same architecture and shapes (a fine-tune)
|
|
257
|
+
# warm_retest = ["spec", "ubatch"] # the knobs a warm start re-tries; the rest are inherited
|
|
258
|
+
# answer_tokens = 1024 # answer length options are scored for (projected objective)
|
|
259
|
+
# swap_limit_mb = 500 # reject options that swap more than this beyond the defaults (default: report only)
|
|
260
|
+
|
|
261
|
+
# Optional: llama-bench for the tuner's fast first stage (found on PATH as llama-bench otherwise).
|
|
262
|
+
# [bench]
|
|
263
|
+
# cmd = ["~/llama.cpp/build/bin/llama-bench"]
|
|
264
|
+
|
|
265
|
+
# Optional per-machine settings, by machine id (tuieval machines shows the ids).
|
|
266
|
+
# [machines.m3max-64gb]
|
|
267
|
+
# memory_headroom_gb = 3 # GPU memory kept free for macOS and other apps (default 4)
|
|
268
|
+
# gpu_residency_gb = 12 # GPU memory the driver keeps resident before churning (default: half of RAM)
|
|
269
|
+
# parallel_models = 2 # models a run serves at a time (default 1)
|
|
270
|
+
```
|
|
271
|
+
|
|
@@ -10,6 +10,11 @@ tuieval new-pack support-bot --grader reply # answer | rag | reply | tool_c
|
|
|
10
10
|
|
|
11
11
|
Edit `packs/support-bot/tests.yaml`, replace the examples with your own questions, and run `tuieval selftest support-bot` after every change.
|
|
12
12
|
|
|
13
|
+
Two shortcuts to a first set of real questions:
|
|
14
|
+
|
|
15
|
+
- **From a spreadsheet:** `tuieval new-pack quiz --from questions.csv` makes one test per row from a `question` and an `answer` column (optional: `id`, `category`, `difficulty`, `tolerance`, and `wrong` with known-wrong answers separated by `|`). Numbers are checked with the tolerance, words case-insensitively, and `NOT_AVAILABLE` means the right answer is "the question doesn't say".
|
|
16
|
+
- **Drafted by a model:** `tuieval new-pack support-bot --grader reply --about "refund questions for a shoe store"` also writes `DRAFT-PROMPT.md`. Paste it into the strongest model you have, save the YAML it writes as `tests.yaml`, and run `tuieval selftest`: it catches drafted tests whose expected answer is wrong or whose grader can't tell a mistake from the right answer. Read the questions anyway; you know your domain better than the model.
|
|
17
|
+
|
|
13
18
|
To try a different set of questions, copy a pack (`cp -r packs/support-bot packs/support-bot-v2`), edit the copy, and pick whichever you want in the TUI.
|
|
14
19
|
|
|
15
20
|
## Fingerprints
|
|
@@ -55,6 +60,7 @@ min_consistency = 0.95 # share of variant groups where every variant p
|
|
|
55
60
|
input: | # the user message
|
|
56
61
|
A customer bought shoes 20 days ago …
|
|
57
62
|
image: images/receipt.png # optional: sent as an image (needs vision)
|
|
63
|
+
input_file: prompts/refund.txt # optional, instead of input: the user message from a file in the pack
|
|
58
64
|
grader: answer # optional: override the pack's grader
|
|
59
65
|
group: refund-window-2 # optional: variants of one case share a group (consistency gate)
|
|
60
66
|
critical: true # optional: any failure of this test is critical (disqualifying)
|
|
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
|
|
|
18
18
|
commit_id: str | None
|
|
19
19
|
__commit_id__: str | None
|
|
20
20
|
|
|
21
|
-
__version__ = version = '0.2.0.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 2, 0, '
|
|
21
|
+
__version__ = version = '0.2.0.dev28'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 0, 'dev28')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# Built-in defaults and servers, used when a workspace's models.toml doesn't define them.
|
|
2
|
+
# [defaults] fill in key by key; [sampling] only when models.toml has no [sampling] at all (it's part of
|
|
3
|
+
# every result's fingerprint); a server here is replaced whole by one of the same name in models.toml.
|
|
4
|
+
|
|
5
|
+
[defaults]
|
|
6
|
+
repeat = 3
|
|
7
|
+
request_timeout_ms = 1800000 # per request; long thinking runs need it
|
|
8
|
+
ready_timeout_s = 1200 # how long a model may take to load before giving up
|
|
9
|
+
model_dirs = ["~/models"] # where "Scan for models" looks for new GGUFs
|
|
10
|
+
|
|
11
|
+
# Sent with every request. A model may override it (see `thinking` / `sampling` below); results
|
|
12
|
+
# record the settings each model actually ran with, and the scorecard shows any difference.
|
|
13
|
+
[sampling]
|
|
14
|
+
temperature = 0.7
|
|
15
|
+
top_p = 0.95
|
|
16
|
+
top_k = 20
|
|
17
|
+
min_p = 0.0
|
|
18
|
+
max_tokens = 16384
|
|
19
|
+
enable_thinking = true # test every model in the SAME mode
|
|
20
|
+
|
|
21
|
+
# llama.cpp's server (https://github.com/ggml-org/llama.cpp), started for each GGUF model.
|
|
22
|
+
[servers.llama]
|
|
23
|
+
# Output-affecting flags only. Anything here must be identical wherever results are compared,
|
|
24
|
+
# so it's part of each result's fingerprint. Speed-only flags live in `perf` and `tune` below.
|
|
25
|
+
cmd = [
|
|
26
|
+
"llama-server",
|
|
27
|
+
"-m", "{model}",
|
|
28
|
+
"--alias", "{served_name}",
|
|
29
|
+
"--host", "127.0.0.1", "--port", "{port}",
|
|
30
|
+
"--jinja",
|
|
31
|
+
"--metrics",
|
|
32
|
+
"-np", "1",
|
|
33
|
+
"-ctk", "{kv_type}", "-ctv", "{kv_type}",
|
|
34
|
+
"-c", "{ctx}",
|
|
35
|
+
"--reasoning-format", "auto",
|
|
36
|
+
]
|
|
37
|
+
port = 8080
|
|
38
|
+
model_is_path = true # check the GGUF exists and fits before starting
|
|
39
|
+
vision_args = ["--mmproj", "{mmproj}"] # appended only for models that set mmproj
|
|
40
|
+
kv_type = "f16" # a model's own `kv_type` (e.g. "q8_0") makes it a different model entry
|
|
41
|
+
max_ctx = 65536 # upper bound; the fit check lowers it per machine (see machines.py)
|
|
42
|
+
version_cmd = ["llama-server", "--version"] # recorded with every result
|
|
43
|
+
# Added to every request. cache_prompt = false: each answer is computed from scratch, so a repeat of
|
|
44
|
+
# the same question reuses nothing from the last time it was asked (and its TTFT is honest).
|
|
45
|
+
# Answers record any prompt tokens the server reused.
|
|
46
|
+
request = { cache_prompt = false }
|
|
47
|
+
|
|
48
|
+
# Speed-only flags, always applied (not tuned).
|
|
49
|
+
perf = ["-ngl", "99"]
|
|
50
|
+
|
|
51
|
+
# Speed-only knobs `tuieval tune` tries, one at a time from the best so far. The first option of
|
|
52
|
+
# each knob is the default for models not tuned on this machine yet. Placeholders: {p} P-cores,
|
|
53
|
+
# {p_minus_2}, {all} all cores. Options that stop the server from starting are skipped.
|
|
54
|
+
# Check the flags against `llama-server --help` for your build.
|
|
55
|
+
[servers.llama.tune]
|
|
56
|
+
threads = [["-t", "{p}", "-tb", "{p}"], ["-t", "{p_minus_2}", "-tb", "{p}"], ["-t", "{all}", "-tb", "{all}"]]
|
|
57
|
+
ubatch = [["-ub", "512"], ["-ub", "256"], ["-ub", "1024"]]
|
|
58
|
+
batch = [["-b", "2048"], ["-b", "4096"]]
|
|
59
|
+
flash_attn = [["-fa", "on"], ["-fa", "off"]]
|
|
60
|
+
|
|
61
|
+
# A server that's already running (LM Studio, Ollama, vLLM, a remote box): give its URL and no cmd.
|
|
62
|
+
# Its models are added with `tuieval add <model id> --server local`.
|
|
63
|
+
[servers.local]
|
|
64
|
+
url = "http://127.0.0.1:1234" # LM Studio's default; Ollama is http://127.0.0.1:11434, vLLM :8000
|
|
65
|
+
|
|
66
|
+
# OpenRouter: ANY model it serves, with no [[models]] entry. Name it when you run:
|
|
67
|
+
# tuieval run --tier smoke --only openrouter:qwen/qwen3-235b-a22b-2507
|
|
68
|
+
# TUI: tick "+ openrouter: any model" in the model list, or press "a" and type openrouter:<model id>
|
|
69
|
+
# Results go to results/or-<id>/ and the model stays listed once it has results. A plain
|
|
70
|
+
# `tuieval run` never includes OpenRouter models (they cost money); name them.
|
|
71
|
+
# Needs OPENROUTER_API_KEY in the environment of the shell that starts tuieval.
|
|
72
|
+
# pin_endpoint: every answer of a model comes from one provider endpoint (the closest to the released
|
|
73
|
+
# weights), with no fallback, since providers run different quantizations.
|
|
74
|
+
# To keep providers that store or train on prompts (and could learn your tests) out entirely, add
|
|
75
|
+
# request = { provider = { data_collection = "deny" } }
|
|
76
|
+
[servers.openrouter]
|
|
77
|
+
url = "https://openrouter.ai/api/v1"
|
|
78
|
+
any_model = true
|
|
79
|
+
label_prefix = "or"
|
|
80
|
+
pin_endpoint = true
|
|
81
|
+
api_key_env = "OPENROUTER_API_KEY"
|
|
82
|
+
thinking_param = "reasoning" # [sampling] enable_thinking -> OpenRouter's reasoning.enabled
|
|
83
|
+
headers = { "X-Title" = "tuieval" }
|
|
@@ -2,28 +2,33 @@
|
|
|
2
2
|
|
|
3
3
|
Usage: tuieval [--workspace DIR] [command] [options]
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
tuieval init [DIR] create a workspace
|
|
7
|
-
tuieval
|
|
8
|
-
tuieval
|
|
9
|
-
tuieval
|
|
10
|
-
tuieval
|
|
11
|
-
tuieval
|
|
5
|
+
Start here
|
|
6
|
+
tuieval init [DIR] create a workspace; in a terminal, a guided setup to a first result
|
|
7
|
+
tuieval open the TUI (? explains any screen)
|
|
8
|
+
tuieval doctor check servers, models, packs and API keys, and say what to fix
|
|
9
|
+
tuieval add [PATH|ID] add a model: a GGUF, a model id, or none to pick from running servers
|
|
10
|
+
tuieval new-pack NAME a new pack of example questions (--from CSV, --about TOPIC, --grader …)
|
|
11
|
+
tuieval selftest check every test's reference and wrong answers against its grader
|
|
12
12
|
tuieval run [...] run evals from the command line (tuieval run --help)
|
|
13
|
+
tuieval verdict which model is ready for what, and what to run next
|
|
14
|
+
|
|
15
|
+
Day to day
|
|
13
16
|
tuieval compare [...] scorecards (--speed, --pairwise, --failures)
|
|
14
|
-
tuieval
|
|
15
|
-
tuieval verdict production readiness (PASS/FAIL per model and use case)
|
|
16
|
-
tuieval report the readiness verdicts with their evidence, as a markdown file
|
|
17
|
+
tuieval report the verdicts with their evidence, as a markdown file
|
|
17
18
|
tuieval history every verdict change, newest first
|
|
18
|
-
tuieval selftest check every test's reference and wrong answers against its grader
|
|
19
19
|
tuieval items tests that don't separate models or look broken
|
|
20
20
|
tuieval capture LOG --pack turn a real failure (logs/live/*.txt) into a new test
|
|
21
|
+
tuieval regrade re-score stored answers after changing a grader
|
|
22
|
+
tuieval list the models tuieval knows, hidden ones separately (--packs: the packs)
|
|
23
|
+
tuieval remove MODEL remove models, or every hidden one (--hidden); archived in removed/
|
|
24
|
+
|
|
25
|
+
Advanced
|
|
26
|
+
tuieval scan list GGUFs in model_dirs that aren't in models.toml yet
|
|
21
27
|
tuieval machines this machine, other recorded machines, and fit and tuning per model
|
|
22
28
|
tuieval tune MODEL find the fastest speed-only server flags for a model on this machine
|
|
23
29
|
tuieval export pi MODEL serve a model in the pi coding agent with its tuned flags
|
|
24
30
|
tuieval watch a proxy that shows the reasoning of any app using your server
|
|
25
|
-
tuieval help
|
|
26
|
-
tuieval --version
|
|
31
|
+
tuieval help · --version
|
|
27
32
|
|
|
28
33
|
The workspace is --workspace DIR, else $TUIEVAL_HOME, else the current folder.
|
|
29
34
|
"""
|
|
@@ -67,6 +72,9 @@ def main(argv=None):
|
|
|
67
72
|
if not workspace.is_workspace():
|
|
68
73
|
sys.exit(f"{workspace.root()} isn't a tuieval workspace; run `tuieval init` first")
|
|
69
74
|
return scaffold.cmd_new_pack(rest)
|
|
75
|
+
if cmd == "doctor":
|
|
76
|
+
from . import doctor
|
|
77
|
+
return doctor.cmd_doctor(rest)
|
|
70
78
|
if cmd == "run":
|
|
71
79
|
from . import run_evals
|
|
72
80
|
return run_evals.main(rest)
|
|
@@ -37,7 +37,8 @@ def _load_native(data):
|
|
|
37
37
|
"settings": data.get("settings", {}), "load_s": run.get("load_s"),
|
|
38
38
|
"peak_rss_mb": run.get("peak_rss_mb"), "pack": pack["name"], "pack_fp": pack["fingerprint"],
|
|
39
39
|
# hosted (OpenRouter) models: the provider endpoint and its declared quantization
|
|
40
|
-
"endpoint": run.get("endpoint"), "quantization": run.get("quantization")
|
|
40
|
+
"endpoint": run.get("endpoint"), "quantization": run.get("quantization"),
|
|
41
|
+
"loaded_alongside": run.get("loaded_alongside") or []}
|
|
41
42
|
rows = []
|
|
42
43
|
for r in data["results"]:
|
|
43
44
|
rows.append({
|
|
@@ -54,6 +55,8 @@ def _load_native(data):
|
|
|
54
55
|
"severity": r.get("severity"), "group": r.get("group"), "repeat": r.get("repeat", 0),
|
|
55
56
|
"difficulty": r.get("difficulty", "unrated"),
|
|
56
57
|
"machine": r.get("machine") or run.get("machine"), "prompt_tokens": r.get("prompt_tokens"),
|
|
58
|
+
# other models served at the same time (parallel_models): its speed was shared with them
|
|
59
|
+
"alongside": r.get("ran_alongside") or [],
|
|
57
60
|
})
|
|
58
61
|
return rows, info
|
|
59
62
|
|
|
@@ -210,6 +213,13 @@ def timing(r, machine=None, speeds=None):
|
|
|
210
213
|
return None, None, None, None, None
|
|
211
214
|
|
|
212
215
|
|
|
216
|
+
def alongside_note(rows):
|
|
217
|
+
"""' · n/N ran alongside others' when some answers ran while other models were served (their
|
|
218
|
+
speed was shared with them), else ''."""
|
|
219
|
+
shared = sum(bool(r.get("alongside")) for r in rows)
|
|
220
|
+
return f" · {shared}/{len(rows)} ran alongside others" if shared else ""
|
|
221
|
+
|
|
222
|
+
|
|
213
223
|
def speed(rows, infos, machine=None, speeds=None):
|
|
214
224
|
"""(header, table, frontier): speed and token use per model.
|
|
215
225
|
|
|
@@ -227,7 +237,9 @@ def speed(rows, infos, machine=None, speeds=None):
|
|
|
227
237
|
load = {}
|
|
228
238
|
for i in infos:
|
|
229
239
|
cur = load.setdefault(i["label"], {"load_s": None, "peak_rss_mb": None, "tags": i["tags"],
|
|
230
|
-
"think": i["settings"].get("enable_thinking"), "served": set()
|
|
240
|
+
"think": i["settings"].get("enable_thinking"), "served": set(),
|
|
241
|
+
"loaded_alongside": False})
|
|
242
|
+
cur["loaded_alongside"] |= bool(i.get("loaded_alongside"))
|
|
231
243
|
if i.get("endpoint"):
|
|
232
244
|
q = i.get("quantization")
|
|
233
245
|
cur["served"].add(f"{'undeclared' if q in (None, 'unknown') else q} via {i['endpoint']}")
|
|
@@ -247,6 +259,7 @@ def speed(rows, infos, machine=None, speeds=None):
|
|
|
247
259
|
here = machine is None or machine in where # load time and memory only where it ran
|
|
248
260
|
if machine is None:
|
|
249
261
|
kind = "measured on " + (", ".join(where) or "?")
|
|
262
|
+
kind += alongside_note(v)
|
|
250
263
|
table.append([
|
|
251
264
|
"★" if m in frontier else "", m, "; ".join(sorted(info.get("served") or [])) or "-",
|
|
252
265
|
",".join(info.get("tags") or []),
|
|
@@ -256,7 +269,8 @@ def speed(rows, infos, machine=None, speeds=None):
|
|
|
256
269
|
_fmt(median(x[3] for x in t), ".0f"), _fmt(median(r["tokens"] for r in v), ".0f"),
|
|
257
270
|
_fmt(median(r["reasoning_tokens"] for r in v), ".0f"), _fmt(median(r["answer_tokens"] for r in v), ".0f"),
|
|
258
271
|
f"{sum(r['tokens'] for r in v) / passes:,.0f}" if passes else "-",
|
|
259
|
-
_fmt(info.get("load_s") if here else None, ".0f")
|
|
272
|
+
_fmt(info.get("load_s") if here else None, ".0f")
|
|
273
|
+
+ (" (alongside others)" if here and info.get("load_s") and info.get("loaded_alongside") else ""),
|
|
260
274
|
_fmt(info["peak_rss_mb"] / 1024 if info.get("peak_rss_mb") and here else None, ".1f"),
|
|
261
275
|
])
|
|
262
276
|
return header, table, frontier
|