Skip to content

Commit 84c3560

Browse files
committed
fix: separate URL resolution for runpod and waverless environments
- Split _resolve_url into _resolve_runpod_url and _resolve_waverless_url - RunPod: replace $RUNPOD_POD_ID placeholder - Waverless: replace $WAVERLESS_POD_ID placeholder - job_get_url: also replace $ID with worker ID at config load time - job_done_url: keep $ID for runtime replacement with job ID - Prioritize waverless env detection over runpod
1 parent 83121bf commit 84c3560

2 files changed

Lines changed: 83 additions & 35 deletions

File tree

src/wavespeed/config.py

Lines changed: 47 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -83,22 +83,20 @@ def _detect_serverless_env() -> Optional[str]:
8383
The serverless environment type ("runpod", "waverless") or None
8484
if not running in a known serverless environment.
8585
"""
86-
# Check for RunPod environment
87-
if os.environ.get("RUNPOD_POD_ID"):
88-
return "runpod"
8986

9087
# Check for native Waverless environment
9188
if os.environ.get("WAVERLESS_POD_ID"):
9289
return "waverless"
9390

94-
return None
91+
# Check for RunPod environment
92+
if os.environ.get("RUNPOD_POD_ID"):
93+
return "runpod"
9594

95+
return None
9696

97-
def _resolve_url(url_template: Optional[str], pod_id: str) -> Optional[str]:
98-
"""Replace pod ID placeholder in URL template.
9997

100-
Note: Only $RUNPOD_POD_ID is replaced here. The $ID placeholder is
101-
replaced later at runtime with the actual job ID in http._handle_result.
98+
def _resolve_runpod_url(url_template: Optional[str], pod_id: str) -> Optional[str]:
99+
"""Replace pod ID placeholder in RunPod URL template.
102100
103101
Args:
104102
url_template: URL template with $RUNPOD_POD_ID placeholder.
@@ -112,6 +110,21 @@ def _resolve_url(url_template: Optional[str], pod_id: str) -> Optional[str]:
112110
return url_template.replace("$RUNPOD_POD_ID", pod_id)
113111

114112

113+
def _resolve_waverless_url(url_template: Optional[str], pod_id: str) -> Optional[str]:
114+
"""Replace pod ID placeholder in Waverless URL template.
115+
116+
Args:
117+
url_template: URL template with $WAVERLESS_POD_ID placeholder.
118+
pod_id: The worker/pod ID to substitute.
119+
120+
Returns:
121+
URL with $WAVERLESS_POD_ID placeholder replaced, or None if template is None.
122+
"""
123+
if not url_template:
124+
return None
125+
return url_template.replace("$WAVERLESS_POD_ID", pod_id)
126+
127+
115128
def _load_runpod_serverless_config() -> None:
116129
"""Load RunPod environment variables into serverless config."""
117130
# Worker identification
@@ -123,15 +136,23 @@ def _load_runpod_serverless_config() -> None:
123136
serverless.webhook_post_stream = os.environ.get("RUNPOD_WEBHOOK_POST_STREAM")
124137
serverless.webhook_ping = os.environ.get("RUNPOD_WEBHOOK_PING")
125138

126-
# Resolved API endpoints (with pod_id substituted)
127-
serverless.job_get_url = _resolve_url(serverless.webhook_get_job, serverless.pod_id)
128-
serverless.job_done_url = _resolve_url(
139+
# Resolved API endpoints (with $RUNPOD_POD_ID substituted)
140+
job_get_url = _resolve_runpod_url(serverless.webhook_get_job, serverless.pod_id)
141+
# job_get_url also needs $ID replaced with worker ID (like runpod-python)
142+
if job_get_url:
143+
job_get_url = job_get_url.replace("$ID", serverless.pod_id)
144+
serverless.job_get_url = job_get_url
145+
146+
# job_done_url keeps $ID for runtime replacement with job_id
147+
serverless.job_done_url = _resolve_runpod_url(
129148
serverless.webhook_post_output, serverless.pod_id
130149
)
131-
serverless.job_stream_url = _resolve_url(
150+
serverless.job_stream_url = _resolve_runpod_url(
132151
serverless.webhook_post_stream, serverless.pod_id
133152
)
134-
serverless.ping_url = _resolve_url(serverless.webhook_ping, serverless.pod_id)
153+
serverless.ping_url = _resolve_runpod_url(
154+
serverless.webhook_ping, serverless.pod_id
155+
)
135156

136157
# Authentication
137158
serverless.api_key = os.environ.get("RUNPOD_AI_API_KEY")
@@ -172,15 +193,23 @@ def _load_waverless_serverless_config() -> None:
172193
serverless.webhook_post_stream = os.environ.get("WAVERLESS_WEBHOOK_POST_STREAM")
173194
serverless.webhook_ping = os.environ.get("WAVERLESS_WEBHOOK_PING")
174195

175-
# Resolved API endpoints (with pod_id substituted)
176-
serverless.job_get_url = _resolve_url(serverless.webhook_get_job, serverless.pod_id)
177-
serverless.job_done_url = _resolve_url(
196+
# Resolved API endpoints (with $WAVERLESS_POD_ID substituted)
197+
job_get_url = _resolve_waverless_url(serverless.webhook_get_job, serverless.pod_id)
198+
# job_get_url also needs $ID replaced with worker ID (like runpod)
199+
if job_get_url:
200+
job_get_url = job_get_url.replace("$ID", serverless.pod_id)
201+
serverless.job_get_url = job_get_url
202+
203+
# job_done_url keeps $ID for runtime replacement with job_id
204+
serverless.job_done_url = _resolve_waverless_url(
178205
serverless.webhook_post_output, serverless.pod_id
179206
)
180-
serverless.job_stream_url = _resolve_url(
207+
serverless.job_stream_url = _resolve_waverless_url(
181208
serverless.webhook_post_stream, serverless.pod_id
182209
)
183-
serverless.ping_url = _resolve_url(serverless.webhook_ping, serverless.pod_id)
210+
serverless.ping_url = _resolve_waverless_url(
211+
serverless.webhook_ping, serverless.pod_id
212+
)
184213

185214
# Authentication
186215
serverless.api_key = os.environ.get("WAVERLESS_API_KEY")

tests/test_config.py

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,70 @@
22

33
import unittest
44

5-
from wavespeed.config import _resolve_url, serverless
5+
from wavespeed.config import _resolve_runpod_url, _resolve_waverless_url, serverless
66

77

8-
class TestResolveUrl(unittest.TestCase):
9-
"""Tests for the _resolve_url function."""
8+
class TestResolveRunpodUrl(unittest.TestCase):
9+
"""Tests for the _resolve_runpod_url function."""
1010

1111
def test_replaces_runpod_pod_id(self):
1212
"""Test that $RUNPOD_POD_ID is replaced with pod_id."""
1313
template = "https://api.runpod.ai/v2/endpoint/job-done/$RUNPOD_POD_ID"
14-
result = _resolve_url(template, "my-pod-123")
14+
result = _resolve_runpod_url(template, "my-pod-123")
1515
self.assertEqual(
1616
result, "https://api.runpod.ai/v2/endpoint/job-done/my-pod-123"
1717
)
1818

1919
def test_preserves_id_placeholder(self):
2020
"""Test that $ID is NOT replaced - it's for job ID at runtime."""
2121
template = "https://api.runpod.ai/v2/endpoint/job-done/$RUNPOD_POD_ID/$ID"
22-
result = _resolve_url(template, "my-pod-123")
23-
# $ID should remain as placeholder for job ID replacement later
22+
result = _resolve_runpod_url(template, "my-pod-123")
2423
self.assertEqual(
2524
result, "https://api.runpod.ai/v2/endpoint/job-done/my-pod-123/$ID"
2625
)
2726

2827
def test_handles_none_template(self):
2928
"""Test that None template returns None."""
30-
result = _resolve_url(None, "my-pod-123")
29+
result = _resolve_runpod_url(None, "my-pod-123")
3130
self.assertIsNone(result)
3231

33-
def test_handles_empty_template(self):
34-
"""Test that empty template returns None (falsy check)."""
35-
result = _resolve_url("", "my-pod-123")
32+
def test_no_placeholders(self):
33+
"""Test URL without any placeholders."""
34+
template = "https://api.example.com/endpoint"
35+
result = _resolve_runpod_url(template, "my-pod-123")
36+
self.assertEqual(result, "https://api.example.com/endpoint")
37+
38+
39+
class TestResolveWaverlessUrl(unittest.TestCase):
40+
"""Tests for the _resolve_waverless_url function."""
41+
42+
def test_replaces_waverless_pod_id_placeholder(self):
43+
"""Test that $WAVERLESS_POD_ID is replaced with pod_id."""
44+
template = "https://api.wavespeed.ai/v2/test/job-take/$WAVERLESS_POD_ID"
45+
result = _resolve_waverless_url(template, "my-pod-123")
46+
self.assertEqual(
47+
result, "https://api.wavespeed.ai/v2/test/job-take/my-pod-123"
48+
)
49+
50+
def test_preserves_id_placeholder(self):
51+
"""Test that $ID is NOT replaced - it's for job/worker ID at runtime."""
52+
template = "https://api.wavespeed.ai/v2/test/job-done/$WAVERLESS_POD_ID/$ID"
53+
result = _resolve_waverless_url(template, "my-pod-123")
54+
self.assertEqual(
55+
result, "https://api.wavespeed.ai/v2/test/job-done/my-pod-123/$ID"
56+
)
57+
58+
def test_handles_none_template(self):
59+
"""Test that None template returns None."""
60+
result = _resolve_waverless_url(None, "my-pod-123")
3661
self.assertIsNone(result)
3762

3863
def test_no_placeholders(self):
3964
"""Test URL without any placeholders."""
4065
template = "https://api.example.com/endpoint"
41-
result = _resolve_url(template, "my-pod-123")
66+
result = _resolve_waverless_url(template, "my-pod-123")
4267
self.assertEqual(result, "https://api.example.com/endpoint")
4368

44-
def test_multiple_pod_id_placeholders(self):
45-
"""Test multiple $RUNPOD_POD_ID placeholders are all replaced."""
46-
template = "https://api.runpod.ai/$RUNPOD_POD_ID/test/$RUNPOD_POD_ID"
47-
result = _resolve_url(template, "pod-456")
48-
self.assertEqual(result, "https://api.runpod.ai/pod-456/test/pod-456")
49-
5069

5170
class TestServerlessConfig(unittest.TestCase):
5271
"""Tests for serverless config loading."""

0 commit comments

Comments
 (0)