Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
3273e78
feat: add standalone Gym Docker container
kajalj22 Jul 28, 2026
1025125
fix: remove pull_request trigger from build-container workflow
kajalj22 Jul 28, 2026
82d97df
fix: align Dockerfile with RL repo patterns and address review comments
kajalj22 Jul 28, 2026
44bb797
fix: remove user-controlled VLLM_VERSION build arg
kajalj22 Jul 28, 2026
3ef2546
feat: add vllm optional extra and install it in the container
kajalj22 Jul 28, 2026
18688bb
fix: remove dead git fetch --unshallow and trailing WORKDIR
kajalj22 Jul 28, 2026
82e559a
fix: pass build-contexts to inject local source into Docker build
kajalj22 Jul 28, 2026
9002bcb
feat: sync vllm versions, add flashinfer pin, add prefetch build arg
kajalj22 Jul 28, 2026
04cc506
fix: use uv pip install -e . in prefetch loop instead of uv sync
kajalj22 Jul 28, 2026
85ec2dc
feat: add smoke test job to build-container workflow
kajalj22 Jul 28, 2026
e871791
fix: drop redundant 'gym' in smoke test docker run
kajalj22 Jul 28, 2026
173cccb
feat: pin flashinfer-python==0.6.14 in local_vllm_model and genrm_model
kajalj22 Jul 28, 2026
5afbaef
refactor: call FW-CI-templates directly, bump to v0.78.0
kajalj22 Jul 28, 2026
6c760ab
fix: add id-token: write permission to test job for Azure OIDC login
kajalj22 Jul 28, 2026
d2e396c
fix: pin runner to linux-amd64-cpu16 for v0.78.0 compatibility
kajalj22 Jul 29, 2026
c32e401
fix: add environment: nemo-ci to test job for Azure OIDC
kajalj22 Jul 29, 2026
c25e520
fix: remove hardcoded version assertions from smoke test
kajalj22 Jul 29, 2026
a975601
fix: use --entrypoint python in smoke test docker run
kajalj22 Jul 29, 2026
8d319ef
fix: revert vllm to 0.20.0, update flashinfer to match
kajalj22 Jul 29, 2026
18838ee
refactor: simplify build-container to build-only, no push
kajalj22 Jul 30, 2026
8366c38
feat: add pull_request trigger on ubuntu-latest for PR build checks
kajalj22 Jul 30, 2026
2524a0b
Merge branch 'main' into add-docker-container
kajalj22 Jul 30, 2026
6746746
Merge branch 'main' into add-docker-container
kajalj22 Jul 30, 2026
ee67933
remove: drop NEMO_GYM_PREFETCH_SERVERS from Dockerfile
kajalj22 Jul 31, 2026
8405b97
Merge branch 'add-docker-container' of https://github.com/NVIDIA-NeMo…
kajalj22 Jul 31, 2026
7c8b07d
restore: re-add NEMO_GYM_PREFETCH_SERVERS to Dockerfile
kajalj22 Aug 3, 2026
37c3aaf
Merge branch 'main' into add-docker-container
kajalj22 Aug 4, 2026
5c4b5bf
feat: add gym env prefetch for baking server venvs into the container…
kajalj22 Aug 4, 2026
ab2737b
Merge branch 'main' into add-docker-container
kajalj22 Aug 4, 2026
abf739f
Merge branch 'main' into add-docker-container
kajalj22 Aug 4, 2026
3e5d035
Merge branch 'main' into add-docker-container
kajalj22 Aug 4, 2026
7de5ee5
Merge branch 'main' into add-docker-container
kajalj22 Aug 4, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 47 additions & 0 deletions .github/workflows/build-container.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# SPDX-FileCopyrightText: Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
name: Build container
on:
pull_request:
paths:
- docker/Dockerfile
- pyproject.toml
- uv.lock
push:
branches: [main]
paths:
- docker/Dockerfile
- pyproject.toml
- uv.lock
workflow_dispatch:

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3

