tuieval 0.2.0.dev135__tar.gz → 0.2.0.dev137__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.dev135 → tuieval-0.2.0.dev137}/CHANGELOG.md +1 -1
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/PKG-INFO +1 -1
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/_version.py +2 -2
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/tui.py +24 -5
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/tests/test_tuieval.py +49 -6
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/.gitignore +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/LICENSE +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/README.md +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/RELEASING.md +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/agent-packs.md +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/images/brand/README.md +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/images/brand/favicon.ico +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/images/brand/readme-header.png +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/images/brand/social-preview.png +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/images/brand/tuieval-icon-1024.png +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/images/brand/tuieval-icon-128.png +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/images/brand/tuieval-icon-16.png +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/images/brand/tuieval-icon-16.svg +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/images/brand/tuieval-icon-256.png +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/images/brand/tuieval-icon-32.png +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/images/brand/tuieval-icon-48.png +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/images/brand/tuieval-icon-512.png +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/images/brand/tuieval-icon-64.png +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/images/brand/tuieval-icon-animated.svg +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/images/brand/tuieval-icon.svg +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/images/tui-answer-reasoning.png +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/images/tui-run-reasoning.png +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/images/tui-setup.png +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/models.md +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/docs/writing-packs.md +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/pyproject.toml +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/__init__.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/__main__.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/agents.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/builtin.toml +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/cli.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/client.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/compare.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/dates.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/doctor.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/engine.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/export.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/graders/__init__.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/graders/answer.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/graders/code.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/graders/rag.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/graders/reply.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/graders/tool_call.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/insights.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/logs.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/machines.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/onboard.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/packs.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/profiles.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/pta.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/remove.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/run_evals.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/scaffold.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/selftest.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/models.toml +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/packs/answer/pack.toml +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/packs/answer/system.txt +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/packs/answer/tests.yaml +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/packs/code/pack.toml +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/packs/code/system.txt +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/packs/code/tests.yaml +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/packs/rag/pack.toml +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/packs/rag/system.txt +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/packs/rag/tests.yaml +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/packs/reply/pack.toml +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/packs/reply/system.txt +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/packs/reply/tests.yaml +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/packs/tool_call/pack.toml +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/packs/tool_call/system.txt +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/packs/tool_call/tests.yaml +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/packs/tool_call/tools.yaml +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/tune.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/verdict.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/watch_proxy.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/workspace.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/yamlout.py +0 -0
- {tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/tests/mock_server.py +0 -0
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
5
|
- Answers stuck in a loop (one short unit repeated over 2000 chars, in the reasoning or the answer) are stopped as they stream and failed as `LOOP`, instead of running to max_tokens. `[defaults] loop_stop_chars` sets the length; 0 turns it off.
|
|
6
|
-
- Repeats: the PTA index, `tuieval compare` and Setup show how many times each model answered each question (`×3`, `×1-3`), so single-run results stand out.
|
|
6
|
+
- Repeats: the PTA index, Per question, `tuieval compare` and Setup show how many times each model answered each question (`×3`, `×1-3`), so single-run results stand out.
|
|
7
7
|
- Partial credit: the PTA index shows each model's score (e.g. 5 of 6 checks passed is 83%) beside accuracy, and can be ordered by it (By score, `tuieval pta --sort score`). Why models fail splits failures into near miss, partly right and wrong; Failures shows each one's kind and checks and filters by kind; Per question cells show `✗ 5/6`. Verdicts are unchanged.
|
|
8
8
|
- 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.
|
|
9
9
|
- 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).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: tuieval
|
|
3
|
-
Version: 0.2.0.
|
|
3
|
+
Version: 0.2.0.dev137
|
|
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.dev137'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 2, 0, 'dev137')
|
|
23
23
|
|
|
24
24
|
__commit_id__ = commit_id = None
|
|
@@ -431,6 +431,15 @@ def trim_names(names, width):
|
|
|
431
431
|
return trimmed if len(set(trimmed)) == len(set(names)) else list(names)
|
|
432
432
|
|
|
433
433
|
|
|
434
|
+
def repeats_tag(counts):
|
|
435
|
+
"""A model's answers per question as markup, as the PTA index shows them: "×3", or "×1-3" when it varies;
|
|
436
|
+
yellow when it's ×1 (one answer decides each result, the roughest), else dim. "" for no answers."""
|
|
437
|
+
if not counts:
|
|
438
|
+
return ""
|
|
439
|
+
few, most = min(counts), max(counts)
|
|
440
|
+
return f"[{'yellow' if most == 1 else 'dim'}]×{few}{'' if few == most else f'-{most}'}[/]"
|
|
441
|
+
|
|
442
|
+
|
|
434
443
|
def fit_first_column(header, rows, room, floor=24):
|
|
435
444
|
"""rows with the first cell trimmed in the middle (mid_trim) so a table of these columns fits room
|
|
436
445
|
cells across, but never below floor cells. Cells are Rich markup; each column adds 2 cells of padding."""
|
|
@@ -3136,7 +3145,10 @@ class ResultsScreen(Screen):
|
|
|
3136
3145
|
base = column_name(col.label)
|
|
3137
3146
|
label = base + SORT_ARROWS[desc] if base == name else base
|
|
3138
3147
|
if col.label.plain != label:
|
|
3139
|
-
col.label
|
|
3148
|
+
text = col.label.copy() # keeps its styling, e.g. a model's ×n
|
|
3149
|
+
if len(text.plain) > len(base): # right_crop(0) would empty it
|
|
3150
|
+
text.right_crop(len(text.plain) - len(base))
|
|
3151
|
+
col.label = text + SORT_ARROWS[desc] if base == name else text
|
|
3140
3152
|
w = cell_len(label) # room for the arrow (a column keeps the width it was added with)
|
|
3141
3153
|
col.content_width = max(col.content_width, w)
|
|
3142
3154
|
if not col.auto_width:
|
|
@@ -3404,7 +3416,9 @@ class ResultsScreen(Screen):
|
|
|
3404
3416
|
here = self.app.engine.machine().id
|
|
3405
3417
|
table = self.query_one("#per-question", DataTable)
|
|
3406
3418
|
table.clear(columns=True)
|
|
3407
|
-
table.add_columns(*self.PQ_FIXED, *
|
|
3419
|
+
table.add_columns(*self.PQ_FIXED, *(Text.from_markup(
|
|
3420
|
+
f"{rich_escape(m)} {repeats_tag([t['cells'][m]['n'] for t in tests if m in t['cells']])}".rstrip())
|
|
3421
|
+
for m in models))
|
|
3408
3422
|
self.pq_tests, self.pq_models = {}, models
|
|
3409
3423
|
for t in tests:
|
|
3410
3424
|
cells = []
|
|
@@ -3427,7 +3441,8 @@ class ResultsScreen(Screen):
|
|
|
3427
3441
|
"[green]bold[/green] = fewest tokens among passing answers · [red]cut[/red] = hit max_tokens · "
|
|
3428
3442
|
"[yellow]over[/yellow] = ran past max_tokens (the provider didn't stop it) · "
|
|
3429
3443
|
"◐ = pack unfinished (not in scores) · dim seconds = hosted, or measured on another machine; tokens compare "
|
|
3430
|
-
"across machines, seconds don't. ×tokens = most / fewest tokens across models.
|
|
3444
|
+
"across machines, seconds don't. ×tokens = most / fewest tokens across models. ×n beside a model: answers "
|
|
3445
|
+
"per question (×1: one answer decides each result, the roughest). Enter on a cell opens the "
|
|
3431
3446
|
"answers. Click a column name to sort by it (a model's column: by its tokens).[/dim]" + ("\n[yellow]Left out, being rerun with other questions or settings: " + ", ".join(
|
|
3432
3447
|
f"{os.path.basename(os.path.dirname(p))} · {os.path.basename(p)[:-5]}" for p in sorted(self.superseded))
|
|
3433
3448
|
+ ". Only the new run's answers (◐) are shown for these.[/yellow]" if self.superseded else ""))
|
|
@@ -3469,6 +3484,7 @@ class ResultsScreen(Screen):
|
|
|
3469
3484
|
def hi(v, text, key, good):
|
|
3470
3485
|
ref = best.get(key) if good == "max" else low.get(key)
|
|
3471
3486
|
return f"[bold green]{text}[/bold green]" if v is not None and v == ref and len(rows) > 1 else text
|
|
3487
|
+
tags = {m: repeats_tag([t["cells"][m]["n"] for t in shared]) for m in models}
|
|
3472
3488
|
cells = []
|
|
3473
3489
|
for r in rows:
|
|
3474
3490
|
tt = r["total_secs"]
|
|
@@ -3489,7 +3505,10 @@ class ResultsScreen(Screen):
|
|
|
3489
3505
|
(f"[red]{r['cut']}[/red]" if r["cut"] else "0")
|
|
3490
3506
|
+ (f" [yellow]+{r['over']} over[/yellow]" if r.get("over") else "")])
|
|
3491
3507
|
table.add_columns(*header)
|
|
3492
|
-
|
|
3508
|
+
tag_w = max(Text.from_markup(tag).cell_len + 1 for tag in tags.values())
|
|
3509
|
+
cells = fit_first_column(header, cells, (self.size.width or self.app.size.width) - 3 - tag_w,
|
|
3510
|
+
floor=24 - tag_w) # the column's floor counts the ×n too
|
|
3511
|
+
table.add_rows([[f"{rich_escape(c[0])} {tags[r['model']]}"] + c[1:] for c, r in zip(cells, rows)])
|
|
3493
3512
|
line = f"[b]Comparison[/b] over the {len(shared)} questions all {len(models)} answered"
|
|
3494
3513
|
if len(models) == 2:
|
|
3495
3514
|
h = compare.head_to_head(shared, *models)
|
|
@@ -3506,7 +3525,7 @@ class ResultsScreen(Screen):
|
|
|
3506
3525
|
title.update(line + " [dim]Time / Tokens: each question's median, added up. Med tok / Med s: median "
|
|
3507
3526
|
"per answer. Tok/✓: tokens per answer right. Prompt t/s / Gen t/s: all tokens over all the time "
|
|
3508
3527
|
"they took (cached prompts left out). Passed: questions right on every repeat, then the score (partial credit, e.g. checks passed) when some failed. Fewest / Fastest: questions won; ties count for both. "
|
|
3509
|
-
"Cut off: answers that hit max_tokens.[/dim]")
|
|
3528
|
+
"Cut off: answers that hit max_tokens. ×n beside a name: answers per question.[/dim]")
|
|
3510
3529
|
|
|
3511
3530
|
@staticmethod
|
|
3512
3531
|
def pq_cell(c, best, here):
|
|
@@ -1739,7 +1739,7 @@ class PTAIndex(unittest.TestCase):
|
|
|
1739
1739
|
print(t.row_count, str(s.query_one("#fail-count", Static).render()))
|
|
1740
1740
|
s.fill_per_question()
|
|
1741
1741
|
pq = s.query_one("#per-question", DataTable)
|
|
1742
|
-
cols = [str(c.label) for c in pq.ordered_columns]
|
|
1742
|
+
cols = [str(c.label).split(" ×")[0] for c in pq.ordered_columns]
|
|
1743
1743
|
print(sorted(str(pq.get_row_at(i)[cols.index("close")]).split()[1] for i in range(pq.row_count)))
|
|
1744
1744
|
asyncio.run(go())
|
|
1745
1745
|
""")
|
|
@@ -1801,7 +1801,7 @@ class PTAIndex(unittest.TestCase):
|
|
|
1801
1801
|
s = app.screen
|
|
1802
1802
|
def state():
|
|
1803
1803
|
pq = s.query_one("#per-question", DataTable)
|
|
1804
|
-
cols = [str(c.label) for c in pq.ordered_columns][len(s.PQ_FIXED):]
|
|
1804
|
+
cols = [str(c.label).split(" ×")[0] for c in pq.ordered_columns][len(s.PQ_FIXED):]
|
|
1805
1805
|
return (sorted(app.pq_models), s.query_one("#pta-pack", Select).value,
|
|
1806
1806
|
str(s.query_one("#pq-models", Button).label), cols, s.query_one("#pq-shared").value)
|
|
1807
1807
|
s.query_one("#pq-pack", Select).value = "apps"
|
|
@@ -1955,7 +1955,7 @@ class PTAIndex(unittest.TestCase):
|
|
|
1955
1955
|
pq.move_cursor(row=0, column=col)
|
|
1956
1956
|
await pilot.press("enter")
|
|
1957
1957
|
await pilot.pause(0.3)
|
|
1958
|
-
opened[
|
|
1958
|
+
opened[pq.ordered_columns[col].label.plain.split(" ×")[0]] = app.screen.entries[0]["model"]
|
|
1959
1959
|
app.screen.dismiss()
|
|
1960
1960
|
await pilot.pause(0.2)
|
|
1961
1961
|
print(json.dumps({"rows": rows, "bars": "P parsimony" in tri, "disagree": [both, alone], "opened": opened,
|
|
@@ -2271,6 +2271,7 @@ class FirstRun(unittest.TestCase):
|
|
|
2271
2271
|
tuieval(self.ws, "add", "my-model", "--server", "local")
|
|
2272
2272
|
code = textwrap.dedent("""
|
|
2273
2273
|
import asyncio
|
|
2274
|
+
from rich.text import Text
|
|
2274
2275
|
from tuieval import compare
|
|
2275
2276
|
from tuieval.tui import EvalsApp, ResultsScreen
|
|
2276
2277
|
from textual.widgets import DataTable, TabbedContent
|
|
@@ -2288,14 +2289,56 @@ class FirstRun(unittest.TestCase):
|
|
|
2288
2289
|
app.screen.fill_pq_summary(compare.per_question(rows), models)
|
|
2289
2290
|
await pilot.pause(0.3)
|
|
2290
2291
|
t = app.screen.query_one("#pq-summary", DataTable)
|
|
2291
|
-
names = [
|
|
2292
|
+
names = [Text.from_markup(t.get_row_at(i)[0]).plain for i in range(t.row_count)]
|
|
2292
2293
|
print(t.row_count, t.region.height, t.virtual_size.width <= t.region.width,
|
|
2293
|
-
all(n.endswith("-q4_k_m-vllm") and "…" in n for n in names), len(set(names)))
|
|
2294
|
+
all(n.endswith("-q4_k_m-vllm ×1") and "…" in n for n in names), len(set(names)))
|
|
2294
2295
|
asyncio.run(go())
|
|
2295
2296
|
""")
|
|
2296
2297
|
out = subprocess.run([sys.executable, "-c", code], capture_output=True, text=True,
|
|
2297
2298
|
env=dict(os.environ, TUIEVAL_HOME=self.ws, TUIEVAL_DETECT_PORTS=""), timeout=60)
|
|
2298
|
-
|
|
2299
|
+
# every row, no scroll; a trimmed name keeps its ×n
|
|
2300
|
+
self.assertEqual(out.stdout.strip(), "12 13 True True 12", out.stderr[-2000:])
|
|
2301
|
+
|
|
2302
|
+
def test_per_question_shows_repeats(self):
|
|
2303
|
+
tuieval(self.ws, "add", "my-model", "--server", "local")
|
|
2304
|
+
code = textwrap.dedent("""
|
|
2305
|
+
import asyncio
|
|
2306
|
+
from tuieval.tui import EvalsApp, ResultsScreen
|
|
2307
|
+
from textual.widgets import DataTable, TabbedContent
|
|
2308
|
+
rows = [{"model": m, "suite": "p", "test": f"p: q{q}", "ok": True, "latency": 1000, "tokens": 10,
|
|
2309
|
+
"truncated": False, "gen_tps": 20.0, "prompt_tps": None, "repeat": r, "score": 1.0}
|
|
2310
|
+
for m, reps in (("once", 1), ("thrice", 3)) for q in range(2) for r in range(reps)]
|
|
2311
|
+
rows += [dict(rows[-1], test="p: q2", repeat=0)] # one more question, answered once
|
|
2312
|
+
async def go():
|
|
2313
|
+
app = EvalsApp({})
|
|
2314
|
+
async with app.run_test(size=(160, 40)) as pilot:
|
|
2315
|
+
await pilot.pause()
|
|
2316
|
+
app.push_screen(ResultsScreen())
|
|
2317
|
+
await pilot.pause(0.5)
|
|
2318
|
+
s = app.screen
|
|
2319
|
+
s.rows, s.partial_rows, s.superseded = rows, [], set()
|
|
2320
|
+
s.query_one(TabbedContent).active = "tab-per-question"
|
|
2321
|
+
await pilot.pause(0.3)
|
|
2322
|
+
s.fill_per_question()
|
|
2323
|
+
await pilot.pause(0.3)
|
|
2324
|
+
pq = s.query_one("#per-question", DataTable)
|
|
2325
|
+
heads = [c.label for c in pq.ordered_columns][len(s.PQ_FIXED):]
|
|
2326
|
+
print([h.plain for h in heads], [str(h.spans[0].style) for h in heads])
|
|
2327
|
+
app.result_sorts["per-question"] = ("thrice ×1-3", True) # as a click on its name does
|
|
2328
|
+
s.sort_table(pq)
|
|
2329
|
+
head = pq.ordered_columns[-1].label
|
|
2330
|
+
print(head.plain, str(head.spans[0].style))
|
|
2331
|
+
t = s.query_one("#pq-summary", DataTable)
|
|
2332
|
+
print([t.get_row_at(i)[0] for i in range(t.row_count)])
|
|
2333
|
+
asyncio.run(go())
|
|
2334
|
+
""")
|
|
2335
|
+
out = subprocess.run([sys.executable, "-c", code], capture_output=True, text=True,
|
|
2336
|
+
env=dict(os.environ, TUIEVAL_HOME=self.ws, TUIEVAL_DETECT_PORTS=""), timeout=60)
|
|
2337
|
+
self.assertEqual(out.stdout.strip().splitlines(), [
|
|
2338
|
+
"['once ×1', 'thrice ×1-3'] ['yellow', 'dim']", # over the questions shown
|
|
2339
|
+
"thrice ×1-3 ▼ dim", # sorting keeps the ×n's colour
|
|
2340
|
+
"['once [yellow]×1[/]', 'thrice [dim]×3[/]']", # Comparison: only the questions both answered
|
|
2341
|
+
], out.stderr[-2000:])
|
|
2299
2342
|
|
|
2300
2343
|
def test_results_open_on_pta_index(self):
|
|
2301
2344
|
tuieval(self.ws, "add", "my-model", "--server", "local")
|
|
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
|
|
File without changes
|
{tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/packs/tool_call/pack.toml
RENAMED
|
File without changes
|
{tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/packs/tool_call/system.txt
RENAMED
|
File without changes
|
{tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/src/tuieval/templates/packs/tool_call/tests.yaml
RENAMED
|
File without changes
|
{tuieval-0.2.0.dev135 → tuieval-0.2.0.dev137}/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
|