| from __future__ import annotations |
|
|
| import csv |
| import subprocess |
| import sys |
| from pathlib import Path |
|
|
| from dovla_cil.experiments.reports import generate_eval_report |
| from dovla_cil.utils.io import write_json |
|
|
|
|
| def test_report_eval_works_with_fake_metrics(tmp_path: Path) -> None: |
| metrics_dir = tmp_path / "runs" |
| _write_fake_metrics(metrics_dir) |
| out_dir = tmp_path / "report" |
|
|
| result = subprocess.run( |
| [ |
| sys.executable, |
| "scripts/report_eval.py", |
| "--inputs", |
| str(metrics_dir / "*" / "metrics.json"), |
| "--out", |
| str(out_dir), |
| "--name", |
| "fake_scaling", |
| ], |
| check=True, |
| text=True, |
| capture_output=True, |
| ) |
|
|
| assert "num runs: 3" in result.stdout |
| assert (out_dir / "aggregate_metrics.csv").exists() |
| assert (out_dir / "report.md").exists() |
| assert (out_dir / "success_rate.png").exists() |
| assert (out_dir / "ranking_accuracy.png").exists() |
| assert (out_dir / "score_vs_k.png").exists() |
|
|
|
|
| def test_eval_report_markdown_and_csv_content(tmp_path: Path) -> None: |
| metrics_dir = tmp_path / "runs" |
| _write_fake_metrics(metrics_dir) |
| out_dir = tmp_path / "report" |
|
|
| summary = generate_eval_report( |
| [metrics_dir / "*" / "metrics.json"], |
| out_dir, |
| experiment_name="fake_scaling", |
| ) |
| rows = _read_csv(out_dir / "aggregate_metrics.csv") |
| markdown = (out_dir / "report.md").read_text(encoding="utf-8") |
|
|
| assert summary["num_runs"] == 3 |
| assert rows[0]["k"] == "1" |
| assert rows[1]["k"] == "2" |
| assert rows[2]["k"] == "4" |
| assert rows[1]["ranking_acc"] == "0.7" |
| assert "# fake_scaling" in markdown |
| assert "Best K by ranking_acc: `2`" in markdown |
| assert "Best K by success: `4`" in markdown |
| assert "beta_log_k" in markdown |
| assert "Warning: ranking_acc does not improve monotonically with K." in markdown |
|
|
|
|
| def _write_fake_metrics(root: Path) -> None: |
| payloads = [ |
| { |
| "run_name": "k1", |
| "k": 1, |
| "task_success_rate": 0.30, |
| "pairwise_ranking_accuracy": 0.50, |
| "top1_action_selection": 0.40, |
| "instruction_switch_accuracy": 0.20, |
| "effect_prediction_mae": 0.90, |
| "regret_calibration_error": 0.30, |
| }, |
| { |
| "run_name": "k2", |
| "k": 2, |
| "task_success_rate": 0.50, |
| "pairwise_ranking_accuracy": 0.70, |
| "top1_action_selection": 0.60, |
| "instruction_switch_accuracy": 0.40, |
| "effect_prediction_mae": 0.70, |
| "regret_calibration_error": 0.20, |
| }, |
| { |
| "run_name": "k4", |
| "k": 4, |
| "success_rate": 0.60, |
| "ranking_acc": 0.65, |
| "top1_action_selection": 0.70, |
| "instruction_switch_acc": 0.50, |
| "effect_mae": 0.60, |
| "regret_ece": 0.15, |
| "regression": {"ranking_acc": {"beta_log_k": 0.1}}, |
| }, |
| ] |
| for payload in payloads: |
| out = root / f"k_{int(payload['k']):04d}" / "metrics.json" |
| write_json(payload, out) |
|
|
|
|
| def _read_csv(path: Path) -> list[dict[str, str]]: |
| with path.open("r", encoding="utf-8", newline="") as handle: |
| return list(csv.DictReader(handle)) |
|
|