Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
OpenDAS
dynamo
Commits
a20b9dde
Unverified
Commit
a20b9dde
authored
Mar 31, 2026
by
Alec
Committed by
GitHub
Mar 31, 2026
Browse files
feat(container): add standalone dynamo-planner image [DYN-2533] (#7696)
parent
c4ef45bb
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
296 additions
and
18 deletions
+296
-18
.github/filters.yaml
.github/filters.yaml
+1
-0
components/src/dynamo/planner/__main__.py
components/src/dynamo/planner/__main__.py
+3
-3
components/src/dynamo/profiler/utils/config.py
components/src/dynamo/profiler/utils/config.py
+4
-3
components/src/dynamo/profiler/utils/dgd_generation.py
components/src/dynamo/profiler/utils/dgd_generation.py
+5
-2
components/src/dynamo/profiler/utils/profile_common.py
components/src/dynamo/profiler/utils/profile_common.py
+24
-8
container/Dockerfile.template
container/Dockerfile.template
+4
-0
container/context.yaml
container/context.yaml
+4
-0
container/deps/README.md
container/deps/README.md
+1
-1
container/deps/requirements.planner.txt
container/deps/requirements.planner.txt
+1
-1
container/render.py
container/render.py
+1
-0
container/templates/args.Dockerfile
container/templates/args.Dockerfile
+7
-0
container/templates/planner.Dockerfile
container/templates/planner.Dockerfile
+98
-0
tests/profiler/test_planner_image_selection.py
tests/profiler/test_planner_image_selection.py
+143
-0
No files found.
.github/filters.yaml
View file @
a20b9dde
...
@@ -116,6 +116,7 @@ deploy:
...
@@ -116,6 +116,7 @@ deploy:
-
'
tests/deploy/**'
-
'
tests/deploy/**'
planner
:
planner
:
-
'
container/templates/planner.Dockerfile'
-
'
components/src/dynamo/planner/**'
-
'
components/src/dynamo/planner/**'
-
'
components/src/dynamo/global_planner/**'
-
'
components/src/dynamo/global_planner/**'
-
'
tests/planner/**'
-
'
tests/planner/**'
...
...
components/src/dynamo/planner/__main__.py
View file @
a20b9dde
...
@@ -84,13 +84,13 @@ def _parse_config() -> PlannerConfig:
...
@@ -84,13 +84,13 @@ def _parse_config() -> PlannerConfig:
@
dynamo_worker
()
@
dynamo_worker
()
async
def
worker
(
runtime
:
DistributedRuntime
):
async
def
worker
(
runtime
:
DistributedRuntime
,
config
:
PlannerConfig
):
config
=
_parse_config
()
await
init_planner
(
runtime
,
config
)
await
init_planner
(
runtime
,
config
)
def
main
():
def
main
():
asyncio
.
run
(
worker
())
# type: ignore[call-arg]
config
=
_parse_config
()
asyncio
.
run
(
worker
(
config
))
# type: ignore[call-arg]
if
__name__
==
"__main__"
:
if
__name__
==
"__main__"
:
...
...
components/src/dynamo/profiler/utils/config.py
View file @
a20b9dde
...
@@ -102,7 +102,7 @@ class DgdPlannerServiceConfig(BaseModel):
...
@@ -102,7 +102,7 @@ class DgdPlannerServiceConfig(BaseModel):
replicas
:
int
=
1
replicas
:
int
=
1
extraPodSpec
:
PodSpec
=
PodSpec
(
extraPodSpec
:
PodSpec
=
PodSpec
(
mainContainer
=
Container
(
mainContainer
=
Container
(
image
=
"my-registry/dynamo-
runtime
:my-tag"
,
# placeholder
image
=
"my-registry/dynamo-
planner
:my-tag"
,
# placeholder
workingDir
=
f
"
{
get_workspace_dir
()
}
/components/src/dynamo/planner"
,
workingDir
=
f
"
{
get_workspace_dir
()
}
/components/src/dynamo/planner"
,
command
=
[
"python3"
,
"-m"
,
"dynamo.planner"
],
command
=
[
"python3"
,
"-m"
,
"dynamo.planner"
],
args
=
[],
args
=
[],
...
@@ -397,7 +397,7 @@ def set_argument_value(args: list[str], arg_name: str, value: str) -> list[str]:
...
@@ -397,7 +397,7 @@ def set_argument_value(args: list[str], arg_name: str, value: str) -> list[str]:
def
update_image
(
config
:
dict
,
image
:
str
)
->
dict
:
def
update_image
(
config
:
dict
,
image
:
str
)
->
dict
:
"""Update container image for
all
DGD services
(frontend, planner, workers)
.
"""Update container image for
non-planner
DGD services.
This is a shared utility function used by all backend config modifiers.
This is a shared utility function used by all backend config modifiers.
...
@@ -410,8 +410,9 @@ def update_image(config: dict, image: str) -> dict:
...
@@ -410,8 +410,9 @@ def update_image(config: dict, image: str) -> dict:
"""
"""
cfg
=
Config
.
model_validate
(
config
)
cfg
=
Config
.
model_validate
(
config
)
# Update image for all services
for
service_name
,
service_config
in
cfg
.
spec
.
services
.
items
():
for
service_name
,
service_config
in
cfg
.
spec
.
services
.
items
():
if
getattr
(
service_config
,
"componentType"
,
None
)
==
"planner"
:
continue
if
service_config
.
extraPodSpec
and
service_config
.
extraPodSpec
.
mainContainer
:
if
service_config
.
extraPodSpec
and
service_config
.
extraPodSpec
.
mainContainer
:
service_config
.
extraPodSpec
.
mainContainer
.
image
=
image
service_config
.
extraPodSpec
.
mainContainer
.
image
=
image
logger
.
debug
(
f
"Updated image for
{
service_name
}
to
{
image
}
"
)
logger
.
debug
(
f
"Updated image for
{
service_name
}
to
{
image
}
"
)
...
...
components/src/dynamo/profiler/utils/dgd_generation.py
View file @
a20b9dde
...
@@ -28,6 +28,7 @@ from dynamo.planner.config.planner_config import PlannerConfig
...
@@ -28,6 +28,7 @@ from dynamo.planner.config.planner_config import PlannerConfig
from
dynamo.profiler.utils.config
import
DgdPlannerServiceConfig
,
set_argument_value
from
dynamo.profiler.utils.config
import
DgdPlannerServiceConfig
,
set_argument_value
from
dynamo.profiler.utils.profile_common
import
(
from
dynamo.profiler.utils.profile_common
import
(
ProfilerOperationalConfig
,
ProfilerOperationalConfig
,
derive_planner_image
,
is_mocker_enabled
,
is_mocker_enabled
,
is_planner_enabled
,
is_planner_enabled
,
needs_profile_data
,
needs_profile_data
,
...
@@ -180,8 +181,10 @@ def add_planner_to_config(
...
@@ -180,8 +181,10 @@ def add_planner_to_config(
planner_cfg
.
profile_results_dir
=
PROFILE_DATA_MOUNT
planner_cfg
.
profile_results_dir
=
PROFILE_DATA_MOUNT
planner_service
=
DgdPlannerServiceConfig
()
planner_service
=
DgdPlannerServiceConfig
()
if
planner_service
.
extraPodSpec
.
mainContainer
:
if
planner_service
.
extraPodSpec
.
mainContainer
and
dgdr
.
image
:
planner_service
.
extraPodSpec
.
mainContainer
.
image
=
dgdr
.
image
planner_service
.
extraPodSpec
.
mainContainer
.
image
=
derive_planner_image
(
dgdr
.
image
)
planner_dict
=
planner_service
.
model_dump
(
exclude_unset
=
False
)
planner_dict
=
planner_service
.
model_dump
(
exclude_unset
=
False
)
...
...
components/src/dynamo/profiler/utils/profile_common.py
View file @
a20b9dde
...
@@ -45,6 +45,29 @@ BACKEND_IMAGE_NAMES: dict[str, str] = {
...
@@ -45,6 +45,29 @@ BACKEND_IMAGE_NAMES: dict[str, str] = {
"trtllm"
:
"tensorrtllm-runtime"
,
"trtllm"
:
"tensorrtllm-runtime"
,
}
}
PLANNER_IMAGE_NAME
=
"dynamo-planner"
def
_replace_image_name
(
image_ref
:
str
,
new_name
:
str
)
->
str
:
"""Replace the image name component in a Docker image reference.
Preserves the registry path prefix and tag suffix, only replacing the
last ``/``-delimited component (before any ``:tag``).
"""
slash_idx
=
image_ref
.
rfind
(
"/"
)
prefix
=
image_ref
[:
slash_idx
+
1
]
if
slash_idx
>=
0
else
""
suffix
=
image_ref
[
slash_idx
+
1
:]
name_and_tag
,
has_digest
,
digest
=
suffix
.
partition
(
"@"
)
colon_idx
=
name_and_tag
.
rfind
(
":"
)
tag
=
name_and_tag
[
colon_idx
:]
if
colon_idx
>=
0
else
""
digest_suffix
=
f
"@
{
digest
}
"
if
has_digest
else
""
return
f
"
{
prefix
}{
new_name
}{
tag
}{
digest_suffix
}
"
def
derive_planner_image
(
profiler_image
:
str
)
->
str
:
"""Derive the planner service image from the profiler image reference."""
return
_replace_image_name
(
profiler_image
,
PLANNER_IMAGE_NAME
)
def
derive_backend_image
(
profiler_image
:
str
,
backend
:
str
)
->
str
:
def
derive_backend_image
(
profiler_image
:
str
,
backend
:
str
)
->
str
:
"""Derive the backend worker image from the profiler image.
"""Derive the backend worker image from the profiler image.
...
@@ -82,14 +105,7 @@ def derive_backend_image(profiler_image: str, backend: str) -> str:
...
@@ -82,14 +105,7 @@ def derive_backend_image(profiler_image: str, backend: str) -> str:
f
"Supported backends:
{
list
(
BACKEND_IMAGE_NAMES
.
keys
())
}
"
f
"Supported backends:
{
list
(
BACKEND_IMAGE_NAMES
.
keys
())
}
"
)
)
# Split off the last path component: "registry/path/name:tag" → "name:tag"
return
_replace_image_name
(
profiler_image
,
backend_image_name
)
slash_idx
=
profiler_image
.
rfind
(
"/"
)
prefix
=
profiler_image
[:
slash_idx
+
1
]
if
slash_idx
>=
0
else
""
suffix
=
profiler_image
[
slash_idx
+
1
:]
colon_idx
=
suffix
.
find
(
":"
)
tag
=
suffix
[
colon_idx
:]
if
colon_idx
>=
0
else
""
return
f
"
{
prefix
}{
backend_image_name
}{
tag
}
"
# ---------------------------------------------------------------------------
# ---------------------------------------------------------------------------
...
...
container/Dockerfile.template
View file @
a20b9dde
...
@@ -13,6 +13,10 @@
...
@@ -13,6 +13,10 @@
{% include "templates/dynamo_base.Dockerfile" %}
{% include "templates/dynamo_base.Dockerfile" %}
{% include "templates/wheel_builder.Dockerfile" %}
{% include "templates/wheel_builder.Dockerfile" %}
{% include "templates/frontend.Dockerfile" %}
{% include "templates/frontend.Dockerfile" %}
{% elif target == "planner" %}
{% include "templates/dynamo_base.Dockerfile" %}
{% include "templates/wheel_builder.Dockerfile" %}
{% include "templates/planner.Dockerfile" %}
{% elif target == "runtime" or target == "dev" or target == "local-dev" %}
{% elif target == "runtime" or target == "dev" or target == "local-dev" %}
{% include "templates/dynamo_base.Dockerfile" %}
{% include "templates/dynamo_base.Dockerfile" %}
{% include "templates/wheel_builder.Dockerfile" %}
{% include "templates/wheel_builder.Dockerfile" %}
...
...
container/context.yaml
View file @
a20b9dde
...
@@ -18,6 +18,10 @@ dynamo:
...
@@ -18,6 +18,10 @@ dynamo:
base_image_tag
:
25.11-cuda13.0-devel-ubuntu24.04
base_image_tag
:
25.11-cuda13.0-devel-ubuntu24.04
epp_image
:
us-central1-docker.pkg.dev/k8s-staging-images/gateway-api-inference-extension/epp:v0.5.1
epp_image
:
us-central1-docker.pkg.dev/k8s-staging-images/gateway-api-inference-extension/epp:v0.5.1
frontend_image
:
nvcr.io/nvidia/base/ubuntu:noble-20250619
frontend_image
:
nvcr.io/nvidia/base/ubuntu:noble-20250619
planner_build_image
:
python
planner_build_image_tag
:
3.12-slim
planner_runtime_image
:
nvcr.io/nvidia/distroless/python
planner_runtime_image_tag
:
3.12-v4.0.3
python_version
:
"
3.12"
python_version
:
"
3.12"
nats_version
:
v2.10.28
nats_version
:
v2.10.28
...
...
container/deps/README.md
View file @
a20b9dde
...
@@ -8,7 +8,7 @@ so each image only installs what it needs.
...
@@ -8,7 +8,7 @@ so each image only installs what it needs.
| File | Purpose |
| File | Purpose |
|------|---------|
|------|---------|
|
`requirements.common.txt`
| Core deps shared by all containers |
|
`requirements.common.txt`
| Core deps shared by all containers |
|
`requirements.planner.txt`
| Planner, profiler, global_planner
,
dep
loy util
s |
|
`requirements.planner.txt`
| Planner, profiler,
and
global_planner deps |
|
`requirements.frontend.txt`
| Frontend deps |
|
`requirements.frontend.txt`
| Frontend deps |
|
`requirements.vllm.txt`
| vLLM-specific deps |
|
`requirements.vllm.txt`
| vLLM-specific deps |
|
`requirements.benchmark.txt`
| Benchmark and profiling tools |
|
`requirements.benchmark.txt`
| Benchmark and profiling tools |
...
...
container/deps/requirements.planner.txt
View file @
a20b9dde
# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
# SPDX-License-Identifier: Apache-2.0
# Dependencies
for
planner, profiler, global_planner
, and deploy util
s.
# Dependencies
required by the
planner, profiler,
and
global_planner
service
s.
aiconfigurator[webapp] @ git+https://github.com/ai-dynamo/aiconfigurator.git@5d419f99d60fdae0d3911cba06a9b571f3b2965c
aiconfigurator[webapp] @ git+https://github.com/ai-dynamo/aiconfigurator.git@5d419f99d60fdae0d3911cba06a9b571f3b2965c
aiofiles<=25.1.0
aiofiles<=25.1.0
...
...
container/render.py
View file @
a20b9dde
...
@@ -140,6 +140,7 @@ def validate_args(args):
...
@@ -140,6 +140,7 @@ def validate_args(args):
"dev"
,
"dev"
,
"local-dev"
,
"local-dev"
,
"frontend"
,
"frontend"
,
"planner"
,
"wheel_builder"
,
"wheel_builder"
,
"base"
,
"base"
,
],
],
...
...
container/templates/args.Dockerfile
View file @
a20b9dde
...
@@ -81,6 +81,13 @@ ARG EPP_IMAGE={{ context.dynamo.epp_image }}
...
@@ -81,6 +81,13 @@ ARG EPP_IMAGE={{ context.dynamo.epp_image }}
ARG
FRONTEND_IMAGE={{ context.dynamo.frontend_image }}
ARG
FRONTEND_IMAGE={{ context.dynamo.frontend_image }}
{% endif %}
{% endif %}
{% if target == "planner" %}
ARG
PLANNER_BUILD_IMAGE={{ context.dynamo.planner_build_image }}
ARG
PLANNER_BUILD_IMAGE_TAG={{ context.dynamo.planner_build_image_tag }}
ARG
PLANNER_RUNTIME_IMAGE={{ context.dynamo.planner_runtime_image }}
ARG
PLANNER_RUNTIME_IMAGE_TAG={{ context.dynamo.planner_runtime_image_tag }}
{% endif %}
{% if framework == "vllm" -%}
{% if framework == "vllm" -%}
# Make sure to update the dependency version in pyproject.toml when updating this
# Make sure to update the dependency version in pyproject.toml when updating this
ARG
VLLM_REF={{ context[framework][device_key].vllm_ref }}
ARG
VLLM_REF={{ context[framework][device_key].vllm_ref }}
...
...
container/templates/planner.Dockerfile
0 → 100644
View file @
a20b9dde
{#
# SPDX-FileCopyrightText: Copyright (c) 2024-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#}
# === BEGIN templates/planner.Dockerfile ===
##############################################
########## Planner / Profiler image ##########
##############################################
# Standalone planner/profiler image:
# - install deps in a slim builder stage that has git/git-lfs available
# - ship only the runtime artifacts in a distroless final stage
FROM
${PLANNER_BUILD_IMAGE}:${PLANNER_BUILD_IMAGE_TAG} AS planner_builder
ARG
PYTHON_VERSION
# Install only the packages needed to resolve and install the planner runtime
# dependencies in the builder stage. git/git-lfs are only needed because
# aiconfigurator is currently installed from a Git URL with LFS-backed assets.
RUN
--mount
=
type
=
cache,target
=
/var/cache/apt,sharing
=
locked
\
apt-get update
-y
&&
\
DEBIAN_FRONTEND
=
noninteractive apt-get
install
-y
--no-install-recommends
\
ca-certificates
\
git
\
git-lfs
\
libgomp1
&&
\
apt-get clean
&&
\
rm
-rf
/var/lib/apt/lists/
*
# Create dynamo user with group 0 for OpenShift compatibility.
RUN
useradd
-m
-s
/bin/bash
-g
0 dynamo
\
&&
[
`
id
-u
dynamo
`
-eq
1000
]
\
&&
mkdir
-p
/home/dynamo/.cache /opt/dynamo /workspace
\
&&
chown
-R
dynamo:0 /home/dynamo /opt/dynamo /workspace
\
&&
chmod
-R
g+w /home/dynamo/.cache /opt/dynamo /workspace
ENV
HOME=/home/dynamo \
VIRTUAL_ENV=/opt/dynamo/venv \
PATH="/opt/dynamo/venv/bin:/usr/local/bin/etcd:/usr/local/bin:/bin" \
PYTHONPATH="/workspace"
WORKDIR
/workspace
COPY
--from=ghcr.io/astral-sh/uv:latest /uv /uvx /bin/
COPY
--from=dynamo_base /usr/local/bin/nats-server /usr/local/bin/nats-server
COPY
--from=dynamo_base /usr/local/bin/etcd /usr/local/bin/etcd
COPY
--chown=dynamo:0 --from=wheel_builder /opt/dynamo/dist/*.whl /opt/dynamo/wheelhouse/
USER
dynamo
RUN
--mount
=
type
=
cache,target
=
/home/dynamo/.cache/uv,uid
=
1000,gid
=
0,mode
=
0775
\
export
UV_CACHE_DIR
=
/home/dynamo/.cache/uv
&&
\
uv venv
${
VIRTUAL_ENV
}
--python
${
PYTHON_VERSION
}
# Install the local wheels and planner/profiler runtime dependencies before the
# repo copies so changes in tests/configs don't invalidate the dependency layer.
RUN
--mount
=
type
=
bind
,source
=
./container/deps/requirements.planner.txt,target
=
/tmp/requirements.planner.txt
\
--mount
=
type
=
cache,target
=
/home/dynamo/.cache/uv,uid
=
1000,gid
=
0,mode
=
0775
\
export
UV_CACHE_DIR
=
/home/dynamo/.cache/uv
UV_GIT_LFS
=
1
UV_HTTP_TIMEOUT
=
300
UV_HTTP_RETRIES
=
5
&&
\
uv pip
install
\
--requirement
/tmp/requirements.planner.txt
\
/opt/dynamo/wheelhouse/ai_dynamo_runtime
*
.whl
\
/opt/dynamo/wheelhouse/ai_dynamo
*
any.whl
# Copy only the subset of the repository needed for planner/profiler service
# startup and targeted planner/profiler unit tests.
COPY
--chmod=664 --chown=dynamo:0 pyproject.toml /workspace/pyproject.toml
COPY
--chmod=775 --chown=dynamo:0 tests /workspace/tests
COPY
--chmod=775 --chown=dynamo:0 components/src/dynamo/planner /workspace/components/src/dynamo/planner
COPY
--chmod=775 --chown=dynamo:0 components/src/dynamo/profiler /workspace/components/src/dynamo/profiler
COPY
--chmod=775 --chown=dynamo:0 components/src/dynamo/global_planner /workspace/components/src/dynamo/global_planner
COPY
--chmod=775 --chown=dynamo:0 deploy /workspace/deploy
COPY
--chmod=775 --chown=dynamo:0 examples /workspace/examples
FROM
${PLANNER_RUNTIME_IMAGE}:${PLANNER_RUNTIME_IMAGE_TAG} AS planner
COPY
--from=planner_builder /etc/group /etc/passwd /etc/
COPY
--from=planner_builder /bin/dash /usr/bin/sh
COPY
--from=planner_builder /bin/uv /bin/uvx /usr/local/bin/
COPY
--chown=1000:0 --from=planner_builder /home/dynamo /home/dynamo
COPY
--chown=1000:0 --from=planner_builder /opt/dynamo/venv /opt/dynamo/venv
COPY
--from=planner_builder /usr/lib/*-linux-gnu/libgomp.so.1* /opt/dynamo/lib/
COPY
--from=planner_builder /usr/local/bin/etcd /usr/local/bin/etcd
COPY
--from=planner_builder /usr/local/bin/nats-server /usr/local/bin/nats-server
COPY
--chown=1000:0 --from=planner_builder /workspace /workspace
ARG
DYNAMO_COMMIT_SHA
ENV
DYNAMO_COMMIT_SHA=${DYNAMO_COMMIT_SHA} \
HOME=/home/dynamo \
VIRTUAL_ENV=/opt/dynamo/venv \
LD_LIBRARY_PATH="/opt/dynamo/lib" \
PATH="/opt/dynamo/venv/bin:/usr/local/bin/etcd:/usr/local/bin:/bin" \
PYTHONPATH="/workspace"
WORKDIR
/workspace
USER
dynamo
CMD
[]
tests/profiler/test_planner_image_selection.py
0 → 100644
View file @
a20b9dde
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
import
sys
from
pathlib
import
Path
import
pytest
project_root
=
Path
(
__file__
).
parent
.
parent
.
parent
sys
.
path
.
insert
(
0
,
str
(
project_root
))
sys
.
path
.
insert
(
0
,
str
(
project_root
/
"components"
/
"src"
))
pytestmark
=
[
pytest
.
mark
.
pre_merge
,
pytest
.
mark
.
gpu_0
,
pytest
.
mark
.
unit
]
try
:
from
dynamo.profiler.utils.config
import
update_image
from
dynamo.profiler.utils.dgd_generation
import
add_planner_to_config
from
dynamo.profiler.utils.dgdr_v1beta1_types
import
(
DynamoGraphDeploymentRequestSpec
,
HardwareSpec
,
SLASpec
,
WorkloadSpec
,
)
from
dynamo.profiler.utils.profile_common
import
(
derive_backend_image
,
derive_planner_image
,
)
except
ImportError
as
e
:
pytest
.
skip
(
f
"Skip (missing dependency):
{
e
}
"
,
allow_module_level
=
True
)
def
_make_dgdr
(
image
:
str
)
->
DynamoGraphDeploymentRequestSpec
:
return
DynamoGraphDeploymentRequestSpec
(
model
=
"Qwen/Qwen3-32B"
,
backend
=
"trtllm"
,
image
=
image
,
hardware
=
HardwareSpec
(
gpuSku
=
"h200_sxm"
,
totalGpus
=
8
,
numGpusPerNode
=
8
),
workload
=
WorkloadSpec
(
isl
=
4000
,
osl
=
1000
),
sla
=
SLASpec
(
ttft
=
2000.0
,
itl
=
50.0
),
)
def
_base_dgd_config
(
image
:
str
)
->
dict
:
return
{
"metadata"
:
{
"name"
:
"test-dgd"
},
"spec"
:
{
"services"
:
{
"Frontend"
:
{
"replicas"
:
1
,
"extraPodSpec"
:
{
"mainContainer"
:
{
"image"
:
image
,
"args"
:
[
"serve"
],
}
},
}
}
},
}
@
pytest
.
mark
.
parametrize
(
(
"image"
,
"expected"
),
[
(
"nvcr.io/nvidia/ai-dynamo/dynamo-frontend:1.2.3"
,
"nvcr.io/nvidia/ai-dynamo/dynamo-planner:1.2.3"
,
),
(
"nvcr.io/nvidia/ai-dynamo/dynamo-frontend@sha256:deadbeef"
,
"nvcr.io/nvidia/ai-dynamo/dynamo-planner@sha256:deadbeef"
,
),
(
"nvcr.io/nvidia/ai-dynamo/dynamo-frontend:1.2.3@sha256:deadbeef"
,
"nvcr.io/nvidia/ai-dynamo/dynamo-planner:1.2.3@sha256:deadbeef"
,
),
],
)
def
test_derive_planner_image_preserves_registry_tag_and_digest
(
image
:
str
,
expected
:
str
):
assert
derive_planner_image
(
image
)
==
expected
@
pytest
.
mark
.
parametrize
(
(
"image"
,
"backend"
,
"expected"
),
[
(
"nvcr.io/nvidia/ai-dynamo/dynamo-frontend:1.2.3"
,
"vllm"
,
"nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.2.3"
,
),
(
"nvcr.io/nvidia/ai-dynamo/dynamo-frontend@sha256:deadbeef"
,
"sglang"
,
"nvcr.io/nvidia/ai-dynamo/sglang-runtime@sha256:deadbeef"
,
),
(
"nvcr.io/nvidia/ai-dynamo/dynamo-frontend:1.2.3@sha256:deadbeef"
,
"trtllm"
,
"nvcr.io/nvidia/ai-dynamo/tensorrtllm-runtime:1.2.3@sha256:deadbeef"
,
),
],
)
def
test_derive_backend_image_preserves_registry_tag_and_digest
(
image
:
str
,
backend
:
str
,
expected
:
str
):
assert
derive_backend_image
(
image
,
backend
)
==
expected
def
test_add_planner_to_config_uses_dynamo_planner_image
():
image
=
"nvcr.io/nvidia/ai-dynamo/dynamo-frontend:1.2.3"
dgdr
=
_make_dgdr
(
image
)
config
=
_base_dgd_config
(
image
)
add_planner_to_config
(
dgdr
,
config
)
planner_image
=
config
[
"spec"
][
"services"
][
"Planner"
][
"extraPodSpec"
][
"mainContainer"
][
"image"
]
assert
planner_image
==
"nvcr.io/nvidia/ai-dynamo/dynamo-planner:1.2.3"
def
test_update_image_does_not_overwrite_planner_service_image
():
profiler_image
=
"nvcr.io/nvidia/ai-dynamo/dynamo-frontend:1.2.3"
worker_image
=
"nvcr.io/nvidia/ai-dynamo/vllm-runtime:1.2.3"
dgdr
=
_make_dgdr
(
profiler_image
)
config
=
_base_dgd_config
(
profiler_image
)
add_planner_to_config
(
dgdr
,
config
)
updated
=
update_image
(
config
,
worker_image
)
assert
(
updated
[
"spec"
][
"services"
][
"Frontend"
][
"extraPodSpec"
][
"mainContainer"
][
"image"
]
==
worker_image
)
assert
(
updated
[
"spec"
][
"services"
][
"Planner"
][
"extraPodSpec"
][
"mainContainer"
][
"image"
]
==
"nvcr.io/nvidia/ai-dynamo/dynamo-planner:1.2.3"
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment