lightning-sdk 0.2.7__py3-none-any.whl → 0.2.9__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/lit_container_api.py +7 -12
- lightning_sdk/api/teamspace_api.py +2 -1
- lightning_sdk/cli/clusters_menu.py +46 -0
- lightning_sdk/cli/list.py +25 -5
- lightning_sdk/cli/serve.py +133 -27
- lightning_sdk/lightning_cloud/openapi/__init__.py +5 -0
- lightning_sdk/lightning_cloud/openapi/api/cloud_space_service_api.py +206 -0
- lightning_sdk/lightning_cloud/openapi/models/__init__.py +5 -0
- lightning_sdk/lightning_cloud/openapi/models/cloudspace_id_systemmetrics_body.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_billing_tier.py +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_cold_start_metrics.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_security_options.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_data_connection.py +27 -1
- 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_google_cloud_direct_v1.py +27 -1
- 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_user_features.py +55 -185
- lightning_sdk/lightning_cloud/openapi/models/v1_weka_data_connection.py +227 -0
- lightning_sdk/lit_container.py +13 -5
- lightning_sdk/models.py +7 -2
- lightning_sdk/serve.py +23 -46
- lightning_sdk/teamspace.py +19 -4
- {lightning_sdk-0.2.7.dist-info → lightning_sdk-0.2.9.dist-info}/METADATA +1 -1
- {lightning_sdk-0.2.7.dist-info → lightning_sdk-0.2.9.dist-info}/RECORD +32 -25
- {lightning_sdk-0.2.7.dist-info → lightning_sdk-0.2.9.dist-info}/LICENSE +0 -0
- {lightning_sdk-0.2.7.dist-info → lightning_sdk-0.2.9.dist-info}/WHEEL +0 -0
- {lightning_sdk-0.2.7.dist-info → lightning_sdk-0.2.9.dist-info}/entry_points.txt +0 -0
- {lightning_sdk-0.2.7.dist-info → lightning_sdk-0.2.9.dist-info}/top_level.txt +0 -0
lightning_sdk/__init__.py
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from lightning_sdk.lightning_cloud.openapi import Externalv1Cluster
|
|
2
|
+
from lightning_sdk.lightning_cloud.rest_client import LightningClient
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ClusterApi:
|
|
6
|
+
"""Internal API client for API requests to cluster endpoints."""
|
|
7
|
+
|
|
8
|
+
def __init__(self) -> None:
|
|
9
|
+
self._client = LightningClient(max_tries=7)
|
|
10
|
+
|
|
11
|
+
def get_cluster(self, cluster_id: str, project_id: str, org_id: str) -> Externalv1Cluster:
|
|
12
|
+
"""Gets the cluster from given params cluster_id, project_id and owner.
|
|
13
|
+
|
|
14
|
+
:param cluster_id: cluster ID test
|
|
15
|
+
:param project_id: the project the cluster is supposed to be associated with
|
|
16
|
+
:param org_id: The owning org of this cluster
|
|
17
|
+
:return:
|
|
18
|
+
"""
|
|
19
|
+
res = self._client.cluster_service_get_cluster(id=cluster_id, org_id=org_id, project_id=project_id)
|
|
20
|
+
if not res:
|
|
21
|
+
raise ValueError(f"Cluster {cluster_id} does not exist")
|
|
22
|
+
return res
|
|
@@ -7,7 +7,6 @@ import requests
|
|
|
7
7
|
from rich.console import Console
|
|
8
8
|
|
|
9
9
|
from lightning_sdk.api.utils import _get_registry_url
|
|
10
|
-
from lightning_sdk.lightning_cloud.env import LIGHTNING_CLOUD_URL
|
|
11
10
|
from lightning_sdk.lightning_cloud.openapi.models import V1DeleteLitRepositoryResponse
|
|
12
11
|
from lightning_sdk.lightning_cloud.rest_client import LightningClient
|
|
13
12
|
from lightning_sdk.teamspace import Teamspace
|
|
@@ -103,11 +102,14 @@ class LitContainerApi:
|
|
|
103
102
|
"""Lists containers of the project ID.
|
|
104
103
|
|
|
105
104
|
:param project_id: The non-human readable project ID used internally to identify projects.
|
|
105
|
+
:param cloud_account: The cluster ID of the cloud account. If None, will use the default cluster.
|
|
106
106
|
:return:
|
|
107
107
|
"""
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
108
|
+
if cloud_account is None:
|
|
109
|
+
project = self._client.lit_registry_service_get_lit_project_registry(project_id)
|
|
110
|
+
else:
|
|
111
|
+
project = self._client.lit_registry_service_get_lit_project_registry(project_id, cluster_id=cloud_account)
|
|
112
|
+
|
|
111
113
|
return project.repositories
|
|
112
114
|
|
|
113
115
|
def delete_container(self, project_id: str, container: str) -> V1DeleteLitRepositoryResponse:
|
|
@@ -138,7 +140,7 @@ class LitContainerApi:
|
|
|
138
140
|
Named cloud-account in the CLI options.
|
|
139
141
|
:param platform: If empty will be linux/amd64. This is important because our entire deployment infra runs on
|
|
140
142
|
linux/amd64. Will show user a warning otherwise.
|
|
141
|
-
:return: Generator[dict, None,
|
|
143
|
+
:return: Generator[dict, None, dict]
|
|
142
144
|
"""
|
|
143
145
|
try:
|
|
144
146
|
self._docker_client.images.get(f"{container}:{tag}")
|
|
@@ -164,13 +166,6 @@ class LitContainerApi:
|
|
|
164
166
|
raise ValueError(f"Could not tag container {container}:{tag} with {repository}:{tag}")
|
|
165
167
|
yield from self._push_with_retry(repository, tag=tag)
|
|
166
168
|
|
|
167
|
-
yield {
|
|
168
|
-
"finish": True,
|
|
169
|
-
"url": f"{LIGHTNING_CLOUD_URL}/{teamspace.owner.name}/{teamspace.name}/containers/{container_basename}"
|
|
170
|
-
f"{f'?clusterId={cloud_account}' if cloud_account is not None else ''}",
|
|
171
|
-
"repository": repository,
|
|
172
|
-
}
|
|
173
|
-
|
|
174
169
|
def _push_with_retry(self, repository: str, tag: str, max_retries: int = 3) -> Iterator[Dict[str, Any]]:
|
|
175
170
|
def is_auth_error(error_msg: str) -> bool:
|
|
176
171
|
auth_errors = ["unauthorized", "authentication required", "unauth"]
|
|
@@ -177,6 +177,7 @@ class TeamspaceApi:
|
|
|
177
177
|
def create_model(
|
|
178
178
|
self,
|
|
179
179
|
name: str,
|
|
180
|
+
version: Optional[str],
|
|
180
181
|
metadata: Dict[str, str],
|
|
181
182
|
private: bool,
|
|
182
183
|
teamspace_id: str,
|
|
@@ -191,7 +192,7 @@ class TeamspaceApi:
|
|
|
191
192
|
)
|
|
192
193
|
assert len(models) == 1, "Multiple models with the same name found"
|
|
193
194
|
return self.models.models_store_create_model_version(
|
|
194
|
-
body=ModelIdVersionsBody(cluster_id=cloud_account),
|
|
195
|
+
body=ModelIdVersionsBody(cluster_id=cloud_account, version=version),
|
|
195
196
|
project_id=teamspace_id,
|
|
196
197
|
model_id=models[0].id,
|
|
197
198
|
)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from typing import List
|
|
3
|
+
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from simple_term_menu import TerminalMenu
|
|
6
|
+
|
|
7
|
+
from lightning_sdk import Teamspace
|
|
8
|
+
from lightning_sdk.api.cluster_api import ClusterApi
|
|
9
|
+
from lightning_sdk.lightning_cloud.openapi import Externalv1Cluster, V1ProjectClusterBinding
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class _ClustersMenu:
|
|
13
|
+
def _get_cluster_from_interactive_menu(self, possible_clusters: List[V1ProjectClusterBinding]) -> str:
|
|
14
|
+
terminal_menu = self._prepare_terminal_menu_teamspaces([cluster.cluster_id for cluster in possible_clusters])
|
|
15
|
+
terminal_menu.show()
|
|
16
|
+
|
|
17
|
+
return possible_clusters[terminal_menu.chosen_menu_index].cluster_id
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def _prepare_terminal_menu_teamspaces(cluster_ids: List[str]) -> TerminalMenu:
|
|
21
|
+
title = "Please select a cluster from the following:"
|
|
22
|
+
|
|
23
|
+
return TerminalMenu(cluster_ids, title=title, clear_menu_on_exit=True)
|
|
24
|
+
|
|
25
|
+
def _resolve_cluster(self, teamspace: Teamspace) -> Externalv1Cluster:
|
|
26
|
+
selected_cluster_id = None
|
|
27
|
+
console = Console()
|
|
28
|
+
try:
|
|
29
|
+
selected_cluster_id = self._get_cluster_from_interactive_menu(
|
|
30
|
+
possible_clusters=teamspace.cloud_account_objs
|
|
31
|
+
)
|
|
32
|
+
cluster_api = ClusterApi()
|
|
33
|
+
|
|
34
|
+
return cluster_api.get_cluster(
|
|
35
|
+
cluster_id=selected_cluster_id, org_id=teamspace.owner.id, project_id=teamspace.id
|
|
36
|
+
)
|
|
37
|
+
except KeyboardInterrupt:
|
|
38
|
+
console.print("Operation cancelled by user")
|
|
39
|
+
sys.exit(0)
|
|
40
|
+
|
|
41
|
+
except Exception:
|
|
42
|
+
console.print(
|
|
43
|
+
f"[red]Could not find the given Cluster:[/red] {selected_cluster_id}. "
|
|
44
|
+
"Please contact Lightning AI directly to resolve this issue."
|
|
45
|
+
)
|
|
46
|
+
sys.exit(1)
|
lightning_sdk/cli/list.py
CHANGED
|
@@ -7,8 +7,9 @@ from rich.table import Table
|
|
|
7
7
|
from typing_extensions import Literal
|
|
8
8
|
|
|
9
9
|
from lightning_sdk import Job, Machine, Studio, Teamspace
|
|
10
|
+
from lightning_sdk.cli.clusters_menu import _ClustersMenu
|
|
10
11
|
from lightning_sdk.cli.teamspace_menu import _TeamspacesMenu
|
|
11
|
-
from lightning_sdk.lightning_cloud.openapi import V1MultiMachineJob
|
|
12
|
+
from lightning_sdk.lightning_cloud.openapi import V1ClusterType, V1MultiMachineJob
|
|
12
13
|
from lightning_sdk.lit_container import LitContainer
|
|
13
14
|
from lightning_sdk.utils.resolve import _get_authed_user
|
|
14
15
|
|
|
@@ -232,18 +233,37 @@ def mmts(
|
|
|
232
233
|
"If not provided, can be selected in an interactive menu."
|
|
233
234
|
),
|
|
234
235
|
)
|
|
235
|
-
|
|
236
|
+
@click.option(
|
|
237
|
+
"--cloud-account",
|
|
238
|
+
"--cloud_account", # The UI will present the above variant, using this as a secondary to be consistent w/ models
|
|
239
|
+
default=None,
|
|
240
|
+
help="The name of the cloud account where containers are stored in.",
|
|
241
|
+
)
|
|
242
|
+
def containers(teamspace: Optional[str] = None, cloud_account: Optional[str] = None) -> None:
|
|
236
243
|
"""Display the list of available containers."""
|
|
237
244
|
api = LitContainer()
|
|
238
245
|
menu = _TeamspacesMenu()
|
|
246
|
+
clusters_menu = _ClustersMenu()
|
|
239
247
|
resolved_teamspace = menu._resolve_teamspace(teamspace=teamspace)
|
|
240
|
-
|
|
248
|
+
|
|
249
|
+
if not cloud_account:
|
|
250
|
+
cloud_account_obj = clusters_menu._resolve_cluster(resolved_teamspace)
|
|
251
|
+
cloud_account = "" if cloud_account_obj.spec.cluster_type == V1ClusterType.GLOBAL else cloud_account_obj.id
|
|
252
|
+
|
|
253
|
+
result = api.list_containers(
|
|
254
|
+
teamspace=resolved_teamspace.name, org=resolved_teamspace.owner.name, cloud_account=cloud_account
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
if not result:
|
|
258
|
+
return
|
|
259
|
+
|
|
241
260
|
table = Table(pad_edge=True, box=None)
|
|
242
261
|
table.add_column("REPOSITORY")
|
|
243
|
-
table.add_column("
|
|
262
|
+
table.add_column("CLOUD ACCOUNT")
|
|
263
|
+
table.add_column("LATEST TAG")
|
|
244
264
|
table.add_column("CREATED")
|
|
245
265
|
for repo in result:
|
|
246
|
-
table.add_row(repo["REPOSITORY"], repo["
|
|
266
|
+
table.add_row(repo["REPOSITORY"], repo["CLOUD ACCOUNT"], repo["LATEST TAG"], repo["CREATED"])
|
|
247
267
|
Console().print(table)
|
|
248
268
|
|
|
249
269
|
|
lightning_sdk/cli/serve.py
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import subprocess
|
|
3
|
+
import time
|
|
4
|
+
import webbrowser
|
|
3
5
|
from datetime import datetime
|
|
4
6
|
from pathlib import Path
|
|
5
7
|
from typing import Optional, Union
|
|
8
|
+
from urllib.parse import urlencode
|
|
6
9
|
|
|
7
10
|
import click
|
|
8
11
|
from rich.console import Console
|
|
@@ -10,24 +13,37 @@ from rich.progress import Progress, SpinnerColumn, TextColumn, TimeElapsedColumn
|
|
|
10
13
|
from rich.prompt import Confirm
|
|
11
14
|
|
|
12
15
|
from lightning_sdk import Machine, Teamspace
|
|
16
|
+
from lightning_sdk.api import UserApi
|
|
13
17
|
from lightning_sdk.api.lit_container_api import LitContainerApi
|
|
14
18
|
from lightning_sdk.cli.teamspace_menu import _TeamspacesMenu
|
|
15
|
-
from lightning_sdk.
|
|
19
|
+
from lightning_sdk.lightning_cloud import env
|
|
20
|
+
from lightning_sdk.lightning_cloud.login import Auth, AuthServer
|
|
21
|
+
from lightning_sdk.serve import _LitServeDeployer
|
|
22
|
+
from lightning_sdk.utils.resolve import _get_authed_user, _resolve_teamspace
|
|
16
23
|
|
|
17
24
|
_MACHINE_VALUES = tuple([machine.name for machine in Machine.__dict__.values() if isinstance(machine, Machine)])
|
|
18
25
|
|
|
19
26
|
|
|
20
|
-
|
|
27
|
+
class _ServeGroup(click.Group):
|
|
28
|
+
def parse_args(self, ctx: click.Context, args: list) -> click.Group:
|
|
29
|
+
# Check if first arg is a file path and not a command name
|
|
30
|
+
if args and os.path.exists(args[0]) and args[0] not in self.commands:
|
|
31
|
+
# Insert the 'api' command before the file path
|
|
32
|
+
args.insert(0, "api")
|
|
33
|
+
return super().parse_args(ctx, args)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@click.group("serve", cls=_ServeGroup)
|
|
21
37
|
def serve() -> None:
|
|
22
38
|
"""Serve a LitServe model.
|
|
23
39
|
|
|
24
40
|
Example:
|
|
25
|
-
lightning serve
|
|
41
|
+
lightning serve server.py # deploy to the cloud
|
|
26
42
|
|
|
27
43
|
Example:
|
|
28
|
-
lightning serve
|
|
44
|
+
lightning serve server.py --local # serve locally
|
|
29
45
|
|
|
30
|
-
You can deploy the API to the cloud by running `lightning serve
|
|
46
|
+
You can deploy the API to the cloud by running `lightning serve server.py`.
|
|
31
47
|
This will build a docker container for the server.py script and deploy it to the Lightning AI platform.
|
|
32
48
|
"""
|
|
33
49
|
|
|
@@ -42,11 +58,11 @@ def serve() -> None:
|
|
|
42
58
|
help="Generate a client for the model",
|
|
43
59
|
)
|
|
44
60
|
@click.option(
|
|
45
|
-
"--
|
|
61
|
+
"--local",
|
|
46
62
|
is_flag=True,
|
|
47
63
|
default=False,
|
|
48
64
|
flag_value=True,
|
|
49
|
-
help="
|
|
65
|
+
help="Run the model locally",
|
|
50
66
|
)
|
|
51
67
|
@click.option("--name", default=None, help="Name of the deployed API (e.g., 'classification-api', 'Llama-api')")
|
|
52
68
|
@click.option(
|
|
@@ -107,7 +123,7 @@ def serve() -> None:
|
|
|
107
123
|
def api(
|
|
108
124
|
script_path: str,
|
|
109
125
|
easy: bool,
|
|
110
|
-
|
|
126
|
+
local: bool,
|
|
111
127
|
name: Optional[str],
|
|
112
128
|
non_interactive: bool,
|
|
113
129
|
machine: str,
|
|
@@ -126,7 +142,7 @@ def api(
|
|
|
126
142
|
return api_impl(
|
|
127
143
|
script_path=script_path,
|
|
128
144
|
easy=easy,
|
|
129
|
-
|
|
145
|
+
local=local,
|
|
130
146
|
repository=name,
|
|
131
147
|
non_interactive=non_interactive,
|
|
132
148
|
machine=machine,
|
|
@@ -146,7 +162,7 @@ def api(
|
|
|
146
162
|
def api_impl(
|
|
147
163
|
script_path: Union[str, Path],
|
|
148
164
|
easy: bool = False,
|
|
149
|
-
|
|
165
|
+
local: bool = False,
|
|
150
166
|
repository: [str] = None,
|
|
151
167
|
tag: Optional[str] = None,
|
|
152
168
|
non_interactive: bool = False,
|
|
@@ -176,7 +192,7 @@ def api_impl(
|
|
|
176
192
|
timestr = datetime.now().strftime("%b-%d-%H_%M")
|
|
177
193
|
repository = f"litserve-{timestr}".lower()
|
|
178
194
|
|
|
179
|
-
if
|
|
195
|
+
if not local:
|
|
180
196
|
repository = repository or "litserve-model"
|
|
181
197
|
machine = Machine.from_str(machine)
|
|
182
198
|
return _handle_cloud(
|
|
@@ -209,6 +225,69 @@ def api_impl(
|
|
|
209
225
|
raise RuntimeError(error_msg) from None
|
|
210
226
|
|
|
211
227
|
|
|
228
|
+
class _AuthServer(AuthServer):
|
|
229
|
+
def get_auth_url(self, port: int) -> str:
|
|
230
|
+
redirect_uri = f"http://localhost:{port}/login-complete"
|
|
231
|
+
params = urlencode({"redirectTo": redirect_uri, "inviteCode": "litserve"})
|
|
232
|
+
return f"{env.LIGHTNING_CLOUD_URL}/sign-in?{params}"
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
class _Auth(Auth):
|
|
236
|
+
def __init__(self, shall_confirm: bool = False) -> None:
|
|
237
|
+
super().__init__()
|
|
238
|
+
self._shall_confirm = shall_confirm
|
|
239
|
+
|
|
240
|
+
def _run_server(self) -> None:
|
|
241
|
+
if self._shall_confirm:
|
|
242
|
+
proceed = Confirm.ask(
|
|
243
|
+
"Authenticating with Lightning AI. This will open a browser window. Continue?", default=True
|
|
244
|
+
)
|
|
245
|
+
if not proceed:
|
|
246
|
+
raise RuntimeError(
|
|
247
|
+
"Login cancelled. Please login to Lightning AI to deploy the API."
|
|
248
|
+
" Run `lightning login` to login."
|
|
249
|
+
) from None
|
|
250
|
+
print("Opening browser for authentication...")
|
|
251
|
+
print("Please come back to the terminal after logging in.")
|
|
252
|
+
time.sleep(3)
|
|
253
|
+
_AuthServer().login_with_browser(self)
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def authenticate(shall_confirm: bool = True) -> None:
|
|
257
|
+
auth = _Auth(shall_confirm)
|
|
258
|
+
auth.authenticate()
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def select_teamspace(teamspace: Optional[str], org: Optional[str], user: Optional[str]) -> Teamspace:
|
|
262
|
+
if teamspace is None:
|
|
263
|
+
user = _get_authed_user()
|
|
264
|
+
menu = _TeamspacesMenu()
|
|
265
|
+
possible_teamspaces = menu._get_possible_teamspaces(user)
|
|
266
|
+
if len(possible_teamspaces) == 1:
|
|
267
|
+
name = next(iter(possible_teamspaces.values()))["name"]
|
|
268
|
+
return Teamspace(name=name, org=org, user=user)
|
|
269
|
+
|
|
270
|
+
return menu._resolve_teamspace(teamspace)
|
|
271
|
+
|
|
272
|
+
return _resolve_teamspace(teamspace=teamspace, org=org, user=user)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def poll_verified_status() -> bool:
|
|
276
|
+
"""Polls the verified status of the user until it is True or a timeout occurs."""
|
|
277
|
+
user_api = UserApi()
|
|
278
|
+
user = _get_authed_user()
|
|
279
|
+
start_time = datetime.now()
|
|
280
|
+
timeout = 600 # 10 minutes
|
|
281
|
+
while True:
|
|
282
|
+
user_resp = user_api.get_user(name=user.name)
|
|
283
|
+
if user_resp.status.verified:
|
|
284
|
+
return True
|
|
285
|
+
if (datetime.now() - start_time).total_seconds() > timeout:
|
|
286
|
+
break
|
|
287
|
+
time.sleep(5)
|
|
288
|
+
return False
|
|
289
|
+
|
|
290
|
+
|
|
212
291
|
def _handle_cloud(
|
|
213
292
|
script_path: Union[str, Path],
|
|
214
293
|
console: Console,
|
|
@@ -233,16 +312,8 @@ def _handle_cloud(
|
|
|
233
312
|
if non_interactive:
|
|
234
313
|
console.print("[italic]non-interactive[/italic] mode enabled, skipping confirmation prompts", style="blue")
|
|
235
314
|
|
|
236
|
-
# Authenticate with LitServe affiliate
|
|
237
|
-
authenticate()
|
|
238
|
-
if teamspace is None:
|
|
239
|
-
menu = _TeamspacesMenu()
|
|
240
|
-
resolved_teamspace = menu._resolve_teamspace(teamspace)
|
|
241
|
-
else:
|
|
242
|
-
resolved_teamspace = Teamspace(name=teamspace, org=org, user=user)
|
|
243
|
-
|
|
244
315
|
port = port or 8000
|
|
245
|
-
ls_deployer = _LitServeDeployer(name=deployment_name, teamspace=
|
|
316
|
+
ls_deployer = _LitServeDeployer(name=deployment_name, teamspace=None)
|
|
246
317
|
path = ls_deployer.dockerize_api(script_path, port=port, gpu=not machine.is_cpu(), tag=tag, print_success=False)
|
|
247
318
|
|
|
248
319
|
console.print(f"\nPlease review the Dockerfile at [u]{path}[/u] and make sure it is correct.", style="bold")
|
|
@@ -251,6 +322,36 @@ def _handle_cloud(
|
|
|
251
322
|
console.print("Please fix the Dockerfile and try again.", style="red")
|
|
252
323
|
return
|
|
253
324
|
|
|
325
|
+
with Progress(
|
|
326
|
+
SpinnerColumn(),
|
|
327
|
+
TextColumn("[progress.description]{task.description}"),
|
|
328
|
+
TimeElapsedColumn(),
|
|
329
|
+
console=console,
|
|
330
|
+
transient=True,
|
|
331
|
+
) as progress:
|
|
332
|
+
try:
|
|
333
|
+
# Build the container
|
|
334
|
+
build_task = progress.add_task("Building Docker image", total=None)
|
|
335
|
+
for line in ls_deployer.build_container(path, repository, tag):
|
|
336
|
+
console.print(line.strip())
|
|
337
|
+
progress.update(build_task, advance=1)
|
|
338
|
+
progress.update(build_task, description="[green]Build completed![/green]", completed=1.0)
|
|
339
|
+
progress.remove_task(build_task)
|
|
340
|
+
|
|
341
|
+
except Exception as e:
|
|
342
|
+
console.print(f"❌ Deployment failed: {e}", style="red")
|
|
343
|
+
return
|
|
344
|
+
|
|
345
|
+
# Push the container to the registry
|
|
346
|
+
console.print("\nPushing container to registry. It may take a while...", style="bold")
|
|
347
|
+
# Authenticate with LitServe affiliate
|
|
348
|
+
authenticate(shall_confirm=not non_interactive)
|
|
349
|
+
resolved_teamspace = select_teamspace(teamspace, org, user)
|
|
350
|
+
verified = poll_verified_status()
|
|
351
|
+
if not verified:
|
|
352
|
+
console.print("❌ Verify phone number to continue. Visit lightning.ai.", style="red")
|
|
353
|
+
return
|
|
354
|
+
|
|
254
355
|
# list containers to create the project if it doesn't exist
|
|
255
356
|
lit_cr = LitContainerApi()
|
|
256
357
|
lit_cr.list_containers(resolved_teamspace.id, cloud_account=cloud_account)
|
|
@@ -263,17 +364,21 @@ def _handle_cloud(
|
|
|
263
364
|
transient=True,
|
|
264
365
|
) as progress:
|
|
265
366
|
try:
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
repository, tag, resolved_teamspace, lit_cr,
|
|
270
|
-
)
|
|
367
|
+
push_task = progress.add_task("Pushing to registry", total=None)
|
|
368
|
+
push_status = {}
|
|
369
|
+
for line in ls_deployer.push_container(
|
|
370
|
+
repository, tag, resolved_teamspace, lit_cr, cloud_account=cloud_account
|
|
371
|
+
):
|
|
372
|
+
push_status = line
|
|
373
|
+
progress.update(push_task, advance=1)
|
|
374
|
+
if not ("Pushing" in line["status"] or "Waiting" in line["status"]):
|
|
375
|
+
console.print(line["status"])
|
|
376
|
+
progress.update(push_task, description="[green]Push completed![/green]")
|
|
271
377
|
except Exception as e:
|
|
272
378
|
console.print(f"❌ Deployment failed: {e}", style="red")
|
|
273
379
|
return
|
|
274
380
|
console.print(f"\n✅ Image pushed to {repository}:{tag}")
|
|
275
|
-
|
|
276
|
-
image = push_status.get("repository")
|
|
381
|
+
image = push_status.get("image")
|
|
277
382
|
|
|
278
383
|
deployment_status = ls_deployer.run_on_cloud(
|
|
279
384
|
deployment_name=deployment_name,
|
|
@@ -290,3 +395,4 @@ def _handle_cloud(
|
|
|
290
395
|
include_credentials=include_credentials,
|
|
291
396
|
)
|
|
292
397
|
console.print(f"🚀 Deployment started, access at [i]{deployment_status.get('url')}[/i]")
|
|
398
|
+
webbrowser.open(deployment_status.get("url"))
|
|
@@ -83,6 +83,7 @@ from lightning_sdk.lightning_cloud.openapi.models.cloud_space_id_versionpublicat
|
|
|
83
83
|
from lightning_sdk.lightning_cloud.openapi.models.cloud_space_id_versions_body import CloudSpaceIdVersionsBody
|
|
84
84
|
from lightning_sdk.lightning_cloud.openapi.models.cloudspace_id_metric_body import CloudspaceIdMetricBody
|
|
85
85
|
from lightning_sdk.lightning_cloud.openapi.models.cloudspace_id_runs_body import CloudspaceIdRunsBody
|
|
86
|
+
from lightning_sdk.lightning_cloud.openapi.models.cloudspace_id_systemmetrics_body import CloudspaceIdSystemmetricsBody
|
|
86
87
|
from lightning_sdk.lightning_cloud.openapi.models.cloudspaces_id_body import CloudspacesIdBody
|
|
87
88
|
from lightning_sdk.lightning_cloud.openapi.models.cluster_id_capacityblock_body import ClusterIdCapacityblockBody
|
|
88
89
|
from lightning_sdk.lightning_cloud.openapi.models.cluster_id_capacityreservations_body import ClusterIdCapacityreservationsBody
|
|
@@ -495,6 +496,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_get_agent_job_env_response
|
|
|
495
496
|
from lightning_sdk.lightning_cloud.openapi.models.v1_get_agent_job_logs_metadata_response import V1GetAgentJobLogsMetadataResponse
|
|
496
497
|
from lightning_sdk.lightning_cloud.openapi.models.v1_get_artifacts_page_response import V1GetArtifactsPageResponse
|
|
497
498
|
from lightning_sdk.lightning_cloud.openapi.models.v1_get_cloud_space_instance_status_response import V1GetCloudSpaceInstanceStatusResponse
|
|
499
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_get_cloud_space_instance_system_metrics_aggregate_response import V1GetCloudSpaceInstanceSystemMetricsAggregateResponse
|
|
498
500
|
from lightning_sdk.lightning_cloud.openapi.models.v1_get_cloud_space_size_response import V1GetCloudSpaceSizeResponse
|
|
499
501
|
from lightning_sdk.lightning_cloud.openapi.models.v1_get_cluster_accelerator_demand_response import V1GetClusterAcceleratorDemandResponse
|
|
500
502
|
from lightning_sdk.lightning_cloud.openapi.models.v1_get_cluster_credentials_response import V1GetClusterCredentialsResponse
|
|
@@ -775,6 +777,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_refresh_response import V1R
|
|
|
775
777
|
from lightning_sdk.lightning_cloud.openapi.models.v1_region_state import V1RegionState
|
|
776
778
|
from lightning_sdk.lightning_cloud.openapi.models.v1_regional_load_balancer import V1RegionalLoadBalancer
|
|
777
779
|
from lightning_sdk.lightning_cloud.openapi.models.v1_report_cloud_space_instance_stop_at_response import V1ReportCloudSpaceInstanceStopAtResponse
|
|
780
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_report_cloud_space_instance_system_metrics_response import V1ReportCloudSpaceInstanceSystemMetricsResponse
|
|
778
781
|
from lightning_sdk.lightning_cloud.openapi.models.v1_report_logs_activity_response import V1ReportLogsActivityResponse
|
|
779
782
|
from lightning_sdk.lightning_cloud.openapi.models.v1_report_restart_timings_response import V1ReportRestartTimingsResponse
|
|
780
783
|
from lightning_sdk.lightning_cloud.openapi.models.v1_request_cluster_access_request import V1RequestClusterAccessRequest
|
|
@@ -840,6 +843,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_subnet_spec import V1Subnet
|
|
|
840
843
|
from lightning_sdk.lightning_cloud.openapi.models.v1_switch_cloud_space_instance_response import V1SwitchCloudSpaceInstanceResponse
|
|
841
844
|
from lightning_sdk.lightning_cloud.openapi.models.v1_system_info import V1SystemInfo
|
|
842
845
|
from lightning_sdk.lightning_cloud.openapi.models.v1_system_metrics import V1SystemMetrics
|
|
846
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_system_metrics_aggregated import V1SystemMetricsAggregated
|
|
843
847
|
from lightning_sdk.lightning_cloud.openapi.models.v1_system_metrics_list import V1SystemMetricsList
|
|
844
848
|
from lightning_sdk.lightning_cloud.openapi.models.v1_telemetry import V1Telemetry
|
|
845
849
|
from lightning_sdk.lightning_cloud.openapi.models.v1_timestamp_code_telemetry import V1TimestampCodeTelemetry
|
|
@@ -903,6 +907,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_verify_verification_respons
|
|
|
903
907
|
from lightning_sdk.lightning_cloud.openapi.models.v1_voltage_park_direct_v1 import V1VoltageParkDirectV1
|
|
904
908
|
from lightning_sdk.lightning_cloud.openapi.models.v1_volume import V1Volume
|
|
905
909
|
from lightning_sdk.lightning_cloud.openapi.models.v1_vultr_direct_v1 import V1VultrDirectV1
|
|
910
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_weka_data_connection import V1WekaDataConnection
|
|
906
911
|
from lightning_sdk.lightning_cloud.openapi.models.v1_work import V1Work
|
|
907
912
|
from lightning_sdk.lightning_cloud.openapi.models.validate import Validate
|
|
908
913
|
from lightning_sdk.lightning_cloud.openapi.models.validateautojoindomain_domain_body import ValidateautojoindomainDomainBody
|