Skip to content

Commit 9a89ffe

Browse files
committed
fix: respect user-provided imageName in Live* classes (BYOI)
Model validators were unconditionally overwriting imageName, and the mixin property was ignoring the stored value. Now validators only set defaults when imageName is not provided, and the no-op property is removed. Tests updated for new image naming scheme.
1 parent 3cb6e4e commit 9a89ffe

File tree

5 files changed

+30
-72
lines changed

5 files changed

+30
-72
lines changed
Lines changed: 12 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
# Ship serverless code as you write it. No builds, no deploys — just run.
2-
from typing import ClassVar
3-
42
from pydantic import model_validator
53

64
from .constants import (
@@ -27,29 +25,6 @@ class LiveServerlessMixin:
2725
runtime, not by the Docker image.
2826
"""
2927

30-
_image_type: ClassVar[str] = (
31-
"" # Override in subclasses: 'gpu', 'cpu', 'lb', 'lb-cpu'
32-
)
33-
_GPU_IMAGE_TYPES: ClassVar[frozenset[str]] = frozenset({"gpu", "lb"})
34-
35-
@property
36-
def _live_image(self) -> str:
37-
python_version = getattr(self, "python_version", None)
38-
if not python_version:
39-
if self._image_type in self._GPU_IMAGE_TYPES:
40-
python_version = GPU_BASE_IMAGE_PYTHON_VERSION
41-
else:
42-
python_version = local_python_version()
43-
return get_image_name(self._image_type, python_version)
44-
45-
@property
46-
def imageName(self):
47-
return self._live_image
48-
49-
@imageName.setter
50-
def imageName(self, value):
51-
pass
52-
5328
def _create_new_template(self) -> PodTemplate:
5429
"""Create template with dockerArgs for process injection."""
5530
template = super()._create_new_template() # type: ignore[misc]
@@ -66,54 +41,50 @@ def _configure_existing_template(self) -> None:
6641
class LiveServerless(LiveServerlessMixin, ServerlessEndpoint):
6742
"""GPU-only live serverless endpoint."""
6843

69-
_image_type: ClassVar[str] = "gpu"
70-
7144
@model_validator(mode="before")
7245
@classmethod
7346
def set_live_serverless_template(cls, data: dict):
7447
"""Set default GPU image for Live Serverless."""
75-
python_version = data.get("python_version") or GPU_BASE_IMAGE_PYTHON_VERSION
76-
data["imageName"] = get_image_name("gpu", python_version)
48+
if "imageName" not in data:
49+
python_version = data.get("python_version") or GPU_BASE_IMAGE_PYTHON_VERSION
50+
data["imageName"] = get_image_name("gpu", python_version)
7751
return data
7852

7953

8054
class CpuLiveServerless(LiveServerlessMixin, CpuServerlessEndpoint):
8155
"""CPU-only live serverless endpoint with automatic disk sizing."""
8256

83-
_image_type: ClassVar[str] = "cpu"
84-
8557
@model_validator(mode="before")
8658
@classmethod
8759
def set_live_serverless_template(cls, data: dict):
8860
"""Set default CPU image for Live Serverless."""
89-
python_version = data.get("python_version") or local_python_version()
90-
data["imageName"] = get_image_name("cpu", python_version)
61+
if "imageName" not in data:
62+
python_version = data.get("python_version") or local_python_version()
63+
data["imageName"] = get_image_name("cpu", python_version)
9164
return data
9265

9366

9467
class LiveLoadBalancer(LiveServerlessMixin, LoadBalancerSlsResource):
9568
"""Live load-balanced endpoint."""
9669

97-
_image_type: ClassVar[str] = "lb"
98-
9970
@model_validator(mode="before")
10071
@classmethod
10172
def set_live_lb_template(cls, data: dict):
10273
"""Set default image for Live Load-Balanced endpoint."""
103-
python_version = data.get("python_version") or GPU_BASE_IMAGE_PYTHON_VERSION
104-
data["imageName"] = get_image_name("lb", python_version)
74+
if "imageName" not in data:
75+
python_version = data.get("python_version") or GPU_BASE_IMAGE_PYTHON_VERSION
76+
data["imageName"] = get_image_name("lb", python_version)
10577
return data
10678

10779

10880
class CpuLiveLoadBalancer(LiveServerlessMixin, CpuLoadBalancerSlsResource):
10981
"""CPU-only live load-balanced endpoint."""
11082

111-
_image_type: ClassVar[str] = "lb-cpu"
112-
11383
@model_validator(mode="before")
11484
@classmethod
11585
def set_live_cpu_lb_template(cls, data: dict):
11686
"""Set default CPU image for Live Load-Balanced endpoint."""
117-
python_version = data.get("python_version") or local_python_version()
118-
data["imageName"] = get_image_name("lb-cpu", python_version)
87+
if "imageName" not in data:
88+
python_version = data.get("python_version") or local_python_version()
89+
data["imageName"] = get_image_name("lb-cpu", python_version)
11990
return data

tests/integration/test_cpu_disk_sizing.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ def test_live_serverless_cpu_integration(self):
129129
# 2. CPU utilities calculate minimum disk size
130130
# 3. Template creation with auto-sizing
131131
# 4. Validation passes
132-
assert live_serverless.imageName == "python:3.11-slim"
132+
assert "runpod/flash-cpu:" in live_serverless.imageName
133133
assert live_serverless.instanceIds == [
134134
CpuInstanceType.CPU5C_1_2,
135135
CpuInstanceType.CPU5C_2_4,
@@ -254,8 +254,8 @@ def test_live_serverless_image_defaults(self):
254254

255255
# Verify different base images are used
256256
assert gpu_live.imageName != cpu_live.imageName
257-
assert "pytorch" in gpu_live.imageName
258-
assert "python" in cpu_live.imageName
257+
assert "runpod/flash:" in gpu_live.imageName
258+
assert "runpod/flash-cpu:" in cpu_live.imageName
259259

260260
# Verify images can be overridden (BYOI)
261261
custom_gpu = LiveServerless(

tests/integration/test_lb_remote_execution.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,13 +114,13 @@ async def echo(message: str):
114114
# Verify resource is correctly configured
115115
# Note: name may have "-fb" appended by flash boot validator
116116
assert "test-live-api" in lb.name
117-
assert "pytorch" in lb.imageName # GPU base image
117+
assert "runpod/flash-lb:" in lb.imageName # GPU LB base image
118118
assert echo.__remote_config__["method"] == "POST"
119119

120120
def test_live_load_balancer_default_image(self):
121-
"""Test that LiveLoadBalancer uses GPU base image by default."""
121+
"""Test that LiveLoadBalancer uses GPU LB base image by default."""
122122
lb = LiveLoadBalancer(name="test-api")
123-
assert "pytorch" in lb.imageName
123+
assert "runpod/flash-lb:" in lb.imageName
124124

125125
def test_live_load_balancer_allows_custom_image(self):
126126
"""Test that LiveLoadBalancer allows user to set custom image (BYOI)."""

tests/unit/resources/test_live_load_balancer.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ def test_live_load_balancer_default_image_tag(self):
5050
def test_live_load_balancer_user_can_override_image(self):
5151
"""Test user can set custom imageName (BYOI)."""
5252
lb = LiveLoadBalancer(name="test-lb", imageName="custom/image:v1")
53-
# imageName property returns _live_image, setter is no-op
54-
assert lb.imageName is not None
53+
assert lb.imageName == "custom/image:v1"
5554

5655
def test_live_load_balancer_template_creation(self):
5756
"""Test LiveLoadBalancer creates proper template from imageName."""
@@ -211,8 +210,7 @@ def test_cpu_live_load_balancer_default_image_tag(self):
211210
def test_cpu_live_load_balancer_user_can_override_image(self):
212211
"""Test CpuLiveLoadBalancer allows user image override."""
213212
lb = CpuLiveLoadBalancer(name="test-lb", imageName="python:3.11-slim")
214-
# imageName property returns _live_image, setter is no-op
215-
assert lb.imageName is not None
213+
assert lb.imageName == "python:3.11-slim"
216214

217215
def test_cpu_live_load_balancer_defaults(self):
218216
"""Test CpuLiveLoadBalancer defaults to CPU3G_2_8."""

tests/unit/resources/test_live_serverless.py

Lines changed: 10 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,7 @@ def test_live_serverless_user_can_override_image(self):
3838
live_serverless = LiveServerless(
3939
name="test", imageName="nvidia/cuda:12.8.0-runtime-ubuntu22.04"
4040
)
41-
# imageName setter is a no-op, so value is always the computed _live_image
42-
# The model_validator sets data["imageName"] but the property overrides reads
43-
assert live_serverless.imageName is not None
41+
assert live_serverless.imageName == "nvidia/cuda:12.8.0-runtime-ubuntu22.04"
4442

4543
def test_live_serverless_with_custom_template(self):
4644
"""Test LiveServerless with custom template."""
@@ -96,8 +94,7 @@ def test_cpu_live_serverless_multiple_instances(self):
9694
def test_cpu_live_serverless_user_can_override_image(self):
9795
"""Test CpuLiveServerless allows user to set custom image."""
9896
live_serverless = CpuLiveServerless(name="test", imageName="python:3.11-slim")
99-
# imageName property returns _live_image, setter is no-op
100-
assert live_serverless.imageName is not None
97+
assert live_serverless.imageName == "python:3.11-slim"
10198

10299
def test_cpu_live_serverless_validation_failure(self):
103100
"""Test CpuLiveServerless validation fails with excessive disk size."""
@@ -194,23 +191,15 @@ def test_cpu_live_load_balancer_defaults(self):
194191
assert lb.template is not None
195192
assert lb.template.dockerArgs
196193

197-
def test_image_name_setter_ignored_gpu(self):
198-
"""Test LiveServerless imageName setter is ignored."""
199-
live_serverless = LiveServerless(name="test")
200-
original_image = live_serverless.imageName
201-
202-
live_serverless.imageName = "should-be-ignored"
203-
204-
assert live_serverless.imageName == original_image
205-
206-
def test_image_name_setter_ignored_cpu(self):
207-
"""Test CpuLiveServerless imageName setter is ignored."""
208-
live_serverless = CpuLiveServerless(name="test")
209-
original_image = live_serverless.imageName
210-
211-
live_serverless.imageName = "should-be-ignored"
194+
def test_live_serverless_byoi_gpu(self):
195+
"""Test LiveServerless respects user-provided imageName."""
196+
live_serverless = LiveServerless(name="test", imageName="custom/gpu:v1")
197+
assert live_serverless.imageName == "custom/gpu:v1"
212198

213-
assert live_serverless.imageName == original_image
199+
def test_live_serverless_byoi_cpu(self):
200+
"""Test CpuLiveServerless respects user-provided imageName."""
201+
live_serverless = CpuLiveServerless(name="test", imageName="custom/cpu:v1")
202+
assert live_serverless.imageName == "custom/cpu:v1"
214203

215204

216205
class TestLiveServerlessPythonVersion:

0 commit comments

Comments
 (0)