tuieval 0.2.0.dev87__py3-none-any.whl → 0.2.0.dev89__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.dev87'
22
- __version_tuple__ = version_tuple = (0, 2, 0, 'dev87')
21
+ __version__ = version = '0.2.0.dev89'
22
+ __version_tuple__ = version_tuple = (0, 2, 0, 'dev89')
23
23
 
24
24
  __commit_id__ = commit_id = None
tuieval/engine.py CHANGED
@@ -646,8 +646,9 @@ def serves(ids, name):
646
646
 
647
647
 
648
648
  def pack_min_context(pack):
649
- """Rough context a pack needs: its longest prompt (~4 chars/token) plus room to answer."""
650
- return max(len(t["input"]) for t in pack.tests) // 4 + 4096
649
+ """Rough context a pack needs: its longest prompt (~4 chars/token) plus room to answer (the
650
+ pack's own max_tokens when it sets one)."""
651
+ return max(len(t["input"]) for t in pack.tests) // 4 + (pack.max_tokens or 4096)
651
652
 
652
653
 
653
654
  class Cancelled(Exception):
@@ -872,7 +873,8 @@ class Engine:
872
873
  needs_tools = m.get("tools") is not False and any("tools" in p.needs for p in self.packs.values())
873
874
  ep = choose_endpoint(eps, m["model"], effective_sampling(self.cfg, m), needs_tools, previous)
874
875
  ep = ep and {"tag": ep["tag"], "provider": ep.get("provider_name"),
875
- "quantization": ep.get("quantization") or "unknown"}
876
+ "quantization": ep.get("quantization") or "unknown",
877
+ "max_output": ep.get("max_completion_tokens")}
876
878
  except (OSError, ValueError):
877
879
  # offline: assume the endpoint earlier results used (the run itself will need the network)
878
880
  ep = {"tag": previous, "provider": None, "quantization": previous_quant} if previous else None
@@ -1196,6 +1198,9 @@ class Engine:
1196
1198
  elif (sv.ctx or 10**9) < pack_min_context(pack):
1197
1199
  job.status, job.note = "skipped", \
1198
1200
  f"needs ~{pack_min_context(pack) // 1024}k context, {sv.ctx // 1024}k fits on {sv.machine.id}"
1201
+ elif pack.max_tokens and pack.max_tokens > ((self.endpoint(m) or {}).get("max_output") or 10**9):
1202
+ job.status, job.note = "skipped", \
1203
+ f"needs {pack.max_tokens:,} output tokens, endpoint allows {self.endpoint(m)['max_output']:,}"
1199
1204
  elif missing:
1200
1205
  job.status, job.note = "skipped", f"{', '.join(missing)} not installed in tuieval's Python"
1201
1206
  elif status and status.startswith("outdated"):
@@ -2037,7 +2042,8 @@ class Engine:
2037
2042
  def _body(self, job, test, rep=0):
2038
2043
  s = job.sampling
2039
2044
  body = {"model": job.model["served_name"], "messages": self._messages(job, test),
2040
- "temperature": s["temperature"], "top_p": s["top_p"], "max_tokens": s["max_tokens"]}
2045
+ "temperature": s["temperature"], "top_p": s["top_p"],
2046
+ "max_tokens": job.pack.max_tokens or s["max_tokens"]}
2041
2047
  for k in ("top_k", "min_p", "presence_penalty", "repeat_penalty", "seed", "reasoning_effort"):
2042
2048
  if k in s:
2043
2049
  body[k] = s[k]
@@ -2380,7 +2386,8 @@ class Engine:
2380
2386
  "model": {**{k: m.get(k) for k in ("label", "served_name", "server", "model", "tags", "vision", "mmproj")},
2381
2387
  **{k: m[k] for k in ("tools", "max_context", "thinking") if k in m}},
2382
2388
  "pack": {"name": job.pack.name, "label": job.pack.label, "group": job.pack.group,
2383
- "fingerprint": job.pack.fingerprint, "tests": len(job.pack.tests)},
2389
+ "fingerprint": job.pack.fingerprint, "tests": len(job.pack.tests),
2390
+ **({"max_tokens": job.pack.max_tokens} if job.pack.max_tokens else {})},
2384
2391
  "settings": job.sampling,
2385
2392
  "serving": job.settings.get("serving"),
2386
2393
  "settings_fingerprint": settings_fingerprint(job.settings),
tuieval/packs.py CHANGED
@@ -50,6 +50,7 @@ class Pack:
50
50
  screen: int # tests in a screening run
51
51
  certify_repeat: int # repeats in a certification run (0 = models.toml default)
52
52
  stop_early: bool = False # stop the pack once its FAIL is certain (pack.toml stop_early)
