Skip to content

Commit fabb261

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 7229bb2 commit fabb261

5 files changed

Lines changed: 42 additions & 24 deletions

File tree

src/runpod_flash/core/resources/live_serverless.py

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
# Ship serverless code as you write it. No builds, no deploys -- just run.
22
from typing import Any, ClassVar
33

4+
# Ship serverless code as you write it. No builds, no deploys — just run.
45
from pydantic import model_validator
56

67
from .constants import (
7-
DEFAULT_PYTHON_VERSION,
8+
GPU_BASE_IMAGE_PYTHON_VERSION,
89
get_image_name,
10+
local_python_version,
911
)
1012
from .injection import build_injection_cmd
1113
from .load_balancer_sls_resource import (
@@ -77,46 +79,58 @@ def _configure_existing_template(self) -> None:
7779
class LiveServerless(LiveServerlessMixin, ServerlessEndpoint):
7880
"""GPU-only live serverless endpoint."""
7981
80-
_image_type: ClassVar[str] = "gpu"
81-
8282
@model_validator(mode="before")
8383
@classmethod
8484
def set_live_serverless_template(cls, data: dict):
8585
"""Default to the GPU Flash runtime image when none is supplied."""
8686
return _apply_default_live_image(data, "gpu")
87+
"""Set default GPU image for Live Serverless."""
88+
if "imageName" not in data:
89+
python_version = data.get("python_version") or GPU_BASE_IMAGE_PYTHON_VERSION
90+
data["imageName"] = get_image_name("gpu", python_version)
91+
return data
8792
8893
8994
class CpuLiveServerless(LiveServerlessMixin, CpuServerlessEndpoint):
9095
"""CPU-only live serverless endpoint with automatic disk sizing."""
9196
92-
_image_type: ClassVar[str] = "cpu"
93-
9497
@model_validator(mode="before")
9598
@classmethod
9699
def set_live_serverless_template(cls, data: dict):
97100
"""Default to the CPU Flash runtime image when none is supplied."""
98101
return _apply_default_live_image(data, "cpu")
102+
"""Set default CPU image for Live Serverless."""
103+
if "imageName" not in data:
104+
python_version = data.get("python_version") or local_python_version()
105+
data["imageName"] = get_image_name("cpu", python_version)
106+
return data
99107
100108
101109
class LiveLoadBalancer(LiveServerlessMixin, LoadBalancerSlsResource):
102110
"""Live load-balanced endpoint."""
103111
104-
_image_type: ClassVar[str] = "lb"
105-
106112
@model_validator(mode="before")
107113
@classmethod
108114
def set_live_lb_template(cls, data: dict):
109115
"""Default to the LB Flash runtime image when none is supplied."""
110116
return _apply_default_live_image(data, "lb")
117+
"""Set default image for Live Load-Balanced endpoint."""
118+
if "imageName" not in data:
119+
python_version = data.get("python_version") or GPU_BASE_IMAGE_PYTHON_VERSION
120+
data["imageName"] = get_image_name("lb", python_version)
121+
return data
111122
112123
113124
class CpuLiveLoadBalancer(LiveServerlessMixin, CpuLoadBalancerSlsResource):
114125
"""CPU-only live load-balanced endpoint."""
115126
116-
_image_type: ClassVar[str] = "lb-cpu"
117-
118127
@model_validator(mode="before")
119128
@classmethod
120129
def set_live_cpu_lb_template(cls, data: dict):
121130
"""Default to the CPU LB Flash runtime image when none is supplied."""
122131
return _apply_default_live_image(data, "lb-cpu")
132+
"""Set default CPU image for Live Load-Balanced endpoint."""
133+
if "imageName" not in data:
134+
python_version = data.get("python_version") or local_python_version()
135+
data["imageName"] = get_image_name("lb-cpu", python_version)
136+
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,
@@ -263,8 +263,8 @@ def test_live_serverless_image_defaults(self):
263263

264264
# Verify different base images are used
265265
assert gpu_live.imageName != cpu_live.imageName
266-
assert "pytorch" in gpu_live.imageName
267-
assert "python" in cpu_live.imageName
266+
assert "runpod/flash:" in gpu_live.imageName
267+
assert "runpod/flash-cpu:" in cpu_live.imageName
268268

269269
def test_live_serverless_image_override_via_constructor(self):
270270
"""Caller-supplied imageName overrides the Flash runtime default (AE-3153)."""

tests/integration/test_lb_remote_execution.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ 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_image_default_and_override(self):
@@ -133,9 +133,9 @@ def test_live_load_balancer_image_default_and_override(self):
133133
# same default (e.g. the override branch reverting to a no-op).
134134
assert default_lb.imageName != custom_lb.imageName
135135
def test_live_load_balancer_default_image(self):
136-
"""Test that LiveLoadBalancer uses GPU base image by default."""
136+
"""Test that LiveLoadBalancer uses GPU LB base image by default."""
137137
lb = LiveLoadBalancer(name="test-api")
138-
assert "pytorch" in lb.imageName
138+
assert "runpod/flash-lb:" in lb.imageName
139139

140140
def test_live_load_balancer_allows_custom_image(self):
141141
"""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."""
@@ -210,8 +209,7 @@ def test_cpu_live_load_balancer_default_image_tag(self):
210209
def test_cpu_live_load_balancer_user_can_override_image(self):
211210
"""Test CpuLiveLoadBalancer allows user image override."""
212211
lb = CpuLiveLoadBalancer(name="test-lb", imageName="python:3.11-slim")
213-
# imageName property returns _live_image, setter is no-op
214-
assert lb.imageName is not None
212+
assert lb.imageName == "python:3.11-slim"
215213

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

tests/unit/resources/test_live_serverless.py

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,9 +58,7 @@ def test_live_serverless_user_can_override_image(self):
5858
live_serverless = LiveServerless(
5959
name="test", imageName="nvidia/cuda:12.8.0-runtime-ubuntu22.04"
6060
)
61-
# imageName setter is a no-op, so value is always the computed _live_image
62-
# The model_validator sets data["imageName"] but the property overrides reads
63-
assert live_serverless.imageName is not None
61+
assert live_serverless.imageName == "nvidia/cuda:12.8.0-runtime-ubuntu22.04"
6462

6563
def test_live_serverless_with_custom_template(self):
6664
"""Test LiveServerless with custom template."""
@@ -133,8 +131,7 @@ def test_cpu_live_serverless_image_default_unchanged(self):
133131
def test_cpu_live_serverless_user_can_override_image(self):
134132
"""Test CpuLiveServerless allows user to set custom image."""
135133
live_serverless = CpuLiveServerless(name="test", imageName="python:3.11-slim")
136-
# imageName property returns _live_image, setter is no-op
137-
assert live_serverless.imageName is not None
134+
assert live_serverless.imageName == "python:3.11-slim"
138135

139136
def test_cpu_live_serverless_validation_failure(self):
140137
"""Test CpuLiveServerless validation fails with excessive disk size."""
@@ -265,6 +262,15 @@ def test_default_image_validator_passes_through_non_dict(self):
265262
original = LiveServerless(name="test", imageName="byo/image:v1")
266263
revalidated = LiveServerless.model_validate(original)
267264
assert revalidated.imageName == "byo/image:v1"
265+
def test_live_serverless_byoi_gpu(self):
266+
"""Test LiveServerless respects user-provided imageName."""
267+
live_serverless = LiveServerless(name="test", imageName="custom/gpu:v1")
268+
assert live_serverless.imageName == "custom/gpu:v1"
269+
270+
def test_live_serverless_byoi_cpu(self):
271+
"""Test CpuLiveServerless respects user-provided imageName."""
272+
live_serverless = CpuLiveServerless(name="test", imageName="custom/cpu:v1")
273+
assert live_serverless.imageName == "custom/cpu:v1"
268274

269275

270276
class TestLiveServerlessPythonVersion:

0 commit comments

Comments
 (0)