lightning-sdk 0.1.43__py3-none-any.whl → 0.1.44__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 CHANGED
@@ -27,5 +27,5 @@ __all__ = [
27
27
  "AIHub",
28
28
  ]
29
29
 
30
- __version__ = "0.1.43"
30
+ __version__ = "0.1.44"
31
31
  _check_version_and_prompt_upgrade(__version__)
@@ -1,10 +1,12 @@
1
1
  import time
2
2
  from typing import TYPE_CHECKING, Dict, List, Optional
3
+ from urllib.request import urlopen
3
4
 
4
5
  from lightning_sdk.api.utils import (
5
6
  _COMPUTE_NAME_TO_MACHINE,
6
7
  _MACHINE_TO_COMPUTE_NAME,
7
8
  _create_app,
9
+ remove_datetime_prefix,
8
10
  )
9
11
  from lightning_sdk.api.utils import (
10
12
  _get_cloud_url as _cloud_url,
@@ -16,7 +18,10 @@ from lightning_sdk.lightning_cloud.openapi import (
16
18
  Externalv1Lightningwork,
17
19
  JobsIdBody1,
18
20
  ProjectIdJobsBody,
21
+ V1CloudSpace,
19
22
  V1ComputeConfig,
23
+ V1DownloadJobLogsResponse,
24
+ V1DownloadLightningappInstanceLogsResponse,
20
25
  V1EnvVar,
21
26
  V1Job,
22
27
  V1JobSpec,
@@ -102,6 +107,12 @@ class JobApiV1:
102
107
  compute: str = compute_config.instance_type
103
108
  return _COMPUTE_NAME_TO_MACHINE[compute]
104
109
 
110
+ def get_studio_name(self, job: Externalv1LightningappInstance) -> str:
111
+ cs: V1CloudSpace = self._client.cloud_space_service_get_cloud_space(
112
+ project_id=job.project_id, id=job.spec.cloud_space_id
113
+ )
114
+ return cs.name
115
+
105
116
  def submit_job(
106
117
  self,
107
118
  name: str,
@@ -150,6 +161,16 @@ class JobApiV1:
150
161
 
151
162
  return Status.Pending
152
163
 
164
+ def get_logs_finished(self, job_id: str, work_id: str, teamspace_id: str) -> str:
165
+ resp: (
166
+ V1DownloadLightningappInstanceLogsResponse
167
+ ) = self._client.lightningapp_instance_service_download_lightningapp_instance_logs(
168
+ project_id=teamspace_id, id=job_id, work_id=work_id
169
+ )
170
+
171
+ data = urlopen(resp.url).read().decode("utf-8")
172
+ return remove_datetime_prefix(str(data))
173
+
153
174
 
154
175
  class JobApiV2:
155
176
  v2_job_state_pending = "pending"
@@ -247,6 +268,20 @@ class JobApiV2:
247
268
  def delete_job(self, job_id: str, teamspace_id: str, cloudspace_id: Optional[str]) -> None:
248
269
  self._client.jobs_service_delete_job(project_id=teamspace_id, id=job_id, cloudspace_id=cloudspace_id or "")
249
270
 
271
+ def get_logs_finished(self, job_id: str, teamspace_id: str) -> str:
272
+ resp: V1DownloadJobLogsResponse = self._client.jobs_service_download_job_logs(
273
+ project_id=teamspace_id, id=job_id
274
+ )
275
+
276
+ data = urlopen(resp.url).read().decode("utf-8")
277
+ return remove_datetime_prefix(str(data))
278
+
279
+ def get_studio_name(self, job: V1Job) -> str:
280
+ cs: V1CloudSpace = self._client.cloud_space_service_get_cloud_space(
281
+ project_id=job.project_id, id=job.spec.cloudspace_id
282
+ )
283
+ return cs.name
284
+
250
285
  def _job_state_to_external(self, state: str) -> "Status":
251
286
  from lightning_sdk.status import Status
252
287
 
@@ -2,6 +2,7 @@ import concurrent.futures
2
2
  import errno
3
3
  import math
4
4
  import os
5
+ import re
5
6
  from concurrent.futures import ThreadPoolExecutor
6
7
  from functools import partial
7
8
  from pathlib import Path
@@ -599,3 +600,10 @@ def _create_app(
599
600
  print(f"Create App: {resp.id=} {teamspace_id=} {studio_id=} {cloud_account=}")
600
601
 
601
602
  return resp
603
+
604
+
605
+ def remove_datetime_prefix(text: str) -> str:
606
+ # Use a regular expression to match the datetime pattern at the start of each line
607
+ # lines looks something like
608
+ # '[2025-01-08T14:15:03.797142418Z] ⚡ ~ echo Hello\n[2025-01-08T14:15:03.803077717Z] Hello\n'
609
+ return re.sub(r"^\[.*?\] ", "", text, flags=re.MULTILINE)
lightning_sdk/job/base.py CHANGED
@@ -273,6 +273,16 @@ class _BaseJob(ABC):
273
273
  """The teamspace the job is part of."""
274
274
  return self._teamspace
275
275
 
276
+ @property
277
+ @abstractmethod
278
+ def logs(self) -> str:
279
+ """The logs of the job."""
280
+
281
+ @property
282
+ def link(self) -> str:
283
+ """A link to view the current job in the UI."""
284
+ return f"https://lightning.ai/{self.teamspace.owner.name}/{self.teamspace.name}/studios/{self._job_api.get_studio_name(self._guaranteed_job)}/app?app_id=jobs&job_name={self.name}"
285
+
276
286
  @property
277
287
  def _guaranteed_job(self) -> Any:
278
288
  """Guarantees that the job was fetched at some point before returning it.
lightning_sdk/job/job.py CHANGED
@@ -5,6 +5,9 @@ from lightning_sdk.api.user_api import UserApi
5
5
  from lightning_sdk.job.base import _BaseJob
6
6
  from lightning_sdk.job.v1 import _JobV1
7
7
  from lightning_sdk.job.v2 import _JobV2
8
+ from lightning_sdk.utils.resolve import _setup_logger
9
+
10
+ _logger = _setup_logger(__name__)
8
11
 
9
12
  if TYPE_CHECKING:
10
13
  from lightning_sdk.machine import Machine
@@ -27,6 +30,8 @@ def _has_jobs_v2() -> bool:
27
30
  class Job(_BaseJob):
28
31
  """Class to submit and manage single-machine jobs on the Lightning AI Platform."""
29
32
 
33
+ _force_v1: bool = False
34
+
30
35
  def __init__(
31
36
  self,
32
37
  name: str,
@@ -45,7 +50,7 @@ class Job(_BaseJob):
45
50
  user: the name of the user owning the :param`teamspace`
46
51
  in case it is owned directly by a user instead of an org.
47
52
  """
48
- internal_job_cls = _JobV2 if _has_jobs_v2() else _JobV1
53
+ internal_job_cls = _JobV2 if _has_jobs_v2() and not self._force_v1 else _JobV1
49
54
 
50
55
  self._internal_job = internal_job_cls(
51
56
  name=name,
@@ -127,6 +132,8 @@ class Job(_BaseJob):
127
132
  )
128
133
  # required for typing with "Job"
129
134
  assert isinstance(ret_val, cls)
135
+
136
+ _logger.info(f"Job was successfully launched. View it at {ret_val.link}")
130
137
  return ret_val
131
138
 
132
139
  def _submit(
@@ -239,9 +246,11 @@ class Job(_BaseJob):
239
246
  return self._internal_job._teamspace
240
247
 
241
248
  @property
242
- def cluster(self) -> Optional[str]:
243
- """The cluster the job is running on."""
244
- return self._internal_job.cluster
249
+ def logs(self) -> str:
250
+ """The logs of the job."""
251
+ if self.status not in (Status.Failed, Status.Completed, Status.Stopped):
252
+ raise RuntimeError("Getting jobs logs while the job is pending or running is not supported yet!")
253
+ return self._internal_job.logs
245
254
 
246
255
  def __getattr__(self, key: str) -> Any:
247
256
  """Forward the attribute lookup to the internal job implementation."""
@@ -249,3 +258,7 @@ class Job(_BaseJob):
249
258
  return getattr(super(), key)
250
259
  except AttributeError:
251
260
  return getattr(self._internal_job, key)
261
+
262
+ @property
263
+ def link(self) -> str:
264
+ return self._internal_job.link
lightning_sdk/job/v1.py CHANGED
@@ -210,6 +210,11 @@ class _JobV1(_BaseJob):
210
210
  """The path to the share of the job in the distributed teamspace filesystem."""
211
211
  return f"/teamspace/jobs/{self.name}/share"
212
212
 
213
+ @property
214
+ def logs(self) -> str:
215
+ """The logs of the job."""
216
+ return self.work.logs
217
+
213
218
  # the following and functions are solely to make the Work class function
214
219
  @property
215
220
  def _id(self) -> str:
lightning_sdk/job/v2.py CHANGED
@@ -167,6 +167,24 @@ class _JobV2(_BaseJob):
167
167
  """The path to the share of the job within the distributed teamspace filesystem."""
168
168
  raise NotImplementedError("Not implemented yet")
169
169
 
170
+ @property
171
+ def logs(self) -> str:
172
+ from lightning_sdk.status import Status
173
+
174
+ if self.status not in (Status.Failed, Status.Completed, Status.Stopped):
175
+ raise RuntimeError("Getting jobs logs while the job is pending or running is not supported yet!")
176
+
177
+ return self._job_api.get_logs_finished(job_id=self._guaranteed_job.id, teamspace_id=self.teamspace.id)
178
+
179
+ @property
180
+ def link(self) -> str:
181
+ if self._guaranteed_job.spec.image:
182
+ return (
183
+ f"https://lightning.ai/{self.teamspace.owner.name}/{self.teamspace.name}/jobs/{self.name}?app_id=jobs"
184
+ )
185
+
186
+ return super().link
187
+
170
188
  def _update_internal_job(self) -> None:
171
189
  if getattr(self, "_job", None) is None:
172
190
  self._job = self._job_api.get_job_by_name(name=self._name, teamspace_id=self._teamspace.id)
lightning_sdk/job/work.py CHANGED
@@ -60,3 +60,13 @@ class Work:
60
60
  @property
61
61
  def status(self) -> "Status":
62
62
  return self._job_api.get_status_from_work(self._latest_work)
63
+
64
+ @property
65
+ def logs(self) -> str:
66
+ """The logs of the work."""
67
+ from lightning_sdk.status import Status
68
+
69
+ if self.status not in (Status.Failed, Status.Completed, Status.Stopped):
70
+ raise RuntimeError("Getting jobs logs while the job is pending or running is not supported yet!")
71
+
72
+ return self._job_api.get_logs_finished(job_id=self._job._id, work_id=self._id, teamspace_id=self._teamspace.id)
@@ -52,6 +52,7 @@ class V1Membership(object):
52
52
  'job_count': 'str',
53
53
  'membership_count': 'str',
54
54
  'name': 'str',
55
+ 'next_free_credits_grant_at': 'datetime',
55
56
  'owner_id': 'str',
56
57
  'owner_type': 'V1OwnerType',
57
58
  'project_id': 'str',
@@ -73,6 +74,7 @@ class V1Membership(object):
73
74
  'job_count': 'jobCount',
74
75
  'membership_count': 'membershipCount',
75
76
  'name': 'name',
77
+ 'next_free_credits_grant_at': 'nextFreeCreditsGrantAt',
76
78
  'owner_id': 'ownerId',
77
79
  'owner_type': 'ownerType',
78
80
  'project_id': 'projectId',
@@ -82,7 +84,7 @@ class V1Membership(object):
82
84
  'user_id': 'userId'
83
85
  }
84
86
 
85
- def __init__(self, balance: 'float' =None, created_at: 'datetime' =None, creator_id: 'str' =None, datastore_count: 'str' =None, description: 'str' =None, display_name: 'str' =None, free_credits_enabled: 'bool' =None, is_default: 'bool' =None, job_count: 'str' =None, membership_count: 'str' =None, name: 'str' =None, owner_id: 'str' =None, owner_type: 'V1OwnerType' =None, project_id: 'str' =None, quotas: 'V1Quotas' =None, roles: 'list[V1Role]' =None, updated_at: 'datetime' =None, user_id: 'str' =None): # noqa: E501
87
+ def __init__(self, balance: 'float' =None, created_at: 'datetime' =None, creator_id: 'str' =None, datastore_count: 'str' =None, description: 'str' =None, display_name: 'str' =None, free_credits_enabled: 'bool' =None, is_default: 'bool' =None, job_count: 'str' =None, membership_count: 'str' =None, name: 'str' =None, next_free_credits_grant_at: 'datetime' =None, owner_id: 'str' =None, owner_type: 'V1OwnerType' =None, project_id: 'str' =None, quotas: 'V1Quotas' =None, roles: 'list[V1Role]' =None, updated_at: 'datetime' =None, user_id: 'str' =None): # noqa: E501
86
88
  """V1Membership - a model defined in Swagger""" # noqa: E501
87
89
  self._balance = None
88
90
  self._created_at = None
@@ -95,6 +97,7 @@ class V1Membership(object):
95
97
  self._job_count = None
96
98
  self._membership_count = None
97
99
  self._name = None
100
+ self._next_free_credits_grant_at = None
98
101
  self._owner_id = None
99
102
  self._owner_type = None
100
103
  self._project_id = None
@@ -125,6 +128,8 @@ class V1Membership(object):
125
128
  self.membership_count = membership_count
126
129
  if name is not None:
127
130
  self.name = name
131
+ if next_free_credits_grant_at is not None:
132
+ self.next_free_credits_grant_at = next_free_credits_grant_at
128
133
  if owner_id is not None:
129
134
  self.owner_id = owner_id
130
135
  if owner_type is not None:
@@ -371,6 +376,27 @@ class V1Membership(object):
371
376
 
372
377
  self._name = name
373
378
 
379
+ @property
380
+ def next_free_credits_grant_at(self) -> 'datetime':
381
+ """Gets the next_free_credits_grant_at of this V1Membership. # noqa: E501
382
+
383
+
384
+ :return: The next_free_credits_grant_at of this V1Membership. # noqa: E501
385
+ :rtype: datetime
386
+ """
387
+ return self._next_free_credits_grant_at
388
+
389
+ @next_free_credits_grant_at.setter
390
+ def next_free_credits_grant_at(self, next_free_credits_grant_at: 'datetime'):
391
+ """Sets the next_free_credits_grant_at of this V1Membership.
392
+
393
+
394
+ :param next_free_credits_grant_at: The next_free_credits_grant_at of this V1Membership. # noqa: E501
395
+ :type: datetime
396
+ """
397
+
398
+ self._next_free_credits_grant_at = next_free_credits_grant_at
399
+
374
400
  @property
375
401
  def owner_id(self) -> 'str':
376
402
  """Gets the owner_id of this V1Membership. # noqa: E501
@@ -38,6 +38,7 @@ class V1MultiMachineJobState(object):
38
38
  allowed enum values
39
39
  """
40
40
  UNSPECIFIED = "MultiMachineJob_STATE_UNSPECIFIED"
41
+ PENDING = "MultiMachineJob_STATE_PENDING"
41
42
  RUNNING = "MultiMachineJob_STATE_RUNNING"
42
43
  STOPPED = "MultiMachineJob_STATE_STOPPED"
43
44
  DELETED = "MultiMachineJob_STATE_DELETED"
@@ -58,6 +58,7 @@ class V1ProjectMembership(object):
58
58
  'last_name': 'str',
59
59
  'membership_count': 'str',
60
60
  'name': 'str',
61
+ 'next_free_credits_grant_at': 'datetime',
61
62
  'organization': 'str',
62
63
  'owner_id': 'str',
63
64
  'owner_type': 'V1OwnerType',
@@ -87,6 +88,7 @@ class V1ProjectMembership(object):
87
88
  'last_name': 'lastName',
88
89
  'membership_count': 'membershipCount',
89
90
  'name': 'name',
91
+ 'next_free_credits_grant_at': 'nextFreeCreditsGrantAt',
90
92
  'organization': 'organization',
91
93
  'owner_id': 'ownerId',
92
94
  'owner_type': 'ownerType',
@@ -98,7 +100,7 @@ class V1ProjectMembership(object):
98
100
  'username': 'username'
99
101
  }
100
102
 
101
- def __init__(self, avatar_url: 'str' =None, balance: 'float' =None, created_at: 'datetime' =None, creator_id: 'str' =None, datastore_count: 'str' =None, description: 'str' =None, display_name: 'str' =None, email: 'str' =None, first_name: 'str' =None, free_credits_enabled: 'bool' =None, inactive: 'bool' =None, is_default: 'bool' =None, job_count: 'str' =None, job_title: 'str' =None, last_name: 'str' =None, membership_count: 'str' =None, name: 'str' =None, organization: 'str' =None, owner_id: 'str' =None, owner_type: 'V1OwnerType' =None, project_id: 'str' =None, quotas: 'V1Quotas' =None, roles: 'list[V1Role]' =None, updated_at: 'datetime' =None, user_id: 'str' =None, username: 'str' =None): # noqa: E501
103
+ def __init__(self, avatar_url: 'str' =None, balance: 'float' =None, created_at: 'datetime' =None, creator_id: 'str' =None, datastore_count: 'str' =None, description: 'str' =None, display_name: 'str' =None, email: 'str' =None, first_name: 'str' =None, free_credits_enabled: 'bool' =None, inactive: 'bool' =None, is_default: 'bool' =None, job_count: 'str' =None, job_title: 'str' =None, last_name: 'str' =None, membership_count: 'str' =None, name: 'str' =None, next_free_credits_grant_at: 'datetime' =None, organization: 'str' =None, owner_id: 'str' =None, owner_type: 'V1OwnerType' =None, project_id: 'str' =None, quotas: 'V1Quotas' =None, roles: 'list[V1Role]' =None, updated_at: 'datetime' =None, user_id: 'str' =None, username: 'str' =None): # noqa: E501
102
104
  """V1ProjectMembership - a model defined in Swagger""" # noqa: E501
103
105
  self._avatar_url = None
104
106
  self._balance = None
@@ -117,6 +119,7 @@ class V1ProjectMembership(object):
117
119
  self._last_name = None
118
120
  self._membership_count = None
119
121
  self._name = None
122
+ self._next_free_credits_grant_at = None
120
123
  self._organization = None
121
124
  self._owner_id = None
122
125
  self._owner_type = None
@@ -161,6 +164,8 @@ class V1ProjectMembership(object):
161
164
  self.membership_count = membership_count
162
165
  if name is not None:
163
166
  self.name = name
167
+ if next_free_credits_grant_at is not None:
168
+ self.next_free_credits_grant_at = next_free_credits_grant_at
164
169
  if organization is not None:
165
170
  self.organization = organization
166
171
  if owner_id is not None:
@@ -537,6 +542,27 @@ class V1ProjectMembership(object):
537
542
 
538
543
  self._name = name
539
544
 
545
+ @property
546
+ def next_free_credits_grant_at(self) -> 'datetime':
547
+ """Gets the next_free_credits_grant_at of this V1ProjectMembership. # noqa: E501
548
+
549
+
550
+ :return: The next_free_credits_grant_at of this V1ProjectMembership. # noqa: E501
551
+ :rtype: datetime
552
+ """
553
+ return self._next_free_credits_grant_at
554
+
555
+ @next_free_credits_grant_at.setter
556
+ def next_free_credits_grant_at(self, next_free_credits_grant_at: 'datetime'):
557
+ """Sets the next_free_credits_grant_at of this V1ProjectMembership.
558
+
559
+
560
+ :param next_free_credits_grant_at: The next_free_credits_grant_at of this V1ProjectMembership. # noqa: E501
561
+ :type: datetime
562
+ """
563
+
564
+ self._next_free_credits_grant_at = next_free_credits_grant_at
565
+
540
566
  @property
541
567
  def organization(self) -> 'str':
542
568
  """Gets the organization of this V1ProjectMembership. # noqa: E501
lightning_sdk/mmt/base.py CHANGED
@@ -36,6 +36,11 @@ class MMTMachine(Protocol):
36
36
  """The status of this job."""
37
37
  ...
38
38
 
39
+ @property
40
+ def logs(self) -> str:
41
+ """The logs of the given machine."""
42
+ ...
43
+
39
44
 
40
45
  class _BaseMMT(_BaseJob):
41
46
  """Base interface to all job types."""
@@ -273,6 +278,11 @@ class _BaseMMT(_BaseJob):
273
278
  """The teamspace the job is part of."""
274
279
  return self._teamspace
275
280
 
281
+ @property
282
+ def logs(self) -> str:
283
+ """Logs of the rank 0 machine."""
284
+ return self.machines[0].logs
285
+
276
286
  @abstractmethod
277
287
  def _update_internal_job(self) -> None:
278
288
  pass
lightning_sdk/mmt/mmt.py CHANGED
@@ -1,3 +1,4 @@
1
+ from contextlib import suppress
1
2
  from functools import lru_cache
2
3
  from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Union
3
4
 
@@ -6,6 +7,7 @@ from lightning_sdk.job.job import _has_jobs_v2
6
7
  from lightning_sdk.mmt.base import MMTMachine, _BaseMMT
7
8
  from lightning_sdk.mmt.v1 import _MMTV1
8
9
  from lightning_sdk.mmt.v2 import _MMTV2
10
+ from lightning_sdk.utils.resolve import _setup_logger
9
11
 
10
12
  if TYPE_CHECKING:
11
13
  from lightning_sdk.machine import Machine
@@ -15,6 +17,8 @@ if TYPE_CHECKING:
15
17
  from lightning_sdk.teamspace import Teamspace
16
18
  from lightning_sdk.user import User
17
19
 
20
+ _logger = _setup_logger(__name__)
21
+
18
22
 
19
23
  @lru_cache(maxsize=None)
20
24
  def _has_mmt_v2() -> bool:
@@ -137,8 +141,15 @@ class MMT(_BaseMMT):
137
141
  artifacts_remote=artifacts_remote,
138
142
  cluster=cluster, # deprecated in favor of cloud_account
139
143
  )
140
- # required for typing with "Job"
144
+ # required for typing with "MMT"
141
145
  assert isinstance(ret_val, cls)
146
+
147
+ msg = "Multi-Machine Job was successfully launched."
148
+
149
+ with suppress(NotImplementedError):
150
+ msg += f" View it at {ret_val.link}"
151
+
152
+ _logger.info(msg)
142
153
  return ret_val
143
154
 
144
155
  def _submit(
@@ -255,6 +266,10 @@ class MMT(_BaseMMT):
255
266
  """The teamspace the job is part of."""
256
267
  return self._internal_mmt._teamspace
257
268
 
269
+ @property
270
+ def link(self) -> str:
271
+ return self._internal_mmt.link
272
+
258
273
  def __getattr__(self, key: str) -> Any:
259
274
  """Forward the attribute lookup to the internal job implementation."""
260
275
  try:
lightning_sdk/mmt/v1.py CHANGED
@@ -172,6 +172,10 @@ class _MMTV1(_BaseMMT):
172
172
  """The teamspace the job is part of."""
173
173
  return self._teamspace
174
174
 
175
+ @property
176
+ def link(self) -> str:
177
+ return f"https://lightning.ai/{self.teamspace.owner.name}/{self.teamspace.name}/studios/{self._job_api.get_studio_name(self._guaranteed_job)}/app?app_id=mmt&app_tab=Runs&job_name={self.name}"
178
+
175
179
  # the following and functions are solely to make the Work class function
176
180
  @property
177
181
  def _id(self) -> str:
lightning_sdk/mmt/v2.py CHANGED
@@ -186,3 +186,8 @@ class _MMTV2(_BaseMMT):
186
186
  def teamspace(self) -> "Teamspace":
187
187
  """The teamspace the job is part of."""
188
188
  return self._teamspace
189
+
190
+ @property
191
+ def link(self) -> str:
192
+ # TODO: Since we don't have a UI for this yet, we can't have a link
193
+ raise NotImplementedError
lightning_sdk/plugin.py CHANGED
@@ -3,7 +3,8 @@ import logging
3
3
  import os
4
4
  import warnings
5
5
  from abc import ABC, abstractmethod
6
- from typing import TYPE_CHECKING, Any, Optional, Protocol, Union, runtime_checkable
6
+ from contextlib import contextmanager
7
+ from typing import TYPE_CHECKING, Any, Generator, Optional, Protocol, Union, runtime_checkable
7
8
 
8
9
  from lightning_sdk.job import Job
9
10
  from lightning_sdk.machine import Machine
@@ -16,6 +17,7 @@ from lightning_sdk.utils.resolve import (
16
17
 
17
18
  if TYPE_CHECKING:
18
19
  from lightning_sdk.lightning_cloud.openapi import Externalv1LightningappInstance
20
+ from lightning_sdk.mmt import MMT
19
21
 
20
22
  _logger = _setup_logger(__name__)
21
23
 
@@ -141,7 +143,7 @@ class JobsPlugin(_Plugin):
141
143
 
142
144
  machine = _resolve_deprecated_cloud_compute(machine, cloud_compute)
143
145
 
144
- job = Job.run(
146
+ return Job.run(
145
147
  name=name,
146
148
  machine=machine,
147
149
  command=command,
@@ -151,10 +153,6 @@ class JobsPlugin(_Plugin):
151
153
  interruptible=interruptible,
152
154
  )
153
155
 
154
- _logger.info(_success_message(job, self))
155
-
156
- return job
157
-
158
156
 
159
157
  class MultiMachineTrainingPlugin(_Plugin):
160
158
  """Plugin handling multi-machine-training jobs."""
@@ -170,7 +168,7 @@ class MultiMachineTrainingPlugin(_Plugin):
170
168
  cloud_compute: Optional[Machine] = None,
171
169
  num_instances: int = 2,
172
170
  interruptible: bool = False,
173
- ) -> Job:
171
+ ) -> "MMT":
174
172
  """Launches an asynchronous multi-machine-training.
175
173
 
176
174
  Args:
@@ -188,20 +186,16 @@ class MultiMachineTrainingPlugin(_Plugin):
188
186
 
189
187
  machine = _resolve_deprecated_cloud_compute(machine, cloud_compute)
190
188
 
191
- MMT._force_v1 = True
192
-
193
- mmt = MMT.run(
194
- name=name,
195
- num_machines=num_instances,
196
- machine=machine,
197
- command=command,
198
- studio=self._studio,
199
- teamspace=self._studio.teamspace,
200
- interruptible=interruptible,
201
- )
202
-
203
- MMT._force_v1 = False
204
- return mmt
189
+ with forced_v1(MMT) as v1mmt:
190
+ return v1mmt.run(
191
+ name=name,
192
+ num_machines=num_instances,
193
+ machine=machine,
194
+ command=command,
195
+ studio=self._studio,
196
+ teamspace=self._studio.teamspace,
197
+ interruptible=interruptible,
198
+ )
205
199
 
206
200
 
207
201
  class MultiMachineDataPrepPlugin(_Plugin):
@@ -245,7 +239,8 @@ class MultiMachineDataPrepPlugin(_Plugin):
245
239
  interruptible=interruptible,
246
240
  )
247
241
 
248
- return Job(resp.name, self._studio.teamspace)
242
+ with forced_v1(Job) as v1_job:
243
+ return v1_job(resp.name, self._studio.teamspace)
249
244
 
250
245
 
251
246
  class InferenceServerPlugin(_Plugin):
@@ -293,7 +288,8 @@ class InferenceServerPlugin(_Plugin):
293
288
  )
294
289
 
295
290
  _logger.info(_success_message(resp, self))
296
- return Job(resp.name, self._studio.teamspace)
291
+ with forced_v1(Job) as v1_job:
292
+ return v1_job(resp.name, self._studio.teamspace)
297
293
 
298
294
 
299
295
  class SlurmJobsPlugin(_Plugin):
@@ -430,3 +426,12 @@ def _run_name(plugin_type: str) -> str:
430
426
  def _success_message(resp: Union["Externalv1LightningappInstance", Job], plugin_instance: _RunnablePlugin) -> str:
431
427
  """Compiles the success message for a given runnable plugin."""
432
428
  return f"{plugin_instance._plugin_run_name} {resp.name} was successfully launched. View it at https://lightning.ai/{plugin_instance._studio.owner.name}/{plugin_instance._studio.teamspace.name}/studios/{plugin_instance.studio}/app?app_id={plugin_instance._slug_name}&job_name={resp.name}"
429
+
430
+
431
+ @contextmanager
432
+ def forced_v1(cls: Any) -> Generator[Any, None, None]:
433
+ """Forces to use the v1 version of a class when using a class with multiple backends."""
434
+ orig_val = getattr(cls, "_force_v1", False)
435
+ cls._force_v1 = True
436
+ yield cls
437
+ cls._force_v1 = orig_val
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lightning_sdk
3
- Version: 0.1.43
3
+ Version: 0.1.44
4
4
  Summary: SDK to develop using Lightning AI Studios
5
5
  Author-email: Lightning-AI <justus@lightning.ai>
6
6
  License: MIT License
@@ -1,5 +1,5 @@
1
1
  docs/source/conf.py,sha256=r8yX20eC-4mHhMTd0SbQb5TlSWHhO6wnJ0VJ_FBFpag,13249
2
- lightning_sdk/__init__.py,sha256=je3-DFVBfoAqqPLrFLZmVZGWPNda7886O9-BrY6dvlQ,925
2
+ lightning_sdk/__init__.py,sha256=XJlg9TRR7l1VeCSLkPRsB0dOcFAU6kcMciUwmYdtojQ,925
3
3
  lightning_sdk/agents.py,sha256=ly6Ma1j0ZgGPFyvPvMN28JWiB9dATIstFa5XM8pMi6I,1577
4
4
  lightning_sdk/ai_hub.py,sha256=kBjtmrzVHPCgqtV_TrSNkuf4oT2DLm8SYRTz4iTQmmY,6624
5
5
  lightning_sdk/constants.py,sha256=ztl1PTUBULnqTf3DyKUSJaV_O20hNtUYT6XvAYIrmIk,749
@@ -8,7 +8,7 @@ lightning_sdk/machine.py,sha256=VdFXStR6ilYBEYuxgGWzcAw2TtW-nEQVsh6hz-2aaEw,750
8
8
  lightning_sdk/models.py,sha256=d27VAYUcbWKd4kuL_CqwCi3IguyjmKUR9EVWfXWTwmc,5606
9
9
  lightning_sdk/organization.py,sha256=WCfzdgjtvY1_A07DnxOpp74V2JR2gQwtXbIEcFDnoVU,1232
10
10
  lightning_sdk/owner.py,sha256=t5svD2it4C9pbSpVuG9WJL46CYi37JXNziwnXxhiU5U,1361
11
- lightning_sdk/plugin.py,sha256=cDvXaQWyove0IdEZYkvlKiaKuQoyjmcUJ8WdZkZ61Z0,14512
11
+ lightning_sdk/plugin.py,sha256=nWFL1l6Q9DE8Lr2krD5qRhZljwmYm00R2eR1tYRb20s,14902
12
12
  lightning_sdk/status.py,sha256=kLDhN4-zdsGuZM577JMl1BbUIoF61bUOadW89ZAATFA,219
13
13
  lightning_sdk/studio.py,sha256=lezGs111RUFejLWgp4Urov5l6uiUmYJjtRK8D8EYFU8,17198
14
14
  lightning_sdk/teamspace.py,sha256=dKT-WrYF2xGP1C1bjOY2aYlEpkrvYkz2fXvtYVgogwo,11508
@@ -17,13 +17,13 @@ lightning_sdk/api/__init__.py,sha256=Qn2VVRvir_gO7w4yxGLkZY-R3T7kdiTPKgQ57BhIA9k
17
17
  lightning_sdk/api/agents_api.py,sha256=G47TbFo9kYqnBMqdw2RW-lfS1VAUBSXDmzs6fpIEMUs,4059
18
18
  lightning_sdk/api/ai_hub_api.py,sha256=CYQLFLA89m3xQ-6Ss3UX4TDK6ZWRwmPGA5DjyJqW3RM,5578
19
19
  lightning_sdk/api/deployment_api.py,sha256=T480Nej7LqmtkAx8SBkPGQ5JxeyQ-GVIDqUCc7Z1yfk,21448
20
- lightning_sdk/api/job_api.py,sha256=I90j2DAhokqbHoyORKI0-dgrsZLvUaFHa8pT49IM60c,9683
20
+ lightning_sdk/api/job_api.py,sha256=KgXl0uO32Ja0AvxOASh-LihUPGERq2Fwy1rovXnq5Sg,11074
21
21
  lightning_sdk/api/mmt_api.py,sha256=texQJqSjbQNpfLLrumpvZ0MauPjmBlJAc8oSk3i46wk,6569
22
22
  lightning_sdk/api/org_api.py,sha256=Ze3z_ATVrukobujV5YdC42DKj45Vuwl7X52q_Vr-o3U,803
23
23
  lightning_sdk/api/studio_api.py,sha256=Cfsq8HFc4uUsj8hncnhnD_TLhw0cg-ryclGowj8S6Y0,26374
24
24
  lightning_sdk/api/teamspace_api.py,sha256=NYhD-Br2NIIn-1Noc8Q94TzWxEwFM1qCkf9RZhOqQu0,10321
25
25
  lightning_sdk/api/user_api.py,sha256=sL7RIjjtmZmvCZWx7BBZslhj1BeNh4Idn-RVcdmf7M0,2598
26
- lightning_sdk/api/utils.py,sha256=iGMi5mabu4NyY9EPZv6HpbbgR5qzLUq8XSyX2zWy7l8,22193
26
+ lightning_sdk/api/utils.py,sha256=GbfTdqW1yV-fjyEwVebsbAdKQQjRmwT_fXUamrcnwAY,22534
27
27
  lightning_sdk/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
28
  lightning_sdk/cli/ai_hub.py,sha256=8oy6TogDiWnHuLT3cv33XEW7vPqXPA0dDMds8kX3Z4g,1649
29
29
  lightning_sdk/cli/download.py,sha256=nyQN3q1vZ0fg4_cfit8cKaokQ9VUd46l_TNcAQWkLwU,5996
@@ -37,11 +37,11 @@ lightning_sdk/cli/upload.py,sha256=H9OyipYTYAQ9Mzy2e8jtoaa-B34-uXHbTQTzY2Vmhv4,9
37
37
  lightning_sdk/deployment/__init__.py,sha256=BLu7_cVLp97TYxe6qe-J1zKUSZXAVcvCjgcA7plV2k4,497
38
38
  lightning_sdk/deployment/deployment.py,sha256=Dp15pn8rFAfMfaDhKn0v3bphFuvLgkPFs3KSNxW6eyc,15472
39
39
  lightning_sdk/job/__init__.py,sha256=1MxjQ6rHkyUHCypSW9RuXuVMVH11WiqhIXcU2LCFMwE,64
40
- lightning_sdk/job/base.py,sha256=I4-iWyiKp1KUkxDy97zJYbwbdQ_7cu6FqCayKwXDloQ,13000
41
- lightning_sdk/job/job.py,sha256=pT9rkSsK5BHu6dSyHpAlYfGXvaF0s_XtrjjhOSAVFWU,11070
42
- lightning_sdk/job/v1.py,sha256=zIcngaM2_c-2thcKiCOPoWGUuIKc4tasJEwEFJv6bAA,8953
43
- lightning_sdk/job/v2.py,sha256=oq54VFInuVV_L-nUO_dnBbn4TxPWiBuIqmdFpNN1LmU,8057
44
- lightning_sdk/job/work.py,sha256=PYopS_6c556I2o8ouSXmzb4FGQflzCe06GpqJiCdedw,1604
40
+ lightning_sdk/job/base.py,sha256=WHd2jz1qEeQWg2ljphlJMwoEVU6qtSBjnO9hMScLF7E,13383
41
+ lightning_sdk/job/job.py,sha256=8Bch1TCKjf4jxf46tVN64_k_ZIq677kblHLJHQJO8v4,11536
42
+ lightning_sdk/job/v1.py,sha256=Ff1iTvZqa1J90eIex6_xDjwAxJztHtUSTmjihFwq5Vg,9060
43
+ lightning_sdk/job/v2.py,sha256=mLEgSbog3QiLKsk1-9pWNWA_yYuQzvNfkWt8Mt2Y598,8720
44
+ lightning_sdk/job/work.py,sha256=_L1eF9L0dW_BI17Wo0HwgoOkwwd48vFkqBD0uOt_rJk,2043
45
45
  lightning_sdk/lightning_cloud/__init__.py,sha256=o91SMAlwr4Ke5ESe8fHjqXcj31_h7rT-MlFoXA-n2EI,173
46
46
  lightning_sdk/lightning_cloud/__version__.py,sha256=lOfmWHtjmiuSG28TbKQqd2B3nwmSGOlKVFwhaj_cRJk,23
47
47
  lightning_sdk/lightning_cloud/env.py,sha256=XZXpF4sD9jlB8DY0herTy_8XiUJuDVjxy5APjRD2_aU,1379
@@ -632,7 +632,7 @@ lightning_sdk/lightning_cloud/openapi/models/v1_magic_link_login_response.py,sha
632
632
  lightning_sdk/lightning_cloud/openapi/models/v1_managed_endpoint.py,sha256=T0IXmXRI5paAN6NxpQQ8873lgTT7bIHYZZmeJEmW4yA,10175
633
633
  lightning_sdk/lightning_cloud/openapi/models/v1_managed_model.py,sha256=97KxjHxTsaLWYlTNQeE2yGhF7KbTbmqLb8c39BEy02s,9117
634
634
  lightning_sdk/lightning_cloud/openapi/models/v1_managed_model_abilities.py,sha256=1of-AEIimGnUd_0fpS5U8apGFYdsIXogRpFGxed67s0,5892
635
- lightning_sdk/lightning_cloud/openapi/models/v1_membership.py,sha256=avU-MIUu2qy7tyjfVWmuPfrGOvZXK9GuirIKR1eu-CY,16140
635
+ lightning_sdk/lightning_cloud/openapi/models/v1_membership.py,sha256=6wqHKmxv3SOj9mTTRAw2g3ljxQyg_HKU18rexmA1KII,17207
636
636
  lightning_sdk/lightning_cloud/openapi/models/v1_message.py,sha256=6E1FDyfNM8fMkJi6FZ4U9UBk4k5VhfmCYOHGkc5CyzA,10189
637
637
  lightning_sdk/lightning_cloud/openapi/models/v1_message_author.py,sha256=QRC1HofufQOPROrxwtLeaEfE8f1h_ehZ0tdqlYfkgyM,3591
638
638
  lightning_sdk/lightning_cloud/openapi/models/v1_message_content.py,sha256=rlYtO8Llal6jISWpQMyOC8zMOvdVWaljXTwlHp_gzdc,4535
@@ -652,7 +652,7 @@ lightning_sdk/lightning_cloud/openapi/models/v1_multi_machine_job_event.py,sha25
652
652
  lightning_sdk/lightning_cloud/openapi/models/v1_multi_machine_job_event_type.py,sha256=HMBQuVSxwwbhbDQAAAMF8k99drvllwifjwjqEQrrDaQ,3248
653
653
  lightning_sdk/lightning_cloud/openapi/models/v1_multi_machine_job_fault_tolerance.py,sha256=5Q0mZ3FM6yRi81mVCnYcyYh84loK4LVZTIv-2GgAy1I,4863
654
654
  lightning_sdk/lightning_cloud/openapi/models/v1_multi_machine_job_fault_tolerance_strategy.py,sha256=FQ8qqDLfpguNtMqEETybXre4IArM32_vFuLrrUOcHb4,3539
655
- lightning_sdk/lightning_cloud/openapi/models/v1_multi_machine_job_state.py,sha256=0Rh4GKRQcdfI0ccGeVt0PxTbC7e7QwaIWYUy1rCnVhE,3349
655
+ lightning_sdk/lightning_cloud/openapi/models/v1_multi_machine_job_state.py,sha256=xNpq6pM1UF9r0hPHoEQ124SlCW2sCb_zW628kEa7IVU,3395
656
656
  lightning_sdk/lightning_cloud/openapi/models/v1_multi_machine_job_status.py,sha256=yoOMJoszmdfEoUP7mzjl3PTK9dq6JUi8yXrxJljrY-0,9540
657
657
  lightning_sdk/lightning_cloud/openapi/models/v1_named_get_logger_metrics.py,sha256=VygKKjSPGIbWT9RxbmJfOSlNR-Fq12yKj8wT_SHd2ZQ,3966
658
658
  lightning_sdk/lightning_cloud/openapi/models/v1_network_config.py,sha256=bHDZVIsqgiSi7eBf72RunvrLR-d-4b9S7oDMAZFBvCM,5476
@@ -682,7 +682,7 @@ lightning_sdk/lightning_cloud/openapi/models/v1_project_artifact.py,sha256=jMg7B
682
682
  lightning_sdk/lightning_cloud/openapi/models/v1_project_cluster_binding.py,sha256=KLZYf_bkkB1OJenDpZsG02BvomPQqJSP3gMf3ofJ83k,8719
683
683
  lightning_sdk/lightning_cloud/openapi/models/v1_project_compute_daily_usage.py,sha256=92qqkHCx9gfZCg7FGVn7TDMQjuHw5E0POtLC7HoQ9pE,6565
684
684
  lightning_sdk/lightning_cloud/openapi/models/v1_project_compute_usage.py,sha256=-IJmDR0j5M2S7wfkwz5qO0riW2ZolAzrLnopnBAfckU,12121
685
- lightning_sdk/lightning_cloud/openapi/models/v1_project_membership.py,sha256=_9GewIzuL3F3Nn9duxOAq1mI7f2AfODWgVhvVNqfCsI,22500
685
+ lightning_sdk/lightning_cloud/openapi/models/v1_project_membership.py,sha256=pzF0Jm8gNDV1-TerXtPWf1xNM2pRgJnCtDtKNRWcb8o,23595
686
686
  lightning_sdk/lightning_cloud/openapi/models/v1_project_membership_invite.py,sha256=V9Djs6edeaKv-X2-GpfAU-EVnx2rlKppGSTLfKzfrP0,9310
687
687
  lightning_sdk/lightning_cloud/openapi/models/v1_project_membership_role_binding.py,sha256=pS5454riAOUNeB7B3Y8ChEj9RJQYaroelZuJQs8w2yM,7727
688
688
  lightning_sdk/lightning_cloud/openapi/models/v1_project_settings.py,sha256=OugDsaWQPBMBGx4MykBJZO-xacRzzVMV3uGbeyLtQi0,5977
@@ -834,10 +834,10 @@ lightning_sdk/lightning_cloud/utils/dataset.py,sha256=4nUspe8iAaRPgSYpXA2uAQCgyd
834
834
  lightning_sdk/lightning_cloud/utils/name_generator.py,sha256=MkciuA10332V0mcE2PxLIiwWomWE0Fm_gNGK01vwRr4,58046
835
835
  lightning_sdk/lightning_cloud/utils/network.py,sha256=axPgl8rhyPcPjxiztDxyksfxax3VNg2OXL5F5Uc81b4,406
836
836
  lightning_sdk/mmt/__init__.py,sha256=ExMu90-96bGBnyp5h0CErQszUGB1-PcjC4-R8_NYbeY,117
837
- lightning_sdk/mmt/base.py,sha256=lUB8pAKXTjn_WD6vcJIUMxtZQwrzwxYnaPJbaiPPtMw,12548
838
- lightning_sdk/mmt/mmt.py,sha256=vMAoR3qjb0VRwCXpKpAMxeTEMALmQB9_RK064fTHM8M,11922
839
- lightning_sdk/mmt/v1.py,sha256=8LjZnMSGgsGLeajuPto3gknJwVRvsGfkYVoo5A_UlO8,7917
840
- lightning_sdk/mmt/v2.py,sha256=TsJ8PNluyU0WnNdHvEKWkpJxMwjmyFFJZIaJKCgugIM,8201
837
+ lightning_sdk/mmt/base.py,sha256=V_ysuSjddXksCjkpGy-YVqD9j8b0jA6hkfFz0oW-Jn0,12768
838
+ lightning_sdk/mmt/mmt.py,sha256=Br73TO7TrEdx7VH80CA_gCGDgz3JX4p16R9DdrwQshk,12307
839
+ lightning_sdk/mmt/v1.py,sha256=Ovzwc-mm3PCsyVb8XfvbWUSFGZEI8E5NKayQB1pXvwg,8159
840
+ lightning_sdk/mmt/v2.py,sha256=mUHnBN33UOfD7LX5akGShG7W1q5vGASkVaOTPhHhpDI,8353
841
841
  lightning_sdk/services/__init__.py,sha256=gSWUjccEhMI9CIWL_nbrFHUK2S6TM2725mEzrLMfK1Y,225
842
842
  lightning_sdk/services/file_endpoint.py,sha256=we5HC_o74J4Y6fSP_31jIizi_I_1FO_Rb2qblspD9eE,7855
843
843
  lightning_sdk/services/utilities.py,sha256=IeOx8hc3F8ZevHeKBysh08BXhJliTNzvKp1gwpEfdik,4087
@@ -846,9 +846,9 @@ lightning_sdk/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
846
846
  lightning_sdk/utils/dynamic.py,sha256=glUTO1JC9APtQ6Gr9SO02a3zr56-sPAXM5C3NrTpgyQ,1959
847
847
  lightning_sdk/utils/enum.py,sha256=h2JRzqoBcSlUdanFHmkj_j5DleBHAu1esQYUsdNI-hU,4106
848
848
  lightning_sdk/utils/resolve.py,sha256=RWvlOWLHjaHhR0W0zT3mN719cbzhFfYCKBss38zfv3k,5783
849
- lightning_sdk-0.1.43.dist-info/LICENSE,sha256=uFIuZwj5z-4TeF2UuacPZ1o17HkvKObT8fY50qN84sg,1064
850
- lightning_sdk-0.1.43.dist-info/METADATA,sha256=qsepNbeCeaApc3hDy1vhjhXs-sIoKUwYfMVK_MaKVUA,4031
851
- lightning_sdk-0.1.43.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
852
- lightning_sdk-0.1.43.dist-info/entry_points.txt,sha256=msB9PJWIJ784dX-OP8by51d4IbKYH3Fj1vCuA9oXjHY,68
853
- lightning_sdk-0.1.43.dist-info/top_level.txt,sha256=ps8doKILFXmN7F1mHncShmnQoTxKBRPIcchC8TpoBw4,19
854
- lightning_sdk-0.1.43.dist-info/RECORD,,
849
+ lightning_sdk-0.1.44.dist-info/LICENSE,sha256=uFIuZwj5z-4TeF2UuacPZ1o17HkvKObT8fY50qN84sg,1064
850
+ lightning_sdk-0.1.44.dist-info/METADATA,sha256=FpuwHfzu6vOmo_c85UKIO8Ky54Hm8l0gyUOgV84XC2Y,4031
851
+ lightning_sdk-0.1.44.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
852
+ lightning_sdk-0.1.44.dist-info/entry_points.txt,sha256=msB9PJWIJ784dX-OP8by51d4IbKYH3Fj1vCuA9oXjHY,68
853
+ lightning_sdk-0.1.44.dist-info/top_level.txt,sha256=ps8doKILFXmN7F1mHncShmnQoTxKBRPIcchC8TpoBw4,19
854
+ lightning_sdk-0.1.44.dist-info/RECORD,,