v2: tunnel-free proxy via SPACE_HOST+Jobs expose_public, heartbeat watchdog (30min stall -> fail), keep UploadCheckpointsCallback
Browse files- run_job.sh +24 -1
- train_lora.py +14 -1
run_job.sh
CHANGED
|
@@ -64,9 +64,32 @@ vllm serve "${MODEL:-LiquidAI/LFM2.5-2.6B}" \
|
|
| 64 |
VLLM_PID=$!
|
| 65 |
|
| 66 |
echo "=== [start] env server (CPU) ==="
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 67 |
PREPARED=/app/prepared bash serve_env.sh &
|
| 68 |
ENV_PID=$!
|
| 69 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
echo "=== [wait] servers ==="
|
| 71 |
python3 - <<'EOF'
|
| 72 |
import time, urllib.request
|
|
@@ -135,5 +158,5 @@ if repo:
|
|
| 135 |
EOF
|
| 136 |
fi
|
| 137 |
|
| 138 |
-
kill $VLLM_PID $ENV_PID 2>/dev/null || true
|
| 139 |
echo "=== done ==="
|
|
|
|
| 64 |
VLLM_PID=$!
|
| 65 |
|
| 66 |
echo "=== [start] env server (CPU) ==="
|
| 67 |
+
# Tunnel-free proxy exposure: with SPACE_HOST set, OpenEnv mounts the capture
|
| 68 |
+
# proxy at /capture on the env server itself, reached through the Jobs proxy at
|
| 69 |
+
# https://<JOB_ID>--8000.hf.jobs — no gradio tunnel to die mid-run
|
| 70 |
+
# (that failure stalled the first full run silently for 13h).
|
| 71 |
+
export SPACE_HOST="${JOB_ID}--8000.hf.jobs"
|
| 72 |
+
echo "capture proxy public base: https://${SPACE_HOST}/capture"
|
| 73 |
PREPARED=/app/prepared bash serve_env.sh &
|
| 74 |
ENV_PID=$!
|
| 75 |
|
| 76 |
+
echo "=== [start] watchdog (fail the job if no training step for 30 min) ==="
|
| 77 |
+
(
|
| 78 |
+
while true; do
|
| 79 |
+
sleep 300
|
| 80 |
+
if [ -f /tmp/rl-heartbeat ]; then
|
| 81 |
+
age=$(( $(date +%s) - $(stat -c %Y /tmp/rl-heartbeat) ))
|
| 82 |
+
if [ "$age" -gt 1800 ]; then
|
| 83 |
+
echo "WATCHDOG: no training step for ${age}s — rollout path is dead, failing the job"
|
| 84 |
+
pkill -f train_lora.py || true
|
| 85 |
+
sleep 30
|
| 86 |
+
exit 1
|
| 87 |
+
fi
|
| 88 |
+
fi
|
| 89 |
+
done
|
| 90 |
+
) &
|
| 91 |
+
WATCHDOG_PID=$!
|
| 92 |
+
|
| 93 |
echo "=== [wait] servers ==="
|
| 94 |
python3 - <<'EOF'
|
| 95 |
import time, urllib.request
|
|
|
|
| 158 |
EOF
|
| 159 |
fi
|
| 160 |
|
| 161 |
+
kill $VLLM_PID $ENV_PID $WATCHDOG_PID 2>/dev/null || true
|
| 162 |
echo "=== done ==="
|
train_lora.py
CHANGED
|
@@ -84,6 +84,19 @@ class UploadCheckpointsCallback(TrainerCallback):
|
|
| 84 |
return control
|
| 85 |
|
| 86 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
def correctness_plus_efficiency(outcome):
|
| 88 |
"""1/0 correctness, plus <= 0.1 for a correct answer in fewer tool calls.
|
| 89 |
|
|
@@ -190,7 +203,7 @@ def main():
|
|
| 190 |
processing_class=tokenizer,
|
| 191 |
rollout_worker=worker,
|
| 192 |
peft_config=peft_config,
|
| 193 |
-
callbacks=[UploadCheckpointsCallback(ADAPTER_REPO)],
|
| 194 |
)
|
| 195 |
trainer.train()
|
| 196 |
trainer.save_model("./checkpoints/final") # adapter_model.safetensors + adapter_config.json
|
|
|
|
| 84 |
return control
|
| 85 |
|
| 86 |
|
| 87 |
+
class HeartbeatCallback(TrainerCallback):
|
| 88 |
+
"""Touch a file at every optimizer step; run_job.sh's watchdog kills the
|
| 89 |
+
job if it goes stale. The first full run stalled silently for 13h when the
|
| 90 |
+
rollout path died — steps just stopped, and nothing noticed."""
|
| 91 |
+
|
| 92 |
+
PATH = "/tmp/rl-heartbeat"
|
| 93 |
+
|
| 94 |
+
def on_step_end(self, args, state, control, **kwargs):
|
| 95 |
+
with open(self.PATH, "w") as f:
|
| 96 |
+
f.write(str(state.global_step))
|
| 97 |
+
return control
|
| 98 |
+
|
| 99 |
+
|
| 100 |
def correctness_plus_efficiency(outcome):
|
| 101 |
"""1/0 correctness, plus <= 0.1 for a correct answer in fewer tool calls.
|
| 102 |
|
|
|
|
| 203 |
processing_class=tokenizer,
|
| 204 |
rollout_worker=worker,
|
| 205 |
peft_config=peft_config,
|
| 206 |
+
callbacks=[UploadCheckpointsCallback(ADAPTER_REPO), HeartbeatCallback()],
|
| 207 |
)
|
| 208 |
trainer.train()
|
| 209 |
trainer.save_model("./checkpoints/final") # adapter_model.safetensors + adapter_config.json
|