lightning-sdk 0.1.46__py3-none-any.whl → 0.1.48__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.
- lightning_sdk/__init__.py +1 -1
- lightning_sdk/api/job_api.py +37 -6
- lightning_sdk/api/lit_container_api.py +19 -0
- lightning_sdk/api/mmt_api.py +18 -1
- lightning_sdk/api/teamspace_api.py +19 -1
- lightning_sdk/api/utils.py +1 -1
- lightning_sdk/cli/delete.py +58 -0
- lightning_sdk/cli/entrypoint.py +8 -0
- lightning_sdk/cli/inspect.py +31 -0
- lightning_sdk/cli/job_and_mmt_action.py +37 -0
- lightning_sdk/cli/jobs_menu.py +57 -0
- lightning_sdk/cli/list.py +54 -0
- lightning_sdk/cli/mmts_menu.py +57 -0
- lightning_sdk/cli/run.py +12 -0
- lightning_sdk/cli/stop.py +37 -0
- lightning_sdk/cli/teamspace_menu.py +94 -0
- lightning_sdk/job/base.py +70 -2
- lightning_sdk/job/job.py +59 -9
- lightning_sdk/job/v1.py +28 -0
- lightning_sdk/job/v2.py +40 -4
- lightning_sdk/job/work.py +10 -1
- lightning_sdk/lightning_cloud/openapi/__init__.py +3 -0
- lightning_sdk/lightning_cloud/openapi/api/lit_registry_service_api.py +202 -0
- lightning_sdk/lightning_cloud/openapi/api/models_store_api.py +250 -8
- lightning_sdk/lightning_cloud/openapi/models/__init__.py +3 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_aws_direct_v1.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_delete_lit_repository_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_deployment_api.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_deployment_state.py +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_google_cloud_direct_v1.py +29 -27
- lightning_sdk/lightning_cloud/openapi/models/v1_job.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_model.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_resource_visibility.py +29 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +27 -53
- lightning_sdk/lightning_cloud/openapi/models/v1_vultr_direct_v1.py +55 -3
- lightning_sdk/lightning_cloud/openapi/models/version_default_body.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/versions_version_body.py +123 -0
- lightning_sdk/lightning_cloud/utils/data_connection.py +1 -1
- lightning_sdk/lit_container.py +57 -0
- lightning_sdk/machine.py +4 -0
- lightning_sdk/mmt/base.py +39 -1
- lightning_sdk/mmt/mmt.py +59 -14
- lightning_sdk/mmt/v1.py +30 -2
- lightning_sdk/mmt/v2.py +31 -2
- lightning_sdk/status.py +11 -7
- lightning_sdk/teamspace.py +51 -1
- {lightning_sdk-0.1.46.dist-info → lightning_sdk-0.1.48.dist-info}/METADATA +1 -1
- {lightning_sdk-0.1.46.dist-info → lightning_sdk-0.1.48.dist-info}/RECORD +52 -39
- {lightning_sdk-0.1.46.dist-info → lightning_sdk-0.1.48.dist-info}/LICENSE +0 -0
- {lightning_sdk-0.1.46.dist-info → lightning_sdk-0.1.48.dist-info}/WHEEL +0 -0
- {lightning_sdk-0.1.46.dist-info → lightning_sdk-0.1.48.dist-info}/entry_points.txt +0 -0
- {lightning_sdk-0.1.46.dist-info → lightning_sdk-0.1.48.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
from typing import Dict, List, Optional
|
|
2
|
+
|
|
3
|
+
from simple_term_menu import TerminalMenu
|
|
4
|
+
|
|
5
|
+
from lightning_sdk.api import OrgApi
|
|
6
|
+
from lightning_sdk.cli.exceptions import StudioCliError
|
|
7
|
+
from lightning_sdk.teamspace import Teamspace
|
|
8
|
+
from lightning_sdk.user import User
|
|
9
|
+
from lightning_sdk.utils.resolve import _get_authed_user
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class _TeamspacesMenu:
|
|
13
|
+
def _get_teamspace_from_interactive_menu(self, possible_teamspaces: Dict[str, Dict[str, str]]) -> Dict[str, str]:
|
|
14
|
+
teamspace_ids = sorted(possible_teamspaces.keys())
|
|
15
|
+
terminal_menu = self._prepare_terminal_menu_teamspaces([possible_teamspaces[k] for k in teamspace_ids])
|
|
16
|
+
terminal_menu.show()
|
|
17
|
+
|
|
18
|
+
selected_id = teamspace_ids[terminal_menu.chosen_menu_index]
|
|
19
|
+
return possible_teamspaces[selected_id]
|
|
20
|
+
|
|
21
|
+
def _get_teamspace_from_name(
|
|
22
|
+
self, teamspace: str, possible_teamspaces: Dict[str, Dict[str, str]]
|
|
23
|
+
) -> Dict[str, str]:
|
|
24
|
+
owner, name = teamspace.split("/", maxsplit=1)
|
|
25
|
+
for _, ts in possible_teamspaces.items():
|
|
26
|
+
if ts["name"] == name and (ts["user"] == owner or ts["org"] == owner):
|
|
27
|
+
return ts
|
|
28
|
+
|
|
29
|
+
print("Could not find Teamspace {teamspace}, please select it from the list:")
|
|
30
|
+
return self._get_teamspace_from_interactive_menu(possible_teamspaces)
|
|
31
|
+
|
|
32
|
+
@staticmethod
|
|
33
|
+
def _prepare_terminal_menu_teamspaces(
|
|
34
|
+
possible_teamspaces: List[Dict[str, str]], title: Optional[str] = None
|
|
35
|
+
) -> TerminalMenu:
|
|
36
|
+
if title is None:
|
|
37
|
+
title = "Please select a Teamspace of the following:"
|
|
38
|
+
|
|
39
|
+
return TerminalMenu(
|
|
40
|
+
[f"{t['user'] or t['org']}/{t['name']}" for t in possible_teamspaces], title=title, clear_menu_on_exit=True
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
@staticmethod
|
|
44
|
+
def _get_possible_teamspaces(user: User, is_owner: bool = True) -> Dict[str, Dict[str, str]]:
|
|
45
|
+
org_api = OrgApi()
|
|
46
|
+
user_api = user._user_api
|
|
47
|
+
|
|
48
|
+
user_api._get_organizations_for_authed_user()
|
|
49
|
+
memberships = user_api._get_all_teamspace_memberships(user_id=user.id)
|
|
50
|
+
|
|
51
|
+
teamspaces = {}
|
|
52
|
+
# get all teamspace memberships
|
|
53
|
+
for membership in memberships:
|
|
54
|
+
teamspace_id = membership.project_id
|
|
55
|
+
teamspace_name = membership.name
|
|
56
|
+
|
|
57
|
+
# get organization if necessary
|
|
58
|
+
if membership.owner_type == "organization":
|
|
59
|
+
org_name = org_api._get_org_by_id(membership.owner_id).name
|
|
60
|
+
user_name = None
|
|
61
|
+
else:
|
|
62
|
+
org_name = None
|
|
63
|
+
|
|
64
|
+
# don't do a request if not necessary
|
|
65
|
+
if membership.owner_id == user.id:
|
|
66
|
+
user_name = user.name
|
|
67
|
+
else:
|
|
68
|
+
user_name = user_api._get_user_by_id(membership.owner_id).username
|
|
69
|
+
|
|
70
|
+
teamspaces[teamspace_id] = {"user": user_name, "org": org_name, "name": teamspace_name}
|
|
71
|
+
|
|
72
|
+
return teamspaces
|
|
73
|
+
|
|
74
|
+
def _resolve_teamspace(self, teamspace: Optional[str]) -> Teamspace:
|
|
75
|
+
try:
|
|
76
|
+
user = _get_authed_user()
|
|
77
|
+
|
|
78
|
+
possible_teamspaces = self._get_possible_teamspaces(user)
|
|
79
|
+
if teamspace is None:
|
|
80
|
+
teamspace_dict = self._get_teamspace_from_interactive_menu(possible_teamspaces=possible_teamspaces)
|
|
81
|
+
else:
|
|
82
|
+
teamspace_dict = self._get_teamspace_from_name(
|
|
83
|
+
teamspace=teamspace, possible_teamspaces=possible_teamspaces
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
return Teamspace(**teamspace_dict)
|
|
87
|
+
except KeyboardInterrupt:
|
|
88
|
+
raise KeyboardInterrupt from None
|
|
89
|
+
|
|
90
|
+
except Exception as e:
|
|
91
|
+
raise StudioCliError(
|
|
92
|
+
f"Could not find the given Teamspace {teamspace}. "
|
|
93
|
+
"Please contact Lightning AI directly to resolve this issue."
|
|
94
|
+
) from e
|
lightning_sdk/job/base.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from abc import ABC, abstractmethod
|
|
2
|
-
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
|
|
2
|
+
from typing import TYPE_CHECKING, Any, Dict, Optional, TypedDict, Union
|
|
3
3
|
|
|
4
|
+
from lightning_sdk.api.utils import _get_cloud_url
|
|
4
5
|
from lightning_sdk.utils.resolve import _resolve_deprecated_cluster, _resolve_teamspace
|
|
5
6
|
|
|
6
7
|
if TYPE_CHECKING:
|
|
@@ -12,6 +13,19 @@ if TYPE_CHECKING:
|
|
|
12
13
|
from lightning_sdk.user import User
|
|
13
14
|
|
|
14
15
|
|
|
16
|
+
class MachineDict(TypedDict):
|
|
17
|
+
name: str
|
|
18
|
+
status: "Status"
|
|
19
|
+
machine: "Machine"
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class JobDict(MachineDict):
|
|
23
|
+
command: str
|
|
24
|
+
teamspace: str
|
|
25
|
+
studio: Optional[str]
|
|
26
|
+
image: Optional[str]
|
|
27
|
+
|
|
28
|
+
|
|
15
29
|
class _BaseJob(ABC):
|
|
16
30
|
"""Base interface to all job types."""
|
|
17
31
|
|
|
@@ -65,6 +79,7 @@ class _BaseJob(ABC):
|
|
|
65
79
|
cloud_account_auth: bool = False,
|
|
66
80
|
artifacts_local: Optional[str] = None,
|
|
67
81
|
artifacts_remote: Optional[str] = None,
|
|
82
|
+
entrypoint: str = "sh -c",
|
|
68
83
|
cluster: Optional[str] = None, # deprecated in favor of cloud_account
|
|
69
84
|
) -> "_BaseJob":
|
|
70
85
|
"""Run async workloads using a docker image or a compute environment from your studio.
|
|
@@ -98,6 +113,10 @@ class _BaseJob(ABC):
|
|
|
98
113
|
within it.
|
|
99
114
|
Note that the connection needs to be added to the teamspace already in order for it to be found.
|
|
100
115
|
Only supported for jobs with a docker image compute environment.
|
|
116
|
+
entrypoint: The entrypoint of your docker container. Defaults to `sh -c` which
|
|
117
|
+
just runs the provided command in a standard shell.
|
|
118
|
+
To use the pre-defined entrypoint of the provided image, set this to an empty string.
|
|
119
|
+
Only applicable when submitting docker jobs.
|
|
101
120
|
"""
|
|
102
121
|
from lightning_sdk.studio import Studio
|
|
103
122
|
|
|
@@ -145,6 +164,9 @@ class _BaseJob(ABC):
|
|
|
145
164
|
"Other jobs will automatically persist artifacts to the teamspace distributed filesystem."
|
|
146
165
|
)
|
|
147
166
|
|
|
167
|
+
if entrypoint != "sh -c":
|
|
168
|
+
raise ValueError("Specifying the entrypoint has no effect for jobs with Studio envs.")
|
|
169
|
+
|
|
148
170
|
else:
|
|
149
171
|
if studio is not None:
|
|
150
172
|
raise RuntimeError(
|
|
@@ -174,6 +196,7 @@ class _BaseJob(ABC):
|
|
|
174
196
|
cloud_account_auth=cloud_account_auth,
|
|
175
197
|
artifacts_local=artifacts_local,
|
|
176
198
|
artifacts_remote=artifacts_remote,
|
|
199
|
+
entrypoint=entrypoint,
|
|
177
200
|
)
|
|
178
201
|
|
|
179
202
|
@abstractmethod
|
|
@@ -190,6 +213,7 @@ class _BaseJob(ABC):
|
|
|
190
213
|
cloud_account_auth: bool = False,
|
|
191
214
|
artifacts_local: Optional[str] = None,
|
|
192
215
|
artifacts_remote: Optional[str] = None,
|
|
216
|
+
entrypoint: str = "sh -c",
|
|
193
217
|
) -> "_BaseJob":
|
|
194
218
|
"""Submit a new job to the Lightning AI platform.
|
|
195
219
|
|
|
@@ -218,6 +242,9 @@ class _BaseJob(ABC):
|
|
|
218
242
|
within it.
|
|
219
243
|
Note that the connection needs to be added to the teamspace already in order for it to be found.
|
|
220
244
|
Only supported for jobs with a docker image compute environment.
|
|
245
|
+
entrypoint: The entrypoint of your docker container. Defaults to sh -c.
|
|
246
|
+
To use the pre-defined entrypoint of the provided image, set this to an empty string.
|
|
247
|
+
Only applicable when submitting docker jobs.
|
|
221
248
|
"""
|
|
222
249
|
|
|
223
250
|
@abstractmethod
|
|
@@ -278,10 +305,51 @@ class _BaseJob(ABC):
|
|
|
278
305
|
def logs(self) -> str:
|
|
279
306
|
"""The logs of the job."""
|
|
280
307
|
|
|
308
|
+
@property
|
|
309
|
+
@abstractmethod
|
|
310
|
+
def image(self) -> Optional[str]:
|
|
311
|
+
"""The image used to submit the job."""
|
|
312
|
+
|
|
313
|
+
@property
|
|
314
|
+
@abstractmethod
|
|
315
|
+
def studio(self) -> Optional["Studio"]:
|
|
316
|
+
"""The studio used to submit the job."""
|
|
317
|
+
|
|
318
|
+
@property
|
|
319
|
+
@abstractmethod
|
|
320
|
+
def command(self) -> str:
|
|
321
|
+
"""The command the job is running."""
|
|
322
|
+
|
|
323
|
+
def dict(self) -> JobDict:
|
|
324
|
+
"""Dict representation of this job."""
|
|
325
|
+
studio = self.studio
|
|
326
|
+
|
|
327
|
+
return {
|
|
328
|
+
"name": self.name,
|
|
329
|
+
"teamspace": f"{self.teamspace.owner.name}/{self.teamspace.name}",
|
|
330
|
+
"studio": studio.name if studio else None,
|
|
331
|
+
"image": self.image,
|
|
332
|
+
"command": self.command,
|
|
333
|
+
"status": self.status,
|
|
334
|
+
"machine": self.machine,
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
def json(self) -> str:
|
|
338
|
+
"""JSON representation of this job."""
|
|
339
|
+
import json
|
|
340
|
+
|
|
341
|
+
return json.dumps(self.dict(), indent=4, sort_keys=True, default=str)
|
|
342
|
+
|
|
281
343
|
@property
|
|
282
344
|
def link(self) -> str:
|
|
283
345
|
"""A link to view the current job in the UI."""
|
|
284
|
-
|
|
346
|
+
studio_name = self._job_api.get_studio_name(self._guaranteed_job)
|
|
347
|
+
if not studio_name:
|
|
348
|
+
raise RuntimeError("Cannot extract studio name from job")
|
|
349
|
+
return (
|
|
350
|
+
f"{_get_cloud_url()}/{self.teamspace.owner.name}/{self.teamspace.name}/studios/"
|
|
351
|
+
f"{studio_name}/app?app_id=jobs&job_name={self.name}"
|
|
352
|
+
)
|
|
285
353
|
|
|
286
354
|
@property
|
|
287
355
|
def _guaranteed_job(self) -> Any:
|
lightning_sdk/job/job.py
CHANGED
|
@@ -50,15 +50,40 @@ class Job(_BaseJob):
|
|
|
50
50
|
user: the name of the user owning the :param`teamspace`
|
|
51
51
|
in case it is owned directly by a user instead of an org.
|
|
52
52
|
"""
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
self.
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
53
|
+
from lightning_sdk.lightning_cloud.openapi.rest import ApiException
|
|
54
|
+
|
|
55
|
+
if _has_jobs_v2() and not self._force_v1:
|
|
56
|
+
# try with v2 and fall back to v1
|
|
57
|
+
try:
|
|
58
|
+
job = _JobV2(
|
|
59
|
+
name=name,
|
|
60
|
+
teamspace=teamspace,
|
|
61
|
+
org=org,
|
|
62
|
+
user=user,
|
|
63
|
+
_fetch_job=_fetch_job,
|
|
64
|
+
)
|
|
65
|
+
except ApiException as e:
|
|
66
|
+
try:
|
|
67
|
+
job = _JobV1(
|
|
68
|
+
name=name,
|
|
69
|
+
teamspace=teamspace,
|
|
70
|
+
org=org,
|
|
71
|
+
user=user,
|
|
72
|
+
_fetch_job=_fetch_job,
|
|
73
|
+
)
|
|
74
|
+
except ApiException:
|
|
75
|
+
raise e from e
|
|
76
|
+
|
|
77
|
+
else:
|
|
78
|
+
job = _JobV1(
|
|
79
|
+
name=name,
|
|
80
|
+
teamspace=teamspace,
|
|
81
|
+
org=org,
|
|
82
|
+
user=user,
|
|
83
|
+
_fetch_job=_fetch_job,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
self._internal_job = job
|
|
62
87
|
|
|
63
88
|
@classmethod
|
|
64
89
|
def run(
|
|
@@ -78,6 +103,7 @@ class Job(_BaseJob):
|
|
|
78
103
|
cloud_account_auth: bool = False,
|
|
79
104
|
artifacts_local: Optional[str] = None,
|
|
80
105
|
artifacts_remote: Optional[str] = None,
|
|
106
|
+
entrypoint: str = "sh -c",
|
|
81
107
|
cluster: Optional[str] = None, # deprecated in favor of cloud_account
|
|
82
108
|
) -> "Job":
|
|
83
109
|
"""Run async workloads using a docker image or a compute environment from your studio.
|
|
@@ -111,6 +137,10 @@ class Job(_BaseJob):
|
|
|
111
137
|
within it.
|
|
112
138
|
Note that the connection needs to be added to the teamspace already in order for it to be found.
|
|
113
139
|
Only supported for jobs with a docker image compute environment.
|
|
140
|
+
entrypoint: The entrypoint of your docker container. Defaults to `sh -c` which
|
|
141
|
+
just runs the provided command in a standard shell.
|
|
142
|
+
To use the pre-defined entrypoint of the provided image, set this to an empty string.
|
|
143
|
+
Only applicable when submitting docker jobs.
|
|
114
144
|
"""
|
|
115
145
|
ret_val = super().run(
|
|
116
146
|
name=name,
|
|
@@ -128,6 +158,7 @@ class Job(_BaseJob):
|
|
|
128
158
|
cloud_account_auth=cloud_account_auth,
|
|
129
159
|
artifacts_local=artifacts_local,
|
|
130
160
|
artifacts_remote=artifacts_remote,
|
|
161
|
+
entrypoint=entrypoint,
|
|
131
162
|
cluster=cluster,
|
|
132
163
|
)
|
|
133
164
|
# required for typing with "Job"
|
|
@@ -149,6 +180,7 @@ class Job(_BaseJob):
|
|
|
149
180
|
cloud_account_auth: bool = False,
|
|
150
181
|
artifacts_local: Optional[str] = None,
|
|
151
182
|
artifacts_remote: Optional[str] = None,
|
|
183
|
+
entrypoint: str = "sh -c",
|
|
152
184
|
) -> "Job":
|
|
153
185
|
"""Submit a new job to the Lightning AI platform.
|
|
154
186
|
|
|
@@ -177,6 +209,9 @@ class Job(_BaseJob):
|
|
|
177
209
|
within it.
|
|
178
210
|
Note that the connection needs to be added to the teamspace already in order for it to be found.
|
|
179
211
|
Only supported for jobs with a docker image compute environment.
|
|
212
|
+
entrypoint: The entrypoint of your docker container. Defaults to sh -c.
|
|
213
|
+
To use the pre-defined entrypoint of the provided image, set this to an empty string.
|
|
214
|
+
Only applicable when submitting docker jobs.
|
|
180
215
|
"""
|
|
181
216
|
self._job = self._internal_job._submit(
|
|
182
217
|
machine=machine,
|
|
@@ -245,6 +280,21 @@ class Job(_BaseJob):
|
|
|
245
280
|
"""The teamspace the job is part of."""
|
|
246
281
|
return self._internal_job._teamspace
|
|
247
282
|
|
|
283
|
+
@property
|
|
284
|
+
def studio(self) -> Optional["Studio"]:
|
|
285
|
+
"""The studio used to submit the job."""
|
|
286
|
+
return self._internal_job.studio
|
|
287
|
+
|
|
288
|
+
@property
|
|
289
|
+
def image(self) -> Optional[str]:
|
|
290
|
+
"""The image used to submit the job."""
|
|
291
|
+
return self._internal_job.image
|
|
292
|
+
|
|
293
|
+
@property
|
|
294
|
+
def command(self) -> str:
|
|
295
|
+
"""The command the job is running."""
|
|
296
|
+
return self._internal_job.command
|
|
297
|
+
|
|
248
298
|
@property
|
|
249
299
|
def logs(self) -> str:
|
|
250
300
|
"""The logs of the job."""
|
lightning_sdk/job/v1.py
CHANGED
|
@@ -100,6 +100,7 @@ class _JobV1(_BaseJob):
|
|
|
100
100
|
cloud_account_auth: bool = False,
|
|
101
101
|
artifacts_local: Optional[str] = None,
|
|
102
102
|
artifacts_remote: Optional[str] = None,
|
|
103
|
+
entrypoint: str = "sh -c",
|
|
103
104
|
) -> "_JobV1":
|
|
104
105
|
"""Submit a job to run on a machine.
|
|
105
106
|
|
|
@@ -115,6 +116,10 @@ class _JobV1(_BaseJob):
|
|
|
115
116
|
cloud_account_auth: Whether to use cloud account authentication for the job (not supported).
|
|
116
117
|
artifacts_local: The local path for persisting artifacts (not supported).
|
|
117
118
|
artifacts_remote: The remote path for persisting artifacts (not supported).
|
|
119
|
+
entrypoint: The entrypoint of your docker container (not supported).
|
|
120
|
+
Defaults to `sh -c` which just runs the provided command in a standard shell.
|
|
121
|
+
To use the pre-defined entrypoint of the provided image, set this to an empty string.
|
|
122
|
+
Only applicable when submitting docker jobs.
|
|
118
123
|
|
|
119
124
|
Returns:
|
|
120
125
|
The submitted job.
|
|
@@ -132,6 +137,10 @@ class _JobV1(_BaseJob):
|
|
|
132
137
|
raise ValueError("Environment variables are not supported for submitting jobs")
|
|
133
138
|
if command is None:
|
|
134
139
|
raise ValueError("Command is required for submitting jobs")
|
|
140
|
+
|
|
141
|
+
if entrypoint != "sh -c":
|
|
142
|
+
raise ValueError("Specifying the entrypoint is not yet supported with jobs")
|
|
143
|
+
|
|
135
144
|
# TODO: add support for empty names (will give an empty string)
|
|
136
145
|
_submitted = self._job_api.submit_job(
|
|
137
146
|
name=self._name,
|
|
@@ -215,6 +224,25 @@ class _JobV1(_BaseJob):
|
|
|
215
224
|
"""The logs of the job."""
|
|
216
225
|
return self.work.logs
|
|
217
226
|
|
|
227
|
+
@property
|
|
228
|
+
def image(self) -> Optional[str]:
|
|
229
|
+
"""The image used to submit the job."""
|
|
230
|
+
# jobsv1 don't support images, so return None here
|
|
231
|
+
return None
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def studio(self) -> Optional["Studio"]:
|
|
235
|
+
"""The studio used to submit the job."""
|
|
236
|
+
from lightning_sdk.studio import Studio
|
|
237
|
+
|
|
238
|
+
studio_name = self._job_api.get_studio_name(self._guaranteed_job)
|
|
239
|
+
return Studio(studio_name, teamspace=self.teamspace)
|
|
240
|
+
|
|
241
|
+
@property
|
|
242
|
+
def command(self) -> str:
|
|
243
|
+
"""The command the job is running."""
|
|
244
|
+
return self._job_api.get_command(self._guaranteed_job)
|
|
245
|
+
|
|
218
246
|
# the following and functions are solely to make the Work class function
|
|
219
247
|
@property
|
|
220
248
|
def _id(self) -> str:
|
lightning_sdk/job/v2.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING, Any, Dict, Optional, Union
|
|
2
2
|
|
|
3
3
|
from lightning_sdk.api.job_api import JobApiV2
|
|
4
|
+
from lightning_sdk.api.utils import _get_cloud_url
|
|
4
5
|
from lightning_sdk.job.base import _BaseJob
|
|
5
6
|
|
|
6
7
|
if TYPE_CHECKING:
|
|
@@ -47,6 +48,7 @@ class _JobV2(_BaseJob):
|
|
|
47
48
|
cloud_account_auth: bool = False,
|
|
48
49
|
artifacts_local: Optional[str] = None,
|
|
49
50
|
artifacts_remote: Optional[str] = None,
|
|
51
|
+
entrypoint: str = "sh -c",
|
|
50
52
|
) -> "_JobV2":
|
|
51
53
|
"""Submit a new job to the Lightning AI platform.
|
|
52
54
|
|
|
@@ -75,6 +77,10 @@ class _JobV2(_BaseJob):
|
|
|
75
77
|
within it.
|
|
76
78
|
Note that the connection needs to be added to the teamspace already in order for it to be found.
|
|
77
79
|
Only supported for jobs with a docker image compute environment.
|
|
80
|
+
entrypoint: The entrypoint of your docker container. Defaults to `sh -c` which
|
|
81
|
+
just runs the provided command in a standard shell.
|
|
82
|
+
To use the pre-defined entrypoint of the provided image, set this to an empty string.
|
|
83
|
+
Only applicable when submitting docker jobs.
|
|
78
84
|
"""
|
|
79
85
|
# Command is required if Studio is provided to know what to run
|
|
80
86
|
# Image is mutually exclusive with Studio
|
|
@@ -107,6 +113,7 @@ class _JobV2(_BaseJob):
|
|
|
107
113
|
cloud_account_auth=cloud_account_auth,
|
|
108
114
|
artifacts_local=artifacts_local,
|
|
109
115
|
artifacts_remote=artifacts_remote,
|
|
116
|
+
entrypoint=entrypoint,
|
|
110
117
|
)
|
|
111
118
|
self._job = submitted
|
|
112
119
|
self._name = submitted.name
|
|
@@ -178,13 +185,42 @@ class _JobV2(_BaseJob):
|
|
|
178
185
|
|
|
179
186
|
@property
|
|
180
187
|
def link(self) -> str:
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
188
|
+
mmt_name = self._job_api.get_mmt_name(self._guaranteed_job)
|
|
189
|
+
|
|
190
|
+
if self._job_api.get_image_name(self._guaranteed_job):
|
|
191
|
+
if mmt_name:
|
|
192
|
+
# don't go via the studio unless we use studio env
|
|
193
|
+
return (
|
|
194
|
+
f"{_get_cloud_url()}/{self.teamspace.owner.name}/{self.teamspace.name}/"
|
|
195
|
+
f"jobs/{mmt_name}?app_id=mmt&machine_name={self.name}"
|
|
196
|
+
)
|
|
197
|
+
return f"{_get_cloud_url()}/{self.teamspace.owner.name}/{self.teamspace.name}/jobs/{self.name}?app_id=jobs"
|
|
185
198
|
|
|
199
|
+
# TODO: MMT env with studio
|
|
186
200
|
return super().link
|
|
187
201
|
|
|
202
|
+
@property
|
|
203
|
+
def image(self) -> Optional[str]:
|
|
204
|
+
"""The image used to submit the job."""
|
|
205
|
+
return self._job_api.get_image_name(self._guaranteed_job)
|
|
206
|
+
|
|
207
|
+
@property
|
|
208
|
+
def studio(self) -> Optional["Studio"]:
|
|
209
|
+
"""The studio used to submit the job."""
|
|
210
|
+
from lightning_sdk.studio import Studio
|
|
211
|
+
|
|
212
|
+
studio_name = self._job_api.get_studio_name(self._guaranteed_job)
|
|
213
|
+
|
|
214
|
+
# if job was submitted with image, studio will be None
|
|
215
|
+
if not studio_name:
|
|
216
|
+
return None
|
|
217
|
+
return Studio(studio_name, teamspace=self.teamspace)
|
|
218
|
+
|
|
219
|
+
@property
|
|
220
|
+
def command(self) -> str:
|
|
221
|
+
"""The command the job is running."""
|
|
222
|
+
return self._job_api.get_command(self._guaranteed_job)
|
|
223
|
+
|
|
188
224
|
def _update_internal_job(self) -> None:
|
|
189
225
|
if getattr(self, "_job", None) is None:
|
|
190
226
|
self._job = self._job_api.get_job_by_name(name=self._name, teamspace_id=self._teamspace.id)
|
lightning_sdk/job/work.py
CHANGED
|
@@ -3,9 +3,10 @@ from typing import TYPE_CHECKING, Any, Optional, Protocol
|
|
|
3
3
|
from lightning_sdk.api.job_api import JobApiV1
|
|
4
4
|
|
|
5
5
|
if TYPE_CHECKING:
|
|
6
|
+
from lightning_sdk.job.base import MachineDict
|
|
7
|
+
from lightning_sdk.machine import Machine
|
|
6
8
|
from lightning_sdk.status import Status
|
|
7
9
|
from lightning_sdk.teamspace import Teamspace
|
|
8
|
-
from lightning_sdk.machine import Machine
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class _WorkHolder(Protocol):
|
|
@@ -70,3 +71,11 @@ class Work:
|
|
|
70
71
|
raise RuntimeError("Getting jobs logs while the job is pending or running is not supported yet!")
|
|
71
72
|
|
|
72
73
|
return self._job_api.get_logs_finished(job_id=self._job._id, work_id=self._id, teamspace_id=self._teamspace.id)
|
|
74
|
+
|
|
75
|
+
def dict(self) -> "MachineDict":
|
|
76
|
+
"""Dict representation of the work."""
|
|
77
|
+
return {
|
|
78
|
+
"name": self.name,
|
|
79
|
+
"status": self.status,
|
|
80
|
+
"machine": self.machine,
|
|
81
|
+
}
|
|
@@ -341,6 +341,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_delete_lightningapp_instanc
|
|
|
341
341
|
from lightning_sdk.lightning_cloud.openapi.models.v1_delete_lightningapp_instance_response import V1DeleteLightningappInstanceResponse
|
|
342
342
|
from lightning_sdk.lightning_cloud.openapi.models.v1_delete_lightningwork_response import V1DeleteLightningworkResponse
|
|
343
343
|
from lightning_sdk.lightning_cloud.openapi.models.v1_delete_lit_page_response import V1DeleteLitPageResponse
|
|
344
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_delete_lit_repository_response import V1DeleteLitRepositoryResponse
|
|
344
345
|
from lightning_sdk.lightning_cloud.openapi.models.v1_delete_logger_artifact_response import V1DeleteLoggerArtifactResponse
|
|
345
346
|
from lightning_sdk.lightning_cloud.openapi.models.v1_delete_managed_endpoint_response import V1DeleteManagedEndpointResponse
|
|
346
347
|
from lightning_sdk.lightning_cloud.openapi.models.v1_delete_metrics_stream_response import V1DeleteMetricsStreamResponse
|
|
@@ -793,8 +794,10 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_vultr_direct_v1 import V1Vu
|
|
|
793
794
|
from lightning_sdk.lightning_cloud.openapi.models.v1_work import V1Work
|
|
794
795
|
from lightning_sdk.lightning_cloud.openapi.models.validate import Validate
|
|
795
796
|
from lightning_sdk.lightning_cloud.openapi.models.validateautojoindomain_domain_body import ValidateautojoindomainDomainBody
|
|
797
|
+
from lightning_sdk.lightning_cloud.openapi.models.version_default_body import VersionDefaultBody
|
|
796
798
|
from lightning_sdk.lightning_cloud.openapi.models.version_uploads_body import VersionUploadsBody
|
|
797
799
|
from lightning_sdk.lightning_cloud.openapi.models.versions_id_body import VersionsIdBody
|
|
800
|
+
from lightning_sdk.lightning_cloud.openapi.models.versions_version_body import VersionsVersionBody
|
|
798
801
|
from lightning_sdk.lightning_cloud.openapi.models.works_id_body import WorksIdBody
|
|
799
802
|
from lightning_sdk.lightning_cloud.openapi.models.v1_image_spec import V1ImageSpec as Gridv1ImageSpec
|
|
800
803
|
from lightning_sdk.lightning_cloud.openapi.models.clusters_id_body import ClustersIdBody as Body
|