omnirun 0.1.0__py3-none-any.whl
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.
- omnirun/__init__.py +3 -0
- omnirun/backends/__init__.py +3 -0
- omnirun/backends/base.py +118 -0
- omnirun/backends/colab.py +605 -0
- omnirun/backends/jobdir.py +225 -0
- omnirun/backends/kaggle.py +605 -0
- omnirun/backends/local.py +193 -0
- omnirun/backends/marketplace.py +464 -0
- omnirun/backends/runpod.py +171 -0
- omnirun/backends/slurm.py +500 -0
- omnirun/backends/ssh.py +312 -0
- omnirun/backends/thunder.py +182 -0
- omnirun/backends/vast.py +201 -0
- omnirun/bootstrap.py +325 -0
- omnirun/chooser.py +219 -0
- omnirun/cli.py +893 -0
- omnirun/config.py +150 -0
- omnirun/daemon.py +486 -0
- omnirun/execlayer/__init__.py +3 -0
- omnirun/execlayer/base.py +97 -0
- omnirun/execlayer/local.py +81 -0
- omnirun/execlayer/ssh.py +259 -0
- omnirun/models.py +196 -0
- omnirun/queue.py +106 -0
- omnirun/repo.py +160 -0
- omnirun/store.py +107 -0
- omnirun-0.1.0.dist-info/METADATA +312 -0
- omnirun-0.1.0.dist-info/RECORD +30 -0
- omnirun-0.1.0.dist-info/WHEEL +4 -0
- omnirun-0.1.0.dist-info/entry_points.txt +2 -0
omnirun/__init__.py
ADDED
omnirun/backends/base.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Backend protocol and registry.
|
|
2
|
+
|
|
3
|
+
A backend turns a JobSpec into a running job somewhere and answers questions
|
|
4
|
+
about it afterwards. Implementations register with @register("type-name") and
|
|
5
|
+
are constructed from their BackendConfig section by config.load_backends().
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from abc import ABC, abstractmethod
|
|
11
|
+
from collections.abc import Iterator
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import TYPE_CHECKING, Any, Callable, TypeVar
|
|
14
|
+
|
|
15
|
+
from omnirun.models import JobHandle, JobSpec, Offer, ResourceSpec, StatusReport
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from omnirun.config import BackendConfig
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BackendError(RuntimeError):
|
|
22
|
+
"""Raised for backend-level failures with a user-actionable message."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Backend(ABC):
|
|
26
|
+
"""One configured execution target (a cluster, a machine, a provider account).
|
|
27
|
+
|
|
28
|
+
Rules for implementations:
|
|
29
|
+
- probe() must be fast (called speculatively with a ~10s budget), must not
|
|
30
|
+
mutate anything, and must NEVER raise: on error return a single not-fit
|
|
31
|
+
Offer carrying the error in unfit_reasons.
|
|
32
|
+
- submit() may take long (repo push, provisioning). It must either return a
|
|
33
|
+
working JobHandle or raise BackendError after cleaning up anything billable.
|
|
34
|
+
- status()/logs()/cancel()/pull_outputs() take the handle produced by submit
|
|
35
|
+
and must tolerate being called from a fresh process (no in-memory state).
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
#: registry type name, set by @register
|
|
39
|
+
type_name: str = ""
|
|
40
|
+
|
|
41
|
+
def __init__(self, name: str, config: "BackendConfig") -> None:
|
|
42
|
+
self.name = name # config key, e.g. "uni"
|
|
43
|
+
self.config = config
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def probe(self, res: ResourceSpec) -> list[Offer]: ...
|
|
47
|
+
|
|
48
|
+
@abstractmethod
|
|
49
|
+
def submit(self, spec: JobSpec, offer: Offer) -> JobHandle: ...
|
|
50
|
+
|
|
51
|
+
@abstractmethod
|
|
52
|
+
def status(self, handle: JobHandle) -> StatusReport: ...
|
|
53
|
+
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def logs(self, handle: JobHandle, follow: bool = False) -> Iterator[str]:
|
|
56
|
+
"""Yield log lines (stdout+stderr merged). follow=True tails until the
|
|
57
|
+
job reaches a terminal state."""
|
|
58
|
+
...
|
|
59
|
+
|
|
60
|
+
@abstractmethod
|
|
61
|
+
def cancel(self, handle: JobHandle) -> None: ...
|
|
62
|
+
|
|
63
|
+
@abstractmethod
|
|
64
|
+
def pull_outputs(self, handle: JobHandle, dest: Path) -> list[Path]:
|
|
65
|
+
"""Copy the job's collected outputs into dest; return copied paths."""
|
|
66
|
+
...
|
|
67
|
+
|
|
68
|
+
def gc(self, handle: JobHandle) -> None:
|
|
69
|
+
"""Release everything the job holds remotely (worktrees, instances).
|
|
70
|
+
|
|
71
|
+
Called by `omnirun gc` for terminal jobs. Default: nothing to do.
|
|
72
|
+
Backends with billable resources MUST override."""
|
|
73
|
+
|
|
74
|
+
def check(self) -> str:
|
|
75
|
+
"""Connectivity/config sanity check for `omnirun backends check`.
|
|
76
|
+
|
|
77
|
+
Return a short human 'ok: ...' description or raise BackendError."""
|
|
78
|
+
return "ok"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
_REGISTRY: dict[str, type[Backend]] = {}
|
|
82
|
+
|
|
83
|
+
_B = TypeVar("_B", bound=Backend)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def register(type_name: str) -> Callable[[type[_B]], type[_B]]:
|
|
87
|
+
def deco(cls: type[_B]) -> type[_B]:
|
|
88
|
+
cls.type_name = type_name
|
|
89
|
+
_REGISTRY[type_name] = cls
|
|
90
|
+
return cls
|
|
91
|
+
|
|
92
|
+
return deco
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def backend_class(type_name: str) -> type[Backend]:
|
|
96
|
+
# Import concrete backend modules lazily so optional deps (kaggle, colab)
|
|
97
|
+
# don't break unrelated commands.
|
|
98
|
+
if type_name not in _REGISTRY:
|
|
99
|
+
import importlib
|
|
100
|
+
|
|
101
|
+
mod = {
|
|
102
|
+
"local": "omnirun.backends.local",
|
|
103
|
+
"ssh": "omnirun.backends.ssh",
|
|
104
|
+
"slurm": "omnirun.backends.slurm",
|
|
105
|
+
"kaggle": "omnirun.backends.kaggle",
|
|
106
|
+
"colab": "omnirun.backends.colab",
|
|
107
|
+
"runpod": "omnirun.backends.runpod",
|
|
108
|
+
"vast": "omnirun.backends.vast",
|
|
109
|
+
"thunder": "omnirun.backends.thunder",
|
|
110
|
+
}.get(type_name)
|
|
111
|
+
if mod is None:
|
|
112
|
+
raise BackendError(f"unknown backend type {type_name!r}")
|
|
113
|
+
importlib.import_module(mod)
|
|
114
|
+
return _REGISTRY[type_name]
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def make_backend(name: str, config: Any) -> Backend:
|
|
118
|
+
return backend_class(config.type)(name, config)
|