Skip to navigation

nemo_gym.server_runtime.base_resources_server

View as Markdown

Module Contents

Classes

NameDescription
BaseMultiRewardVerifyResponseBase verify response for environments with multiple reward objectives.
BaseResourcesServer-
BaseResourcesServerConfig-
BaseRunRequest-
BaseSeedSessionRequest-
BaseSeedSessionResponse-
BaseVerifyRequest-
BaseVerifyResponse-
LostSessionVerificationThe verify response fields of a masked sample for a session the server no longer holds.
MCPServerMetadataMetadata returned from /seed_session for per-rollout Gym MCP access.
ResourcesCloseSessionRequestClose resources-server state.
ResourcesCloseSessionResponseConfirm resources-server state was closed.
ResourcesSeedRejectedErrorRefuse a seed whose input no retry can fix, such as task_data that fails validation.
ResourcesSeedSessionRequestIdempotently initialize resources state under a caller-assigned identifier.
ResourcesSeedSessionResponseConfirm resources state and return optional agent access and an initial observation.
ResourcesSessionConflictErrorRefuse a seed or close that conflicts with what this server recorded for the session.
ResourcesSessionFencesThe fences of the resources sessions this worker closed, each lasting the close retry window.
ResourcesSessionLocksLock each resources session so that its seed and close never interleave.
ResourcesSessionLostErrorAnswer a tool call for a session this server no longer holds with HTTP 404.
ResourcesVerifyRequestCarry typed environment output to a resources server.
ReverifyMode-
SimpleResourcesServer-
_SessionLock-

Functions

NameDescription
lost_session_verificationReturn the verify response fields for an episode whose session this server no longer holds.
mcp_session_token_payloadThe payload of a Gym MCP session token whose signature verifies, or None if it does not.
mcp_session_token_serializerThe signer of Gym MCP session tokens; build it once per server.
normalize_tool_nameMap a trajectory tool-call name to the server’s bare tool name.

Data

FenceRecordT

MCP_TOKEN_EPISODE_CLAIM

NEMO_GYM_MCP_METADATA_KEY

NEMO_GYM_MCP_SESSION_TOKEN_HEADER

RESERVED_MCP_TOOL_NAMES

RESOURCES_SESSIONS

VerificationInputT

_MCP_TOKEN_SALT

_SESSION_HOOKS

logger

API

class nemo_gym.server_runtime.base_resources_server.BaseMultiRewardVerifyResponse()

Bases: BaseVerifyResponse

Base verify response for environments with multiple reward objectives.

Subclass this response instead of declaring reward_components on an environment-specific BaseVerifyResponse subclass. The mapping is required, and its objective keys should remain consistent across every task in the environment.

Set the inherited reward to the scalar aggregate expected by single-reward consumers. To include individual objectives in aggregate metrics, also expose them as top-level numeric fields because metrics do not descend into this mapping. See resources_servers/example_tool_call_multireward for a complete example.

reward_components
dict[str, float]
class nemo_gym.server_runtime.base_resources_server.BaseResourcesServer()

Bases: BaseServer

config
BaseResourcesServerConfig
class nemo_gym.server_runtime.base_resources_server.BaseResourcesServerConfig()

Bases: BaseRunServerInstanceConfig

REVERIFY_MODE
ReverifyMode = ReverifyMode.UNKNOWN
expose_tools_over_mcp
bool = False
session_close_retry_window_seconds
float
class nemo_gym.server_runtime.base_resources_server.BaseRunRequest()

Bases: BaseModel

capture_rollout_id
Optional[str]
model_config
= ConfigDict(populate_by_name=True)
responses_create_params
NeMoGymResponseCreateParamsNonStreaming
class nemo_gym.server_runtime.base_resources_server.BaseSeedSessionRequest()

Bases: BaseModel

class nemo_gym.server_runtime.base_resources_server.BaseSeedSessionResponse()

Bases: BaseModel

class nemo_gym.server_runtime.base_resources_server.BaseVerifyRequest()

Bases: BaseRunRequest

response
NeMoGymResponse
class nemo_gym.server_runtime.base_resources_server.BaseVerifyResponse()

Bases: BaseVerifyRequest