53
+ max_tokens: int = 0 # output budget for this pack's answers (0 = [sampling] max_tokens)
53
54
 
54
55
  @property
55
56
  def modules(self):
@@ -135,12 +136,15 @@ NOT_FINGERPRINTED = {"reference", "wrong", "description", "category", "critical"
135
136
  DIFFICULTIES = ("easy", "medium", "hard")
136
137
 
137
138
 
138
- def _fingerprint(system, tools, grader, tests, images):
139
- """Hash of what the model sees and what decides pass/fail."""
139
+ def _fingerprint(system, tools, grader, tests, images, max_tokens=0):
140
+ """Hash of what the model sees and what decides pass/fail. A pack's own max_tokens counts
141
+ (it decides what gets cut off); packs without one keep the fingerprint they always had."""
140
142
  h = hashlib.sha256()
141
- h.update(json.dumps({"system": system, "tools": tools, "grader": grader,
142
- "tests": [{k: v for k, v in t.items() if k not in NOT_FINGERPRINTED} for t in tests]},
143
- sort_keys=True, default=str).encode())
143
+ what = {"system": system, "tools": tools, "grader": grader,
144
+ "tests": [{k: v for k, v in t.items() if k not in NOT_FINGERPRINTED} for t in tests]}
145
+ if max_tokens:
146
+ what["max_tokens"] = max_tokens
147
+ h.update(json.dumps(what, sort_keys=True, default=str).encode())
144
148
  for path in sorted(images):
145
149
  with open(path, "rb") as f:
146
150
  h.update(f.read())
@@ -217,13 +221,16 @@ def load_pack(path):
217
221
  if tools and "tools" not in needs:
218
222
  needs = list(needs) + ["tools"]
219
223
  gate = {"min_accuracy": 0.8, **meta.get("gate", {})}
224
+ max_tokens = meta.get("max_tokens", 0)
225
+ if "max_tokens" in meta and (isinstance(max_tokens, bool) or not isinstance(max_tokens, int) or max_tokens < 1):
226
+ raise PackError(f"{meta_path}: max_tokens must be a whole number above 0, not {max_tokens!r}")
220
227
  return Pack(name=name, path=path, label=meta.get("label", name), group=meta.get("group", "Other"),
221
228
  description=meta.get("description", ""), grader=meta.get("grader", "answer"),
222
229
  system=system, needs=list(needs), tools=tools, tests=tests,
223
- fingerprint=_fingerprint(system, tools, meta.get("grader", "answer"), tests, images),
230
+ fingerprint=_fingerprint(system, tools, meta.get("grader", "answer"), tests, images, max_tokens),
224
231
  gate=gate, screen=int(meta.get("screen", 10)),
225
232
  certify_repeat=int(meta.get("certify", {}).get("repeat", 0)),
226
- stop_early=bool(meta.get("stop_early", False)))
233
+ stop_early=bool(meta.get("stop_early", False)), max_tokens=max_tokens)
227
234
 
228
235
 
229
236
  def load_packs(packs_dir=None, errors=None):
@@ -6,6 +6,7 @@ grader = "answer" # how answers are scored (answer, rag, reply, t
6
6
  system = "system.txt" # the system prompt sent with every question
7
7
  order = 100 # position in the list
8
8
  screen = 6 # tests in a Screen run
9
+ # max_tokens = 32768 # this pack's output budget, instead of models.toml [sampling] max_tokens
9
10
 
10
11
  [certify]
11
12
  repeat = 3 # times each question is asked in a Certify run
tuieval/tui.py CHANGED
@@ -531,7 +531,8 @@ class AnswerScreen(ModalScreen):
531
531
  partial = " [cyan](pack still running or unfinished: not in scores yet)[/cyan]" if entry.get("partial") else ""
532
532
  quant = f" [magenta]{rec['quantization']}[/magenta]" if rec.get("quantization") else ""
533
533
  finish = rec.get("finish") or "?"
534
- finish = f"[bold red]{finish} (cut off at max_tokens)[/]" if finish == "length" else finish
534
+ limit = f"this pack's max_tokens, {pack.max_tokens:,}" if pack and pack.max_tokens else "max_tokens"
535
+ finish = f"[bold red]{finish} (cut off at {limit})[/]" if finish == "length" else finish
535
536
  toks = rec.get("completion_tokens")
536
537
  split = ", ".join(f"{k} {rec[k + '_tokens']:,}" for k in ("reasoning", "answer")
537
538
  if rec.get(k + "_tokens") is not None)
