tuieval 0.2.0.dev75__tar.gz → 0.2.0.dev77__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 (78) hide show
  1. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/CHANGELOG.md +1 -0
  2. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/PKG-INFO +3 -1
  3. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/README.md +2 -0
  4. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/_version.py +2 -2
  5. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/cli.py +1 -1
  6. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/compare.py +58 -1
  7. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/tests/test_tuieval.py +23 -0
  8. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/.gitignore +0 -0
  9. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/LICENSE +0 -0
  10. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/RELEASING.md +0 -0
  11. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/images/brand/README.md +0 -0
  12. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/images/brand/favicon.ico +0 -0
  13. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/images/brand/readme-header.png +0 -0
  14. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/images/brand/social-preview.png +0 -0
  15. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/images/brand/tuieval-icon-1024.png +0 -0
  16. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/images/brand/tuieval-icon-128.png +0 -0
  17. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/images/brand/tuieval-icon-16.png +0 -0
  18. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/images/brand/tuieval-icon-16.svg +0 -0
  19. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/images/brand/tuieval-icon-256.png +0 -0
  20. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/images/brand/tuieval-icon-32.png +0 -0
  21. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/images/brand/tuieval-icon-48.png +0 -0
  22. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/images/brand/tuieval-icon-512.png +0 -0
  23. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/images/brand/tuieval-icon-64.png +0 -0
  24. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/images/brand/tuieval-icon-animated.svg +0 -0
  25. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/images/brand/tuieval-icon.svg +0 -0
  26. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/images/tui-answer-reasoning.png +0 -0
  27. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/images/tui-run-reasoning.png +0 -0
  28. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/images/tui-setup.png +0 -0
  29. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/models.md +0 -0
  30. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/docs/writing-packs.md +0 -0
  31. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/pyproject.toml +0 -0
  32. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/__init__.py +0 -0
  33. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/__main__.py +0 -0
  34. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/builtin.toml +0 -0
  35. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/client.py +0 -0
  36. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/doctor.py +0 -0
  37. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/engine.py +0 -0
  38. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/export.py +0 -0
  39. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/graders/__init__.py +0 -0
  40. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/graders/answer.py +0 -0
  41. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/graders/code.py +0 -0
  42. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/graders/rag.py +0 -0
  43. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/graders/reply.py +0 -0
  44. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/graders/tool_call.py +0 -0
  45. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/logs.py +0 -0
  46. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/machines.py +0 -0
  47. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/onboard.py +0 -0
  48. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/packs.py +0 -0
  49. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/profiles.py +0 -0
  50. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/pta.py +0 -0
  51. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/remove.py +0 -0
  52. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/run_evals.py +0 -0
  53. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/scaffold.py +0 -0
  54. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/selftest.py +0 -0
  55. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/templates/models.toml +0 -0
  56. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/templates/packs/answer/pack.toml +0 -0
  57. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/templates/packs/answer/system.txt +0 -0
  58. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/templates/packs/answer/tests.yaml +0 -0
  59. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/templates/packs/code/pack.toml +0 -0
  60. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/templates/packs/code/system.txt +0 -0
  61. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/templates/packs/code/tests.yaml +0 -0
  62. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/templates/packs/rag/pack.toml +0 -0
  63. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/templates/packs/rag/system.txt +0 -0
  64. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/templates/packs/rag/tests.yaml +0 -0
  65. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/templates/packs/reply/pack.toml +0 -0
  66. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/templates/packs/reply/system.txt +0 -0
  67. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/templates/packs/reply/tests.yaml +0 -0
  68. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/templates/packs/tool_call/pack.toml +0 -0
  69. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/templates/packs/tool_call/system.txt +0 -0
  70. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/templates/packs/tool_call/tests.yaml +0 -0
  71. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/templates/packs/tool_call/tools.yaml +0 -0
  72. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/tui.py +0 -0
  73. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/tune.py +0 -0
  74. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/verdict.py +0 -0
  75. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/watch_proxy.py +0 -0
  76. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/workspace.py +0 -0
  77. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/src/tuieval/yamlout.py +0 -0
  78. {tuieval-0.2.0.dev75 → tuieval-0.2.0.dev77}/tests/mock_server.py +0 -0
