SaylorTwift HF Staff
Pin trl==1.15.0 (pre-tree-packing, LFM2 hybrid compatible) + vllm v0.31.0 image
73712c7 verified |
Download README.md from SaylorTwift/lfm-opencode-lora-rl-code: direct link, hf CLI and curl.
- Browser
- Download file 6.52 kB
-
https://huggingface.co/SaylorTwift/lfm-opencode-lora-rl-code/resolve/main/README.md
- Command line
-
hf download hf://SaylorTwift/lfm-opencode-lora-rl-code/README.md
-
curl -L -o README.md https://huggingface.co/SaylorTwift/lfm-opencode-lora-rl-code/resolve/main/README.md
6.52 kB
| # OpenCode-only RL on LFM2.5-2.6B with LoRA | |
| Reproduction of the OpenCode-only run from | |
| [The ultimate guide to multi-harness RL](https://huggingface.co/spaces/FineEnvs/multi-harness-rl), | |
| but training a **LoRA adapter** instead of full weights, so the result is a small, | |
| distributable adapter on top of `LiquidAI/LFM2.5-2.6B`. | |
| Stack: OpenEnv (capture proxy + Harbor integration) β Harbor (tasks + **HF Sandboxes**) β | |
| TRL `AsyncGRPOTrainer` with `HarnessRolloutWorker` β vLLM policy server. | |
| Every API here was verified against the released wheels (trl 1.16.0, openenv 0.10.0, | |
| harbor 0.24.0), the `harbor_env` source, and the FineEnvs production env Space. | |
| **Version pins that matter:** `trl==1.15.0` (1.16.0 made tree packing unconditional | |
| in AsyncGRPOTrainer and refuses hybrid models β LFM2.5 has conv layers; 1.15.0 has | |
| the harness worker but predates tree packing) and `vllm/vllm-openai:v0.31.0` (top | |
| of trl 1.15.0's supported vLLM band, and predates its mcp>=2 requirement which | |
| conflicts with openenv's fastmcp 3.x). | |
| **Sandboxes are HF Sandboxes** (Harbor's `hf-sandbox` backend, harbor>=0.24): each | |
| rollout boots an HF Jobs cpu-basic machine running the task's prebuilt image | |
| (`savatar101/env-data-agent-train:base` β the same image every task's Dockerfile is | |
| a one-line `FROM` of). Only `HF_TOKEN` is needed; no Daytona/E2B account. | |
| ## Why merged-weight sync (source-verified) | |
| Generation goes harness β capture proxy β vLLM, addressed by a **fixed model name**. | |
| TRL's adapter-only sync publishes versioned adapters (`trl-policy-v{N}`) and only the | |
| trainer-built default worker learns the versioned name; an injected | |
| `HarnessRolloutWorker` never gets one. So the server runs *without* `--enable-lora` | |
| and the trainer merges the LoRA into the base and NCCL-streams full weights each step | |
| (~5.2 GB for 2.6B β trivial on one node; vLLM also serves adapters 1.33β1.39x slower | |
| per token). **Checkpoints still contain only the LoRA adapter** β that is what you ship. | |
| ## Files | |
| | File | Purpose | | |
| |---|---| | |
| | `train_lora.py` | Worker wiring + `AsyncGRPOConfig` + `LoraConfig` (r=32 on q/k/v/out_proj, w1/2/3). Task dataset is discovered from the env server via `HarborSessionFactory.prompt_rows()` β `create()` rejects any prompt the server doesn't serve | | |
| | `serve_policy.sh` | vLLM with capture-proxy flags + NCCL weight transfer | | |
| | `serve_env.sh` | `openenv harbor serve` with production env vars (`MAX_CONCURRENT_ENVS`, pinned OpenCode 1.18.31, 4096 output cap) | | |
| | `prepare_tasks.py` + `data/` | Materializes the pinned 1,000 train / 250 test tasks (hash-checked, from the FineEnvs Space) and patches `docker_image` into each task.toml β the one change HF Sandboxes require | | |
| | `run_job.sh` | Single-container entrypoint for HF Jobs (setup + all 3 processes) | | |
| | `export_adapter.py` | Publish any adapter checkpoint to the Hub (+ optional merged model) | | |
| ## Launch on HF Jobs | |
| ```bash | |
| # 0. One-time: put this folder on the Hub so the job can pull it | |
| hf repos create YOU/lfm-opencode-lora-rl-code --type model --private | |
| hf upload YOU/lfm-opencode-lora-rl-code . \ | |
| --include "*.py" --include "*.sh" --include "*.md" --include "data/*.json" | |
| # 1. Smoke run (~1-2h): validates weight sync, capture contract, sandbox boots | |
| hf jobs run vllm/vllm-openai:v0.31.0 --flavor h200x2 --timeout 6h --detach \ | |
| --secrets HF_TOKEN="$(hf auth token)" \ | |
| --env ADAPTER_REPO=YOU/LFM2.5-2.6B-opencode-lora-RL MAX_STEPS=20 \ | |
| -- bash -c 'pip -q install huggingface_hub && hf download YOU/lfm-opencode-lora-rl-code --local-dir /app && bash /app/run_job.sh' | |
| # 2. Watch: merged weight transfers (no .vllm_lora/), sandbox Jobs booting under | |
| # your account (hf jobs ps), rollouts returning rewards, no CaptureContractError. | |
| hf jobs logs <job-id> --follow | |
| # 3. Full run (the article's OpenCode-only run took ~32h on 2x H100) | |
| hf jobs run vllm/vllm-openai:v0.31.0 --flavor h200x2 --timeout 72h --detach \ | |
| --secrets HF_TOKEN="$(hf auth token)" \ | |
| --env ADAPTER_REPO=YOU/LFM2.5-2.6B-opencode-lora-RL MAX_STEPS=1000 \ | |
| -- bash -c 'pip -q install huggingface_hub && hf download YOU/lfm-opencode-lora-rl-code --local-dir /app && bash /app/run_job.sh' | |
| ``` | |
| Checkpoints (adapter-only) push to `ADAPTER_REPO` every 100 steps, so a dead job | |
| loses little. `SANDBOX=daytona`/`e2b` + the matching API key secret still works. | |
| ## What you distribute | |
| Every checkpoint contains `adapter_model.safetensors` + `adapter_config.json` | |
| (~50 MB at r=32). `export_adapter.py` adds a model card and pushes: | |
| ```bash | |
| python export_adapter.py checkpoints/checkpoint-1000 YOU/LFM2.5-2.6B-opencode-lora-RL [--also-merged] | |
| ``` | |
| Consumers load it with `PeftModel.from_pretrained("LiquidAI/LFM2.5-2.6B", YOU/...)` or | |
| `vllm serve LiquidAI/LFM2.5-2.6B --enable-lora --max-lora-rank 32 --lora-modules opencode-lora=YOU/...` | |
| (`Lfm2ForCausalLM` implements `SupportsLoRA`). | |
| ## Recipe choices carried over from the article (don't regress these) | |
| - `top_p=1.0`, `top_k=0` β truncation biases the policy and breaks logprob agreement. | |
| - Same 4096-token output cap in training and evaluation (`OPENENV_MAX_OUTPUT_TOKENS` | |
| + worker `max_tokens`). A Qwen run with 16k training answers collapsed at 4k eval. | |
| - Correctness + <=0.1 tool-efficiency bonus on correct answers (else all-correct | |
| GRPO groups carry no signal and tool calls creep up). | |
| - `num_generations=8`, one harness per group, so advantage compares actions. | |
| - `max_tool_calling_iterations=32` on the worker β the loop-owning default would | |
| silently cap the agent at 8 model calls. | |
| - `max_staleness=4` (config default) matches the article's async setup. | |
| ## Honest caveats | |
| - `HarnessRolloutWorker` + `peft_config` and `sandbox="hf-sandbox"` are both | |
| untested-in-print combinations on this stack. All pieces are released and the | |
| interfaces line up (verified in source) β the smoke run exists to prove it before | |
| you spend 32+ GPU-hours. | |
| - HF Sandboxes are experimental (per HF docs) and each rollout is an HF Job under | |
| your token: mind your Jobs concurrency quota (`MAX_CONCURRENT_ENVS=8` bounds it) | |
| and per-minute billing of thousands of short cpu-basic jobs over a full run. | |
| - Sandbox boot time (job scheduling + first image pull) adds latency per rollout | |
| vs Daytona/E2B's warm pools; expect slower wall-clock than the article's 32h. | |
| - Expect the article's single-harness shape: gains concentrate in OpenCode | |
| (34 β 58% there in their run) and transfer unevenly elsewhere. | |
| - LoRA r=32 may cap peak accuracy vs full FT; no published comparison on this stack. | |