tuieval 0.2.0.dev105__tar.gz → 0.2.0.dev107__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 (79) hide show
  1. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/CHANGELOG.md +1 -0
  2. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/PKG-INFO +1 -1
  3. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/_version.py +2 -2
  4. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/tui.py +29 -7
  5. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/tests/test_tuieval.py +16 -1
  6. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/.gitignore +0 -0
  7. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/LICENSE +0 -0
  8. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/README.md +0 -0
  9. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/RELEASING.md +0 -0
  10. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/images/brand/README.md +0 -0
  11. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/images/brand/favicon.ico +0 -0
  12. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/images/brand/readme-header.png +0 -0
  13. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/images/brand/social-preview.png +0 -0
  14. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/images/brand/tuieval-icon-1024.png +0 -0
  15. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/images/brand/tuieval-icon-128.png +0 -0
  16. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/images/brand/tuieval-icon-16.png +0 -0
  17. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/images/brand/tuieval-icon-16.svg +0 -0
  18. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/images/brand/tuieval-icon-256.png +0 -0
  19. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/images/brand/tuieval-icon-32.png +0 -0
  20. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/images/brand/tuieval-icon-48.png +0 -0
  21. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/images/brand/tuieval-icon-512.png +0 -0
  22. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/images/brand/tuieval-icon-64.png +0 -0
  23. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/images/brand/tuieval-icon-animated.svg +0 -0
  24. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/images/brand/tuieval-icon.svg +0 -0
  25. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/images/tui-answer-reasoning.png +0 -0
  26. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/images/tui-run-reasoning.png +0 -0
  27. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/images/tui-setup.png +0 -0
  28. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/models.md +0 -0
  29. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/docs/writing-packs.md +0 -0
  30. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/pyproject.toml +0 -0
  31. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/__init__.py +0 -0
  32. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/__main__.py +0 -0
  33. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/builtin.toml +0 -0
  34. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/cli.py +0 -0
  35. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/client.py +0 -0
  36. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/compare.py +0 -0
  37. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/doctor.py +0 -0
  38. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/engine.py +0 -0
  39. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/export.py +0 -0
  40. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/graders/__init__.py +0 -0
  41. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/graders/answer.py +0 -0
  42. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/graders/code.py +0 -0
  43. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/graders/rag.py +0 -0
  44. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/graders/reply.py +0 -0
  45. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/graders/tool_call.py +0 -0
  46. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/insights.py +0 -0
  47. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/logs.py +0 -0
  48. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/machines.py +0 -0
  49. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/onboard.py +0 -0
  50. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/packs.py +0 -0
  51. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/profiles.py +0 -0
  52. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/pta.py +0 -0
  53. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/remove.py +0 -0
  54. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/run_evals.py +0 -0
  55. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/scaffold.py +0 -0
  56. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/selftest.py +0 -0
  57. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/templates/models.toml +0 -0
  58. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/templates/packs/answer/pack.toml +0 -0
  59. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/templates/packs/answer/system.txt +0 -0
  60. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/templates/packs/answer/tests.yaml +0 -0
  61. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/templates/packs/code/pack.toml +0 -0
  62. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/templates/packs/code/system.txt +0 -0
  63. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/templates/packs/code/tests.yaml +0 -0
  64. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/templates/packs/rag/pack.toml +0 -0
  65. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/templates/packs/rag/system.txt +0 -0
  66. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/templates/packs/rag/tests.yaml +0 -0
  67. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/templates/packs/reply/pack.toml +0 -0
  68. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/templates/packs/reply/system.txt +0 -0
  69. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/templates/packs/reply/tests.yaml +0 -0
  70. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/templates/packs/tool_call/pack.toml +0 -0
  71. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/templates/packs/tool_call/system.txt +0 -0
  72. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/templates/packs/tool_call/tests.yaml +0 -0
  73. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/templates/packs/tool_call/tools.yaml +0 -0
  74. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/tune.py +0 -0
  75. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/verdict.py +0 -0
  76. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/watch_proxy.py +0 -0
  77. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/workspace.py +0 -0
  78. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/src/tuieval/yamlout.py +0 -0
  79. {tuieval-0.2.0.dev105 → tuieval-0.2.0.dev107}/tests/mock_server.py +0 -0
