tuieval 0.2.0.dev116__tar.gz → 0.2.0.dev118__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 (80) hide show
  1. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/CHANGELOG.md +1 -0
  2. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/PKG-INFO +1 -1
  3. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/_version.py +2 -2
  4. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/tune.py +33 -0
  5. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/tests/test_tuieval.py +31 -0
  6. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/.gitignore +0 -0
  7. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/LICENSE +0 -0
  8. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/README.md +0 -0
  9. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/RELEASING.md +0 -0
  10. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/images/brand/README.md +0 -0
  11. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/images/brand/favicon.ico +0 -0
  12. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/images/brand/readme-header.png +0 -0
  13. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/images/brand/social-preview.png +0 -0
  14. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/images/brand/tuieval-icon-1024.png +0 -0
  15. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/images/brand/tuieval-icon-128.png +0 -0
  16. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/images/brand/tuieval-icon-16.png +0 -0
  17. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/images/brand/tuieval-icon-16.svg +0 -0
  18. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/images/brand/tuieval-icon-256.png +0 -0
  19. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/images/brand/tuieval-icon-32.png +0 -0
  20. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/images/brand/tuieval-icon-48.png +0 -0
  21. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/images/brand/tuieval-icon-512.png +0 -0
  22. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/images/brand/tuieval-icon-64.png +0 -0
  23. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/images/brand/tuieval-icon-animated.svg +0 -0
  24. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/images/brand/tuieval-icon.svg +0 -0
  25. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/images/tui-answer-reasoning.png +0 -0
  26. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/images/tui-run-reasoning.png +0 -0
  27. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/images/tui-setup.png +0 -0
  28. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/models.md +0 -0
  29. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/docs/writing-packs.md +0 -0
  30. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/pyproject.toml +0 -0
  31. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/__init__.py +0 -0
  32. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/__main__.py +0 -0
  33. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/builtin.toml +0 -0
  34. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/cli.py +0 -0
  35. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/client.py +0 -0
  36. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/compare.py +0 -0
  37. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/dates.py +0 -0
  38. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/doctor.py +0 -0
  39. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/engine.py +0 -0
  40. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/export.py +0 -0
  41. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/graders/__init__.py +0 -0
  42. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/graders/answer.py +0 -0
  43. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/graders/code.py +0 -0
  44. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/graders/rag.py +0 -0
  45. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/graders/reply.py +0 -0
  46. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/graders/tool_call.py +0 -0
  47. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/insights.py +0 -0
  48. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/logs.py +0 -0
  49. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/machines.py +0 -0
  50. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/onboard.py +0 -0
  51. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/packs.py +0 -0
  52. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/profiles.py +0 -0
  53. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/pta.py +0 -0
  54. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/remove.py +0 -0
  55. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/run_evals.py +0 -0
  56. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/scaffold.py +0 -0
  57. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/selftest.py +0 -0
  58. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/templates/models.toml +0 -0
  59. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/templates/packs/answer/pack.toml +0 -0
  60. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/templates/packs/answer/system.txt +0 -0
  61. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/templates/packs/answer/tests.yaml +0 -0
  62. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/templates/packs/code/pack.toml +0 -0
  63. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/templates/packs/code/system.txt +0 -0
  64. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/templates/packs/code/tests.yaml +0 -0
  65. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/templates/packs/rag/pack.toml +0 -0
  66. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/templates/packs/rag/system.txt +0 -0
  67. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/templates/packs/rag/tests.yaml +0 -0
  68. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/templates/packs/reply/pack.toml +0 -0
  69. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/templates/packs/reply/system.txt +0 -0
  70. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/templates/packs/reply/tests.yaml +0 -0
  71. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/templates/packs/tool_call/pack.toml +0 -0
  72. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/templates/packs/tool_call/system.txt +0 -0
  73. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/templates/packs/tool_call/tests.yaml +0 -0
  74. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/templates/packs/tool_call/tools.yaml +0 -0
  75. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/tui.py +0 -0
  76. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/verdict.py +0 -0
  77. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/watch_proxy.py +0 -0
  78. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/workspace.py +0 -0
  79. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/src/tuieval/yamlout.py +0 -0
  80. {tuieval-0.2.0.dev116 → tuieval-0.2.0.dev118}/tests/mock_server.py +0 -0
@@ -2,6 +2,7 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - Tuning: a server that refuses the `ignore_eos` request field is asked again without it, so strict servers can be tuned; the profile notes that workload answers may end early.
5
6
  - Fix: a server that crashes mid-request (the stream ends with no finish reason) was graded as an empty or cut-short answer; it's a server error now, never counted against the model.
6
7
  - Dates: answers record `answered_at` (older ones read it from their log name); tests get an added date (an `added` field, or the day tuieval first saw them, in `test-dates.json`). Shown in the answer view, Pick tests, Per question, Failures and Production readiness.
7
8
  - Copy reasoning: `y` in the answer view copies its full reasoning, `a` the answer; `y` on the run screen copies the reasoning streamed so far. Uses pbcopy, wl-copy, xclip, xsel or clip, else the terminal (OSC 52).
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: tuieval
3
- Version: 0.2.0.dev116
3
+ Version: 0.2.0.dev118
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.dev116'
22
- __version_tuple__ = version_tuple = (0, 2, 0, 'dev116')
21
+ __version__ = version = '0.2.0.dev118'
22
+ __version_tuple__ = version_tuple = (0, 2, 0, 'dev118')
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -169,7 +169,37 @@ def _body(m, sampling, msgs, max_tokens=GEN_TOKENS, request=None):
169
169
  **(request or {})}
