feat(cli): Add pass@k and pass^k metrics to nao test KPIs - #1368
feat(cli): Add pass@k and pass^k metrics to nao test KPIs#1368justin212407 wants to merge 2 commits into
Conversation
Signed-off-by: justin212407 <charlesjustin2124@gmail.com>
|
This PR was auto-closed. Only contributors approved with Maintainers review auto-closed issues daily. Issues that do not meet the quality bar in CONTRIBUTING.md will not be reopened or receive a reply. If a maintainer replies See CONTRIBUTING.md. |
|
@Bl3f Can you take a look and review this? Please lemme know if the changes follow the desired implementation. If not happy to follow up accordingly. |
socallmebertille
left a comment
There was a problem hiding this comment.
Hi, thanks for your implementation !
Sorry for the lack of specification on this issue. Let me give you a preview of how we'd like the new metrics to be rendered in the terminal.
- First table (the one printed by nao test, one row per test) :
- The green check in the Status column should only appear when all runs of that test passed (100% success)
- Add a new column next to it showing the % of runs that passed for that test
- Second table (the summary) :
- Instead of listing every run with its success %, show only a single totals row — i.e. the aggregate across all tests (like the "total" line of the first table)
- Add an "Always Pass %" column to this totals row: the share of tests that passed on every run (100% success across all attempts)
Something similar to :
Let me know if anything is unclear!
Signed-off-by: justin212407 <charlesjustin2124@gmail.com>
There was a problem hiding this comment.
1 issue found across 7 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="cli/nao_core/commands/test/runner.py">
<violation number="1" location="cli/nao_core/commands/test/runner.py:323">
P2: The PR states the CLI table shows pass@k and pass^k columns when k>1, but the code never renders pass@k (any attempt passed). The run table exposes only pass@1 ("Success %") and pass^k ("Status"/"Always Pass %"), so pass@k is available only in the saved JSON. If pass@k should be visible in the table output as described, add a column driven by metrics["pass_at_k"] (and pass_hat_k) rather than only pass_at_1.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| "Test": name, | ||
| "Model": model, | ||
| "Status": status_icon(float(metrics["pass_hat_k"]) == 1.0), | ||
| "Success %": format_pass_fraction(float(metrics["pass_at_1"])), |
There was a problem hiding this comment.
P2: The PR states the CLI table shows pass@k and pass^k columns when k>1, but the code never renders pass@k (any attempt passed). The run table exposes only pass@1 ("Success %") and pass^k ("Status"/"Always Pass %"), so pass@k is available only in the saved JSON. If pass@k should be visible in the table output as described, add a column driven by metrics["pass_at_k"] (and pass_hat_k) rather than only pass_at_1.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At cli/nao_core/commands/test/runner.py, line 323:
<comment>The PR states the CLI table shows pass@k and pass^k columns when k>1, but the code never renders pass@k (any attempt passed). The run table exposes only pass@1 ("Success %") and pass^k ("Status"/"Always Pass %"), so pass@k is available only in the saved JSON. If pass@k should be visible in the table output as described, add a column driven by metrics["pass_at_k"] (and pass_hat_k) rather than only pass_at_1.</comment>
<file context>
@@ -295,59 +302,62 @@ def save_results(results: list[TestRunResult], output_dir: Path) -> Path:
+ "Test": name,
+ "Model": model,
+ "Status": status_icon(float(metrics["pass_hat_k"]) == 1.0),
+ "Success %": format_pass_fraction(float(metrics["pass_at_1"])),
+ "Tokens": sum(run.get("tokens") or 0 for run in group),
+ "Cost": sum(run.get("cost") or 0 for run in group),
</file context>
|
@socallmebertille I have updated the pr with the intended layout we wanted for this issue in the recent commit, here is the mockup of how the outputs look like as of now: |

Description
Closes: #1248
Adds
pass@kandpass^kmetrics tonao test, alongside the existing per-run pass/fail accuracy score.nao testcan now run each test casektimes (nao test --k 5), and reportspass@1(default),pass@k, andpass^kboth per test case and aggregated across the suite, in the CLI output and in the structured JSON results file.Type of change
Changes made
TestConfig.k(default1,ge=1) tonao_config.yaml'stest:block, overridable withk/-k, following the same pattern as the existingthreadsoption.(test case, model)pair now runsktimes instead of once. AddedTestRunResult.attempt(1-indexed) so a run is self-describing even after the threaded executor reorders results.pass_metrics_for_group()/summarize_pass_metrics()insummary.py, computing metrics empirically per(test, model)group since-k Nruns exactly N attempts, not an oversampled pool:pass@1- mean ofpassedacross the group's attemptspass@k- 1.0 if any attempt passed, else 0.0pass^k- 1.0 if all attempts passed, else 0.0pass_metricskey ({k, aggregate, by_test}). Existingsummary/by_modelkeys areunchanged in shape;
ModelSummarygained additivek/pass_at_1/pass_at_k/pass_hat_kfields.pass@1always;pass@k/pass^kcolumns only appear whenk > 1, so default (k=1) output is unchanged.with_model_summaries()still loads older result files withoutk/pass_metrics, sonao test serverkeeps working on old data.Test plan
make lintpasses (ty check,ruff check, import sort,ruff format --check)uv run pytest -qpasses - 999 passed, 225 skipped (skips are a pre-existingtest_mssql.pyenvironment gap, unrelated to this change)pass_metrics_for_group/summarize_pass_metrics(mixed pass/fail, all-pass, all-fail, k=1 equivalence, aggregation weighted by test case not attempt)attemptpopulation, and CLI/config override + rejection of invalidknao test --k 3againstexample/: confirmed config loading and correct run-count calculation (1 test × 1 model × 3 attempts = 3 runs)nao test --k 3against a running backend with a real LLM key) - not validated end to end in this environment; plumbing up to the network boundary is verified by the aboveManual testing steps:
Review guidance
Changed files:
cli/nao_core/commands/test/runner.py,cli/nao_core/commands/test/summary.py,cli/nao_core/config/test/__init__.py,cli/README.md,example/nao_config.yaml, plus matching test files undercli/tests/. No new dependencies, no changes outsidecli/(backend/frontend untouched).The metric-definition choice is worth a close look: since
--k Nruns exactly N attempts (not an oversampled pool),pass@k/pass^kare computed as direct any/all over the group rather than the combinatorial unbiased-estimator formula from the Codex/HumanEval pass@k paper - that estimator only applies when n > k.summarize_pass_metrics()andpass_metrics_for_group()insummary.pyare the two functions to check this logic in.Checklist
print/debug statements left in.Screenshots: