tuieval 0.2.0.dev107__tar.gz → 0.2.0.dev108__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.dev107 → tuieval-0.2.0.dev108}/CHANGELOG.md +1 -0
  2. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/PKG-INFO +1 -1
  3. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/_version.py +2 -2
  4. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/tui.py +34 -7
  5. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/tests/test_tuieval.py +41 -0
  6. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/.gitignore +0 -0
  7. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/LICENSE +0 -0
  8. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/README.md +0 -0
  9. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/RELEASING.md +0 -0
  10. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/images/brand/README.md +0 -0
  11. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/images/brand/favicon.ico +0 -0
  12. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/images/brand/readme-header.png +0 -0
  13. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/images/brand/social-preview.png +0 -0
  14. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/images/brand/tuieval-icon-1024.png +0 -0
  15. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/images/brand/tuieval-icon-128.png +0 -0
  16. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/images/brand/tuieval-icon-16.png +0 -0
  17. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/images/brand/tuieval-icon-16.svg +0 -0
  18. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/images/brand/tuieval-icon-256.png +0 -0
  19. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/images/brand/tuieval-icon-32.png +0 -0
  20. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/images/brand/tuieval-icon-48.png +0 -0
  21. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/images/brand/tuieval-icon-512.png +0 -0
  22. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/images/brand/tuieval-icon-64.png +0 -0
  23. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/images/brand/tuieval-icon-animated.svg +0 -0
  24. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/images/brand/tuieval-icon.svg +0 -0
  25. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/images/tui-answer-reasoning.png +0 -0
  26. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/images/tui-run-reasoning.png +0 -0
  27. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/images/tui-setup.png +0 -0
  28. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/models.md +0 -0
  29. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/docs/writing-packs.md +0 -0
  30. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/pyproject.toml +0 -0
  31. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/__init__.py +0 -0
  32. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/__main__.py +0 -0
  33. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/builtin.toml +0 -0
  34. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/cli.py +0 -0
  35. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/client.py +0 -0
  36. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/compare.py +0 -0
  37. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/doctor.py +0 -0
  38. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/engine.py +0 -0
  39. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/export.py +0 -0
  40. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/graders/__init__.py +0 -0
  41. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/graders/answer.py +0 -0
  42. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/graders/code.py +0 -0
  43. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/graders/rag.py +0 -0
  44. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/graders/reply.py +0 -0
  45. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/graders/tool_call.py +0 -0
  46. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/insights.py +0 -0
  47. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/logs.py +0 -0
  48. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/machines.py +0 -0
  49. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/onboard.py +0 -0
  50. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/packs.py +0 -0
  51. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/profiles.py +0 -0
  52. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/pta.py +0 -0
  53. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/remove.py +0 -0
  54. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/run_evals.py +0 -0
  55. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/scaffold.py +0 -0
  56. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/selftest.py +0 -0
  57. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/templates/models.toml +0 -0
  58. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/templates/packs/answer/pack.toml +0 -0
  59. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/templates/packs/answer/system.txt +0 -0
  60. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/templates/packs/answer/tests.yaml +0 -0
  61. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/templates/packs/code/pack.toml +0 -0
  62. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/templates/packs/code/system.txt +0 -0
  63. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/templates/packs/code/tests.yaml +0 -0
  64. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/templates/packs/rag/pack.toml +0 -0
  65. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/templates/packs/rag/system.txt +0 -0
  66. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/templates/packs/rag/tests.yaml +0 -0
  67. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/templates/packs/reply/pack.toml +0 -0
  68. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/templates/packs/reply/system.txt +0 -0
  69. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/templates/packs/reply/tests.yaml +0 -0
  70. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/templates/packs/tool_call/pack.toml +0 -0
  71. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/templates/packs/tool_call/system.txt +0 -0
  72. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/templates/packs/tool_call/tests.yaml +0 -0
  73. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/templates/packs/tool_call/tools.yaml +0 -0
  74. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/tune.py +0 -0
  75. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/verdict.py +0 -0
  76. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/watch_proxy.py +0 -0
  77. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/workspace.py +0 -0
  78. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/src/tuieval/yamlout.py +0 -0
  79. {tuieval-0.2.0.dev107 → tuieval-0.2.0.dev108}/tests/mock_server.py +0 -0
