Skip to content
Open
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8229adb
Add SkyRL-TX Qwen SFT and RL example
devin-ai-integration[bot] Jun 8, 2026
7ea0a21
Pin SkyRL-TX example uv dependency
devin-ai-integration[bot] Jun 8, 2026
1bd7fd4
Address SkyRL-TX review feedback
devin-ai-integration[bot] Jun 8, 2026
20a5eb4
Fix SkyRL-TX clustered run coordination
devin-ai-integration[bot] Jun 8, 2026
4adc767
Thread GPU topology into SkyRL-TX backend config
devin-ai-integration[bot] Jun 8, 2026
b675889
Use full H100 nodes for SkyRL-TX
devin-ai-integration[bot] Jun 8, 2026
825c2e9
Add checkpoint eval logging to SkyRL-TX example
devin-ai-integration[bot] Jun 8, 2026
9d4bb3c
Avoid uv resync in SkyRL-TX subprocesses
devin-ai-integration[bot] Jun 8, 2026
b588e00
Fix SkyRL-TX health readiness check
devin-ai-integration[bot] Jun 8, 2026
5c558c7
Use IPv4 cluster addresses for SkyRL-TX coordinator
devin-ai-integration[bot] Jun 8, 2026
0669119
Stabilize SkyRL-TX smoke learning rates
devin-ai-integration[bot] Jun 9, 2026
5e66341
Stabilize SkyRL-TX smoke optimizer defaults
devin-ai-integration[bot] Jun 9, 2026
b2c3abc
Gate SkyRL-TX worker startup on coordinator launch
devin-ai-integration[bot] Jun 9, 2026
890b37e
Match SkyRL Tinker optimizer defaults
devin-ai-integration[bot] Jun 9, 2026
558adb5
Use local Orbax checkpointing on Modal volumes
devin-ai-integration[bot] Jun 9, 2026
1788571
Allow SkyRL checkpoint restore adapters
devin-ai-integration[bot] Jun 9, 2026
1299b23
Use default Orbax restore barriers
devin-ai-integration[bot] Jun 9, 2026
d2b6ba3
Synchronize SkyRL JAX RPC dispatch
devin-ai-integration[bot] Jun 9, 2026
0143272
Evaluate saved SkyRL sampler checkpoints
devin-ai-integration[bot] Jun 9, 2026
035234d
Scope SkyRL checkpoint listings per run
devin-ai-integration[bot] Jun 9, 2026
4c16a84
Fail fast on zero-step RL runs
devin-ai-integration[bot] Jun 9, 2026
e2af749
Add Tinker cookbook compatibility reports
devin-ai-integration[bot] Jun 9, 2026
f3e4233
Add validated SkyRL-TX cookbook smoke suite
devin-ai-integration[bot] Jun 9, 2026
c27f75b
Generalize SkyRL checkpoint artifact listing
devin-ai-integration[bot] Jun 9, 2026
08e5a52
Document per-example cookbook smoke runs
devin-ai-integration[bot] Jun 9, 2026
0e7da85
Allow cookbook result-only smoke runs
devin-ai-integration[bot] Jun 9, 2026
b2cfb7e
Respect cookbook smoke LoRA rank
devin-ai-integration[bot] Jun 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ Use this repository to learn how to build distributed training jobs on Modal.
- [**`lightning/`**](/lightning/) a simple lightning.ai Fabric example.
- [**`nanoGPT/`**](/nanoGPT/) training Karpathy's nanoGPT reproduction of OpenAI's GPT-2.
- [**`resnet50/`**](/resnet50/) training a ResNet50 model on the ImageNet dataset.
- [**`skyrl-tx/`**](/skyrl-tx/) SkyRL-TX Tinker-compatible SFT and RL on Qwen3-8B.
- [**`starcoder/`**](/starcoder) accelerated finetuning of Llama-2-7B on Rust and Go code, supporting either `torchrun` or `accelerate`.

# Documentation
Expand Down
129 changes: 129 additions & 0 deletions skyrl-tx/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
# SkyRL-TX Qwen SFT and RL on Modal

