tuieval 0.2.0.dev50__py3-none-any.whl → 0.2.0.dev56__py3-none-any.whl

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.
tuieval/_version.py CHANGED
@@ -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.dev50'
22
- __version_tuple__ = version_tuple = (0, 2, 0, 'dev50')
21
+ __version__ = version = '0.2.0.dev56'
22
+ __version_tuple__ = version_tuple = (0, 2, 0, 'dev56')
23
23
 
24
24
  __commit_id__ = commit_id = None
tuieval/engine.py CHANGED
@@ -987,16 +987,16 @@ class Engine:
987
987
  del per_pack[name]
988
988
  return {l: p for l, p in out.items() if p}
989
989
 
990
- def estimate_seconds(self, jobs):
991
- """(seconds, notes): expected run time for the waiting jobs, estimated per pack.
990
+ def seconds_per_answer(self, jobs, waiting_only=True):
991
+ """({job key: expected seconds per answer}, borrowed, guessed, thin) for the waiting jobs.
992
992
  A pack the model has answered uses its own median time there. Otherwise the median of
993
993
  other models' times on that pack, scaled by how much slower or faster this model is than
994
- them on packs both have answered. notes lists what made the estimate rough ([] = solid)."""
994
+ them on packs both have answered. The sets say which estimates are rough (see estimate_seconds)."""
995
995
  times = self.answer_times()
996
996
  med = {l: {p: statistics.median(ts) for p, ts in pp.items()} for l, pp in times.items()}
997
- total, borrowed, guessed, thin = 0.0, set(), set(), set()
997
+ spr_of, borrowed, guessed, thin = {}, set(), set(), set()
998
998
  for j in jobs:
999
- if j.status != "waiting":
999
+ if j.status in ("done", "skipped", "failed") or (waiting_only and j.status != "waiting"):
1000
1000
  continue
1001
1001
  own = med.get(j.label, {})
1002
1002
  ref = {p: statistics.median(v[p] for l, v in med.items() if p in v and l != j.label)
@@ -1013,8 +1013,18 @@ class Engine:
1013
1013
  else:
1014
1014
  spr = statistics.median(own.values()) if own else DEFAULT_SECONDS_PER_REQUEST
1015
1015
  guessed.add((j.key, bool(own)))
1016
- done = len(self._done_keys(j)) if j.merge else 0
1017
- total += max(0, j.total - done) * spr
1016
+ spr_of[j.key] = spr
1017
+ return spr_of, borrowed, guessed, thin
1018
+
1019
+ def estimate_seconds(self, jobs):
1020
+ """(seconds, notes): expected run time for the waiting jobs, estimated per pack
1021
+ (seconds_per_answer). notes lists what made the estimate rough ([] = solid)."""
1022
+ spr_of, borrowed, guessed, thin = self.seconds_per_answer(jobs)
1023
+ total = 0.0
1024
+ for j in jobs:
1025
+ if j.key in spr_of:
1026
+ done = len(self._done_keys(j)) if j.merge else 0
1027
+ total += max(0, j.total - done) * spr_of[j.key]
1018
1028
  notes = []
1019
1029
  if borrowed:
1020
1030
  factors = sorted({f for _, f, _ in borrowed if f is not None})
tuieval/machines.py CHANGED
@@ -65,10 +65,10 @@ def detect():
65
65
  # macOS default when unset: about 2/3 of RAM up to 36 GB, 3/4 above.
66
66
  limit = wired / 1024 if wired > 0 else ram * (0.75 if ram > 36 else 2 / 3)
67
67
  gpu_cores = None
68
- try:
69
- out = subprocess.run(["system_profiler", "SPDisplaysDataType"], capture_output=True, text=True,
70
- timeout=15).stdout
71
- m = re.search(r"Total Number of Cores:\s*(\d+)", out)
68
+ try: # the GPU driver's own count: instant, where system_profiler can take seconds (CI's virtual Macs)
69
+ out = subprocess.run(["ioreg", "-rc", "AGXAccelerator", "-d1"], capture_output=True, text=True,
70
+ timeout=5).stdout
71
+ m = re.search(r'"gpu-core-count"\s*=\s*(\d+)', out)
72
72
  gpu_cores = int(m.group(1)) if m else None
73
73
  except (OSError, subprocess.TimeoutExpired):
74
74
  pass
tuieval/tui.py CHANGED
@@ -1855,6 +1855,8 @@ class RunScreen(Screen):
1855
1855
  self.current = ""
1856
1856
  self.req_first = None
1857
1857
  self.stats = []
1858
+ self.spr, self.rough = None, set() # job key -> expected seconds per answer (ETA), rough ones
1859
+ self.live = {} # job key -> seconds per answer in this session
1858
1860
  self.mem = {}
1859
1861
  self.outcome = "" # "Finished" or "Cancelled" once the queue is done
1860
1862
  self.ended = None
@@ -2037,6 +2039,8 @@ class RunScreen(Screen):
2037
2039
  + (f" [red]copy of repeat {r['identical_to_repeat'] + 1}[/red]"
2038
2040
  if r.get("identical_to_repeat") is not None else ""))
