podstack 1.2.2__tar.gz → 1.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {podstack-1.2.2 → podstack-1.3.0}/PKG-INFO +1 -1
- {podstack-1.2.2 → podstack-1.3.0}/podstack/__init__.py +7 -1
- {podstack-1.2.2 → podstack-1.3.0}/podstack/annotations.py +112 -4
- {podstack-1.2.2 → podstack-1.3.0}/podstack/gpu_runner.py +56 -5
- {podstack-1.2.2 → podstack-1.3.0}/podstack.egg-info/PKG-INFO +1 -1
- {podstack-1.2.2 → podstack-1.3.0}/pyproject.toml +1 -1
- {podstack-1.2.2 → podstack-1.3.0}/LICENSE +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/README.md +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack/client.py +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack/exceptions.py +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack/execution.py +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack/models.py +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack/notebook.py +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack/registry/__init__.py +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack/registry/client.py +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack/registry/exceptions.py +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack/registry/experiment.py +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack/registry/model.py +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack/registry/model_utils.py +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack.egg-info/SOURCES.txt +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack.egg-info/dependency_links.txt +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack.egg-info/requires.txt +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack.egg-info/top_level.txt +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack_gpu/__init__.py +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack_gpu/app.py +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack_gpu/exceptions.py +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack_gpu/image.py +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack_gpu/runner.py +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack_gpu/secret.py +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack_gpu/utils.py +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/podstack_gpu/volume.py +0 -0
- {podstack-1.2.2 → podstack-1.3.0}/setup.cfg +0 -0
|
@@ -54,7 +54,7 @@ Decorators:
|
|
|
54
54
|
...
|
|
55
55
|
"""
|
|
56
56
|
|
|
57
|
-
__version__ = "1.
|
|
57
|
+
__version__ = "1.3.0"
|
|
58
58
|
|
|
59
59
|
from .client import Client
|
|
60
60
|
from .notebook import Notebook, NotebookStatus
|
|
@@ -84,12 +84,15 @@ from .gpu_runner import (
|
|
|
84
84
|
GPURunner,
|
|
85
85
|
GPUExecutionResult,
|
|
86
86
|
run as run_on_gpu,
|
|
87
|
+
list_runners,
|
|
87
88
|
)
|
|
88
89
|
|
|
89
90
|
# Annotations module import
|
|
90
91
|
from . import annotations
|
|
91
92
|
from .annotations import (
|
|
92
93
|
gpu,
|
|
94
|
+
runner,
|
|
95
|
+
RunnerConfig,
|
|
93
96
|
environment,
|
|
94
97
|
auto_shutdown,
|
|
95
98
|
experiment,
|
|
@@ -207,9 +210,12 @@ __all__ = [
|
|
|
207
210
|
"GPURunner",
|
|
208
211
|
"GPUExecutionResult",
|
|
209
212
|
"run_on_gpu",
|
|
213
|
+
"list_runners",
|
|
210
214
|
# Annotations
|
|
211
215
|
"annotations",
|
|
212
216
|
"gpu",
|
|
217
|
+
"runner",
|
|
218
|
+
"RunnerConfig",
|
|
213
219
|
"environment",
|
|
214
220
|
"auto_shutdown",
|
|
215
221
|
"experiment",
|
|
@@ -51,6 +51,7 @@ from .gpu_runner import (
|
|
|
51
51
|
|
|
52
52
|
# Global state for configuration
|
|
53
53
|
_current_gpu_config: Dict[str, Any] = {}
|
|
54
|
+
_current_runner_config: Optional[Dict[str, Any]] = None
|
|
54
55
|
_current_environment: str = "pytorch"
|
|
55
56
|
_auto_shutdown_minutes: int = 60
|
|
56
57
|
_remote_execution_enabled: bool = True
|
|
@@ -176,8 +177,10 @@ class GPUConfig:
|
|
|
176
177
|
stripped = line.strip()
|
|
177
178
|
if stripped.startswith('@podstack.gpu') or stripped.startswith('@gpu'):
|
|
178
179
|
skip_decorator = True
|
|
179
|
-
# Count parentheses in this line
|
|
180
180
|
paren_depth += line.count('(') - line.count(')')
|
|
181
|
+
# Single-line decorator — reset immediately so def line isn't skipped
|
|
182
|
+
if paren_depth <= 0:
|
|
183
|
+
skip_decorator = False
|
|
181
184
|
continue
|
|
182
185
|
if skip_decorator:
|
|
183
186
|
# Continue skipping until parentheses are balanced
|
|
@@ -210,6 +213,23 @@ if __podstack_result__ is not None:
|
|
|
210
213
|
print(repr(__podstack_result__))
|
|
211
214
|
"""
|
|
212
215
|
|
|
216
|
+
# Check for active runner config from @podstack.runner() decorator
|
|
217
|
+
global _current_runner_config
|
|
218
|
+
runner_name = None
|
|
219
|
+
effective_pip = self.pip
|
|
220
|
+
effective_uv = self.uv
|
|
221
|
+
effective_conda = self.conda
|
|
222
|
+
if _current_runner_config is not None:
|
|
223
|
+
runner_name = _current_runner_config.get("image")
|
|
224
|
+
# Merge package lists from runner config
|
|
225
|
+
if _current_runner_config.get("pip") and not effective_pip:
|
|
226
|
+
effective_pip = _current_runner_config["pip"]
|
|
227
|
+
if _current_runner_config.get("uv") and not effective_uv:
|
|
228
|
+
effective_uv = _current_runner_config["uv"]
|
|
229
|
+
if _current_runner_config.get("conda") and not effective_conda:
|
|
230
|
+
effective_conda = _current_runner_config["conda"]
|
|
231
|
+
_current_runner_config = None # Clear after use
|
|
232
|
+
|
|
213
233
|
# Execute on remote GPU with improved error handling
|
|
214
234
|
# Auto-enable streaming in Jupyter notebooks for real-time output
|
|
215
235
|
try:
|
|
@@ -220,11 +240,12 @@ if __podstack_result__ is not None:
|
|
|
220
240
|
fraction=self.fraction,
|
|
221
241
|
timeout=self.timeout,
|
|
222
242
|
env=self.env,
|
|
223
|
-
pip=
|
|
224
|
-
uv=
|
|
225
|
-
conda=
|
|
243
|
+
pip=effective_pip,
|
|
244
|
+
uv=effective_uv,
|
|
245
|
+
conda=effective_conda,
|
|
226
246
|
requirements=self.requirements,
|
|
227
247
|
use_uv=self.use_uv,
|
|
248
|
+
runner=runner_name,
|
|
228
249
|
wait=True,
|
|
229
250
|
stream=None # Auto-detect: True in Jupyter, False otherwise
|
|
230
251
|
)
|
|
@@ -400,6 +421,91 @@ def gpu(
|
|
|
400
421
|
)
|
|
401
422
|
|
|
402
423
|
|
|
424
|
+
class RunnerConfig:
|
|
425
|
+
"""
|
|
426
|
+
Runner configuration decorator.
|
|
427
|
+
|
|
428
|
+
Used alongside @podstack.gpu() to specify which runner image and
|
|
429
|
+
additional packages to use.
|
|
430
|
+
|
|
431
|
+
Usage:
|
|
432
|
+
@podstack.gpu(type="L40S", count=1)
|
|
433
|
+
@podstack.runner(image="pytorch-2.3.0-cuda-12.1-py3.11")
|
|
434
|
+
def my_func():
|
|
435
|
+
...
|
|
436
|
+
"""
|
|
437
|
+
|
|
438
|
+
def __init__(
|
|
439
|
+
self,
|
|
440
|
+
image: str = None,
|
|
441
|
+
pip: Union[str, list] = None,
|
|
442
|
+
uv: Union[str, list] = None,
|
|
443
|
+
conda: Union[str, list] = None,
|
|
444
|
+
):
|
|
445
|
+
self.image = image
|
|
446
|
+
self.pip = pip
|
|
447
|
+
self.uv = uv
|
|
448
|
+
self.conda = conda
|
|
449
|
+
|
|
450
|
+
# Store in global state
|
|
451
|
+
global _current_runner_config
|
|
452
|
+
_current_runner_config = {
|
|
453
|
+
"image": image,
|
|
454
|
+
"pip": pip,
|
|
455
|
+
"uv": uv,
|
|
456
|
+
"conda": conda,
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
def __call__(self, func: Callable) -> Callable:
|
|
460
|
+
"""Decorator usage - stores runner config for the next gpu() call."""
|
|
461
|
+
@functools.wraps(func)
|
|
462
|
+
def wrapper(*args, **kwargs):
|
|
463
|
+
global _current_runner_config
|
|
464
|
+
_current_runner_config = {
|
|
465
|
+
"image": self.image,
|
|
466
|
+
"pip": self.pip,
|
|
467
|
+
"uv": self.uv,
|
|
468
|
+
"conda": self.conda,
|
|
469
|
+
}
|
|
470
|
+
return func(*args, **kwargs)
|
|
471
|
+
return wrapper
|
|
472
|
+
|
|
473
|
+
|
|
474
|
+
def runner(
|
|
475
|
+
image: str = None,
|
|
476
|
+
pip: Union[str, list] = None,
|
|
477
|
+
uv: Union[str, list] = None,
|
|
478
|
+
conda: Union[str, list] = None,
|
|
479
|
+
) -> RunnerConfig:
|
|
480
|
+
"""
|
|
481
|
+
Configure which runner image and packages to use for GPU execution.
|
|
482
|
+
|
|
483
|
+
Used alongside @podstack.gpu() to select a pre-built runner image.
|
|
484
|
+
|
|
485
|
+
Examples:
|
|
486
|
+
@podstack.gpu(type="L40S")
|
|
487
|
+
@podstack.runner(image="pytorch-2.3.0-cuda-12.1-py3.11")
|
|
488
|
+
def train():
|
|
489
|
+
import torch
|
|
490
|
+
...
|
|
491
|
+
|
|
492
|
+
@podstack.gpu(type="L40S")
|
|
493
|
+
@podstack.runner(image="pytorch-2.3.0-cuda-12.1-py3.11", pip=["transformers"])
|
|
494
|
+
def train_llm():
|
|
495
|
+
...
|
|
496
|
+
|
|
497
|
+
Args:
|
|
498
|
+
image: Runner name from podstack runners (e.g. "pytorch-2.3.0-cuda-12.1-py3.11")
|
|
499
|
+
pip: Additional pip packages - string "pkg1,pkg2" or list ["pkg1", "pkg2"]
|
|
500
|
+
uv: Additional UV packages (faster than pip) - string or list
|
|
501
|
+
conda: Additional conda packages - string or list
|
|
502
|
+
|
|
503
|
+
Returns:
|
|
504
|
+
RunnerConfig instance
|
|
505
|
+
"""
|
|
506
|
+
return RunnerConfig(image=image, pip=pip, uv=uv, conda=conda)
|
|
507
|
+
|
|
508
|
+
|
|
403
509
|
def environment(env: str = "pytorch") -> str:
|
|
404
510
|
"""
|
|
405
511
|
Set the runtime environment.
|
|
@@ -706,6 +812,8 @@ def get_auto_shutdown_minutes() -> int:
|
|
|
706
812
|
|
|
707
813
|
__all__ = [
|
|
708
814
|
"gpu",
|
|
815
|
+
"runner",
|
|
816
|
+
"RunnerConfig",
|
|
709
817
|
"environment",
|
|
710
818
|
"auto_shutdown",
|
|
711
819
|
"experiment",
|
|
@@ -268,7 +268,8 @@ class GPURunner:
|
|
|
268
268
|
uv: Union[str, list] = None,
|
|
269
269
|
conda: Union[str, list] = None,
|
|
270
270
|
requirements: str = None,
|
|
271
|
-
use_uv: bool = False
|
|
271
|
+
use_uv: bool = False,
|
|
272
|
+
runner: str = None
|
|
272
273
|
) -> str:
|
|
273
274
|
"""Build the @podstack annotation string."""
|
|
274
275
|
parts = [f"#@podstack gpu={gpu}"]
|
|
@@ -307,6 +308,10 @@ class GPURunner:
|
|
|
307
308
|
if use_uv:
|
|
308
309
|
parts.append("use_uv=true")
|
|
309
310
|
|
|
311
|
+
# Runner name (resolved by notebook-service via platform-service)
|
|
312
|
+
if runner:
|
|
313
|
+
parts.append(f"runner={runner}")
|
|
314
|
+
|
|
310
315
|
return " ".join(parts)
|
|
311
316
|
|
|
312
317
|
def submit(
|
|
@@ -322,7 +327,8 @@ class GPURunner:
|
|
|
322
327
|
conda: Union[str, list] = None,
|
|
323
328
|
requirements: str = None,
|
|
324
329
|
use_uv: bool = False,
|
|
325
|
-
add_annotation: bool = True
|
|
330
|
+
add_annotation: bool = True,
|
|
331
|
+
runner: str = None
|
|
326
332
|
) -> Dict[str, Any]:
|
|
327
333
|
"""
|
|
328
334
|
Submit code for GPU execution (non-blocking).
|
|
@@ -352,7 +358,7 @@ class GPURunner:
|
|
|
352
358
|
|
|
353
359
|
# Add annotation if not present
|
|
354
360
|
if add_annotation and not code.strip().startswith("#@podstack"):
|
|
355
|
-
annotation = self._build_annotation(gpu, count, fraction, timeout, env, pip, uv, conda, requirements, use_uv)
|
|
361
|
+
annotation = self._build_annotation(gpu, count, fraction, timeout, env, pip, uv, conda, requirements, use_uv, runner)
|
|
356
362
|
code = f"{annotation}\n\n{code}"
|
|
357
363
|
|
|
358
364
|
# Build installation code for packages
|
|
@@ -635,7 +641,8 @@ _stream_install(
|
|
|
635
641
|
max_retries: int = 3,
|
|
636
642
|
provisioning_timeout: int = 300,
|
|
637
643
|
cancel_on_timeout: bool = True,
|
|
638
|
-
stream: bool = None
|
|
644
|
+
stream: bool = None,
|
|
645
|
+
runner: str = None
|
|
639
646
|
) -> GPUExecutionResult:
|
|
640
647
|
"""
|
|
641
648
|
Execute code on GPU and optionally wait for completion.
|
|
@@ -670,7 +677,7 @@ _stream_install(
|
|
|
670
677
|
ValueError: If parameters are invalid
|
|
671
678
|
"""
|
|
672
679
|
# Submit the code
|
|
673
|
-
submission = self.submit(code, gpu, count, fraction, timeout, env, pip, uv, conda, requirements, use_uv)
|
|
680
|
+
submission = self.submit(code, gpu, count, fraction, timeout, env, pip, uv, conda, requirements, use_uv, runner=runner)
|
|
674
681
|
execution_id = submission.get("execution_id")
|
|
675
682
|
|
|
676
683
|
if not execution_id:
|
|
@@ -958,6 +965,35 @@ _stream_install(
|
|
|
958
965
|
except:
|
|
959
966
|
return {"status": "cancelled"}
|
|
960
967
|
|
|
968
|
+
def list_runners(self) -> list:
|
|
969
|
+
"""
|
|
970
|
+
List available GPU/CPU runners.
|
|
971
|
+
|
|
972
|
+
Returns:
|
|
973
|
+
List of runner dictionaries with name, type, description, image, libraries, scenarios.
|
|
974
|
+
"""
|
|
975
|
+
url = f"{self.api_url}/api/v1/runners"
|
|
976
|
+
|
|
977
|
+
with httpx.Client(timeout=self.timeout) as client:
|
|
978
|
+
try:
|
|
979
|
+
response = client.get(url, headers=self._get_headers())
|
|
980
|
+
except httpx.ConnectError as e:
|
|
981
|
+
raise ConnectionError(f"Failed to connect to {url}: {e}")
|
|
982
|
+
except httpx.TimeoutException:
|
|
983
|
+
raise TimeoutError(f"Request to {url} timed out")
|
|
984
|
+
|
|
985
|
+
if response.status_code >= 400:
|
|
986
|
+
try:
|
|
987
|
+
error_msg = response.json().get("error", response.text)
|
|
988
|
+
except Exception:
|
|
989
|
+
error_msg = response.text[:500] if response.text else f"HTTP {response.status_code}"
|
|
990
|
+
raise RuntimeError(f"Failed to list runners: {error_msg}")
|
|
991
|
+
|
|
992
|
+
try:
|
|
993
|
+
return response.json()
|
|
994
|
+
except Exception:
|
|
995
|
+
raise RuntimeError(f"Invalid JSON response: {response.text[:200]}")
|
|
996
|
+
|
|
961
997
|
def run_function(
|
|
962
998
|
self,
|
|
963
999
|
func: Callable,
|
|
@@ -1131,6 +1167,21 @@ def run(
|
|
|
1131
1167
|
)
|
|
1132
1168
|
|
|
1133
1169
|
|
|
1170
|
+
def list_runners() -> list:
|
|
1171
|
+
"""
|
|
1172
|
+
List available GPU/CPU runners.
|
|
1173
|
+
|
|
1174
|
+
Returns:
|
|
1175
|
+
List of runner dictionaries with name, type, description, image, libraries, scenarios.
|
|
1176
|
+
|
|
1177
|
+
Example:
|
|
1178
|
+
runners = podstack.gpu_runner.list_runners()
|
|
1179
|
+
for r in runners:
|
|
1180
|
+
print(f"{r['name']} ({r['type']}): {r['description']}")
|
|
1181
|
+
"""
|
|
1182
|
+
return get_runner().list_runners()
|
|
1183
|
+
|
|
1184
|
+
|
|
1134
1185
|
def stream_output(execution_id: str, show_output: bool = True) -> Iterator[Dict[str, Any]]:
|
|
1135
1186
|
"""
|
|
1136
1187
|
Stream real-time output from a running execution.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|