@@ -2,6 +2,7 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - Results → Failures: a Models button filters the list (the same pick as the other tabs), with the number of failures shown.
5
6
  - Fix: an answer followed by a stray `</think>` (or a stray code fence after it) was graded as empty; the answer before the tag is graded now. The code grader also reads a doubled ```` ```python ```` fence as one.
6
7
  - Run screen: `x` removes the highlighted pack from the run going if it hasn't started (the reverse of adding it); a model left with nothing isn't started.
7
8
  - `k` (skip model) asks first, naming the model. Yes/no dialogs start on No; `y` answers yes, `n` or Esc no.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tuieval
3
- Version: 0.2.0.dev105
3
+ Version: 0.2.0.dev107
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
@@ -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.dev105'
22
- __version_tuple__ = version_tuple = (0, 2, 0, 'dev105')
21
+ __version__ = version = '0.2.0.dev107'
22
+ __version_tuple__ = version_tuple = (0, 2, 0, 'dev107')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -454,7 +454,8 @@ with the model's reasoning and answer as they stream, then recent results with t
454
454
  Results open here.
455
455
  Production readiness one verdict per model and use case; the table says why and what to run next
456
456
  Per question every question, model by model (filters: pack, level, Models disagree)
457
- Failures every wrong answer with the grader's reason; enter opens it in full
457
+ Failures every wrong answer with the grader's reason (filter: Models); enter opens it
458
+ in full
458
459
 
459
460
  Click a column name to sort a table by it; click it again to reverse. Empty cells stay last.
460
461
 
@@ -2825,6 +2826,9 @@ class ResultsScreen(Screen):
2825
2826
  yield Static(id="pq-legend")
2826
2827
  yield DataTable(id="per-question", zebra_stripes=True, cursor_type="cell")
2827
2828
  with TabPane("Failures", id="tab-failures"):
2829
+ with Horizontal(id="fail-controls"):
2830
+ yield Button("Models: all", id="fail-models")
2831
+ yield Static(id="fail-count")
2828
2832
  yield Static("[dim]Enter on a row shows the whole answer, its reasoning and what was checked.[/dim]")
2829
2833
  yield DataTable(id="failures", zebra_stripes=True, cursor_type="row")
2830
2834
  yield Footer()
@@ -2902,12 +2906,7 @@ class ResultsScreen(Screen):
2902
2906
  info.update(f"{len(paths)} {'Smoke ' if smoke else ''}result files in {os.path.relpath(results_dir)}/" + banner
2903
2907
  + "".join(f"\n[yellow]⚠ {n}[/yellow]" for n in notes))
2904
2908
  self.rows, self.infos = rows, infos
2905
- f = self.query_one("#failures", DataTable)
2906
- f.add_columns("Model", "Level", "Test", "Reason")
2907
- # compare.failures lists the failed rows in order, so they line up with fail_rows
2908
- self.fail_rows = [r for r in rows if not r["ok"]]
2909
- for i, (model, test, reason, level) in enumerate(compare.failures(rows)):
2910
- f.add_row(model, diff_badge(level), rich_escape(test), rich_escape(reason[:130]), key=str(i))
2909
+ self.fill_failures()
2911
2910
  self.fill_per_question()
2912
2911
  self.fill_pta()
2913
2912
  self.fill_readiness(results_dir)
@@ -2916,6 +2915,24 @@ class ResultsScreen(Screen):
2916
2915
  self.fill_insights()
2917
2916
  self.sort_tables()
2918
2917
 
2918
+ def fill_failures(self):
2919
+ """Every wrong answer, of the models picked (Models: shared with the other tabs)."""
2920
+ f = self.query_one("#failures", DataTable)
2921
+ f.clear(columns=True)
2922
+ f.add_columns("Model", "Level", "Test", "Reason")
2923
+ every = {r["model"] for r in self.rows}
2924
+ chosen = {m for m in self.app.pq_models if m in every}
2925
+ rows = [r for r in self.rows if not chosen or r["model"] in chosen]
2926
+ # compare.failures lists the failed rows in order, so they line up with fail_rows
2927
+ self.fail_rows = [r for r in rows if not r["ok"]]
2928
+ for i, (model, test, reason, level) in enumerate(compare.failures(rows)):
2929
+ f.add_row(model, diff_badge(level), rich_escape(test), rich_escape(reason[:130]), key=str(i))
2930
+ self.query_one("#fail-models", Button).label = f"Models: {len(chosen)} picked" if chosen else "Models: all"
2931
+ n = len(self.fail_rows)
2932
+ self.query_one("#fail-count", Static).update(
2933
+ f"{n} failure{'s' * (n != 1)}" + (f" of {len(chosen)} model{'s' * (len(chosen) != 1)}" if chosen else ""))
2934
+ self.sort_table(f)
2935
+
2919
2936
  @on(DataTable.RowSelected, "#failures")
2920
2937
  def failure_selected(self, event):
2921
2938
  # in the order shown, so [ and ] step through them as sorted
@@ -2979,6 +2996,7 @@ class ResultsScreen(Screen):
2979
2996
  @on(Button.Pressed, "#pq-models")
2980
2997
  @on(Button.Pressed, "#pta-models")
2981
2998
  @on(Button.Pressed, "#ins-models")
2999
+ @on(Button.Pressed, "#fail-models")
2982
3000
  def pick_models(self):
2983
3001
  counts = {}
2984
3002
  for t in getattr(self, "pq_all_tests", []):
@@ -2997,6 +3015,7 @@ class ResultsScreen(Screen):
2997
3015
  # after the picker has closed: a table rebuilt under a modal keeps its header-only widths
2998
3016
  self.call_after_refresh(self.fill_per_question)
2999
3017
  self.call_after_refresh(self.fill_pta)
3018
+ self.call_after_refresh(self.fill_failures)
3000
3019
  self.insights_stale = True
3001
3020
  if self.query_one(TabbedContent).active == "tab-insights":
3002
3021
  self.call_after_refresh(self.fill_insights)
@@ -3397,6 +3416,9 @@ class EvalsApp(App):
3397
3416
  #pq-controls Checkbox { width: auto; }
3398
3417
  #pq-controls Select { width: 22; margin-right: 1; }
3399
3418
  #pq-models { margin-right: 1; min-width: 20; }
3419
+ #fail-controls { height: 3; }
3420
+ #fail-models { margin-right: 1; min-width: 20; }
3421
+ #fail-count { width: auto; padding: 1 1; }
3400
3422
  #pq-compare-title { padding: 0 1; height: auto; }
3401
3423
  #pq-summary { height: auto; } /* every picked model, no scrolling: the tab scrolls instead */
3402
3424
  #pq-legend { padding: 0 1; height: auto; }
@@ -1571,6 +1571,17 @@ class PTAIndex(unittest.TestCase):
1571
1571
  app.screen.query_one(TabbedContent).active = "tab-failures"
1572
1572
  await pilot.pause(0.3)
1573
1573
  f = app.screen.query_one("#failures", DataTable)
1574
+ count = lambda: str(app.screen.query_one("#fail-count", Static).render())
1575
+ fails = {"all": [f.row_count, count()]}
1576
+ for m in ("good", "hosted"): # Models: only that model's failures
1577
+ app.pq_models = {m}
1578
+ app.screen.fill_failures()
1579
+ await pilot.pause(0.2)
1580
+ fails[m] = [f.row_count, count(), str(app.screen.query_one("#fail-models").label),
1581
+ sorted({str(f.get_row_at(r)[0]) for r in range(f.row_count)})]
1582
+ app.pq_models = set()
1583
+ app.screen.fill_failures()
1584
+ await pilot.pause(0.2)
1574
1585
  await click_header(f, "Test")
1575
1586
  sorts["tests"], _ = await click_header(f, "Test")
1576
1587
  sorts["tests"] = [str(f.get_row_at(r)[2]) for r in range(f.row_count)]
@@ -1581,7 +1592,7 @@ class PTAIndex(unittest.TestCase):
1581
1592
  s = app.screen
1582
1593
  sorts["opened"] = s.entries[s.index]["record"]["test"]
1583
1594
  print(json.dumps({"rows": rows, "bars": "P parsimony" in tri, "disagree": [both, alone],
1584
- "tab": tab, "sorts": sorts}))
1595
+ "tab": tab, "sorts": sorts, "fails": fails}))
1585
1596
  asyncio.run(go())
1586
1597
  """)
1587
1598
  p = subprocess.run([sys.executable, "-c", code], capture_output=True, text=True, timeout=300,
@@ -1596,6 +1607,10 @@ class PTAIndex(unittest.TestCase):
1596
1607
  self.assertEqual((s["up"], s["down"], s["kept"]), (["hosted", "good"], ["good", "hosted"], ["good", "hosted"]))
1597
1608
  self.assertIn("answers right ▼", s["labels"])
1598
1609
  self.assertEqual((s["tests"][0], s["opened"]), ("apps: d", "d"))
1610
+ fails = got["fails"]
1611
+ self.assertEqual(fails["all"], [4, "4 failures"])
1612
+ self.assertEqual(fails["good"], [0, "0 failures of 1 model", "Models: 1 picked", []])
1613
+ self.assertEqual(fails["hosted"], [4, "4 failures of 1 model", "Models: 1 picked", ["hosted"]])
1599
1614
 
1600
1615
 
1601
1616
  class Unavailable(unittest.TestCase):
File without changes
File without changes