2039
2041
  self.stats.append(r)
2042
+ if r.get("total_s"):
2043
+ self.live.setdefault(job.key, []).append(r["total_s"])
2040
2044
  self.recent_entries[row_key] = {"model": job.label, "pack": job.pack.name, "record": r,
2041
2045
  "pack_fp": job.pack.fingerprint}
2042
2046
  if recent.row_count > 500:
@@ -2187,6 +2191,34 @@ class RunScreen(Screen):
2187
2191
  ("note", short_note(j.note))):
2188
2192
  q.update_cell(j.key, col, value, update_width=True)
2189
2193
 
2194
+ LIVE_ANSWERS = 5 # a pack's own times this session take over from its history after this many
2195
+
2196
+ def eta_seconds(self):
2197
+ """(seconds, rough): time left, pack by pack: answers left × seconds per answer. A pack's own
2198
+ average this session once it has LIVE_ANSWERS of them, before that its history (Engine.
2199
+ seconds_per_answer: this model's past times there, or other models' scaled). Divided by the
2200
+ models running side by side. rough: some packs have no history of their own yet."""
2201
+ new = [j for j in self.jobs if self.spr is None or (j.key not in self.spr and j.status not in
2202
+ ("done", "skipped", "failed"))]
2203
+ if new: # at the start, and for evals added to the run
2204
+ spr, borrowed, guessed, _ = self.app.engine.seconds_per_answer(new, waiting_only=False)
2205
+ self.spr = {**(self.spr or {}), **spr}
2206
+ self.rough |= {k for k, *_ in borrowed} | {k for k, _ in guessed}
2207
+ left, rough, models = 0.0, False, set()
2208
+ for j in self.jobs:
2209
+ if j.status in ("done", "skipped", "failed") or j.done >= j.total:
2210
+ continue
2211
+ live = self.live.get(j.key, [])
2212
+ if len(live) >= self.LIVE_ANSWERS:
2213
+ spr = sum(live) / len(live)
2214
+ else:
2215
+ spr = self.spr.get(j.key, engine.DEFAULT_SECONDS_PER_REQUEST)
2216
+ rough |= j.key in self.rough
2217
+ left += (j.total - j.done) * spr
2218
+ models.add(j.label)
2219
+ lanes = max(1, min(self.parallel or self.app.engine.parallel_models(), len(models)))
2220
+ return left / lanes, rough
2221
+
2190
2222
  def update_overall(self):
2191
2223
  done = sum(j.done for j in self.jobs)
2192
2224
  passed = sum(j.passed for j in self.jobs)
@@ -2194,9 +2226,9 @@ class RunScreen(Screen):
2194
2226
  self.query_one("#bar", ProgressBar).update(progress=done)
2195
2227
  elapsed = time.time() - self.started
2196
2228
  eta = ""
2197
- done_now = sum(j.done - j.resumed for j in self.jobs if j.started) # answers from earlier sittings
2198
- if self.running and done_now > 0 and self.total > done: # took no time in this one
2199
- eta = f" · ETA {fmt_secs(elapsed / done_now * (self.total - done))}"
2229
+ if self.running and self.total > done:
2230
+ left, rough = self.eta_seconds()
2231
+ eta = f" · ETA {'~' if rough else ''}{fmt_secs(left)}"
2200
2232
  pct = f"{100 * passed / (passed + failed):.0f}%" if passed + failed else "-"
