lightning-sdk 2025.10.14__py3-none-any.whl → 2025.10.23__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 +6 -3
- lightning_sdk/api/base_studio_api.py +13 -9
- lightning_sdk/api/job_api.py +4 -1
- lightning_sdk/api/license_api.py +26 -59
- lightning_sdk/api/studio_api.py +7 -2
- lightning_sdk/base_studio.py +30 -17
- lightning_sdk/cli/base_studio/list.py +1 -3
- lightning_sdk/cli/entrypoint.py +11 -34
- lightning_sdk/cli/groups.py +7 -0
- lightning_sdk/cli/license/__init__.py +14 -0
- lightning_sdk/cli/license/get.py +15 -0
- lightning_sdk/cli/license/list.py +45 -0
- lightning_sdk/cli/license/set.py +13 -0
- lightning_sdk/cli/studio/connect.py +42 -92
- lightning_sdk/cli/studio/create.py +23 -1
- lightning_sdk/cli/studio/start.py +12 -2
- lightning_sdk/cli/utils/get_base_studio.py +24 -0
- lightning_sdk/cli/utils/handle_machine_and_gpus_args.py +69 -0
- lightning_sdk/cli/utils/logging.py +121 -0
- lightning_sdk/cli/utils/ssh_connection.py +1 -1
- lightning_sdk/constants.py +1 -0
- lightning_sdk/helpers.py +53 -34
- lightning_sdk/job/base.py +7 -0
- lightning_sdk/job/job.py +8 -0
- lightning_sdk/job/v1.py +3 -0
- lightning_sdk/job/v2.py +4 -0
- lightning_sdk/lightning_cloud/login.py +260 -10
- lightning_sdk/lightning_cloud/openapi/__init__.py +16 -3
- lightning_sdk/lightning_cloud/openapi/api/auth_service_api.py +279 -0
- lightning_sdk/lightning_cloud/openapi/api/k8_s_cluster_service_api.py +117 -0
- lightning_sdk/lightning_cloud/openapi/api/product_license_service_api.py +108 -108
- lightning_sdk/lightning_cloud/openapi/models/__init__.py +16 -3
- lightning_sdk/lightning_cloud/openapi/models/create_machine_request_represents_the_request_to_create_a_machine.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/externalv1_cloud_space_instance_status.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/id_fork_body1.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/license_key_validate_body.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/update1.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_create_license_request.py +175 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_data_connection.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_delete_license_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_external_cluster_spec.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_external_search_user.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_filesystem_metric.py +201 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_get_cloud_space_transfer_estimate_response.py +29 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_incident.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_incident_detail.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_incident_event.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_license.py +227 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_filesystem_metrics_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/{v1_list_product_licenses_response.py → v1_list_license_response.py} +16 -16
- lightning_sdk/lightning_cloud/openapi/models/v1_list_platform_notifications_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_machine.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_platform_notification.py +279 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_reset_api_key_request.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_reset_api_key_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_slack_notifier.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_token_login_request.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_token_login_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_token_owner_type.py +104 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +139 -191
- lightning_sdk/lightning_cloud/openapi/models/{v1_product_license_check_response.py → v1_validate_license_response.py} +21 -21
- lightning_sdk/lightning_cloud/rest_client.py +48 -45
- lightning_sdk/machine.py +5 -0
- lightning_sdk/pipeline/steps.py +1 -0
- lightning_sdk/studio.py +55 -13
- lightning_sdk/utils/config.py +18 -3
- lightning_sdk/utils/license.py +13 -0
- lightning_sdk/utils/resolve.py +6 -1
- {lightning_sdk-2025.10.14.dist-info → lightning_sdk-2025.10.23.dist-info}/METADATA +1 -1
- {lightning_sdk-2025.10.14.dist-info → lightning_sdk-2025.10.23.dist-info}/RECORD +74 -54
- lightning_sdk/lightning_cloud/openapi/models/v1_product_license.py +0 -435
- lightning_sdk/services/license.py +0 -363
- {lightning_sdk-2025.10.14.dist-info → lightning_sdk-2025.10.23.dist-info}/LICENSE +0 -0
- {lightning_sdk-2025.10.14.dist-info → lightning_sdk-2025.10.23.dist-info}/WHEEL +0 -0
- {lightning_sdk-2025.10.14.dist-info → lightning_sdk-2025.10.23.dist-info}/entry_points.txt +0 -0
- {lightning_sdk-2025.10.14.dist-info → lightning_sdk-2025.10.23.dist-info}/top_level.txt +0 -0
|
@@ -28,7 +28,7 @@ if TYPE_CHECKING:
|
|
|
28
28
|
from datetime import datetime
|
|
29
29
|
from lightning_sdk.lightning_cloud.openapi.models import *
|
|
30
30
|
|
|
31
|
-
class
|
|
31
|
+
class V1ValidateLicenseResponse(object):
|
|
32
32
|
"""NOTE: This class is auto generated by the swagger code generator program.
|
|
33
33
|
|
|
34
34
|
Do not edit the class manually.
|
|
@@ -41,40 +41,40 @@ class V1ProductLicenseCheckResponse(object):
|
|
|
41
41
|
and the value is json key in definition.
|
|
42
42
|
"""
|
|
43
43
|
swagger_types = {
|
|
44
|
-
'
|
|
44
|
+
'is_valid': 'bool'
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
attribute_map = {
|
|
48
|
-
'
|
|
48
|
+
'is_valid': 'isValid'
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
def __init__(self,
|
|
52
|
-
"""
|
|
53
|
-
self.
|
|
51
|
+
def __init__(self, is_valid: 'bool' =None): # noqa: E501
|
|
52
|
+
"""V1ValidateLicenseResponse - a model defined in Swagger""" # noqa: E501
|
|
53
|
+
self._is_valid = None
|
|
54
54
|
self.discriminator = None
|
|
55
|
-
if
|
|
56
|
-
self.
|
|
55
|
+
if is_valid is not None:
|
|
56
|
+
self.is_valid = is_valid
|
|
57
57
|
|
|
58
58
|
@property
|
|
59
|
-
def
|
|
60
|
-
"""Gets the
|
|
59
|
+
def is_valid(self) -> 'bool':
|
|
60
|
+
"""Gets the is_valid of this V1ValidateLicenseResponse. # noqa: E501
|
|
61
61
|
|
|
62
62
|
|
|
63
|
-
:return: The
|
|
63
|
+
:return: The is_valid of this V1ValidateLicenseResponse. # noqa: E501
|
|
64
64
|
:rtype: bool
|
|
65
65
|
"""
|
|
66
|
-
return self.
|
|
66
|
+
return self._is_valid
|
|
67
67
|
|
|
68
|
-
@
|
|
69
|
-
def
|
|
70
|
-
"""Sets the
|
|
68
|
+
@is_valid.setter
|
|
69
|
+
def is_valid(self, is_valid: 'bool'):
|
|
70
|
+
"""Sets the is_valid of this V1ValidateLicenseResponse.
|
|
71
71
|
|
|
72
72
|
|
|
73
|
-
:param
|
|
73
|
+
:param is_valid: The is_valid of this V1ValidateLicenseResponse. # noqa: E501
|
|
74
74
|
:type: bool
|
|
75
75
|
"""
|
|
76
76
|
|
|
77
|
-
self.
|
|
77
|
+
self._is_valid = is_valid
|
|
78
78
|
|
|
79
79
|
def to_dict(self) -> dict:
|
|
80
80
|
"""Returns the model properties as a dict"""
|
|
@@ -97,7 +97,7 @@ class V1ProductLicenseCheckResponse(object):
|
|
|
97
97
|
))
|
|
98
98
|
else:
|
|
99
99
|
result[attr] = value
|
|
100
|
-
if issubclass(
|
|
100
|
+
if issubclass(V1ValidateLicenseResponse, dict):
|
|
101
101
|
for key, value in self.items():
|
|
102
102
|
result[key] = value
|
|
103
103
|
|
|
@@ -111,13 +111,13 @@ class V1ProductLicenseCheckResponse(object):
|
|
|
111
111
|
"""For `print` and `pprint`"""
|
|
112
112
|
return self.to_str()
|
|
113
113
|
|
|
114
|
-
def __eq__(self, other: '
|
|
114
|
+
def __eq__(self, other: 'V1ValidateLicenseResponse') -> bool:
|
|
115
115
|
"""Returns true if both objects are equal"""
|
|
116
|
-
if not isinstance(other,
|
|
116
|
+
if not isinstance(other, V1ValidateLicenseResponse):
|
|
117
117
|
return False
|
|
118
118
|
|
|
119
119
|
return self.__dict__ == other.__dict__
|
|
120
120
|
|
|
121
|
-
def __ne__(self, other: '
|
|
121
|
+
def __ne__(self, other: 'V1ValidateLicenseResponse') -> bool:
|
|
122
122
|
"""Returns true if both objects are not equal"""
|
|
123
123
|
return not self == other
|
|
@@ -2,41 +2,43 @@ import functools
|
|
|
2
2
|
import logging
|
|
3
3
|
import time
|
|
4
4
|
from functools import wraps
|
|
5
|
-
from typing import Callable, Optional
|
|
5
|
+
from typing import Any, Callable, Optional
|
|
6
6
|
|
|
7
7
|
import urllib3
|
|
8
|
+
|
|
8
9
|
from lightning_sdk.lightning_cloud import env
|
|
9
10
|
from lightning_sdk.lightning_cloud.login import Auth
|
|
10
11
|
from lightning_sdk.lightning_cloud.openapi import (
|
|
11
12
|
ApiClient,
|
|
13
|
+
AssistantsServiceApi,
|
|
12
14
|
AuthServiceApi,
|
|
15
|
+
BillingServiceApi,
|
|
16
|
+
CloudSpaceEnvironmentTemplateServiceApi,
|
|
13
17
|
CloudSpaceServiceApi,
|
|
14
18
|
ClusterServiceApi,
|
|
15
19
|
Configuration,
|
|
16
20
|
DataConnectionServiceApi,
|
|
21
|
+
DatasetServiceApi,
|
|
22
|
+
DeploymentTemplatesServiceApi,
|
|
23
|
+
EndpointServiceApi,
|
|
24
|
+
JobsServiceApi,
|
|
17
25
|
LightningappInstanceServiceApi,
|
|
18
26
|
LightningappV2ServiceApi,
|
|
19
27
|
LightningworkServiceApi,
|
|
20
|
-
ProjectsServiceApi,
|
|
21
|
-
SecretServiceApi,
|
|
22
|
-
SSHPublicKeyServiceApi,
|
|
23
|
-
DatasetServiceApi,
|
|
24
|
-
OrganizationsServiceApi,
|
|
25
|
-
UserServiceApi,
|
|
26
|
-
BillingServiceApi,
|
|
27
|
-
EndpointServiceApi,
|
|
28
|
-
SlurmJobsUserServiceApi,
|
|
29
28
|
LitLoggerServiceApi,
|
|
30
|
-
JobsServiceApi,
|
|
31
|
-
AssistantsServiceApi,
|
|
32
|
-
StorageServiceApi,
|
|
33
|
-
DeploymentTemplatesServiceApi,
|
|
34
|
-
ModelsStoreApi,
|
|
35
29
|
LitRegistryServiceApi,
|
|
30
|
+
ModelsStoreApi,
|
|
31
|
+
OrganizationsServiceApi,
|
|
36
32
|
PipelinesServiceApi,
|
|
37
|
-
SchedulesServiceApi,
|
|
38
33
|
ProductLicenseServiceApi,
|
|
39
|
-
|
|
34
|
+
ProjectsServiceApi,
|
|
35
|
+
SchedulesServiceApi,
|
|
36
|
+
SDKCommandHistoryServiceApi,
|
|
37
|
+
SecretServiceApi,
|
|
38
|
+
SlurmJobsUserServiceApi,
|
|
39
|
+
SSHPublicKeyServiceApi,
|
|
40
|
+
StorageServiceApi,
|
|
41
|
+
UserServiceApi,
|
|
40
42
|
)
|
|
41
43
|
from lightning_sdk.lightning_cloud.openapi.rest import ApiException
|
|
42
44
|
from lightning_sdk.lightning_cloud.source_code.logs_socket_api import LightningLogsSocketAPI
|
|
@@ -76,34 +78,35 @@ def create_swagger_client(check_context: bool = True, with_auth: bool = True):
|
|
|
76
78
|
|
|
77
79
|
|
|
78
80
|
class GridRestClient(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
81
|
+
LightningLogsSocketAPI,
|
|
82
|
+
LightningappInstanceServiceApi,
|
|
83
|
+
LightningappV2ServiceApi,
|
|
84
|
+
AuthServiceApi,
|
|
85
|
+
CloudSpaceServiceApi,
|
|
86
|
+
ClusterServiceApi,
|
|
87
|
+
ProjectsServiceApi,
|
|
88
|
+
LightningworkServiceApi,
|
|
89
|
+
SecretServiceApi,
|
|
90
|
+
SSHPublicKeyServiceApi,
|
|
91
|
+
DataConnectionServiceApi,
|
|
92
|
+
DatasetServiceApi,
|
|
93
|
+
OrganizationsServiceApi,
|
|
94
|
+
UserServiceApi,
|
|
95
|
+
BillingServiceApi,
|
|
96
|
+
EndpointServiceApi,
|
|
97
|
+
SlurmJobsUserServiceApi,
|
|
98
|
+
LitLoggerServiceApi,
|
|
99
|
+
JobsServiceApi,
|
|
100
|
+
AssistantsServiceApi,
|
|
101
|
+
StorageServiceApi,
|
|
102
|
+
DeploymentTemplatesServiceApi,
|
|
103
|
+
ModelsStoreApi,
|
|
104
|
+
LitRegistryServiceApi,
|
|
105
|
+
PipelinesServiceApi,
|
|
106
|
+
SchedulesServiceApi,
|
|
107
|
+
ProductLicenseServiceApi,
|
|
108
|
+
CloudSpaceEnvironmentTemplateServiceApi,
|
|
109
|
+
SDKCommandHistoryServiceApi,
|
|
107
110
|
):
|
|
108
111
|
|
|
109
112
|
def __init__(self, api_client: Optional[ApiClient] = None, with_auth: bool = True):
|
lightning_sdk/machine.py
CHANGED
|
@@ -34,6 +34,7 @@ class Machine:
|
|
|
34
34
|
|
|
35
35
|
# supported GPU types
|
|
36
36
|
# supported T4 variations
|
|
37
|
+
T4_SMALL: ClassVar["Machine"]
|
|
37
38
|
T4: ClassVar["Machine"]
|
|
38
39
|
T4_X_2: ClassVar["Machine"]
|
|
39
40
|
T4_X_4: ClassVar["Machine"]
|
|
@@ -162,6 +163,7 @@ Machine.DATA_PREP_ULTRA = Machine(
|
|
|
162
163
|
|
|
163
164
|
# GPU machines
|
|
164
165
|
# available T4 machines
|
|
166
|
+
Machine.T4_SMALL = Machine(name="T4_SMALL", slug="lit-t4-1-small", family="T4", accelerator_count=1)
|
|
165
167
|
Machine.T4 = Machine(name="T4", slug="lit-t4-1", family="T4", accelerator_count=1)
|
|
166
168
|
Machine.T4_X_2 = Machine(name="T4_X_2", slug="lit-t4-2", family="T4", accelerator_count=2)
|
|
167
169
|
Machine.T4_X_4 = Machine(name="T4_X_4", slug="lit-t4-4", family="T4", accelerator_count=4)
|
|
@@ -216,3 +218,6 @@ Machine.H200 = Machine(name="H200", slug="lit-h200x-1", family="H200", accelerat
|
|
|
216
218
|
Machine.H200_X_8 = Machine(name="H200_X_8", slug="lit-h200x-8", family="H200", accelerator_count=8)
|
|
217
219
|
# available B200 machines
|
|
218
220
|
Machine.B200_X_8 = Machine(name="B200_X_8", slug="lit-b200x-8", family="B200", accelerator_count=8)
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
DEFAULT_MACHINE = Machine.CPU.name
|
lightning_sdk/pipeline/steps.py
CHANGED
lightning_sdk/studio.py
CHANGED
|
@@ -11,7 +11,7 @@ from lightning_sdk.api.studio_api import StudioApi
|
|
|
11
11
|
from lightning_sdk.base_studio import BaseStudio
|
|
12
12
|
from lightning_sdk.constants import _LIGHTNING_DEBUG
|
|
13
13
|
from lightning_sdk.lightning_cloud.openapi import V1ClusterType
|
|
14
|
-
from lightning_sdk.machine import CloudProvider, Machine
|
|
14
|
+
from lightning_sdk.machine import DEFAULT_MACHINE, CloudProvider, Machine
|
|
15
15
|
from lightning_sdk.organization import Organization
|
|
16
16
|
from lightning_sdk.owner import Owner
|
|
17
17
|
from lightning_sdk.status import Status
|
|
@@ -103,7 +103,16 @@ class Studio:
|
|
|
103
103
|
self._plugins = {}
|
|
104
104
|
self._studio = None
|
|
105
105
|
|
|
106
|
-
|
|
106
|
+
# Check to see if we're inside a studio
|
|
107
|
+
current_studio = None
|
|
108
|
+
studio_id = os.environ.get("LIGHTNING_CLOUD_SPACE_ID", None)
|
|
109
|
+
if studio_id is not None:
|
|
110
|
+
# We're inside a studio, get it by ID
|
|
111
|
+
current_studio = self._studio_api.get_studio_by_id(studio_id=studio_id, teamspace_id=self._teamspace.id)
|
|
112
|
+
|
|
113
|
+
cloud_account = _resolve_deprecated_cluster(
|
|
114
|
+
cloud_account, cluster, current_studio.cluster_id if current_studio else None
|
|
115
|
+
)
|
|
107
116
|
cloud_provider = _resolve_deprecated_provider(cloud_provider, provider)
|
|
108
117
|
|
|
109
118
|
cls_name = self._cls_name
|
|
@@ -119,7 +128,7 @@ class Studio:
|
|
|
119
128
|
|
|
120
129
|
self._studio_type = None
|
|
121
130
|
if studio_type:
|
|
122
|
-
self._base_studio = BaseStudio()
|
|
131
|
+
self._base_studio = BaseStudio(teamspace=self._teamspace)
|
|
123
132
|
self._available_base_studios = self._base_studio.list()
|
|
124
133
|
for bst in self._available_base_studios:
|
|
125
134
|
if (
|
|
@@ -135,14 +144,14 @@ class Studio:
|
|
|
135
144
|
f"Available studio types: "
|
|
136
145
|
f"{[bst.name.lower().replace(' ', '-') for bst in self._available_base_studios]}"
|
|
137
146
|
)
|
|
147
|
+
else:
|
|
148
|
+
if current_studio:
|
|
149
|
+
self._studio_type = current_studio.environment_template_id
|
|
138
150
|
|
|
139
151
|
# Resolve studio name if not provided: explicit → env (LIGHTNING_CLOUD_SPACE_ID) → config defaults
|
|
140
152
|
if name is None and not getattr(self._skip_init, "value", False):
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
# We're inside a studio, get it by ID
|
|
144
|
-
self._studio = self._studio_api.get_studio_by_id(studio_id=studio_id, teamspace_id=self._teamspace.id)
|
|
145
|
-
name = self._studio.name
|
|
153
|
+
if current_studio:
|
|
154
|
+
name = current_studio.name
|
|
146
155
|
else:
|
|
147
156
|
# Try config defaults
|
|
148
157
|
from lightning_sdk.utils.config import Config, DefaultConfigKeys
|
|
@@ -272,7 +281,7 @@ class Studio:
|
|
|
272
281
|
|
|
273
282
|
def start(
|
|
274
283
|
self,
|
|
275
|
-
machine: Union[Machine, str] =
|
|
284
|
+
machine: Optional[Union[Machine, str]] = None,
|
|
276
285
|
interruptible: Optional[bool] = None,
|
|
277
286
|
max_runtime: Optional[int] = None,
|
|
278
287
|
) -> None:
|
|
@@ -287,6 +296,22 @@ class Studio:
|
|
|
287
296
|
Defaults to 3h
|
|
288
297
|
|
|
289
298
|
"""
|
|
299
|
+
# Check to see if we're inside a studio and if its running
|
|
300
|
+
current_studio_machine = None
|
|
301
|
+
studio_id = os.environ.get("LIGHTNING_CLOUD_SPACE_ID", None)
|
|
302
|
+
if studio_id is not None:
|
|
303
|
+
# We're inside a studio, get the machine if it is running
|
|
304
|
+
current_studio = self._studio_api.get_studio_by_id(studio_id=studio_id, teamspace_id=self._teamspace.id)
|
|
305
|
+
current_status = self._studio_api._get_studio_instance_status_from_object(current_studio)
|
|
306
|
+
|
|
307
|
+
if current_status and _internal_status_to_external_status(current_status) == Status.Running:
|
|
308
|
+
current_studio_machine = self._studio_api.get_machine(
|
|
309
|
+
current_studio.id,
|
|
310
|
+
self._teamspace.id,
|
|
311
|
+
current_studio.cluster_id,
|
|
312
|
+
_get_org_id(self._teamspace),
|
|
313
|
+
)
|
|
314
|
+
|
|
290
315
|
status = self.status
|
|
291
316
|
|
|
292
317
|
if interruptible is None:
|
|
@@ -296,9 +321,14 @@ class Studio:
|
|
|
296
321
|
else:
|
|
297
322
|
interruptible = self.teamspace.start_studios_on_interruptible
|
|
298
323
|
|
|
299
|
-
new_machine =
|
|
300
|
-
if not
|
|
301
|
-
new_machine =
|
|
324
|
+
new_machine = DEFAULT_MACHINE
|
|
325
|
+
if machine is not None:
|
|
326
|
+
new_machine = machine
|
|
327
|
+
elif current_studio_machine is not None:
|
|
328
|
+
new_machine = current_studio_machine
|
|
329
|
+
|
|
330
|
+
if not isinstance(new_machine, Machine):
|
|
331
|
+
new_machine = Machine.from_str(new_machine)
|
|
302
332
|
|
|
303
333
|
if status == Status.Running:
|
|
304
334
|
if new_machine != self.machine:
|
|
@@ -353,7 +383,10 @@ class Studio:
|
|
|
353
383
|
self._studio_api.delete_studio(self._studio.id, self._teamspace.id)
|
|
354
384
|
|
|
355
385
|
def duplicate(
|
|
356
|
-
self,
|
|
386
|
+
self,
|
|
387
|
+
target_teamspace: Optional[Union["Teamspace", str]] = None,
|
|
388
|
+
machine: Machine = Machine.CPU,
|
|
389
|
+
name: Optional[str] = None,
|
|
357
390
|
) -> "Studio":
|
|
358
391
|
"""Duplicates the existing Studio.
|
|
359
392
|
|
|
@@ -386,6 +419,7 @@ class Studio:
|
|
|
386
419
|
teamspace_id=self._teamspace.id,
|
|
387
420
|
target_teamspace_id=target_teamspace_id,
|
|
388
421
|
machine=machine,
|
|
422
|
+
new_name=name,
|
|
389
423
|
)
|
|
390
424
|
return Studio(**kwargs)
|
|
391
425
|
|
|
@@ -659,6 +693,14 @@ class Studio:
|
|
|
659
693
|
self._assistant_id = assistant.id
|
|
660
694
|
_logger.info(assistant_info)
|
|
661
695
|
|
|
696
|
+
def rename(self, new_name: str) -> None:
|
|
697
|
+
"""Renames the current Studio to the provided new name."""
|
|
698
|
+
if new_name == self._studio.name:
|
|
699
|
+
return
|
|
700
|
+
|
|
701
|
+
self._studio_api._update_cloudspace(self._studio, self._teamspace.id, "display_name", new_name)
|
|
702
|
+
self._update_studio_reference()
|
|
703
|
+
|
|
662
704
|
@property
|
|
663
705
|
def auto_sleep(self) -> bool:
|
|
664
706
|
"""Returns if a Studio has auto-sleep enabled."""
|
lightning_sdk/utils/config.py
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
|
-
from collections.abc import Mapping
|
|
3
2
|
from dataclasses import dataclass
|
|
4
|
-
from typing import Any, Dict, Optional, Sequence
|
|
3
|
+
from typing import Any, Dict, Mapping, Optional, Sequence
|
|
5
4
|
|
|
6
5
|
import yaml
|
|
7
6
|
|
|
@@ -26,6 +25,8 @@ class DefaultConfigKeys:
|
|
|
26
25
|
cloud_account: str = "cloud_account.name"
|
|
27
26
|
cloud_provider: str = "cloud_provider.name"
|
|
28
27
|
|
|
28
|
+
license: str = "license"
|
|
29
|
+
|
|
29
30
|
|
|
30
31
|
class ConfigProxy:
|
|
31
32
|
def __init__(self, root: "Config", *path: str) -> None:
|
|
@@ -101,6 +102,20 @@ class Config:
|
|
|
101
102
|
Returns:
|
|
102
103
|
The config value if found, None otherwise
|
|
103
104
|
"""
|
|
105
|
+
return self._get_value_type(key_path, str)
|
|
106
|
+
|
|
107
|
+
def get_sub_config(self, key_path: str) -> Optional[Mapping[str, Any]]:
|
|
108
|
+
"""Gets a subconfig from the config using dot notation.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
key_path: the dot-separated path to the subconfig (e.g. "license")
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
The subconfig if found, None otherwise
|
|
115
|
+
"""
|
|
116
|
+
return self._get_value_type(key_path, Mapping)
|
|
117
|
+
|
|
118
|
+
def _get_value_type(self, key_path: str, subtype: type) -> Optional[Any]:
|
|
104
119
|
config = self._load_config()
|
|
105
120
|
if not isinstance(config, Mapping):
|
|
106
121
|
return None
|
|
@@ -111,7 +126,7 @@ class Config:
|
|
|
111
126
|
if not isinstance(curr, dict) or k not in curr:
|
|
112
127
|
return None
|
|
113
128
|
curr = curr[k]
|
|
114
|
-
return curr if isinstance(curr,
|
|
129
|
+
return curr if isinstance(curr, subtype) else None
|
|
115
130
|
|
|
116
131
|
def __getattr__(self, name: str) -> ConfigProxy:
|
|
117
132
|
"""Returns a proxy to the actual values to allow for nested access.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from functools import lru_cache
|
|
2
|
+
|
|
3
|
+
from lightning_sdk.api.license_api import LicenseApi
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class License:
|
|
7
|
+
def __init__(self, license_key: str, product_name: str) -> None:
|
|
8
|
+
self.license_key = license_key
|
|
9
|
+
self.product_name = product_name
|
|
10
|
+
|
|
11
|
+
@lru_cache(maxsize=1) # noqa: B019
|
|
12
|
+
def validate(self) -> bool:
|
|
13
|
+
return LicenseApi(self.license_key).validate_license(self.license_key, self.product_name)
|
lightning_sdk/utils/resolve.py
CHANGED
|
@@ -75,7 +75,9 @@ def _resolve_deprecated_provider(
|
|
|
75
75
|
return cloud_provider
|
|
76
76
|
|
|
77
77
|
|
|
78
|
-
def _resolve_deprecated_cluster(
|
|
78
|
+
def _resolve_deprecated_cluster(
|
|
79
|
+
cloud_account: Optional[str], cluster: Optional[str], current_cloud_account: Optional[str] = None
|
|
80
|
+
) -> Optional[str]:
|
|
79
81
|
if cluster is not None:
|
|
80
82
|
if cloud_account is not None:
|
|
81
83
|
raise ValueError(
|
|
@@ -96,6 +98,9 @@ def _resolve_deprecated_cluster(cloud_account: Optional[str], cluster: Optional[
|
|
|
96
98
|
config = Config()
|
|
97
99
|
cloud_account = config.get_value(DefaultConfigKeys.cloud_account)
|
|
98
100
|
|
|
101
|
+
if cloud_account is None:
|
|
102
|
+
cloud_account = current_cloud_account
|
|
103
|
+
|
|
99
104
|
return cloud_account
|
|
100
105
|
|
|
101
106
|
|