tuieval 0.2.0.dev65__tar.gz → 0.2.0.dev66__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.
Files changed (77) hide show
  1. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/CHANGELOG.md +1 -1
  2. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/PKG-INFO +1 -1
  3. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/models.md +2 -2
  4. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/_version.py +2 -2
  5. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/pta.py +41 -21
  6. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/tui.py +4 -3
  7. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/tests/test_tuieval.py +8 -4
  8. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/.gitignore +0 -0
  9. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/LICENSE +0 -0
  10. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/README.md +0 -0
  11. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/RELEASING.md +0 -0
  12. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/images/brand/README.md +0 -0
  13. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/images/brand/favicon.ico +0 -0
  14. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/images/brand/readme-header.png +0 -0
  15. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/images/brand/social-preview.png +0 -0
  16. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/images/brand/tuieval-icon-1024.png +0 -0
  17. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/images/brand/tuieval-icon-128.png +0 -0
  18. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/images/brand/tuieval-icon-16.png +0 -0
  19. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/images/brand/tuieval-icon-16.svg +0 -0
  20. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/images/brand/tuieval-icon-256.png +0 -0
  21. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/images/brand/tuieval-icon-32.png +0 -0
  22. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/images/brand/tuieval-icon-48.png +0 -0
  23. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/images/brand/tuieval-icon-512.png +0 -0
  24. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/images/brand/tuieval-icon-64.png +0 -0
  25. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/images/brand/tuieval-icon-animated.svg +0 -0
  26. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/images/brand/tuieval-icon.svg +0 -0
  27. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/images/tui-answer-reasoning.png +0 -0
  28. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/images/tui-run-reasoning.png +0 -0
  29. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/images/tui-setup.png +0 -0
  30. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/docs/writing-packs.md +0 -0
  31. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/pyproject.toml +0 -0
  32. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/__init__.py +0 -0
  33. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/__main__.py +0 -0
  34. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/builtin.toml +0 -0
  35. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/cli.py +0 -0
  36. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/client.py +0 -0
  37. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/compare.py +0 -0
  38. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/doctor.py +0 -0
  39. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/engine.py +0 -0
  40. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/export.py +0 -0
  41. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/graders/__init__.py +0 -0
  42. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/graders/answer.py +0 -0
  43. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/graders/code.py +0 -0
  44. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/graders/rag.py +0 -0
  45. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/graders/reply.py +0 -0
  46. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/graders/tool_call.py +0 -0
  47. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/machines.py +0 -0
  48. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/onboard.py +0 -0
  49. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/packs.py +0 -0
  50. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/profiles.py +0 -0
  51. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/remove.py +0 -0
  52. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/run_evals.py +0 -0
  53. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/scaffold.py +0 -0
  54. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/selftest.py +0 -0
  55. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/templates/models.toml +0 -0
  56. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/templates/packs/answer/pack.toml +0 -0
  57. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/templates/packs/answer/system.txt +0 -0
  58. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/templates/packs/answer/tests.yaml +0 -0
  59. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/templates/packs/code/pack.toml +0 -0
  60. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/templates/packs/code/system.txt +0 -0
  61. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/templates/packs/code/tests.yaml +0 -0
  62. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/templates/packs/rag/pack.toml +0 -0
  63. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/templates/packs/rag/system.txt +0 -0
  64. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/templates/packs/rag/tests.yaml +0 -0
  65. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/templates/packs/reply/pack.toml +0 -0
  66. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/templates/packs/reply/system.txt +0 -0
  67. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/templates/packs/reply/tests.yaml +0 -0
  68. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/templates/packs/tool_call/pack.toml +0 -0
  69. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/templates/packs/tool_call/system.txt +0 -0
  70. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/templates/packs/tool_call/tests.yaml +0 -0
  71. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/templates/packs/tool_call/tools.yaml +0 -0
  72. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/tune.py +0 -0
  73. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/verdict.py +0 -0
  74. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/watch_proxy.py +0 -0
  75. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/workspace.py +0 -0
  76. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/src/tuieval/yamlout.py +0 -0
  77. {tuieval-0.2.0.dev65 → tuieval-0.2.0.dev66}/tests/mock_server.py +0 -0
@@ -7,7 +7,7 @@
7
7
  - The run's ETA adds up each pack's answers left × that model's time per answer there (its history, then its own times once it has 5 answers), instead of extrapolating from the first few answers.
8
8
  - Faster start on Macs: the GPU core count comes from `ioreg` instead of `system_profiler`, which took seconds on some Macs.
9
9
  - A model a server won't serve (HTTP 401-404, e.g. OpenRouter's "No endpoints found") is never judged on it: those answers don't count, and the model stops at once instead of failing every question.
