tuieval 0.2.0.dev86__tar.gz → 0.2.0.dev87__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.dev86 → tuieval-0.2.0.dev87}/CHANGELOG.md +1 -0
  2. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/PKG-INFO +1 -1
  3. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/_version.py +2 -2
  4. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/tui.py +20 -0
  5. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/tests/test_tuieval.py +65 -0
  6. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/.gitignore +0 -0
  7. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/LICENSE +0 -0
  8. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/README.md +0 -0
  9. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/RELEASING.md +0 -0
  10. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/images/brand/README.md +0 -0
  11. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/images/brand/favicon.ico +0 -0
  12. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/images/brand/readme-header.png +0 -0
  13. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/images/brand/social-preview.png +0 -0
  14. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/images/brand/tuieval-icon-1024.png +0 -0
  15. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/images/brand/tuieval-icon-128.png +0 -0
  16. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/images/brand/tuieval-icon-16.png +0 -0
  17. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/images/brand/tuieval-icon-16.svg +0 -0
  18. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/images/brand/tuieval-icon-256.png +0 -0
  19. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/images/brand/tuieval-icon-32.png +0 -0
  20. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/images/brand/tuieval-icon-48.png +0 -0
  21. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/images/brand/tuieval-icon-512.png +0 -0
  22. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/images/brand/tuieval-icon-64.png +0 -0
  23. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/images/brand/tuieval-icon-animated.svg +0 -0
  24. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/images/brand/tuieval-icon.svg +0 -0
  25. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/images/tui-answer-reasoning.png +0 -0
  26. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/images/tui-run-reasoning.png +0 -0
  27. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/images/tui-setup.png +0 -0
  28. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/models.md +0 -0
  29. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/docs/writing-packs.md +0 -0
  30. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/pyproject.toml +0 -0
  31. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/__init__.py +0 -0
  32. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/__main__.py +0 -0
  33. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/builtin.toml +0 -0
  34. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/cli.py +0 -0
  35. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/client.py +0 -0
  36. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/compare.py +0 -0
  37. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/doctor.py +0 -0
  38. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/engine.py +0 -0
  39. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/export.py +0 -0
  40. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/graders/__init__.py +0 -0
  41. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/graders/answer.py +0 -0
  42. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/graders/code.py +0 -0
  43. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/graders/rag.py +0 -0
  44. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/graders/reply.py +0 -0
  45. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/graders/tool_call.py +0 -0
  46. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/logs.py +0 -0
  47. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/machines.py +0 -0
  48. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/onboard.py +0 -0
  49. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/packs.py +0 -0
  50. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/profiles.py +0 -0
  51. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/pta.py +0 -0
  52. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/remove.py +0 -0
  53. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/run_evals.py +0 -0
  54. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/scaffold.py +0 -0
  55. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/selftest.py +0 -0
  56. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/templates/models.toml +0 -0
  57. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/templates/packs/answer/pack.toml +0 -0
  58. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/templates/packs/answer/system.txt +0 -0
  59. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/templates/packs/answer/tests.yaml +0 -0
  60. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/templates/packs/code/pack.toml +0 -0
  61. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/templates/packs/code/system.txt +0 -0
  62. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/templates/packs/code/tests.yaml +0 -0
  63. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/templates/packs/rag/pack.toml +0 -0
  64. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/templates/packs/rag/system.txt +0 -0
  65. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/templates/packs/rag/tests.yaml +0 -0
  66. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/templates/packs/reply/pack.toml +0 -0
  67. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/templates/packs/reply/system.txt +0 -0
  68. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/templates/packs/reply/tests.yaml +0 -0
  69. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/templates/packs/tool_call/pack.toml +0 -0
  70. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/templates/packs/tool_call/system.txt +0 -0
  71. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/templates/packs/tool_call/tests.yaml +0 -0
  72. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/templates/packs/tool_call/tools.yaml +0 -0
  73. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/tune.py +0 -0
  74. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/verdict.py +0 -0
  75. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/watch_proxy.py +0 -0
  76. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/workspace.py +0 -0
  77. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/src/tuieval/yamlout.py +0 -0
  78. {tuieval-0.2.0.dev86 → tuieval-0.2.0.dev87}/tests/mock_server.py +0 -0
