Skip to navigation

nemo_gym.server_runtime.base_environment_server

View as Markdown

Shared lifecycle for environment servers.

Module Contents

Classes

NameDescription
BaseEnvironmentServerExpose a typed episode protocol with shared limits and cleanup.
BaseEnvironmentServerConfigConfigure protocol-neutral episode limits.
CleanupContextHold bounded process-local cleanup callbacks for one episode.
CleanupHandleClose one registered participant at a protocol boundary.
_CleanupEntry-

Functions

NameDescription
dependency_failureTurn a failed call to another server into the episode’s failure.
handled_episode_failureThe environment’s handled episode failure: one NgFailureEvent with the local policy kept apart.
is_handled_episode_failureWhether error is an environment failure built by handled_episode_failure.
is_retryable_dependency_errorReturn whether a failed call to another server is worth another episode attempt.
is_retryable_dependency_statusReturn whether an HTTP error status from another server is worth another episode attempt.

Data

CleanupCallback

EpisodeRequestT

EpisodeResponseT

LOGGER

_ENVIRONMENT_CHECKPOINT_MODES

_RETRYABLE_CLIENT_ERROR_STATUSES

API

class nemo_gym.server_runtime.base_environment_server.BaseEnvironmentServer()

Bases: SimpleServer, Generic[EpisodeRequestT, EpisodeResponseT]

Expose a typed episode protocol with shared limits and cleanup.