10
- - PTA index: parsimony (tokens), time and accuracy per model as bars, with tokens and time shown as multiples of the best (2.4×): the first Results tab (where `r` lands), `tuieval pta` and the report. Server errors aren't answers, and models with few answers are left out unless picked.
10
+ - PTA index: parsimony (tokens), time and accuracy per model as bars (tokens and time as long as they took, shorter is better; accuracy longer is better), with tokens and time also shown as multiples of the best (2.4×): the first Results tab (where `r` lands), `tuieval pta` and the report. Server errors aren't answers, and models with few answers are left out unless picked.
11
11
  - 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.
12
12
  - More evals during a run: `n` opens Setup, and Start adds them to the run going (same tier) or queues them after it.
13
13
  - 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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tuieval
3
- Version: 0.2.0.dev65
3
+ Version: 0.2.0.dev66
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
@@ -98,13 +98,13 @@ By default a run serves one model at a time. On a machine with room for more, se
98
98
 
99
99
  ## PTA index
100
100
 
101
- The **PTA index** puts parsimony (tokens), time and accuracy side by side, as a bar for each per model (longer is better), with a table underneath. It's the first tab in Results, where `r` opens, and it's also `tuieval pta` and a section in `tuieval report`. There's no combined number; verdicts and critical failures stay in Production readiness.
101
+ The **PTA index** puts parsimony (tokens), time and accuracy side by side, as a bar for each per model, with a table underneath. It's the first tab in Results, where `r` opens, and it's also `tuieval pta` and a section in `tuieval report`. There's no combined number; verdicts and critical failures stay in Production readiness.
102
102
 
103
103
  - **P parsimony (tokens)**: the tokens used, reasoning and answer, on every question compared (each question at its median over repeats). Beside the bar: how many times the leanest model's, e.g. `2.4×` (`1.0×` is the leanest, ★ in the table's **tokens/answer**). Unlike time it doesn't depend on your machine: it's how much a model says to get to an answer, which is what you pay for on an API and what fills its context.
104
104
  - **T time**: the total time to answer those questions, as how many times the fastest model's, e.g. `28×` (★ in **time/answer**).
105
105
  - **A accuracy**: the share of answers to those questions that passed.
106
106
 
107
- The P and T bars use a log scale: full for the best, 15 points (of 100) shorter for every doubling, empty at about 100 times the best. So a model at 28× still shows a bar, and adding a better model shortens the others'.
107
+ The P and T bars are as long as the tokens and time used: the model that used the most fills its bar, the others in proportion (at least one block), so **shorter is better**. The A bar is the share of answers right, so **longer is better**; it's full only when every answer passed. Each heading says which way is better.
108
108
 
109
109
  Models are compared on the questions all of them answered, so one that skipped some never looks faster. Server and connection errors aren't answers: a model with only those is listed as having none. A model with fewer than half as many answers as the most-answered one is left out (and listed), or it would shrink the shared questions for everyone. Pick the models and pack to compare in the TUI, or use `--only` and `--packs`: models you pick are always compared.
110
110
 
@@ -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.dev65'
22
- __version_tuple__ = version_tuple = (0, 2, 0, 'dev65')
21
+ __version__ = version = '0.2.0.dev66'
22
+ __version_tuple__ = version_tuple = (0, 2, 0, 'dev66')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -1,4 +1,4 @@
1
- """PTA index: parsimony (tokens), time and accuracy per model, a bar each (longer = better).
1
+ """PTA index: parsimony (tokens), time and accuracy per model, a bar each.
2
2
 
3
3
  P parsimony (tokens) the tokens (reasoning and answer) used on every question compared (each
4
4
  question at its median over repeats), shown as how many times the leanest
@@ -7,8 +7,10 @@
7
7
  model's ("28×").
8
8
  A accuracy the share of answers to those questions that passed (%).
9
9
 
10
- Behind the P and T bars is a 0-100 score: 100 for the best, POINTS_PER_DOUBLING points less for every
11
- doubling (0 at about 100 times more), so models far behind the best still get a visible bar.
10
+ The P and T bars are as long as the tokens and time used (the model that used the most fills its bar,
11
+ the others in proportion, at least one block), so shorter is better; the A bar: longer is better.
12
+ P and T also carry a 0-100 score (100 for the best, POINTS_PER_DOUBLING points less for every
13
+ doubling), used to order models with the same accuracy.
12
14
 
13
15
  Models are compared on the questions all of them answered, so one that skipped some never looks
14
16
  faster. Server and connection errors aren't answers. By default a model with fewer than half as many
@@ -93,9 +95,11 @@ def index(rows, models=None, memory=None):
93
95
  "most": most}
94
96
 
95
97
 
