# GPU worker runbook (`server_app.py`) Long-lived Modal GPU for iterative finetune / eval loops. Intended for **humans** and **AI coding agents** running many experiments from the same warm container. **Full docs:** [README.md](README.md) · **Code:** [`server_app.py`](server_app.py) · **Jobs:** [`experiments.yaml`](experiments.yaml) --- ## Prerequisites Run from **repo root**. ```bash pip install modal modal setup modal secret create huggingface HF_TOKEN= # once modal deploy research/modal/server_app.py # once per image change ``` | Name | Value | | ---- | ----- | | App | `slm-gpu-worker` | | Class | `GpuWorker` | | GPU | `A10G` | | Volumes | `hf-cache` → `/root/.cache/huggingface`, `slm-finetune` → `/vol/finetuned` | --- ## Start session (human or agent) ```bash # Option A: block terminal (default 4h keep-alive) modal run research/modal/server_app.py # Option B: detached — preferred for agent loops modal run -d research/modal/server_app.py --hours 6 # Verify worker modal run research/modal/server_app.py --ping # → {"status": "ok", "app": "slm-gpu-worker"} ``` --- ## Experiment commands (repeat freely) All commands use the deployed warm worker when `modal deploy` has been run. ```bash # --- Train --- modal run research/modal/server_app.py --job lesson-lora --max-steps 20 modal run research/modal/server_app.py --job alpaca-lora --max-steps 50 modal run research/modal/server_app.py --job smoltalk-lora --max-steps 50 # --- Eval only (adapter must exist on Volume) --- modal run research/modal/server_app.py --eval-only --job lesson-lora modal run research/modal/server_app.py --eval-only # all jobs in experiments.yaml # --- Full pipeline (same container: baseline → train → eval) --- modal run research/modal/server_app.py --pipeline --job lesson-lora --max-steps 20 modal run research/modal/server_app.py --pipeline --job lesson-lora --max-steps 20 --skip-baseline # --- Custom finetune.py flags --- modal run research/modal/server_app.py --cmd \ "uv run python research/finetune.py --preset minicpm5-1b --mode lora \ --dataset research/data/education-lesson-chat.jsonl --format chat \ --out /vol/finetuned/lesson-lora --max_steps 10" # --- Custom lm-eval --- modal run research/modal/server_app.py --cmd \ "uv run --package slm-evals slm-lm-eval \ --config research/evals/configs/lm_eval_smoke.yaml \ --experiment-name lesson-lora__manual \ --output-dir /vol/finetuned/results/lm_eval \ --model openbmb/MiniCPM5-1B \ --adapter /vol/finetuned/lesson-lora" ``` Job names and datasets: [`experiments.yaml`](experiments.yaml). --- ## Inspect results (human or agent) ```bash # List Volume modal volume ls slm-finetune modal volume ls slm-finetune lesson-lora modal volume ls slm-finetune results/lm_eval # Download to laptop modal volume get slm-finetune lesson-lora ./models/finetuned/minicpm5-1b-lora modal volume get slm-finetune results/lm_eval ./results/lm_eval # Stream worker logs modal app logs slm-gpu-worker -f ``` Key artifacts on Volume: | Path | Content | | ---- | ------- | | `/vol/finetuned//` | LoRA adapter + `training_results.json` | | `/vol/finetuned/results/lm_eval//` | `results.json`, `summary.md`, `comparison.md` | --- ## End session ```bash modal app stop slm-gpu-worker -y ``` Stops the deployed app and warm GPU pool. Volume data is retained. --- ## AI agent loop (structured) Use this sequence when an agent is iterating on training or eval without local CUDA. ``` 1. CHECK modal run research/modal/server_app.py --ping 2. BOOT if ping fails → modal deploy ... then modal run -d ... --hours 6 3. SMOKE modal run ... --job lesson-lora --max-steps 5 4. EVAL modal run ... --eval-only --job lesson-lora 5. READ modal volume ls slm-finetune results/lm_eval modal volume get ... (or read comparison.md locally after get) 6. ADJUST edit experiments.yaml OR pass --max-steps / --lm-eval-config 7. GOTO 3 until metrics acceptable 8. PULL modal volume get slm-finetune lesson-lora ./models/finetuned/minicpm5-1b-lora 9. STOP modal app stop slm-gpu-worker -y (optional, saves GPU cost) ``` ### Agent decision rules | Situation | Action | | --------- | ------ | | First time in repo | `modal deploy research/modal/server_app.py` | | `ping` returns ok | Skip boot; run task commands | | `ping` fails / timeout | `modal run -d research/modal/server_app.py --hours 6`, retry ping | | Train OOM | `--cmd` with `--mode qlora` or lower `--max-steps` | | Eval missing adapter | Train first, or `modal volume ls slm-finetune ` | | Need batch parallel GPUs | Use `finetune_app.py --parallel` instead | | Need one-shot CI sweep | Use `finetune_app.py` (not server) | | Image / code changed | Re-run `modal deploy research/modal/server_app.py` | ### Python API (agents in Modal notebook or scripts) ```python import modal Worker = modal.Cls.from_name("slm-gpu-worker", "GpuWorker") w = Worker() assert w.ping.remote()["status"] == "ok" w.finetune.remote({ "name": "lesson-lora", "preset": "minicpm5-1b", "mode": "lora", "dataset": "research/data/education-lesson-chat.jsonl", "format": "chat", "max_steps": 20, }) w.run_pipeline.remote(job_names=["lesson-lora"], max_steps=20) ``` --- ## `finetune_app.py` vs `server_app.py` | | `finetune_app.py` | `server_app.py` | | --- | --- | --- | | App name | `slm-finetune-benchmark` | `slm-gpu-worker` | | Container | New per function call | Warm pool, reused | | Deploy | Optional | **Required** for cross-terminal reuse | | Parallel jobs | `--parallel` (3 GPUs) | Sequential on one GPU | | Best for | Full sweep, reproducible batch | Interactive / agent iteration | | Entry | `modal run research/modal/finetune_app.py` | `modal deploy` + `modal run research/modal/server_app.py` | --- ## Troubleshooting | Symptom | Fix | | ------- | --- | | `scaledown_window must be between 2 and 3600` | Already fixed in `_common.py` (3600 max) | | Deploy succeeds but ping fails | Wait ~30s for warm pool; check `modal app list` | | Command uses cold container | Run `modal deploy` first; confirm app name `slm-gpu-worker` | | HF download every run | `hf-cache` volume should mount; first run populates cache | | Writes not visible | Paths must be under `/vol/finetuned/`, not `/repo/models/` | | GPU still billing overnight | `modal app stop slm-gpu-worker` | --- ## References - [Modal Volumes](https://modal.com/docs/guide/volumes) - [Modal Images](https://modal.com/docs/guide/images) - [modal run](https://modal.com/docs/reference/cli/run) - [modal app stop](https://modal.com/docs/reference/cli/app#modal-app-stop) - [modal shell](https://modal.com/docs/reference/cli/shell) — debug: `modal shell research/modal/server_app.py::GpuWorker.finetune`