Repository navigation
feat(training): train on a bundle with slime through its OpenAI adapter over the OpenEnv server - #462
Merged
Merged
Conversation
…er over the OpenEnv server agent_env.training.slime's generate function opens a session for each sample on slime's OpenAI adapter (slime.agent), which records the model's turns, plays the row's episode through it and returns the session's turns as samples carrying the episode's reward. The chat rows and the episode played over a trainer's chat endpoint move from the Miles helper into agent_env.training._chat, which both helpers share; the Miles helper's public names stay. tst/slime plays rows through slime's own adapter with slime's own arguments, and slime.yml runs it on slime 540ea91 and main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l a rollout mid-episode Episodes come back as a sample per turn when the adapter can't join a turn to the one before (Qwen3's thinking-off template), and slime's eval reward averages over samples, so it weighted episodes by their turns. In an eval the generate function now drops the session and returns one sample carrying the reward, as slime's own agent examples do. generate opts into slime's request-level abort: when a step's batch is full, slime cancels the rollouts still playing, and each drops its session and closes its episode instead of playing on against the aborted model and running its verifiers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…NTS.md's training helpers by module The slime helper depends on slime.agent's adapter and server helpers and slime's Sample only: its adapter is cached on the class, and its tokenizer loaded with transformers, rather than through slime's SingletonMeta and load_tokenizer, so the tier resets it without slime's private state. The tasks and model call the trainer tiers share (PLAYED, FAILS_AFTER_THE_TURN, ADDS_A_CRATE) live in tst/unit/training/_support.py, not copied between tiers or read from nemo_gym_test, and the shared rows and episode are tested in chat_test.py. AGENTS.md's training row becomes a Training helpers subsection, one entry per module, adding the harness helper it lacked. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ers directly Importing transformers made the module read as using the extras that declare it, which don't declare slime; declaring transformers in the slime extra would let an install move slime's environment off the transformers its SGLang pins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
agent_env.training.slimetrains on a bundle's tasks with slime through slime's agent layer (slime.agent). The model plays the task, as with the Miles, verl and SkyRL helpers, on a bundle served as an OpenEnv env byagent_env.openenv.build_app.python -m agent_env.training.slime URL OUTwrites the server's tasks as slime's prompt data,OUT/<split>.jsonl. The rows are the Miles helper's:messages;metadatawith the episode, its tools and the server's settings.agent_env.training.slime.generate.generateis slime's generate function (--custom-generate-function-path). For each sample it:OpenAIAdapter, which records the model's turns as training samples;It shares the Miles helper's episode loop, so it also inherits:
In an eval,
generatereturns one sample per episode carrying the reward, as slime's own agent examples do.generateopts into slime's request-level abort (generate.abort_mode = "request").generate.keep_scored_groupsis the dynamic-sampling filter (--dynamic-sampling-filter-path). It leaves out a flagged episode's group, and an episode with no recorded turn comes back as one aborted sample withremove_sample.The adapter is one per rollout process, served in-process. It needs
--sglang-tool-call-parser, so the helper refuses to start without one. Without a parser, every rollout would quietly score 0, as it did in Miles' first GPU run.The
slimeextra is theopenenvextra under the trainer's name; install it into slime's own environment.Shared with the Miles helper
The chat rows, the row-writer command and the episode played over a trainer's chat endpoint move from
agent_env.training.milesinto a privateagent_env.training._chat, which both helpers use. The code is unchanged and the Miles helper's public names stay:miles.agent.run,rewardandkeep_scored_groups;miles.prepare.write_rows;python -m agent_env.training.miles.Maintainability
SingletonMeta, and the tier resets it through that class rather than through slime's private state. The tokenizer still comes from slime'sload_tokenizer, the helper slime's own examples build this adapter with. Importing transformers directly would need it declared in theslimeextra, and that could move slime's environment off the transformers its SGLang pins.tst/unit/training/_support.py, beside theservefixture:PLAYED,FAILS_AFTER_THE_TURNandADDS_A_CRATE. Before,tst/milesandtst/slimeeach had a copy, and the unit tier and five trainer tiers imported them fromnemo_gym_test. The unit test of the shared rows and episode is nowchat_test.py.harness.py, which had no entry.slime versions
rollout_args, which main's adapter uses for score centering, and per-sessionapply_chat_template_kwargs, which thinking-off training needs. The CI tier caught this.GPU runs
Setup:
slimerl/slime:latest(main 540ea91) and agentenv-framework 0.9.1418 installed into it.build_appon the host's Docker.user, inside a generate function of the same shape. This PR's code runs through slime's real adapter intst/slimeon CPU.A 20-step run first:
Training updated (grad norm about 0.85 every step) but showed no measurable gain, as SkyRL, TRL and Unsloth didn't at 20 steps. A 40-step run, from a fresh SFT warm start:
What slime needed in those runs:
--sglang-tool-call-parser qwen25.--attention-backend unfused. The image has no FlashAttention-2:flashuses FlashAttention 4's CuTe kernels, which don't compile on sm_89;fused(cuDNN) doesn't take packed sequences there.--max-tokens-per-gpu 4096and no--accumulate-allreduce-grads-in-fp32. At 8192 tokens with fp32 gradient accumulation the backward ran out of memory.--global-batch-sizecounts rollouts, and a rollout's samples stay in one step. slime's owneval/evalaverages per sample, so it weights episodes by their turn count; the tables above are per episode.Tests
tst/unit/training/miles_test.pycovers the shared rows, episode, reward hook and filter. Its command test now runs bothpython -m agent_env.training.milesandpython -m agent_env.training.slime.tst/slime(make slime-test) plays rows through the generate function on slime's ownOpenAIAdapter. It uses slime's ownparse_args, and a stand-in for SGLang's/generatewith Qwen3's tokenizer. It checks:call_dynamic_filterleaves its group out;--sglang-tool-call-parser..github/workflows/slime.ymlruns the tier on slime 540ea91 and main. slime isn't on PyPI, so the job installs:PYTHONPATH, at the SGLang tag and Megatron-LM commit slime's Dockerfile builds on;slimeextra and the shared_chatmodule.Run on a Linux box:
tst/unit/training, the extras test andtst/slimewithout slime: 151 passed, and the tier skipped on its capability;tst/slimeagainst slime main 540ea91: 4 passed;🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge; no actionable new issue was found.Summary
Adds slime training through its OpenAI adapter and an OpenEnv server.
Diagram
%%{init: {'theme': 'neutral'}}%% flowchart LR Rows[Chat rows] --> Generate[slime generate] Generate --> Adapter[slime OpenAI adapter] Adapter --> Model[SGLang] Generate --> Episode[OpenEnv episode] Episode --> Reward[Episode reward] Reward --> Samples[Training samples or one evaluation sample]Reviews (4) · Last reviewed commit: "fix(slime): load the adapter's tokenizer..." · Reviewed by Greptile