Skip to content

Add standalone checkpoint evaluation for Puzzletron - #2177

Merged
j-rausch merged 2 commits into
feature/puzzletron_v2from
jrausch/puzzletron-checkpoint-evaluation
Aug 12, 2026
Merged

Add standalone checkpoint evaluation for Puzzletron#2177
j-rausch merged 2 commits into
feature/puzzletron_v2from
jrausch/puzzletron-checkpoint-evaluation

Conversation

@j-rausch

@j-rausch j-rausch commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Type of change: New feature

Adds a standalone command for evaluating compatible local Hugging Face checkpoints with lmms-eval and vLLM, without creating or running a Puzzletron campaign. The reusable evaluator owns command construction, completion validation, timeout cleanup, and isolated attempt artifacts; downstream campaign evaluation now consumes that evaluator as an adapter.

The convenience command provides a small text smoke by default, detects Qwen 3.5 checkpoint metadata and supplies the required reasoning parser automatically, and exposes explicit override and opt-out controls. It keeps the native lmms-eval CLI available for options outside the convenience surface. The pinned evaluator snapshot and pass-through argument surfaces keep behavior reproducible without coupling ModelOpt to lmms-eval internals.

Usage

python examples/puzzletron/evaluate_lmms_checkpoint.py \
  --checkpoint /path/to/checkpoint \
  --output-dir /path/to/results/checkpoint-smoke

Use --full after the smoke succeeds to run complete task datasets. Qwen 3.5 checkpoints are configured automatically.

Testing

Adds unit coverage for the standalone CLI defaults, Qwen detection and overrides, command construction and reserved-argument protection, result completeness, artifact preservation, timeout cleanup, and downstream campaign reuse. Existing Puzzletron CI covers the pinned evaluator environment.

Summary by CodeRabbit

  • New Features

    • Added standalone evaluation for local Hugging Face checkpoints, with smoke and full evaluation modes.
    • Added configurable tasks, runtimes, timeouts, GPU settings, parser overrides, and JSON results.
    • Added automatic Qwen 3.5 configuration and GSM8K compatibility support.
    • Added detailed evaluation, troubleshooting, and output guidance.
  • Improvements

    • Campaign-based downstream evaluation now uses the shared checkpoint evaluation workflow and publishes task metrics consistently.

@copy-pr-bot

copy-pr-bot Bot commented Aug 12, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@j-rausch
j-rausch marked this pull request as ready for review August 12, 2026 15:57
@j-rausch
j-rausch requested review from a team as code owners August 12, 2026 15:57
@j-rausch
j-rausch requested review from danielkorzekwa and kevalmorabia97 and removed request for a team August 12, 2026 15:57
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 2a408547-b618-4992-9269-b00c0de8ac1e

📥 Commits

Reviewing files that changed from the base of the PR and between ec0f281 and 282dfd2.

📒 Files selected for processing (6)
  • examples/puzzletron/README.md
  • examples/puzzletron/docs/checkpoint_evaluation.md
  • examples/puzzletron/evaluate_lmms_checkpoint.py
  • modelopt/torch/puzzletron/evaluation/lmms.py
  • tests/unit/torch/puzzletron/test_evaluate_lmms_checkpoint_cli.py
  • tests/unit/torch/puzzletron/test_lmms_evaluation.py
🚧 Files skipped from review as they are similar to previous changes (5)
  • examples/puzzletron/docs/checkpoint_evaluation.md
  • tests/unit/torch/puzzletron/test_evaluate_lmms_checkpoint_cli.py
  • examples/puzzletron/evaluate_lmms_checkpoint.py
  • examples/puzzletron/README.md
  • modelopt/torch/puzzletron/evaluation/lmms.py

📝 Walkthrough

Walkthrough

Adds a reusable lmms-eval checkpoint backend, a standalone evaluation CLI, and downstream delegation for materialized candidates. The change also adds validation, timeout handling, result artifacts, tests, and Puzzletron evaluation documentation.

Changes

Puzzletron evaluation