2201
2233
  recent = self.stats[-50:]
2202
2234
  tps = [r["gen_tps"] for r in recent if r.get("gen_tps")]
tuieval/watch_proxy.py CHANGED
@@ -18,6 +18,7 @@ import http.client
18
18
  import itertools
19
19
  import json
20
20
  import os
21
+ import socketserver
21
22
  import sys
22
23
  import threading
23
24
  import time
@@ -129,6 +130,12 @@ class Proxy:
129
130
  class Server(ThreadingHTTPServer):
130
131
  daemon_threads = True
131
132
 
133
+ def server_bind(self):
134
+ # HTTPServer looks up its own hostname here (socket.getfqdn), which can take half a
135
+ # minute where DNS is slow; the name is only cosmetic, so use the address.
136
+ socketserver.TCPServer.server_bind(self)
137
+ self.server_name, self.server_port = self.server_address[:2]
138
+
132
139
  def handle_error(self, request, client_address):
133
140
  err = sys.exc_info()[1]
134
141
  if isinstance(err, (BrokenPipeError, ConnectionResetError)):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tuieval
3
- Version: 0.2.0.dev50
3
+ Version: 0.2.0.dev56
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
@@ -1,14 +1,14 @@
1
1
  tuieval/__init__.py,sha256=myVXVX7htbwezW6k3x_FuKgn1RSZ2WcTaDEMByGn6Wg,312
2
2
  tuieval/__main__.py,sha256=E6Gls0DNz8GQK2K-kOUIx8cYhgANW_CH54VKrfCfs14,52
3
- tuieval/_version.py,sha256=cUlv5rRBNcBZ7a8L_s4uRBMTmFx7dza4nVKsHqXS0Pg,535
3
+ tuieval/_version.py,sha256=w3V6noQtCnYcwkOc0Hi3EmCrxSS1deuXpcG9aisclJI,535
4
4
  tuieval/builtin.toml,sha256=Kxw22RZCJhURUyu5Wz8tj5Y1nM85A8hFVRbA4ocoPUg,4293
5
5
  tuieval/cli.py,sha256=gzGBzhqvR0WKJQQNHB8CudpgViIeIyawrNWa54whmoI,4502
6
6
  tuieval/client.py,sha256=kpsyLEZKfxCsvIGBHBbBy83TpI97CvnGCDq-4AbJS4Y,9986
7
7
  tuieval/compare.py,sha256=SHN_o_nlppFcHMBqBZRITIkeQDPaR_JxhUZItQLMBwo,26270
8
8
  tuieval/doctor.py,sha256=y1C_ggjd8oikXTrovgTBf26wjThP8EoVkfTVeO_n5dQ,6070
9
- tuieval/engine.py,sha256=w3KsgxzFAMcRyVzFGQXhTgN3d9qYRtAZZxNLXZOgnrQ,114222
9
+ tuieval/engine.py,sha256=0PuHFus1k02qAY8nlsI6z5iQo7IT82bMwmHDZT6fGtU,114784
10
10
  tuieval/export.py,sha256=dbHQgL_ubWtIPM1sLzAew_gETnrDJyd0zgqVA5vkpXM,9657
11
- tuieval/machines.py,sha256=ImEihNj8QlBRdCHLED4QIdjJUvjVtZgo6DiVU5h6Zzs,13415
11
+ tuieval/machines.py,sha256=tdRrn3WNOmCHwfcxcWz1NuAuOJdI7WnJFR1y6xgF89c,13512
12
12
  tuieval/onboard.py,sha256=IYETVDWCCo9vF0Dtj4_rr9VanYeCky8PwP3-2g5A_l0,6215
13
13
  tuieval/packs.py,sha256=tiLmxsrX_M5EUMuR4Yvb7pLBLEI15wamHSjflD6wjoY,11299
14
14
  tuieval/profiles.py,sha256=soeLxjdB-l-OxSY8jZ4yjwfdSnsBV-QICcwKxRJS7nQ,3663