@@ -709,6 +710,8 @@ class SetupScreen(Screen):
709
710
  count = (f"[b cyan]{len(picked)}/{len(pk.tests)} tests picked[/]" if picked
710
711
  else f"[dim]{len(pk.tests)} tests[/dim]")
711
712
  needs = f" [magenta]{', '.join(pk.needs)}[/magenta]" if pk.needs else ""
713
+ if pk.max_tokens:
714
+ needs += f" [dim]max_tokens {pk.max_tokens:,}[/dim]"
712
715
  mix = {lvl: sum(t.get("difficulty") == lvl for t in pk.tests) for lvl in ("easy", "medium", "hard")}
713
716
  mixed = " ".join(f"[{DIFF_STYLE[l]}]{n}{l[0].upper()}[/]" for l, n in mix.items() if n)
714
717
  packs.add_option(Selection(f"[dim]{pk.group} ·[/dim] {pk.label} {count} {mixed}{needs}",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tuieval
3
- Version: 0.2.0.dev87
3
+ Version: 0.2.0.dev89
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,24 +1,24 @@
1
1
  tuieval/__init__.py,sha256=myVXVX7htbwezW6k3x_FuKgn1RSZ2WcTaDEMByGn6Wg,312
2
2
  tuieval/__main__.py,sha256=E6Gls0DNz8GQK2K-kOUIx8cYhgANW_CH54VKrfCfs14,52
3
- tuieval/_version.py,sha256=MbDAskHZqL7Ly_g4fmQSqzeTJS3q2IGRhFiHFeJp5zA,535
3
+ tuieval/_version.py,sha256=XiUzkOIwjrCyySM-wvAr3iW2t63wQjeKE_BTt6XJQng,535
4
4
  tuieval/builtin.toml,sha256=Kxw22RZCJhURUyu5Wz8tj5Y1nM85A8hFVRbA4ocoPUg,4293
5
5
  tuieval/cli.py,sha256=VwiaU1F9bZtU8C-gEowJq58kPeYQY4t6SY_HwrV-qgc,4702
6
6
  tuieval/client.py,sha256=kpsyLEZKfxCsvIGBHBbBy83TpI97CvnGCDq-4AbJS4Y,9986
7
7
  tuieval/compare.py,sha256=uXU_Yzf4zDSHoupHQrTmBhsfc8wCyI1nMaZQN81ybiA,30522
8
8
  tuieval/doctor.py,sha256=j2MdDORstsAGPnRmpsnxau1_fR6sTc_oEFGM5zugTJU,7246
9
- tuieval/engine.py,sha256=yMWvYq85WmYmk8fpMLRzYcxJxo7XcjEIUTa_y2VK7Kk,124538
9
+ tuieval/engine.py,sha256=BS2BXLaxtw3RFI6-5PDvhV5VLh5v0m0cB_F3fPC5MtM,125105
10
10
  tuieval/export.py,sha256=dbHQgL_ubWtIPM1sLzAew_gETnrDJyd0zgqVA5vkpXM,9657
11
11
  tuieval/logs.py,sha256=01gZK6tf53dIThaIrs1bEsz9eqKYVlcxpyaLnSPTVxo,8127
12
12
  tuieval/machines.py,sha256=t9tu6CZeXZON4EtOVnEwP4VtaynpVLf9Cz8TNjf7jtY,20124
13
13
  tuieval/onboard.py,sha256=IYETVDWCCo9vF0Dtj4_rr9VanYeCky8PwP3-2g5A_l0,6215
14
- tuieval/packs.py,sha256=8bkF3xIDnWlt1JD2t3seHpMXW_RbMBgrCdIGEC-w-ZU,11458
14
+ tuieval/packs.py,sha256=ZgaZ1m2ePYvDjmddSc3nafnMEn7kTq9WcDpCaJSZleQ,12026
15
15
  tuieval/profiles.py,sha256=soeLxjdB-l-OxSY8jZ4yjwfdSnsBV-QICcwKxRJS7nQ,3663
16
16
  tuieval/pta.py,sha256=i5oPj-Pz3N9u7bzzuZwsQvMGdxWdAncww2nR15K6PHY,12108
17
17
  tuieval/remove.py,sha256=Vwh43nm9lVb9U9aN-h6CMYbkTwdR3UWfZObwweF72n0,5157
18
18
  tuieval/run_evals.py,sha256=FU3bvu3jt9ZWg3YLsXNINbdeClXMioyc4zQ4yEhmBQs,47855
19
19
  tuieval/scaffold.py,sha256=YBOqniJpf_GtKKAFF3_OHxxMGbmTWdFjcClGcOnrrKE,12130
20
20
  tuieval/selftest.py,sha256=nExkrOidgymlmvkxImEBfrQG5iTUN5WuRMIKtqusCCs,9835
21
- tuieval/tui.py,sha256=ZE9FEOr84Vx2YULSiBpaZM4DyOo0iIkuKlH870K88I8,176810
21
+ tuieval/tui.py,sha256=yD-3pQJ7I71r8lg49m0aWD5SlfA_Bk9BIz6XUJj8_UM,177016
22
22
  tuieval/tune.py,sha256=azxxthgJeag8JyoQUgaRxq6oI1XvEgzb9vkszjJZH7w,39645
23
23
  tuieval/verdict.py,sha256=3SM8qrVdWNIFDENagaR5ZPBYEnjsBtkYB9WS5TDVssU,20517
24
24
  tuieval/watch_proxy.py,sha256=bnDO1i6n6yUs1hODoUWcMppJMGPavoQqs6aNYrIxpT8,11416
@@ -31,7 +31,7 @@ tuieval/graders/rag.py,sha256=j_gEjmaXmY_hgKu7-7pVQpQ8Cwb49gadnwFurih-1Hw,1447
31
31
  tuieval/graders/reply.py,sha256=BdkL7EWSSMoyjwCOFa97TTQMR1QyrpMH-ntcgjzDPEg,2199
32
32
  tuieval/graders/tool_call.py,sha256=_QXURtOk4ZKBnxXVMzFL1c9jWLWksRlLZ4VqOCL0t90,3578
33
33
  tuieval/templates/models.toml,sha256=0RsHR_oCNmkkUdPBemLahNIwaOOPKqT97hA97TJFDYw,1021
34
- tuieval/templates/packs/answer/pack.toml,sha256=8bjMBxG1Zz3yxEnYCqRTNBJ-JGYlquqiK19p8URWwTc,1167
34
+ tuieval/templates/packs/answer/pack.toml,sha256=8tKQZ-4y9p7I6LjtcJ6CKmqL8M8ZsPna0TT-6mWtmcI,1274
35
35
  tuieval/templates/packs/answer/system.txt,sha256=m4V6X1i49Bas2HowZMWgfdDSPDHTObb_WxHXt0xw1Yo,248
36
36
  tuieval/templates/packs/answer/tests.yaml,sha256=WiYPOknPcO9E6f1wXhLcfA0W5v-oK8MgZV956uWtcUQ,2660
37
37
  tuieval/templates/packs/code/pack.toml,sha256=nnrUVSWlIHAoORTIc4W3emoniwWOcfx2z-ipqIqU3ks,457
@@ -47,8 +47,8 @@ tuieval/templates/packs/tool_call/pack.toml,sha256=n4bx0lGJHSmW41uVqhfIXJ2BWZpmT
47
47
  tuieval/templates/packs/tool_call/system.txt,sha256=mKxu-lB-0YjLHXdgeFJa8x2C180OvcNz5cHv2N5Bg3U,254
48
48
  tuieval/templates/packs/tool_call/tests.yaml,sha256=Nv7c4syrzJ9cU8EBKSK_1ZSFx9V02aCjW4-2TBCPT7s,1859
49
49
  tuieval/templates/packs/tool_call/tools.yaml,sha256=HgxCkh0On2VwbHSsjAGM8tNCK7ZtKLGrs7lMCn3Fprs,1048
50
- tuieval-0.2.0.dev87.dist-info/METADATA,sha256=XHHaef6pqjG5DaNsjztrZJ9K_ZPhGi63ONwA_J8XFqg,14580
51
- tuieval-0.2.0.dev87.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
52
- tuieval-0.2.0.dev87.dist-info/entry_points.txt,sha256=7eIhi1GpFxBP2OLJV02Nn8LwsNuO512eKOGt1IBbj04,45
53
- tuieval-0.2.0.dev87.dist-info/licenses/LICENSE,sha256=7wfRgGtBGH1aXhZkIn47oUznqKC18kEPMvUVgol7Ffc,1077
54
- tuieval-0.2.0.dev87.dist-info/RECORD,,
50
+ tuieval-0.2.0.dev89.dist-info/METADATA,sha256=t3hBuHdUYssk-1LBJ7YfWAHkTPjsTXR9CnErwPjDKo8,14580
51
+ tuieval-0.2.0.dev89.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
52
+ tuieval-0.2.0.dev89.dist-info/entry_points.txt,sha256=7eIhi1GpFxBP2OLJV02Nn8LwsNuO512eKOGt1IBbj04,45
53
+ tuieval-0.2.0.dev89.dist-info/licenses/LICENSE,sha256=7wfRgGtBGH1aXhZkIn47oUznqKC18kEPMvUVgol7Ffc,1077
54
+ tuieval-0.2.0.dev89.dist-info/RECORD,,