Files
Magnus HedemarkGitHubfactory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
92299e1238 feat(skill): beef up ml-engineering with scripts/templates/evals (#257)
Add a schema-valid eval manifest (6 cases: fine-tuning plan review, eval-set
design, quantization decision, deployment plan, regression triage, training-run
reproducibility), three fillable templates (training-run record, eval regression
table, quantization decision record), a stdlib eval-set overlap/leakage checker
with a unittest suite, routing to the llama-cpp tool skill, and a README Quick
Start documenting the script. Closes #240.

Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
2026-08-03 15:25:06 -04:00

204 lines
7.7 KiB
Python

"""Tests for check-eval-overlap.py.
Covers: clean corpora (exit 0), exact-duplicate eval files (exit 1), partial
overlap above and below the threshold, JSON report shape, directory recursion,
token n-gram mode, the --shingle-size flag, missing-path errors, an empty eval
file, a training corpus too short to shingle, and --help.
Discoverable by both pytest and unittest (unittest.TestCase classes).
"""
import json
import os
import subprocess
import sys
import tempfile
import unittest
SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__))
CHECKER = os.path.join(SCRIPTS_DIR, "check-eval-overlap.py")
TRAIN_PARAGRAPH = (
"The quick brown fox jumps over the lazy dog near the riverbank while the "
"sun sets behind the mountains and the evening train crosses the old bridge."
)
EVAL_SENTENCE = (
"The quick brown fox jumps over the lazy dog near the riverbank"
)
UNIQUE_FILLER = (
"blueberry pancakes with maple syrup served at the corner cafe on tuesday "
"morning while the radio played classical music and the barista practiced "
"latte art with carefully steamed milk and a gentle hand."
)
def run_checker(args):
proc = subprocess.run(
[sys.executable, CHECKER, *args],
capture_output=True,
text=True,
timeout=30,
)
return proc.returncode, proc.stdout, proc.stderr
class TempCorpora(unittest.TestCase):
"""Per-test temporary train/ and eval/ directories."""
def setUp(self):
self._tmp = tempfile.TemporaryDirectory()
self.root = self._tmp.name
self.train = os.path.join(self.root, "train")
self.eval_dir = os.path.join(self.root, "eval")
os.makedirs(self.train)
os.makedirs(self.eval_dir)
def tearDown(self):
self._tmp.cleanup()
def write_train(self, name, content):
path = os.path.join(self.train, name)
with open(path, "w", encoding="utf-8") as handle:
handle.write(content)
return path
def write_eval(self, name, content):
path = os.path.join(self.eval_dir, name)
with open(path, "w", encoding="utf-8") as handle:
handle.write(content)
return path
class TestOverlapDetection(TempCorpora):
def test_help_exits_zero(self):
rc, stdout, _ = run_checker(["--help"])
self.assertEqual(rc, 0)
self.assertIn("overlap", stdout)
def test_clean_corpora_exit_zero(self):
self.write_train("train.txt", TRAIN_PARAGRAPH)
self.write_eval("eval.txt", UNIQUE_FILLER)
rc, stdout, _ = run_checker(["--train", self.train, "--eval", self.eval_dir])
self.assertEqual(rc, 0)
self.assertIn("No leakage", stdout)
self.assertIn("ok", stdout)
def test_exact_duplicate_flags_leak(self):
self.write_train("train.txt", TRAIN_PARAGRAPH)
self.write_eval("eval.txt", TRAIN_PARAGRAPH)
rc, stdout, _ = run_checker(["--train", self.train, "--eval", self.eval_dir])
self.assertEqual(rc, 1)
self.assertIn("LEAK", stdout)
self.assertIn("Leakage detected in 1", stdout)
def test_partial_overlap_above_threshold_flags_leak(self):
# Eval reuses the whole train sentence (53 shingles) plus ~210 chars of
# unique filler, so overlap is roughly 20% and must be flagged.
self.write_train("train.txt", TRAIN_PARAGRAPH)
self.write_eval("eval.txt", EVAL_SENTENCE + " " + UNIQUE_FILLER[:210])
rc, stdout, _ = run_checker(["--train", self.train, "--eval", self.eval_dir])
self.assertEqual(rc, 1)
self.assertIn("LEAK", stdout)
def test_overlap_below_threshold_clean(self):
# Eval reuses one sentence (~54 shingles) plus unique filler (~210
# shingles), for roughly 20-25% overlap. A raised threshold must accept
# it; the default threshold flags the same input (see the partial-overlap
# test above).
self.write_train("train.txt", TRAIN_PARAGRAPH)
self.write_eval("eval.txt", EVAL_SENTENCE + " " + UNIQUE_FILLER)
rc, stdout, _ = run_checker(
["--train", self.train, "--eval", self.eval_dir, "--max-overlap-fraction", "0.30"]
)
self.assertEqual(rc, 0)
self.assertIn("No leakage", stdout)
class TestOverlapCli(TempCorpora):
def test_json_report_shape(self):
self.write_train("train.txt", TRAIN_PARAGRAPH)
self.write_eval("eval.txt", TRAIN_PARAGRAPH)
rc, stdout, _ = run_checker(
["--train", self.train, "--eval", self.eval_dir, "--json"]
)
self.assertEqual(rc, 1)
report = json.loads(stdout)
self.assertEqual(report["tool"], "check-eval-overlap.py")
self.assertEqual(report["train_files"], 1)
self.assertEqual(report["eval_files"], 1)
self.assertEqual(len(report["leaked"]), 1)
entry = report["files"][0]
self.assertEqual(entry["verdict"], "LEAK")
self.assertAlmostEqual(entry["fraction"], 1.0, places=4)
self.assertGreater(entry["shared"], 0)
def test_directory_recursion(self):
nested = os.path.join(self.train, "nested")
os.makedirs(nested)
with open(os.path.join(nested, "corpus.txt"), "w", encoding="utf-8") as handle:
handle.write(TRAIN_PARAGRAPH)
self.write_eval("eval.txt", TRAIN_PARAGRAPH)
rc, _, _ = run_checker(["--train", self.train, "--eval", self.eval_dir])
self.assertEqual(rc, 1)
def test_multiple_eval_files_partial_leak(self):
self.write_train("train.txt", TRAIN_PARAGRAPH)
self.write_eval("clean.txt", UNIQUE_FILLER)
self.write_eval("duplicate.txt", TRAIN_PARAGRAPH)
rc, stdout, _ = run_checker(["--train", self.train, "--eval", self.eval_dir])
self.assertEqual(rc, 1)
self.assertIn("Leakage detected in 1 of 2", stdout)
def test_token_ngram_mode(self):
tokens = "alpha beta gamma delta epsilon"
self.write_train("train.txt", tokens)
self.write_eval("eval.txt", tokens)
rc, stdout, _ = run_checker(
["--train", self.train, "--eval", self.eval_dir, "--token-ngram", "3"]
)
self.assertEqual(rc, 1)
self.assertIn("token-3", stdout)
def test_shingle_size_flag(self):
# A short shared word is invisible at size 8 but flagged at size 4.
self.write_train("train.txt", "zebra crossing")
self.write_eval("eval.txt", "zebra")
rc_default, _, _ = run_checker(["--train", self.train, "--eval", self.eval_dir])
rc_small, stdout, _ = run_checker(
["--train", self.train, "--eval", self.eval_dir, "--shingle-size", "4"]
)
self.assertEqual(rc_default, 0)
self.assertEqual(rc_small, 1)
self.assertIn("LEAK", stdout)
class TestOverlapErrors(TempCorpora):
def test_missing_path_exit_two(self):
self.write_train("train.txt", TRAIN_PARAGRAPH)
rc, _, stderr = run_checker(
["--train", self.train, "--eval", os.path.join(self.root, "nope")]
)
self.assertEqual(rc, 2)
self.assertIn("ERROR", stderr)
def test_empty_eval_file_handled(self):
self.write_train("train.txt", TRAIN_PARAGRAPH)
self.write_eval("empty.txt", "")
rc, _, _ = run_checker(["--train", self.train, "--eval", self.eval_dir])
self.assertEqual(rc, 0)
def test_train_corpus_too_short_exit_two(self):
self.write_train("tiny.txt", "hi")
self.write_eval("eval.txt", TRAIN_PARAGRAPH)
rc, _, stderr = run_checker(["--train", self.train, "--eval", self.eval_dir])
self.assertEqual(rc, 2)
self.assertIn("no shingles", stderr)
def test_missing_train_required_flag(self):
rc, _, _ = run_checker(["--eval", self.eval_dir])
self.assertEqual(rc, 2)
if __name__ == "__main__":
unittest.main()