@@ -17,10 +17,10 @@ tuieval/remove.py,sha256=Vwh43nm9lVb9U9aN-h6CMYbkTwdR3UWfZObwweF72n0,5157
17
17
  tuieval/run_evals.py,sha256=5bQEqU533XYiLze9dYcl72c5GSIpohMj1eqJrePRwnc,46450
18
18
  tuieval/scaffold.py,sha256=YBOqniJpf_GtKKAFF3_OHxxMGbmTWdFjcClGcOnrrKE,12130
19
19
  tuieval/selftest.py,sha256=nExkrOidgymlmvkxImEBfrQG5iTUN5WuRMIKtqusCCs,9835
20
- tuieval/tui.py,sha256=21Re8fM5rf3zbHIKvK9laMV-mL2HjcGfMReoRg0DRLw,164268
20
+ tuieval/tui.py,sha256=xAQnR270m7x-rfTdK7LBPMM9MEQC37zndD1TOTq8EUU,166103
21
21
  tuieval/tune.py,sha256=jXwYuSjfdqbLCE3WonP2xaUHwk_FnhI67twB1BaHggI,38992
22
22
  tuieval/verdict.py,sha256=Muo1a05mCv781MlEJsXwDsGaKbRi0JS4Lv8HdvK_Aco,19054
23
- tuieval/watch_proxy.py,sha256=z916NRhEur04a9RnLm6ZpIEY1SnnUGPiHYbmlD9ktQ0,11035
23
+ tuieval/watch_proxy.py,sha256=bnDO1i6n6yUs1hODoUWcMppJMGPavoQqs6aNYrIxpT8,11416
24
24
  tuieval/workspace.py,sha256=V_HhkaN1odr0OpjNsPYradRWLpzDldM7SwpvxohBL4U,895
25
25
  tuieval/yamlout.py,sha256=r1IokjLqW-Ztpg1fSn33ZW3VK00v1VvxymmU5Qy7GEw,606
26
26
  tuieval/graders/__init__.py,sha256=_YCFY5euaInIQdX4RoQGS3kMuiK6q5J7HF2e4i0ou3Q,5940
@@ -46,8 +46,8 @@ tuieval/templates/packs/tool_call/pack.toml,sha256=n4bx0lGJHSmW41uVqhfIXJ2BWZpmT
46
46
  tuieval/templates/packs/tool_call/system.txt,sha256=mKxu-lB-0YjLHXdgeFJa8x2C180OvcNz5cHv2N5Bg3U,254
47
47
  tuieval/templates/packs/tool_call/tests.yaml,sha256=Nv7c4syrzJ9cU8EBKSK_1ZSFx9V02aCjW4-2TBCPT7s,1859
48
48
  tuieval/templates/packs/tool_call/tools.yaml,sha256=HgxCkh0On2VwbHSsjAGM8tNCK7ZtKLGrs7lMCn3Fprs,1048
49
- tuieval-0.2.0.dev50.dist-info/METADATA,sha256=VW1tuB9QtgVUt9irfVVzrDhkF907wql1_WpM7PasfPU,13881
50
- tuieval-0.2.0.dev50.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
51
- tuieval-0.2.0.dev50.dist-info/entry_points.txt,sha256=7eIhi1GpFxBP2OLJV02Nn8LwsNuO512eKOGt1IBbj04,45
52
- tuieval-0.2.0.dev50.dist-info/licenses/LICENSE,sha256=7wfRgGtBGH1aXhZkIn47oUznqKC18kEPMvUVgol7Ffc,1077
53
- tuieval-0.2.0.dev50.dist-info/RECORD,,
49
+ tuieval-0.2.0.dev56.dist-info/METADATA,sha256=nUWInJiqgtyGg3hiyaYIUuGD-BYOzoyqi3kRoSloRPk,13881
50
+ tuieval-0.2.0.dev56.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
51
+ tuieval-0.2.0.dev56.dist-info/entry_points.txt,sha256=7eIhi1GpFxBP2OLJV02Nn8LwsNuO512eKOGt1IBbj04,45
52
+ tuieval-0.2.0.dev56.dist-info/licenses/LICENSE,sha256=7wfRgGtBGH1aXhZkIn47oUznqKC18kEPMvUVgol7Ffc,1077
53
+ tuieval-0.2.0.dev56.dist-info/RECORD,,