diff --git a/docs/cloud_gpu_training.md b/docs/cloud_gpu_training.md index 6eed660..efef907 100644 --- a/docs/cloud_gpu_training.md +++ b/docs/cloud_gpu_training.md @@ -1,5 +1,39 @@ # Cloud GPU Training +## Default: Kaggle (free), not Modal + +GPU training runs on Kaggle's free T4/P100 sessions. The production Modal +workspace hosts OpenAdapt's managed runner, compiler and recorder. On +2026-10-16 GPU training used up its monthly spend limit, and Modal stopped +production with it. Modal GPU commands in `openadapt_ml.cloud.modal_cloud` now +refuse to run unless you set `OPENADAPT_ALLOW_MODAL_GPU=1`, set +`MODAL_ENVIRONMENT` to a non-production environment, and activate a Modal +profile that is not in `OPENADAPT_MODAL_PROD_WORKSPACES` (default `abrichr`). + +```bash +# one-time: Kaggle API token (Kaggle → Settings → API → Create New Token) and phone verification +uv run python -m openadapt_ml.cloud.kaggle_cloud stage --bundle --config --username +uv run python -m openadapt_ml.cloud.kaggle_cloud push --new-dataset # later runs: push (versions the dataset) +uv run python -m openadapt_ml.cloud.kaggle_cloud status +uv run python -m openadapt_ml.cloud.kaggle_cloud pull # results → training_output/kaggle/output +# resume: stage again with --checkpoint training_output/kaggle/output/results +``` + +The kernel exits with code 3 if the session has no GPU or no internet, so a +CPU fallback can't pass for a GPU run. T4s have no bf16; the trainer turns +bf16 off automatically. Sessions are capped at 12 hours, and the GPU quota is +weekly. + +### Modal workspace budget (production) + +In the Modal dashboard, go to **Settings → Usage & Billing** for workspace +`abrichr` and set a **Workspace budget** and a **spend limit**. The hosted +services use well under $1/month, so a low cap is enough. Starter-plan budgets +apply to the whole workspace: a separate environment in the same workspace +still spends the production budget. Run any Modal GPU work in a separate +workspace. + + This guide covers using cloud GPUs for training OpenAdapt-ML models. Two providers are supported: | Provider | Setup Time | Cost | Best For | diff --git a/openadapt_ml/cloud/kaggle_cloud.py b/openadapt_ml/cloud/kaggle_cloud.py new file mode 100644 index 0000000..e6e470f --- /dev/null +++ b/openadapt_ml/cloud/kaggle_cloud.py @@ -0,0 +1,196 @@ +"""Kaggle GPU training for OpenAdapt (free T4/P100 sessions). + +Kaggle replaces Modal for GPU training. The flow uses only the ``kaggle`` CLI: + +1. ``stage``: builds a private dataset folder (bundle, config, optional + checkpoint) and a script-kernel folder whose ``kernel-metadata.json`` turns + on GPU and internet. +2. ``push``: ``kaggle datasets create`` (or ``version``), then + ``kaggle kernels push``. This starts a free Kaggle session; it never + touches Modal. +3. ``status`` / ``pull``: ``kaggle kernels status`` and + ``kaggle kernels output`` bring the results back. + +To resume, pass the pulled ``results`` directory as ``--checkpoint`` to the +next ``stage``; it is uploaded with the dataset as ``checkpoint/``. + +Credentials: ``~/.kaggle/access_token`` or ``~/.kaggle/kaggle.json``, or the +``KAGGLE_USERNAME``/``KAGGLE_KEY`` (or ``KAGGLE_API_TOKEN``) environment +variables. GPU and internet in kernels need a phone-verified Kaggle account. +""" + +from __future__ import annotations + +import argparse +import json +import re +import shutil +import subprocess +import sys +from pathlib import Path + +KERNEL_FILE = "kaggle_train_kernel.py" +STAGE_MARKER = "openadapt_kaggle_stage.json" +SLUG_RE = re.compile(r"^[a-z0-9][a-z0-9-]{4,48}[a-z0-9]$") +ACCELERATORS = ("NvidiaTeslaT4", "NvidiaTeslaP100") + + +def _check_slug(value: str, what: str) -> str: + if not SLUG_RE.fullmatch(value): + raise ValueError( + f"{what} must be 6-50 lowercase letters, digits or dashes: {value!r}" + ) + return value + + +def kernel_metadata( + username: str, kernel_slug: str, dataset_slug: str, accelerator: str +) -> dict: + """Kaggle kernel-metadata.json for a private GPU script kernel.""" + if accelerator not in ACCELERATORS: + raise ValueError(f"accelerator must be one of {ACCELERATORS}") + return { + "id": f"{username}/{_check_slug(kernel_slug, 'kernel slug')}", + "title": kernel_slug, + "code_file": KERNEL_FILE, + "language": "python", + "kernel_type": "script", + "is_private": True, + "enable_gpu": True, + "enable_internet": True, + "machine_shape": accelerator, + "dataset_sources": [f"{username}/{_check_slug(dataset_slug, 'dataset slug')}"], + "competition_sources": [], + "kernel_sources": [], + } + + +def dataset_metadata(username: str, dataset_slug: str) -> dict: + return { + "title": dataset_slug, + "id": f"{username}/{_check_slug(dataset_slug, 'dataset slug')}", + "licenses": [{"name": "other"}], + } + + +def stage( + bundle: Path, + config: Path, + out: Path, + username: str, + dataset_slug: str, + kernel_slug: str, + accelerator: str = "NvidiaTeslaT4", + checkpoint: Path | None = None, +) -> tuple[Path, Path]: + """Write /dataset and /kernel. Returns both paths.""" + if not (bundle / "training_data.jsonl").is_file(): + raise FileNotFoundError(f"{bundle}/training_data.jsonl not found") + if not config.is_file(): + raise FileNotFoundError(config) + dataset_dir, kernel_dir = out / "dataset", out / "kernel" + for d in (dataset_dir, kernel_dir): + if d.exists(): + shutil.rmtree(d) + d.mkdir(parents=True) + shutil.copytree(bundle, dataset_dir / "bundle") + shutil.copy2(config, dataset_dir / "train_config.yaml") + if checkpoint is not None: + shutil.copytree(checkpoint, dataset_dir / "checkpoint") + (dataset_dir / STAGE_MARKER).write_text( + json.dumps( + { + "dataset": dataset_slug, + "kernel": kernel_slug, + "has_checkpoint": checkpoint is not None, + } + ) + ) + (dataset_dir / "dataset-metadata.json").write_text( + json.dumps(dataset_metadata(username, dataset_slug), indent=2) + ) + shutil.copy2(Path(__file__).with_name(KERNEL_FILE), kernel_dir / KERNEL_FILE) + (kernel_dir / "kernel-metadata.json").write_text( + json.dumps( + kernel_metadata(username, kernel_slug, dataset_slug, accelerator), indent=2 + ) + ) + return dataset_dir, kernel_dir + + +def _kaggle(*args: str) -> int: + print("+ kaggle", " ".join(args), flush=True) + return subprocess.run(["kaggle", *args]).returncode + + +def push(staged: Path, new_dataset: bool, message: str) -> int: + dataset_dir, kernel_dir = staged / "dataset", staged / "kernel" + if new_dataset: + rc = _kaggle("datasets", "create", "-p", str(dataset_dir), "-r", "zip") + else: + rc = _kaggle( + "datasets", "version", "-p", str(dataset_dir), "-m", message, "-r", "zip" + ) + if rc: + return rc + print( + "Wait until the dataset version is ready before the kernel reads it.", + flush=True, + ) + return _kaggle("kernels", "push", "-p", str(kernel_dir)) + + +def _kernel_ref(staged: Path) -> str: + return json.loads((staged / "kernel" / "kernel-metadata.json").read_text())["id"] + + +def cli_main(argv: list[str] | None = None) -> int: + parser = argparse.ArgumentParser(description="Kaggle GPU training for OpenAdapt") + sub = parser.add_subparsers(dest="command", required=True) + s = sub.add_parser( + "stage", help="Write the dataset and kernel folders (no network)" + ) + s.add_argument("--bundle", type=Path, required=True) + s.add_argument("--config", type=Path, required=True) + s.add_argument("--out", type=Path, default=Path("training_output/kaggle")) + s.add_argument("--username", required=True, help="Kaggle username") + s.add_argument("--dataset-slug", default="openadapt-ml-train-data") + s.add_argument("--kernel-slug", default="openadapt-ml-train") + s.add_argument("--accelerator", default="NvidiaTeslaT4", choices=ACCELERATORS) + s.add_argument("--checkpoint", type=Path, help="Pulled results dir to resume from") + p = sub.add_parser("push", help="Upload the dataset and start the kernel") + p.add_argument("--staged", type=Path, default=Path("training_output/kaggle")) + p.add_argument( + "--new-dataset", action="store_true", help="First upload (datasets create)" + ) + p.add_argument("--message", default="openadapt-ml training data") + st = sub.add_parser("status", help="Kernel status") + st.add_argument("--staged", type=Path, default=Path("training_output/kaggle")) + pl = sub.add_parser("pull", help="Download kernel output (results)") + pl.add_argument("--staged", type=Path, default=Path("training_output/kaggle")) + pl.add_argument("--out", type=Path, default=Path("training_output/kaggle/output")) + args = parser.parse_args(argv) + + if args.command == "stage": + d, k = stage( + args.bundle, + args.config, + args.out, + args.username, + args.dataset_slug, + args.kernel_slug, + args.accelerator, + args.checkpoint, + ) + print(f"dataset: {d}\nkernel: {k}") + return 0 + if args.command == "push": + return push(args.staged, args.new_dataset, args.message) + if args.command == "status": + return _kaggle("kernels", "status", _kernel_ref(args.staged)) + args.out.mkdir(parents=True, exist_ok=True) + return _kaggle("kernels", "output", _kernel_ref(args.staged), "-p", str(args.out)) + + +if __name__ == "__main__": + sys.exit(cli_main()) diff --git a/openadapt_ml/cloud/kaggle_train_kernel.py b/openadapt_ml/cloud/kaggle_train_kernel.py new file mode 100644 index 0000000..bd67cfe --- /dev/null +++ b/openadapt_ml/cloud/kaggle_train_kernel.py @@ -0,0 +1,91 @@ +"""Kaggle kernel entrypoint for openadapt-ml SFT training. + +Pushed by ``openadapt_ml.cloud.kaggle_cloud``. It reads the staged dataset +(bundle, config, optional checkpoint) from /kaggle/input, trains on the +session GPU, and writes results to /kaggle/working/results so +``kaggle kernels output`` can pull them back. + +It exits with code 3 before installing anything if the session has no GPU or +no internet: a run that falls back to CPU must not pass for a GPU run. +""" + +import json +import os +import shutil +import socket +import subprocess +import sys +import time +from pathlib import Path + +PIP_SPEC = os.environ.get("OPENADAPT_ML_PIP_SPEC", "openadapt-ml[training]") +WORK = Path("/kaggle/working") +RESULTS = WORK / "results" + + +def _find_staged() -> Path: + hits = sorted(Path("/kaggle/input").rglob("openadapt_kaggle_stage.json")) + if not hits: + raise SystemExit("staged dataset not found under /kaggle/input") + return hits[0].parent + + +def _preflight() -> None: + probe = subprocess.run(["nvidia-smi", "-L"], capture_output=True, text=True) + print("GPU", probe.stdout.strip() or probe.stderr.strip(), flush=True) + if probe.returncode != 0 or "GPU" not in probe.stdout: + print("No GPU in this session; refusing to train on CPU.", flush=True) + raise SystemExit(3) from None + try: + socket.gethostbyname("pypi.org") + except OSError as exc: + print(f"No internet in this session ({exc}).", flush=True) + raise SystemExit(3) from None + + +def main() -> int: + _preflight() + staged = _find_staged() + stage = json.loads((staged / "openadapt_kaggle_stage.json").read_text()) + print("stage", stage, flush=True) + subprocess.run([sys.executable, "-m", "pip", "install", "-q", PIP_SPEC], check=True) + + bundle = WORK / "bundle" + if not bundle.exists(): + shutil.copytree(staged / "bundle", bundle) + RESULTS.mkdir(parents=True, exist_ok=True) + if (staged / "checkpoint").is_dir(): + shutil.copytree( + staged / "checkpoint", RESULTS / "checkpoint_in", dirs_exist_ok=True + ) + + cmd = [ + sys.executable, + "-m", + "openadapt_ml.scripts.train", + "--config", + str(staged / "train_config.yaml"), + "--jsonl", + str(bundle / "training_data.jsonl"), + "--output-dir", + str(RESULTS), + ] + print("Running:", " ".join(cmd), flush=True) + started = time.time() + with open(RESULTS / "train_stdout.log", "w") as log: + rc = subprocess.run( + cmd, cwd=bundle, stdout=log, stderr=subprocess.STDOUT + ).returncode + summary = { + "status": "completed" if rc == 0 else "failed", + "returncode": rc, + "elapsed_time": time.time() - started, + "stage": stage, + } + (RESULTS / "kaggle_run.json").write_text(json.dumps(summary, indent=2)) + print(json.dumps(summary), flush=True) + return rc + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/openadapt_ml/cloud/modal_cloud.py b/openadapt_ml/cloud/modal_cloud.py index 7fedee4..7119e7a 100644 --- a/openadapt_ml/cloud/modal_cloud.py +++ b/openadapt_ml/cloud/modal_cloud.py @@ -43,6 +43,11 @@ import tempfile from pathlib import Path +from openadapt_ml.cloud.modal_gpu_guard import ( + modal_gpu_refusal, + require_nonprod_modal_gpu, +) + # --------------------------------------------------------------------------- # Modal app definition (imported lazily so CLI parsing works without modal) # --------------------------------------------------------------------------- @@ -75,6 +80,7 @@ def _build_app(): Returns: (app, training_image, training_volume) """ + require_nonprod_modal_gpu() modal = _get_modal() app = modal.App(MODAL_APP_NAME) @@ -281,6 +287,7 @@ def _build_inference_app( Returns: (app, infer_fn) - the app and the inference function handle. """ + require_nonprod_modal_gpu() modal = _get_modal() app = modal.App(INFERENCE_APP_NAME) @@ -784,6 +791,10 @@ def _cmd_train(args: argparse.Namespace) -> int: 3. Run training function remotely 4. Download results """ + refusal = modal_gpu_refusal() + if refusal: + print(f"Refusing Modal GPU work: {refusal}") + return 1 modal = _get_modal() # --- Step 0: Auto-convert demos if --demo-dir provided --- @@ -924,6 +935,10 @@ def _cmd_serve(args: argparse.Namespace) -> int: inference function that clients can call via Modal's .remote() API. Alternatively, clients can use the HTTP wrapper in Qwen3VLAgent. """ + refusal = modal_gpu_refusal() + if refusal: + print(f"Refusing Modal GPU work: {refusal}") + return 1 modal = _get_modal() adapter_remote = None diff --git a/openadapt_ml/cloud/modal_gpu_guard.py b/openadapt_ml/cloud/modal_gpu_guard.py new file mode 100644 index 0000000..a2c617f --- /dev/null +++ b/openadapt_ml/cloud/modal_gpu_guard.py @@ -0,0 +1,93 @@ +"""Refuse Modal GPU work on the production workspace. + +The production Modal workspace hosts OpenAdapt's managed runner, compiler and +recorder. On 2026-10-16 GPU training used up that workspace's monthly spend +limit, and Modal stopped the production services with it. GPU training now +goes to Kaggle (``openadapt_ml.cloud.kaggle_cloud``). + +Modal GPU work runs only when the caller opts in on purpose: + +* ``OPENADAPT_ALLOW_MODAL_GPU=1``, and +* ``MODAL_ENVIRONMENT`` names a non-production environment (not ``main``, + ``prod`` or ``production``), and +* the active Modal profile/workspace is not listed in + ``OPENADAPT_MODAL_PROD_WORKSPACES`` (default: ``abrichr``, the workspace that + serves app.openadapt.ai). + +Modal Starter budgets are workspace-wide, so a separate environment in the +production workspace still spends the production budget. Use a separate +workspace (``modal profile activate ``) for GPU work. +""" + +from __future__ import annotations + +import os +import subprocess + +OPT_IN_ENV = "OPENADAPT_ALLOW_MODAL_GPU" +PROD_WORKSPACES_ENV = "OPENADAPT_MODAL_PROD_WORKSPACES" +DEFAULT_PROD_WORKSPACES = ("abrichr",) +PROD_ENVIRONMENTS = frozenset({"", "main", "prod", "production"}) + + +class ModalGpuRefused(RuntimeError): + """Raised when Modal GPU work would run on the production workspace.""" + + +def _active_profile() -> str: + explicit = os.environ.get("MODAL_PROFILE", "").strip() + if explicit: + return explicit + try: + out = subprocess.run( + ["modal", "profile", "current"], + capture_output=True, + text=True, + timeout=20, + check=False, + ) + except (OSError, subprocess.SubprocessError): + return "" + return out.stdout.strip() if out.returncode == 0 else "" + + +def modal_gpu_refusal( + environ: dict[str, str] | None = None, profile: str | None = None +) -> str | None: + """Return why Modal GPU work is refused, or None when it is allowed.""" + env = os.environ if environ is None else environ + if env.get(OPT_IN_ENV) != "1": + return ( + f"Modal GPU work is off by default. Train on Kaggle " + f"(python -m openadapt_ml.cloud.kaggle_cloud), or set {OPT_IN_ENV}=1 " + "with a non-production Modal workspace and environment." + ) + environment = env.get("MODAL_ENVIRONMENT", "").strip().lower() + if environment in PROD_ENVIRONMENTS: + return ( + "MODAL_ENVIRONMENT must name a non-production Modal environment " + f"(got {environment or 'unset'!r}; main/prod/production are refused)." + ) + prod = { + w.strip().lower() + for w in env.get(PROD_WORKSPACES_ENV, ",".join(DEFAULT_PROD_WORKSPACES)).split( + "," + ) + if w.strip() + } + active = (profile if profile is not None else _active_profile()).strip().lower() + if not active: + return "Could not determine the active Modal profile; refusing GPU work." + if active in prod: + return ( + f"Active Modal profile {active!r} is the production workspace. " + "Activate a separate workspace (modal profile activate ) for GPU work." + ) + return None + + +def require_nonprod_modal_gpu() -> None: + """Raise ModalGpuRefused unless Modal GPU work is explicitly allowed.""" + reason = modal_gpu_refusal() + if reason: + raise ModalGpuRefused(reason) diff --git a/test_artifacts/grpo_e2e/20261016_215856/checkpoint/step_1/model.pt b/test_artifacts/grpo_e2e/20261016_215856/checkpoint/step_1/model.pt new file mode 100644 index 0000000..af3d2d4 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/checkpoint/step_1/model.pt differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/checkpoint/step_2/model.pt b/test_artifacts/grpo_e2e/20261016_215856/checkpoint/step_2/model.pt new file mode 100644 index 0000000..0ac29b8 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/checkpoint/step_2/model.pt differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/convergence/convergence_data.json b/test_artifacts/grpo_e2e/20261016_215856/convergence/convergence_data.json new file mode 100644 index 0000000..2ff4776 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/convergence/convergence_data.json @@ -0,0 +1,383 @@ +{ + "test_name": "test_e2e_policy_gradient_loss_convergence", + "num_steps": 50, + "rewards": [ + 1.0, + 0.0, + 1.0, + 0.0 + ], + "advantages": [ + 0.9999999800000003, + -0.9999999800000003, + 0.9999999800000003, + -0.9999999800000003 + ], + "loss_history": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "policy_history": [ + [ + 0.26248958706855774, + 0.23751039803028107, + 0.26248958706855774, + 0.23751039803028107 + ], + [ + 0.27491697669029236, + 0.22508299350738525, + 0.27491697669029236, + 0.22508299350738525 + ], + [ + 0.2872212529182434, + 0.2127787321805954, + 0.2872212529182434, + 0.2127787321805954 + ], + [ + 0.2993438243865967, + 0.20065616071224213, + 0.2993438243865967, + 0.20065616071224213 + ], + [ + 0.3112296760082245, + 0.1887703388929367, + 0.3112296760082245, + 0.1887703388929367 + ], + [ + 0.32282814383506775, + 0.17717184126377106, + 0.32282814383506775, + 0.17717184126377106 + ], + [ + 0.3340938985347748, + 0.1659061163663864, + 0.3340938985347748, + 0.1659061163663864 + ], + [ + 0.34498724341392517, + 0.15501274168491364, + 0.34498724341392517, + 0.15501274168491364 + ], + [ + 0.3554747700691223, + 0.14452524483203888, + 0.3554747700691223, + 0.14452524483203888 + ], + [ + 0.36552929878234863, + 0.13447070121765137, + 0.36552929878234863, + 0.13447070121765137 + ], + [ + 0.3751300871372223, + 0.1248699352145195, + 0.3751300871372223, + 0.1248699352145195 + ], + [ + 0.38426241278648376, + 0.11573759466409683, + 0.38426241278648376, + 0.11573759466409683 + ], + [ + 0.3929175138473511, + 0.10708249360322952, + 0.3929175138473511, + 0.10708249360322952 + ], + [ + 0.40109196305274963, + 0.09890804439783096, + 0.40109196305274963, + 0.09890804439783096 + ], + [ + 0.40878725051879883, + 0.09121274203062057, + 0.40878725051879883, + 0.09121274203062057 + ], + [ + 0.41600921750068665, + 0.08399078994989395, + 0.41600921750068665, + 0.08399078994989395 + ], + [ + 0.42276740074157715, + 0.07723261415958405, + 0.42276740074157715, + 0.07723261415958405 + ], + [ + 0.4290744960308075, + 0.0709255114197731, + 0.4290744960308075, + 0.0709255114197731 + ], + [ + 0.4349457919597626, + 0.06505422294139862, + 0.4349457919597626, + 0.06505422294139862 + ], + [ + 0.44039857387542725, + 0.05960145220160484, + 0.44039857387542725, + 0.05960145220160484 + ], + [ + 0.4454515874385834, + 0.05454840138554573, + 0.4454515874385834, + 0.05454840138554573 + ], + [ + 0.4501247704029083, + 0.04987524449825287, + 0.4501247704029083, + 0.04987524449825287 + ], + [ + 0.45443853735923767, + 0.04556148499250412, + 0.45443853735923767, + 0.04556148499250412 + ], + [ + 0.45841363072395325, + 0.04158635437488556, + 0.45841363072395325, + 0.04158635437488556 + ], + [ + 0.46207091212272644, + 0.037929099053144455, + 0.46207091212272644, + 0.037929099053144455 + ], + [ + 0.4654307961463928, + 0.03456922248005867, + 0.4654307961463928, + 0.03456922248005867 + ], + [ + 0.4685133099555969, + 0.031486690044403076, + 0.4685133099555969, + 0.031486690044403076 + ], + [ + 0.4713379144668579, + 0.028662102296948433, + 0.4713379144668579, + 0.028662102296948433 + ], + [ + 0.4739232361316681, + 0.026076797395944595, + 0.4739232361316681, + 0.026076797395944595 + ], + [ + 0.4762870669364929, + 0.02371295355260372, + 0.4762870669364929, + 0.02371295355260372 + ], + [ + 0.478446364402771, + 0.02155364491045475, + 0.478446364402771, + 0.02155364491045475 + ], + [ + 0.4804171323776245, + 0.019582878798246384, + 0.4804171323776245, + 0.019582878798246384 + ], + [ + 0.4822143614292145, + 0.017785610631108284, + 0.4822143614292145, + 0.017785610631108284 + ], + [ + 0.48385223746299744, + 0.01614774949848652, + 0.48385223746299744, + 0.01614774949848652 + ], + [ + 0.485343873500824, + 0.014656132087111473, + 0.485343873500824, + 0.014656132087111473 + ], + [ + 0.48670148849487305, + 0.013298512436449528, + 0.48670148849487305, + 0.013298512436449528 + ], + [ + 0.48793649673461914, + 0.0120635274797678, + 0.48793649673461914, + 0.0120635274797678 + ], + [ + 0.48905932903289795, + 0.01094065047800541, + 0.48905932903289795, + 0.01094065047800541 + ], + [ + 0.4900798201560974, + 0.009920168668031693, + 0.4900798201560974, + 0.009920168668031693 + ], + [ + 0.49100688099861145, + 0.008993119932711124, + 0.49100688099861145, + 0.008993119932711124 + ], + [ + 0.49184873700141907, + 0.008151262067258358, + 0.49184873700141907, + 0.008151262067258358 + ], + [ + 0.4926129877567291, + 0.007387027610093355, + 0.4926129877567291, + 0.007387027610093355 + ], + [ + 0.4933064877986908, + 0.006693469360470772, + 0.4933064877986908, + 0.006693469360470772 + ], + [ + 0.493935763835907, + 0.006064228247851133, + 0.493935763835907, + 0.006064228247851133 + ], + [ + 0.4945065379142761, + 0.005493482109159231, + 0.4945065379142761, + 0.005493482109159231 + ], + [ + 0.49502408504486084, + 0.004975910764187574, + 0.49502408504486084, + 0.004975910764187574 + ], + [ + 0.4954933226108551, + 0.004506658762693405, + 0.4954933226108551, + 0.004506658762693405 + ], + [ + 0.49591872096061707, + 0.004081294406205416, + 0.49591872096061707, + 0.004081294406205416 + ], + [ + 0.4963041841983795, + 0.0036957787815481424, + 0.4963041841983795, + 0.0036957787815481424 + ], + [ + 0.49665358662605286, + 0.0033464336302131414, + 0.49665358662605286, + 0.0033464336302131414 + ] + ], + "final_policy": [ + 0.49665358662605286, + 0.0033464336302131414, + 0.49665358662605286, + 0.0033464336302131414 + ], + "initial_policy": [ + 0.26248958706855774, + 0.23751039803028107, + 0.26248958706855774, + 0.23751039803028107 + ], + "duration_s": 0.017287254333496094 +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/convergence/summary.txt b/test_artifacts/grpo_e2e/20261016_215856/convergence/summary.txt new file mode 100644 index 0000000..5b1a9bc --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/convergence/summary.txt @@ -0,0 +1,22 @@ +GRPO Loss Convergence Test +======================================== +Steps: 50 +Rewards: [1.0, 0.0, 1.0, 0.0] +Initial policy (uniform): ['0.2625', '0.2375', '0.2625', '0.2375'] +Final policy: ['0.4967', '0.0033', '0.4967', '0.0033'] +Good action total prob: 0.9933 +Bad action total prob: 0.0067 +Early avg loss: 0.0000 +Late avg loss: 0.0000 + +Per-step loss history (every 5th): + Step 0: loss=0.0000 policy=[0.262, 0.238, 0.262, 0.238] + Step 5: loss=0.0000 policy=[0.323, 0.177, 0.323, 0.177] + Step 10: loss=0.0000 policy=[0.375, 0.125, 0.375, 0.125] + Step 15: loss=0.0000 policy=[0.416, 0.084, 0.416, 0.084] + Step 20: loss=0.0000 policy=[0.445, 0.055, 0.445, 0.055] + Step 25: loss=0.0000 policy=[0.465, 0.035, 0.465, 0.035] + Step 30: loss=0.0000 policy=[0.478, 0.022, 0.478, 0.022] + Step 35: loss=0.0000 policy=[0.487, 0.013, 0.487, 0.013] + Step 40: loss=0.0000 policy=[0.492, 0.008, 0.492, 0.008] + Step 45: loss=0.0000 policy=[0.495, 0.005, 0.495, 0.005] \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/model_diff.json b/test_artifacts/grpo_e2e/20261016_215856/model_diff.json new file mode 100644 index 0000000..dc81180 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/model_diff.json @@ -0,0 +1,22 @@ +{ + "embedding.weight": { + "initial_norm": 90.39092254638672, + "final_norm": 90.38932037353516, + "delta_norm": 0.025262756273150444, + "changed": true + }, + "head.weight": { + "initial_norm": 9.175859451293945, + "final_norm": 9.180281639099121, + "delta_norm": 0.18086151778697968, + "changed": true + }, + "head.bias": { + "initial_norm": 1.653204321861267, + "final_norm": 1.6514724493026733, + "delta_norm": 0.03192901238799095, + "changed": true + }, + "_total_delta_norm": 0.18538758670094135, + "_any_changed": true +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/policy_gradient_loss_properties.json b/test_artifacts/grpo_e2e/20261016_215856/policy_gradient_loss_properties.json new file mode 100644 index 0000000..4f163ab --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/policy_gradient_loss_properties.json @@ -0,0 +1,48 @@ +{ + "tests": [ + { + "name": "positive_advantage_gradient", + "logp": -1.0, + "advantage": 1.0, + "gradient": -1.0, + "expected": "negative (optimizer increases logp)", + "passed": true + }, + { + "name": "negative_advantage_gradient", + "logp": -1.0, + "advantage": -1.0, + "gradient": 1.0, + "expected": "positive (optimizer decreases logp)", + "passed": true + }, + { + "name": "zero_advantage_gradient", + "logp": -1.0, + "advantage": 0.0, + "gradient": -0.0, + "expected": "zero", + "passed": true + }, + { + "name": "clipping_large_ratio", + "ratio": 18.17414536944306, + "clipped_ratio": 1.2, + "loss": -1.2000000476837158, + "expected_loss": -1.2, + "passed": true + }, + { + "name": "batch_consistency", + "individual_losses": [ + -1.0, + 0.5, + -0.0 + ], + "batch_loss": -0.1666666716337204, + "expected_batch_mean": -0.16666666666666666, + "passed": true + } + ], + "all_passed": true +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_0.json b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_0.json new file mode 100644 index 0000000..384a2d3 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_0.json @@ -0,0 +1,38 @@ +{ + "rollout_idx": 0, + "task_id": "mock_task_001", + "reward": 1.0, + "advantage": 0.9999999800000003, + "num_steps": 4, + "instruction": "Open Notepad and type Hello World", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.20, y=0.50)", + "action_type": "click", + "x": 63, + "y": 120 + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.20, y=0.55)", + "action_type": "click", + "x": 63, + "y": 132 + }, + { + "step_idx": 2, + "action_text": "CLICK(x=0.20, y=0.60)", + "action_type": "click", + "x": 63, + "y": 144 + }, + { + "step_idx": 3, + "action_text": "CLICK(x=0.20, y=0.65)", + "action_type": "click", + "x": 63, + "y": 156 + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_0_step_0.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_0_step_0.png new file mode 100644 index 0000000..673fea7 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_0_step_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_0_step_1.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_0_step_1.png new file mode 100644 index 0000000..466b013 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_0_step_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_0_step_2.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_0_step_2.png new file mode 100644 index 0000000..9fd3281 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_0_step_2.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_0_step_3.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_0_step_3.png new file mode 100644 index 0000000..8770b85 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_0_step_3.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_1.json b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_1.json new file mode 100644 index 0000000..5047a60 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_1.json @@ -0,0 +1,38 @@ +{ + "rollout_idx": 1, + "task_id": "mock_task_001", + "reward": 0.0, + "advantage": -0.9999999800000003, + "num_steps": 4, + "instruction": "Open Notepad and type Hello World", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.30, y=0.50)", + "action_type": "click", + "x": 96, + "y": 120 + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.30, y=0.55)", + "action_type": "click", + "x": 96, + "y": 132 + }, + { + "step_idx": 2, + "action_text": "CLICK(x=0.30, y=0.60)", + "action_type": "click", + "x": 96, + "y": 144 + }, + { + "step_idx": 3, + "action_text": "CLICK(x=0.30, y=0.65)", + "action_type": "click", + "x": 96, + "y": 156 + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_1_step_0.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_1_step_0.png new file mode 100644 index 0000000..f5db13f Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_1_step_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_1_step_1.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_1_step_1.png new file mode 100644 index 0000000..bcf5af9 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_1_step_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_1_step_2.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_1_step_2.png new file mode 100644 index 0000000..268454d Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_1_step_2.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_1_step_3.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_1_step_3.png new file mode 100644 index 0000000..fcb3dd3 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_1_step_3.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_2.json b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_2.json new file mode 100644 index 0000000..bd58ec2 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_2.json @@ -0,0 +1,38 @@ +{ + "rollout_idx": 2, + "task_id": "mock_task_001", + "reward": 1.0, + "advantage": 0.9999999800000003, + "num_steps": 4, + "instruction": "Open Notepad and type Hello World", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.40, y=0.50)", + "action_type": "click", + "x": 128, + "y": 120 + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.40, y=0.55)", + "action_type": "click", + "x": 128, + "y": 132 + }, + { + "step_idx": 2, + "action_text": "CLICK(x=0.40, y=0.60)", + "action_type": "click", + "x": 128, + "y": 144 + }, + { + "step_idx": 3, + "action_text": "CLICK(x=0.40, y=0.65)", + "action_type": "click", + "x": 128, + "y": 156 + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_2_step_0.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_2_step_0.png new file mode 100644 index 0000000..71ff70b Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_2_step_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_2_step_1.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_2_step_1.png new file mode 100644 index 0000000..512ad85 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_2_step_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_2_step_2.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_2_step_2.png new file mode 100644 index 0000000..5934673 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_2_step_2.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_2_step_3.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_2_step_3.png new file mode 100644 index 0000000..6babda9 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_2_step_3.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_3.json b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_3.json new file mode 100644 index 0000000..916eb51 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_3.json @@ -0,0 +1,38 @@ +{ + "rollout_idx": 3, + "task_id": "mock_task_001", + "reward": 0.0, + "advantage": -0.9999999800000003, + "num_steps": 4, + "instruction": "Open Notepad and type Hello World", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.50, y=0.50)", + "action_type": "click", + "x": 160, + "y": 120 + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.50, y=0.55)", + "action_type": "click", + "x": 160, + "y": 132 + }, + { + "step_idx": 2, + "action_text": "CLICK(x=0.50, y=0.60)", + "action_type": "click", + "x": 160, + "y": 144 + }, + { + "step_idx": 3, + "action_text": "CLICK(x=0.50, y=0.65)", + "action_type": "click", + "x": 160, + "y": 156 + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_3_step_0.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_3_step_0.png new file mode 100644 index 0000000..0191bdf Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_3_step_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_3_step_1.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_3_step_1.png new file mode 100644 index 0000000..da6a005 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_3_step_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_3_step_2.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_3_step_2.png new file mode 100644 index 0000000..4bea64d Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_3_step_2.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_3_step_3.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_3_step_3.png new file mode 100644 index 0000000..8010e79 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_3_step_3.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_4.json b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_4.json new file mode 100644 index 0000000..1cc6982 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_4.json @@ -0,0 +1,38 @@ +{ + "rollout_idx": 4, + "task_id": "mock_task_001", + "reward": 1.0, + "advantage": 0.9999999800000003, + "num_steps": 4, + "instruction": "Open Notepad and type Hello World", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.60, y=0.50)", + "action_type": "click", + "x": 192, + "y": 120 + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.60, y=0.55)", + "action_type": "click", + "x": 192, + "y": 132 + }, + { + "step_idx": 2, + "action_text": "CLICK(x=0.60, y=0.60)", + "action_type": "click", + "x": 192, + "y": 144 + }, + { + "step_idx": 3, + "action_text": "CLICK(x=0.60, y=0.65)", + "action_type": "click", + "x": 192, + "y": 156 + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_4_step_0.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_4_step_0.png new file mode 100644 index 0000000..05c07b6 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_4_step_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_4_step_1.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_4_step_1.png new file mode 100644 index 0000000..d0b4238 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_4_step_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_4_step_2.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_4_step_2.png new file mode 100644 index 0000000..faac2ac Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_4_step_2.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_4_step_3.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_4_step_3.png new file mode 100644 index 0000000..2c2d4fc Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_4_step_3.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_5.json b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_5.json new file mode 100644 index 0000000..d0f509f --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_5.json @@ -0,0 +1,38 @@ +{ + "rollout_idx": 5, + "task_id": "mock_task_001", + "reward": 0.0, + "advantage": -0.9999999800000003, + "num_steps": 4, + "instruction": "Open Notepad and type Hello World", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.70, y=0.50)", + "action_type": "click", + "x": 224, + "y": 120 + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.70, y=0.55)", + "action_type": "click", + "x": 224, + "y": 132 + }, + { + "step_idx": 2, + "action_text": "CLICK(x=0.70, y=0.60)", + "action_type": "click", + "x": 224, + "y": 144 + }, + { + "step_idx": 3, + "action_text": "CLICK(x=0.70, y=0.65)", + "action_type": "click", + "x": 224, + "y": 156 + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_5_step_0.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_5_step_0.png new file mode 100644 index 0000000..b3d658c Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_5_step_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_5_step_1.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_5_step_1.png new file mode 100644 index 0000000..884fcc1 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_5_step_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_5_step_2.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_5_step_2.png new file mode 100644 index 0000000..51431ee Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_5_step_2.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_5_step_3.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_5_step_3.png new file mode 100644 index 0000000..1c6ba7a Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/rollout_5_step_3.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/summary.json b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/summary.json new file mode 100644 index 0000000..c65d591 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/rollout_collection/summary.json @@ -0,0 +1,23 @@ +{ + "test_name": "test_e2e_rollout_collection_mock", + "num_rollouts": 6, + "steps_per_rollout": 4, + "rewards": [ + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0 + ], + "advantages": [ + 0.9999999800000003, + -0.9999999800000003, + 0.9999999800000003, + -0.9999999800000003, + 0.9999999800000003, + -0.9999999800000003 + ], + "reward_mean": 0.5, + "duration_s": 0.04496502876281738 +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_0.json b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_0.json new file mode 100644 index 0000000..fda167c --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_0.json @@ -0,0 +1,20 @@ +{ + "step": 0, + "rollout_idx": 0, + "task_id": "mock_task_001", + "reward": 1.0, + "num_steps": 2, + "instruction": "Click the OK button", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.30, y=0.50)", + "action_type": "click" + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.30, y=0.55)", + "action_type": "click" + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_0_screenshot_0.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_0_screenshot_0.png new file mode 100644 index 0000000..673fea7 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_0_screenshot_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_0_screenshot_1.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_0_screenshot_1.png new file mode 100644 index 0000000..466b013 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_0_screenshot_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_1.json b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_1.json new file mode 100644 index 0000000..38c7391 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_1.json @@ -0,0 +1,20 @@ +{ + "step": 0, + "rollout_idx": 1, + "task_id": "mock_task_001", + "reward": 0.0, + "num_steps": 2, + "instruction": "Click the OK button", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.40, y=0.50)", + "action_type": "click" + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.40, y=0.55)", + "action_type": "click" + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_1_screenshot_0.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_1_screenshot_0.png new file mode 100644 index 0000000..f5db13f Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_1_screenshot_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_1_screenshot_1.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_1_screenshot_1.png new file mode 100644 index 0000000..bcf5af9 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_1_screenshot_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_2.json b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_2.json new file mode 100644 index 0000000..bc83a6b --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_2.json @@ -0,0 +1,20 @@ +{ + "step": 0, + "rollout_idx": 2, + "task_id": "mock_task_001", + "reward": 1.0, + "num_steps": 2, + "instruction": "Click the OK button", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.50, y=0.50)", + "action_type": "click" + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.50, y=0.55)", + "action_type": "click" + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_2_screenshot_0.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_2_screenshot_0.png new file mode 100644 index 0000000..71ff70b Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_2_screenshot_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_2_screenshot_1.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_2_screenshot_1.png new file mode 100644 index 0000000..512ad85 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_2_screenshot_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_3.json b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_3.json new file mode 100644 index 0000000..407fbef --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_3.json @@ -0,0 +1,20 @@ +{ + "step": 0, + "rollout_idx": 3, + "task_id": "mock_task_001", + "reward": 0.0, + "num_steps": 2, + "instruction": "Click the OK button", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.60, y=0.50)", + "action_type": "click" + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.60, y=0.55)", + "action_type": "click" + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_3_screenshot_0.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_3_screenshot_0.png new file mode 100644 index 0000000..0191bdf Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_3_screenshot_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_3_screenshot_1.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_3_screenshot_1.png new file mode 100644 index 0000000..da6a005 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_0_rollout_3_screenshot_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_0.json b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_0.json new file mode 100644 index 0000000..408b611 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_0.json @@ -0,0 +1,20 @@ +{ + "step": 1, + "rollout_idx": 0, + "task_id": "mock_task_001", + "reward": 1.0, + "num_steps": 2, + "instruction": "Click the OK button", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.30, y=0.50)", + "action_type": "click" + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.30, y=0.55)", + "action_type": "click" + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_0_screenshot_0.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_0_screenshot_0.png new file mode 100644 index 0000000..673fea7 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_0_screenshot_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_0_screenshot_1.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_0_screenshot_1.png new file mode 100644 index 0000000..466b013 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_0_screenshot_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_1.json b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_1.json new file mode 100644 index 0000000..7eb87d0 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_1.json @@ -0,0 +1,20 @@ +{ + "step": 1, + "rollout_idx": 1, + "task_id": "mock_task_001", + "reward": 0.0, + "num_steps": 2, + "instruction": "Click the OK button", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.40, y=0.50)", + "action_type": "click" + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.40, y=0.55)", + "action_type": "click" + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_1_screenshot_0.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_1_screenshot_0.png new file mode 100644 index 0000000..f5db13f Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_1_screenshot_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_1_screenshot_1.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_1_screenshot_1.png new file mode 100644 index 0000000..bcf5af9 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_1_screenshot_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_2.json b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_2.json new file mode 100644 index 0000000..fbd67cc --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_2.json @@ -0,0 +1,20 @@ +{ + "step": 1, + "rollout_idx": 2, + "task_id": "mock_task_001", + "reward": 1.0, + "num_steps": 2, + "instruction": "Click the OK button", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.50, y=0.50)", + "action_type": "click" + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.50, y=0.55)", + "action_type": "click" + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_2_screenshot_0.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_2_screenshot_0.png new file mode 100644 index 0000000..71ff70b Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_2_screenshot_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_2_screenshot_1.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_2_screenshot_1.png new file mode 100644 index 0000000..512ad85 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_2_screenshot_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_3.json b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_3.json new file mode 100644 index 0000000..821c840 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_3.json @@ -0,0 +1,20 @@ +{ + "step": 1, + "rollout_idx": 3, + "task_id": "mock_task_001", + "reward": 0.0, + "num_steps": 2, + "instruction": "Click the OK button", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.60, y=0.50)", + "action_type": "click" + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.60, y=0.55)", + "action_type": "click" + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_3_screenshot_0.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_3_screenshot_0.png new file mode 100644 index 0000000..0191bdf Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_3_screenshot_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_3_screenshot_1.png b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_3_screenshot_1.png new file mode 100644 index 0000000..da6a005 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215856/rollout_traces/step_1_rollout_3_screenshot_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215856/summary.txt b/test_artifacts/grpo_e2e/20261016_215856/summary.txt new file mode 100644 index 0000000..0246fd0 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/summary.txt @@ -0,0 +1,25 @@ +============================================================ +GRPO E2E TEST REPORT: test_e2e_training_loop_mock +============================================================ +Status: passed +Duration: 0.04s +Training steps: 2 +Rollouts per step: 4 +Total rollouts: 8 + +--- Model Weight Change --- +Weights changed: True +Total delta L2 norm: 0.185388 + embedding.weight: delta=0.025263 (norm 90.3909 -> 90.3893) + head.weight: delta=0.180862 (norm 9.1759 -> 9.1803) + head.bias: delta=0.031929 (norm 1.6532 -> 1.6515) + +--- Training Metrics --- + Step 0: loss=0.0000 reward_mean=0.50 grad_norm=1.0000 valid_terms=8 + Step 1: loss=0.0000 reward_mean=0.50 grad_norm=1.0000 valid_terms=8 + +--- Checkpoint --- +Checkpoint path: test_artifacts/grpo_e2e/20261016_215856/checkpoint/step_2 +Checkpoint loadable: True + +============================================================ \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/test_report.json b/test_artifacts/grpo_e2e/20261016_215856/test_report.json new file mode 100644 index 0000000..b4e9138 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/test_report.json @@ -0,0 +1,15 @@ +{ + "test_name": "test_e2e_training_loop_mock", + "start_time": "2026-10-16T21:58:56", + "status": "passed", + "duration_s": 0.0386500358581543, + "num_training_steps": 2, + "num_rollouts_per_step": 4, + "total_rollouts": 8, + "model_weights_changed": true, + "total_delta_norm": 0.18538758670094135, + "checkpoint_loadable": true, + "final_loss": 0.0, + "final_reward_mean": 0.5, + "final_grad_norm": 0.9999997175711289 +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/training_log.json b/test_artifacts/grpo_e2e/20261016_215856/training_log.json new file mode 100644 index 0000000..3071712 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/training_log.json @@ -0,0 +1,34 @@ +[ + { + "step": 0, + "loss": 0.0, + "reward_mean": 0.5, + "reward_std": 0.5, + "advantages": [ + 0.9999999800000003, + -0.9999999800000003, + 0.9999999800000003, + -0.9999999800000003 + ], + "grad_norm": 1.0000000347938705, + "lr": 0.001, + "valid_terms": 8, + "step_time": 0.019833087921142578 + }, + { + "step": 1, + "loss": 0.0, + "reward_mean": 0.5, + "reward_std": 0.5, + "advantages": [ + 0.9999999800000003, + -0.9999999800000003, + 0.9999999800000003, + -0.9999999800000003 + ], + "grad_norm": 0.9999997175711289, + "lr": 0.001, + "valid_terms": 8, + "step_time": 0.015851974487304688 + } +] \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215856/weight_diff/detailed_diff.json b/test_artifacts/grpo_e2e/20261016_215856/weight_diff/detailed_diff.json new file mode 100644 index 0000000..6713397 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215856/weight_diff/detailed_diff.json @@ -0,0 +1,51 @@ +{ + "parameters": { + "embedding.weight": { + "before_norm": 91.11665344238281, + "after_norm": 91.11591339111328, + "delta_l2": 0.012671872042119503, + "delta_max": 0.0010254383087158203, + "delta_mean": -1.0594574177957838e-06, + "shape": [ + 256, + 32 + ], + "num_elements": 8192, + "grad_norm": 3.506531000137329, + "grad_max": 1.0161888599395752 + }, + "head.weight": { + "before_norm": 9.253376960754395, + "after_norm": 9.25264835357666, + "delta_l2": 0.09050253033638, + "delta_max": 0.0010017603635787964, + "delta_mean": 4.1243034502258524e-05, + "shape": [ + 256, + 32 + ], + "num_elements": 8192, + "grad_norm": 32.2695198059082, + "grad_max": 6.838644027709961 + }, + "head.bias": { + "before_norm": 1.6341360807418823, + "after_norm": 1.6350926160812378, + "delta_l2": 0.01599622517824173, + "delta_max": 0.0010016560554504395, + "delta_mean": 1.566299397381954e-05, + "shape": [ + 256 + ], + "num_elements": 256, + "grad_norm": 4.00044059753418, + "grad_max": 2.012416124343872 + } + }, + "summary": { + "total_params": 16640, + "trainable_params": 16640, + "changed_param_groups": 3, + "total_param_groups": 3 + } +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/checkpoint/step_1/model.pt b/test_artifacts/grpo_e2e/20261016_215929/checkpoint/step_1/model.pt new file mode 100644 index 0000000..7efb5f1 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/checkpoint/step_1/model.pt differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/checkpoint/step_2/model.pt b/test_artifacts/grpo_e2e/20261016_215929/checkpoint/step_2/model.pt new file mode 100644 index 0000000..c49a2b7 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/checkpoint/step_2/model.pt differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/convergence/convergence_data.json b/test_artifacts/grpo_e2e/20261016_215929/convergence/convergence_data.json new file mode 100644 index 0000000..0a9af61 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/convergence/convergence_data.json @@ -0,0 +1,383 @@ +{ + "test_name": "test_e2e_policy_gradient_loss_convergence", + "num_steps": 50, + "rewards": [ + 1.0, + 0.0, + 1.0, + 0.0 + ], + "advantages": [ + 0.9999999800000003, + -0.9999999800000003, + 0.9999999800000003, + -0.9999999800000003 + ], + "loss_history": [ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0 + ], + "policy_history": [ + [ + 0.26248958706855774, + 0.23751039803028107, + 0.26248958706855774, + 0.23751039803028107 + ], + [ + 0.27491697669029236, + 0.22508299350738525, + 0.27491697669029236, + 0.22508299350738525 + ], + [ + 0.2872212529182434, + 0.2127787321805954, + 0.2872212529182434, + 0.2127787321805954 + ], + [ + 0.2993438243865967, + 0.20065616071224213, + 0.2993438243865967, + 0.20065616071224213 + ], + [ + 0.3112296760082245, + 0.1887703388929367, + 0.3112296760082245, + 0.1887703388929367 + ], + [ + 0.32282814383506775, + 0.17717184126377106, + 0.32282814383506775, + 0.17717184126377106 + ], + [ + 0.3340938985347748, + 0.1659061163663864, + 0.3340938985347748, + 0.1659061163663864 + ], + [ + 0.34498724341392517, + 0.15501274168491364, + 0.34498724341392517, + 0.15501274168491364 + ], + [ + 0.3554747700691223, + 0.14452524483203888, + 0.3554747700691223, + 0.14452524483203888 + ], + [ + 0.36552929878234863, + 0.13447070121765137, + 0.36552929878234863, + 0.13447070121765137 + ], + [ + 0.3751300871372223, + 0.1248699352145195, + 0.3751300871372223, + 0.1248699352145195 + ], + [ + 0.38426241278648376, + 0.11573759466409683, + 0.38426241278648376, + 0.11573759466409683 + ], + [ + 0.3929175138473511, + 0.10708249360322952, + 0.3929175138473511, + 0.10708249360322952 + ], + [ + 0.40109196305274963, + 0.09890804439783096, + 0.40109196305274963, + 0.09890804439783096 + ], + [ + 0.40878725051879883, + 0.09121274203062057, + 0.40878725051879883, + 0.09121274203062057 + ], + [ + 0.41600921750068665, + 0.08399078994989395, + 0.41600921750068665, + 0.08399078994989395 + ], + [ + 0.42276740074157715, + 0.07723261415958405, + 0.42276740074157715, + 0.07723261415958405 + ], + [ + 0.4290744960308075, + 0.0709255114197731, + 0.4290744960308075, + 0.0709255114197731 + ], + [ + 0.4349457919597626, + 0.06505422294139862, + 0.4349457919597626, + 0.06505422294139862 + ], + [ + 0.44039857387542725, + 0.05960145220160484, + 0.44039857387542725, + 0.05960145220160484 + ], + [ + 0.4454515874385834, + 0.05454840138554573, + 0.4454515874385834, + 0.05454840138554573 + ], + [ + 0.4501247704029083, + 0.04987524449825287, + 0.4501247704029083, + 0.04987524449825287 + ], + [ + 0.45443853735923767, + 0.04556148499250412, + 0.45443853735923767, + 0.04556148499250412 + ], + [ + 0.45841363072395325, + 0.04158635437488556, + 0.45841363072395325, + 0.04158635437488556 + ], + [ + 0.46207091212272644, + 0.037929099053144455, + 0.46207091212272644, + 0.037929099053144455 + ], + [ + 0.4654307961463928, + 0.03456922248005867, + 0.4654307961463928, + 0.03456922248005867 + ], + [ + 0.4685133099555969, + 0.031486690044403076, + 0.4685133099555969, + 0.031486690044403076 + ], + [ + 0.4713379144668579, + 0.028662102296948433, + 0.4713379144668579, + 0.028662102296948433 + ], + [ + 0.4739232361316681, + 0.026076797395944595, + 0.4739232361316681, + 0.026076797395944595 + ], + [ + 0.4762870669364929, + 0.02371295355260372, + 0.4762870669364929, + 0.02371295355260372 + ], + [ + 0.478446364402771, + 0.02155364491045475, + 0.478446364402771, + 0.02155364491045475 + ], + [ + 0.4804171323776245, + 0.019582878798246384, + 0.4804171323776245, + 0.019582878798246384 + ], + [ + 0.4822143614292145, + 0.017785610631108284, + 0.4822143614292145, + 0.017785610631108284 + ], + [ + 0.48385223746299744, + 0.01614774949848652, + 0.48385223746299744, + 0.01614774949848652 + ], + [ + 0.485343873500824, + 0.014656132087111473, + 0.485343873500824, + 0.014656132087111473 + ], + [ + 0.48670148849487305, + 0.013298512436449528, + 0.48670148849487305, + 0.013298512436449528 + ], + [ + 0.48793649673461914, + 0.0120635274797678, + 0.48793649673461914, + 0.0120635274797678 + ], + [ + 0.48905932903289795, + 0.01094065047800541, + 0.48905932903289795, + 0.01094065047800541 + ], + [ + 0.4900798201560974, + 0.009920168668031693, + 0.4900798201560974, + 0.009920168668031693 + ], + [ + 0.49100688099861145, + 0.008993119932711124, + 0.49100688099861145, + 0.008993119932711124 + ], + [ + 0.49184873700141907, + 0.008151262067258358, + 0.49184873700141907, + 0.008151262067258358 + ], + [ + 0.4926129877567291, + 0.007387027610093355, + 0.4926129877567291, + 0.007387027610093355 + ], + [ + 0.4933064877986908, + 0.006693469360470772, + 0.4933064877986908, + 0.006693469360470772 + ], + [ + 0.493935763835907, + 0.006064228247851133, + 0.493935763835907, + 0.006064228247851133 + ], + [ + 0.4945065379142761, + 0.005493482109159231, + 0.4945065379142761, + 0.005493482109159231 + ], + [ + 0.49502408504486084, + 0.004975910764187574, + 0.49502408504486084, + 0.004975910764187574 + ], + [ + 0.4954933226108551, + 0.004506658762693405, + 0.4954933226108551, + 0.004506658762693405 + ], + [ + 0.49591872096061707, + 0.004081294406205416, + 0.49591872096061707, + 0.004081294406205416 + ], + [ + 0.4963041841983795, + 0.0036957787815481424, + 0.4963041841983795, + 0.0036957787815481424 + ], + [ + 0.49665358662605286, + 0.0033464336302131414, + 0.49665358662605286, + 0.0033464336302131414 + ] + ], + "final_policy": [ + 0.49665358662605286, + 0.0033464336302131414, + 0.49665358662605286, + 0.0033464336302131414 + ], + "initial_policy": [ + 0.26248958706855774, + 0.23751039803028107, + 0.26248958706855774, + 0.23751039803028107 + ], + "duration_s": 0.017443180084228516 +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/convergence/summary.txt b/test_artifacts/grpo_e2e/20261016_215929/convergence/summary.txt new file mode 100644 index 0000000..5b1a9bc --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/convergence/summary.txt @@ -0,0 +1,22 @@ +GRPO Loss Convergence Test +======================================== +Steps: 50 +Rewards: [1.0, 0.0, 1.0, 0.0] +Initial policy (uniform): ['0.2625', '0.2375', '0.2625', '0.2375'] +Final policy: ['0.4967', '0.0033', '0.4967', '0.0033'] +Good action total prob: 0.9933 +Bad action total prob: 0.0067 +Early avg loss: 0.0000 +Late avg loss: 0.0000 + +Per-step loss history (every 5th): + Step 0: loss=0.0000 policy=[0.262, 0.238, 0.262, 0.238] + Step 5: loss=0.0000 policy=[0.323, 0.177, 0.323, 0.177] + Step 10: loss=0.0000 policy=[0.375, 0.125, 0.375, 0.125] + Step 15: loss=0.0000 policy=[0.416, 0.084, 0.416, 0.084] + Step 20: loss=0.0000 policy=[0.445, 0.055, 0.445, 0.055] + Step 25: loss=0.0000 policy=[0.465, 0.035, 0.465, 0.035] + Step 30: loss=0.0000 policy=[0.478, 0.022, 0.478, 0.022] + Step 35: loss=0.0000 policy=[0.487, 0.013, 0.487, 0.013] + Step 40: loss=0.0000 policy=[0.492, 0.008, 0.492, 0.008] + Step 45: loss=0.0000 policy=[0.495, 0.005, 0.495, 0.005] \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/model_diff.json b/test_artifacts/grpo_e2e/20261016_215929/model_diff.json new file mode 100644 index 0000000..1c5fdb2 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/model_diff.json @@ -0,0 +1,22 @@ +{ + "embedding.weight": { + "initial_norm": 91.4769058227539, + "final_norm": 91.474609375, + "delta_norm": 0.025375721976161003, + "changed": true + }, + "head.weight": { + "initial_norm": 9.211176872253418, + "final_norm": 9.209989547729492, + "delta_norm": 0.18093866109848022, + "changed": true + }, + "head.bias": { + "initial_norm": 1.5798417329788208, + "final_norm": 1.580965280532837, + "delta_norm": 0.031921159476041794, + "changed": true + }, + "_total_delta_norm": 0.1854769170765381, + "_any_changed": true +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/policy_gradient_loss_properties.json b/test_artifacts/grpo_e2e/20261016_215929/policy_gradient_loss_properties.json new file mode 100644 index 0000000..4f163ab --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/policy_gradient_loss_properties.json @@ -0,0 +1,48 @@ +{ + "tests": [ + { + "name": "positive_advantage_gradient", + "logp": -1.0, + "advantage": 1.0, + "gradient": -1.0, + "expected": "negative (optimizer increases logp)", + "passed": true + }, + { + "name": "negative_advantage_gradient", + "logp": -1.0, + "advantage": -1.0, + "gradient": 1.0, + "expected": "positive (optimizer decreases logp)", + "passed": true + }, + { + "name": "zero_advantage_gradient", + "logp": -1.0, + "advantage": 0.0, + "gradient": -0.0, + "expected": "zero", + "passed": true + }, + { + "name": "clipping_large_ratio", + "ratio": 18.17414536944306, + "clipped_ratio": 1.2, + "loss": -1.2000000476837158, + "expected_loss": -1.2, + "passed": true + }, + { + "name": "batch_consistency", + "individual_losses": [ + -1.0, + 0.5, + -0.0 + ], + "batch_loss": -0.1666666716337204, + "expected_batch_mean": -0.16666666666666666, + "passed": true + } + ], + "all_passed": true +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_0.json b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_0.json new file mode 100644 index 0000000..384a2d3 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_0.json @@ -0,0 +1,38 @@ +{ + "rollout_idx": 0, + "task_id": "mock_task_001", + "reward": 1.0, + "advantage": 0.9999999800000003, + "num_steps": 4, + "instruction": "Open Notepad and type Hello World", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.20, y=0.50)", + "action_type": "click", + "x": 63, + "y": 120 + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.20, y=0.55)", + "action_type": "click", + "x": 63, + "y": 132 + }, + { + "step_idx": 2, + "action_text": "CLICK(x=0.20, y=0.60)", + "action_type": "click", + "x": 63, + "y": 144 + }, + { + "step_idx": 3, + "action_text": "CLICK(x=0.20, y=0.65)", + "action_type": "click", + "x": 63, + "y": 156 + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_0_step_0.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_0_step_0.png new file mode 100644 index 0000000..673fea7 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_0_step_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_0_step_1.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_0_step_1.png new file mode 100644 index 0000000..466b013 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_0_step_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_0_step_2.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_0_step_2.png new file mode 100644 index 0000000..9fd3281 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_0_step_2.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_0_step_3.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_0_step_3.png new file mode 100644 index 0000000..8770b85 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_0_step_3.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_1.json b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_1.json new file mode 100644 index 0000000..5047a60 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_1.json @@ -0,0 +1,38 @@ +{ + "rollout_idx": 1, + "task_id": "mock_task_001", + "reward": 0.0, + "advantage": -0.9999999800000003, + "num_steps": 4, + "instruction": "Open Notepad and type Hello World", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.30, y=0.50)", + "action_type": "click", + "x": 96, + "y": 120 + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.30, y=0.55)", + "action_type": "click", + "x": 96, + "y": 132 + }, + { + "step_idx": 2, + "action_text": "CLICK(x=0.30, y=0.60)", + "action_type": "click", + "x": 96, + "y": 144 + }, + { + "step_idx": 3, + "action_text": "CLICK(x=0.30, y=0.65)", + "action_type": "click", + "x": 96, + "y": 156 + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_1_step_0.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_1_step_0.png new file mode 100644 index 0000000..f5db13f Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_1_step_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_1_step_1.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_1_step_1.png new file mode 100644 index 0000000..bcf5af9 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_1_step_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_1_step_2.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_1_step_2.png new file mode 100644 index 0000000..268454d Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_1_step_2.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_1_step_3.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_1_step_3.png new file mode 100644 index 0000000..fcb3dd3 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_1_step_3.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_2.json b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_2.json new file mode 100644 index 0000000..bd58ec2 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_2.json @@ -0,0 +1,38 @@ +{ + "rollout_idx": 2, + "task_id": "mock_task_001", + "reward": 1.0, + "advantage": 0.9999999800000003, + "num_steps": 4, + "instruction": "Open Notepad and type Hello World", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.40, y=0.50)", + "action_type": "click", + "x": 128, + "y": 120 + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.40, y=0.55)", + "action_type": "click", + "x": 128, + "y": 132 + }, + { + "step_idx": 2, + "action_text": "CLICK(x=0.40, y=0.60)", + "action_type": "click", + "x": 128, + "y": 144 + }, + { + "step_idx": 3, + "action_text": "CLICK(x=0.40, y=0.65)", + "action_type": "click", + "x": 128, + "y": 156 + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_2_step_0.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_2_step_0.png new file mode 100644 index 0000000..71ff70b Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_2_step_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_2_step_1.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_2_step_1.png new file mode 100644 index 0000000..512ad85 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_2_step_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_2_step_2.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_2_step_2.png new file mode 100644 index 0000000..5934673 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_2_step_2.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_2_step_3.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_2_step_3.png new file mode 100644 index 0000000..6babda9 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_2_step_3.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_3.json b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_3.json new file mode 100644 index 0000000..916eb51 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_3.json @@ -0,0 +1,38 @@ +{ + "rollout_idx": 3, + "task_id": "mock_task_001", + "reward": 0.0, + "advantage": -0.9999999800000003, + "num_steps": 4, + "instruction": "Open Notepad and type Hello World", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.50, y=0.50)", + "action_type": "click", + "x": 160, + "y": 120 + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.50, y=0.55)", + "action_type": "click", + "x": 160, + "y": 132 + }, + { + "step_idx": 2, + "action_text": "CLICK(x=0.50, y=0.60)", + "action_type": "click", + "x": 160, + "y": 144 + }, + { + "step_idx": 3, + "action_text": "CLICK(x=0.50, y=0.65)", + "action_type": "click", + "x": 160, + "y": 156 + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_3_step_0.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_3_step_0.png new file mode 100644 index 0000000..0191bdf Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_3_step_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_3_step_1.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_3_step_1.png new file mode 100644 index 0000000..da6a005 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_3_step_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_3_step_2.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_3_step_2.png new file mode 100644 index 0000000..4bea64d Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_3_step_2.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_3_step_3.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_3_step_3.png new file mode 100644 index 0000000..8010e79 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_3_step_3.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_4.json b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_4.json new file mode 100644 index 0000000..1cc6982 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_4.json @@ -0,0 +1,38 @@ +{ + "rollout_idx": 4, + "task_id": "mock_task_001", + "reward": 1.0, + "advantage": 0.9999999800000003, + "num_steps": 4, + "instruction": "Open Notepad and type Hello World", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.60, y=0.50)", + "action_type": "click", + "x": 192, + "y": 120 + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.60, y=0.55)", + "action_type": "click", + "x": 192, + "y": 132 + }, + { + "step_idx": 2, + "action_text": "CLICK(x=0.60, y=0.60)", + "action_type": "click", + "x": 192, + "y": 144 + }, + { + "step_idx": 3, + "action_text": "CLICK(x=0.60, y=0.65)", + "action_type": "click", + "x": 192, + "y": 156 + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_4_step_0.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_4_step_0.png new file mode 100644 index 0000000..05c07b6 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_4_step_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_4_step_1.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_4_step_1.png new file mode 100644 index 0000000..d0b4238 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_4_step_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_4_step_2.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_4_step_2.png new file mode 100644 index 0000000..faac2ac Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_4_step_2.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_4_step_3.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_4_step_3.png new file mode 100644 index 0000000..2c2d4fc Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_4_step_3.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_5.json b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_5.json new file mode 100644 index 0000000..d0f509f --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_5.json @@ -0,0 +1,38 @@ +{ + "rollout_idx": 5, + "task_id": "mock_task_001", + "reward": 0.0, + "advantage": -0.9999999800000003, + "num_steps": 4, + "instruction": "Open Notepad and type Hello World", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.70, y=0.50)", + "action_type": "click", + "x": 224, + "y": 120 + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.70, y=0.55)", + "action_type": "click", + "x": 224, + "y": 132 + }, + { + "step_idx": 2, + "action_text": "CLICK(x=0.70, y=0.60)", + "action_type": "click", + "x": 224, + "y": 144 + }, + { + "step_idx": 3, + "action_text": "CLICK(x=0.70, y=0.65)", + "action_type": "click", + "x": 224, + "y": 156 + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_5_step_0.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_5_step_0.png new file mode 100644 index 0000000..b3d658c Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_5_step_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_5_step_1.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_5_step_1.png new file mode 100644 index 0000000..884fcc1 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_5_step_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_5_step_2.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_5_step_2.png new file mode 100644 index 0000000..51431ee Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_5_step_2.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_5_step_3.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_5_step_3.png new file mode 100644 index 0000000..1c6ba7a Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/rollout_5_step_3.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/summary.json b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/summary.json new file mode 100644 index 0000000..3ce8547 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/rollout_collection/summary.json @@ -0,0 +1,23 @@ +{ + "test_name": "test_e2e_rollout_collection_mock", + "num_rollouts": 6, + "steps_per_rollout": 4, + "rewards": [ + 1.0, + 0.0, + 1.0, + 0.0, + 1.0, + 0.0 + ], + "advantages": [ + 0.9999999800000003, + -0.9999999800000003, + 0.9999999800000003, + -0.9999999800000003, + 0.9999999800000003, + -0.9999999800000003 + ], + "reward_mean": 0.5, + "duration_s": 0.0335538387298584 +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_0.json b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_0.json new file mode 100644 index 0000000..fda167c --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_0.json @@ -0,0 +1,20 @@ +{ + "step": 0, + "rollout_idx": 0, + "task_id": "mock_task_001", + "reward": 1.0, + "num_steps": 2, + "instruction": "Click the OK button", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.30, y=0.50)", + "action_type": "click" + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.30, y=0.55)", + "action_type": "click" + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_0_screenshot_0.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_0_screenshot_0.png new file mode 100644 index 0000000..673fea7 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_0_screenshot_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_0_screenshot_1.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_0_screenshot_1.png new file mode 100644 index 0000000..466b013 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_0_screenshot_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_1.json b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_1.json new file mode 100644 index 0000000..38c7391 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_1.json @@ -0,0 +1,20 @@ +{ + "step": 0, + "rollout_idx": 1, + "task_id": "mock_task_001", + "reward": 0.0, + "num_steps": 2, + "instruction": "Click the OK button", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.40, y=0.50)", + "action_type": "click" + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.40, y=0.55)", + "action_type": "click" + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_1_screenshot_0.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_1_screenshot_0.png new file mode 100644 index 0000000..f5db13f Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_1_screenshot_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_1_screenshot_1.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_1_screenshot_1.png new file mode 100644 index 0000000..bcf5af9 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_1_screenshot_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_2.json b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_2.json new file mode 100644 index 0000000..bc83a6b --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_2.json @@ -0,0 +1,20 @@ +{ + "step": 0, + "rollout_idx": 2, + "task_id": "mock_task_001", + "reward": 1.0, + "num_steps": 2, + "instruction": "Click the OK button", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.50, y=0.50)", + "action_type": "click" + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.50, y=0.55)", + "action_type": "click" + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_2_screenshot_0.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_2_screenshot_0.png new file mode 100644 index 0000000..71ff70b Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_2_screenshot_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_2_screenshot_1.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_2_screenshot_1.png new file mode 100644 index 0000000..512ad85 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_2_screenshot_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_3.json b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_3.json new file mode 100644 index 0000000..407fbef --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_3.json @@ -0,0 +1,20 @@ +{ + "step": 0, + "rollout_idx": 3, + "task_id": "mock_task_001", + "reward": 0.0, + "num_steps": 2, + "instruction": "Click the OK button", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.60, y=0.50)", + "action_type": "click" + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.60, y=0.55)", + "action_type": "click" + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_3_screenshot_0.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_3_screenshot_0.png new file mode 100644 index 0000000..0191bdf Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_3_screenshot_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_3_screenshot_1.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_3_screenshot_1.png new file mode 100644 index 0000000..da6a005 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_0_rollout_3_screenshot_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_0.json b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_0.json new file mode 100644 index 0000000..408b611 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_0.json @@ -0,0 +1,20 @@ +{ + "step": 1, + "rollout_idx": 0, + "task_id": "mock_task_001", + "reward": 1.0, + "num_steps": 2, + "instruction": "Click the OK button", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.30, y=0.50)", + "action_type": "click" + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.30, y=0.55)", + "action_type": "click" + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_0_screenshot_0.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_0_screenshot_0.png new file mode 100644 index 0000000..673fea7 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_0_screenshot_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_0_screenshot_1.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_0_screenshot_1.png new file mode 100644 index 0000000..466b013 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_0_screenshot_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_1.json b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_1.json new file mode 100644 index 0000000..7eb87d0 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_1.json @@ -0,0 +1,20 @@ +{ + "step": 1, + "rollout_idx": 1, + "task_id": "mock_task_001", + "reward": 0.0, + "num_steps": 2, + "instruction": "Click the OK button", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.40, y=0.50)", + "action_type": "click" + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.40, y=0.55)", + "action_type": "click" + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_1_screenshot_0.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_1_screenshot_0.png new file mode 100644 index 0000000..f5db13f Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_1_screenshot_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_1_screenshot_1.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_1_screenshot_1.png new file mode 100644 index 0000000..bcf5af9 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_1_screenshot_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_2.json b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_2.json new file mode 100644 index 0000000..fbd67cc --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_2.json @@ -0,0 +1,20 @@ +{ + "step": 1, + "rollout_idx": 2, + "task_id": "mock_task_001", + "reward": 1.0, + "num_steps": 2, + "instruction": "Click the OK button", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.50, y=0.50)", + "action_type": "click" + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.50, y=0.55)", + "action_type": "click" + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_2_screenshot_0.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_2_screenshot_0.png new file mode 100644 index 0000000..71ff70b Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_2_screenshot_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_2_screenshot_1.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_2_screenshot_1.png new file mode 100644 index 0000000..512ad85 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_2_screenshot_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_3.json b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_3.json new file mode 100644 index 0000000..821c840 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_3.json @@ -0,0 +1,20 @@ +{ + "step": 1, + "rollout_idx": 3, + "task_id": "mock_task_001", + "reward": 0.0, + "num_steps": 2, + "instruction": "Click the OK button", + "actions": [ + { + "step_idx": 0, + "action_text": "CLICK(x=0.60, y=0.50)", + "action_type": "click" + }, + { + "step_idx": 1, + "action_text": "CLICK(x=0.60, y=0.55)", + "action_type": "click" + } + ] +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_3_screenshot_0.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_3_screenshot_0.png new file mode 100644 index 0000000..0191bdf Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_3_screenshot_0.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_3_screenshot_1.png b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_3_screenshot_1.png new file mode 100644 index 0000000..da6a005 Binary files /dev/null and b/test_artifacts/grpo_e2e/20261016_215929/rollout_traces/step_1_rollout_3_screenshot_1.png differ diff --git a/test_artifacts/grpo_e2e/20261016_215929/summary.txt b/test_artifacts/grpo_e2e/20261016_215929/summary.txt new file mode 100644 index 0000000..464d3a6 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/summary.txt @@ -0,0 +1,25 @@ +============================================================ +GRPO E2E TEST REPORT: test_e2e_training_loop_mock +============================================================ +Status: passed +Duration: 0.04s +Training steps: 2 +Rollouts per step: 4 +Total rollouts: 8 + +--- Model Weight Change --- +Weights changed: True +Total delta L2 norm: 0.185477 + embedding.weight: delta=0.025376 (norm 91.4769 -> 91.4746) + head.weight: delta=0.180939 (norm 9.2112 -> 9.2100) + head.bias: delta=0.031921 (norm 1.5798 -> 1.5810) + +--- Training Metrics --- + Step 0: loss=0.0000 reward_mean=0.50 grad_norm=1.0000 valid_terms=8 + Step 1: loss=0.0000 reward_mean=0.50 grad_norm=1.0000 valid_terms=8 + +--- Checkpoint --- +Checkpoint path: test_artifacts/grpo_e2e/20261016_215929/checkpoint/step_2 +Checkpoint loadable: True + +============================================================ \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/test_report.json b/test_artifacts/grpo_e2e/20261016_215929/test_report.json new file mode 100644 index 0000000..a187795 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/test_report.json @@ -0,0 +1,15 @@ +{ + "test_name": "test_e2e_training_loop_mock", + "start_time": "2026-10-16T21:59:29", + "status": "passed", + "duration_s": 0.036730051040649414, + "num_training_steps": 2, + "num_rollouts_per_step": 4, + "total_rollouts": 8, + "model_weights_changed": true, + "total_delta_norm": 0.1854769170765381, + "checkpoint_loadable": true, + "final_loss": 0.0, + "final_reward_mean": 0.5, + "final_grad_norm": 1.0000000528613704 +} \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/training_log.json b/test_artifacts/grpo_e2e/20261016_215929/training_log.json new file mode 100644 index 0000000..ba6679a --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/training_log.json @@ -0,0 +1,34 @@ +[ + { + "step": 0, + "loss": 0.0, + "reward_mean": 0.5, + "reward_std": 0.5, + "advantages": [ + 0.9999999800000003, + -0.9999999800000003, + 0.9999999800000003, + -0.9999999800000003 + ], + "grad_norm": 1.0000001900372488, + "lr": 0.001, + "valid_terms": 8, + "step_time": 0.01689004898071289 + }, + { + "step": 1, + "loss": 0.0, + "reward_mean": 0.5, + "reward_std": 0.5, + "advantages": [ + 0.9999999800000003, + -0.9999999800000003, + 0.9999999800000003, + -0.9999999800000003 + ], + "grad_norm": 1.0000000528613704, + "lr": 0.001, + "valid_terms": 8, + "step_time": 0.015859127044677734 + } +] \ No newline at end of file diff --git a/test_artifacts/grpo_e2e/20261016_215929/weight_diff/detailed_diff.json b/test_artifacts/grpo_e2e/20261016_215929/weight_diff/detailed_diff.json new file mode 100644 index 0000000..7467ea5 --- /dev/null +++ b/test_artifacts/grpo_e2e/20261016_215929/weight_diff/detailed_diff.json @@ -0,0 +1,51 @@ +{ + "parameters": { + "embedding.weight": { + "before_norm": 90.87411499023438, + "after_norm": 90.87342071533203, + "delta_l2": 0.012669733725488186, + "delta_max": 0.0010232925415039062, + "delta_mean": 5.214037628320511e-07, + "shape": [ + 256, + 32 + ], + "num_elements": 8192, + "grad_norm": 3.2811145782470703, + "grad_max": 0.7971603870391846 + }, + "head.weight": { + "before_norm": 9.232221603393555, + "after_norm": 9.232917785644531, + "delta_l2": 0.09049731492996216, + "delta_max": 0.0010017603635787964, + "delta_mean": -0.00018775406351778656, + "shape": [ + 256, + 32 + ], + "num_elements": 8192, + "grad_norm": 32.86104965209961, + "grad_max": 7.829895496368408 + }, + "head.bias": { + "before_norm": 1.5759165287017822, + "after_norm": 1.576166033744812, + "delta_l2": 0.015995899215340614, + "delta_max": 0.0010016560554504395, + "delta_mean": -7.669492333661765e-06, + "shape": [ + 256 + ], + "num_elements": 256, + "grad_norm": 3.992352247238159, + "grad_max": 2.0116944313049316 + } + }, + "summary": { + "total_params": 16640, + "trainable_params": 16640, + "changed_param_groups": 3, + "total_param_groups": 3 + } +} \ No newline at end of file diff --git a/tests/test_kaggle_cloud_and_modal_guard.py b/tests/test_kaggle_cloud_and_modal_guard.py new file mode 100644 index 0000000..fef3c44 --- /dev/null +++ b/tests/test_kaggle_cloud_and_modal_guard.py @@ -0,0 +1,88 @@ +"""Kaggle staging and the Modal GPU production guard (no network).""" + +from __future__ import annotations + +import json + +import pytest + +from openadapt_ml.cloud import kaggle_cloud +from openadapt_ml.cloud.modal_gpu_guard import modal_gpu_refusal + + +def test_guard_refuses_by_default(): + assert "off by default" in modal_gpu_refusal({}, profile="other") + + +@pytest.mark.parametrize("environment", ["", "main", "prod", "Production"]) +def test_guard_refuses_production_environments(environment): + env = {"OPENADAPT_ALLOW_MODAL_GPU": "1", "MODAL_ENVIRONMENT": environment} + assert "non-production" in modal_gpu_refusal(env, profile="research") + + +def test_guard_refuses_production_workspace(): + env = {"OPENADAPT_ALLOW_MODAL_GPU": "1", "MODAL_ENVIRONMENT": "gpu-research"} + assert "production workspace" in modal_gpu_refusal(env, profile="abrichr") + assert modal_gpu_refusal(env, profile="") is not None + + +def test_guard_allows_explicit_nonprod_target(): + env = {"OPENADAPT_ALLOW_MODAL_GPU": "1", "MODAL_ENVIRONMENT": "gpu-research"} + assert modal_gpu_refusal(env, profile="openadapt-research") is None + + +def test_modal_train_cli_refuses_without_opt_in(monkeypatch, tmp_path, capsys): + from openadapt_ml.cloud.modal_cloud import cli_main + + monkeypatch.delenv("OPENADAPT_ALLOW_MODAL_GPU", raising=False) + rc = cli_main(["train", "--bundle", str(tmp_path)]) + assert rc == 1 + assert "Refusing Modal GPU work" in capsys.readouterr().out + + +def test_stage_writes_gpu_kernel_and_dataset(tmp_path): + bundle = tmp_path / "bundle" + bundle.mkdir() + (bundle / "training_data.jsonl").write_text("{}\n") + config = tmp_path / "c.yaml" + config.write_text("model: x\n") + ckpt = tmp_path / "results" + ckpt.mkdir() + (ckpt / "adapter.bin").write_bytes(b"0") + d, k = kaggle_cloud.stage( + bundle, + config, + tmp_path / "out", + "someone", + "oa-train-data", + "oa-train", + checkpoint=ckpt, + ) + meta = json.loads((k / "kernel-metadata.json").read_text()) + assert ( + meta["enable_gpu"] is True + and meta["enable_internet"] is True + and meta["is_private"] is True + ) + assert meta["dataset_sources"] == ["someone/oa-train-data"] + assert (k / meta["code_file"]).is_file() + assert (d / "bundle" / "training_data.jsonl").is_file() + assert (d / "checkpoint" / "adapter.bin").is_file() + assert ( + json.loads((d / "dataset-metadata.json").read_text())["id"] + == "someone/oa-train-data" + ) + + +def test_stage_rejects_bad_slug_and_missing_bundle(tmp_path): + with pytest.raises(ValueError): + kaggle_cloud.kernel_metadata("u", "Bad Slug", "oa-train-data", "NvidiaTeslaT4") + with pytest.raises(FileNotFoundError): + kaggle_cloud.stage( + tmp_path, + tmp_path / "c.yaml", + tmp_path / "o", + "u", + "oa-train-data", + "oa-train", + ) diff --git a/tests/test_modal_cloud.py b/tests/test_modal_cloud.py index ff2e274..73c91b0 100644 --- a/tests/test_modal_cloud.py +++ b/tests/test_modal_cloud.py @@ -9,6 +9,16 @@ import pytest + +@pytest.fixture(autouse=True) +def _allow_modal_gpu_in_mocked_tests(): + """These tests mock Modal; the production GPU guard is tested separately.""" + with ( + patch("openadapt_ml.cloud.modal_cloud.modal_gpu_refusal", return_value=None), + patch("openadapt_ml.cloud.modal_cloud.require_nonprod_modal_gpu"), + ): + yield + # --------------------------------------------------------------------------- # CLI argument parsing tests # ---------------------------------------------------------------------------