96
- # (letter, name, what the number beside the bar shows, colour); the bar itself is the 0-100 score
97
- LETTERS = (("P", "parsimony (tokens)", "tokens_x", "cyan"), ("T", "time", "time_x", "magenta"),
98
- ("A", "accuracy", "A", "green"))
98
+ # (letter, name, which way is better, what the bar measures, the number beside it, colour). P and T bars
99
+ # are the amount itself (tokens, time: shorter is better); the A bar is the share right (longer is better).
100
+ LETTERS = (("P", "parsimony (tokens)", "▼ shorter is better", "tokens", "tokens_x", "cyan"),
101
+ ("T", "time", "▼ shorter is better", "total_s", "time_x", "magenta"),
102
+ ("A", "accuracy", "▲ longer is better", "A", "A", "green"))
99
103
 
100
104
 
101
105
  def times(v):
@@ -103,26 +107,41 @@ def times(v):
103
107
  return "-" if v is None else f"{v:.1f}×" if v < 9.95 else f"{v:.0f}×"
104
108
 
105
109
 
110
+ def _blocks(width, v, most):
111
+ """How many of width blocks a bar fills: v as a share of most, at least one block when v > 0."""
112
+ if not v or not most:
113
+ return 0
114
+ return max(1, min(width, round(width * v / most)))
115
+
116
+
106
117
  def bars(result, width=20, markup=True):
107
- """Lines with a row per model and a bar per score (P, T, A), longer = better: the at-a-glance view.
108
- Beside P and T, how many times the leanest / fastest model's tokens / time; beside A, the percentage.
109
- markup=False gives plain text (the report)."""
118
+ """Lines with a row per model and a bar each for P, T and A: the at-a-glance view. The P and T bars
119
+ are proportional to tokens and time (the longest is the model that used most; shorter is better),
120
+ with how many times the leanest / fastest model's beside them; the A bar is the share of answers
121
+ right (longer is better, full only when every answer passed). markup=False gives plain text."""
110
122
  if not result["models"]:
111
123
  return []
112
124
  name_w = max(len(x["model"]) for x in result["models"])
113
- # each column: the bar, a space, "100%" or "9.8×" (5), then a gap; at least as wide as its heading
114
- cell_w = max(width + 9, *(len(f"{k} {name}") + 2 for k, name, _, _ in LETTERS))
115
- head = " " * (name_w + 2) + "".join(f"{f'{k} {name}':<{cell_w}}" for k, name, _, _ in LETTERS)
116
- lines = [f"[b]{head.rstrip()}[/b]" if markup else head.rstrip()]
125
+ # each column: the bar, a space, "100%" or "9.8×" (6), then a gap; at least as wide as its heading
126
+ cell_w = max(width + 9, *(len(t) + 2 for k, name, better, *_ in LETTERS for t in (f"{k} {name}", better)))
127
+ pad = " " * (name_w + 2)
128
+ head = pad + "".join(f"{f'{k} {name}':<{cell_w}}" for k, name, *_ in LETTERS)
129
+ sub = pad + "".join(f"{better:<{cell_w}}" for _, _, better, *_ in LETTERS)
130
+ lines = [f"[b]{head.rstrip()}[/b]" if markup else head.rstrip(), f"[dim]{sub.rstrip()}[/dim]" if markup else sub.rstrip()]
131
+ most = {m: max((x.get(m) or 0 for x in result["models"]), default=0) for _, _, _, m, _, _ in LETTERS}
117
132
  for x in result["models"]:
118
133
  cells = []
119
- for k, _, shown, color in LETTERS:
120
- v = x[k]
121
- full = 0 if v is None else round(width * max(0, min(100, v)) / 100)
122
- if v is not None and v < 100: # full only when perfect (accuracy) or the best (P, T)
123
- full = min(full, width - 1)
134
+ for k, _, _, measure, shown, color in LETTERS:
135
+ if k == "A":
136
+ v = x["A"]
137
+ full = 0 if v is None else round(width * max(0, min(100, v)) / 100)
138
+ if v is not None and v < 100: # full only when every answer passed
139
+ full = min(full, width - 1)
140
+ num = "-" if v is None else compare.pct(v / 100)
141
+ else:
142
+ full = _blocks(width, x.get(measure), most[measure])
143
+ num = times(x.get(shown))
124
144
  bar, rest = "█" * full, "░" * (width - full)
125
- num = (("-" if v is None else compare.pct(v / 100)) if shown == "A" else times(x.get(shown)))
126
145
  bar = f"[{color}]{bar}[/][grey37]{rest}[/]" if markup else bar + rest
127
146
  cells.append(f"{bar} {num:>6}" + " " * (cell_w - width - 7))
128
147
  name = f"{x['model']:<{name_w}}"
@@ -183,8 +202,9 @@ def scope_note(result, models):
183
202
  if not result["questions"]:
