tuieval 0.2.0.dev127__tar.gz → 0.2.0.dev129__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.
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/CHANGELOG.md +1 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/PKG-INFO +1 -1
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/_version.py +2 -2
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/compare.py +11 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/tui.py +47 -9
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/tests/test_tuieval.py +77 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/.gitignore +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/LICENSE +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/README.md +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/RELEASING.md +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/agent-packs.md +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/images/brand/README.md +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/images/brand/favicon.ico +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/images/brand/readme-header.png +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/images/brand/social-preview.png +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/images/brand/tuieval-icon-1024.png +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/images/brand/tuieval-icon-128.png +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/images/brand/tuieval-icon-16.png +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/images/brand/tuieval-icon-16.svg +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/images/brand/tuieval-icon-256.png +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/images/brand/tuieval-icon-32.png +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/images/brand/tuieval-icon-48.png +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/images/brand/tuieval-icon-512.png +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/images/brand/tuieval-icon-64.png +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/images/brand/tuieval-icon-animated.svg +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/images/brand/tuieval-icon.svg +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/images/tui-answer-reasoning.png +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/images/tui-run-reasoning.png +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/images/tui-setup.png +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/models.md +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/docs/writing-packs.md +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/pyproject.toml +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/__init__.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/__main__.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/agents.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/builtin.toml +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/cli.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/client.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/dates.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/doctor.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/engine.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/export.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/graders/__init__.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/graders/answer.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/graders/code.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/graders/rag.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/graders/reply.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/graders/tool_call.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/insights.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/logs.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/machines.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/onboard.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/packs.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/profiles.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/pta.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/remove.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/run_evals.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/scaffold.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/selftest.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/models.toml +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/answer/pack.toml +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/answer/system.txt +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/answer/tests.yaml +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/code/pack.toml +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/code/system.txt +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/code/tests.yaml +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/rag/pack.toml +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/rag/system.txt +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/rag/tests.yaml +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/reply/pack.toml +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/reply/system.txt +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/reply/tests.yaml +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/tool_call/pack.toml +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/tool_call/system.txt +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/tool_call/tests.yaml +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/tool_call/tools.yaml +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/tune.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/verdict.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/watch_proxy.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/workspace.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/yamlout.py +0 -0
- {tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/tests/mock_server.py +0 -0
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- Results: picking a pack (PTA index or Per question) compares the models that answered all of it; the Models button can change that, and All packs goes back to the earlier pick.
|
|
5
6
|
- Fix: a request that runs out of time says `time limit: timed out after N s`, not `connection error`, and `tuieval compare` counts it against the model like the run does (it was left out as an error).
|
|
6
7
|
- Agent packs (`kind = "agent"`): each test is a small project with a task, worked on by a command-line agent you define in `[agents]`, using the model tuieval serves, and graded by the task's visible and hidden tests. Results show the agent's stats, diff and transcript; `tuieval compare` adds an Agent tasks table; `tuieval selftest` checks each task's reference and wrong fixes. See docs/agent-packs.md.
|
|
7
8
|
- Answers that used more tokens than the `max_tokens` sent (a provider that didn't stop them) are marked over budget: `(n over budget)` in the PTA index, an **over budget** column, `over` in Per question and a note in the answer view. `[defaults] over_budget = "fail"` fails them instead. Works on stored results.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: tuieval
|
|
3
|
-
Version: 0.2.0.
|
|
3
|
+
Version: 0.2.0.dev129
|
|
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.
|
|
22
|
-
__version_tuple__ = version_tuple = (0, 2, 0, '
|
|
21
|
+
__version__ = version = '0.2.0.dev129'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 0, 'dev129')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -390,6 +390,17 @@ def with_models(test, models):
|
|
|
390
390
|
return dict(test, cells=cells, spread=_spread(cells))
|
|
391
391
|
|
|
392
392
|
|
|
393
|
+
def complete_models(rows, suite, test_ids=None):
|
|
394
|
+
"""The models whose rows answer every question of a pack: test_ids (the pack's current tests),
|
|
395
|
+
or every question of it seen in the rows. Partial rows (a pack still running) don't count."""
|
|
396
|
+
rows = [r for r in rows if r["suite"] == suite and not r.get("partial")]
|
|
397
|
+
wanted = {f"{suite}: {t}" for t in test_ids} if test_ids is not None else {r["test"] for r in rows}
|
|
398
|
+
answered = {}
|
|
399
|
+
for r in rows:
|
|
400
|
+
answered.setdefault(r["model"], set()).add(r["test"])
|
|
401
|
+
return sorted(m for m, tests in answered.items() if wanted and wanted <= tests)
|
|
402
|
+
|
|
403
|
+
|
|
393
404
|
def compare_models(tests, models):
|
|
394
405
|
"""Per model over per_question entries that all `models` answered: passes, token and time
|
|
395
406
|
totals/medians (totals add each question's median over repeats, so repeats don't inflate them), questions where it used the fewest tokens / was fastest (among passing
|
|
@@ -2963,6 +2963,9 @@ class ResultsScreen(Screen):
|
|
|
2963
2963
|
self.rows, self.infos, self.partial_rows, self.superseded = [], [], [], set()
|
|
2964
2964
|
if not hasattr(self.app, "pq_models"):
|
|
2965
2965
|
self.app.pq_models = set()
|
|
2966
|
+
if getattr(self.app, "pack_pick", None): # the pack lists open on All packs again
|
|
2967
|
+
self.app.pq_models = self.app.pack_pick["before"]
|
|
2968
|
+
self.app.pack_pick = None
|
|
2966
2969
|
if not hasattr(self.app, "result_sorts"):
|
|
2967
2970
|
self.app.result_sorts = {} # table id -> (column name, descending), kept for this session
|
|
2968
2971
|
self.fill()
|
|
@@ -3054,7 +3057,7 @@ class ResultsScreen(Screen):
|
|
|
3054
3057
|
for i, (model, test, reason, level) in enumerate(compare.failures(rows)):
|
|
3055
3058
|
when = dates.day(dates.answered_at(self.fail_rows[i].get("raw") or {}))
|
|
3056
3059
|
f.add_row(model, diff_badge(level), rich_escape(test), when, rich_escape(reason[:130]), key=str(i))
|
|
3057
|
-
self.query_one("#fail-models", Button).label =
|
|
3060
|
+
self.query_one("#fail-models", Button).label = self.models_label(chosen)
|
|
3058
3061
|
n = len(self.fail_rows)
|
|
3059
3062
|
self.query_one("#fail-count", Static).update(
|
|
3060
3063
|
f"{n} failure{'s' * (n != 1)}" + (f" of {len(chosen)} model{'s' * (len(chosen) != 1)}" if chosen else ""))
|
|
@@ -3072,7 +3075,6 @@ class ResultsScreen(Screen):
|
|
|
3072
3075
|
self.app.push_screen(AnswerScreen(keep, keep.index(entries[i]), what="failure"))
|
|
3073
3076
|
|
|
3074
3077
|
# -- per question: tokens and time for every model on the same question
|
|
3075
|
-
@on(Select.Changed, "#pq-pack")
|
|
3076
3078
|
@on(Select.Changed, "#pq-level")
|
|
3077
3079
|
@on(Checkbox.Changed, "#pq-partial")
|
|
3078
3080
|
@on(Checkbox.Changed, "#pq-shared")
|
|
@@ -3214,7 +3216,7 @@ class ResultsScreen(Screen):
|
|
|
3214
3216
|
finally:
|
|
3215
3217
|
self.filling_insights = False
|
|
3216
3218
|
chosen = {m for m in self.app.pq_models if m in every}
|
|
3217
|
-
self.query_one("#ins-models", Button).label =
|
|
3219
|
+
self.query_one("#ins-models", Button).label = self.models_label(chosen)
|
|
3218
3220
|
main = os.path.abspath(self.results_dir or e.results_dir) == os.path.abspath(e.results_dir)
|
|
3219
3221
|
bases = {m["label"]: m["base"] for m in e.cfg["models"] if m.get("base")}
|
|
3220
3222
|
by = self.query_one("#ins-cost", Select).value or "tokens"
|
|
@@ -3222,9 +3224,46 @@ class ResultsScreen(Screen):
|
|
|
3222
3224
|
body.update("\n\n".join(insights.render(p, i) for i, p in enumerate(out, 1)))
|
|
3223
3225
|
|
|
3224
3226
|
@on(Select.Changed, "#pta-pack")
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3227
|
+
@on(Select.Changed, "#pq-pack")
|
|
3228
|
+
def pack_changed(self, event):
|
|
3229
|
+
"""A pack picked (PTA index or Per question, kept the same in both): compare the models that
|
|
3230
|
+
answered every question of it (the Models button can change that); All packs: back to the
|
|
3231
|
+
models picked before."""
|
|
3232
|
+
if getattr(self, "rows", None) is None:
|
|
3233
|
+
return
|
|
3234
|
+
e, pack = self.app.engine, event.value or ""
|
|
3235
|
+
for other, values in (("#pta-pack", "pta_pack_values"), ("#pq-pack", "pq_pack_values")):
|
|
3236
|
+
sel = self.query_one(other, Select)
|
|
3237
|
+
if sel is not event.select and sel.value != pack and pack in getattr(self, values, [""]):
|
|
3238
|
+
with sel.prevent(Select.Changed):
|
|
3239
|
+
sel.value = pack
|
|
3240
|
+
shared = self.query_one("#pq-shared", Checkbox)
|
|
3241
|
+
p = self.app.pack_pick or {"before": set(self.app.pq_models), "shared": shared.value}
|
|
3242
|
+
before = p["before"]
|
|
3243
|
+
if not pack:
|
|
3244
|
+
self.app.pack_pick = None
|
|
3245
|
+
with shared.prevent(Checkbox.Changed): # set_models may have ticked it
|
|
3246
|
+
shared.value = p["shared"]
|
|
3247
|
+
self.set_models(before)
|
|
3248
|
+
return
|
|
3249
|
+
rows = [r for r in self.rows if r.get("path") not in self.superseded]
|
|
3250
|
+
complete = set(compare.complete_models(rows, pack, [t["id"] for t in e.packs[pack].tests]
|
|
3251
|
+
if pack in e.packs else None))
|
|
3252
|
+
self.app.pack_pick = {"pack": pack, "models": complete, "before": before, "shared": p["shared"]}
|
|
3253
|
+
if not complete:
|
|
3254
|
+
self.notify(f"No model has answered every question of {self.pack_label(pack)} yet: "
|
|
3255
|
+
"the models picked before are kept.", severity="warning", timeout=8)
|
|
3256
|
+
self.set_models(complete or before)
|
|
3257
|
+
|
|
3258
|
+
def pack_label(self, pack):
|
|
3259
|
+
return self.app.engine.packs[pack].label if pack in self.app.engine.packs else pack
|
|
3260
|
+
|
|
3261
|
+
def models_label(self, chosen):
|
|
3262
|
+
"""The Models buttons' text: the models a pack pick chose say so."""
|
|
3263
|
+
p = self.app.pack_pick
|
|
3264
|
+
if chosen and p and chosen == p["models"]:
|
|
3265
|
+
return f"Models: {len(chosen)} complete on {self.pack_label(p['pack'])}"
|
|
3266
|
+
return f"Models: {len(chosen)} picked" if chosen else "Models: all"
|
|
3228
3267
|
|
|
3229
3268
|
def fill_pta(self):
|
|
3230
3269
|
"""The PTA index (pta.py) of the picked models (Models button, shared with Per question) on
|
|
@@ -3245,7 +3284,7 @@ class ResultsScreen(Screen):
|
|
|
3245
3284
|
rows = [r for r in rows if r["suite"] == sel.value]
|
|
3246
3285
|
present = {r["model"] for r in rows}
|
|
3247
3286
|
chosen = {m for m in self.app.pq_models if m in present}
|
|
3248
|
-
self.query_one("#pta-models", Button).label =
|
|
3287
|
+
self.query_one("#pta-models", Button).label = self.models_label(chosen)
|
|
3249
3288
|
t = self.query_one("#pta", DataTable)
|
|
3250
3289
|
t.clear(columns=True)
|
|
3251
3290
|
if not present:
|
|
@@ -3300,8 +3339,7 @@ class ResultsScreen(Screen):
|
|
|
3300
3339
|
all_tests = compare.per_question(rows)
|
|
3301
3340
|
self.pq_all_tests = all_tests
|
|
3302
3341
|
chosen = {m for m in self.app.pq_models if any(m in t["cells"] for t in all_tests)}
|
|
3303
|
-
self.query_one("#pq-models", Button).label = (
|
|
3304
|
-
f"Models: {len(chosen)} picked" if chosen else "Models: all")
|
|
3342
|
+
self.query_one("#pq-models", Button).label = self.models_label(chosen)
|
|
3305
3343
|
tests = []
|
|
3306
3344
|
for t in all_tests:
|
|
3307
3345
|
if (pack and t["suite"] != pack) or (level and t["difficulty"] != level):
|
|
@@ -1656,6 +1656,83 @@ class PTAIndex(unittest.TestCase):
|
|
|
1656
1656
|
self.assertEqual(sorted(cells, key=sort_key), ["10/20", "3/4", "4/4", "abc", "-"])
|
|
1657
1657
|
self.assertEqual(sorted(cells, key=lambda c: sort_key(c, True)), ["4/4", "3/4", "10/20", "abc", "-"])
|
|
1658
1658
|
|
|
1659
|
+
def test_complete_models(self):
|
|
1660
|
+
from tuieval import compare
|
|
1661
|
+
rows = [{"model": m, "suite": s, "test": f"{s}: {t}", **({"partial": True} if m == "running" else {})}
|
|
1662
|
+
for m, s, ts in (("full", "p", "abc"), ("half", "p", "ab"), ("running", "p", "abc"), ("full", "o", "a"))
|
|
1663
|
+
for t in ts]
|
|
1664
|
+
self.assertEqual(compare.complete_models(rows, "p", ["a", "b", "c"]), ["full"]) # half misses c; running is ◐
|
|
1665
|
+
self.assertEqual(compare.complete_models(rows, "p", ["a", "b"]), ["full", "half"])
|
|
1666
|
+
self.assertEqual(compare.complete_models(rows, "p", ["a", "b", "c", "d"]), []) # a question added since
|
|
1667
|
+
self.assertEqual(compare.complete_models(rows, "p"), ["full"]) # pack gone: questions seen
|
|
1668
|
+
self.assertEqual(compare.complete_models(rows, "x", []), [])
|
|
1669
|
+
|
|
1670
|
+
def test_results_pack_picks_the_models_that_answered_all_of_it(self):
|
|
1671
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
1672
|
+
ws = os.path.join(tmp, "ws")
|
|
1673
|
+
tuieval(tmp, "init", ws)
|
|
1674
|
+
for name, ids in (("apps", "abcd"), ("other", "ab")):
|
|
1675
|
+
d = os.path.join(ws, "packs", name)
|
|
1676
|
+
os.makedirs(d)
|
|
1677
|
+
write(os.path.join(d, "pack.toml"), f'label = "{name.title()}"\n[certify]\nrepeat = 1\n')
|
|
1678
|
+
write(os.path.join(d, "tests.yaml"), "".join(
|
|
1679
|
+
f"- {{id: {t}, input: question {t}, expected: 1, reference: 'ANSWER: 1', wrong: ['ANSWER: 2'], "
|
|
1680
|
+
"difficulty: easy}\n" for t in ids))
|
|
1681
|
+
mock = Mock(os.path.join(ws, "packs"), "oracle")
|
|
1682
|
+
try:
|
|
1683
|
+
with open(os.path.join(ws, "models.toml"), "a") as f:
|
|
1684
|
+
f.write(f'\n[servers.here]\nurl = "http://127.0.0.1:{mock.port}"\n')
|
|
1685
|
+
for label in ("full", "half", "solo"):
|
|
1686
|
+
tuieval(ws, "add", "mock", "--server", "here", "--label", label)
|
|
1687
|
+
tuieval(ws, "run", "--tier", "certify", "--only", "full")
|
|
1688
|
+
tuieval(ws, "run", "--tier", "certify", "--only", "half", "--packs", "apps", "--tests", "a,b")
|
|
1689
|
+
tuieval(ws, "run", "--tier", "certify", "--only", "solo", "--packs", "other")
|
|
1690
|
+
finally:
|
|
1691
|
+
mock.stop()
|
|
1692
|
+
code = textwrap.dedent("""
|
|
1693
|
+
import asyncio
|
|
1694
|
+
from tuieval.tui import EvalsApp, ResultsScreen
|
|
1695
|
+
from textual.widgets import Button, DataTable, Select
|
|
1696
|
+
async def go():
|
|
1697
|
+
app = EvalsApp({})
|
|
1698
|
+
async with app.run_test(size=(180, 50)) as pilot:
|
|
1699
|
+
await pilot.pause()
|
|
1700
|
+
app.pq_models = {"half"} # picked by hand before
|
|
1701
|
+
app.push_screen(ResultsScreen())
|
|
1702
|
+
await pilot.pause(0.5)
|
|
1703
|
+
s = app.screen
|
|
1704
|
+
def state():
|
|
1705
|
+
pq = s.query_one("#per-question", DataTable)
|
|
1706
|
+
cols = [str(c.label) for c in pq.ordered_columns][len(s.PQ_FIXED):]
|
|
1707
|
+
return (sorted(app.pq_models), s.query_one("#pta-pack", Select).value,
|
|
1708
|
+
str(s.query_one("#pq-models", Button).label), cols, s.query_one("#pq-shared").value)
|
|
1709
|
+
s.query_one("#pq-pack", Select).value = "apps"
|
|
1710
|
+
await pilot.pause(0.3)
|
|
1711
|
+
print(state())
|
|
1712
|
+
s.query_one("#pta-pack", Select).value = "other"
|
|
1713
|
+
await pilot.pause(0.3)
|
|
1714
|
+
print(state())
|
|
1715
|
+
s.query_one("#pta-pack", Select).value = ""
|
|
1716
|
+
await pilot.pause(0.3)
|
|
1717
|
+
print(state())
|
|
1718
|
+
app.pq_models = set() # none picked: All packs gives every model again
|
|
1719
|
+
s.set_models(set())
|
|
1720
|
+
await pilot.pause(0.3)
|
|
1721
|
+
for pack in ("apps", ""):
|
|
1722
|
+
s.query_one("#pq-pack", Select).value = pack
|
|
1723
|
+
await pilot.pause(0.3)
|
|
1724
|
+
print(state())
|
|
1725
|
+
asyncio.run(go())
|
|
1726
|
+
""")
|
|
1727
|
+
out = subprocess.run([sys.executable, "-c", code], capture_output=True, text=True,
|
|
1728
|
+
env=dict(os.environ, TUIEVAL_HOME=ws, TUIEVAL_DETECT_PORTS=""), timeout=60)
|
|
1729
|
+
self.assertEqual(out.stdout.strip().splitlines(), [
|
|
1730
|
+
"(['full'], 'apps', 'Models: 1 complete on Apps', ['full'], False)",
|
|
1731
|
+
"(['full', 'solo'], 'other', 'Models: 2 complete on Other', ['full', 'solo'], False)",
|
|
1732
|
+
"(['half'], '', 'Models: 1 picked', ['half'], False)",
|
|
1733
|
+
"(['full'], 'apps', 'Models: 1 complete on Apps', ['full'], True)", # none picked before: ticked
|
|
1734
|
+
"([], '', 'Models: all', ['full', 'half', 'solo'], False)"], out.stderr[-2000:])
|
|
1735
|
+
|
|
1659
1736
|
def test_cli_report_and_tui(self):
|
|
1660
1737
|
with tempfile.TemporaryDirectory() as tmp:
|
|
1661
1738
|
ws = os.path.join(tmp, "ws")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/tool_call/pack.toml
RENAMED
|
File without changes
|
{tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/tool_call/system.txt
RENAMED
|
File without changes
|
{tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/tool_call/tests.yaml
RENAMED
|
File without changes
|
{tuieval-0.2.0.dev127 → tuieval-0.2.0.dev129}/src/tuieval/templates/packs/tool_call/tools.yaml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|