Repository navigation
feat(training): train with TRL on a bundle served as an OpenEnv env - #363
Merged
Merged
Conversation
agent_env.training.trl, behind a new `trl` extra (openenv and datasets): AgentEnv(url) reads a build_app server's tasks into a dataset, one row per task, and builds TRL's environment_factory. Each environment holds one session on the server and plays one episode at a time over OpenEnv's WebSocket. The turn's tools become typed, documented methods, reset runs in the background, end_turn hands over the next prompt, and get_reward owns the reward, None when the episode ended in an error. GRPOTrainer and AsyncGRPO take both unchanged. AgentEnv.local(bundle) serves the bundle in the training process. The Task API now names each task's toolset (the played agent's envs, role, and whether it gets bash). Tasks with different toolsets take TRL's multi-environment mode. The dependency tests now allow an extra that includes another, as trl includes openenv. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
… and tool access joins the toolset - end_turn takes the reset deadline, not a tool's. The server answers it once the next turn opens or the verifiers have scored the episode. - factory lists each toolset's tools in turn, so a server that allows one session still sets up. - A task's toolset now includes its modify_env_tool_access changes for the played agent's role. Tasks that hide different tools from the same role get classes of their own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…instructions reach the model - factory plays its probe task through every turn, ending each one empty, and builds the class from every tool any turn offers. A tool a turn doesn't offer answers with the server's not-offered message, and each rollout's first turn is still checked against the probe's. - end_turn puts the next turn's system prompt ahead of its prompt when it changed, since TRL can't change the conversation's own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e on the whole probe Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…module, and transformers' read of them tested - trl.py takes end_turn's name from agent_env.openenv's END_TURN, and the Task API's toolset is a Toolset TypedDict that both sides use. The session's list_tools, call_tool and end_turn messages are built in one place each. - The JSON-Schema-to-method code moves to agent_env/training/_tools.py, which no trainer owns; trl.py keeps the TRL API and the sessions. - tools_test.py checks that transformers' get_json_schema, which TRL shows a model the tools through, reads each generated method back as the env describes it. transformers joins dev. - The README and the module say which TRL is verified (1.16, GRPOTrainer) and needed (1.15 or later). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
Had Claude run a review, can you take a look please: Must fix
Worth fixing (can be follow-ups)
|
…, rows checked by task id, and more From a review on the pull request: 1. A tool's deadline travels in the call's action metadata, and the server ends the call at it. The model is told the tool didn't answer, the episode goes on, and the session is kept. Before, the client dropped the session while the server still held its slot, so the next reset could hit the server's limit and stop training. Only an environment's first session now raises when refused. A later one, opened in the background after a drop, is retried while the server is full, and failing that costs one rollout. 2. Rows carry a task_id, and a rollout is checked against the task it plays, not its prompt, which the user may change. 3. Toolsets include the env versions the task deploys, and a rollout whose tools aren't its class's makes get_reward raise instead of leaving tasks unscored without a sound. 4. The Task API gives each task's turn count, so building the factory lists every turn's tools and ends the probe unscored, without running the verifiers. 5. A test imports agent_env.openenv and agent_env.training.trl with nothing beyond their own extra installed. 6. The TRL integration tests are in the slow tier. 7. AgentEnv's docstring says that the model's reply reaches the task only through end_turn. 8. The loop helpers are private, the exit hook waits at most 30 s per stage, and the README section moves to the pull request as notes for the docs site. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Collaborator
Author
|
Thanks for the thorough review, all addressed in 6b6cb5b:
|
…ait out teardown at exit Cancelling a timed-out tool call doesn't stop what the call does, so the server now ends the episode and the rollout is left unscored; the session is kept for the next one. The exit hook waits up to 180 s for each local server to tear down its episodes, past a sandbox's two-minute teardown, and warns when one is still tearing down. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
An episode's teardown saves the agents' changelogs, ends open recordings and terminates the sandboxes, each part within TERMINATE_TIMEOUT_SECONDS, so a server AgentEnv.local started now gets three of those plus a minute to stop, at close and at exit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
earakely-scale
enabled auto-merge (squash)
October 28, 2026 18:47
earakely-scale
disabled auto-merge
October 28, 2026 18:47
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.
Summary
TRL's trainers can now train on a bundle served by
agent_env.openenv.build_app, through TRL's ownenvironment_factory, with no changes to TRL.GRPOTraineris verified with TRL 1.16, including on a GPU (below).environment_factory, but hasn't been run yet.What's new:
agent_env/training/trl.py, behind a newtrlextra (agentenv-framework[openenv]anddatasets). TRL itself stays the trainer's to pin. Later trainers' modules go inagent_env/training/too, each behind its own extra.agent_env/training/_tools.pyturns an env's tools into typed, documented Python methods from their JSON Schema, in the form transformers'get_json_schemareads, which is how TRL shows tools to a model.env.datasethas one row per task of the split, read from the server's Task API:splitandindexthatresetplays;task_idthat each rollout is checked against.env.dataset_for(split)reads any other split.env.factoryis a generated class. Its methods are every tool the tasks' turns offer, plusend_turn.GenericEnvClient), and plays one episode at a time.resetreturns at once, becauseGRPOTrainerresets one rollout at a time. The first tool call orget_rewardwaits for the deploy.end_turnanswers with the next turn's prompt, preceded by its system prompt when that changed. A turn the model leaves open ends with an empty reply. TRL hands an environment no completion, so the model's reply reaches the task only throughend_turn(response=...).timeout_s), and the server ends the call at it. Cancelling the call doesn't stop what it does, so the server ends the episode too: the rollout is left unscored (None), and the session is kept for the next one.The environment owns the reward through
get_reward, which both trainers read, so noreward_funcsare needed.Nonewhen the episode ended in an error or played a different task than its row.Fails early, with the fix in the message:
MAX_CONCURRENT_ENVS/max_concurrent_envs=.A later session, opened in the background after one was dropped, is retried while the server is full. If it still fails, only that rollout is lost.
Changes to the server's Task API:
toolset: each task's description names the played agent's envs, the env versions the task deploys, the agent's role, itsmodify_env_tool_accesschanges, and whether its turns offerbash. Tasks with different toolsets get one class each (factoryis then a dict), and each row names itsenvironment.turns: each task's description says how many turns it has.AgentEnvEnvironmenthonors a tool call'stimeout_smetadata. A call that misses it ends the episode with an error.Also in this PR:
AgentEnv.local(bundle, ...)servesbuild_appin process on a loopback port. A trainer that exits or crashes ends the episodes it left running and stops that server. It waits up to 30 s for the sessions to close, then up to 7 minutes for the server to tear down their sandboxes: a teardown's three parts (changelogs, recordings, sandboxes) take at most 2 minutes each, plus a minute. It warns if the server is still tearing down.close()gives the server the same time. A hard kill (OOM,SIGKILL, a preempted node) runs no teardown, so those sandboxes last as long as their provider lets them.build_apphas its app construction split into_app(served, ...), so the unit tests serve hand-built tasks through the same server.trlincludesopenenv). A new check importsagent_env.openenvandagent_env.training.trlwith nothing installed beyond their own extra.THIRD_PARTY_NOTICES.mdadds the five runtime packages the extra brings: datasets, dill, multiprocess, pyarrow and xxhash.dev, for theget_json_schematest.OpenEnv 0.10 requires
fastmcp<4, and so mcp 1. A trainer with OpenEnv installed therefore runs vLLM 0.29 or older, which TRL 1.16 allows. Once OpenEnv releases FastMCP 4 and agent-env supports mcp 2, the helper runs on vLLM 0.30+ unchanged.What the docs site should cover
A "Train with TRL" page under the OpenEnv docs:
pip install 'agentenv-framework[trl]', with TRL 1.15+ installed by the user; vLLM 0.29 or older while OpenEnv needs mcp 1.openenv importanduv run serverwithMAX_CONCURRENT_ENVS, orbuild_app, orAgentEnv.local(bundle)in the training process.AgentEnv(url, split=...), withenv.dataset/env.dataset_for(split)as the datasets andenv.factoryas theenvironment_factory. Noreward_funcs: the environment owns the reward.end_turn(response=...). A turn left open is scored as an empty reply, so tell the model to end its turn withend_turn.end_turnreturns the next prompt; new system instructions come first.tool_timeout(a call that misses it ends the episode, unscored) andreset_timeout(deploying, and ending each turn).None), and what stops training.Verified on a GPU
GRPOTrainerwith Qwen3.5-9B (LoRA) trained on the PortSim training pack, served bybuild_appon an EC2 g7e:Five steps don't measure learning.
Tests
tst/unit/training/trl_test.pyruns against the real OpenEnv server over the Episode API's fake steps, driven the way TRL drives it. It covers:end_turnthat waits for a slow verifier;local(), with teardown atcloseand at exit, waiting out a slow teardown.tst/unit/training/tools_test.pychecks the generated methods, and that transformers'get_json_schemareads each one back as the env describes it.tst/integration/training/trl_test.py(slow tier, local Docker) has 2 tests:+bashtoolset of its own, plays throughbash, and scores 1.0. Nothing is left running.🤖 Generated with Claude Code


Confidence Score: 5/5
The PR appears safe to merge; no blocking finding remains.Summary
Adds
AgentEnvso TRL can train on bundle tasks served through OpenEnv.Diagram
%%{init: {'theme': 'neutral'}}%% flowchart LR A[Bundle tasks] --> B[OpenEnv server] B --> C[AgentEnv dataset and factory] C --> D[TRL rollout] D --> E[Tool calls and end_turn] E --> B B --> F[Episode reward] F --> DReviews (9) · Last reviewed commit: "fix(training): give a local server a tea..." · Reviewed by Greptile