ReNunney/scripts/run_orchestration.py

169 lines
5.6 KiB
Python

"""
run_orchestration.py
Small CLI for the minimal SQLite-backed orchestration layer.
"""
from __future__ import annotations
import argparse
import json
from pathlib import Path
import sys
REPO_ROOT = Path(__file__).resolve().parents[1]
SRC_DIR = REPO_ROOT / "src"
if str(SRC_DIR) not in sys.path:
sys.path.insert(0, str(SRC_DIR))
from renunney.orchestration import (
collate_track1_figure1,
expand_track1_figure1_manifest,
initialize_registry,
list_jobs,
run_one_job,
run_worker_loop,
submit_job_manifest,
submit_track1_figure1_jobs,
)
def save_payload(payload: dict, path: str | Path) -> None:
out = Path(path)
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Minimal orchestration CLI for cost-of-substitution sweeps.")
subparsers = parser.add_subparsers(dest="command", required=True)
init_db = subparsers.add_parser("init-db")
init_db.add_argument("--db", required=True)
submit = subparsers.add_parser("submit")
submit.add_argument("--db", required=True)
submit.add_argument("--manifest", required=True)
submit_fig1 = subparsers.add_parser("submit-figure1")
submit_fig1.add_argument("--db", required=True)
submit_fig1.add_argument("--config", required=True)
submit_fig1.add_argument("--job-prefix", required=True)
submit_fig1.add_argument("--priority", type=int, default=0)
submit_fig1.add_argument("--created-by", default="codex")
submit_fig1.add_argument("--result-prefix", default="results/track1/figure1")
list_cmd = subparsers.add_parser("list")
list_cmd.add_argument("--db", required=True)
run_one = subparsers.add_parser("run-one")
run_one.add_argument("--db", required=True)
run_one.add_argument("--result-root", required=True)
run_one.add_argument("--worker-backend", default="python-track1")
run_one.add_argument("--worker-host", default=None)
run_one.add_argument("--scratch-root", default=None)
run_loop = subparsers.add_parser("run-loop")
run_loop.add_argument("--db", required=True)
run_loop.add_argument("--result-root", required=True)
run_loop.add_argument("--worker-backend", default="python-track1")
run_loop.add_argument("--worker-host", default=None)
run_loop.add_argument("--scratch-root", default=None)
run_loop.add_argument("--max-jobs", type=int, default=None)
collate = subparsers.add_parser("collate-figure1")
collate.add_argument("--db", required=True)
collate.add_argument("--output", default=None)
return parser
def main() -> int:
parser = build_parser()
args = parser.parse_args()
if args.command == "init-db":
initialize_registry(args.db)
print(json.dumps({"db": str(Path(args.db)), "status": "initialized"}, indent=2, sort_keys=True))
return 0
if args.command == "submit":
manifest = json.loads(Path(args.manifest).read_text(encoding="utf-8"))
job_id = submit_job_manifest(args.db, manifest)
print(json.dumps({"db": str(Path(args.db)), "job_id": job_id, "status": "submitted"}, indent=2, sort_keys=True))
return 0
if args.command == "submit-figure1":
config = json.loads(Path(args.config).read_text(encoding="utf-8"))
manifests = expand_track1_figure1_manifest(
base_job_id_prefix=args.job_prefix,
config=config,
priority=args.priority,
created_by=args.created_by,
result_prefix=args.result_prefix,
)
job_ids = submit_track1_figure1_jobs(
db_path=args.db,
base_job_id_prefix=args.job_prefix,
config=config,
priority=args.priority,
created_by=args.created_by,
result_prefix=args.result_prefix,
)
print(
json.dumps(
{
"db": str(Path(args.db)),
"job_count": len(job_ids),
"job_ids": job_ids,
"status": "submitted",
"preview": manifests,
},
indent=2,
sort_keys=True,
)
)
return 0
if args.command == "list":
print(json.dumps(list_jobs(args.db), indent=2, sort_keys=True))
return 0
if args.command == "run-one":
result = run_one_job(
db_path=args.db,
result_root=args.result_root,
worker_backend=args.worker_backend,
worker_host=args.worker_host,
scratch_root=args.scratch_root,
cwd=REPO_ROOT,
)
print(json.dumps(result, indent=2, sort_keys=True))
return 0 if result is None or result["status"] == "succeeded" else 1
if args.command == "run-loop":
payload = run_worker_loop(
db_path=args.db,
result_root=args.result_root,
worker_backend=args.worker_backend,
worker_host=args.worker_host,
scratch_root=args.scratch_root,
cwd=REPO_ROOT,
max_jobs=args.max_jobs,
)
print(json.dumps(payload, indent=2, sort_keys=True))
return 0 if payload["failed_jobs"] == 0 else 1
if args.command == "collate-figure1":
payload = collate_track1_figure1(args.db)
if args.output:
save_payload(payload, args.output)
print(json.dumps(payload, indent=2, sort_keys=True))
return 0
raise ValueError(args.command)
if __name__ == "__main__":
raise SystemExit(main())