_admission
Semaphore | None = None
_checkpoint
EnvironmentParticipant | None = None
checkpoint_mode
CheckpointMode = 'restart_only'
config
BaseEnvironmentServerConfig
request_model
type[EpisodeRequestT]
response_model
type[EpisodeResponseT]
nemo_gym.server_runtime.base_environment_server.BaseEnvironmentServer._unhandled_failure_response(
error: Exception
nemo_gym.server_runtime.base_environment_server.BaseEnvironmentServer.aggregate_metrics(
asyncabstract

Aggregate per-rollout scores into task-level metrics.

nemo_gym.server_runtime.base_environment_server.BaseEnvironmentServer.checkpoint_boundary(
state: dict[str, pydantic.JsonValue] | collections.abc.Callable[[], dict[str, pydantic.JsonValue]]
) -> None
async

Record that a protocol step completed; state names the next step and what it needs.

While a checkpoint is open the episode parks here until resume. A replacement attempt that continues a checkpoint receives the latest recorded state from checkpoint_continuation. Pass a function when building state costs something: it is called only with checkpointing on.

nemo_gym.server_runtime.base_environment_server.BaseEnvironmentServer.checkpoint_continuation(
) -> dict[str, pydantic.JsonValue] | None

Return the protocol state this attempt continues from a checkpoint, once, or None.

nemo_gym.server_runtime.base_environment_server.BaseEnvironmentServer.checkpoint_restart(
) -> None
async

Mark this episode as a restart: a server it uses cannot capture its part, such as a restart-only agent.

Call it when a seed reply says so (nemo_gym.server_runtime._checkpoint.steps.seed_restarts). The episode then never holds up a checkpoint and is never exported; after a crash, the controller starts it over from its input.

nemo_gym.server_runtime.base_environment_server.BaseEnvironmentServer.checkpoint_step(
step_type: nemo_gym.server_runtime._checkpoint.steps.StepType
) -> contextlib.AbstractAsyncContextManager[None]

Run one protocol step as a wait or replay step (see nemo_gym.server_runtime._checkpoint.steps).

Use replay only for a step that is safe to run again after a crash and does not change checkpointed state; everything else waits.

nemo_gym.server_runtime.base_environment_server.BaseEnvironmentServer.episode_timeout_seconds(
) -> float | None
async

The wall-clock bound of this episode: default_episode_timeout_seconds (None is no bound).

A protocol whose tasks carry their own budgets may derive a longer one per episode (the Harbor server does for a multi-step task whose steps add up to more than the default); it must never raise here, as nothing of the episode has started yet.

Adapt runtime event, local policy and evidence to the protocol’s wire failure model.

The failure’s FailureReport goes in the reply’s failure_report, beside the failure.

nemo_gym.server_runtime.base_environment_server.BaseEnvironmentServer.model_post_init(
context: typing.Any
) -> None
asyncabstract

Run one concrete environment protocol.

nemo_gym.server_runtime.base_environment_server.BaseEnvironmentServer.run_request(
async
nemo_gym.server_runtime.base_environment_server.BaseEnvironmentServer.setup_environment_checkpoint(
app: fastapi.FastAPI
) -> None

Take part in partial-rollout checkpoints when they are enabled.

Every episode is tracked. A protocol that records boundaries (checkpoint_mode = "exported") continues after a restore; the episodes of a protocol that records none are restarts, which never hold up a checkpoint and start over from their input after a crash. Call this from any setup_webserver that builds its own app.

nemo_gym.server_runtime.base_environment_server.BaseEnvironmentServer.setup_webserver() -> fastapi.FastAPI
nemo_gym.server_runtime.base_environment_server.BaseEnvironmentServer.validate_response_identity(
) -> None
staticmethod
class nemo_gym.server_runtime.base_environment_server.BaseEnvironmentServerConfig()

Bases: BaseRunServerInstanceConfig

Configure protocol-neutral episode limits.

cleanup_timeout_seconds
PositiveFloat
default_episode_timeout_seconds
PositiveFloat | None = None
max_concurrent_episodes
PositiveInt | None = None
model_config
= ConfigDict(extra='forbid')
queue_timeout_seconds
PositiveFloat | None = None
nemo_gym.server_runtime.base_environment_server.BaseEnvironmentServerConfig.validate_queue_timeout() -> typing_extensions.Self
class nemo_gym.server_runtime.base_environment_server.CleanupContext(
cleanup_timeout_seconds: float,
failure_report: nemo_gym.core.failure_records.FailureReport = FailureReport()
)
Dataclass

Hold bounded process-local cleanup callbacks for one episode.

Callbacks:

  • are process-local Python objects, although they may issue remote close requests;
  • run sequentially in LIFO order, each with its own cleanup timeout;
  • must be idempotent because a timed-out remote request may have succeeded;
  • are lost on process or host failure, so remote owners need expiry or reaping.

Callback failures or timeouts do not prevent later owners from tearing down their resources. Total cleanup is bounded by the number of registered callbacks times the per-callback timeout. Cleanup has no durable retry after this context is discarded.

_cleanups
list[_CleanupEntry] = field(default_factory=list)
cleanup_timeout_seconds
float
episode_id
EpisodeId
failure_report
FailureReport = field(default_factory=FailureReport)
nemo_gym.server_runtime.base_environment_server.CleanupContext._observe_failure(
error: Exception
) -> None
nemo_gym.server_runtime.base_environment_server.CleanupContext.aclose() -> None
async
nemo_gym.server_runtime.base_environment_server.CleanupContext.register_cleanup(
name: str,
class nemo_gym.server_runtime.base_environment_server.CleanupHandle(
timeout_seconds: float
)
Dataclass

Close one registered participant at a protocol boundary.

entry
_CleanupEntry
timeout_seconds
float
nemo_gym.server_runtime.base_environment_server.CleanupHandle.close() -> None
async

Run this idempotent callback once.

The episode timeout bounds calls made during the protocol. The context’s cleanup timeout bounds callbacks left for final unwinding.

class nemo_gym.server_runtime.base_environment_server._CleanupEntry(
name: str,
active: bool = True,
lock: asyncio.Lock = asyncio.Lock()
)
Dataclass
active
bool = True
callback
CleanupCallback
lock
Lock = field(default_factory=(asyncio.Lock))
name
str
nemo_gym.server_runtime.base_environment_server._CleanupEntry.close() -> None
async
nemo_gym.server_runtime.base_environment_server.dependency_failure(
error: BaseException,
terminal: bool | None = None,
name_error_type: bool = False,
fields: typing.Any = {}

Turn a failed call to another server into the episode’s failure.

failure_reason keeps the dependency’s own explanation from its response body (see format_exception_with_response_body), prefixed with the error’s class name when name_error_type is set, and bounded to the failure’s 2000 characters. terminal defaults to not is_retryable_dependency_error(error); pass it only where the stage decides on its own. fields retain partial evidence for the response adapter, such as a partial response.

nemo_gym.server_runtime.base_environment_server.handled_episode_failure(
terminal: bool,
evidence: dict[str, object] | None = None,
failure_kind: str | None = None,
event_fields: typing.Any = {}

The environment’s handled episode failure: one NgFailureEvent with the local policy kept apart.

terminal and evidence are runtime outcome data for the response adapter, never event fields; the adapter builds the protocol’s EpisodeFailure after cleanup. failure_kind is the outcome’s kind and may stay unset; the event then records the registered episode_failed.

nemo_gym.server_runtime.base_environment_server.is_handled_episode_failure(
error: BaseException
) -> bool

Whether error is an environment failure built by handled_episode_failure.

nemo_gym.server_runtime.base_environment_server.is_retryable_dependency_error(
error: BaseException
) -> bool

Return whether a failed call to another server is worth another episode attempt.

An HTTP error is classified by its status (see is_retryable_dependency_status). A refused or dropped connection and a timeout are transient. Any other error, such as a reply that fails validation, would fail the same way again.

nemo_gym.server_runtime.base_environment_server.is_retryable_dependency_status(
status: int
) -> bool

Return whether an HTTP error status from another server is worth another episode attempt.

408, 425, 429 and every 5xx are retried; every other 4xx is terminal.

nemo_gym.server_runtime.base_environment_server.CleanupCallback = Callable[[], Awaitable[None]]
nemo_gym.server_runtime.base_environment_server.EpisodeRequestT = TypeVar('EpisodeRequestT', bound=(BaseEpisodeRequest[Any]))
nemo_gym.server_runtime.base_environment_server.EpisodeResponseT = TypeVar('EpisodeResponseT', bound=(BaseEpisodeResponse[Any]))
nemo_gym.server_runtime.base_environment_server.LOGGER = logging.getLogger(__name__)
nemo_gym.server_runtime.base_environment_server._ENVIRONMENT_CHECKPOINT_MODES = ('restart_only', 'exported')
nemo_gym.server_runtime.base_environment_server._RETRYABLE_CLIENT_ERROR_STATUSES = frozenset({408, 425, 429})