tuieval 0.2.0.dev14__tar.gz → 0.2.0.dev23__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.dev23}/CHANGELOG.md +10 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/PKG-INFO +18 -7
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/README.md +17 -6
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/docs/models.md +117 -2
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/docs/writing-packs.md +6 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/_version.py +2 -2
- tuieval-0.2.0.dev23/src/tuieval/builtin.toml +83 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/cli.py +21 -13
- tuieval-0.2.0.dev23/src/tuieval/doctor.py +131 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/engine.py +150 -4
- tuieval-0.2.0.dev23/src/tuieval/onboard.py +132 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/packs.py +8 -2
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/run_evals.py +54 -4
- tuieval-0.2.0.dev23/src/tuieval/scaffold.py +243 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/selftest.py +11 -7
- tuieval-0.2.0.dev23/src/tuieval/templates/models.toml +23 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/tui.py +206 -20
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/verdict.py +57 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/tests/test_tuieval.py +204 -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.dev23}/.gitignore +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/LICENSE +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/RELEASING.md +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/docs/images/brand/README.md +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/docs/images/brand/favicon.ico +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/docs/images/brand/readme-header.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/docs/images/brand/social-preview.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/docs/images/brand/tuieval-icon-1024.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/docs/images/brand/tuieval-icon-128.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/docs/images/brand/tuieval-icon-16.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/docs/images/brand/tuieval-icon-16.svg +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/docs/images/brand/tuieval-icon-256.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/docs/images/brand/tuieval-icon-32.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/docs/images/brand/tuieval-icon-48.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/docs/images/brand/tuieval-icon-512.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/docs/images/brand/tuieval-icon-64.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/docs/images/brand/tuieval-icon-animated.svg +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/docs/images/brand/tuieval-icon.svg +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/docs/images/tui-setup.png +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/pyproject.toml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/__init__.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/__main__.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/client.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/compare.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/export.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/graders/__init__.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/graders/answer.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/graders/code.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/graders/rag.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/graders/reply.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/graders/tool_call.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/machines.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/profiles.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/remove.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/templates/packs/answer/pack.toml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/templates/packs/answer/system.txt +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/templates/packs/answer/tests.yaml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/templates/packs/code/pack.toml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/templates/packs/code/system.txt +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/templates/packs/code/tests.yaml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/templates/packs/rag/pack.toml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/templates/packs/rag/system.txt +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/templates/packs/rag/tests.yaml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/templates/packs/reply/pack.toml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/templates/packs/reply/system.txt +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/templates/packs/reply/tests.yaml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/templates/packs/tool_call/pack.toml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/templates/packs/tool_call/system.txt +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/templates/packs/tool_call/tests.yaml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/templates/packs/tool_call/tools.yaml +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/tune.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/watch_proxy.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/workspace.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/src/tuieval/yamlout.py +0 -0
- {tuieval-0.2.0.dev14 → tuieval-0.2.0.dev23}/tests/mock_server.py +0 -0
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- 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.
|
|
6
|
+
- 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.
|
|
7
|
+
- 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.
|
|
8
|
+
- 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.
|
|
9
|
+
- `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).
|
|
10
|
+
- `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.
|
|
11
|
+
- `?` on any TUI screen explains what it's for, its keys, and the words tuieval uses (tiers, verdicts, critical failures, gates).
|
|
12
|
+
- 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).
|
|
13
|
+
- 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.
|
|
14
|
+
- `tuieval doctor` checks servers, models, packs and API keys, and says what to fix.
|
|
5
15
|
- 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
16
|
- `tuieval tune` scores options on projected full-length answers (`[tune] answer_tokens`, default 1024), so generation speed counts as in real runs.
|
|
7
17
|
- `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.dev23
|
|
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
|
|
@@ -142,3 +143,117 @@ On Apple Silicon Macs, once the system's GPU allocations pass about half of RAM,
|
|
|
142
143
|
| `api_key_env` | environment variable holding the API key |
|
|
143
144
|
| `thinking_param` | `reasoning` to send `[sampling] enable_thinking` as OpenRouter's `reasoning.enabled` |
|
|
144
145
|
| `headers` | extra HTTP headers |
|
|
146
|
+
|
|
147
|
+
## Built-in servers and defaults
|
|
148
|
+
|
|
149
|
+
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.
|
|
150
|
+
|
|
151
|
+
```toml
|
|
152
|
+
[defaults] fill in key by key; [sampling] only when models.toml has no [sampling] at all (it's part of
|
|
153
|
+
# every result's fingerprint); a server here is replaced whole by one of the same name in models.toml.
|
|
154
|
+
|
|
155
|
+
[defaults]
|
|
156
|
+
repeat = 3
|
|
157
|
+
request_timeout_ms = 1800000 # per request; long thinking runs need it
|
|
158
|
+
ready_timeout_s = 1200 # how long a model may take to load before giving up
|
|
159
|
+
model_dirs = ["~/models"] # where "Scan for models" looks for new GGUFs
|
|
160
|
+
|
|
161
|
+
# Sent with every request. A model may override it (see `thinking` / `sampling` below); results
|
|
162
|
+
# record the settings each model actually ran with, and the scorecard shows any difference.
|
|
163
|
+
[sampling]
|
|
164
|
+
temperature = 0.7
|
|
165
|
+
top_p = 0.95
|
|
166
|
+
top_k = 20
|
|
167
|
+
min_p = 0.0
|
|
168
|
+
max_tokens = 16384
|
|
169
|
+
enable_thinking = true # test every model in the SAME mode
|
|
170
|
+
|
|
171
|
+
# llama.cpp's server (https://github.com/ggml-org/llama.cpp), started for each GGUF model.
|
|
172
|
+
[servers.llama]
|
|
173
|
+
# Output-affecting flags only. Anything here must be identical wherever results are compared,
|
|
174
|
+
# so it's part of each result's fingerprint. Speed-only flags live in `perf` and `tune` below.
|
|
175
|
+
cmd = [
|
|
176
|
+
"llama-server",
|
|
177
|
+
"-m", "{model}",
|
|
178
|
+
"--alias", "{served_name}",
|
|
179
|
+
"--host", "127.0.0.1", "--port", "{port}",
|
|
180
|
+
"--jinja",
|
|
181
|
+
"--metrics",
|
|
182
|
+
"-np", "1",
|
|
183
|
+
"-ctk", "{kv_type}", "-ctv", "{kv_type}",
|
|
184
|
+
"-c", "{ctx}",
|
|
185
|
+
"--reasoning-format", "auto",
|
|
186
|
+
]
|
|
187
|
+
port = 8080
|
|
188
|
+
model_is_path = true # check the GGUF exists and fits before starting
|
|
189
|
+
vision_args = ["--mmproj", "{mmproj}"] # appended only for models that set mmproj
|
|
190
|
+
kv_type = "f16" # a model's own `kv_type` (e.g. "q8_0") makes it a different model entry
|
|
191
|
+
max_ctx = 65536 # upper bound; the fit check lowers it per machine (see machines.py)
|
|
192
|
+
version_cmd = ["llama-server", "--version"] # recorded with every result
|
|
193
|
+
# Added to every request. cache_prompt = false: each answer is computed from scratch, so a repeat of
|
|
194
|
+
# the same question reuses nothing from the last time it was asked (and its TTFT is honest).
|
|
195
|
+
# Answers record any prompt tokens the server reused.
|
|
196
|
+
request = { cache_prompt = false }
|
|
197
|
+
|
|
198
|
+
# Speed-only flags, always applied (not tuned).
|
|
199
|
+
perf = ["-ngl", "99"]
|
|
200
|
+
|
|
201
|
+
# Speed-only knobs `tuieval tune` tries, one at a time from the best so far. The first option of
|
|
202
|
+
# each knob is the default for models not tuned on this machine yet. Placeholders: {p} P-cores,
|
|
203
|
+
# {p_minus_2}, {all} all cores. Options that stop the server from starting are skipped.
|
|
204
|
+
# Check the flags against `llama-server --help` for your build.
|
|
205
|
+
[servers.llama.tune]
|
|
206
|
+
threads = [["-t", "{p}", "-tb", "{p}"], ["-t", "{p_minus_2}", "-tb", "{p}"], ["-t", "{all}", "-tb", "{all}"]]
|
|
207
|
+
ubatch = [["-ub", "512"], ["-ub", "256"], ["-ub", "1024"]]
|
|
208
|
+
batch = [["-b", "2048"], ["-b", "4096"]]
|
|
209
|
+
flash_attn = [["-fa", "on"], ["-fa", "off"]]
|
|
210
|
+
|
|
211
|
+
# A server that's already running (LM Studio, Ollama, vLLM, a remote box): give its URL and no cmd.
|
|
212
|
+
# Its models are added with `tuieval add <model id> --server local`.
|
|
213
|
+
[servers.local]
|
|
214
|
+
url = "http://127.0.0.1:1234" # LM Studio's default; Ollama is http://127.0.0.1:11434, vLLM :8000
|
|
215
|
+
|
|
216
|
+
# OpenRouter: ANY model it serves, with no [[models]] entry. Name it when you run:
|
|
217
|
+
# tuieval run --tier smoke --only openrouter:qwen/qwen3-235b-a22b-2507
|
|
218
|
+
# TUI: tick "+ openrouter: any model" in the model list, or press "a" and type openrouter:<model id>
|
|
219
|
+
# Results go to results/or-<id>/ and the model stays listed once it has results. A plain
|
|
220
|
+
# `tuieval run` never includes OpenRouter models (they cost money); name them.
|
|
221
|
+
# Needs OPENROUTER_API_KEY in the environment of the shell that starts tuieval.
|
|
222
|
+
# pin_endpoint: every answer of a model comes from one provider endpoint (the closest to the released
|
|
223
|
+
# weights), with no fallback, since providers run different quantizations.
|
|
224
|
+
# To keep providers that store or train on prompts (and could learn your tests) out entirely, add
|
|
225
|
+
# request = { provider = { data_collection = "deny" } }
|
|
226
|
+
[servers.openrouter]
|
|
227
|
+
url = "https://openrouter.ai/api/v1"
|
|
228
|
+
any_model = true
|
|
229
|
+
label_prefix = "or"
|
|
230
|
+
pin_endpoint = true
|
|
231
|
+
api_key_env = "OPENROUTER_API_KEY"
|
|
232
|
+
thinking_param = "reasoning" # [sampling] enable_thinking -> OpenRouter's reasoning.enabled
|
|
233
|
+
headers = { "X-Title" = "tuieval" }
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
`[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.
|
|
237
|
+
|
|
238
|
+
Optional sections:
|
|
239
|
+
|
|
240
|
+
```toml
|
|
241
|
+
# Optional tuner settings.
|
|
242
|
+
# [tune]
|
|
243
|
+
# guard_similarity = 0.6 # greedy answers under a candidate flag must stay this similar to the defaults'
|
|
244
|
+
# request_timeout_s = 600 # a tuning request that takes longer fails that candidate
|
|
245
|
+
# warm_start = true # start from a tuned model with the same architecture and shapes (a fine-tune)
|
|
246
|
+
# warm_retest = ["spec", "ubatch"] # the knobs a warm start re-tries; the rest are inherited
|
|
247
|
+
# answer_tokens = 1024 # answer length options are scored for (projected objective)
|
|
248
|
+
# swap_limit_mb = 500 # reject options that swap more than this beyond the defaults (default: report only)
|
|
249
|
+
|
|
250
|
+
# Optional: llama-bench for the tuner's fast first stage (found on PATH as llama-bench otherwise).
|
|
251
|
+
# [bench]
|
|
252
|
+
# cmd = ["~/llama.cpp/build/bin/llama-bench"]
|
|
253
|
+
|
|
254
|
+
# Optional per-machine settings, by machine id (tuieval machines shows the ids).
|
|
255
|
+
# [machines.m3max-64gb]
|
|
256
|
+
# memory_headroom_gb = 3 # GPU memory kept free for macOS and other apps (default 4)
|
|
257
|
+
# gpu_residency_gb = 12 # GPU memory the driver keeps resident before churning (default: half of RAM)
|
|
258
|
+
```
|
|
259
|
+
|
|
@@ -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.dev23'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 0, 'dev23')
|
|
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)
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""tuieval doctor: checks the setup and says what to fix (servers, models, packs, API keys).
|
|
2
|
+
|
|
3
|
+
tuieval doctor
|
|
4
|
+
|
|
5
|
+
Each line is ✓ (fine), ! (worth knowing) or ✗ (a run would fail), with the fix. Exits 1 if any ✗.
|
|
6
|
+
"""
|
|
7
|
+
import argparse
|
|
8
|
+
import os
|
|
9
|
+
import platform
|
|
10
|
+
import sys
|
|
11
|
+
|
|
12
|
+
from . import engine
|
|
13
|
+
from . import workspace
|
|
14
|
+
|
|
15
|
+
OK, WARN, BAD = "✓", "!", "✗"
|
|
16
|
+
COLOR = {OK: "\033[32m", WARN: "\033[33m", BAD: "\033[31m"}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class Report:
|
|
20
|
+
def __init__(self):
|
|
21
|
+
self.bad = 0
|
|
22
|
+
self.color = sys.stdout.isatty() and not os.environ.get("NO_COLOR")
|
|
23
|
+
|
|
24
|
+
def line(self, mark, text, fix=""):
|
|
25
|
+
self.bad += mark == BAD
|
|
26
|
+
m = f"{COLOR[mark]}{mark}\033[0m" if self.color else mark
|
|
27
|
+
print(f" {m} {text}" + (f"\n → {fix}" if fix else ""))
|
|
28
|
+
|
|
29
|
+
def head(self, text):
|
|
30
|
+
print(f"\n{text}")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def check(models_path=None):
|
|
34
|
+
"""Print the report; returns the number of problems that would make a run fail."""
|
|
35
|
+
r = Report()
|
|
36
|
+
r.head("tuieval")
|
|
37
|
+
py = platform.python_version()
|
|
38
|
+
r.line(OK if sys.version_info >= (3, 11) else BAD, f"Python {py}", "" if sys.version_info >= (3, 11)
|
|
39
|
+
else "tuieval needs Python 3.11 or newer")
|
|
40
|
+
try:
|
|
41
|
+
e = engine.Engine(models_path=models_path)
|
|
42
|
+
except Exception as ex: # a broken models.toml: nothing else can be checked
|
|
43
|
+
r.line(BAD, f"models.toml in {workspace.root()}: {ex}", "fix the file (tuieval init makes a fresh one elsewhere)")
|
|
44
|
+
return r.bad
|
|
45
|
+
r.line(OK, f"workspace {e.root}")
|
|
46
|
+
|
|
47
|
+
r.head("Packs")
|
|
48
|
+
for err in e.pack_errors:
|
|
49
|
+
r.line(BAD, err, "fix the pack, then: tuieval selftest")
|
|
50
|
+
if e.packs:
|
|
51
|
+
tests = sum(len(p.tests) for p in e.packs.values())
|
|
52
|
+
r.line(OK, f"{len(e.packs)} pack(s), {tests} tests (tuieval selftest checks their answers)")
|
|
53
|
+
elif not e.pack_errors:
|
|
54
|
+
r.line(BAD, "no packs yet", "tuieval new-pack my-first-pack (a pack of example questions to edit)")
|
|
55
|
+
|
|
56
|
+
r.head("Servers")
|
|
57
|
+
found = engine.detect_servers(engine.detect_ports(e.cfg))
|
|
58
|
+
used = {m["server"] for m in e.cfg["models"]}
|
|
59
|
+
answering = {}
|
|
60
|
+
for name, server in e.cfg["servers"].items():
|
|
61
|
+
if server.get("any_model") or server.get("api_key_env"):
|
|
62
|
+
key = server.get("api_key_env")
|
|
63
|
+
if key and os.environ.get(key):
|
|
64
|
+
r.line(OK, f"{name}: {key} is set")
|
|
65
|
+
elif key:
|
|
66
|
+
r.line(WARN, f"{name}: {key} isn't set (needed only for {name} models)",
|
|
67
|
+
f"export {key}=… in the shell you start tuieval from")
|
|
68
|
+
continue
|
|
69
|
+
if server.get("cmd"):
|
|
70
|
+
missing = engine.missing_program([str(a) for a in server["cmd"]],
|
|
71
|
+
engine.expand(server["cwd"]) if server.get("cwd") else None)
|
|
72
|
+
if missing:
|
|
73
|
+
r.line(BAD if name in used else WARN,
|
|
74
|
+
f"{name}: {missing} isn't installed" + ("" if name in used else " (no model uses it yet)"),
|
|
75
|
+
engine.install_hint(missing))
|
|
76
|
+
else:
|
|
77
|
+
r.line(OK, f"{name}: {server['cmd'][0]} is installed; tuieval starts it for each model")
|
|
78
|
+
continue
|
|
79
|
+
url = server.get("url", "").rstrip("/").removesuffix("/v1")
|
|
80
|
+
ids = engine.probe_models(url) if url else None
|
|
81
|
+
answering[name] = ids
|
|
82
|
+
if ids is not None:
|
|
83
|
+
r.line(OK, f"{name}: {url} answers, serving {len(ids)} model(s)" + (f": {', '.join(ids[:5])}" if ids else ""))
|
|
84
|
+
else:
|
|
85
|
+
elsewhere = [f for f in found if f["url"] != url]
|
|
86
|
+
fix = (f"start the server at {url}, or "
|
|
87
|
+
+ ("run `tuieval add` to add the models of the one running at " + ", ".join(
|
|
88
|
+
f"{f['url']}" + (f" ({f['usual']})" if f["usual"] else "") for f in elsewhere)
|
|
89
|
+
if elsewhere else engine.url_fix(e.cfg, name)))
|
|
90
|
+
r.line(BAD if name in used else WARN,
|
|
91
|
+
f"{name}: nothing answers at {url}" + ("" if name in used else " (no model uses it yet)"), fix)
|
|
92
|
+
known = {s.get("url", "").rstrip("/").removesuffix("/v1") for s in e.cfg["servers"].values()}
|
|
93
|
+
for f in found:
|
|
94
|
+
if f["url"] not in known:
|
|
95
|
+
r.line(WARN, f"found a server at {f['url']}" + (f" ({f['usual']}'s usual port)" if f["usual"] else "")
|
|
96
|
+
+ f" serving {len(f['models'])} model(s), not in models.toml",
|
|
97
|
+
"add its models with: tuieval add")
|
|
98
|
+
|
|
99
|
+
r.head("Models")
|
|
100
|
+
if not e.cfg["models"]:
|
|
101
|
+
r.line(BAD, "no models yet", "tuieval add <GGUF path or model id>, or press a in the TUI")
|
|
102
|
+
for m in e.cfg["models"]:
|
|
103
|
+
server = e.cfg["servers"][m["server"]]
|
|
104
|
+
label = f"{m['label']} ({m['server']})"
|
|
105
|
+
if server.get("model_is_path"):
|
|
106
|
+
path = engine.expand(m["model"])
|
|
107
|
+
r.line(OK if os.path.exists(path) else BAD, f"{label}: {path}",
|
|
108
|
+
"" if os.path.exists(path) else "the file is gone; fix `model` in models.toml or tuieval remove it")
|
|
109
|
+
elif m["server"] in answering:
|
|
110
|
+
ids = answering[m["server"]]
|
|
111
|
+
if ids is None:
|
|
112
|
+
r.line(BAD, f"{label}: its server isn't running", "see Servers above")
|
|
113
|
+
elif engine.serves(ids, m["served_name"]):
|
|
114
|
+
r.line(OK, f"{label}: served")
|
|
115
|
+
else:
|
|
116
|
+
r.line(BAD, f"{label}: the server doesn't list {m['served_name']!r}",
|
|
117
|
+
f"load it in the server, or use one of: {', '.join(ids[:8]) or 'none listed'}")
|
|
118
|
+
else:
|
|
119
|
+
r.line(OK, label)
|
|
120
|
+
|
|
121
|
+
print("\n" + ("Ready: open the TUI with `tuieval`, or run `tuieval run --tier smoke`." if not r.bad
|
|
122
|
+
else f"{r.bad} problem(s) to fix before a run (✗ above)."))
|
|
123
|
+
return r.bad
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def cmd_doctor(argv):
|
|
127
|
+
p = argparse.ArgumentParser(prog="tuieval doctor", description=__doc__,
|
|
128
|
+
formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
129
|
+
p.add_argument("--models", default=None, help="default: the workspace's models.toml")
|
|
130
|
+
a = p.parse_args(argv)
|
|
131
|
+
sys.exit(1 if check(a.models) else 0)
|