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
5 changes: 2 additions & 3 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,8 @@
**/*safetensors
container/Dockerfile*
container/**/*.Dockerfile
# The AISimulate dependency contract test reads this template from runtime test
# images; keep every other generated/template Dockerfile excluded.
!container/templates/wheel_builder.Dockerfile
# Dependency contract tests inspect the source installation templates in test images.
!container/templates/*.Dockerfile
container/render.py
container/context.yaml
# container/compliance/ is now COPY'd into each image's `licenses` Dockerfile
Expand Down
9 changes: 4 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ keywords = ["llm", "genai", "inference", "nvidia", "distributed"]
dynamo-router-policies = { path = "lib/router-plugins/builtin", version = "1.7.0" }
dynamo-generate-format-catalog = { path = "lib/generate-format-catalog", version = "1.7.0" }
# Published separately by https://github.com/ai-dynamo/aisimulate.
aisimulate-core = { version = "=0.13.0-dev.202610290000000087" }
aisimulate-core = { package = "aisim-core", version = "=0.14.0" }

# Local crates
dynamo-runtime = { path = "lib/runtime", version = "1.7.0" }
Expand Down Expand Up @@ -237,3 +237,7 @@ lto = "thin"
inherits = "release"
debug = true
strip = false

# Release-only AISim 0.14.0 RC0 source pin for Dynamo 1.7.0.
[patch.crates-io]
aisim-core = { git = "https://github.com/ai-dynamo/aisimulate.git", rev = "74dcdfcde184070d39e813ba24424b32e9627f00" }
2 changes: 1 addition & 1 deletion benchmarks/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ classifiers = [
]

dependencies = [
"aisimulate==0.13.0.dev202610290000000087 ; python_version >= '3.11' and python_version < '3.14'",
"aisim==0.14.0 ; python_version >= '3.11' and python_version < '3.14'",
"aiperf==0.13.0",
"matplotlib",
"networkx",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ engine:
parallelism:
preset:
- {replicas: 1, tensor: 1, pipeline: 1, attention_data: 1, moe_tensor: 1, moe_expert: 1}
scheduler: {max_batched_tokens: 32, max_sequences: {choices: [1, 4]}}
scheduler: {max_batched_tokens: 64, max_sequences: {choices: [1, 4]}}
kv_cache: {block_size: 64, prefix_caching: true, capacity: {type: fixed, blocks: 256}}
timing: {type: fixed, prefill_ms: 1, decode_ms: 1}
optimization:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,11 @@ def test_endpoint_fpm_reaches_planner_and_drives_scale_up(tmp_path):
subprocess.run(
[sys.executable, __file__],
check=True,
env={**os.environ, "DYN_FILE_KV": str(tmp_path)},
env={
**os.environ,
"DYN_FILE_KV": str(tmp_path),
"DYN_EVENT_PLANE_HOST": "127.0.0.1",
},
timeout=30,
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ async def async_internal(distributed_runtime):
await client.complete(event)


@pytest.mark.timeout(15)
@pytest.mark.timeout(50)
def test_wait_for_unacknowledged_decision(tmp_path):
# DistributedRuntime is process-global, so keep it out of the pytest worker.
with _isolated_etcd(tmp_path / "etcd") as etcd_port:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1330,6 +1330,7 @@ def test_agg_optimizer_synthetic_replay_smoke(monkeypatch) -> None:
),
router_mode="both",
overlap_credits=[0.0, 1.0],
prefill_load_scales=[1.0],
)
)

Expand Down Expand Up @@ -1357,6 +1358,7 @@ def test_agg_optimizer_timed_trace_smoke(tmp_path, monkeypatch) -> None:
),
router_mode="both",
overlap_credits=[0.0, 1.0],
prefill_load_scales=[1.0],
)
)

Expand All @@ -1383,6 +1385,7 @@ def test_optimizer_synthetic_replay_smoke(tmp_path, monkeypatch) -> None:
mean_e2e_latency_ms=100000.0,
),
overlap_credits=[0.0, 1.0],
prefill_load_scales=[1.0],
)
)

Expand All @@ -1409,6 +1412,7 @@ def test_optimizer_timed_trace_smoke(tmp_path, monkeypatch) -> None:
mean_e2e_latency_ms=100000.0,
),
overlap_credits=[0.0, 1.0],
prefill_load_scales=[1.0],
)
)

Expand Down
2 changes: 2 additions & 0 deletions container/Dockerfile.test
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ COPY --chmod=755 --chown=dynamo:0 \
.github/scripts/helm_chart_version.py \
.github/scripts/retry_kubectl.sh \
.github/scripts/collect_vcluster_diagnostics.sh /workspace/.github/scripts/
# Dependency contract tests inspect every runtime installation template.
COPY --chmod=775 --chown=dynamo:0 container/templates/ /workspace/container/templates/
# Required: blocks dynamo.vllm/sglang from shadowing the installed packages.
COPY --chmod=664 --chown=dynamo:0 conftest.py /workspace/conftest.py
COPY --chmod=775 --chown=dynamo:0 benchmarks/ /workspace/benchmarks/
Expand Down
2 changes: 1 addition & 1 deletion container/deps/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ so each image only installs what it needs.
| File | Purpose |
|------|---------|
| `requirements.common.txt` | Core deps shared by all containers |
| `requirements.aisimulate.txt` | Published AISimulate wheel staged into wheelhouses |
| `requirements.aisimulate.txt` | Source-built AISim wheel staged into release wheelhouses |
| `requirements.planner.txt` | Planner, profiler, and global_planner deps |
| `requirements.frontend.txt` | Frontend deps |
| `requirements.benchmark.txt` | Benchmark and profiling tools |
Expand Down
78 changes: 78 additions & 0 deletions container/deps/build_aisimulate.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

"""Build the AISimulate wheel from Dynamo's exact Rust source patch."""

import argparse
import re
import subprocess
import sys
from pathlib import Path

from packaging.requirements import Requirement

try:
import tomllib
except ModuleNotFoundError: # Python 3.10
import tomli as tomllib


def build(root: Path, source: Path, output: Path) -> None:
with (root / "Cargo.toml").open("rb") as handle:
patch = tomllib.load(handle)["patch"]["crates-io"]["aisim-core"]
repository, revision = patch["git"], patch["rev"]
if repository != "https://github.com/ai-dynamo/aisimulate.git" or not re.fullmatch(
r"[0-9a-f]{40}", revision
):
raise ValueError(
"AISimulate source patch must use the canonical repository and a full SHA"
)

source.mkdir(parents=True, exist_ok=False)
subprocess.run(["git", "init", str(source)], check=True)
subprocess.run(
["git", "-C", str(source), "fetch", "--depth=1", patch["git"], patch["rev"]],
check=True,
)
subprocess.run(
["git", "-C", str(source), "checkout", "--detach", "FETCH_HEAD"], check=True
)
project = source / "python/aisimulate"
with (project / "pyproject.toml").open("rb") as handle:
project_metadata = tomllib.load(handle)["project"]
if project_metadata["name"] != "aisim":
raise ValueError("RC source must publish the aisim distribution")
version = project_metadata["version"]
with (root / "pyproject.toml").open("rb") as handle:
requirements = tomllib.load(handle)["project"]["dependencies"]
requirement = next(
Requirement(item) for item in requirements if Requirement(item).name == "aisim"
)
if requirement.url is not None or not requirement.specifier.contains(version):
raise ValueError(
f"Source wheel version {version} does not satisfy {requirement}"
)
print(f"Building AISimulate {version} from {patch['rev']}", flush=True)
subprocess.run(
[
"maturin",
"build",
"--release",
"--locked",
"--interpreter",
sys.executable,
"--out",
str(output.resolve()),
],
cwd=project,
check=True,
)


if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--root", type=Path, required=True)
parser.add_argument("--source", type=Path, required=True)
parser.add_argument("--output", type=Path, required=True)
args = parser.parse_args()
build(args.root, args.source, args.output)
6 changes: 3 additions & 3 deletions container/deps/requirements.aisimulate.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0

# Published AISimulate wheel staged into planner and framework runtime wheelhouses.
# Keep in sync with the aisimulate-core pins in Cargo.toml and lib/bindings/python/Cargo.toml.
aisimulate==0.13.0.dev202610290000000087
# Source-built AISim wheel staged into all release wheelhouses.
# Keep in sync with the aisim-core pins in Cargo.toml and lib/bindings/python/Cargo.toml.
aisim==0.14.0
1 change: 1 addition & 0 deletions container/templates/dynamo_runtime.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ RUN --mount=type=cache,id=uv-dynamo-{{ context.dynamo.uv_version }},target=/home
export UV_CACHE_DIR=/home/dynamo/.cache/uv && \
uv pip install \
/opt/dynamo/wheelhouse/ai_dynamo_runtime*.whl \
/opt/dynamo/wheelhouse/aisim-*.whl \
/opt/dynamo/wheelhouse/ai_dynamo*any.whl \
/opt/dynamo/wheelhouse/nixl/nixl*.whl
{% else %}
Expand Down
1 change: 1 addition & 0 deletions container/templates/frontend.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ RUN --mount=type=bind,source=./container/deps/overrides.frontend.txt,target=/tmp
uv pip install \
--overrides /tmp/overrides.frontend.txt \
/opt/dynamo/wheelhouse/ai_dynamo_runtime*.whl \
/opt/dynamo/wheelhouse/aisim-*.whl \
/opt/dynamo/wheelhouse/ai_dynamo*any.whl && \
# The meta package requires both backends unconditionally (its cu12/cu13
# extras are vestigial), so install the backend first and the meta module
Expand Down
2 changes: 1 addition & 1 deletion container/templates/planner.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ RUN --mount=type=bind,source=./container/deps/requirements.planner.txt,target=/t
--requirement /tmp/requirements.benchmark.txt \
/opt/dynamo/wheelhouse/ai_dynamo_runtime*.whl \
/opt/dynamo/wheelhouse/ai_dynamo*any.whl \
/opt/dynamo/wheelhouse/aisimulate*.whl
/opt/dynamo/wheelhouse/aisim-*.whl

# Copy only the subset of the repository needed for planner/profiler service
# startup and the component-local planner-family test suites. AI Simulate
Expand Down
4 changes: 2 additions & 2 deletions container/templates/sglang_runtime.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ COPY --chmod=775 --chown=dynamo:0 --from=wheel_builder /opt/dynamo/dist/*.whl /o
RUN pip install --no-deps \
/opt/dynamo/wheelhouse/ai_dynamo_runtime*.whl \
/opt/dynamo/wheelhouse/ai_dynamo*any.whl \
/opt/dynamo/wheelhouse/aisimulate*.whl \
/opt/dynamo/wheelhouse/aisim-*.whl \
/opt/dynamo/wheelhouse/nixl/nixl*.whl \
"distro==1.9.0"
{% else %}
Expand All @@ -137,7 +137,7 @@ RUN --mount=type=cache,target=/root/.cache/pip,sharing=locked \
pip install --break-system-packages --no-deps \
/opt/dynamo/wheelhouse/ai_dynamo_runtime*.whl \
/opt/dynamo/wheelhouse/ai_dynamo*any.whl \
/opt/dynamo/wheelhouse/aisimulate*.whl
/opt/dynamo/wheelhouse/aisim-*.whl

# Install accelerate for diffusion/video worker pipelines (diffusers requires it
# for enable_model_cpu_offload but the upstream SGLang runtime image omits it)
Expand Down
1 change: 1 addition & 0 deletions container/templates/triton_runtime.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ RUN --mount=type=cache,target=/root/.cache/uv,sharing=locked \
uv pip install \
certifi==2026.10.23 pip==26.2.2 \
/opt/dynamo/wheelhouse/ai_dynamo_runtime*.whl \
/opt/dynamo/wheelhouse/aisim-*.whl \
/opt/dynamo/wheelhouse/ai_dynamo*any.whl && \
\
# Triton's Python bindings (from the release image) and the tritonclient gRPC client.
Expand Down
2 changes: 1 addition & 1 deletion container/templates/trtllm_runtime.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,7 @@ RUN --mount=type=cache,id=uv-root-{{ context.dynamo.uv_version }},target=/root/.
# Dynamo's own wheels — --no-deps preserves upstream's solve.
uv pip install --no-deps /opt/dynamo/wheelhouse/ai_dynamo_runtime*.whl && \
uv pip install --no-deps /opt/dynamo/wheelhouse/ai_dynamo*any.whl && \
uv pip install --no-deps /opt/dynamo/wheelhouse/aisimulate*.whl && \
uv pip install --no-deps /opt/dynamo/wheelhouse/aisim-*.whl && \
\
# nixl/nixl-cu13 for `import nixl` (dynamo.nixl_connect); version from NIXL_REF.
# LD_PRELOAD swaps the runtime .so (nixl#1668).
Expand Down
2 changes: 1 addition & 1 deletion container/templates/vllm_runtime.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ RUN --mount=type=cache,id=uv-root-{{ context.dynamo.uv_version }},target=/root/.
export UV_CACHE_DIR=/root/.cache/uv && \
uv pip install {{ pip_target }} --no-deps /opt/dynamo/wheelhouse/ai_dynamo_runtime*.whl && \
uv pip install {{ pip_target }} --no-deps /opt/dynamo/wheelhouse/ai_dynamo*any.whl && \
uv pip install {{ pip_target }} --no-deps /opt/dynamo/wheelhouse/aisimulate*.whl && \
uv pip install {{ pip_target }} --no-deps /opt/dynamo/wheelhouse/aisim-*.whl && \
if [ "${ENABLE_GPU_MEMORY_SERVICE}" = "true" ]; then \
GMS_WHEEL=$(ls /opt/dynamo/wheelhouse/gpu_memory_service*.whl 2>/dev/null | head -1); \
if [ -n "$GMS_WHEEL" ]; then uv pip install {{ pip_target }} --no-deps "$GMS_WHEEL"; fi; \
Expand Down
35 changes: 17 additions & 18 deletions container/templates/wheel_builder.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -684,24 +684,23 @@ COPY examples/router/custom-policy-example/ /opt/dynamo/examples/router/custom-p
COPY deploy/inference-gateway/ext-proc/ /opt/dynamo/deploy/inference-gateway/ext-proc/
COPY deploy/inference-gateway/sidecar/ /opt/dynamo/deploy/inference-gateway/sidecar/

{% if target == "planner" or (target == "runtime" and framework in ("vllm", "sglang", "trtllm")) %}
COPY container/deps/requirements.aisimulate.txt /opt/dynamo/container/deps/requirements.aisimulate.txt

# AISimulate is released separately as an abi3 wheel. Stage the pinned wheel
# directly from NVIDIA's package index. Download only this
# distribution; runtime images own dependency installation through their
# requirements files and local wheels.
RUN --mount=type=cache,id=uv-root-{{ context.dynamo.uv_version }},target=/root/.cache/uv,sharing=shared \
export UV_CACHE_DIR=/root/.cache/uv && \
# Build Python and Rust AISimulate from the same source revision. The published
# Dynamo metadata keeps a version requirement so its wheel can upload to PyPI.
COPY container/deps/build_aisimulate.py /opt/dynamo/container/deps/build_aisimulate.py
RUN --mount=type=secret,id=aws-web-identity-token,target=/run/secrets/aws-token \
--mount=type=secret,id=aws-role-arn,env=AWS_ROLE_ARN \
--mount=type=cache,target=/usr/local/cargo/registry,sharing=shared \
--mount=type=cache,target=/usr/local/cargo/git,sharing=shared \
export AWS_WEB_IDENTITY_TOKEN_FILE=/run/secrets/aws-token && \
export SCCACHE_S3_KEY_PREFIX=${SCCACHE_S3_KEY_PREFIX:-${TARGETARCH}} && \
if [ "$USE_SCCACHE" = "true" ]; then \
eval $(/tmp/use-sccache.sh setup-env); \
fi && \
source ${VIRTUAL_ENV}/bin/activate && \
python -m pip download \
--only-binary=:all: \
--no-deps \
--no-index \
--find-links https://pypi.nvidia.com/aisimulate/ \
--dest /opt/dynamo/dist \
--requirement /opt/dynamo/container/deps/requirements.aisimulate.txt
{% endif %}
python /opt/dynamo/container/deps/build_aisimulate.py \
--root /opt/dynamo \
--source /opt/aisimulate-source \
--output /opt/dynamo/dist

# Compliance: harvest each crate's real LICENSE files from the cargo registry
# source cache so the rust NOTICES generator can inline upstream license text
Expand Down Expand Up @@ -734,7 +733,7 @@ RUN --mount=type=cache,target=/root/.cargo/registry,sharing=shared \
# the bare system python3 lacks it and the step would no-op with a warning.
COPY container/compliance /opt/compliance
RUN set -u; injected=0; \
for whl in /opt/dynamo/dist/ai_dynamo_runtime*.whl /opt/dynamo/dist/aisimulate*.whl; do \
for whl in /opt/dynamo/dist/ai_dynamo_runtime*.whl /opt/dynamo/dist/aisim-*.whl; do \
[ -e "$whl" ] || continue; \
PYTHONPATH=/opt ${VIRTUAL_ENV}/bin/python3 -m compliance.bundle_wheel_notices \
--wheel "$whl" --licenses-dir /opt/dynamo/rust-licenses -v \
Expand Down
6 changes: 3 additions & 3 deletions deploy/operator/hack/test_aisimulate_hardware_catalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ def _pinned_version() -> Version:
requirements = [
Requirement(dependency)
for dependency in dependencies
if canonicalize_name(Requirement(dependency).name) == "aisimulate"
if canonicalize_name(Requirement(dependency).name) == "aisim"
]
assert len(requirements) == 1, "pyproject.toml must contain one AISimulate pin"

Expand All @@ -50,7 +50,7 @@ def _pinned_version() -> Version:


def _packaged_hardware_skus() -> list[str]:
distribution = metadata.distribution("aisimulate")
distribution = metadata.distribution("aisim")
system_files = [
path
for path in distribution.files or []
Expand Down Expand Up @@ -86,7 +86,7 @@ def _operator_hardware_skus() -> list[str]:


def test_dgdr_hardware_catalog_matches_pinned_aisimulate() -> None:
assert Version(metadata.version("aisimulate")) == _pinned_version()
assert Version(metadata.version("aisim")) == _pinned_version()

expected = _packaged_hardware_skus()
assert _operator_hardware_skus() == expected
Expand Down
Loading
Loading