failure_kind
Optional[str]
failure_outcome_action
OutcomeAction | None
failure_outcome_attribution
FailureClass | None
failure_reason
Optional[str] = None
mask_sample
bool
ng_verifier_invocations
list[VerifierInvocation]
reward
float
nemo_gym.server_runtime.base_resources_server.BaseVerifyResponse._warn_on_unregistered_failure_kind(
value: typing.Optional[str]
) -> typing.Optional[str]
classmethod

Producers learn about a name outside the vocabulary without losing the failure.

Validation warns rather than rejects: an unregistered kind is a migration signal, and dropping the response over it would replace a visible wrong label with an invisible lost failure.

class nemo_gym.server_runtime.base_resources_server.LostSessionVerification

Bases: typing.TypedDict

The verify response fields of a masked sample for a session the server no longer holds.

failure_kind
str
failure_reason
str
mask_sample
bool
reward
float
class nemo_gym.server_runtime.base_resources_server.MCPServerMetadata()

Bases: BaseModel

Metadata returned from /seed_session for per-rollout Gym MCP access.

headers
dict[str, str]
server_name
str
transport
str = 'http'
url_path
str = '/mcp'
class nemo_gym.server_runtime.base_resources_server.ResourcesCloseSessionRequest()

Bases: BaseModel

Close resources-server state.

episode_id
EpisodeId
model_config
= ConfigDict(extra='forbid')
resources_session_id
str
class nemo_gym.server_runtime.base_resources_server.ResourcesCloseSessionResponse()

Bases: BaseModel

Confirm resources-server state was closed.

model_config
= ConfigDict(extra='forbid')
resources_session_id
str
class nemo_gym.server_runtime.base_resources_server.ResourcesSeedRejectedError(
detail: str
)
Exception

Bases: HTTPException, ValueError

Refuse a seed whose input no retry can fix, such as task_data that fails validation.

FastAPI answers it with HTTP 422, which an Environment Server treats as terminal, so the episode fails once with this message instead of being dispatched again with the same input. It is also a ValueError, so code that seeds outside a request (a direct call in a test) keeps catching it as before.

class nemo_gym.server_runtime.base_resources_server.ResourcesSeedSessionRequest()

Bases: BaseModel

Idempotently initialize resources state under a caller-assigned identifier.

Repeating the same identifier and episode must return the existing session. Closing an unknown identifier must prevent a racing seed from creating it for session_close_retry_window_seconds (see ResourcesSessionFences); callers must use unique IDs and finish seed retries within that window. External resources should use provider TTLs when available. Current process-local session records may remain until the server shuts down.

episode_id
EpisodeId
model_config
= ConfigDict(extra='forbid')
resources_session_id
str = Field(min_length=1)
task_data
dict[str, JsonValue]
task_id
TaskId
class nemo_gym.server_runtime.base_resources_server.ResourcesSeedSessionResponse()

Bases: BaseModel

Confirm resources state and return optional agent access and an initial observation.

model_config
= ConfigDict(extra='forbid')
observation
str | None = None
resources_session_id
str
resources_tools
MCPServerMetadata | None = None
sandbox_access
SandboxAccess | None = None
nemo_gym.server_runtime.base_resources_server.ResourcesSeedSessionResponse._serialize_observation(
handler: pydantic.SerializerFunctionWrapHandler
)

Preserve the seed wire shape when the server supplies no observation field.

class nemo_gym.server_runtime.base_resources_server.ResourcesSessionConflictError(
detail: str
)
Exception

Bases: HTTPException, ValueError

Refuse a seed or close that conflicts with what this server recorded for the session.

Examples: a resources_session_id already bound to another episode or task, a close that names another episode, or a seed for a session that already closed. FastAPI answers it with HTTP 409, which an Environment Server treats as terminal. It is also a ValueError for direct callers.

class nemo_gym.server_runtime.base_resources_server.ResourcesSessionFences(
close_retry_window_seconds: float
)

Bases: Generic[FenceRecordT]

The fences of the resources sessions this worker closed, each lasting the close retry window.

A fence is the record a close leaves so that a seed arriving after the close (a retry, or a reply that raced the close) is refused instead of creating the session again. Each fence holds a record of the server’s choice, such as the episode that first closed the session, which a retried close can check. A fence expires close_retry_window_seconds after the first close; a retried close does not extend it. Construct it with the server’s session_close_retry_window_seconds.