@@ -2,6 +2,7 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - The Models heading shows how many are selected, naming any the filter hides; Start asks first when some are hidden.
5
6
  - Results → Failures: a Models button filters the list (the same pick as the other tabs), with the number of failures shown.
6
7
  - 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.
7
8
  - 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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tuieval
3
- Version: 0.2.0.dev107
3
+ Version: 0.2.0.dev108
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.dev107'
22
- __version_tuple__ = version_tuple = (0, 2, 0, 'dev107')
21
+ __version__ = version = '0.2.0.dev108'
22
+ __version_tuple__ = version_tuple = (0, 2, 0, 'dev108')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -776,10 +776,7 @@ class SetupScreen(Screen):
776
776
  self.verdicts = {}
777
777
  self.app.log(f"verdicts failed: {err!r}")
778
778
  codes = group_codes(e)
779
- self.query_one("#models-legend", Label).update(
780
- "[b]Models[/b] [dim][green]✓[/green] pass [red]✗[/red] fail [yellow]?[/yellow] inconclusive "
781
- "grey = earlier · x hides[/dim]\n[dim]"
782
- + " ".join(f"{c} {g}" for g, c in codes.items()) + "[/dim]")
779
+ self.legend_codes = codes
783
780
  n_hidden = sum(m["label"] in hidden for m in e.cfg["models"])
784
781
  self.query_one("#show-hidden", Checkbox).label = f"Show hidden ({n_hidden})"
785
782
  sections = {}
@@ -819,8 +816,27 @@ class SetupScreen(Screen):
819
816
  at = values.index(keep) if keep in values else min(keep_at or 0, models.option_count - 1)
820
817
  models.highlighted = min((i for i in enabled if i >= at), default=enabled[-1])
821
818
  self.show_detail()
819
+ self.update_legend()
822
820
  self.update_estimate()
823
821
 
822
+ def unseen_models(self):
823
+ """Ticked models the list doesn't show right now (the filter leaves them out)."""
824
+ models = self.query_one("#models", SelectionList)
825
+ visible = {models.get_option_at_index(i).value for i in range(models.option_count)}
826
+ return [m["label"] for m in self.app.engine.cfg["models"]
827
+ if m["label"] in self.selected_models and m["label"] not in visible]
828
+
829
+ def update_legend(self):
830
+ """The Models heading: how many are ticked (and which ones the filter hides), then the key."""
831
+ n, unseen = len(self.selected_models), self.unseen_models()
832
+ count = f"[b]{n}[/b] selected" if n else "[dim]none selected[/dim]"
833
+ if unseen:
834
+ count += f" [yellow]({len(unseen)} not shown: {rich_escape(', '.join(unseen))})[/yellow]"
835
+ self.query_one("#models-legend", Label).update(
836
+ f"[b]Models[/b] {count} [dim][green]✓[/green] pass [red]✗[/red] fail [yellow]?[/yellow] "
837
+ "inconclusive grey = earlier · x hides[/dim]\n[dim]"
838
+ + " ".join(f"{c} {g}" for g, c in getattr(self, "legend_codes", {}).items()) + "[/dim]")
839
+
824
840
  @on(SelectionList.SelectionHighlighted, "#models")
825
841
  def show_detail(self, *_):
826
842
  """Everything about the highlighted model: verdicts in full, serving, per-pack results."""
@@ -894,6 +910,7 @@ class SetupScreen(Screen):
894
910
  return
895
911
  visible = {models.get_option_at_index(i).value for i in range(models.option_count)}
896
912
  self.selected_models = (self.selected_models - visible) | set(models.selected)
913
+ self.update_legend()
897
914
  self.update_estimate()
898
915
 
899
916
  def tier(self):