This example runs [SkyRL-TX](https://github.com/NovaSky-AI/SkyRL/tree/main/skyrl-tx)
as a Tinker-compatible training server on a Modal multi-node GPU cluster.
It uses `Qwen/Qwen3-8B` by default and exercises both:

- supervised fine-tuning with Tinker's `cross_entropy` loss
- policy-gradient RL with Tinker's `ppo` loss over sampled arithmetic rollouts

The default topology is 2 nodes × 8 H100 GPUs. Within each node SkyRL-TX uses
tensor parallelism; across nodes it uses JAX FSDP.

## Prerequisites

- Modal CLI installed and authenticated
- Modal environment selected: `export MODAL_ENVIRONMENT=<your-env>`
- Modal secret `huggingface-secret` with `HF_TOKEN`
- Access to multi-node GPU clusters

Run all commands from the repo root.

## Quickstart

Download the Qwen checkpoint into the persistent Hugging Face cache volume:

```bash
modal run skyrl-tx/modal_train.py::download_model
```

Run the supervised fine-tuning smoke:

```bash
modal run --detach skyrl-tx/modal_train.py::run_sft
```

Run the RL smoke:

```bash
modal run --detach skyrl-tx/modal_train.py::run_rl
```

Use detached mode for the training jobs; the image build, model load, JAX
initialization, and first compile can take several minutes.

## Cluster sizing

The launcher reads this sizing environment variable at import time:

| Variable | Default | Purpose |
| --- | --- | --- |
| `SKYRL_TX_N_NODES` | `2` | Number of Modal containers in the JAX cluster |

Each container always requests a full `H100:8` node. Partial H100 allocations do
not work for multi-node SkyRL-TX runs.

For the default `Qwen/Qwen3-8B` run:

```text
total GPUs = SKYRL_TX_N_NODES × 8 = 16 by default
tensor_parallel_size = 8
fully_sharded_data_parallel_size = SKYRL_TX_N_NODES = 2
```

## How it works

`run_sft` and `run_rl` create a per-run ephemeral Modal Dict for coordination,
then launch clustered `run_sft_cluster` or `run_rl_cluster` functions. Rank 0
starts the SkyRL-TX Tinker API server:

```bash
uv run --extra gpu --extra tinker --extra jax -m skyrl.tinker.api \
--base-model Qwen/Qwen3-8B \
--backend jax \
--backend-config '{"tensor_parallel_size": 8, "fully_sharded_data_parallel_size": 2, ...}'
```

Ranks 1..N start SkyRL-TX JAX workers:

```bash
uv run --extra gpu --extra tinker --extra jax -m skyrl.backends.jax \
--coordinator-address <rank-0-ip>:7777 \
--num-processes <N> \
--process-id <rank>
```

After the API server reports healthy, rank 0 runs either `sft_client.py` or
`rl_client.py` against `http://localhost:8000`.

## Volumes

| Volume | Mount path | Purpose |
| --- | --- | --- |
| `skyrl-tx-hf-cache` | `/root/.cache/huggingface` | Qwen model cache |
| `skyrl-tx-checkpoints` | `/checkpoints` | SkyRL-TX LoRA checkpoints |

Each training job saves both a Tinker training-state checkpoint and a sampler
checkpoint. The clients run a small evaluation pass after training, then load the
saved sampler checkpoint for a sample/reward evaluation. Rank 0 lists the
checkpoint files it found and commits the Modal volume. Successful runs print
lines like:

```text
sft_state_checkpoint=file://...
sft_eval_loss=...
sft_sampler_checkpoint=file://...
sft_sampler_eval_sample=...
sft_checkpoint_file=... bytes=...
sft_checkpoint_volume_committed=...
rl_state_checkpoint=file://...
rl_eval_loss_outputs=...
rl_sampler_checkpoint=file://...
rl_eval mean_reward=... trajectories=...
rl_checkpoint_file=... bytes=...
rl_checkpoint_volume_committed=...
```

## Adjusting the smoke

Both entrypoints expose small training-loop knobs:

```bash
modal run --detach skyrl-tx/modal_train.py::run_sft --steps 16 --lora-rank 8 --learning-rate 1e-6
modal run --detach skyrl-tx/modal_train.py::run_rl --steps 8 --samples-per-prompt 4 --learning-rate 1e-6
```

The clients intentionally use tiny arithmetic datasets so the example validates
the end-to-end SkyRL-TX path without requiring a full benchmark-scale run. The
default `1e-6` learning rate keeps short LoRA smoke runs stable on the tiny
batches used here.
Loading