With several workers, the worker’s ownership of the ID expires that long after the last request for the session, and the fence is set before the close’s reply is sent. So ownership never expires before the fence (see nemo_gym.server_runtime.request_handling.session_routing.ResourcesSessionLifetimes). While ownership lasts, a late seed reaches the worker that holds the fence and is refused. After it expires, every worker treats the ID as new, so the answer does not depend on which worker gets it.

_fences
OrderedDict[str, tuple[float, FenceRecordT]] = OrderedDict()
nemo_gym.server_runtime.base_resources_server.ResourcesSessionFences.__contains__(
resources_session_id: object
) -> bool
nemo_gym.server_runtime.base_resources_server.ResourcesSessionFences.__len__() -> int
nemo_gym.server_runtime.base_resources_server.ResourcesSessionFences._expire() -> None
nemo_gym.server_runtime.base_resources_server.ResourcesSessionFences.clear() -> None

Delete every fence, for a server that drops all of its sessions at shutdown.

nemo_gym.server_runtime.base_resources_server.ResourcesSessionFences.fence(
resources_session_id: str,
) -> None

Fence a closed session; call it only after its state was released.

A retried close keeps the first close’s record and expiry.

nemo_gym.server_runtime.base_resources_server.ResourcesSessionFences.get(
resources_session_id: str

Return the record of the session’s fence, or None if it is not fenced or its fence expired.

A fence may hold a None record, so test resources_session_id in fences to ask whether a session is fenced.

nemo_gym.server_runtime.base_resources_server.ResourcesSessionFences.raise_if_fenced(
resources_session_id: str
) -> None

Refuse a seed for a fenced session with 409, which the environment server treats as terminal.

class nemo_gym.server_runtime.base_resources_server.ResourcesSessionLocks()

Lock each resources session so that its seed and close never interleave.

A session’s lock exists only while a request holds or waits for it.

_locks
dict[str, _SessionLock] = {}
nemo_gym.server_runtime.base_resources_server.ResourcesSessionLocks.locked(
resources_session_id: str
) -> collections.abc.AsyncIterator[None]
async

Hold the lock for resources_session_id.

class nemo_gym.server_runtime.base_resources_server.ResourcesSessionLostError(
message: str,
failure_kind: str = SESSION_LOST
)
Exception

Bases: HTTPException

Answer a tool call for a session this server no longer holds with HTTP 404.

Raise this instead of returning an ordinary tool observation that says the session is gone. What a harness does with the 404 varies: simple_agent returns its body to the model as the tool output and keeps going, Stirrup fails the run so the episode is dispatched again, and an MCP client reports a tool error. In every case the trajectory records a failed tool call whose detail names the failure_kind, which a log can group by. The verify for the session answers with lost_session_verification, whose masked sample keeps the episode out of the score.

class nemo_gym.server_runtime.base_resources_server.ResourcesVerifyRequest()

Bases: BaseModel, Generic[VerificationInputT]

Carry typed environment output to a resources server.

episode_id
EpisodeId
model_config
= ConfigDict(extra='forbid')
task_id
TaskId
verification_input
VerificationInputT
class nemo_gym.server_runtime.base_resources_server.ReverifyMode

Bases: enum.Enum

STATELESS
= 'stateless'
UNKNOWN
= 'unknown'
UNSUPPORTED
= 'unsupported'
class nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer()

Bases: BaseResourcesServer, AggregateMetricsMixin, SimpleServer

_checkpoint
Optional[ResourcesParticipant] = PrivateAttr(default=None)
_mcp_token_serializer
Any = PrivateAttr(default=None)
checkpoint_mode
CheckpointMode = 'restart_only'
checkpoint_participant
Optional[ResourcesParticipant]

The participant that takes part in partial-rollout checkpoints, or None with checkpointing off.

checkpoint_session_starts
tuple[str, ...] = ('/seed_session',)
checkpoint_verify
StepType = 'wait'
config
BaseResourcesServerConfig
grading_may_use_uninstrumented_model
bool = False
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer._mcp_token_claims(
scope: dict[str, typing.Any]
) -> typing.Optional[nemo_gym.server_runtime._checkpoint.resources.MCPTokenClaims]

The session and attempt an MCP request’s signed token names.

None without a token or for one that does not verify; the MCP endpoint rejects a bad token. An attempt claim that does not decode counts as missing.

nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.aggregate_metrics(
async

Compute aggregate metrics from verify responses.

RewardProfiler provides baseline stats. Override compute_metrics(), compute_repeat_metrics(), and/or get_key_metrics() for benchmark-specific customization.

nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.checkpoint_session_ended(
request: fastapi.Request
) -> None

Report that the request’s session ended; standard /close_session and /verify do this.

Call it from a protocol route that ends a session under another name, such as a terminal step.

nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.close_resources_session(
async

Close state an Environment Server seeded; the default keeps none.

Served at /close_session. Servers that keep per-rollout state override this method, and may accept other close bodies by overriding it with a different signature. The name differs from the close_session helpers some servers already define for their own state.

An override must:

  • succeed when the session is already closed or was never seeded;
  • keep a seed that arrives after the close from creating the session again, for a bounded time (ResourcesSessionFences does this for session_close_retry_window_seconds);
  • release the session’s state by resources_session_id even when the request carries no session cookie;
  • raise when releasing fails rather than recording the session as closed, so the caller can retry.

A served resources server routes sessions (see nemo_gym.server_runtime.request_handling.session_routing): an episode seed’s session cookie carries resources_session_id as its SESSION_ID_KEY, and an episode close that carries no cookie, as when the seed’s reply was lost, is given that ID too, so a server may key state by either; they name the same session. An app built without session routing, such as one under TestClient, gives a new session a random ID instead, so release by body.resources_session_id to be correct either way.

nemo_gym.server_runtime.testing.session_conformance.check_resources_session_contract checks these rules from a server’s tests.

TODO: also expire sessions that are never closed, as when the environment server dies before its close. Planned rule: a fixed lifetime from the seed (the sandbox ttl_s when set, else 24 hours), checked on the next request and at shutdown; an expired session is released, fenced, and verifies as SESSION_EXPIRED.

nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.create_resources_app() -> fastapi.FastAPI

Build the app with what every resources server shares, whatever its episode protocol.

That is the session and rollout middleware, the checkpoint participant, /aggregate_metrics and /reverify_mode. A server with another protocol, such as Gymnasium’s /reset and /step, overrides register_protocol_routes instead of building its own app.

nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.export_session_states(
session_ids: list[str]
) -> dict[str, pydantic.JsonValue]
async

Return each session’s state for a checkpoint, leaving out sessions this server no longer holds.

nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.get_reverify_mode() -> nemo_gym.server_runtime.base_resources_server.ReverifyMode
async
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.mcp_allowed_tools_for_session(
seed_body: dict[str, typing.Any]
) -> typing.Optional[list[str]]

Per-session tool restriction: return the tool names allowed for this rollout’s MCP token, or None (the default) for unrestricted. seed_body is the JSON body POSTed to /seed_session, or its task_data when an Environment Server seeds the session.

nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.mcp_tools(
catchall: typing.Optional[typing.Any]

Return the MCP tools to expose (default: the auto-harvested typed POST routes).

Override to exclude (filter harvested), add catch-all-backed tools (harvested + [catchall.tool(…)]), or disable (return None). ‘catchall’ is None unless the server has one parameterized catch-all route.

nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.normalize_tool_name(
name: str
) -> str

Strip this server’s MCP namespace from a trajectory tool-call name (see module function).

nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.owned_session_routing() -> dict[str, typing.Any]
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.register_protocol_routes(
app: fastapi.FastAPI
) -> None

Register the episode routes /seed_session, /close_session and /verify.

nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.restore_session_states(
states: dict[str, pydantic.JsonValue]
) -> None
async

Validate every state, then install all of them; never install a partial set.

nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.retire_session_state(
session_id: str
) -> None
async

Discard a session whose attempt was retired; called in every checkpoint mode.

The default does nothing. A server that keeps per-session state releases it here, since a retired session’s late requests, a terminal step among them, are refused.

async

Seed per-rollout state; the default keeps none.

An Environment Server seeds with a ResourcesSeedSessionRequest and gets its caller-assigned resources_session_id back. An Agent’s /run seeds with its legacy body and gets an empty response. Servers that keep per-rollout state override this method.

An override refuses a seed that no retry can make succeed with a terminal 4xx, never a plain exception (HTTP 500), which the Environment Server retries by dispatching the whole episode again:

  • ResourcesSeedRejectedError (422) for input that fails validation, such as task_data or a TaskId that does not match the task row;
  • ResourcesSessionConflictError (409) for an ID bound to another episode or task, or a closed session.

A failure that a retry may fix, such as an unavailable sandbox provider, stays a 5xx.

nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.setup_checkpoint_participant(
app: fastapi.FastAPI
) -> None
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.setup_resources_checkpoint(
app: fastapi.FastAPI
) -> None
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.setup_webserver() -> fastapi.FastAPI
nemo_gym.server_runtime.base_resources_server.SimpleResourcesServer.verify(
asyncabstract

Score the episode.

A server whose score depends on per-rollout state answers a verify for a session it no longer holds (never seeded, closed, or lost) with a masked sample built by lost_session_verification, never a plain reward of 0 and never an exception that dispatches the episode again. A tool call for such a session raises ResourcesSessionLostError (404).

class nemo_gym.server_runtime.base_resources_server._SessionLock(
lock: asyncio.Lock = asyncio.Lock(),
users: int = 0
)
Dataclass
lock
Lock = field(default_factory=(asyncio.Lock))
users
int = 0
nemo_gym.server_runtime.base_resources_server.lost_session_verification(
reason: str,
failure_kind: str = SESSION_LOST

Return the verify response fields for an episode whose session this server no longer holds.

The sample is masked: the environment lost the state it would measure, so a reward of 0 would read as a policy that solved nothing. Merge the result over the request’s fields so these win, for example BaseVerifyResponse(**(body.model_dump() | lost_session_verification("sandbox gone"))); spreading both as keyword arguments raises TypeError when a request model that allows extra fields already carries a reward. Use failure_kind=SESSION_EXPIRED when the server closed the session itself because its lifetime ran out.

Each call logs a warning, because a masked sample is otherwise visible only in coverage metrics: many of them point at a wiring fault such as a session cookie that never reaches /verify.

nemo_gym.server_runtime.base_resources_server.mcp_session_token_payload(
serializer: itsdangerous.URLSafeSerializer,
token: str
) -> typing.Any

The payload of a Gym MCP session token whose signature verifies, or None if it does not.

nemo_gym.server_runtime.base_resources_server.mcp_session_token_serializer(
secret: str
) -> itsdangerous.URLSafeSerializer

The signer of Gym MCP session tokens; build it once per server.

nemo_gym.server_runtime.base_resources_server.normalize_tool_name(
name: str,
server_name: typing.Optional[str] = None
) -> str

Map a trajectory tool-call name to the server’s bare tool name.

HTTP-driven agents record bare tool names (“email_reply_email”); MCP-native agents (e.g. Claude Code) record them namespaced per server (“mcp__workplace_assistant__email_reply_email”). Verifiers compare trajectory names against dataset/ground-truth vocabulary, so names are normalized before verify sees them and rollouts score identically on both transports. Non-namespaced names pass through unchanged. When server_name is given, only that server’s prefix is stripped (robust to tool names that themselves contain double underscores). This runs only for servers exposed over MCP and mirrors how MCP clients namespace tool names, so a real tool that is itself named mcp__<server>__x being stripped is accepted.

nemo_gym.server_runtime.base_resources_server.FenceRecordT = TypeVar('FenceRecordT')
nemo_gym.server_runtime.base_resources_server.MCP_TOKEN_EPISODE_CLAIM = 'episode'
nemo_gym.server_runtime.base_resources_server.NEMO_GYM_MCP_METADATA_KEY = 'mcp'
nemo_gym.server_runtime.base_resources_server.NEMO_GYM_MCP_SESSION_TOKEN_HEADER = 'X-NeMo-Gym-Session-Token'
nemo_gym.server_runtime.base_resources_server.RESERVED_MCP_TOOL_NAMES = frozenset({'verify', 'seed_session', 'close_session', 'aggregate_metrics', 'mcp'...
nemo_gym.server_runtime.base_resources_server.RESOURCES_SESSIONS = SessionKind(cookie_key=SESSION_ID_KEY, id_field='resources_session_id', seed_pat...
nemo_gym.server_runtime.base_resources_server.VerificationInputT = TypeVar('VerificationInputT')
nemo_gym.server_runtime.base_resources_server._MCP_TOKEN_SALT = 'nemo-gym-mcp-session-token'
nemo_gym.server_runtime.base_resources_server._SESSION_HOOKS = ('export_session_states', 'restore_session_states', 'retire_session_state')
nemo_gym.server_runtime.base_resources_server.logger = logging.getLogger(__name__)