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.
Files changed (60) hide show
  1. lightning_sdk/__init__.py +1 -1
  2. lightning_sdk/api/cluster_api.py +22 -0
  3. lightning_sdk/api/deployment_api.py +1 -0
  4. lightning_sdk/api/lit_container_api.py +24 -5
  5. lightning_sdk/api/teamspace_api.py +22 -17
  6. lightning_sdk/api/utils.py +1 -1
  7. lightning_sdk/cli/clusters_menu.py +46 -0
  8. lightning_sdk/cli/entrypoint.py +2 -2
  9. lightning_sdk/cli/list.py +25 -5
  10. lightning_sdk/cli/serve.py +232 -24
  11. lightning_sdk/cli/upload.py +4 -1
  12. lightning_sdk/deployment/deployment.py +5 -2
  13. lightning_sdk/lightning_cloud/openapi/__init__.py +10 -0
  14. lightning_sdk/lightning_cloud/openapi/api/cloud_space_service_api.py +303 -0
  15. lightning_sdk/lightning_cloud/openapi/models/__init__.py +10 -0
  16. lightning_sdk/lightning_cloud/openapi/models/cloudspace_id_systemmetrics_body.py +149 -0
  17. lightning_sdk/lightning_cloud/openapi/models/cluster_id_capacityreservations_body.py +55 -3
  18. lightning_sdk/lightning_cloud/openapi/models/create.py +53 -1
  19. lightning_sdk/lightning_cloud/openapi/models/orgs_id_body.py +55 -3
  20. lightning_sdk/lightning_cloud/openapi/models/update.py +27 -1
  21. lightning_sdk/lightning_cloud/openapi/models/v1_billing_tier.py +1 -0
  22. lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space.py +53 -1
  23. lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_cold_start_metrics.py +53 -1
  24. lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_cold_start_metrics_stats.py +357 -0
  25. lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_environment_template_config.py +29 -3
  26. lightning_sdk/lightning_cloud/openapi/models/v1_cloudflare_v1.py +227 -0
  27. lightning_sdk/lightning_cloud/openapi/models/v1_cluster_accelerator.py +27 -1
  28. lightning_sdk/lightning_cloud/openapi/models/v1_cluster_capacity_reservation.py +55 -3
  29. lightning_sdk/lightning_cloud/openapi/models/v1_cluster_security_options.py +79 -1
  30. lightning_sdk/lightning_cloud/openapi/models/v1_cluster_spec.py +27 -1
  31. lightning_sdk/lightning_cloud/openapi/models/v1_create_cloud_space_environment_template_request.py +29 -3
  32. lightning_sdk/lightning_cloud/openapi/models/v1_create_cluster_capacity_reservation_response.py +27 -1
  33. lightning_sdk/lightning_cloud/openapi/models/v1_data_connection.py +53 -1
  34. lightning_sdk/lightning_cloud/openapi/models/v1_gcp_direct_vpc.py +149 -0
  35. lightning_sdk/lightning_cloud/openapi/models/v1_get_cloud_space_cold_start_metrics_stats_response.py +123 -0
  36. lightning_sdk/lightning_cloud/openapi/models/v1_get_cloud_space_instance_system_metrics_aggregate_response.py +123 -0
  37. lightning_sdk/lightning_cloud/openapi/models/v1_get_user_response.py +53 -1
  38. lightning_sdk/lightning_cloud/openapi/models/v1_google_cloud_direct_v1.py +43 -17
  39. lightning_sdk/lightning_cloud/openapi/models/v1_organization.py +55 -3
  40. lightning_sdk/lightning_cloud/openapi/models/v1_project_cluster_binding.py +27 -1
  41. lightning_sdk/lightning_cloud/openapi/models/v1_r2_data_connection.py +253 -0
  42. lightning_sdk/lightning_cloud/openapi/models/v1_report_cloud_space_instance_system_metrics_response.py +97 -0
  43. lightning_sdk/lightning_cloud/openapi/models/v1_server_alert_phase.py +1 -0
  44. lightning_sdk/lightning_cloud/openapi/models/v1_system_metrics_aggregated.py +227 -0
  45. lightning_sdk/lightning_cloud/openapi/models/v1_update_user_request.py +27 -1
  46. lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +104 -208
  47. lightning_sdk/lightning_cloud/openapi/models/v1_validate_data_connection_response.py +27 -1
  48. lightning_sdk/lightning_cloud/openapi/models/v1_weka_data_connection.py +201 -0
  49. lightning_sdk/lightning_cloud/openapi/models/validate.py +27 -1
  50. lightning_sdk/lit_container.py +25 -7
  51. lightning_sdk/models.py +26 -8
  52. lightning_sdk/serve.py +3 -20
  53. lightning_sdk/teamspace.py +21 -4
  54. lightning_sdk/utils/resolve.py +11 -4
  55. {lightning_sdk-0.2.8.dist-info → lightning_sdk-0.2.10.dist-info}/METADATA +1 -1
  56. {lightning_sdk-0.2.8.dist-info → lightning_sdk-0.2.10.dist-info}/RECORD +60 -48
  57. {lightning_sdk-0.2.8.dist-info → lightning_sdk-0.2.10.dist-info}/LICENSE +0 -0
  58. {lightning_sdk-0.2.8.dist-info → lightning_sdk-0.2.10.dist-info}/WHEEL +0 -0
  59. {lightning_sdk-0.2.8.dist-info → lightning_sdk-0.2.10.dist-info}/entry_points.txt +0 -0
  60. {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
@@ -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 as e:
28
- raise ValueError(f"Could not resolve teamspace: {e}") from e
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
- "IMAGE ID": repo.id,
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
- ) -> None:
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(container, teamspace, tag, cloud_account, platform=platform)
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 _parse_model_name_and_version(name: str) -> Tuple[str, str, str, str]:
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], "default"
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 = _parse_model_name_and_version(name)
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, _ = _parse_model_name_and_version(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}
@@ -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) if cloud_account is None else cloud_account
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
- metadata={"filenames": ",".join(relative_paths)},
306
+ version=version,
307
+ metadata=metadata,
291
308
  private=True,
292
309
  teamspace_id=self.id,
293
310
  cloud_account=cloud_account,
@@ -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], "default"
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" single colon, but got: {name}"
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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lightning_sdk
3
- Version: 0.2.8
3
+ Version: 0.2.10
4
4
  Summary: SDK to develop using Lightning AI Studios
5
5
  Author-email: Lightning-AI <justus@lightning.ai>
6
6
  License: MIT License