@@ -1110,10 +1127,20 @@ class SetupScreen(Screen):
1110
1127
  self.notify(f"Nothing to run: {item.problem}.", severity="warning")
1111
1128
  return
1112
1129
  self.app.start_or_queue(item)
1113
- if rows:
1114
- self.app.push_screen(RetestScreen(rows, force, rerun), go)
1130
+ def asked(yes=True):
1131
+ if not yes:
1132
+ return
1133
+ if rows:
1134
+ self.app.push_screen(RetestScreen(rows, force, rerun), go)
1135
+ else:
1136
+ go({})
1137
+ unseen = self.unseen_models()
1138
+ if unseen: # ticks the filter hides are easy to forget: name them before anything starts
1139
+ self.app.push_screen(ConfirmScreen(
1140
+ f"Start {len(labels)} model(s)? {len(unseen)} of them the filter hides:\n "
1141
+ + rich_escape("\n ".join(unseen) + f"\n\nAll selected: {', '.join(labels)}")), asked)
1115
1142
  else:
1116
- go({})
1143
+ asked()
1117
1144
 
1118
1145
  def action_tune(self):
1119
1146
  e = self.app.engine
@@ -1130,6 +1130,47 @@ class AddToRun(unittest.TestCase):
1130
1130
  self.assertEqual(fewer, 4) # its 4 questions are no longer counted
1131
1131
  self.assertEqual(out["running"], [False, True])
1132
1132
 
1133
+ def test_tui_counts_ticked_models_and_names_filtered_ones_before_start(self):
1134
+ code = textwrap.dedent("""
1135
+ import asyncio, json
1136
+ from tuieval.tui import EvalsApp, ConfirmScreen
1137
+ from textual.widgets import SelectionList, Input, Label
1138
+ async def go():
1139
+ app = EvalsApp({})
1140
+ async with app.run_test(size=(160, 50)) as pilot:
1141
+ await pilot.pause()
1142
+ s, out = app.screen, {}
1143
+ legend = lambda: str(s.query_one("#models-legend", Label).render())
1144
+ out["none"] = legend()
1145
+ s.query_one("#suites", SelectionList).select("apps")
1146
+ models = s.query_one("#models", SelectionList)
1147
+ models.select("a")
1148
+ models.select("b")
1149
+ await pilot.pause()
1150
+ out["two"] = legend()
1151
+ s.query_one("#filter", Input).value = "zzz-nothing"
1152
+ await pilot.pause()
1153
+ out["filtered"] = legend()
1154
+ s.action_start()
1155
+ await pilot.pause()
1156
+ out["asked"] = isinstance(app.screen, ConfirmScreen) and str(app.screen.question)
1157
+ await pilot.press("n")
1158
+ await pilot.pause()
1159
+ out["cancelled"] = app.screen is s and not app.is_busy()
1160
+ print(json.dumps(out))
1161
+ asyncio.run(go())
1162
+ """)
1163
+ p = subprocess.run([sys.executable, "-c", code], capture_output=True, text=True, timeout=600,
1164
+ env=dict(os.environ, TUIEVAL_HOME=self.ws, TUIEVAL_DETECT_PORTS=""))
1165
+ out = json.loads(p.stdout.strip().splitlines()[-1]) if p.stdout.strip() else p.stderr
1166
+ self.assertIsInstance(out, dict, out)
1167
+ self.assertIn("none selected", out["none"])
1168
+ self.assertIn("2 selected", out["two"])
1169
+ self.assertNotIn("not shown", out["two"])
1170
+ self.assertIn("2 not shown: a, b", out["filtered"])
1171
+ self.assertIn("All selected: a, b", out["asked"])
1172
+ self.assertTrue(out["cancelled"])
1173
+
1133
1174
  def test_tui_asks_before_skipping_a_model(self):
1134
1175
  path = os.path.join(self.ws, "models.toml")
1135
1176
  write(path, read(path).replace('"--delay", "0.2"', '"--delay", "3"')) # still running while it's asked
File without changes
File without changes