@@ -2,6 +2,7 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - `tuieval compare --reference <model>`: every other model paired against one on the questions both answered, per pack and overall: the difference with a 95% CI, better/tie/worse, critical failures and tokens vs the reference. `--packs` limits any compare to some packs.
5
6
  - Fix: a question description or grader reason containing `[` no longer crashes the run screen.
6
7
  - Retest asks per pack before it starts: from zero, continue or leave out. It now really starts from zero, and replaced smoke results go to history/.
7
8
  - Retest: the setup checkbox "Rerun existing results" is now "Retest (ask again, even if already answered)", and `tuieval run --retest` is the same (`--force` still works).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tuieval
3
- Version: 0.2.0.dev75
3
+ Version: 0.2.0.dev77
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
@@ -160,6 +160,7 @@ tuieval verdict # PASS / FAIL / INCONCLUSIVE per m
160
160
  tuieval report # the same with evidence, as a markdown file
161
161
  tuieval history # every verdict change over time
162
162
  tuieval compare --speed --pairwise --failures # scorecards
163
+ tuieval compare --reference <model> --packs X # each model vs one: better, tie or worse
163
164
  tuieval selftest # check every test's reference and wrong answers
164
165
  tuieval items # tests that don't separate models or look broken
165
166
  tuieval logs # follow the server log of the model running now
@@ -179,6 +180,7 @@ tuieval help
179
180
  - **Start with Production readiness.** A single critical failure already means FAIL, whatever the accuracy: a model that breaks a hard rule once in 300 answers will do it in production. The Failures tab and `tuieval report` list exactly which answers failed.
180
181
  - **INCONCLUSIVE is not "nearly passed".** It says what's missing (usually a Certify run, or more trials).
181
182
  - **Trust `tuieval compare --pairwise` over raw percentages.** Differences of one or two tests are usually noise.
183
+ - **Comparing variants of one model (fine-tunes, quantizations)?** `tuieval compare --reference <base>` pairs each against the base on the same questions and says better, tie or worse. Keep the server, quantization and settings the same, or they'll show up as differences too.
182
184
  - **Read `trunc` before accuracy.** A model that runs out of tokens isn't wrong; it's thinking too long for the budget.
183
185
  - **Watch TTFT for interactive use.** A model that is 5% more accurate but takes 3 s longer to start answering may be the worse choice.
184
186
 
@@ -134,6 +134,7 @@ tuieval verdict # PASS / FAIL / INCONCLUSIVE per m
134
134
  tuieval report # the same with evidence, as a markdown file
135
135
  tuieval history # every verdict change over time
136
136
  tuieval compare --speed --pairwise --failures # scorecards
137
+ tuieval compare --reference <model> --packs X # each model vs one: better, tie or worse
137
138
  tuieval selftest # check every test's reference and wrong answers
138
139
  tuieval items # tests that don't separate models or look broken
139
140
  tuieval logs # follow the server log of the model running now
@@ -153,6 +154,7 @@ tuieval help
153
154
  - **Start with Production readiness.** A single critical failure already means FAIL, whatever the accuracy: a model that breaks a hard rule once in 300 answers will do it in production. The Failures tab and `tuieval report` list exactly which answers failed.
154
155
  - **INCONCLUSIVE is not "nearly passed".** It says what's missing (usually a Certify run, or more trials).
155
156
  - **Trust `tuieval compare --pairwise` over raw percentages.** Differences of one or two tests are usually noise.
157
+ - **Comparing variants of one model (fine-tunes, quantizations)?** `tuieval compare --reference <base>` pairs each against the base on the same questions and says better, tie or worse. Keep the server, quantization and settings the same, or they'll show up as differences too.
156
158
  - **Read `trunc` before accuracy.** A model that runs out of tokens isn't wrong; it's thinking too long for the budget.
157
159
  - **Watch TTFT for interactive use.** A model that is 5% more accurate but takes 3 s longer to start answering may be the worse choice.
158
160
 
@@ -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.dev75'
22
- __version_tuple__ = version_tuple = (0, 2, 0, 'dev75')
21
+ __version__ = version = '0.2.0.dev77'
22
+ __version_tuple__ = version_tuple = (0, 2, 0, 'dev77')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -13,7 +13,7 @@ Start here
13
13
  tuieval verdict which model is ready for what, and what to run next
14
14
 
15
15
  Day to day
