Repository navigation
refactor(report): split generate_section_report into per-section helpers (complexity 53 -> 4) - #169
Conversation
Eleven cases render a whole model-compare section and compare it byte for byte with a recorded golden (one JSON), ahead of splitting the 53-complexity builder into per-section helpers. Together they reach every line and branch of the function (coverage.py --branch): treatment and baseline-only runs, a mixed pair, one and two scored arms, a pinned arm, cost rows with and without a summary, SciPy on and off, Holm and BH, difficulty buckets a run is missing from, replicate pooling with a label clash and a left-out run, and an empty section. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
The model-compare section builder was one 556-line function with cyclomatic complexity 53, the highest in the repo. Each report section is now its own helper (pooling, models compared, resource usage, overall summary, significance, by difficulty, head-to-head, context-arm benefit, ranking), and the builder computes the shared inputs once and concatenates them. No output changes: the goldens added in the previous commit pass byte for byte. generate_section_report goes from 53 to 4 and no new function is over 6 (ruff C901 and the ratchet's own counter agree); the file goes from 1296 to 1209 lines. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
|
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #169 +/- ##
==========================================
+ Coverage 91.78% 91.83% +0.05%
==========================================
Files 223 223
Lines 27623 27599 -24
Branches 4549 4535 -14
==========================================
- Hits 25354 25346 -8
+ Misses 1606 1597 -9
+ Partials 663 656 -7
🚀 New features to boost your workflow:
|
antonwolfy
left a comment
There was a problem hiding this comment.
Review at d139a2a. The refactor itself holds up: the output is unchanged. The goldens are narrower than the PR claims, so there are five test gaps: two inline and three below. I'd close the two inline ones before merge.
What I ran, on a Linux review host at d139a2a:
make ci-local: 19 passed, 9 failed, 7 skipped. The suite passed (6892 passed, 21 skipped) and changed-line coverage is 100% (196 of 196). Eight failures are tools missing from my venv (ruff, zizmor, pre-commit, bandit, mypy, deptry, mkdocs, pip-audit; exit 127):make devstops at the tools install on this base, which #179 fixed onmain. Static checks, mypy and the strict docs build pass on GitHub's CI at this head. Thechangelogfailure is covered byskip-changelog.- A differential run of the old and new
generate_section_reporton 3000 seeded inputs (0 to 5 runs, missing, NaN, tied and zero scores, odd labels, one or two treatment arms, pinned arm, baseline-only runs, Holm and BH, SciPy on and off, pooling on and off), with the LLM transport mocked. Output or exception (type and message) was identical every time. - The new test file against the base module: 12 passed. Branch coverage of
generate_section_reportand every new helper by the new tests alone is complete. - A mutation run: 92 mutants of the new helpers, 35 of which pass the new tests (31 also pass the existing report tests). The ones that matter are listed below.
More test gaps from the same run (all pass the new tests):
_cost_cellis only tested with3 < 8.cost_known < total_callsto<=passes the new tests, and thecs.get("n")fallback fortotal_callsis never reached (model_compare.py:908-909).- The pooled footnote flag is untested.
_resource_usage_lines(runs, bool(replicate_note))(model_compare.py:781) passes withpool_replicatesin its place, and no fixture uses abaseline_armother than the default. - Unscored count and arm list. The unscored count is only 0, 2 or 9, so
if st["unpaired"]to> 1passes. Each fixture run has one arm, so the sorted, joined arm cell is untested.
Checked, no issues
- Complexity: an AST branch count gives 53 on the base and 4 at the head for
generate_section_report; the helper numbers match the table;extract_scores(16) andcheck_run_consistency(13) are unchanged. Line counts are 1296 and 1209. - Kept quirks: the
|---|---:||separator and the first-runNin difficulty rows are as in the base. - Repo rules: SPDX header on the test file,
.jsonfixture exempt, no generated artifacts, tests offline, signature unchanged, helpers private, no docs mention the function.
|
Review-body items, all in 10b4bbe (tests only, no package change): 1. _cost_cell: golden unscored_and_arms has 4/5 with the count read from |
…nd arm cells of the section report Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…t from the baseline Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…-sections Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com> # Conflicts: # dokimi/scripts/growth_baseline.json
antonwolfy
left a comment
There was a problem hiding this comment.
Review at 12a5836. The refactor holds up and 10b4bbe closes both of my threads. One test gap is left: zero values in the cost cell and the benefit row are not pinned.
What I ran, on a Linux review host at 12a5836:
make ci-local: 27 passed, 2 failed, 7 skipped. The suite passed (7229 passed, 1 skipped) and the changed-line coverage check passes. Thechangelogfailure is covered byskip-changelog.pip-auditfails on the lock files, which this PR does not touch.- A differential run of the old and new
generate_section_reporton 2500 seeded inputs (0 to 5 runs, missing, NaN, tied and zero scores, one or two treatment arms, pinned arm, baseline-only runs, Holm and BH, SciPy on and off, pooling on and off, cost summaries with and withoutn), with the LLM transport mocked. 1726 renders matched byte for byte, and the 774 inputs that raise gave the same exception type and message in both. Since d139a2a,model_compare.pychanged in one place only: the_check_judge_contexterror text. - A mutation run: 72 mutants of the new helpers against the new tests and
test_model_compare.py. All four significance mutants from my thread and all the truthiness mutants of_run_summaryand_difficulty_cellsnow fail. So docost_known < total_callsto<=, dropping thenfallback,pool_replicatesin place ofbool(replicate_note), andif st["unpaired"]to> 1.
Checked, no issues
- Growth baseline:
model_compare.pyhas 1210 lines, matching the entry. Onlyextract_scores(16) andcheck_run_consistency(13) are over 10 underruff --select C901, and both are still listed. - Goldens: the fixture has 17 keys and the case list has the same 17. Output does not depend on dict or set order. The sorted arm cell is caught on about half the hash seeds, because the
unscored_and_armsgolden hasdocsbeforeskill:x. - Repo rules: SPDX header on the test file, tests offline, no generated artifacts, signature unchanged.
|
…aseline mean in the section goldens Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
antonwolfy
left a comment
There was a problem hiding this comment.
LGTM. 08a8f5d closes my last thread: the zero_values golden pins the zero cost, latency, cache-hit, improved-count and baseline-mean cells. One nit inline.
What I ran, on a Linux review host at 08a8f5d:
make ci-local: 27 passed, 2 failed, 7 skipped. The suite passed (7230 passed, 1 skipped) and the changed-line coverage check passes. Thechangelogfailure is covered byskip-changelog.pip-auditfails on the lock files, which this PR does not touch.- The five mutants from my thread against the new tests and
test_model_compare.py(91 tests):not cost_usd,not st["improved"],not cs.get(f)for latency and cache hit,not woin the treatment layout, andcost_known and total_callsall fail on the new golden. Four more truthiness mutants of_run_summaryand_difficulty_cellsfail too. - Since 12a5836 the diff is test-only: one fixture key, the
zero_valuescase and its expected-substring entry.
Checked, no issues
- Goldens:
zero_valuesrenders$0.0000 (0/5),0.00s,0.0%,0 (0%)and0.0 (baseline), as the thread asked. - Repo rules: no package change since the last review, no generated artifacts, tests offline.
Signed-off-by: Anton Volkov <antonwolfy@gmail.com>
Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com> # Conflicts: # dokimi/scripts/growth_baseline.json
antonwolfy
left a comment
There was a problem hiding this comment.
LGTM. 56b041c closes my last nit: _FREE_COST now carries zero token counts and the zero_values golden prints 0 | 0 | 0. Nothing else is open.
What I ran, on a Linux review host at e7c2507:
make ci-local: 28 passed, 1 failed, 7 skipped. The suite passed (7430 passed, 1 skipped). Thechangelogfailure is covered byskip-changelog.- A differential run of the old and new
generate_section_reporton 1500 seeded inputs, each with SciPy on and off, with the LLM transport mocked. 2008 renders matched byte for byte, and the 992 inputs that raise gave the same exception type and message in both. - A mutation run: 20 mutants of
_resource_usage_lines,_cost_cell,_benefit_linesand_difficulty_cellsagainst the new tests andtest_model_compare.py(91 tests). 18 fail. The mutant that prints—for a zero token count fails on the new golden.
Checked, no issues
- Since 12a5836: the PR's own changes are test-only: the
_FREE_COSTfields, one expected-substring entry and one fixture key. The rest of the diff is merges from main. - Growth baseline:
model_compare.pyhas 1210 lines, matching the entry. Onlyextract_scores(16) andcheck_run_consistency(13) are over 10 underruff --select C901, and both are still listed. - Repo rules: SPDX header on the test file, tests offline,
DOKIMI_UPDATE_GOLDENonly rewrites the fixture when set, no generated artifacts, signature unchanged.
Summary
generate_section_report(model-compare report) was one 556-line function with cyclomatic complexity 53, the highest in the repo. Each report section is now a helper. The builder works out the shared inputs once (common questions, scored arm,has_treatment, per-run summary) and joins the helpers' lines. The output does not change.dokimi/report/model_compare.pygoes from 1296 to 1209 lines. Its other functions are unchanged (extract_scores16 andcheck_run_consistency13 are still over 10).#93 interaction: #93's
growth_baseline.jsonentries for this file drop.dokimi/report/model_compare.py::generate_section_report(53) goes away, and the file'slinesentry goes from 1296 to 1209. Whichever of #93 and this PR merges second runspython scripts/check_growth.py --updateto lower them. This PR does not touch #93's branch.Evidence
Two commits. The first adds only the characterization tests:
tests/test_model_compare_sections.pywith goldens intests/fixtures/model_compare_sections.json. Its 11 cases each render a whole section and compare it byte for byte with the golden. Together they cover every line and branch of the old function (coverage.py--branch). The cases are: treatment, Holm and BH, no SciPy, baseline-only, a treatment run beside a baseline-only one, two scored arms (Context arm), a pinned arm, cost rows with and without a summary, pooled replicates with a label clash and a run left out ofrun_ids, singleton pooling, and an empty section.Complexity:
ruff check --select C901 --config 'lint.mccabe.max-complexity=10'reports only the untouchedextract_scores(16) andcheck_run_consistency(13), andcheck_growth.py'sfunction_complexitiesgives the same numbers as above.Local: full
pytest6911 passed, 2 skipped;ruff check .clean;mypyclean (222 files).Merge Danger
Door: two-way. This is an internal refactor with no change to the public API, and a revert restores the old function.
Blast Radius: none
generate_section_reportkeeps its signature and output. The new helpers are private. There are no persisted-format, schema or flag changes. Two odd outputs from the old code are kept on purpose and are pinned by the goldens. First, the baseline-only "By Difficulty" separator is|---|---:||, with a doubled pipe. Second, a difficulty row'sNcounts the first run that has that bucket, not every run. Fixing either would change the output, so each belongs in its own PR.Checklist
pytestpassesruff,mypy, and schema checks pass.github/.licenserc.yaml)🤖 Generated with Claude Code