@@ -2,6 +2,7 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - The run screen's queue keeps the running model in view, unless you scroll it yourself (scrolling back to it follows again).
5
6
  - Start while a run is going skips models and packs already running or queued (it says which); picking only those adds nothing. Retest and picked questions still go through.
6
7
  - Fix: something queued just as a run ended (while its server was stopping) now starts instead of waiting forever.
7
8
  - Hosted models (OpenRouter and other APIs) run beside local ones instead of waiting for a slot: up to `parallel_hosted` at a time (default 4, `--parallel-hosted N`). Local models can be added to a hosted-only run.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tuieval
3
- Version: 0.2.0.dev86
3
+ Version: 0.2.0.dev87
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.dev86'
22
- __version_tuple__ = version_tuple = (0, 2, 0, 'dev86')
21
+ __version__ = version = '0.2.0.dev87'
22
+ __version_tuple__ = version_tuple = (0, 2, 0, 'dev87')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -2206,6 +2206,7 @@ class RunScreen(Screen):
2206
2206
  yield Footer()
2207
2207
 
2208
2208
  def on_mount(self):
2209
+ self._queue_follow, self._queue_y = True, None # follow_queue
2209
2210
  q = self.query_one("#queue", DataTable)
2210
2211
  for label, key in (("Model", "model"), ("Eval", "suite"), ("Status", "status"), ("Progress", "progress"),
2211
2212
  ("✓", "pass"), ("✗", "fail"), ("Time", "time"), ("Note", "note")):
@@ -2523,6 +2524,25 @@ class RunScreen(Screen):
2523
2524
  self.update_job(j)
2524
2525
  self.update_overall()
2525
2526
  self.refresh_current()
2527
+ self.follow_queue()
2528
+
2529
+ def follow_queue(self):
2530
+ """Keep the running model's row in view in the queue, unless you scrolled the queue yourself;
2531
+ scrolling back to where the running row shows picks the following up again."""
2532
+ q = self.query_one("#queue", DataTable)
2533
+ keys = [r.key.value for r in q.ordered_rows]
2534
+ now = next((i for i, k in enumerate(keys)
2535
+ if any(j.key == k and j.status in ("loading", "running") for j in self.jobs)), None)
2536
+ if now is None:
2537
+ return
2538
+ if self._queue_y is not None and q.scroll_y != self._queue_y: # you scrolled since we last did
2539
+ seen = q.scroll_y <= now < q.scroll_y + max(1, q.scrollable_content_region.height - 1)
2540
+ self._queue_follow = seen
2541
+ if self._queue_follow:
2542
+ q.scroll_to(y=max(0, now - 1), animate=False)
2543
+ self._queue_y = max(0, min(now - 1, q.max_scroll_y))
2544
+ else:
2545
+ self._queue_y = q.scroll_y
2526
2546
 
2527
2547
  # -- actions
2528
2548
  def action_skip_model(self):
@@ -1148,6 +1148,71 @@ class AddToRun(unittest.TestCase):
1148
1148
  self.assertEqual(out["sessions"], [["a/apps", "done"], ["a/other", "done"], ["b/apps", "done"]])
1149
1149
 
1150
1150
 
1151
+ class QueueFollows(unittest.TestCase):
1152
+ """The run screen's queue keeps the running model in view unless you scroll it yourself."""
1153
+
1154
+ def test_queue_follows_the_running_row(self):
1155
+ with tempfile.TemporaryDirectory() as tmp:
1156
+ ws = os.path.join(tmp, "ws")
1157
+ tuieval(tmp, "init", ws)
1158
+ for i in range(15):
1159
+ d = os.path.join(ws, "packs", f"p{i:02}")
1160
+ os.makedirs(d)
1161
+ write(os.path.join(d, "pack.toml"), f'label = "p{i:02}"\norder = {i}\n[certify]\nrepeat = 1\n[gate]\nmin_accuracy = 0.1\n')
1162
+ write(os.path.join(d, "tests.yaml"), f"- {{id: a, input: q{i} a, expected: 1, reference: 'ANSWER: 1', difficulty: easy}}\n"
1163
+ f"- {{id: b, input: q{i} b, expected: 1, reference: 'ANSWER: 1', difficulty: easy}}\n")
1164
+ mock = [sys.executable, os.path.join(HERE, "mock_server.py"), "--port", "{port}", "--packs",
1165
+ os.path.join(ws, "packs"), "--model", "{served_name}", "--delay", "0.4"]
1166
+ with open(os.path.join(ws, "models.toml"), "a") as f:
1167
+ f.write(f'\n[servers.mk]\ncmd = {json.dumps(mock)}\nport = {free_port()}\n'
1168
+ '\n[[models]]\nlabel = "m"\nserver = "mk"\nmodel = "m"\n')
1169
+ code = textwrap.dedent("""
1170
+ import asyncio, json, time
1171
+ from tuieval.tui import EvalsApp, RunScreen
1172
+ from textual.widgets import SelectionList, DataTable
1173
+ async def go():
1174
+ app = EvalsApp({})
1175
+ async with app.run_test(size=(160, 50)) as pilot:
1176
+ await pilot.pause()
1177
+ s = app.screen
1178
+ for i in range(15):
1179
+ s.query_one("#suites", SelectionList).select(f"p{i:02}")
1180
+ s.selected_models = {"m"}
1181
+ s.refresh_models()
1182
+ s.query_one("#tier-certify").value = True
1183
+ await pilot.pause()
1184
+ s.action_start()
1185
+ def at(run, n):
1186
+ return sum(j.status == "done" for j in run.jobs) >= n
1187
+ deadline = time.time() + 240
1188
+ while time.time() < deadline:
1189
+ await pilot.pause(0.2)
1190
+ if isinstance(app.screen, RunScreen) and at(app.screen, 12):
1191
+ break
1192
+ run = app.screen
1193
+ q = run.query_one("#queue", DataTable)
1194
+ await pilot.pause(1.5)
1195
+ followed = q.scroll_y
1196
+ q.scroll_to(y=0, animate=False) # you scroll up
1197
+ await pilot.pause(2.5)
1198
+ stayed = q.scroll_y
1199
+ q.scroll_to(y=q.max_scroll_y, animate=False) # back to where it runs
1200
+ await pilot.pause(2.5)
1201
+ again = q.scroll_y
1202
+ while run.running and time.time() < deadline:
1203
+ await pilot.pause(0.2)
1204
+ print(json.dumps({"followed": followed, "stayed": stayed, "again": again}))
1205
+ asyncio.run(go())
1206
+ """)
1207
+ p = subprocess.run([sys.executable, "-c", code], capture_output=True, text=True, timeout=480,
1208
+ env=dict(os.environ, TUIEVAL_HOME=ws, TUIEVAL_DETECT_PORTS=""))
1209
+ out = json.loads(p.stdout.strip().splitlines()[-1]) if p.stdout.strip() else p.stderr
1210
+ self.assertIsInstance(out, dict, out)
1211
+ self.assertGreater(out["followed"], 0, out) # scrolled down to the running row by itself
1212
+ self.assertEqual(out["stayed"], 0, out) # your scroll wins
1213
+ self.assertGreater(out["again"], 0, out) # and following resumes once you're back
1214
+
1215
+
1151
1216
  class PTAIndex(unittest.TestCase):
1152
1217
  """The PTA index: parsimony from tokens and speed from time, each relative to the best on a log
1153
1218
  scale, and accuracy, all over the questions every compared model answered."""
File without changes
File without changes
File without changes