lightning-sdk 0.2.5__py3-none-any.whl → 0.2.7__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/ai_hub_api.py +1 -0
- lightning_sdk/api/lit_container_api.py +84 -24
- lightning_sdk/api/teamspace_api.py +8 -9
- lightning_sdk/api/utils.py +0 -1
- lightning_sdk/cli/docker.py +1 -1
- lightning_sdk/cli/download.py +10 -2
- lightning_sdk/cli/serve.py +26 -19
- lightning_sdk/cli/upload.py +41 -6
- lightning_sdk/lightning_cloud/openapi/__init__.py +16 -0
- lightning_sdk/lightning_cloud/openapi/api/cloud_space_environment_template_service_api.py +9 -9
- lightning_sdk/lightning_cloud/openapi/api/cloud_space_service_api.py +202 -0
- lightning_sdk/lightning_cloud/openapi/api/cluster_service_api.py +105 -0
- lightning_sdk/lightning_cloud/openapi/api/lit_registry_service_api.py +15 -3
- lightning_sdk/lightning_cloud/openapi/models/__init__.py +16 -0
- lightning_sdk/lightning_cloud/openapi/models/alerts_config_billing.py +175 -0
- lightning_sdk/lightning_cloud/openapi/models/alerts_config_studios.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/environmenttemplates_id_body.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/externalv1_cloud_space_instance_status.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/externalv1_cluster.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/orgs_id_body.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/project_id_cloudspaces_body.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/projects_id_body.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/server_id_alerts_body.py +201 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_alert_method.py +102 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_alerts_config.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_cold_start_metrics.py +617 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_environment_template.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_environment_template_config.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_security_options.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_spec.py +131 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_conversation_response_chunk.py +29 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_create_cloud_space_environment_template_request.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_create_project_request.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_create_server_alert_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_data_connection.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_google_cloud_direct_v1.py +81 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_lambda_labs_direct_v1.py +1 -29
- lightning_sdk/lightning_cloud/openapi/models/v1_list_cloud_space_cold_start_metrics_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_lit_registry_repository_image_artifact_versions_response.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_message.py +29 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_metadata.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_organization.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_project_settings.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_report_cloud_space_instance_stop_at_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_reservation_details.py +201 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_rule_resource.py +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_server_alert.py +201 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_server_alert_phase.py +104 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_server_alert_severity.py +103 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_server_alert_type.py +105 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_subnet_spec.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +131 -79
- lightning_sdk/lightning_cloud/openapi/models/v1_voltage_park_direct_v1.py +29 -29
- lightning_sdk/lightning_cloud/openapi/models/v1_vultr_direct_v1.py +1 -27
- lightning_sdk/lit_container.py +19 -4
- lightning_sdk/models.py +1 -1
- lightning_sdk/serve.py +86 -15
- lightning_sdk/teamspace.py +32 -18
- {lightning_sdk-0.2.5.dist-info → lightning_sdk-0.2.7.dist-info}/METADATA +1 -1
- {lightning_sdk-0.2.5.dist-info → lightning_sdk-0.2.7.dist-info}/RECORD +65 -49
- {lightning_sdk-0.2.5.dist-info → lightning_sdk-0.2.7.dist-info}/LICENSE +0 -0
- {lightning_sdk-0.2.5.dist-info → lightning_sdk-0.2.7.dist-info}/WHEEL +0 -0
- {lightning_sdk-0.2.5.dist-info → lightning_sdk-0.2.7.dist-info}/entry_points.txt +0 -0
- {lightning_sdk-0.2.5.dist-info → lightning_sdk-0.2.7.dist-info}/top_level.txt +0 -0
lightning_sdk/serve.py
CHANGED
|
@@ -2,7 +2,7 @@ import os
|
|
|
2
2
|
import shlex
|
|
3
3
|
import subprocess
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Generator, Optional
|
|
5
|
+
from typing import Generator, List, Optional, Union
|
|
6
6
|
from urllib.parse import urlencode
|
|
7
7
|
|
|
8
8
|
import docker
|
|
@@ -10,7 +10,7 @@ from rich.console import Console
|
|
|
10
10
|
from rich.progress import Progress
|
|
11
11
|
|
|
12
12
|
from lightning_sdk import Deployment, Machine, Teamspace
|
|
13
|
-
from lightning_sdk.api.deployment_api import AutoScaleConfig
|
|
13
|
+
from lightning_sdk.api.deployment_api import AutoScaleConfig, DeploymentApi, Env, Secret
|
|
14
14
|
from lightning_sdk.api.lit_container_api import LitContainerApi
|
|
15
15
|
from lightning_sdk.api.utils import _get_cloud_url
|
|
16
16
|
from lightning_sdk.lightning_cloud import env
|
|
@@ -35,16 +35,18 @@ class _Auth(Auth):
|
|
|
35
35
|
_AuthServer().login_with_browser(self)
|
|
36
36
|
|
|
37
37
|
|
|
38
|
+
def authenticate() -> None:
|
|
39
|
+
auth = _Auth()
|
|
40
|
+
auth.authenticate()
|
|
41
|
+
|
|
42
|
+
|
|
38
43
|
class _LitServeDeployer:
|
|
39
|
-
def __init__(self) -> None:
|
|
44
|
+
def __init__(self, name: Optional[str], teamspace: Optional[Teamspace]) -> None:
|
|
45
|
+
self.name = name
|
|
46
|
+
self.teamspace = teamspace
|
|
40
47
|
self._console = Console()
|
|
41
48
|
self._client = None
|
|
42
49
|
|
|
43
|
-
@staticmethod
|
|
44
|
-
def authenticate() -> None:
|
|
45
|
-
auth = _Auth()
|
|
46
|
-
auth.authenticate()
|
|
47
|
-
|
|
48
50
|
@property
|
|
49
51
|
def client(self) -> docker.DockerClient:
|
|
50
52
|
if self._client is None:
|
|
@@ -56,6 +58,10 @@ class _LitServeDeployer:
|
|
|
56
58
|
raise RuntimeError(_DOCKER_NOT_RUNNING_MSG) from None
|
|
57
59
|
return self._client
|
|
58
60
|
|
|
61
|
+
@property
|
|
62
|
+
def created(self) -> bool:
|
|
63
|
+
return DeploymentApi().get_deployment_by_name(self.name, self.teamspace.id) is not None
|
|
64
|
+
|
|
59
65
|
def dockerize_api(
|
|
60
66
|
self,
|
|
61
67
|
server_filename: str,
|
|
@@ -194,13 +200,19 @@ Update [underline]{os.path.abspath("Dockerfile")}[/underline] to add any additio
|
|
|
194
200
|
progress.update(build_task, description="[green]Build completed![/green]")
|
|
195
201
|
|
|
196
202
|
def push_container(
|
|
197
|
-
self,
|
|
203
|
+
self,
|
|
204
|
+
repository: str,
|
|
205
|
+
tag: str,
|
|
206
|
+
teamspace: Teamspace,
|
|
207
|
+
lit_cr: LitContainerApi,
|
|
208
|
+
progress: Progress,
|
|
209
|
+
cloud_account: str,
|
|
198
210
|
) -> dict:
|
|
199
211
|
console = self._console
|
|
200
212
|
push_task = progress.add_task("Pushing to registry", total=None)
|
|
201
213
|
console.print("\nPushing image...", style="bold blue")
|
|
202
214
|
lit_cr.authenticate()
|
|
203
|
-
push_status = lit_cr.upload_container(repository, teamspace, tag=tag)
|
|
215
|
+
push_status = lit_cr.upload_container(repository, teamspace, tag=tag, cloud_account=cloud_account)
|
|
204
216
|
last_status = {}
|
|
205
217
|
for line in push_status:
|
|
206
218
|
last_status = line
|
|
@@ -214,7 +226,38 @@ Update [underline]{os.path.abspath("Dockerfile")}[/underline] to add any additio
|
|
|
214
226
|
progress.update(push_task, description="[green]Push completed![/green]")
|
|
215
227
|
return last_status
|
|
216
228
|
|
|
217
|
-
def
|
|
229
|
+
def _update_deployment(
|
|
230
|
+
self,
|
|
231
|
+
deployment: Deployment,
|
|
232
|
+
machine: Optional[Machine] = None,
|
|
233
|
+
image: Optional[str] = None,
|
|
234
|
+
entrypoint: Optional[str] = None,
|
|
235
|
+
command: Optional[str] = None,
|
|
236
|
+
env: Optional[List[Union[Env, Secret]]] = None,
|
|
237
|
+
min_replica: Optional[int] = 0,
|
|
238
|
+
max_replica: Optional[int] = 1,
|
|
239
|
+
spot: Optional[bool] = None,
|
|
240
|
+
replicas: Optional[int] = 1,
|
|
241
|
+
cloud_account: Optional[str] = None,
|
|
242
|
+
port: Optional[int] = 8000,
|
|
243
|
+
include_credentials: Optional[bool] = True,
|
|
244
|
+
) -> None:
|
|
245
|
+
return deployment.update(
|
|
246
|
+
machine=machine,
|
|
247
|
+
image=image,
|
|
248
|
+
entrypoint=entrypoint,
|
|
249
|
+
command=command,
|
|
250
|
+
env=env,
|
|
251
|
+
max_replicas=max_replica,
|
|
252
|
+
min_replicas=min_replica,
|
|
253
|
+
replicas=replicas,
|
|
254
|
+
spot=spot,
|
|
255
|
+
cloud_account=cloud_account,
|
|
256
|
+
ports=[port],
|
|
257
|
+
include_credentials=include_credentials,
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
def run_on_cloud(
|
|
218
261
|
self,
|
|
219
262
|
deployment_name: str,
|
|
220
263
|
teamspace: Teamspace,
|
|
@@ -229,16 +272,44 @@ Update [underline]{os.path.abspath("Dockerfile")}[/underline] to add any additio
|
|
|
229
272
|
port: Optional[int] = 8000,
|
|
230
273
|
include_credentials: Optional[bool] = True,
|
|
231
274
|
) -> dict:
|
|
275
|
+
"""Run a deployment on the cloud. If the deployment already exists, it will be updated.
|
|
276
|
+
|
|
277
|
+
Args:
|
|
278
|
+
deployment_name: The name of the deployment.
|
|
279
|
+
teamspace: The teamspace to run the deployment on.
|
|
280
|
+
image: The image to run the deployment on.
|
|
281
|
+
metric: The metric to use for autoscaling. Defaults to None.
|
|
282
|
+
machine: The machine to run the deployment on. Defaults to None.
|
|
283
|
+
min_replica: The minimum number of replicas to run. Defaults to 0.
|
|
284
|
+
max_replica: The maximum number of replicas to run. Defaults to 1.
|
|
285
|
+
spot: Whether to run the deployment on spot instances. Defaults to None.
|
|
286
|
+
replicas: The number of replicas to run. Defaults to 1.
|
|
287
|
+
cloud_account: The cloud account to run the deployment on. Defaults to None.
|
|
288
|
+
port: The port to run the deployment on. Defaults to 8000.
|
|
289
|
+
include_credentials: Whether to include credentials in the deployment. Defaults to True.
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
dict: The deployment and the URL of the deployment.
|
|
293
|
+
"""
|
|
232
294
|
url = f"{_get_cloud_url()}/{teamspace.owner.name}/{teamspace.name}/jobs/{deployment_name}"
|
|
233
295
|
machine = machine or Machine.CPU
|
|
234
296
|
metric = metric or ("CPU" if machine.is_cpu() else "GPU")
|
|
235
297
|
deployment = Deployment(deployment_name, teamspace)
|
|
236
298
|
if deployment.is_started:
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
299
|
+
self._console.print(f"Deployment with name {deployment_name} already running. Updating the deployment.")
|
|
300
|
+
self._update_deployment(
|
|
301
|
+
deployment,
|
|
302
|
+
machine,
|
|
303
|
+
image,
|
|
304
|
+
min_replica=min_replica,
|
|
305
|
+
max_replica=max_replica,
|
|
306
|
+
spot=spot,
|
|
307
|
+
replicas=replicas,
|
|
308
|
+
cloud_account=cloud_account,
|
|
309
|
+
port=port,
|
|
310
|
+
include_credentials=include_credentials,
|
|
241
311
|
)
|
|
312
|
+
return {"deployment": deployment, "url": url, "updated": True}
|
|
242
313
|
autoscale = AutoScaleConfig(min_replicas=min_replica, max_replicas=max_replica, metric=metric, threshold=0.95)
|
|
243
314
|
deployment.start(
|
|
244
315
|
machine=machine,
|
lightning_sdk/teamspace.py
CHANGED
|
@@ -243,7 +243,7 @@ class Teamspace:
|
|
|
243
243
|
|
|
244
244
|
def upload_model(
|
|
245
245
|
self,
|
|
246
|
-
path: Union[str, Path],
|
|
246
|
+
path: Union[str, Path, List[Union[str, Path]]],
|
|
247
247
|
name: str,
|
|
248
248
|
cloud_account: Optional[str] = None,
|
|
249
249
|
progress_bar: bool = True,
|
|
@@ -261,30 +261,33 @@ class Teamspace:
|
|
|
261
261
|
raise ValueError("No path provided to upload")
|
|
262
262
|
if not name:
|
|
263
263
|
raise ValueError("No name provided for the model")
|
|
264
|
-
path = Path(path).resolve()
|
|
265
|
-
if not path.exists():
|
|
266
|
-
raise FileNotFoundError(str(path))
|
|
267
264
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
265
|
+
if isinstance(path, (str, Path)):
|
|
266
|
+
path = [path]
|
|
267
|
+
file_paths, relative_paths = [], []
|
|
268
|
+
for p in path:
|
|
269
|
+
lpaths, rpaths = _list_files(p)
|
|
270
|
+
file_paths.extend(lpaths)
|
|
271
|
+
relative_paths.extend(rpaths)
|
|
272
272
|
|
|
273
|
-
if not
|
|
273
|
+
if not file_paths:
|
|
274
274
|
raise FileNotFoundError(
|
|
275
275
|
"The path to upload doesn't contain any files. Make sure it points to a file or"
|
|
276
276
|
f" non-empty folder: {path}"
|
|
277
277
|
)
|
|
278
|
+
if len(relative_paths) != len(set(relative_paths)):
|
|
279
|
+
raise RuntimeError(
|
|
280
|
+
"The paths to upload contain files with the same name or relative path in folders."
|
|
281
|
+
f"The listed files are: {file_paths}\nThe relative paths are: {relative_paths}"
|
|
282
|
+
)
|
|
278
283
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
filenames = ",".join(str(f.relative_to(root_path)) for f in filepaths)
|
|
284
|
+
cloud_account = (
|
|
285
|
+
self._teamspace_api._determine_cloud_account(self.id) if cloud_account is None else cloud_account
|
|
286
|
+
)
|
|
284
287
|
|
|
285
288
|
model = self._teamspace_api.create_model(
|
|
286
289
|
name=name,
|
|
287
|
-
metadata={"filenames":
|
|
290
|
+
metadata={"filenames": ",".join(relative_paths)},
|
|
288
291
|
private=True,
|
|
289
292
|
teamspace_id=self.id,
|
|
290
293
|
cloud_account=cloud_account,
|
|
@@ -292,9 +295,8 @@ class Teamspace:
|
|
|
292
295
|
self._teamspace_api.upload_model_files(
|
|
293
296
|
model_id=model.model_id,
|
|
294
297
|
version=model.version,
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
cloud_account=cloud_account,
|
|
298
|
+
file_paths=file_paths,
|
|
299
|
+
remote_paths=relative_paths,
|
|
298
300
|
teamspace_id=self.id,
|
|
299
301
|
progress_bar=progress_bar,
|
|
300
302
|
)
|
|
@@ -371,6 +373,18 @@ class Teamspace:
|
|
|
371
373
|
self._teamspace_api.delete_model(name=name, version=version, teamspace_id=self.id)
|
|
372
374
|
|
|
373
375
|
|
|
376
|
+
def _list_files(path: Union[str, Path]) -> Tuple[List[Path], List[str]]:
|
|
377
|
+
"""List all folders in a directory and return them as a list and relative path."""
|
|
378
|
+
path = Path(path).resolve()
|
|
379
|
+
if not path.exists():
|
|
380
|
+
raise FileNotFoundError(f"Path {path} does not exist")
|
|
381
|
+
if not path.is_dir():
|
|
382
|
+
return [path], [str(path.name)]
|
|
383
|
+
local_paths = [p for p in path.rglob("*") if p.is_file()]
|
|
384
|
+
relative_paths = [str(p.relative_to(path)) for p in local_paths]
|
|
385
|
+
return local_paths, relative_paths
|
|
386
|
+
|
|
387
|
+
|
|
374
388
|
def _resolve_valueerror_message(error: ValueError, owner: Owner, teamspace_name: str) -> ValueError:
|
|
375
389
|
"""Resolves the ValueError Message and replaces it with a nicer message."""
|
|
376
390
|
message = error.args[0]
|