Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/wheel-constraints/nemo-platform-services.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ langchain-community==0.3.31
langchain-core==1.4.9
langchain-nvidia-ai-endpoints==1.4.3
langchain-openai==1.3.5
langchain==1.3.13
langchain==1.3.14
lark==1.3.1
litellm<1.92 # 1.92.0 native build has no py3.14 wheel
nemo-anonymizer==0.3.0
Expand Down
5 changes: 3 additions & 2 deletions docker-bake.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -412,8 +412,9 @@ target "nmp-rl-training" {
context = "."
dockerfile = "docker/Dockerfile.nmp-rl-training"
contexts = {
platform-workspace = "target:rl-platform-workspace"
nmp-rl-base = "target:nmp-rl-base-builder"
platform-workspace = "target:rl-platform-workspace"
nmp-rl-base = "target:nmp-rl-base-builder"
ffmpeg-vlm-wheel-image = ffmpeg_vlm_wheel_context()
}
cache-to = maybe_registry_cache_to("nmp-rl-training")
cache-from = maybe_registry_cache_from("nmp-rl-training")
Expand Down
50 changes: 50 additions & 0 deletions docker/Dockerfile.nmp-customizer-tasks
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,56 @@ RUN --mount=type=cache,target=/root/.cache/uv \
-e /app/packages/nmp_customization_common \
-e /app/services/core/models

# Pin scanner-tracked runtime packages into /opt/venv before removing stale
# NGC system-site copies inherited from nvcr.io/nvidia/pytorch:26.05-py3.
RUN --mount=type=cache,target=/root/.cache/uv \
uv pip install --python ${VIRTUAL_ENV}/bin/python --no-cache \
--overrides /app/docker/customizer/preserve_base_torch.txt \
"aiohttp>=3.14.1,<4" \
"grpcio>=1.81.1,<2" \
"jupyter-server>=2.20.0,<3" \
"mistune>=3.3.3,<4" \
"soupsieve>=2.8.4,<3" \
"tornado>=6.5.7,<7" \
"urllib3>=2.7.0,<3" \
"wandb>=0.28.1,<1"

# CVE cleanup for unused/stale packages from the NGC base image. The task entry
# points import from /opt/venv; scanners still report the shadowed system copies.
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends --only-upgrade openssl libssl3t64 && \
rm -rf /var/lib/apt/lists/* && \
rm -rf \
/usr/local/lib/python3.12/dist-packages/aiohttp \
/usr/local/lib/python3.12/dist-packages/aiohttp-*.dist-info \
/usr/local/lib/python3.12/dist-packages/black \
/usr/local/lib/python3.12/dist-packages/_black* \
/usr/local/lib/python3.12/dist-packages/black-*.dist-info \
/usr/local/lib/python3.12/dist-packages/blackd \
/usr/local/lib/python3.12/dist-packages/tornado \
/usr/local/lib/python3.12/dist-packages/tornado-*.dist-info \
/usr/local/lib/python3.12/dist-packages/onnx \
/usr/local/lib/python3.12/dist-packages/onnx-*.dist-info \
/usr/local/lib/python3.12/dist-packages/urllib3 \
/usr/local/lib/python3.12/dist-packages/urllib3-*.dist-info \
/usr/local/lib/python3.12/dist-packages/grpc \
/usr/local/lib/python3.12/dist-packages/grpcio \
/usr/local/lib/python3.12/dist-packages/grpcio-*.dist-info \
/usr/local/lib/python3.12/dist-packages/wandb \
/usr/local/lib/python3.12/dist-packages/wandb-*.dist-info \
/usr/local/lib/python3.12/dist-packages/mlflow \
/usr/local/lib/python3.12/dist-packages/mlflow-*.dist-info \
/usr/local/lib/python3.12/dist-packages/mlflow_skinny-*.dist-info \
/usr/local/lib/python3.12/dist-packages/mistune \
/usr/local/lib/python3.12/dist-packages/mistune-*.dist-info \
/usr/local/lib/python3.12/dist-packages/soupsieve \
/usr/local/lib/python3.12/dist-packages/soupsieve-*.dist-info \
/usr/local/lib/python3.12/dist-packages/jupyter_server \
/usr/local/lib/python3.12/dist-packages/jupyter_server-*.dist-info \
/usr/local/cuda/NsightSystems-cli-* \
/usr/local/bin/nsys \
/usr/local/cuda/bin/nsys

ENV PATH="${VIRTUAL_ENV}/bin:${PATH}"
ENTRYPOINT ["/opt/venv/bin/python"]
CMD ["-m", "nmp.customization_common.tasks.file_io", "--help"]
Expand Down
37 changes: 37 additions & 0 deletions docker/Dockerfile.nmp-rl-training
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

ARG SMOKE_MARKER=smoke_nmp_rl_training

FROM ffmpeg-vlm-wheel-image AS ffmpeg-vlm-wheel-src

# Supplied by bake (target:nmp-rl-base-builder).
FROM nmp-rl-base AS rl-base

Expand Down Expand Up @@ -40,6 +42,41 @@ RUN mkdir -p /home/${USERNAME}/.cache && \
# image has a real /opt/venv).
RUN ln -sfn /opt/nemo_rl_venv /opt/venv

# Replace base-image av/opencv/decord2 wheels that bundle vulnerable FFmpeg.
# decord2 stays installed because NeMo-RL imports the `decord` module for
# multimodal video loading; rebuild it from source against FFmpeg 8.1.2.
RUN --mount=from=ffmpeg-vlm-wheel-src,target=/tmp/ffmpeg-vlm-wheel-src,readonly \
Comment thread
mckornfield marked this conversation as resolved.
set -e; \
for site in ${VIRTUAL_ENV}/lib/python*/site-packages; do \
[ -d "${site}" ] || continue; \
rm -rf \
"${site}/av" \
"${site}"/av-*.dist-info \
"${site}/av.libs" \
"${site}/cv2" \
"${site}/opencv_python_headless.libs" \
"${site}"/opencv_python_headless-*.dist-info \
"${site}/decord" \
"${site}"/decord-*.dist-info \
"${site}/decord.libs" \
"${site}/decord2" \
"${site}"/decord2-*.dist-info \
"${site}/decord2.libs"; \
done; \
for archive in /root/.cache/uv/archive-v0/*; do \
[ -d "${archive}" ] || continue; \
if [ -e "${archive}/av.libs" ] || \
[ -e "${archive}/opencv_python_headless.libs" ] || \
[ -e "${archive}/decord.libs" ] || \
[ -e "${archive}/decord2.libs" ]; then \
rm -rf "${archive}"; \
fi; \
done; \
UV_LINK_MODE=copy uv pip install --python ${VIRTUAL_ENV}/bin/python --no-cache --no-deps --reinstall \
/tmp/ffmpeg-vlm-wheel-src/wheels/av-*-abi3-manylinux_2_28_*.whl \
/tmp/ffmpeg-vlm-wheel-src/wheels/opencv_python_headless-*cp313*.whl \
/tmp/ffmpeg-vlm-wheel-src/wheels/decord2-*cp313*.whl

# The NeMo-RL base venv ships some packages with missing dist-info METADATA (a
# known property of the image — the package code is present, only uv's metadata
# file is gone). Normal `uv pip install` reads ALL installed metadata during
Expand Down
2 changes: 1 addition & 1 deletion docker/automodel/Dockerfile.nmp-automodel-base
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \
"urllib3>=2.7.0,<3" \
"mlflow-skinny>=3.11.1,<3.12.0" \
"grpcio>=1.81.1,<2" \
"wandb>=0.28.0,<1"
"wandb>=0.28.1,<1"

# Replace PyPI av/opencv (bundled FFmpeg < 8.1.2) with custom wheels from ffmpeg-vlm-wheel.
# vlm sync pulls PyPI copies transitively; drop them before installing our manylinux wheels.
Expand Down
68 changes: 65 additions & 3 deletions docker/base/Dockerfile.python-wheels
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# Builds Python wheels for:
# - causal-conv1d (CUDA extension required by mamba-ssm)
# - mamba-ssm (selective state space model)
# - av + opencv-python-headless (FFmpeg 8.1.2 bundled wheels for nspect / VLM)
# - av + opencv-python-headless + decord2 (FFmpeg 8.1.2 bundled wheels for nspect / VLM)
#
# causal-conv1d / mamba-ssm only ship source on PyPI and require nvcc.
# av / opencv wheels bundle FFmpeg; upstream PyPI wheels lag nspect 8.1.2.
Expand All @@ -19,6 +19,8 @@
# FFMPEG_VERSION - FFmpeg for av/opencv wheels (default: 8.1.2)
# PYAV_VERSION - PyAV git tag (default: v17.1.0)
# OPENCV_PYTHON_GIT_TAG - opencv-python git tag (92 = PyPI 4.13.0.92)
# DECORD2_REPO - decord2 git repository
# DECORD2_COMMIT - decord2 commit to build (default: 1b4cfbd...)
#######

ARG CUDA_VERSION=12.8.1
Expand Down Expand Up @@ -276,7 +278,7 @@ COPY --from=mamba-ssm-23-wheel-builder-py312-cu13.1.1 /wheels /wheels/cu13.1.1
COPY --from=mamba-ssm-23-wheel-builder-py312-cu13.2 /wheels /wheels/cu13.2

# =============================================================================
# FFmpeg / VLM wheels (manylinux_2_28, cp312) — av + opencv-python-headless
# FFmpeg / VLM wheels (manylinux_2_28) — av + opencv-python-headless + decord2
# =============================================================================
FROM quay.io/pypa/manylinux_2_28_x86_64:latest AS ffmpeg-vendor-base-py312-amd64
FROM quay.io/pypa/manylinux_2_28_aarch64:latest AS ffmpeg-vendor-base-py312-arm64
Expand Down Expand Up @@ -324,6 +326,7 @@ RUN mkdir -p /wheels /tmp/wheelhouse && \

# =============================================================================
# opencv-python-headless wheel — opencv-python 4.13 against shared FFmpeg 8.1.2
# Python 3.12
# =============================================================================
FROM ffmpeg-vendor-base-py312 AS opencv-headless-wheel-builder-py312

Expand All @@ -350,9 +353,68 @@ RUN mkdir -p /wheels /tmp/wheelhouse && \
auditwheel repair /tmp/wheelhouse/opencv_python_headless-*.whl -w /wheels && \
rm -rf /src/opencv-python /tmp/wheelhouse

# =============================================================================
# opencv-python-headless wheel — opencv-python 4.13 against shared FFmpeg 8.1.2
# Python 3.13
# =============================================================================
FROM ffmpeg-vendor-base-py312 AS opencv-headless-wheel-builder-py313

ARG OPENCV_PYTHON_GIT_TAG=92

ENV CI_BUILD=1 \
ENABLE_HEADLESS=1 \
PKG_CONFIG_PATH=/ffmpeg_build/lib/pkgconfig:/usr/local/lib/pkgconfig \
LD_LIBRARY_PATH=/ffmpeg_build/lib:${LD_LIBRARY_PATH:-} \
LDFLAGS=-L/ffmpeg_build/lib

RUN yum install -y \
zlib-devel \
libjpeg-turbo-devel \
&& yum clean all

RUN mkdir -p /wheels /tmp/wheelhouse && \
/opt/python/cp313-cp313/bin/pip install -U "pip<26" "numpy>=2.0" auditwheel && \
git clone --depth 1 --branch ${OPENCV_PYTHON_GIT_TAG} https://github.com/opencv/opencv-python.git /src/opencv-python && \
cd /src/opencv-python && \
git submodule update --init --recursive opencv && \
OPENCV_PYTHON_SKIP_GIT_COMMANDS=1 \
/opt/python/cp313-cp313/bin/pip wheel . --no-deps -w /tmp/wheelhouse && \
auditwheel repair /tmp/wheelhouse/opencv_python_headless-*.whl -w /wheels && \
rm -rf /src/opencv-python /tmp/wheelhouse

# =============================================================================
# decord2 wheel — decord2 3.4 against shared FFmpeg 8.1.2
# Python 3.13
# =============================================================================
FROM ffmpeg-vendor-base-py312 AS decord2-wheel-builder-py313

ARG DECORD2_REPO=https://github.com/johnnynunez/decord2.git
ARG DECORD2_COMMIT=1b4cfbd5d2964d411341ccf252051e516845430e

ENV PKG_CONFIG_PATH=/ffmpeg_build/lib/pkgconfig \
LD_LIBRARY_PATH=/ffmpeg_build/lib:${LD_LIBRARY_PATH:-} \
PATH=/opt/python/cp313-cp313/bin:${PATH}

RUN mkdir -p /wheels /tmp/wheelhouse && \
pip install -U "pip<26" "cmake>=3.21" ninja "numpy>=2.0" auditwheel wheel setuptools && \
git clone "${DECORD2_REPO}" /src/decord2 && \
cd /src/decord2 && \
git checkout "${DECORD2_COMMIT}" && \
git submodule update --init --recursive && \
cmake -S . -B build -G Ninja -DUSE_CUDA=OFF -DCMAKE_BUILD_TYPE=Release -DFFMPEG_DIR=/ffmpeg_build && \
cmake --build build --parallel "$(nproc)" && \
cd /src/decord2/python && \
pip wheel . --no-deps -w /tmp/wheelhouse && \
auditwheel repair /tmp/wheelhouse/decord2-*.whl -w /wheels && \
rm -rf /src/decord2 /tmp/wheelhouse

# The final ffmpeg-vlm-wheel image contains (one arch per platform build):
# - av-*-cp311-abi3-manylinux_2_28_*.whl (PyAV stable ABI; works on cp312)
# - av-*-cp311-abi3-manylinux_2_28_*.whl (PyAV stable ABI; works on cp312/cp313)
# - opencv_python_headless-*-cp312-*.whl
# - opencv_python_headless-*-cp313-*.whl
# - decord2-*-cp313-*.whl
FROM scratch AS ffmpeg-vlm-wheel
COPY --from=av-wheel-builder-py312 /wheels /wheels
COPY --from=opencv-headless-wheel-builder-py312 /wheels /wheels
COPY --from=opencv-headless-wheel-builder-py313 /wheels /wheels
COPY --from=decord2-wheel-builder-py313 /wheels /wheels
2 changes: 1 addition & 1 deletion packages/nemo_platform/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ nemo-evaluator-sdk = [
nemo-guardrails-plugin = [
"nemo-platform-plugin",
"nemoguardrails[tracing]==0.23.0",
"langchain>=1.3.13",
"langchain>=1.3.14",
"langchain-community>=0.3.31,<0.4",
"langchain-core>=1.4.9",
"langchain-openai>=1.3.5",
Expand Down
18 changes: 15 additions & 3 deletions packages/nmp_common/src/nmp/common/sdk_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ def get_platform_sdk(
internal: bool = False,
http_client: httpx.Client | None = None,
on_behalf_of: str | Principal | None = None,
base_url: str | None = None,
) -> NeMoPlatform:
"""
Returns an instance of the NeMoPlatform SDK configured with the platform's base URL.
Expand All @@ -199,13 +200,14 @@ def get_platform_sdk(
Use this for controllers and background tasks that make internal API calls.
http_client: Optional sync HTTP client to use for requests.
on_behalf_of: Optional principal ID to use for on-behalf-of authorization.
base_url: Optional platform base URL. Defaults to configured platform base URL.

Returns:
Configured NeMoPlatform SDK instance.
"""
headers = _get_default_headers(as_service, internal, on_behalf_of)
sdk = NeMoPlatform(
base_url=_base_url_from_config(),
base_url=base_url or _base_url_from_config(),
http_client=http_client or shared_sync_http_client(),
default_headers=headers if headers else None,
)
Expand Down Expand Up @@ -273,6 +275,7 @@ def get_async_platform_sdk(
internal: bool = False,
http_client: Optional[httpx.AsyncClient] = None,
on_behalf_of: Optional[str | Principal] = None,
base_url: str | None = None,
) -> AsyncNeMoPlatform:
"""
Returns an instance of the AsyncNeMoPlatform SDK configured with the platform's base URL.
Expand All @@ -287,6 +290,7 @@ def get_async_platform_sdk(
http_client: Optional HTTP client to use for requests. Used for test injection
via DependencyProvider. See architecture/docs/http-client-injection.md.
on_behalf_of: Optional principal ID to use for on-behalf-of authorization.
base_url: Optional platform base URL. Defaults to configured platform base URL.
Returns:
Configured AsyncNeMoPlatform SDK instance.
"""
Expand All @@ -297,7 +301,7 @@ def get_async_platform_sdk(
effective_client = http_client or _test_http_client or shared_async_http_client()

sdk = AsyncNeMoPlatform(
base_url=_base_url_from_config(),
base_url=base_url or _base_url_from_config(),
http_client=effective_client,
default_headers=headers if headers else None,
)
Expand Down Expand Up @@ -430,12 +434,14 @@ def get_platform_sdk(
internal: bool = False,
http_client: httpx.Client | None = None,
on_behalf_of: str | Principal | None = None,
base_url: str | None = None,
) -> NeMoPlatform:
return get_platform_sdk(
as_service=as_service,
internal=internal,
http_client=http_client,
on_behalf_of=on_behalf_of,
base_url=base_url,
)

def get_async_platform_sdk(
Expand All @@ -444,5 +450,11 @@ def get_async_platform_sdk(
as_service: str | None = None,
internal: bool = False,
on_behalf_of: str | Principal | None = None,
base_url: str | None = None,
) -> AsyncNeMoPlatform:
return get_async_platform_sdk(as_service=as_service, internal=internal, on_behalf_of=on_behalf_of)
return get_async_platform_sdk(
as_service=as_service,
internal=internal,
on_behalf_of=on_behalf_of,
base_url=base_url,
)
2 changes: 1 addition & 1 deletion plugins/nemo-guardrails/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ dependencies = [
"nemo-platform-plugin",
"nemo-platform",
"nemoguardrails[tracing]==0.23.0",
"langchain>=1.3.13",
"langchain>=1.3.14",
"langchain-community>=0.3.31,<0.4",
"langchain-core>=1.4.9",
"langchain-openai>=1.3.5",
Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,7 @@ constraint-dependencies = [
"litellm>=1.83.10",
"lxml>=6.1.0",
"mistune>=3.3.2",
"mcp>=1.28.1",
"msgpack>=1.2.1",
"nbconvert>=7.17.1",
"nltk>=3.10.0",
Expand Down Expand Up @@ -322,7 +323,7 @@ override-dependencies = [
"ujson>=5.12.0", # High – nmp-api/cpu-tasks/gpu-tasks
"xgrammar>=0.1.32", # High – customizer + nmp-gpu-tasks
"fastmcp>=3.2.0", # GHSA-vv7q-7jx5-f767 (Critical) + GHSA-rww4-4w9c-7733 (High); overrides vendored sdk/python/nemo-platform <3 constraint
"wandb>=0.28.0", # wandb-core Go CVEs (nspect); unsloth + training images
"wandb>=0.28.1", # wandb-core Go CVEs (nspect); unsloth + training images
"cryptography>=48.0.1,<49", # Pulse Critical; overrides data-designer-engine 0.7.0's <47 cap after changelog review
"click>=8.2.0", # Below CVEs are based on constraints that garak has
"litellm>=1.83.10", # CVE-2026-42208 (Critical SQL injection), CVE-2026-40217, and 5 other High CVEs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def create_server(base_url: str | None = None) -> FastMCP:
server = FastMCP("NeMo Entities Service")

# Create NeMo SDK client using shared factory
nemo_client = get_platform_sdk(base_url)
nemo_client = get_platform_sdk(base_url=base_url)

# === WORKSPACE TOOLS ===

Expand Down
2 changes: 1 addition & 1 deletion services/core/mcp/tests/integration/smoke_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ def nmp_base_url() -> str:
@pytest.fixture(scope="module")
def nemo_sdk(nmp_base_url: str) -> Generator[NeMoPlatform, None, None]:
"""Create NeMo SDK client for direct API validation."""
client = get_platform_sdk(nmp_base_url)
client = get_platform_sdk(base_url=nmp_base_url)
yield client


Expand Down
Loading