test_planner_image_selection.py 4.32 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# 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"
    )