File size: 2,196 Bytes
cd24b4a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
"""Publish a trained checkpoint into the central directory under its canonical name.

The trainer writes ``model_<epoch>.pth`` to the staging dir on each val-loss improvement
(with save_policy=min the last/highest-epoch file is the best). This copies that file to
``<key>.pth`` so the interface registry picks it up, then refreshes the manifest.

  conda run -n SynthScope python interface/scripts/promote_checkpoint.py <key> [--epoch N] [--copy]
"""
import argparse
import re
import shutil
import sys
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
from backend import config              # noqa: E402
from backend.registry import get_spec   # noqa: E402


def best_epoch_file(stage_dir: Path, epoch: int | None) -> Path:
    files = list(stage_dir.glob("model_*.pth"))
    if not files:
        raise FileNotFoundError(f"No model_*.pth in {stage_dir}")
    if epoch is not None:
        f = stage_dir / f"model_{epoch}.pth"
        if not f.exists():
            raise FileNotFoundError(f"{f} not found")
        return f
    # Most recently written = the current run's best (save_policy=min writes the best last).
    # Use mtime, NOT the epoch number, so stale higher-epoch checkpoints left in the staging dir
    # by a *previous* run (possibly a different architecture) are never picked.
    return max(files, key=lambda p: p.stat().st_mtime)


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("key")
    ap.add_argument("--epoch", type=int, default=None)
    ap.add_argument("--copy", action="store_true", help="copy instead of symlink")
    args = ap.parse_args()

    spec = get_spec(args.key)
    if spec.checkpoint is None:
        print(f"{args.key} is training-free; nothing to promote.")
        return
    stage = config.CHECKPOINTS_DIR / "_train" / args.key
    src = best_epoch_file(stage, args.epoch)
    dst = config.CHECKPOINTS_DIR / spec.checkpoint
    if dst.exists() or dst.is_symlink():
        dst.unlink()
    if args.copy:
        shutil.copy2(src, dst)
        print(f"copied {src} -> {dst}")
    else:
        dst.symlink_to(src)
        print(f"linked {dst} -> {src}")


if __name__ == "__main__":
    main()