184
203
  return "These models have no question in common yet, so there's nothing to compare."
185
204
  return (f"Compared on the {result['questions']} questions all {len(result['models'])} model(s) answered. "
186
- "Longer bars are better. P parsimony (tokens) and T time: how many times the leanest / fastest "
187
- "model's (1.0×, ★ in the table). A accuracy: answers right.")
205
+ "P parsimony (tokens) and T time: bars as long as the tokens / time used (shorter is better), "
206
+ "and how many times the leanest / fastest model's (1.0×, ★ in the table). A accuracy: answers right "
207
+ "(longer is better).")
188
208
 
189
209
 
190
210
  def for_engine(e, labels=None, packs=None, results_dir=None):
@@ -332,9 +332,10 @@ with the model's reasoning and answer as they stream, then recent results with t
332
332
  v with several models at a time: stream the next one (k skips the one streaming)""",
333
333
  "ResultsScreen": """[b]Results[/b]
334
334
 
335
- PTA index parsimony (tokens), time and accuracy per model, a bar each (longer = better;
336
- 2.4× = 2.4 times the best), with per-answer numbers, tok/s, TTFT and memory in
337
- the table. Results open here.
335
+ PTA index parsimony (tokens), time and accuracy per model, a bar each: tokens and time
336
+ as long as they took (shorter is better; 2.4× = 2.4 times the best), accuracy
337
+ longer is better. Per-answer numbers, tok/s, TTFT and memory in the table.
338
+ Results open here.
338
339
  Production readiness one verdict per model and use case; the table says why and what to run next
339
340
  Per question every question, model by model (filters: pack, level, Models disagree)
340
341
  Failures every wrong answer with the grader's reason; enter opens it in full
@@ -761,10 +761,14 @@ class PTAIndex(unittest.TestCase):
761
761
  self.assertEqual((cells["slow"][1], cells["fast"][1:3]), ("10 ★", ["40", "2.0s ★"])) # per answer
762
762
  bars = pta.bars(res, 10, markup=False)
763
763
  self.assertEqual(bars[0].split(), ["P", "parsimony", "(tokens)", "T", "time", "A", "accuracy"])
764
+ self.assertEqual(bars[1].split(), ["▼", "shorter", "is", "better"] * 2 + ["▲", "longer", "is", "better"])
765
+ slow, fast = bars[2], bars[3]
764
766
  self.assertEqual((bars[0].index("P parsimony"), bars[0].index("T time")), # each bar starts under
765
- (bars[2].index("███████░░░"), bars[2].index("██████████"))) # its heading
766
- # the bar is the score (4x the tokens: 70); beside it, how many times the best
767
- self.assertEqual(bars[2].split(), ["fast", "███████░░░", "4.0×", "██████████", "1.0×", "█████░░░░░", "50.0%"])
767
+ (fast.index("██████████"), fast.index("███░░░░░░░"))) # its heading
768
+ # P and T bars as long as tokens and time (shorter is better): fast used 4x the tokens of slow but
769
+ # took 4 s against 13 s, so it has the longest token bar and the shortest time bar
770
+ self.assertEqual(fast.split(), ["fast", "██████████", "4.0×", "███░░░░░░░", "1.0×", "█████░░░░░", "50.0%"])
771
+ self.assertEqual(slow.split()[1:5], ["██░░░░░░░░", "1.0×", "██████████", "3.2×"])
768
772
  self.assertEqual((pta.times(1), pta.times(9.84), pta.times(27.9)), ("1.0×", "9.8×", "28×"))
769
773
  self.assertEqual((by["fast"]["A"], by["slow"]["A"]), (50, 100))
770
774
  self.assertEqual([x["model"] for x in res["models"]], ["slow", "fast"]) # best accuracy first
@@ -774,7 +778,7 @@ class PTAIndex(unittest.TestCase):
774
778
  self.assertEqual([compare.pct(f) for f in (467 / 469, 1, 0, 0.5)], ["99.6%", "100%", "0%", "50.0%"])
775
779
  row = lambda model, a: {"model": model, "P": 100, "T": 100, "A": a, "tokens_x": 1, "time_x": 1}
776
780
  bars = pta.bars({"models": [row("two-wrong", 100 * 467 / 469), row("perfect", 100.0)]}, 10, markup=False)
777
- two_wrong, perfect = bars[1].split(), bars[2].split()
781
+ two_wrong, perfect = bars[2].split(), bars[3].split()
778
782
  self.assertEqual((two_wrong[-2], two_wrong[-1]), ("█████████░", "99.6%")) # not a full bar
779
783
  self.assertEqual((perfect[-2], perfect[-1]), ("██████████", "100%"))
780
784
 
File without changes
File without changes
File without changes