- name: Build
uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5
with:
context: .
file: docker/Dockerfile
build-contexts: nemo-gym=.
target: release
push: false
106 changes: 106 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# syntax=docker/dockerfile:1
# Usage:
# Self-contained build (default: builds from main):
# docker buildx build -f docker/Dockerfile --tag <registry>/nemo-gym:latest --push .
#
# Self-contained build (specific git ref):
# docker buildx build -f docker/Dockerfile --build-arg GYM_GIT_REF=v0.4.0 \
# --tag <registry>/nemo-gym:v0.4.0 --push .
#
# Local source override:
# docker buildx build --build-context nemo-gym=. -f docker/Dockerfile \
# --tag <registry>/nemo-gym:latest --push .
#

ARG BASE_IMAGE=nvcr.io/nvidia/cuda-dl-base:26.03-cuda13.2-devel-ubuntu24.04
Comment thread
ananthsub marked this conversation as resolved.
Resolved

# Source stage: clones Gym from GitHub. Override with --build-context nemo-gym=.
# to inject a local checkout instead.
FROM scratch AS nemo-gym
ARG GYM_GIT_REF=main
ADD --keep-git-dir=true https://github.com/NVIDIA-NeMo/Gym.git#${GYM_GIT_REF} /

FROM ${BASE_IMAGE} AS base
ENV NEMO_GYM_CONTAINER=1
USER root

RUN <<"EOF" bash -exu -o pipefail
export DEBIAN_FRONTEND=noninteractive
export TZ=America/Los_Angeles

apt-get update
apt-get install -y --no-install-recommends \
jq \
curl \
git \
rsync \
wget \
less \
vim