Layer / File(s) Summary
Shared checkpoint evaluation backend
modelopt/torch/puzzletron/..., tests/unit/torch/puzzletron/test_lmms_evaluation.py
Adds deterministic command construction, isolated subprocess execution, timeout cleanup, result validation, metric normalization, and persisted evaluation artifacts.
Standalone checkpoint evaluation CLI
examples/puzzletron/evaluate_lmms_checkpoint.py, tests/unit/torch/puzzletron/test_evaluate_lmms_checkpoint_cli.py
Adds local-checkpoint evaluation with smoke and full modes, task aliases, GSM8K compatibility configuration, Qwen 3.5 detection, runtime overrides, and structured JSON output.
Downstream evaluation integration
modelopt/torch/puzzletron/post_mip/runner.py, tests/unit/torch/puzzletron/test_post_mip_runner.py
Replaces embedded lmms-eval execution with the shared checkpoint runner while preserving candidate output directories, metrics, and timeout handling.
Evaluation workflow documentation
examples/puzzletron/README.md, examples/puzzletron/docs/*
Documents standalone checkpoint evaluation, configuration options, output artifacts, troubleshooting, and downstream campaign evaluation.

Estimated code review effort: 4 (Complex) | ~45 minutes

Mergeability Score: 🔵 Low · up to 282df

The evaluator now persists a result path that may refer to different output artifacts, which could cause campaign consumers to read the wrong file. The PR is mergeable with explicit owner awareness and follow-up to clarify or normalize this contract.

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant run_lmms_eval_checkpoint
  participant lmms_eval
  participant AttemptDirectory
  CLI->>run_lmms_eval_checkpoint: pass checkpoint and evaluation settings
  run_lmms_eval_checkpoint->>lmms_eval: run generated vLLM command
  lmms_eval-->>run_lmms_eval_checkpoint: return output and result JSON
  run_lmms_eval_checkpoint->>AttemptDirectory: write command, streams, summary, and metrics
  run_lmms_eval_checkpoint-->>CLI: return normalized JSON result
Loading

Suggested reviewers: danielkorzekwa, kevalmorabia97

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 3.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the primary change: adding standalone checkpoint evaluation for Puzzletron.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Security Anti-Patterns ✅ Passed No security anti-patterns found. The PR properly exposes trust_remote_code as a configurable CLI argument defaulting to False, uses safe subprocess execution via asyncio.create_subprocess_exec with...
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jrausch/puzzletron-checkpoint-evaluation

Comment @coderabbitai help to get the list of available commands.

@j-rausch
j-rausch enabled auto-merge (squash) August 12, 2026 15:58
@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-08-12 23:29 UTC

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.

Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.

👉 Steps to fix this

Actionable comments posted: 4

🧹 Nitpick comments (2)
examples/puzzletron/evaluate_lmms_checkpoint.py (1)

271-272: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add __all__ and a docstring for main.

main is the public entry point of this example module. Declare the public surface and document the arguments, return code, and stdout/stderr contract.

♻️ Proposed change
+__all__ = ["main"]
+
+
 def main(argv: list[str] | None = None) -> int:
+    """Evaluate one local checkpoint and print machine-readable JSON to stdout.
+
+    Args:
+        argv: Command-line arguments; defaults to `sys.argv[1:]`.
+
+    Returns:
+        0 on success, 1 on failure. Failure diagnostics go to stderr as JSON.
+    """
     args = _build_parser().parse_args(argv)

Place __all__ near the module constants.

As per coding guidelines: "Define each module's public API with __all__" and "Document public and higher-level APIs with docstrings, including examples when useful."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@examples/puzzletron/evaluate_lmms_checkpoint.py` around lines 271 - 272, Add
a module-level __all__ near the existing constants that exposes main, and add a
docstring to main documenting the optional argv argument, integer return code,
and stdout/stderr behavior while preserving its current argument parsing and
execution flow.

Source: Coding guidelines

tests/unit/torch/puzzletron/test_lmms_evaluation.py (1)

275-311: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add one end-to-end case that runs the real _run_process.

Every run_lmms_eval_checkpoint test replaces _run_process with a fake, so the real Popen path, cwd handling, and stream capture stay untested. A hermetic case is cheap: set command_prefix to [sys.executable, "-c", script] where script writes a results.json into the current directory and exits 0. The existing fake-based tests can stay for the failure paths.

As per coding guidelines: "Tests must exercise the behavior they claim to validate. For backend or runtime behavior, include an end-to-end test using the real implementation rather than replacing it with a fake."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unit/torch/puzzletron/test_lmms_evaluation.py` around lines 275 - 311,
Add an end-to-end test for run_lmms_eval_checkpoint that does not monkeypatch
lmms._run_process. Configure command_prefix with sys.executable and an inline
script that writes results.json in the current working directory and exits
successfully, then assert the returned metrics and captured command, stdout,
stderr, and result artifacts. Keep the existing fake-based tests for
failure-path coverage.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@modelopt/torch/puzzletron/evaluation/lmms.py`:
- Line 25: Remove the inline `# nosec B404` from the `subprocess` import at
modelopt/torch/puzzletron/evaluation/lmms.py:25-25 and remove `# nosec B603`
from the `subprocess.Popen` call at
modelopt/torch/puzzletron/evaluation/lmms.py:535-536, preserving the explanatory
comment at line 535. If Bandit still requires an exception, configure it in
repository settings or obtain the specified codeowner approval with PR
justification.
- Around line 230-242: Update _command_prefix to tokenize string command_prefix
values with shlex.split, matching the existing _extra_args behavior, while
preserving sequence handling and the current empty-value validation. Ensure a
string such as “python -m lmms_eval” produces separate argv elements rather than
one executable name.
- Around line 323-337: Update the environment construction in the settings
normalization flow so an explicit settings["cache_dir"] sets LMMS_EVAL_HOME even
when the process environment already contains that variable, while preserving a
caller-provided settings["env"]["LMMS_EVAL_HOME"] override. Replace the
setdefault behavior around cache_dir without changing the timeout handling.

In `@modelopt/torch/puzzletron/post_mip/runner.py`:
- Line 24: Remove the # nosec B404 suppression from the subprocess import in
runner.py. Import TimeoutExpired directly, then update the exception checks in
the runner logic around the two timeout-handling usages to reference
TimeoutExpired while preserving the existing TimeoutError behavior.

---

Nitpick comments:
In `@examples/puzzletron/evaluate_lmms_checkpoint.py`:
- Around line 271-272: Add a module-level __all__ near the existing constants
that exposes main, and add a docstring to main documenting the optional argv
argument, integer return code, and stdout/stderr behavior while preserving its
current argument parsing and execution flow.

In `@tests/unit/torch/puzzletron/test_lmms_evaluation.py`:
- Around line 275-311: Add an end-to-end test for run_lmms_eval_checkpoint that
does not monkeypatch lmms._run_process. Configure command_prefix with
sys.executable and an inline script that writes results.json in the current
working directory and exits successfully, then assert the returned metrics and
captured command, stdout, stderr, and result artifacts. Keep the existing
fake-based tests for failure-path coverage.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 0ca1dc7a-3932-43a7-adbd-bb3b1b633da4

📥 Commits

Reviewing files that changed from the base of the PR and between 849cccc and 22317fb.

📒 Files selected for processing (13)
  • examples/puzzletron/README.md
  • examples/puzzletron/ci_environment.json
  • examples/puzzletron/docs/checkpoint_evaluation.md
  • examples/puzzletron/docs/post_mip_pipeline.md
  • examples/puzzletron/evaluate_lmms_checkpoint.py
  • examples/puzzletron/requirements.txt
  • modelopt/torch/puzzletron/__init__.py
  • modelopt/torch/puzzletron/evaluation/__init__.py
  • modelopt/torch/puzzletron/evaluation/lmms.py
  • modelopt/torch/puzzletron/post_mip/runner.py
  • tests/unit/torch/puzzletron/test_evaluate_lmms_checkpoint_cli.py
  • tests/unit/torch/puzzletron/test_lmms_evaluation.py
  • tests/unit/torch/puzzletron/test_post_mip_runner.py

Comment thread modelopt/torch/puzzletron/evaluation/lmms.py Outdated
Comment thread modelopt/torch/puzzletron/evaluation/lmms.py
Comment thread modelopt/torch/puzzletron/evaluation/lmms.py
Comment thread modelopt/torch/puzzletron/post_mip/runner.py Outdated
@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 0% with 385 lines in your changes missing coverage. Please review.
✅ Project coverage is 53.52%. Comparing base (5da9a05) to head (282dfd2).
⚠️ Report is 2 commits behind head on feature/puzzletron_v2.

Files with missing lines Patch % Lines
modelopt/torch/puzzletron/evaluation/lmms.py 0.00% 376 Missing ⚠️
modelopt/torch/puzzletron/post_mip/runner.py 0.00% 8 Missing ⚠️
modelopt/torch/puzzletron/evaluation/__init__.py 0.00% 1 Missing ⚠️
Additional details and impacted files
@@                    Coverage Diff                    @@
##           feature/puzzletron_v2    #2177      +/-   ##
=========================================================
+ Coverage                  53.15%   53.52%   +0.36%     
=========================================================
  Files                        704      706       +2     
  Lines                      91506    91565      +59     
=========================================================
+ Hits                       48640    49009     +369     
+ Misses                     42866    42556     -310     
Flag Coverage Δ
puzzletron 31.40% <0.00%> (-0.03%) ⬇️
regression 8.97% <0.00%> (?)
unit 29.52% <0.00%> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Signed-off-by: Johannes Rausch <jrausch@nvidia.com>
@j-rausch
j-rausch force-pushed the jrausch/puzzletron-checkpoint-evaluation branch from 22317fb to ec0f281 Compare August 12, 2026 23:09

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.

Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.

👉 Steps to fix this

Actionable comments posted: 2

🧹 Nitpick comments (2)
modelopt/torch/puzzletron/evaluation/lmms.py (2)

273-280: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Exclude bytes and bytearray in the extra_args sequence branch.

_command_prefix at Line 260 rejects bytes and bytearray before the sequence branch. _extra_args does not. A bytes value iterates as integers, so values becomes a list of decimal byte codes and the argv is silently wrong. Align both helpers.

♻️ Proposed change
     if isinstance(raw, str):
         values = shlex.split(raw)
-    elif isinstance(raw, Sequence):
+    elif isinstance(raw, Sequence) and not isinstance(raw, (bytes, bytearray)):
         values = [str(item) for item in raw]
     else:
         raise TypeError("evaluation settings.extra_args must be a string or sequence")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modelopt/torch/puzzletron/evaluation/lmms.py` around lines 273 - 280, Update
the extra_args parsing branch in _extra_args to exclude bytes and bytearray from
the Sequence check, matching the validation performed by _command_prefix. Ensure
these values are rejected rather than converted into integer byte codes, while
preserving handling for strings and other valid sequences.

701-714: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Give the two result_path values distinct names.

summary.json stores result_path as the raw lmms-eval JSON path at Line 704. The returned mapping stores result_path as the summary path at Line 710 and the raw path under raw_result_path. The same key therefore means two different files depending on where a consumer reads it. _downstream_evaluation in modelopt/torch/puzzletron/post_mip/runner.py returns this mapping directly into candidate rows, so the ambiguity reaches persisted campaign records.

Rename the key inside the summary payload, for example to raw_result_path, so both surfaces agree.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@modelopt/torch/puzzletron/evaluation/lmms.py` around lines 701 - 714, Rename
the summary payload’s `result_path` field in the `summary` object to
`raw_result_path`, matching the returned mapping and ensuring both surfaces
identify the raw lmms-eval JSON consistently. Leave the returned `result_path`
pointing to `summary_path` and preserve the existing `raw_result_path` entry.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@examples/puzzletron/evaluate_lmms_checkpoint.py`:
- Around line 219-224: Update the argument parser near the existing
trust-remote-code option to accept a remainder argument such as
--lmms-eval-args, allowing native lmms-eval options without parser rejection. In
_settings(), merge those forwarded values with the compatibility --include_path
entry in settings["extra_args"], preserving include_path behavior. Add a CLI
test that verifies an allowed native option is forwarded, while leaving backend
rejection of reserved flags unchanged.

In `@modelopt/torch/puzzletron/evaluation/lmms.py`:
- Around line 574-585: Update the process-wait timeout handling around the
existing asyncio.wait_for calls to catch both asyncio.TimeoutError and the
built-in TimeoutError. Preserve the current SIGTERM/SIGKILL cleanup flow and
ensure the outer timeout path still classifies the run as timed_out.

---

Nitpick comments:
In `@modelopt/torch/puzzletron/evaluation/lmms.py`:
- Around line 273-280: Update the extra_args parsing branch in _extra_args to
exclude bytes and bytearray from the Sequence check, matching the validation
performed by _command_prefix. Ensure these values are rejected rather than
converted into integer byte codes, while preserving handling for strings and
other valid sequences.
- Around line 701-714: Rename the summary payload’s `result_path` field in the
`summary` object to `raw_result_path`, matching the returned mapping and
ensuring both surfaces identify the raw lmms-eval JSON consistently. Leave the
returned `result_path` pointing to `summary_path` and preserve the existing
`raw_result_path` entry.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: b1527127-9bcd-4349-ba49-ade485d0367a

📥 Commits

Reviewing files that changed from the base of the PR and between 22317fb and ec0f281.

📒 Files selected for processing (5)
  • examples/puzzletron/evaluate_lmms_checkpoint.py
  • modelopt/torch/puzzletron/evaluation/lmms.py
  • modelopt/torch/puzzletron/post_mip/runner.py
  • tests/unit/torch/puzzletron/test_evaluate_lmms_checkpoint_cli.py
  • tests/unit/torch/puzzletron/test_lmms_evaluation.py

Comment thread examples/puzzletron/evaluate_lmms_checkpoint.py
Comment thread modelopt/torch/puzzletron/evaluation/lmms.py
Forward native lmms-eval options through the convenience CLI, preserve timeout compatibility, reject byte-string arguments, and disambiguate persisted result paths.

Signed-off-by: Johannes Rausch <jrausch@nvidia.com>
@j-rausch
j-rausch merged commit fa8aae2 into feature/puzzletron_v2 Aug 12, 2026
22 of 23 checks passed
@j-rausch
j-rausch deleted the jrausch/puzzletron-checkpoint-evaluation branch August 12, 2026 23:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant