"""HF Jobs launcher — boots env locally + runs training in same container. Optimized 3-round coevolution-light: Round 0: SFT warmstart (Unsloth, 1 epoch on 611 traces) Round 1: GRPO defender (train_grpo.py, 100 steps) vs scripted env adversary Round 2: GRPO defender (train_grpo.py, 100 steps) vs scripted env adversary (E2-E4) Round 3: Final eval + push LoRA to Hub Total: ~3 × 30min on a100-large = 90min, ~$3.75. Usage on HF Jobs: hf jobs uv run --flavor a100-large \ --with "trl>=0.18,unsloth,peft,bitsandbytes,openenv-core,vllm,matplotlib,networkx,datasets" \ --secrets HF_TOKEN \ https://huggingface.co/spaces/sai1906/opsguard/raw/main/scripts/hf_launcher.py """ from __future__ import annotations import os import subprocess import sys import time from pathlib import Path REPO_ZIP = "https://huggingface.co/spaces/sai1906/opsguard/resolve/main" WORK = Path("/tmp/opsguard") def sh(cmd, check=True, **kw): print(f"[sh] {cmd if isinstance(cmd, str) else ' '.join(cmd)}", flush=True) return subprocess.run(cmd, shell=isinstance(cmd, str), check=check, **kw) def clone_repo(): if WORK.exists(): sh(["rm", "-rf", str(WORK)]) WORK.mkdir(parents=True, exist_ok=True) sh(["git", "clone", "https://huggingface.co/spaces/sai1906/opsguard", str(WORK)]) os.chdir(WORK) sys.path.insert(0, str(WORK)) def install_deps(): sh([sys.executable, "-m", "pip", "install", "-q", "trl>=0.18", "peft>=0.13", "bitsandbytes>=0.44", "openenv-core[core]>=0.2.2", "matplotlib", "networkx>=3", "datasets", "huggingface_hub", "accelerate>=1.0", "transformers>=4.46", "jmespath", "fastapi", "uvicorn", "requests"]) def boot_env_server() -> subprocess.Popen: print("[env] starting opsguard env server on 0.0.0.0:8001...", flush=True) proc = subprocess.Popen( [sys.executable, "-m", "uvicorn", "server.app:app", "--host", "0.0.0.0", "--port", "8001"], cwd=str(WORK), env={**os.environ, "PYTHONPATH": str(WORK)}, ) for _ in range(60): time.sleep(2) try: import urllib.request urllib.request.urlopen("http://0.0.0.0:8001/health", timeout=2).read() print("[env] healthy", flush=True) return proc except Exception: continue raise RuntimeError("env server did not become healthy within 120s") def sft_warmstart(): print("\n=== SFT WARM-START ===", flush=True) sh([sys.executable, "scripts/sft_warmstart.py", "--model", "Qwen/Qwen2.5-3B-Instruct", "--episodes", "32", "--output-dir", "/tmp/opsguard-sft", "--epochs", "1", "--train-only", ]) def grpo_round(round_idx: int, num_steps: int, sft_adapter: str | None, hub_repo: str): print(f"\n=== ROUND {round_idx} — GRPO DEFENDER ({num_steps} steps) ===", flush=True) args = [ sys.executable, "scripts/train_grpo.py", "--model", "unsloth/Qwen2.5-7B-Instruct-bnb-4bit", "--env-url", "http://0.0.0.0:8001", "--num-steps", str(num_steps), "--num-generations", "4", "--max-steps-per-episode", "30", "--scenarios", "E2_social_eng_buildup", "E3_compromised_maintainer", "E4_multi_vector", "--no-vllm", "--max-completion-length", "512", "--output-dir", f"/tmp/opsguard-grpo-r{round_idx}", ] if sft_adapter: args.extend(["--sft-adapter", sft_adapter]) if hub_repo: args.extend(["--hub-repo", hub_repo]) sh(args) def final_eval(): print("\n=== FINAL EVAL: trained LoRA vs baseline policies ===", flush=True) sh([sys.executable, "scripts/run_baseline_eval.py", "--policies", "keyword_security_triager", "memory_aware", "debate", "ensemble_voting", "--scenarios", "E2_social_eng_buildup", "E3_compromised_maintainer", "E4_multi_vector", "E5_jia_tan_saga", "--seeds", "0", "1", "2", "--out", "/tmp/eval_post_train"]) def push_outputs(): from huggingface_hub import HfApi api = HfApi(token=os.environ.get("HF_TOKEN")) try: api.create_repo("sai1906/opsguard-grpo", repo_type="model", exist_ok=True) except Exception as e: print(f"[push] create_repo: {e}", flush=True) for src, dest in [ ("/tmp/opsguard-grpo-r2", "checkpoints/r2"), ("/tmp/eval_post_train", "eval_post_train"), ]: if Path(src).exists(): try: api.upload_folder(folder_path=src, repo_id="sai1906/opsguard-grpo", path_in_repo=dest, repo_type="model") print(f"[push] uploaded {src} -> {dest}", flush=True) except Exception as e: print(f"[push] upload failed for {src}: {e}", flush=True) def main(): if not os.environ.get("HF_TOKEN"): raise SystemExit("HF_TOKEN required as secret") clone_repo() install_deps() try: from huggingface_hub import HfApi as _HfApi _HfApi(token=os.environ["HF_TOKEN"]).create_repo( "sai1906/opsguard-sft", repo_type="model", exist_ok=True ) print("[preflight] hub repo ready", flush=True) except Exception as _e: print(f"[preflight] create_repo: {_e}", flush=True) sft_train_and_push() final_eval() push_outputs() def sft_train_and_push(): print("\n=== SFT TRAINING (Qwen 7B 4bit + LoRA) ===", flush=True) sh([sys.executable, "scripts/sft_warmstart.py", "--model", "unsloth/Qwen2.5-7B-Instruct-bnb-4bit", "--episodes", "32", "--output-dir", "/tmp/opsguard-sft", "--epochs", "2", "--train-only", "--push-to-hub", "sai1906/opsguard-sft", ]) if __name__ == "__main__": main()