apt-get clean
rm -rf /var/lib/apt/lists/*
EOF

ARG UV_VERSION=0.11.6
ARG PYTHON_VERSION=3.12.11
ENV PATH="/root/.local/bin:$PATH"
RUN curl -LsSf https://astral.sh/uv/${UV_VERSION}/install.sh | sh && \
uv python install ${PYTHON_VERSION}

ENV RAY_USAGE_STATS_ENABLED=0


FROM base AS hermetic

WORKDIR /opt/nemo-gym

ARG BASE_IMAGE
ARG UV_VERSION

ENV UV_PROJECT_ENVIRONMENT=/opt/nemo_gym_venv
ENV UV_LINK_MODE=copy

# Copy only dependency metadata first for layer caching.
COPY --from=nemo-gym pyproject.toml uv.lock ./
COPY --from=nemo-gym nemo_gym/__init__.py nemo_gym/package_info.py ./nemo_gym/

RUN uv venv --seed && \
Comment thread
kajalj22 marked this conversation as resolved.
Resolved
uv sync --link-mode symlink --locked --extra vllm --no-install-project

ENV PATH="/opt/nemo_gym_venv/bin:$PATH"


FROM hermetic AS release

ARG NEMO_GYM_PREFETCH_CONFIGS=""
ARG NEMO_GYM_COMMIT
ARG NVIDIA_BUILD_ID
ARG NVIDIA_BUILD_REF
ARG RC_DATE=00.00
ARG TARGETARCH
ENV NEMO_GYM_COMMIT=${NEMO_GYM_COMMIT:-<unknown>}
ENV NVIDIA_BUILD_ID=${NVIDIA_BUILD_ID:-<unknown>}
ENV NVIDIA_BUILD_REF=${NVIDIA_BUILD_REF:-<unknown>}
LABEL com.nvidia.build.id="${NVIDIA_BUILD_ID}"
LABEL com.nvidia.build.ref="${NVIDIA_BUILD_REF}"

# Copy full source. Exclude pyproject.toml and uv.lock since they are
# already present from the hermetic stage.
COPY --from=nemo-gym --exclude=pyproject.toml --exclude=uv.lock . /opt/nemo-gym

# Install the nemo-gym project itself along with the vllm extra.
RUN UV_LINK_MODE=symlink uv sync --locked --extra vllm
Comment thread
ananthsub marked this conversation as resolved.
Resolved

# Optional: pre-warm per-server venvs at build time so they are ready at runtime.
# Pass --build-arg NEMO_GYM_PREFETCH_CONFIGS="path/to/config1.yaml,path/to/config2.yaml"
# Uses 'gym env prefetch' which accepts the same config format as 'gym env start',
# installs each server's venv serially, and exits without starting any server process.
RUN <<"EOF" bash -exu
if [[ -n "${NEMO_GYM_PREFETCH_CONFIGS:-}" ]]; then
gym env prefetch "+config_paths=[${NEMO_GYM_PREFETCH_CONFIGS}]"
fi
EOF

ENTRYPOINT ["gym"]
CMD ["--help"]
68 changes: 68 additions & 0 deletions nemo_gym/cli/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,74 @@ def check_http_server_statuses(self, successful_servers: List[str]) -> List[Tupl
return statuses


@exit_cleanly_on_config_error
def prefetch(
global_config_dict_parser_config: Optional[GlobalConfigDictParserConfig] = None,
): # pragma: no cover
"""
Pre-warm per-server venvs without starting any servers.

Accepts the same config format as 'gym env start'. For each server in the config,
creates its isolated venv and installs dependencies serially — similar to how
RL's prefetch_venvs.py installs actor venvs at container build time.

No Ray is initialised and no server processes are started. Intended for use in
Dockerfile builds so venvs are ready at runtime with no network access needed.

Configuration Parameter:
config_paths (List[str]): Paths to YAML configuration files. Specify via Hydra: `+config_paths="[file1.yaml,file2.yaml]"`

Examples:

```bash
# Pre-warm venvs for a specific config
config_paths="responses_api_models/local_vllm_model/configs/local_vllm_model.yaml,\\
resources_servers/math/configs/math.yaml"
gym env prefetch "+config_paths=[${config_paths}]"
```
"""
global_config_dict = get_global_config_dict(global_config_dict_parser_config=global_config_dict_parser_config)
GlobalConfigDictParser().raise_on_no_server_instances(global_config_dict)

top_level_paths = [k for k in global_config_dict.keys() if k not in NEMO_GYM_RESERVED_TOP_LEVEL_KEYS]

seen_dirs: set = set()
for top_level_path in top_level_paths:
server_config_dict = global_config_dict[top_level_path]
if not isinstance(server_config_dict, DictConfig):
continue

first_key = list(server_config_dict)[0]
server_config_dict = server_config_dict[first_key]
if not isinstance(server_config_dict, DictConfig):
continue
second_key = list(server_config_dict)[0]
server_config_dict = server_config_dict[second_key]
if not isinstance(server_config_dict, DictConfig):
continue

if "entrypoint" not in server_config_dict:
continue

dir_path = _resolve_server_dir(Path(first_key, second_key))
if dir_path in seen_dirs:
print(f"Skipping duplicate: {dir_path}")
continue
seen_dirs.add(dir_path)
print(f"Pre-warming venv for: {dir_path}")

# Run only the setup (venv creation + dep install) — no server start.
# Serial execution reduces uv cache size (same reasoning as dry_run in RunHelper.start).
setup_cmd = setup_env_command(dir_path, global_config_dict, top_level_path)
process = run_command(setup_cmd, dir_path, server_name=top_level_path)
returncode = process.wait()
if returncode != 0:
print(f"ERROR: prefetch failed for {dir_path} (exit {returncode})")
raise SystemExit(returncode)

print("Prefetch complete.")


@exit_cleanly_on_config_error
def run(
global_config_dict_parser_config: Optional[GlobalConfigDictParserConfig] = None,
Expand Down
12 changes: 12 additions & 0 deletions nemo_gym/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,18 @@ def _dataset_download(args: argparse.Namespace, overrides: list[str]) -> None:
MODEL_API_KEY,
),
),
"env prefetch": Command(
target="nemo_gym.cli.env:prefetch",
summary="Pre-warm per-server venvs without starting servers.",
flags=(
CONFIG,
BENCHMARK,
ENVIRONMENT,
RESOURCES_SERVER_CONFIG,
MODEL_TYPE,
SEARCH_DIR,
),
),
"env status": Command(target="nemo_gym.cli.env:status", summary="Print the server status.", flags=(JSON,)),
"eval prepare": Command(
target="nemo_gym.cli.eval:prepare_benchmark",
Expand Down
9 changes: 9 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,15 @@ all = [
"nemo-gym[dev,sandbox]",
]

vllm = [
# Pin to the same version used by local_vllm_model / genrm_model server venvs.
"vllm==0.20.0",
# Pin flashinfer to the exact version vllm 0.20.0 specifies, ensuring pre-compiled
# CUDA kernels are used (avoids JIT compilation on first generation == slow step time).
# flashinfer>=0.2 bundles compiled kernels in the python package directly.
"flashinfer-python==0.6.8.post1",
]
Comment thread
ananthsub marked this conversation as resolved.
Resolved

sandbox = [
# Tenacity: Retry helpers used by sandbox providers.
# Updated: Sat May 09, 2026 with tenacity==9.1.4
Expand Down
7 changes: 6 additions & 1 deletion responses_api_models/genrm_model/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@

# We specifically pin the vllm dependency because we have tested on this version.
# Updated Tue Jun 23, 2026 with vllm==0.20.0
# License: Apache 2.0 https://github.com/vllm-project/vllm/blob/88d34c6409e9fb3c7b8ca0c04756f061d2099eb1/LICENSE
# License: Apache 2.0 https://github.com/vllm-project/vllm/blob/main/LICENSE
# "vllm==0.20.0",
# VLLM is resolved below since installation on Macs requires special workarounds.

Expand All @@ -48,6 +48,11 @@
dependencies.append("vllm==0.11.0")
else:
dependencies.append("vllm==0.20.0")
# Pin flashinfer to the exact version vllm 0.20.0 requires — pre-compiled CUDA kernels,
# avoids JIT compilation on first generation. Must stay in sync with pyproject.toml [vllm].
# Updated Tue Jun 23, 2026 with flashinfer-python==0.6.8.post1
# License: Apache 2.0 https://github.com/flashinfer-ai/flashinfer/blob/main/LICENSE
dependencies.append("flashinfer-python==0.6.8.post1")


setuptools.setup(install_requires=dependencies)
7 changes: 6 additions & 1 deletion responses_api_models/local_vllm_model/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

# We specifically pin the vllm dependency because we have tested on this version.
# Updated Tue Jun 23, 2026 with vllm==0.20.0
# License: Apache 2.0 https://github.com/vllm-project/vllm/blob/88d34c6409e9fb3c7b8ca0c04756f061d2099eb1/LICENSE
# License: Apache 2.0 https://github.com/vllm-project/vllm/blob/main/LICENSE
# "vllm==0.20.0",
# VLLM is resolved below since installation on Macs requires special workarounds.

Expand All @@ -45,6 +45,11 @@
dependencies.append("vllm==0.11.0")
else:
dependencies.append("vllm==0.20.0")
# Pin flashinfer to the exact version vllm 0.20.0 requires — pre-compiled CUDA kernels,
# avoids JIT compilation on first generation. Must stay in sync with pyproject.toml [vllm].
# Updated Tue Jun 23, 2026 with flashinfer-python==0.6.8.post1
# License: Apache 2.0 https://github.com/flashinfer-ai/flashinfer/blob/main/LICENSE
dependencies.append("flashinfer-python==0.6.8.post1")


setuptools.setup(install_requires=dependencies)
Loading
Loading