170
170
 
171
171
 
172
+ def _rejects_ignore_eos(res):
173
+ """True if the server refused the request because it doesn't accept the ignore_eos field."""
174
+ err = res.get("error") or ""
175
+ return err.startswith("server returned HTTP 4") and "ignore_eos" in err
176
+
177
+
172
178
  def _request(eng, base_url, body, name, emit, limit):
179
+ """One streamed request with live progress and a wall-clock limit. A server that refuses
180
+ ignore_eos is asked again without it, and later requests leave it out (see no_ignore_eos)."""
181
+ key = (base_url, body.get("model"))
182
+ rejected = no_ignore_eos(eng)
183
+ if key in rejected:
184
+ body = {k: v for k, v in body.items() if k != "ignore_eos"}
185
+ res = _stream_request(eng, base_url, body, name, emit, limit)
186
+ if body.get("ignore_eos") and _rejects_ignore_eos(res):
187
+ rejected.add(key)
188
+ emit("tune_step", message=" the server doesn't accept ignore_eos: answers may end early "
189
+ "(speeds are per token, so still comparable)")
190
+ res = _stream_request(eng, base_url, {k: v for k, v in body.items() if k != "ignore_eos"},
191
+ name, emit, limit)
192
+ return res
193
+
194
+
195
+ def no_ignore_eos(eng):
196
+ """(base_url, served model) pairs whose server refused ignore_eos in this tuning session."""
197
+ if not hasattr(eng, "_tune_no_ignore_eos"):
198
+ eng._tune_no_ignore_eos = set()
199
+ return eng._tune_no_ignore_eos
200
+
201
+
202
+ def _stream_request(eng, base_url, body, name, emit, limit):
173
203
  """One streamed request with live progress (every PROGRESS_EVERY_S: waiting for the first
174
204
  token, or ~tokens so far and tok/s) and a wall-clock limit. Returns client.stream_chat's result."""
175
205
  st, t0 = client.Stream(), time.time()
@@ -681,6 +711,9 @@ def tune(eng, label, emit=lambda *a, **k: None, max_starts=16, min_gain=0.03, us
681
711
  if warm_info:
682
712
  notes.insert(0, f"warm start from {warm_info['from']}: inherited "
683
713
  f"{', '.join(warm_info['inherited']) or 'nothing'}")
714
+ if any(model == m["served_name"] for _, model in no_ignore_eos(eng)):
715
+ notes.insert(0, f"the server doesn't accept ignore_eos, so workload answers could end before "
716
+ f"{gen_tokens} tokens (speeds are per token)")
684
717
  if best_r.facts.get("extra_swap_mb") and best_r is not base:
685
718
  warnings.append(f"the chosen flags use more memory than the defaults: macOS swapped "
686
719
  f"~{best_r.facts['extra_swap_mb']} MB more of other apps' memory; close apps during runs, "
@@ -2512,6 +2512,37 @@ class WarmTune(unittest.TestCase):
2512
2512
  self.assertEqual(out.score("projected"), 66.0)
2513
2513
  self.assertEqual(out.score("total"), 10.0)
2514
2514
 
2515
+ def test_server_refusing_ignore_eos_is_asked_without_it(self):
2516
+ sent = []
2517
+
2518
+ def server(eng, base_url, body, *a): # refuses unknown fields, like a strict server
2519
+ sent.append(dict(body))
2520
+ if "ignore_eos" in body:
2521
+ return {"error": 'server returned HTTP 400: {"error":{"message":"unsupported request field: '
2522
+ 'ignore_eos"}}', "answer": "", "reasoning": ""}
2523
+ return {"error": None, "answer": "ok", "reasoning": ""}
2524
+ orig = self.tune._stream_request
2525
+ self.tune._stream_request = server
2526
+ self.addCleanup(setattr, self.tune, "_stream_request", orig)
2527
+ body = {"model": "new", "messages": [], "ignore_eos": True}
2528
+ res = self.tune._request(self.eng, "http://x", body, "p", lambda *a, **k: None, 60)
2529
+ self.assertIsNone(res["error"])
2530
+ self.assertEqual(["ignore_eos" in b for b in sent], [True, False]) # retried without it
2531
+ self.tune._request(self.eng, "http://x", body, "p", lambda *a, **k: None, 60)
2532
+ self.assertEqual(len(sent), 3) # later requests leave it out at once
2533
+ self.assertNotIn("ignore_eos", sent[-1])
2534
+ self.assertIn(("http://x", "new"), self.tune.no_ignore_eos(self.eng))
2535
+
2536
+ def test_other_request_errors_are_not_retried(self):
2537
+ sent = []
2538
+ orig = self.tune._stream_request
2539
+ self.tune._stream_request = lambda eng, url, body, *a: sent.append(body) or {
2540
+ "error": "server returned HTTP 400: bad messages", "answer": "", "reasoning": ""}
2541
+ self.addCleanup(setattr, self.tune, "_stream_request", orig)
2542
+ res = self.tune._request(self.eng, "http://x", {"model": "new", "ignore_eos": True}, "p",
2543
+ lambda *a, **k: None, 60)
2544
+ self.assertEqual((len(sent), res["error"]), (1, "server returned HTTP 400: bad messages"))
2545
+
2515
2546
  def test_family_ignores_the_mtp_layer(self):
2516
2547
  # one GGUF lists KV heads per layer, none on the MTP (last) layer; the other gives one number
2517
2548
  d = self.tmp.name
File without changes
File without changes