16
- tuieval compare [...] scorecards (--speed, --pairwise, --failures)
16
+ tuieval compare [...] scorecards (--speed, --pairwise, --reference M, --failures)
17
17
  tuieval pta the PTA index: parsimony (tokens), time and accuracy per model, as bars
18
18
  tuieval report the verdicts with their evidence, as a markdown file
19
19
  tuieval history every verdict change, newest first
@@ -5,6 +5,8 @@ Usage:
5
5
  tuieval compare --speed # speed & tokens table (TTFT, tok/s, tokens, memory)
6
6
  tuieval compare --machine m3max-64gb # speed on another machine (measured there, or projected)
7
7
  tuieval compare --pairwise --failures # is each difference real? which tests failed?
8
+ tuieval compare --reference MODEL # every model paired against one: better, tie or worse
9
+ tuieval compare --packs coding,rag # only these packs (works with every option)
8
10
  tuieval compare results/some-model/*.json # specific files
9
11
  """
10
12
  import collections
@@ -409,6 +411,49 @@ def pairwise(rows, rng=None):
409
411
  return out
410
412
 
411
413
 
414
+ def vs_reference(rows, ref, rng=None):
415
+ """Every other model against one reference model, paired on the questions both answered:
416
+ [{model, pack ("all" = every pack together), n, diff, lo, hi, verdict, crit, ref_crit, tokens}].
417
+
418
+ diff is the mean over shared questions of (pass rate - the reference's pass rate), with a 95%
419
+ bootstrap CI; verdict is "better" or "worse" when the CI excludes 0, else "tie". crit/ref_crit
420
+ count critical failures on those questions; tokens is the median of per-question token ratios
421
+ (median tokens over repeats, model / reference), so 0.5 means half the tokens."""
422
+ rng = rng or random.Random(0)
423
+ by = collections.defaultdict(lambda: collections.defaultdict(list)) # model -> test -> rows
424
+ suite = {}
425
+ for r in rows:
426
+ by[r["model"]][r["test"]].append(r)
427
+ suite[r["test"]] = r["suite"]
428
+ if ref not in by:
429
+ return []
430
+ out = []
431
+ for m in sorted(set(by) - {ref}):
432
+ shared = sorted(set(by[m]) & set(by[ref]))
433
+ for pack in sorted({suite[t] for t in shared}) + ["all"]:
434
+ tests = [t for t in shared if pack in ("all", suite[t])]
435
+ if not tests:
436
+ continue
437
+ rate = lambda model, t: statistics.fmean(r["ok"] for r in by[model][t])
438
+ diffs = [rate(m, t) - rate(ref, t) for t in tests]
439
+ lo, hi = bootstrap_ci(diffs, rng)
440
+ crit = lambda model: sum(r.get("severity") == "critical" for t in tests for r in by[model][t])
441
+ ratios = [a / b for t in tests
442
+ if (a := median(r["tokens"] for r in by[m][t])) and (b := median(r["tokens"] for r in by[ref][t]))]
443
+ out.append({"model": m, "pack": pack, "n": len(tests), "diff": statistics.fmean(diffs), "lo": lo, "hi": hi,
444
+ "verdict": "better" if lo > 0 else "worse" if hi < 0 else "tie",
445
+ "crit": crit(m), "ref_crit": crit(ref), "tokens": median(ratios)})
446
+ return out
447
+
448
+
449
+ def vs_reference_table(results, ref):
450
+ header = ["model", "pack", "questions", f"vs {ref}", "95% CI", "verdict", "critical (vs ref)", "tokens vs ref"]
451
+ table = [[x["model"], x["pack"], x["n"], f"{100 * x['diff']:+.1f} pts", f"{100 * x['lo']:+.1f} to {100 * x['hi']:+.1f}",
452
+ x["verdict"].upper() if x["pack"] == "all" else x["verdict"], f"{x['crit']} ({x['ref_crit']})",
453
+ _fmt(x["tokens"], ".2f") + ("×" if x["tokens"] is not None else "")] for x in results]
454
+ return header, table
455
+
456
+
412
457
  def failures(rows):
413
458
  return [(r["model"], r.get("name", r["test"]), ("TRUNCATED " if r["truncated"] and not r["reason"].startswith("TRUNCATED")
414
459
  else "") + r["reason"], r.get("difficulty", "unrated"))
@@ -499,10 +544,22 @@ def main(argv):
499
544
  flags = {a for a in argv if a.startswith("--")}
500
545
  log_dir = argv[argv.index("--logs") + 1] if "--logs" in argv else None
501
546
  machine_arg = argv[argv.index("--machine") + 1] if "--machine" in argv else None
502
- paths = [a for a in argv if not a.startswith("--") and a not in (log_dir, machine_arg)] or default_paths()
547
+ ref = argv[argv.index("--reference") + 1] if "--reference" in argv else None
548
+ only_packs = argv[argv.index("--packs") + 1].split(",") if "--packs" in argv else None
549
+ values = (log_dir, machine_arg, ref, ",".join(only_packs) if only_packs else None)
550
+ paths = [a for a in argv if not a.startswith("--") and a not in values] or default_paths()
551
+ if only_packs:
552
+ paths = [p for p in paths if os.path.basename(p)[:-len(".json")] in only_packs]
503
553
  if not paths:
504
554
  sys.exit(__doc__ + "\n(no results found in results/<model>/<pack>.json: run some evals first)")
505
555
  rows, infos = load(paths)
556
+ if ref:
557
+ if ref not in {r["model"] for r in rows}:
558
+ sys.exit(f"no results for reference model {ref!r}" + (f" in {', '.join(only_packs)}" if only_packs else ""))
559
+ print(f"Paired against {ref} on the questions both answered (pass rate per question, repeats averaged;"
560
+ "\n95% bootstrap CI over questions; BETTER/WORSE only when the CI excludes 0):")
561
+ print_table(*vs_reference_table(vs_reference(rows, ref, random.Random(0)), ref))
562
+ return
506
563
  rng = random.Random(0)
507
564
  from . import packs as packs_mod
508
565
  packs_mod.load_packs(errors=[]) # loads the workspace's graders, and their Scorecard columns
@@ -209,6 +209,29 @@ class Units(unittest.TestCase):
209
209
  self.assertEqual(compare.settings_notes(infos),
210
210
  ["Settings differ: effort medium on 4 models; penalty 1.5 on j."])
211
211
 
212
+ def test_vs_reference_pairs_on_shared_questions(self):
213
+ from tuieval import compare
214
+ row = lambda m, pack, t, ok, tokens=100, sev=None, rep=0: {
215
+ "model": m, "suite": pack, "test": f"{pack}: {t}", "ok": ok, "tokens": tokens, "severity": sev, "repeat": rep}
216
+ rows = [row("ref", "a", f"q{i}", i < 10) for i in range(20)] # ref: 10/20 on a
217
+ rows += [row("good", "a", f"q{i}", True, tokens=50) for i in range(20)] # good: 20/20, half the tokens
218
+ rows += [row("same", "a", f"q{i}", i < 10) for i in range(20)]
219
+ rows += [row("bad", "a", f"q{i}", False, sev="critical" if i == 0 else None) for i in range(20)]
220
+ rows += [row("ref", "b", "x", True), row("good", "b", "x", False), row("good", "b", "x", True, rep=1)]
221
+ rows += [row("good", "b", "only-good", True)] # not shared: ignored
222
+ res = {(x["model"], x["pack"]): x for x in compare.vs_reference(rows, "ref")}
223
+ self.assertEqual(res["good", "a"]["verdict"], "better")
224
+ self.assertAlmostEqual(res["good", "a"]["diff"], 0.5)
225
+ self.assertEqual(res["good", "a"]["tokens"], 0.5)
226
+ self.assertEqual(res["same", "a"]["verdict"], "tie")
227
+ self.assertEqual((res["bad", "a"]["verdict"], res["bad", "a"]["crit"], res["bad", "a"]["ref_crit"]), ("worse", 1, 0))
228
+ self.assertEqual(res["good", "b"]["n"], 1)
229
+ self.assertAlmostEqual(res["good", "b"]["diff"], -0.5) # repeats averaged
230
+ self.assertEqual(res["good", "all"]["n"], 21)
231
+ self.assertEqual(compare.vs_reference(rows, "nobody"), [])
232
+ header, table = compare.vs_reference_table([res["good", "all"]], "ref")
233
+ self.assertEqual(table[0][5], "BETTER")
234
+
212
235
  def test_eta_goes_pack_by_pack(self):
213
236
  from tuieval.tui import RunScreen
214
237
  job = lambda key, label, total, done=0, status="waiting": types.SimpleNamespace(
File without changes
File without changes