lightning-sdk 0.2.8__py3-none-any.whl → 0.2.10__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/cluster_api.py +22 -0
- lightning_sdk/api/deployment_api.py +1 -0
- lightning_sdk/api/lit_container_api.py +24 -5
- lightning_sdk/api/teamspace_api.py +22 -17
- lightning_sdk/api/utils.py +1 -1
- lightning_sdk/cli/clusters_menu.py +46 -0
- lightning_sdk/cli/entrypoint.py +2 -2
- lightning_sdk/cli/list.py +25 -5
- lightning_sdk/cli/serve.py +232 -24
- lightning_sdk/cli/upload.py +4 -1
- lightning_sdk/deployment/deployment.py +5 -2
- lightning_sdk/lightning_cloud/openapi/__init__.py +10 -0
- lightning_sdk/lightning_cloud/openapi/api/cloud_space_service_api.py +303 -0
- lightning_sdk/lightning_cloud/openapi/models/__init__.py +10 -0
- lightning_sdk/lightning_cloud/openapi/models/cloudspace_id_systemmetrics_body.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/cluster_id_capacityreservations_body.py +55 -3
- lightning_sdk/lightning_cloud/openapi/models/create.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/orgs_id_body.py +55 -3
- lightning_sdk/lightning_cloud/openapi/models/update.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_billing_tier.py +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_cold_start_metrics.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_cold_start_metrics_stats.py +357 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_environment_template_config.py +29 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_cloudflare_v1.py +227 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_accelerator.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_capacity_reservation.py +55 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_security_options.py +79 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_spec.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_create_cloud_space_environment_template_request.py +29 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_create_cluster_capacity_reservation_response.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_data_connection.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_gcp_direct_vpc.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_get_cloud_space_cold_start_metrics_stats_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_get_cloud_space_instance_system_metrics_aggregate_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_get_user_response.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_google_cloud_direct_v1.py +43 -17
- lightning_sdk/lightning_cloud/openapi/models/v1_organization.py +55 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_project_cluster_binding.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_r2_data_connection.py +253 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_report_cloud_space_instance_system_metrics_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_server_alert_phase.py +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_system_metrics_aggregated.py +227 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_update_user_request.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +104 -208
- lightning_sdk/lightning_cloud/openapi/models/v1_validate_data_connection_response.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_weka_data_connection.py +201 -0
- lightning_sdk/lightning_cloud/openapi/models/validate.py +27 -1
- lightning_sdk/lit_container.py +25 -7
- lightning_sdk/models.py +26 -8
- lightning_sdk/serve.py +3 -20
- lightning_sdk/teamspace.py +21 -4
- lightning_sdk/utils/resolve.py +11 -4
- {lightning_sdk-0.2.8.dist-info → lightning_sdk-0.2.10.dist-info}/METADATA +1 -1
- {lightning_sdk-0.2.8.dist-info → lightning_sdk-0.2.10.dist-info}/RECORD +60 -48
- {lightning_sdk-0.2.8.dist-info → lightning_sdk-0.2.10.dist-info}/LICENSE +0 -0
- {lightning_sdk-0.2.8.dist-info → lightning_sdk-0.2.10.dist-info}/WHEEL +0 -0
- {lightning_sdk-0.2.8.dist-info → lightning_sdk-0.2.10.dist-info}/entry_points.txt +0 -0
- {lightning_sdk-0.2.8.dist-info → lightning_sdk-0.2.10.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
external/v1/auth_service.proto
|
|
5
|
+
|
|
6
|
+
No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) # noqa: E501
|
|
7
|
+
|
|
8
|
+
OpenAPI spec version: version not set
|
|
9
|
+
|
|
10
|
+
Generated by: https://github.com/swagger-api/swagger-codegen.git
|
|
11
|
+
|
|
12
|
+
NOTE
|
|
13
|
+
----
|
|
14
|
+
standard swagger-codegen-cli for this python client has been modified
|
|
15
|
+
by custom templates. The purpose of these templates is to include
|
|
16
|
+
typing information in the API and Model code. Please refer to the
|
|
17
|
+
main grid repository for more info
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
import pprint
|
|
21
|
+
import re # noqa: F401
|
|
22
|
+
|
|
23
|
+
from typing import TYPE_CHECKING
|
|
24
|
+
|
|
25
|
+
import six
|
|
26
|
+
|
|
27
|
+
if TYPE_CHECKING:
|
|
28
|
+
from datetime import datetime
|
|
29
|
+
from lightning_sdk.lightning_cloud.openapi.models import *
|
|
30
|
+
|
|
31
|
+
class V1WekaDataConnection(object):
|
|
32
|
+
"""NOTE: This class is auto generated by the swagger code generator program.
|
|
33
|
+
|
|
34
|
+
Do not edit the class manually.
|
|
35
|
+
"""
|
|
36
|
+
"""
|
|
37
|
+
Attributes:
|
|
38
|
+
swagger_types (dict): The key is attribute name
|
|
39
|
+
and the value is attribute type.
|
|
40
|
+
attribute_map (dict): The key is attribute name
|
|
41
|
+
and the value is json key in definition.
|
|
42
|
+
"""
|
|
43
|
+
swagger_types = {
|
|
44
|
+
'backend_ips': 'list[str]',
|
|
45
|
+
'file_system_name': 'str',
|
|
46
|
+
'memory_mb': 'int',
|
|
47
|
+
'readonly': 'bool'
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
attribute_map = {
|
|
51
|
+
'backend_ips': 'backendIps',
|
|
52
|
+
'file_system_name': 'fileSystemName',
|
|
53
|
+
'memory_mb': 'memoryMb',
|
|
54
|
+
'readonly': 'readonly'
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
def __init__(self, backend_ips: 'list[str]' =None, file_system_name: 'str' =None, memory_mb: 'int' =None, readonly: 'bool' =None): # noqa: E501
|
|
58
|
+
"""V1WekaDataConnection - a model defined in Swagger""" # noqa: E501
|
|
59
|
+
self._backend_ips = None
|
|
60
|
+
self._file_system_name = None
|
|
61
|
+
self._memory_mb = None
|
|
62
|
+
self._readonly = None
|
|
63
|
+
self.discriminator = None
|
|
64
|
+
if backend_ips is not None:
|
|
65
|
+
self.backend_ips = backend_ips
|
|
66
|
+
if file_system_name is not None:
|
|
67
|
+
self.file_system_name = file_system_name
|
|
68
|
+
if memory_mb is not None:
|
|
69
|
+
self.memory_mb = memory_mb
|
|
70
|
+
if readonly is not None:
|
|
71
|
+
self.readonly = readonly
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def backend_ips(self) -> 'list[str]':
|
|
75
|
+
"""Gets the backend_ips of this V1WekaDataConnection. # noqa: E501
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
:return: The backend_ips of this V1WekaDataConnection. # noqa: E501
|
|
79
|
+
:rtype: list[str]
|
|
80
|
+
"""
|
|
81
|
+
return self._backend_ips
|
|
82
|
+
|
|
83
|
+
@backend_ips.setter
|
|
84
|
+
def backend_ips(self, backend_ips: 'list[str]'):
|
|
85
|
+
"""Sets the backend_ips of this V1WekaDataConnection.
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
:param backend_ips: The backend_ips of this V1WekaDataConnection. # noqa: E501
|
|
89
|
+
:type: list[str]
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
self._backend_ips = backend_ips
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def file_system_name(self) -> 'str':
|
|
96
|
+
"""Gets the file_system_name of this V1WekaDataConnection. # noqa: E501
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
:return: The file_system_name of this V1WekaDataConnection. # noqa: E501
|
|
100
|
+
:rtype: str
|
|
101
|
+
"""
|
|
102
|
+
return self._file_system_name
|
|
103
|
+
|
|
104
|
+
@file_system_name.setter
|
|
105
|
+
def file_system_name(self, file_system_name: 'str'):
|
|
106
|
+
"""Sets the file_system_name of this V1WekaDataConnection.
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
:param file_system_name: The file_system_name of this V1WekaDataConnection. # noqa: E501
|
|
110
|
+
:type: str
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
self._file_system_name = file_system_name
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def memory_mb(self) -> 'int':
|
|
117
|
+
"""Gets the memory_mb of this V1WekaDataConnection. # noqa: E501
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
:return: The memory_mb of this V1WekaDataConnection. # noqa: E501
|
|
121
|
+
:rtype: int
|
|
122
|
+
"""
|
|
123
|
+
return self._memory_mb
|
|
124
|
+
|
|
125
|
+
@memory_mb.setter
|
|
126
|
+
def memory_mb(self, memory_mb: 'int'):
|
|
127
|
+
"""Sets the memory_mb of this V1WekaDataConnection.
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
:param memory_mb: The memory_mb of this V1WekaDataConnection. # noqa: E501
|
|
131
|
+
:type: int
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
self._memory_mb = memory_mb
|
|
135
|
+
|
|
136
|
+
@property
|
|
137
|
+
def readonly(self) -> 'bool':
|
|
138
|
+
"""Gets the readonly of this V1WekaDataConnection. # noqa: E501
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
:return: The readonly of this V1WekaDataConnection. # noqa: E501
|
|
142
|
+
:rtype: bool
|
|
143
|
+
"""
|
|
144
|
+
return self._readonly
|
|
145
|
+
|
|
146
|
+
@readonly.setter
|
|
147
|
+
def readonly(self, readonly: 'bool'):
|
|
148
|
+
"""Sets the readonly of this V1WekaDataConnection.
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
:param readonly: The readonly of this V1WekaDataConnection. # noqa: E501
|
|
152
|
+
:type: bool
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
self._readonly = readonly
|
|
156
|
+
|
|
157
|
+
def to_dict(self) -> dict:
|
|
158
|
+
"""Returns the model properties as a dict"""
|
|
159
|
+
result = {}
|
|
160
|
+
|
|
161
|
+
for attr, _ in six.iteritems(self.swagger_types):
|
|
162
|
+
value = getattr(self, attr)
|
|
163
|
+
if isinstance(value, list):
|
|
164
|
+
result[attr] = list(map(
|
|
165
|
+
lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
|
|
166
|
+
value
|
|
167
|
+
))
|
|
168
|
+
elif hasattr(value, "to_dict"):
|
|
169
|
+
result[attr] = value.to_dict()
|
|
170
|
+
elif isinstance(value, dict):
|
|
171
|
+
result[attr] = dict(map(
|
|
172
|
+
lambda item: (item[0], item[1].to_dict())
|
|
173
|
+
if hasattr(item[1], "to_dict") else item,
|
|
174
|
+
value.items()
|
|
175
|
+
))
|
|
176
|
+
else:
|
|
177
|
+
result[attr] = value
|
|
178
|
+
if issubclass(V1WekaDataConnection, dict):
|
|
179
|
+
for key, value in self.items():
|
|
180
|
+
result[key] = value
|
|
181
|
+
|
|
182
|
+
return result
|
|
183
|
+
|
|
184
|
+
def to_str(self) -> str:
|
|
185
|
+
"""Returns the string representation of the model"""
|
|
186
|
+
return pprint.pformat(self.to_dict())
|
|
187
|
+
|
|
188
|
+
def __repr__(self) -> str:
|
|
189
|
+
"""For `print` and `pprint`"""
|
|
190
|
+
return self.to_str()
|
|
191
|
+
|
|
192
|
+
def __eq__(self, other: 'V1WekaDataConnection') -> bool:
|
|
193
|
+
"""Returns true if both objects are equal"""
|
|
194
|
+
if not isinstance(other, V1WekaDataConnection):
|
|
195
|
+
return False
|
|
196
|
+
|
|
197
|
+
return self.__dict__ == other.__dict__
|
|
198
|
+
|
|
199
|
+
def __ne__(self, other: 'V1WekaDataConnection') -> bool:
|
|
200
|
+
"""Returns true if both objects are not equal"""
|
|
201
|
+
return not self == other
|
|
@@ -48,6 +48,7 @@ class Validate(object):
|
|
|
48
48
|
'filestore': 'V1FilestoreDataConnection',
|
|
49
49
|
'gcp': 'V1GcpDataConnection',
|
|
50
50
|
'gcs_folder': 'V1GCSFolderDataConnection',
|
|
51
|
+
'r2': 'V1R2DataConnection',
|
|
51
52
|
's3_folder': 'V1S3FolderDataConnection'
|
|
52
53
|
}
|
|
53
54
|
|
|
@@ -59,10 +60,11 @@ class Validate(object):
|
|
|
59
60
|
'filestore': 'filestore',
|
|
60
61
|
'gcp': 'gcp',
|
|
61
62
|
'gcs_folder': 'gcsFolder',
|
|
63
|
+
'r2': 'r2',
|
|
62
64
|
's3_folder': 's3Folder'
|
|
63
65
|
}
|
|
64
66
|
|
|
65
|
-
def __init__(self, aws: 'V1AwsDataConnection' =None, check_is_public: 'bool' =None, cluster_ids: 'list[str]' =None, efs: 'V1EfsConfig' =None, filestore: 'V1FilestoreDataConnection' =None, gcp: 'V1GcpDataConnection' =None, gcs_folder: 'V1GCSFolderDataConnection' =None, s3_folder: 'V1S3FolderDataConnection' =None): # noqa: E501
|
|
67
|
+
def __init__(self, aws: 'V1AwsDataConnection' =None, check_is_public: 'bool' =None, cluster_ids: 'list[str]' =None, efs: 'V1EfsConfig' =None, filestore: 'V1FilestoreDataConnection' =None, gcp: 'V1GcpDataConnection' =None, gcs_folder: 'V1GCSFolderDataConnection' =None, r2: 'V1R2DataConnection' =None, s3_folder: 'V1S3FolderDataConnection' =None): # noqa: E501
|
|
66
68
|
"""Validate - a model defined in Swagger""" # noqa: E501
|
|
67
69
|
self._aws = None
|
|
68
70
|
self._check_is_public = None
|
|
@@ -71,6 +73,7 @@ class Validate(object):
|
|
|
71
73
|
self._filestore = None
|
|
72
74
|
self._gcp = None
|
|
73
75
|
self._gcs_folder = None
|
|
76
|
+
self._r2 = None
|
|
74
77
|
self._s3_folder = None
|
|
75
78
|
self.discriminator = None
|
|
76
79
|
if aws is not None:
|
|
@@ -87,6 +90,8 @@ class Validate(object):
|
|
|
87
90
|
self.gcp = gcp
|
|
88
91
|
if gcs_folder is not None:
|
|
89
92
|
self.gcs_folder = gcs_folder
|
|
93
|
+
if r2 is not None:
|
|
94
|
+
self.r2 = r2
|
|
90
95
|
if s3_folder is not None:
|
|
91
96
|
self.s3_folder = s3_folder
|
|
92
97
|
|
|
@@ -237,6 +242,27 @@ class Validate(object):
|
|
|
237
242
|
|
|
238
243
|
self._gcs_folder = gcs_folder
|
|
239
244
|
|
|
245
|
+
@property
|
|
246
|
+
def r2(self) -> 'V1R2DataConnection':
|
|
247
|
+
"""Gets the r2 of this Validate. # noqa: E501
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
:return: The r2 of this Validate. # noqa: E501
|
|
251
|
+
:rtype: V1R2DataConnection
|
|
252
|
+
"""
|
|
253
|
+
return self._r2
|
|
254
|
+
|
|
255
|
+
@r2.setter
|
|
256
|
+
def r2(self, r2: 'V1R2DataConnection'):
|
|
257
|
+
"""Sets the r2 of this Validate.
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
:param r2: The r2 of this Validate. # noqa: E501
|
|
261
|
+
:type: V1R2DataConnection
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
self._r2 = r2
|
|
265
|
+
|
|
240
266
|
@property
|
|
241
267
|
def s3_folder(self) -> 'V1S3FolderDataConnection':
|
|
242
268
|
"""Gets the s3_folder of this Validate. # noqa: E501
|
lightning_sdk/lit_container.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
from typing import Dict, List, Optional
|
|
3
3
|
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
|
|
4
6
|
from lightning_sdk.api.lit_container_api import LitContainerApi
|
|
5
7
|
from lightning_sdk.utils.resolve import _resolve_teamspace
|
|
6
8
|
|
|
@@ -10,7 +12,7 @@ class LitContainer:
|
|
|
10
12
|
self._api = LitContainerApi()
|
|
11
13
|
|
|
12
14
|
def list_containers(
|
|
13
|
-
self, teamspace: str, org: Optional[str] = None, user: Optional[str] = None
|
|
15
|
+
self, teamspace: str, org: Optional[str] = None, user: Optional[str] = None, cloud_account: Optional[str] = None
|
|
14
16
|
) -> List[Dict[str, str]]:
|
|
15
17
|
"""List available docker repositories.
|
|
16
18
|
|
|
@@ -18,16 +20,19 @@ class LitContainer:
|
|
|
18
20
|
teamspace: The teamspace to list containers from.
|
|
19
21
|
org: The organization to list containers from.
|
|
20
22
|
user: The user to list the containers from.
|
|
23
|
+
cloud_account: The cloud account to list the containers from.
|
|
21
24
|
|
|
22
25
|
Returns:
|
|
23
26
|
A list of dictionaries containing repository details.
|
|
24
27
|
"""
|
|
28
|
+
console = Console()
|
|
25
29
|
try:
|
|
26
30
|
teamspace = _resolve_teamspace(teamspace=teamspace, org=org, user=user)
|
|
27
|
-
except Exception
|
|
28
|
-
|
|
31
|
+
except Exception:
|
|
32
|
+
console.print(f"[bold red]Could not resolve teamspace: {teamspace}[/bold red]")
|
|
33
|
+
return []
|
|
29
34
|
project_id = teamspace.id
|
|
30
|
-
repositories = self._api.list_containers(project_id)
|
|
35
|
+
repositories = self._api.list_containers(project_id, cloud_account=cloud_account)
|
|
31
36
|
table = []
|
|
32
37
|
for repo in repositories:
|
|
33
38
|
created_date = repo.creation_time
|
|
@@ -39,8 +44,11 @@ class LitContainer:
|
|
|
39
44
|
table.append(
|
|
40
45
|
{
|
|
41
46
|
"REPOSITORY": repo.name,
|
|
42
|
-
"
|
|
47
|
+
"CLOUD ACCOUNT": cloud_account
|
|
48
|
+
if cloud_account != "" and cloud_account is not None
|
|
49
|
+
else "Lightning cloud",
|
|
43
50
|
"CREATED": created,
|
|
51
|
+
"LATEST TAG": repo.latest_artifact.tag_name,
|
|
44
52
|
}
|
|
45
53
|
)
|
|
46
54
|
return table
|
|
@@ -72,7 +80,8 @@ class LitContainer:
|
|
|
72
80
|
tag: str = "latest",
|
|
73
81
|
cloud_account: Optional[str] = None,
|
|
74
82
|
platform: Optional[str] = "linux/amd64",
|
|
75
|
-
|
|
83
|
+
return_final_dict: bool = False,
|
|
84
|
+
) -> Optional[Dict]:
|
|
76
85
|
"""Upload a container to the docker registry.
|
|
77
86
|
|
|
78
87
|
Args:
|
|
@@ -83,15 +92,24 @@ class LitContainer:
|
|
|
83
92
|
tag: The tag to use for the container.
|
|
84
93
|
cloud_account: The cloud account where the container is stored.
|
|
85
94
|
platform: The platform the container is meant to run on.
|
|
95
|
+
return_final_dict: Instructs function to return metadata about container location in platform.
|
|
86
96
|
"""
|
|
87
97
|
try:
|
|
88
98
|
teamspace = _resolve_teamspace(teamspace=teamspace, org=org, user=user)
|
|
89
99
|
except Exception as e:
|
|
90
100
|
raise ValueError(f"Could not resolve teamspace: {e}") from e
|
|
91
101
|
|
|
92
|
-
resp = self._api.upload_container(
|
|
102
|
+
resp = self._api.upload_container(
|
|
103
|
+
container, teamspace, tag, cloud_account, platform=platform, return_final_dict=return_final_dict
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
final_dict = None
|
|
93
107
|
for line in resp:
|
|
94
108
|
print(line)
|
|
109
|
+
if return_final_dict and isinstance(line, dict) and line.get("finish") is True:
|
|
110
|
+
final_dict = line
|
|
111
|
+
|
|
112
|
+
return final_dict if return_final_dict else None
|
|
95
113
|
|
|
96
114
|
def download_container(
|
|
97
115
|
self,
|
lightning_sdk/models.py
CHANGED
|
@@ -85,7 +85,7 @@ def _extend_model_name_with_teamspace(name: str) -> str:
|
|
|
85
85
|
return f"{teamspace.owner.name}/{teamspace.name}/{name}"
|
|
86
86
|
|
|
87
87
|
|
|
88
|
-
def
|
|
88
|
+
def _parse_org_teamspace_model_version(name: str) -> Tuple[str, str, str, Optional[str]]:
|
|
89
89
|
"""Parse the name argument into its components."""
|
|
90
90
|
try:
|
|
91
91
|
org_name, teamspace_name, model_name = name.split("/")
|
|
@@ -95,7 +95,7 @@ def _parse_model_name_and_version(name: str) -> Tuple[str, str, str, str]:
|
|
|
95
95
|
) from err
|
|
96
96
|
parts = model_name.split(":")
|
|
97
97
|
if len(parts) == 1:
|
|
98
|
-
return org_name, teamspace_name, parts[0],
|
|
98
|
+
return org_name, teamspace_name, parts[0], None
|
|
99
99
|
if len(parts) == 2:
|
|
100
100
|
return org_name, teamspace_name, parts[0], parts[1]
|
|
101
101
|
# The rest of the validation for name and version happens in the backend
|
|
@@ -119,17 +119,14 @@ def download_model(
|
|
|
119
119
|
progress_bar: Whether to show a progress bar when downloading.
|
|
120
120
|
"""
|
|
121
121
|
name = _extend_model_name_with_teamspace(name)
|
|
122
|
-
teamspace_owner_name, teamspace_name, model_name, version =
|
|
123
|
-
|
|
124
|
-
download_dir = Path(download_dir)
|
|
125
|
-
|
|
122
|
+
teamspace_owner_name, teamspace_name, model_name, version = _parse_org_teamspace_model_version(name)
|
|
126
123
|
api = TeamspaceApi()
|
|
127
124
|
|
|
128
125
|
try:
|
|
129
126
|
return api.download_model_files(
|
|
130
127
|
name=model_name,
|
|
131
128
|
version=version,
|
|
132
|
-
download_dir=download_dir,
|
|
129
|
+
download_dir=Path(download_dir),
|
|
133
130
|
teamspace_name=teamspace_name,
|
|
134
131
|
teamspace_owner_name=teamspace_owner_name,
|
|
135
132
|
progress_bar=progress_bar,
|
|
@@ -147,6 +144,7 @@ def upload_model(
|
|
|
147
144
|
path: Union[str, Path, List[Union[str, Path]]] = ".",
|
|
148
145
|
cloud_account: Optional[str] = None,
|
|
149
146
|
progress_bar: bool = True,
|
|
147
|
+
metadata: Optional[Dict[str, Any]] = None,
|
|
150
148
|
) -> UploadedModelInfo:
|
|
151
149
|
"""Upload a Model.
|
|
152
150
|
|
|
@@ -157,13 +155,33 @@ def upload_model(
|
|
|
157
155
|
cloud_account: The name of the cloud account to store the Model in.
|
|
158
156
|
If not provided, the default cloud account for the Teamspace will be used.
|
|
159
157
|
progress_bar: Whether to show a progress bar for the upload.
|
|
158
|
+
metadata: Metadata to attach to the uploaded model.
|
|
159
|
+
If not provided, an empty dictionary will be used.
|
|
160
160
|
"""
|
|
161
161
|
name = _extend_model_name_with_teamspace(name)
|
|
162
|
-
org_name, teamspace_name, model_name,
|
|
162
|
+
org_name, teamspace_name, model_name, version = _parse_org_teamspace_model_version(name)
|
|
163
163
|
teamspace = _get_teamspace(name=teamspace_name, organization=org_name)
|
|
164
164
|
return teamspace.upload_model(
|
|
165
165
|
path=path,
|
|
166
166
|
name=model_name,
|
|
167
|
+
version=version,
|
|
167
168
|
cloud_account=cloud_account,
|
|
168
169
|
progress_bar=progress_bar,
|
|
170
|
+
metadata=metadata,
|
|
169
171
|
)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def delete_model(
|
|
175
|
+
name: str,
|
|
176
|
+
) -> None:
|
|
177
|
+
"""Delete a model or a version of model.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
name: The name of the model you want to delete or with specified version it deltes only that version.
|
|
181
|
+
This should have the format <ORGANIZATION-NAME>/<TEAMSPACE-NAME>/<MODEL-NAME> for full model deletion
|
|
182
|
+
or <ORGANIZATION-NAME>/<TEAMSPACE-NAME>/<MODEL-NAME>:<VERSION> for version deletion.
|
|
183
|
+
"""
|
|
184
|
+
name = _extend_model_name_with_teamspace(name)
|
|
185
|
+
org_name, teamspace_name, model_name, version = _parse_org_teamspace_model_version(name)
|
|
186
|
+
teamspace = _get_teamspace(name=teamspace_name, organization=org_name)
|
|
187
|
+
teamspace.delete_model(name=model_name)
|
lightning_sdk/serve.py
CHANGED
|
@@ -3,7 +3,6 @@ import shlex
|
|
|
3
3
|
import subprocess
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
from typing import Generator, List, Optional, Union
|
|
6
|
-
from urllib.parse import urlencode
|
|
7
6
|
|
|
8
7
|
import docker
|
|
9
8
|
from rich.console import Console
|
|
@@ -12,9 +11,7 @@ from lightning_sdk import Deployment, Machine, Teamspace
|
|
|
12
11
|
from lightning_sdk.api.deployment_api import AutoScaleConfig, DeploymentApi, Env, Secret
|
|
13
12
|
from lightning_sdk.api.lit_container_api import LitContainerApi
|
|
14
13
|
from lightning_sdk.api.utils import _get_cloud_url, _get_registry_url
|
|
15
|
-
from lightning_sdk.lightning_cloud import env
|
|
16
14
|
from lightning_sdk.lightning_cloud.env import LIGHTNING_CLOUD_URL
|
|
17
|
-
from lightning_sdk.lightning_cloud.login import Auth, AuthServer
|
|
18
15
|
|
|
19
16
|
_DOCKER_NOT_RUNNING_MSG = (
|
|
20
17
|
"Deploying LitServe requires Docker to be running on the machine. "
|
|
@@ -23,23 +20,6 @@ _DOCKER_NOT_RUNNING_MSG = (
|
|
|
23
20
|
)
|
|
24
21
|
|
|
25
22
|
|
|
26
|
-
class _AuthServer(AuthServer):
|
|
27
|
-
def get_auth_url(self, port: int) -> str:
|
|
28
|
-
redirect_uri = f"http://localhost:{port}/login-complete"
|
|
29
|
-
params = urlencode({"redirectTo": redirect_uri, "inviteCode": "litserve"})
|
|
30
|
-
return f"{env.LIGHTNING_CLOUD_URL}/sign-in?{params}"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
class _Auth(Auth):
|
|
34
|
-
def _run_server(self) -> None:
|
|
35
|
-
_AuthServer().login_with_browser(self)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
def authenticate() -> None:
|
|
39
|
-
auth = _Auth()
|
|
40
|
-
auth.authenticate()
|
|
41
|
-
|
|
42
|
-
|
|
43
23
|
class _LitServeDeployer:
|
|
44
24
|
def __init__(self, name: Optional[str], teamspace: Optional[Teamspace]) -> None:
|
|
45
25
|
self.name = name
|
|
@@ -268,6 +248,7 @@ Update [underline]{os.path.abspath("Dockerfile")}[/underline] to add any additio
|
|
|
268
248
|
cloud_account: Optional[str] = None,
|
|
269
249
|
port: Optional[int] = 8000,
|
|
270
250
|
include_credentials: Optional[bool] = True,
|
|
251
|
+
cloudspace_id: Optional[str] = None,
|
|
271
252
|
) -> dict:
|
|
272
253
|
"""Run a deployment on the cloud. If the deployment already exists, it will be updated.
|
|
273
254
|
|
|
@@ -284,6 +265,7 @@ Update [underline]{os.path.abspath("Dockerfile")}[/underline] to add any additio
|
|
|
284
265
|
cloud_account: The cloud account to run the deployment on. Defaults to None.
|
|
285
266
|
port: The port to run the deployment on. Defaults to 8000.
|
|
286
267
|
include_credentials: Whether to include credentials in the deployment. Defaults to True.
|
|
268
|
+
cloudspace_id: Connect to a Studio.
|
|
287
269
|
|
|
288
270
|
Returns:
|
|
289
271
|
dict: The deployment and the URL of the deployment.
|
|
@@ -317,6 +299,7 @@ Update [underline]{os.path.abspath("Dockerfile")}[/underline] to add any additio
|
|
|
317
299
|
cloud_account=cloud_account,
|
|
318
300
|
ports=[port],
|
|
319
301
|
include_credentials=include_credentials,
|
|
302
|
+
cloudspace_id=cloudspace_id,
|
|
320
303
|
)
|
|
321
304
|
|
|
322
305
|
return {"deployment": deployment, "url": url}
|
lightning_sdk/teamspace.py
CHANGED
|
@@ -3,8 +3,10 @@ import warnings
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
from typing import TYPE_CHECKING, List, Optional, Tuple, Union
|
|
5
5
|
|
|
6
|
+
import lightning_sdk
|
|
6
7
|
from lightning_sdk.agents import Agent
|
|
7
8
|
from lightning_sdk.api import TeamspaceApi
|
|
9
|
+
from lightning_sdk.lightning_cloud.openapi import V1ProjectClusterBinding
|
|
8
10
|
from lightning_sdk.machine import Machine
|
|
9
11
|
from lightning_sdk.models import UploadedModelInfo
|
|
10
12
|
from lightning_sdk.organization import Organization
|
|
@@ -124,6 +126,11 @@ class Teamspace:
|
|
|
124
126
|
clusters = self._teamspace_api.list_cloud_accounts(teamspace_id=self.id)
|
|
125
127
|
return [cl.cluster_name for cl in clusters]
|
|
126
128
|
|
|
129
|
+
@property
|
|
130
|
+
def cloud_account_objs(self) -> List[V1ProjectClusterBinding]:
|
|
131
|
+
"""All cloud accounts associated with that teamspace."""
|
|
132
|
+
return self._teamspace_api.list_cloud_accounts(teamspace_id=self.id)
|
|
133
|
+
|
|
127
134
|
@property
|
|
128
135
|
def clusters(self) -> List[str]:
|
|
129
136
|
"""All clusters associated with that teamspace."""
|
|
@@ -245,17 +252,22 @@ class Teamspace:
|
|
|
245
252
|
self,
|
|
246
253
|
path: Union[str, Path, List[Union[str, Path]]],
|
|
247
254
|
name: str,
|
|
255
|
+
version: Optional[str] = None,
|
|
248
256
|
cloud_account: Optional[str] = None,
|
|
249
257
|
progress_bar: bool = True,
|
|
258
|
+
metadata: Optional[dict] = None,
|
|
250
259
|
) -> UploadedModelInfo:
|
|
251
260
|
"""Upload a local checkpoint file to the model store.
|
|
252
261
|
|
|
253
262
|
Args:
|
|
254
263
|
path: Path to the model file or folder to upload.
|
|
255
264
|
name: Name tag of the model to upload.
|
|
265
|
+
version: Version tag of the model to upload.
|
|
266
|
+
If not provided, the ``vX`` version will be used where X is running index.
|
|
256
267
|
cloud_account: The name of the cloud account to store the Model in.
|
|
257
268
|
If not provided, the default cloud account for the Teamspace will be used.
|
|
258
269
|
progress_bar: Whether to show a progress bar for the upload.
|
|
270
|
+
metadata: Metadata to attach to the model. Can be a dictionary.
|
|
259
271
|
"""
|
|
260
272
|
if not path:
|
|
261
273
|
raise ValueError("No path provided to upload")
|
|
@@ -281,13 +293,18 @@ class Teamspace:
|
|
|
281
293
|
f"The listed files are: {file_paths}\nThe relative paths are: {relative_paths}"
|
|
282
294
|
)
|
|
283
295
|
|
|
284
|
-
cloud_account
|
|
285
|
-
self._teamspace_api._determine_cloud_account(self.id)
|
|
286
|
-
|
|
296
|
+
if cloud_account is None:
|
|
297
|
+
cloud_account = self._teamspace_api._determine_cloud_account(self.id)
|
|
298
|
+
if not metadata:
|
|
299
|
+
metadata = {}
|
|
300
|
+
if not isinstance(metadata, dict):
|
|
301
|
+
raise TypeError(f"Metadata must be a dictionary, but provided {type(metadata)}")
|
|
302
|
+
metadata.update({"lightning-sdk": lightning_sdk.__version__})
|
|
287
303
|
|
|
288
304
|
model = self._teamspace_api.create_model(
|
|
289
305
|
name=name,
|
|
290
|
-
|
|
306
|
+
version=version,
|
|
307
|
+
metadata=metadata,
|
|
291
308
|
private=True,
|
|
292
309
|
teamspace_id=self.id,
|
|
293
310
|
cloud_account=cloud_account,
|
lightning_sdk/utils/resolve.py
CHANGED
|
@@ -179,16 +179,23 @@ def skip_studio_init() -> Generator[None, None, None]:
|
|
|
179
179
|
Studio._skip_init = prev_studio_init_state
|
|
180
180
|
|
|
181
181
|
|
|
182
|
-
def _parse_model_and_version(name: str) -> Tuple[str, str]:
|
|
182
|
+
def _parse_model_and_version(name: str) -> Tuple[str, Optional[str]]:
|
|
183
|
+
"""Parse the model name and version from the given string.
|
|
184
|
+
|
|
185
|
+
>>> _parse_model_and_version("org/teamspace/modelname")
|
|
186
|
+
('org/teamspace/modelname', None)
|
|
187
|
+
>>> _parse_model_and_version("org/teamspace/modelname:version")
|
|
188
|
+
('org/teamspace/modelname', 'version')
|
|
189
|
+
"""
|
|
183
190
|
parts = name.split(":")
|
|
184
191
|
if len(parts) == 1:
|
|
185
|
-
return parts[0],
|
|
192
|
+
return parts[0], None
|
|
186
193
|
if len(parts) == 2:
|
|
187
194
|
return parts[0], parts[1]
|
|
188
195
|
# The rest of the validation for name and version happens in the backend
|
|
189
196
|
raise ValueError(
|
|
190
|
-
"Model version is expected to be in the format `entity/modelname:version` separated by a"
|
|
191
|
-
f"
|
|
197
|
+
"Model version is expected to be in the format `entity/modelname:version` separated by a single colon,"
|
|
198
|
+
f" but got: {name}"
|
|
192
199
|
)
|
|
193
200
|
|
|
194
201
|
|