Repository navigation
Conversation
This was referenced Oct 30, 2026
Contributor
Author
|
/ok to test 7db0586 |
ffrujeri
force-pushed
the
ffrujeri/self-contained-environment-server
branch
from
October 30, 2026 16:21
7db0586 to
fb8001f
Compare
Contributor
Author
|
/ok to test fb8001f |
ffrujeri
force-pushed
the
ffrujeri/self-contained-environment-server
branch
from
October 31, 2026 13:16
fb8001f to
cabd187
Compare
Contributor
Author
|
/ok to test cabd187 |
ffrujeri
force-pushed
the
ffrujeri/self-contained-environment-server
branch
from
November 2, 2026 22:21
cabd187 to
24cd72d
Compare
Contributor
Author
|
/ok to test 24cd72d |
ffrujeri
force-pushed
the
ffrujeri/self-contained-environment-server
branch
from
November 2, 2026 23:01
24cd72d to
ae441ee
Compare
Contributor
Author
|
/ok to test ae441ee |
ffrujeri
force-pushed
the
ffrujeri/self-contained-environment-server
branch
from
November 4, 2026 21:33
ae441ee to
991166f
Compare
Contributor
Author
|
/ok to test 991166f |
…n episode's tools BaseEnvironmentServerWithTools is the base class for an Environment Server that serves each episode's tools itself and owns the episode's state and verification, with no Resources Server: run() holds the episode's state, and serve_tools() grants the agent the episode's tools until the episode's cleanup runs. - EpisodeToolset: the tools of one episode, as specs plus a call method. TypedToolset builds one from @episode_tool methods with a Pydantic model of arguments; a toolset can also come from the task. - Tool calls go to POST /<tool name> at the server's root, as on a Resources Server, with a signed episode token (EpisodeTokens) in a header, and under a rollout prefix as well. - Calls to one episode run one at a time and are recorded in order. - A call after the episode ends gets 410; a missing or foreign token 401, another grant's nonce 403, an unknown tool 404, arguments the tool rejects 422, and a tool that raises 500. - Until session routing covers these servers, a server refuses num_workers > 1, because a tool call can reach a worker that does not hold the episode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Felipe Vieira Frujeri <ffrujeri@nvidia.com>
ffrujeri
force-pushed
the
ffrujeri/self-contained-environment-server
branch
from
November 6, 2026 17:16
991166f to
72c6981
Compare
Contributor
Author
|
/ok to test 72c6981 |
This branch was previously deployed
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.
Stacked on #5346 (episode tokens). Review that one first; this PR's diff against it is the base class and the toolset.
What changed
BaseEnvironmentServerWithTools(nemo_gym/server_runtime/base_environment_server.py, next toBaseEnvironmentServer) is a base class for an Environment Server that serves each episode's tools itself, so it needs no Resources Server. Other Environment Servers grant the agent a Resources Server's tools; this one owns the tools, the episode's state and verification. It does not impose turns: a subclass runs its episodes as it likes, and the user-simulation layer adds the turn loop on top.BaseEnvironmentServerdoes not change.EpisodeToolset(nemo_gym/server_runtime/episode_tools.py): the tools of one episode, asspecs()andcall(name, arguments).TypedToolsetbuilds one from@episode_toolmethods that take a Pydantic model of arguments; the docstring is the description and the model's JSON schema the parameters. A toolset can also come from the task, asnemo_user_sim's probe tools will.serve_tools(request, toolset, cleanup)grants the toolset until the episode's cleanup runs. It returns aDirectHTTPToolAccessthat points at this server, with a signed episode token (feat(environment): sign episode tokens for Environment Server tool grants #5346) in its headers, and the list that records each call.POST /<tool name>at the server's root, as on a Resources Server, sosimple_agentcalls them unchanged, also under a/ng-rollout/<id>/prefix. A tool cannot take the name of one of the server's routes, such asrun.nemo_gym.server_runtimeexportsBaseEnvironmentServerWithTools,ServedToolsand the toolset names (EpisodeToolset,TypedToolset,episode_tool,ToolSpec,ToolResult,ToolCallRecord,ToolArgumentsError), next toBaseEnvironmentServer, because a server outsidenemo_gym/imports from the package, not from its submodules.ToolCallRecord: name, arguments, output, error flag, duration).ToolResult(is_error=True)for an error the agent should handle)Why
This is the core abstraction for environments that drop their Resources Server: BIRD-Interact first, then
nemo_user_sim(Linear GYMINT-640; the user-simulation RFC's layer 1,BaseEnvironmentServerWithTools). Per review, it is a new base class rather than a change toBaseEnvironmentServer, so existing Environment Servers are untouched. It was briefly namedBaseSelfContainedEnvironmentServer, thenBaseTurnBasedEnvironmentServer; it is now named for what it adds, since it does not impose turns.Interim limit: until session routing covers these servers (the next PR in the stack), a
BaseEnvironmentServerWithToolsrefusesnum_workers > 1. With several workers, a tool call can reach a worker that does not hold the episode. Single-worker is not the target; the routing PR removes the limit.Validation
pytest tests/unit_tests/server_runtime/test_base_environment_server_with_tools.py tests/unit_tests/server_runtime/test_episode_tools.py tests/unit_tests/server_runtime/test_environment_server.py tests/unit_tests/test_public_api.py tests/unit_tests/test_import_linter_coverage.py(after the rebase onto main atdda609f86)pytest tests/unit_tests/server_runtime/test_base_environment_server_with_tools.py tests/unit_tests/server_runtime/test_episode_tools.pyhttpx.ASGITransport) whilerun()is in progress: calls change the episode's state and are recorded in order; a call after the episode ends gets 410; five token cases (missing, garbage, another server's, a guessed nonce, another episode's); unknown tool, rejected arguments, a non-object body and a raising tool; a rollout-prefixed call; two concurrent episodes with separate tokens; conflicting toolsets fail the episode; one grant per episode;num_workers=2refused.test_calls_to_one_episode_run_one_at_a_timefails (three calls ran at once), so the test checks the lockpytest tests/unit_tests/server_runtime/test_environment_server.py tests/unit_tests/server_runtime/test_episode_tokens.pypre-commit runon the changed filesRollout evidence
N/A: a base class that no environment uses yet. BIRD-Interact's move onto it (Linear GYMINT-646) will run rollouts with
simple_agentand Codex.Compatibility
New modules only.
BaseEnvironmentServerand every existing server are unchanged.Next in the stack
MCP exposure of an episode's toolset, multi-worker session routing on the episode token, and running open episodes' cleanups at shutdown.
🤖 Generated with Claude Code