Repository navigation
integrations/nemo-gym: a runtime layer that injects the NeMo servers into any task image - #111
Closed
Tomer Glottmann (tomergee) wants to merge 1 commit into
Closed
Tomer Glottmann (tomergee) wants to merge 1 commit into
Tomer Glottmann (tomergee) wants to merge 1 commit into
Conversation
…into any task image integrations/nemo-gym/layer is one OCI image, built from scratch the way integrations/openhands/layer is: python-build-standalone CPython 3.12.15, hash-pinned wheels installed with uv, and two servers started with the layer's own interpreter so the task image contributes only its kernel and filesystem: - nemo_runtime.sandbox_server serves NeMo Skills' local sandbox server (POST /execute for ipython, python, shell and lean4; IPython sessions by X-Session-ID; GET /health), vendored verbatim from NeMo-Skills bcf059a. Flask binds 127.0.0.1 by default, which the router cannot reach, so the wrapper binds 0.0.0.0. The layer's interpreter is appended to PATH as the fallback for language: python, so an image's own Python wins when it has one and an image without Python still executes code. - nemo_runtime.exec_server runs NeMo Gym's exec server (POST /exec, /upload, GET /download, /health), the EXEC_SERVER_SCRIPT of the ECS Fargate provider at nemo-gym d4fd5e8, written out verbatim. Both vendored files keep their Apache-2.0 license and a provenance README under layer/third_party. lock.sh regenerates the hash-pinned requirements for amd64 and arm64. The README section on injected daemons is rewritten around the real layer: build and push, the template command, the two servers, the trust note, and the limits (the NeMo Skills server no longer needs a Python in the image; the layer needs glibc 2.17 or newer). The base template must name a task image of its own, because the guest's image is Alpine-based and cannot run the layer's interpreter, so a base without one never bakes its golden snapshot; and a recipe selects the layered base through create.template, not a per-sandbox provider_options template, which would select the base itself and skip the task image. hack/runtime-layer-demo.sh registers the template and calls both servers from inside the environment, through the header form and through the path form. tests/test_e2e.py gains test_runtime_layer_endpoint, gated by SUBSTRATE_E2E_RUNTIME_TEMPLATE and SUBSTRATE_E2E_RUNTIME_IMAGE. RUNTIMES.md and the top-level README point at the layer. No provider code changes. Verified on a GKE cluster (gVisor pool, Substrate ddb46651, ate-env-api with the port proxy): environments from python:3.12-slim and from debian (no Python inside) served 4 to 7 s after create; /execute answered from inside, through the header form and the path form; an IPython session kept its state; language: python ran on the layer's 3.12.15 in the Debian image; the exec server answered /exec; the e2e test passed in 6.4 s. Layer image 87 MB compressed.
Tomer Glottmann (tomergee)
force-pushed
the
nemo-runtime-layer
branch
from
October 21, 2026 23:40
1348f7d to
067df9a
Compare
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.
Makes the "injected daemon" section of the NeMo Gym integration real: a runtime layer that brings the NeMo servers into any task image, started by the guest, reached through
endpoint().Why
NeMo components that talk to a service inside the sandbox, NeMo Skills' code-execution sandbox server (
ns_tools, the math recipes) and NeMo Gym's own exec server (what the ECS Fargate provider injects), need that service in the task image. SWE-bench-style images do not have it, and the integration README so far described the layer mechanism (#70) and the port proxy (#92) with a hypotheticalnemo-daemon. This PR ships the daemon.What
integrations/nemo-gym/layer/: one OCI image built from scratch (the recipe of #93's OpenHands layer): python-build-standalone CPython 3.12.15 plus hash-pinned wheels installed with uv, and two entry points served with the layer's own interpreter, so the task image contributes only its kernel and filesystem:/opt/nemo/python/bin/python3 -m nemo_runtime.sandbox_server --host 0.0.0.0 --port 6000POST /execute(ipython,python,shell,lean4), IPython sessions byX-Session-ID,GET /health/opt/nemo/python/bin/python3 -m nemo_runtime.exec_server --host 0.0.0.0 --port 19542POST /exec,POST /upload,GET /download,GET /healthBoth are vendored verbatim under
layer/third_party/with their Apache-2.0 license and a provenance README (NeMo Skillsbcf059a, NeMo Gymd4fd5e8); the wrappers only choose the bind address (Flask's default is 127.0.0.1, which the router cannot reach) and the port. Forlanguage: pythonthe layer's interpreter is appended toPATHas the fallback, so an image's own Python wins when it has one and an image without Python still executes code.lock.shregenerates the hash-pinned requirements for amd64 and arm64.Docs and tooling: the README section is rewritten around the real layer (build, template command, the two servers, trust note, limits updated: the NeMo Skills server no longer needs a Python in the image, the layer needs glibc 2.17+); the mapping table gains
endpoint();hack/runtime-layer-demo.shregisters the template and calls the servers from inside the environment, through the header form and through the path form; RUNTIMES.md and the top-level README point at it. Two things the live runs taught, both now in the README: the base template must name a task image of its own (--rootfs-image), because the guest's image is Alpine-based and cannot run the layer's glibc interpreter, so a base without one never bakes its golden snapshot and showsERROR(the derived templates still work; theoh-agent-serverbase on the test cluster shows the same); and a recipe must select the layered base throughcreate.template, not a per-sandboxprovider_optionstemplate, which selects the base itself and skips the task image.Test:
tests/test_e2e.py::test_runtime_layer_endpoint, gated bySUBSTRATE_E2E_RUNTIME_TEMPLATE, creates a sandbox on the layered template, resolvesendpoint(), runsprint(6 * 7)through the proxy, keeps an IPython session across two calls, and checks that an undeclared port is refused. No provider code changes.Verification
pytest integrations/nemo-gym/tests45 passed;make verify-boilerplate verify-gofmt; ruff clean on the new Python; shellcheck clean on the scripts.ddb46651, ate-env-api with the port proxy), layer image 87 MB compressed:hack/runtime-layer-demo.shwithpython:3.12-slimand withdebian:bookworm-slim(no Python inside): serving 4 to 5 s aftercreatewith readiness gated on both sidecars;/executeanswered from inside, through the header form and through the path form; an IPython session keptxbetween calls;language: pythonprinted the layer's3.12.15in the Debian image; the exec server answered/exec.test_runtime_layer_endpointagainst the same template and the Debian image: passed in 6.4 s, no sandbox left behind.ddb46651, substrate#2780) no longer takessandboxConfigin a template andmain'sate-env manifest templatestill emits it. The layer, the scripts and the test do not depend on that.Limits
LocalSandboxclient buildshttp://host:port/executeitself and cannot add headers, so a recipe that uses it directly needs the path form as its base URL or a per-sandbox proxy;ns_toolstoday points at one shared sandbox server, not one per Gym sandbox.