lightning-sdk 0.2.2__py3-none-any.whl → 0.2.4__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/ai_hub.py +7 -16
- lightning_sdk/api/ai_hub_api.py +5 -3
- lightning_sdk/api/deployment_api.py +4 -0
- lightning_sdk/api/lit_container_api.py +1 -0
- lightning_sdk/cli/serve.py +166 -31
- lightning_sdk/deployment/deployment.py +16 -4
- lightning_sdk/lightning_cloud/openapi/models/project_id_cloudspaces_body.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +27 -27
- lightning_sdk/lit_container.py +8 -1
- lightning_sdk/mmt/mmt.py +1 -18
- lightning_sdk/mmt/v1.py +1 -28
- lightning_sdk/models.py +15 -6
- lightning_sdk/pipeline/pipeline.py +2 -2
- lightning_sdk/serve.py +147 -27
- {lightning_sdk-0.2.2.dist-info → lightning_sdk-0.2.4.dist-info}/METADATA +1 -1
- {lightning_sdk-0.2.2.dist-info → lightning_sdk-0.2.4.dist-info}/RECORD +21 -21
- {lightning_sdk-0.2.2.dist-info → lightning_sdk-0.2.4.dist-info}/LICENSE +0 -0
- {lightning_sdk-0.2.2.dist-info → lightning_sdk-0.2.4.dist-info}/WHEEL +0 -0
- {lightning_sdk-0.2.2.dist-info → lightning_sdk-0.2.4.dist-info}/entry_points.txt +0 -0
- {lightning_sdk-0.2.2.dist-info → lightning_sdk-0.2.4.dist-info}/top_level.txt +0 -0
lightning_sdk/__init__.py
CHANGED
lightning_sdk/ai_hub.py
CHANGED
|
@@ -3,6 +3,7 @@ from urllib.parse import quote
|
|
|
3
3
|
|
|
4
4
|
from lightning_sdk.api import AIHubApi
|
|
5
5
|
from lightning_sdk.api.utils import _get_cloud_url
|
|
6
|
+
from lightning_sdk.lightning_cloud.openapi.models import V1Deployment
|
|
6
7
|
from lightning_sdk.user import User
|
|
7
8
|
from lightning_sdk.utils.resolve import _resolve_teamspace
|
|
8
9
|
|
|
@@ -109,7 +110,7 @@ class AIHub:
|
|
|
109
110
|
org: Optional[Union[str, "Organization"]] = None,
|
|
110
111
|
user: Optional[Union[str, "User"]] = None,
|
|
111
112
|
machine: Optional[Union[str, "Machine"]] = None,
|
|
112
|
-
) ->
|
|
113
|
+
) -> V1Deployment:
|
|
113
114
|
"""Deploy an API from the AI Hub.
|
|
114
115
|
|
|
115
116
|
Example:
|
|
@@ -133,7 +134,7 @@ class AIHub:
|
|
|
133
134
|
machine: The machine to run the deployment on. Defaults to the first option set in the AI Hub template.
|
|
134
135
|
|
|
135
136
|
Returns:
|
|
136
|
-
A
|
|
137
|
+
A V1Deployment object containing the name of the deployed API,
|
|
137
138
|
the URL to access it, and whether it is interruptible.
|
|
138
139
|
|
|
139
140
|
Raises:
|
|
@@ -169,16 +170,9 @@ class AIHub:
|
|
|
169
170
|
|
|
170
171
|
print("Deployment available at:", url)
|
|
171
172
|
|
|
172
|
-
return
|
|
173
|
-
"id": deployment.id,
|
|
174
|
-
"name": deployment.name,
|
|
175
|
-
"deployment_url": url,
|
|
176
|
-
"api_endpoint": deployment.status.urls[0],
|
|
177
|
-
"interruptible": deployment.spec.spot,
|
|
178
|
-
"teamspace id": teamspace_id,
|
|
179
|
-
}
|
|
173
|
+
return deployment
|
|
180
174
|
|
|
181
|
-
def delete_deployment(self, deployment:
|
|
175
|
+
def delete_deployment(self, deployment: V1Deployment) -> None:
|
|
182
176
|
"""Delete a deployment from the AI Hub.
|
|
183
177
|
|
|
184
178
|
Example:
|
|
@@ -188,9 +182,6 @@ class AIHub:
|
|
|
188
182
|
hub.delete_deployment(deployment)
|
|
189
183
|
|
|
190
184
|
Args:
|
|
191
|
-
deployment: The deployment
|
|
185
|
+
deployment: The deployment object returned by the run method.
|
|
192
186
|
"""
|
|
193
|
-
|
|
194
|
-
raise ValueError("Deployment dictionary must contain 'teamspace id' and 'id' keys.")
|
|
195
|
-
|
|
196
|
-
self._api.delete_api(deployment["id"], deployment["teamspace id"])
|
|
187
|
+
self._api.delete_api(deployment.id, deployment.project_id)
|
lightning_sdk/api/ai_hub_api.py
CHANGED
|
@@ -95,15 +95,17 @@ class AIHubApi:
|
|
|
95
95
|
|
|
96
96
|
if p.type == V1DeploymentTemplateParameterType.CHECKBOX and p.checkbox:
|
|
97
97
|
api_arguments[p.name] = (
|
|
98
|
-
(p.checkbox.true_value or "
|
|
99
|
-
if p.checkbox.is_checked
|
|
100
|
-
else (p.checkbox.false_value or "False")
|
|
98
|
+
(p.checkbox.true_value or "") if p.checkbox.is_checked else (p.checkbox.false_value or "")
|
|
101
99
|
)
|
|
102
100
|
|
|
103
101
|
for p in parameters:
|
|
104
102
|
name = p.name
|
|
105
103
|
pattern = f"${{{name}}}"
|
|
106
104
|
if name in api_arguments:
|
|
105
|
+
if p.type == V1DeploymentTemplateParameterType.CHECKBOX and p.checkbox:
|
|
106
|
+
api_arguments[p.name] = (
|
|
107
|
+
(p.checkbox.true_value or "") if api_arguments[name] is True else (p.checkbox.false_value or "")
|
|
108
|
+
)
|
|
107
109
|
AIHubApi._update_parameters(job, p.placements, pattern, api_arguments[name])
|
|
108
110
|
elif not p.required:
|
|
109
111
|
AIHubApi._update_parameters(job, p.placements, pattern, "")
|
|
@@ -258,6 +258,7 @@ class DeploymentApi:
|
|
|
258
258
|
auth: Optional[Union[BasicAuth, TokenAuth]] = None,
|
|
259
259
|
custom_domain: Optional[str] = None,
|
|
260
260
|
quantity: Optional[int] = None,
|
|
261
|
+
include_credentials: Optional[bool] = None,
|
|
261
262
|
) -> V1Deployment:
|
|
262
263
|
# Update the deployment in place
|
|
263
264
|
|
|
@@ -283,6 +284,7 @@ class DeploymentApi:
|
|
|
283
284
|
requires_release |= apply_change(deployment.spec, "cluster_id", cloud_account)
|
|
284
285
|
requires_release |= apply_change(deployment.spec, "spot", spot)
|
|
285
286
|
requires_release |= apply_change(deployment.spec, "quantity", quantity)
|
|
287
|
+
requires_release |= apply_change(deployment.spec, "include_credentials", include_credentials)
|
|
286
288
|
|
|
287
289
|
if requires_release:
|
|
288
290
|
if deployment.strategy is None:
|
|
@@ -549,6 +551,7 @@ def to_spec(
|
|
|
549
551
|
env: Union[List[Union[Secret, Env]], Dict[str, str], None] = None,
|
|
550
552
|
health_check: Optional[Union[HttpHealthCheck, ExecHealthCheck]] = None,
|
|
551
553
|
quantity: Optional[int] = None,
|
|
554
|
+
include_credentials: Optional[bool] = None,
|
|
552
555
|
) -> V1JobSpec:
|
|
553
556
|
if cloud_account is None:
|
|
554
557
|
raise ValueError("The cloud account should be defined.")
|
|
@@ -569,6 +572,7 @@ def to_spec(
|
|
|
569
572
|
instance_name=_machine_to_compute_name(machine),
|
|
570
573
|
readiness_probe=to_health_check(health_check),
|
|
571
574
|
quantity=quantity,
|
|
575
|
+
include_credentials=include_credentials,
|
|
572
576
|
)
|
|
573
577
|
|
|
574
578
|
|
|
@@ -120,6 +120,7 @@ class LitContainerApi:
|
|
|
120
120
|
yield {
|
|
121
121
|
"finish": True,
|
|
122
122
|
"url": f"{LIGHTNING_CLOUD_URL}/{teamspace.owner.name}/{teamspace.name}/containers/{container_basename}",
|
|
123
|
+
"repository": repository,
|
|
123
124
|
}
|
|
124
125
|
|
|
125
126
|
def _push_with_retry(self, repository: str, max_retries: int = 3) -> Iterator[Dict[str, Any]]:
|
lightning_sdk/cli/serve.py
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
|
+
import os
|
|
1
2
|
import subprocess
|
|
2
3
|
from pathlib import Path
|
|
3
4
|
from typing import Optional, Union
|
|
4
5
|
|
|
5
6
|
import click
|
|
6
|
-
import docker
|
|
7
7
|
from rich.console import Console
|
|
8
8
|
from rich.progress import Progress, SpinnerColumn, TextColumn, TimeElapsedColumn
|
|
9
9
|
from rich.prompt import Confirm
|
|
10
10
|
|
|
11
|
+
from lightning_sdk import Machine, Teamspace
|
|
12
|
+
from lightning_sdk.api.deployment_api import DeploymentApi
|
|
11
13
|
from lightning_sdk.api.lit_container_api import LitContainerApi
|
|
14
|
+
from lightning_sdk.cli.exceptions import StudioCliError
|
|
12
15
|
from lightning_sdk.cli.teamspace_menu import _TeamspacesMenu
|
|
13
16
|
from lightning_sdk.serve import _LitServeDeployer
|
|
14
17
|
|
|
18
|
+
_MACHINE_VALUES = tuple([machine.name for machine in Machine.__dict__.values() if isinstance(machine, Machine)])
|
|
19
|
+
|
|
15
20
|
|
|
16
21
|
@click.group("serve")
|
|
17
22
|
def serve() -> None:
|
|
@@ -21,11 +26,10 @@ def serve() -> None:
|
|
|
21
26
|
lightning serve api server.py # serve locally
|
|
22
27
|
|
|
23
28
|
Example:
|
|
24
|
-
lightning serve api server.py --cloud # deploy to the cloud
|
|
29
|
+
lightning serve api server.py --cloud --name litserve-api # deploy to the cloud
|
|
25
30
|
|
|
26
31
|
You can deploy the API to the cloud by running `lightning serve api server.py --cloud`.
|
|
27
|
-
This will
|
|
28
|
-
Deploying to the cloud requires pre-login to the docker registry.
|
|
32
|
+
This will build a docker container for the server.py script and deploy it to the Lightning AI platform.
|
|
29
33
|
"""
|
|
30
34
|
|
|
31
35
|
|
|
@@ -45,8 +49,7 @@ def serve() -> None:
|
|
|
45
49
|
flag_value=True,
|
|
46
50
|
help="Deploy the model to the Lightning AI platform",
|
|
47
51
|
)
|
|
48
|
-
@click.option("--
|
|
49
|
-
@click.option("--repository", default=None, help="Docker repository name (e.g., 'username/model-name')")
|
|
52
|
+
@click.option("--name", default=None, help="Name of the deployed API (e.g., 'classification-api', 'Llama-api')")
|
|
50
53
|
@click.option(
|
|
51
54
|
"--non-interactive",
|
|
52
55
|
"--non_interactive",
|
|
@@ -55,17 +58,88 @@ def serve() -> None:
|
|
|
55
58
|
flag_value=True,
|
|
56
59
|
help="Do not prompt for confirmation",
|
|
57
60
|
)
|
|
61
|
+
@click.option(
|
|
62
|
+
"--machine",
|
|
63
|
+
default="CPU",
|
|
64
|
+
show_default=True,
|
|
65
|
+
type=click.Choice(_MACHINE_VALUES),
|
|
66
|
+
help="The machine type to deploy the API on.",
|
|
67
|
+
)
|
|
68
|
+
@click.option(
|
|
69
|
+
"--interruptible",
|
|
70
|
+
is_flag=True,
|
|
71
|
+
default=False,
|
|
72
|
+
flag_value=True,
|
|
73
|
+
help="Whether the machine should be interruptible (spot) or not.",
|
|
74
|
+
)
|
|
75
|
+
@click.option(
|
|
76
|
+
"--teamspace",
|
|
77
|
+
default=None,
|
|
78
|
+
help="The teamspace the deployment should be associated with. Defaults to the current teamspace.",
|
|
79
|
+
)
|
|
80
|
+
@click.option(
|
|
81
|
+
"--org",
|
|
82
|
+
default=None,
|
|
83
|
+
help="The organization owning the teamspace (if any). Defaults to the current organization.",
|
|
84
|
+
)
|
|
85
|
+
@click.option("--user", default=None, help="The user owning the teamspace (if any). Defaults to the current user.")
|
|
86
|
+
@click.option(
|
|
87
|
+
"--cloud-account",
|
|
88
|
+
"--cloud_account",
|
|
89
|
+
default=None,
|
|
90
|
+
help=(
|
|
91
|
+
"The cloud account to run the deployment on. "
|
|
92
|
+
"Defaults to the studio cloud account if running with studio compute env. "
|
|
93
|
+
"If not provided will fall back to the teamspaces default cloud account."
|
|
94
|
+
),
|
|
95
|
+
)
|
|
96
|
+
@click.option("--port", default=8000, help="The port to expose the API on.")
|
|
97
|
+
@click.option("--min_replica", "--min-replica", default=0, help="Number of replicas to start with.")
|
|
98
|
+
@click.option("--max_replica", "--max-replica", default=1, help="Number of replicas to scale up to.")
|
|
99
|
+
@click.option("--replicas", "--replicas", default=1, help="Deployment will start with this many replicas.")
|
|
100
|
+
@click.option(
|
|
101
|
+
"--no_credentials",
|
|
102
|
+
"--no-credentials",
|
|
103
|
+
is_flag=True,
|
|
104
|
+
default=False,
|
|
105
|
+
flag_value=True,
|
|
106
|
+
help="Whether to include credentials in the deployment.",
|
|
107
|
+
)
|
|
58
108
|
def api(
|
|
59
109
|
script_path: str,
|
|
60
110
|
easy: bool,
|
|
61
111
|
cloud: bool,
|
|
62
|
-
|
|
63
|
-
repository: str,
|
|
112
|
+
name: str,
|
|
64
113
|
non_interactive: bool,
|
|
114
|
+
machine: str,
|
|
115
|
+
interruptible: bool,
|
|
116
|
+
teamspace: Optional[str],
|
|
117
|
+
org: Optional[str],
|
|
118
|
+
user: Optional[str],
|
|
119
|
+
cloud_account: Optional[str],
|
|
120
|
+
port: Optional[int],
|
|
121
|
+
min_replica: Optional[int],
|
|
122
|
+
max_replica: Optional[int],
|
|
123
|
+
replicas: Optional[int],
|
|
124
|
+
no_credentials: Optional[bool],
|
|
65
125
|
) -> None:
|
|
66
126
|
"""Deploy a LitServe model script."""
|
|
67
127
|
return api_impl(
|
|
68
|
-
script_path=script_path,
|
|
128
|
+
script_path=script_path,
|
|
129
|
+
easy=easy,
|
|
130
|
+
cloud=cloud,
|
|
131
|
+
repository=name,
|
|
132
|
+
non_interactive=non_interactive,
|
|
133
|
+
machine=machine,
|
|
134
|
+
interruptible=interruptible,
|
|
135
|
+
teamspace=teamspace,
|
|
136
|
+
org=org,
|
|
137
|
+
user=user,
|
|
138
|
+
cloud_account=cloud_account,
|
|
139
|
+
port=port,
|
|
140
|
+
min_replica=min_replica,
|
|
141
|
+
max_replica=max_replica,
|
|
142
|
+
include_credentials=not no_credentials,
|
|
69
143
|
)
|
|
70
144
|
|
|
71
145
|
|
|
@@ -73,9 +147,20 @@ def api_impl(
|
|
|
73
147
|
script_path: Union[str, Path],
|
|
74
148
|
easy: bool = False,
|
|
75
149
|
cloud: bool = False,
|
|
76
|
-
|
|
77
|
-
|
|
150
|
+
repository: [str] = None,
|
|
151
|
+
tag: Optional[str] = None,
|
|
78
152
|
non_interactive: bool = False,
|
|
153
|
+
machine: str = "CPU",
|
|
154
|
+
interruptible: bool = False,
|
|
155
|
+
teamspace: Optional[str] = None,
|
|
156
|
+
org: Optional[str] = None,
|
|
157
|
+
user: Optional[str] = None,
|
|
158
|
+
cloud_account: Optional[str] = None,
|
|
159
|
+
port: Optional[int] = 8000,
|
|
160
|
+
min_replica: Optional[int] = 0,
|
|
161
|
+
max_replica: Optional[int] = 1,
|
|
162
|
+
replicas: Optional[int] = 1,
|
|
163
|
+
include_credentials: Optional[bool] = True,
|
|
79
164
|
) -> None:
|
|
80
165
|
"""Deploy a LitServe model script."""
|
|
81
166
|
console = Console()
|
|
@@ -85,12 +170,29 @@ def api_impl(
|
|
|
85
170
|
if not script_path.is_file():
|
|
86
171
|
raise ValueError(f"Path is not a file: {script_path}")
|
|
87
172
|
|
|
88
|
-
|
|
89
|
-
ls_deployer.generate_client() if easy else None
|
|
173
|
+
_LitServeDeployer.generate_client() if easy else None
|
|
90
174
|
|
|
91
175
|
if cloud:
|
|
92
|
-
|
|
93
|
-
|
|
176
|
+
repository = repository or "litserve-model"
|
|
177
|
+
machine = Machine.from_str(machine)
|
|
178
|
+
return _handle_cloud(
|
|
179
|
+
script_path,
|
|
180
|
+
console,
|
|
181
|
+
repository=repository,
|
|
182
|
+
tag=tag,
|
|
183
|
+
non_interactive=non_interactive,
|
|
184
|
+
machine=machine,
|
|
185
|
+
interruptible=interruptible,
|
|
186
|
+
teamspace=teamspace,
|
|
187
|
+
org=org,
|
|
188
|
+
user=user,
|
|
189
|
+
cloud_account=cloud_account,
|
|
190
|
+
port=port,
|
|
191
|
+
min_replica=min_replica,
|
|
192
|
+
max_replica=max_replica,
|
|
193
|
+
replicas=replicas,
|
|
194
|
+
include_credentials=include_credentials,
|
|
195
|
+
)
|
|
94
196
|
|
|
95
197
|
try:
|
|
96
198
|
subprocess.run(
|
|
@@ -106,20 +208,30 @@ def api_impl(
|
|
|
106
208
|
def _handle_cloud(
|
|
107
209
|
script_path: Union[str, Path],
|
|
108
210
|
console: Console,
|
|
109
|
-
gpu: bool,
|
|
110
211
|
repository: str = "litserve-model",
|
|
111
212
|
tag: Optional[str] = None,
|
|
112
|
-
teamspace: Optional[str] = None,
|
|
113
213
|
non_interactive: bool = False,
|
|
214
|
+
machine: Machine = "CPU",
|
|
215
|
+
interruptible: bool = False,
|
|
216
|
+
teamspace: Optional[str] = None,
|
|
217
|
+
org: Optional[str] = None,
|
|
218
|
+
user: Optional[str] = None,
|
|
219
|
+
cloud_account: Optional[str] = None,
|
|
220
|
+
port: Optional[int] = 8000,
|
|
221
|
+
min_replica: Optional[int] = 0,
|
|
222
|
+
max_replica: Optional[int] = 1,
|
|
223
|
+
replicas: Optional[int] = 1,
|
|
224
|
+
include_credentials: Optional[bool] = True,
|
|
114
225
|
) -> None:
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
226
|
+
if teamspace is None:
|
|
227
|
+
menu = _TeamspacesMenu()
|
|
228
|
+
resolved_teamspace = menu._resolve_teamspace(teamspace)
|
|
229
|
+
else:
|
|
230
|
+
resolved_teamspace = Teamspace(name=teamspace, org=org, user=user)
|
|
120
231
|
|
|
232
|
+
port = port or 8000
|
|
121
233
|
ls_deployer = _LitServeDeployer()
|
|
122
|
-
path = ls_deployer.dockerize_api(script_path, port=
|
|
234
|
+
path = ls_deployer.dockerize_api(script_path, port=port, gpu=not machine.is_cpu(), tag=tag, print_success=False)
|
|
123
235
|
console.clear()
|
|
124
236
|
if non_interactive:
|
|
125
237
|
console.print("[italic]non-interactive[/italic] mode enabled, skipping confirmation prompts", style="blue")
|
|
@@ -133,18 +245,41 @@ def _handle_cloud(
|
|
|
133
245
|
tag = tag if tag else "latest"
|
|
134
246
|
|
|
135
247
|
lit_cr = LitContainerApi()
|
|
136
|
-
|
|
137
|
-
|
|
248
|
+
deployment_name = os.path.basename(repository)
|
|
249
|
+
|
|
250
|
+
ls_deployer.authenticate()
|
|
251
|
+
if DeploymentApi().get_deployment_by_name(deployment_name, resolved_teamspace.id):
|
|
252
|
+
raise StudioCliError(f"Deployment {deployment_name} already exists. Please choose a different name.") from None
|
|
253
|
+
|
|
138
254
|
with Progress(
|
|
139
255
|
SpinnerColumn(),
|
|
140
256
|
TextColumn("[progress.description]{task.description}"),
|
|
141
257
|
TimeElapsedColumn(),
|
|
142
258
|
console=console,
|
|
143
|
-
transient=
|
|
259
|
+
transient=True,
|
|
144
260
|
) as progress:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
261
|
+
try:
|
|
262
|
+
ls_deployer.build_container(path, repository, tag, console, progress)
|
|
263
|
+
push_status = ls_deployer.push_container(repository, tag, resolved_teamspace, lit_cr, progress)
|
|
264
|
+
except Exception as e:
|
|
265
|
+
console.print(f"❌ Deployment failed: {e}", style="red")
|
|
266
|
+
return
|
|
267
|
+
console.print(f"\n✅ Image pushed to {repository}:{tag}")
|
|
268
|
+
console.print(f"🔗 You can access the container at: [i]{push_status.get('url')}[/i]")
|
|
269
|
+
repository = push_status.get("repository")
|
|
270
|
+
|
|
271
|
+
deployment_status = ls_deployer._run_on_cloud(
|
|
272
|
+
deployment_name=deployment_name,
|
|
273
|
+
image=repository,
|
|
274
|
+
teamspace=resolved_teamspace,
|
|
275
|
+
metric=None,
|
|
276
|
+
machine=machine,
|
|
277
|
+
spot=interruptible,
|
|
278
|
+
cloud_account=cloud_account,
|
|
279
|
+
port=port,
|
|
280
|
+
min_replica=min_replica,
|
|
281
|
+
max_replica=max_replica,
|
|
282
|
+
replicas=replicas,
|
|
283
|
+
include_credentials=include_credentials,
|
|
150
284
|
)
|
|
285
|
+
console.print(f"🚀 Deployment started, access at [i]{deployment_status.get('url')}[/i]")
|
|
@@ -32,7 +32,7 @@ from lightning_sdk.organization import Organization
|
|
|
32
32
|
from lightning_sdk.services.utilities import _get_cluster
|
|
33
33
|
from lightning_sdk.teamspace import Teamspace
|
|
34
34
|
from lightning_sdk.user import User
|
|
35
|
-
from lightning_sdk.utils.resolve import _resolve_deprecated_cluster,
|
|
35
|
+
from lightning_sdk.utils.resolve import _resolve_deprecated_cluster, _resolve_teamspace, _resolve_user
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
class Deployment:
|
|
@@ -73,13 +73,12 @@ class Deployment:
|
|
|
73
73
|
raise e
|
|
74
74
|
|
|
75
75
|
self._name = name
|
|
76
|
-
self._org = _resolve_org(org)
|
|
77
76
|
self._user = _resolve_user(self._user or user)
|
|
78
77
|
|
|
79
78
|
self._teamspace = _resolve_teamspace(
|
|
80
79
|
teamspace=teamspace,
|
|
81
|
-
org=
|
|
82
|
-
user=
|
|
80
|
+
org=org,
|
|
81
|
+
user=user,
|
|
83
82
|
)
|
|
84
83
|
if self._teamspace is None:
|
|
85
84
|
raise ValueError("You need to pass a teamspace or an org for your deployment.")
|
|
@@ -116,6 +115,7 @@ class Deployment:
|
|
|
116
115
|
custom_domain: Optional[str] = None,
|
|
117
116
|
cluster: Optional[str] = None, # deprecated in favor of cloud_account
|
|
118
117
|
quantity: Optional[int] = None,
|
|
118
|
+
include_credentials: Optional[bool] = None,
|
|
119
119
|
) -> None:
|
|
120
120
|
"""The Lightning AI Deployment.
|
|
121
121
|
|
|
@@ -140,6 +140,7 @@ class Deployment:
|
|
|
140
140
|
Doesn't matter when the studio already exists.
|
|
141
141
|
custom_domain: Whether your service would be referenced under a custom doamin.
|
|
142
142
|
quantity: The number of machines per replica to deploy.
|
|
143
|
+
include_credentials: Whether to include the environement variables for the SDK to authenticate
|
|
143
144
|
|
|
144
145
|
Note:
|
|
145
146
|
Since a teamspace can either be owned by an org or by a user directly,
|
|
@@ -172,6 +173,7 @@ class Deployment:
|
|
|
172
173
|
machine=machine,
|
|
173
174
|
health_check=health_check,
|
|
174
175
|
quantity=quantity,
|
|
176
|
+
include_credentials=include_credentials if include_credentials is not None else True,
|
|
175
177
|
),
|
|
176
178
|
strategy=to_strategy(release_strategy),
|
|
177
179
|
)
|
|
@@ -203,6 +205,7 @@ class Deployment:
|
|
|
203
205
|
custom_domain: Optional[str] = None,
|
|
204
206
|
cluster: Optional[str] = None, # deprecated in favor of cloud_account
|
|
205
207
|
quantity: Optional[int] = None,
|
|
208
|
+
include_credentials: Optional[bool] = None,
|
|
206
209
|
) -> None:
|
|
207
210
|
cloud_account = _resolve_deprecated_cluster(cloud_account, cluster)
|
|
208
211
|
|
|
@@ -225,6 +228,7 @@ class Deployment:
|
|
|
225
228
|
health_check=health_check,
|
|
226
229
|
release_strategy=release_strategy,
|
|
227
230
|
quantity=quantity,
|
|
231
|
+
include_credentials=include_credentials if include_credentials is not None else True,
|
|
228
232
|
)
|
|
229
233
|
|
|
230
234
|
def stop(self) -> None:
|
|
@@ -359,6 +363,14 @@ class Deployment:
|
|
|
359
363
|
return self._deployment.spec.quantity
|
|
360
364
|
return None
|
|
361
365
|
|
|
366
|
+
@property
|
|
367
|
+
def include_credentials(self) -> Optional[bool]:
|
|
368
|
+
"""The number of machines per replica."""
|
|
369
|
+
if self._deployment:
|
|
370
|
+
self._deployment = self._deployment_api.get_deployment_by_name(self._name, self._teamspace.id)
|
|
371
|
+
return self._deployment.spec.include_credentials
|
|
372
|
+
return None
|
|
373
|
+
|
|
362
374
|
@property
|
|
363
375
|
def user(self) -> Optional[User]:
|
|
364
376
|
"""The teamspace of the deployment."""
|
|
@@ -52,6 +52,7 @@ class ProjectIdCloudspacesBody(object):
|
|
|
52
52
|
'name': 'str',
|
|
53
53
|
'plugins': 'list[str]',
|
|
54
54
|
'requested_run_duration_seconds': 'str',
|
|
55
|
+
'same_compute_on_resume': 'bool',
|
|
55
56
|
'seed_files': 'list[V1CloudSpaceSeedFile]',
|
|
56
57
|
'spot': 'bool'
|
|
57
58
|
}
|
|
@@ -68,11 +69,12 @@ class ProjectIdCloudspacesBody(object):
|
|
|
68
69
|
'name': 'name',
|
|
69
70
|
'plugins': 'plugins',
|
|
70
71
|
'requested_run_duration_seconds': 'requestedRunDurationSeconds',
|
|
72
|
+
'same_compute_on_resume': 'sameComputeOnResume',
|
|
71
73
|
'seed_files': 'seedFiles',
|
|
72
74
|
'spot': 'spot'
|
|
73
75
|
}
|
|
74
76
|
|
|
75
|
-
def __init__(self, can_download_source_code: 'bool' =None, cloud_space_instance_cpu_image_override: 'str' =None, cloud_space_instance_gpu_image_override: 'str' =None, cluster_id: 'str' =None, compute_name: 'str' =None, data_connection_mounts: 'list[V1DataConnectionMount]' =None, disk_size: 'str' =None, display_name: 'str' =None, name: 'str' =None, plugins: 'list[str]' =None, requested_run_duration_seconds: 'str' =None, seed_files: 'list[V1CloudSpaceSeedFile]' =None, spot: 'bool' =None): # noqa: E501
|
|
77
|
+
def __init__(self, can_download_source_code: 'bool' =None, cloud_space_instance_cpu_image_override: 'str' =None, cloud_space_instance_gpu_image_override: 'str' =None, cluster_id: 'str' =None, compute_name: 'str' =None, data_connection_mounts: 'list[V1DataConnectionMount]' =None, disk_size: 'str' =None, display_name: 'str' =None, name: 'str' =None, plugins: 'list[str]' =None, requested_run_duration_seconds: 'str' =None, same_compute_on_resume: 'bool' =None, seed_files: 'list[V1CloudSpaceSeedFile]' =None, spot: 'bool' =None): # noqa: E501
|
|
76
78
|
"""ProjectIdCloudspacesBody - a model defined in Swagger""" # noqa: E501
|
|
77
79
|
self._can_download_source_code = None
|
|
78
80
|
self._cloud_space_instance_cpu_image_override = None
|
|
@@ -85,6 +87,7 @@ class ProjectIdCloudspacesBody(object):
|
|
|
85
87
|
self._name = None
|
|
86
88
|
self._plugins = None
|
|
87
89
|
self._requested_run_duration_seconds = None
|
|
90
|
+
self._same_compute_on_resume = None
|
|
88
91
|
self._seed_files = None
|
|
89
92
|
self._spot = None
|
|
90
93
|
self.discriminator = None
|
|
@@ -110,6 +113,8 @@ class ProjectIdCloudspacesBody(object):
|
|
|
110
113
|
self.plugins = plugins
|
|
111
114
|
if requested_run_duration_seconds is not None:
|
|
112
115
|
self.requested_run_duration_seconds = requested_run_duration_seconds
|
|
116
|
+
if same_compute_on_resume is not None:
|
|
117
|
+
self.same_compute_on_resume = same_compute_on_resume
|
|
113
118
|
if seed_files is not None:
|
|
114
119
|
self.seed_files = seed_files
|
|
115
120
|
if spot is not None:
|
|
@@ -346,6 +351,27 @@ class ProjectIdCloudspacesBody(object):
|
|
|
346
351
|
|
|
347
352
|
self._requested_run_duration_seconds = requested_run_duration_seconds
|
|
348
353
|
|
|
354
|
+
@property
|
|
355
|
+
def same_compute_on_resume(self) -> 'bool':
|
|
356
|
+
"""Gets the same_compute_on_resume of this ProjectIdCloudspacesBody. # noqa: E501
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
:return: The same_compute_on_resume of this ProjectIdCloudspacesBody. # noqa: E501
|
|
360
|
+
:rtype: bool
|
|
361
|
+
"""
|
|
362
|
+
return self._same_compute_on_resume
|
|
363
|
+
|
|
364
|
+
@same_compute_on_resume.setter
|
|
365
|
+
def same_compute_on_resume(self, same_compute_on_resume: 'bool'):
|
|
366
|
+
"""Sets the same_compute_on_resume of this ProjectIdCloudspacesBody.
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
:param same_compute_on_resume: The same_compute_on_resume of this ProjectIdCloudspacesBody. # noqa: E501
|
|
370
|
+
:type: bool
|
|
371
|
+
"""
|
|
372
|
+
|
|
373
|
+
self._same_compute_on_resume = same_compute_on_resume
|
|
374
|
+
|
|
349
375
|
@property
|
|
350
376
|
def seed_files(self) -> 'list[V1CloudSpaceSeedFile]':
|
|
351
377
|
"""Gets the seed_files of this ProjectIdCloudspacesBody. # noqa: E501
|
|
@@ -61,6 +61,7 @@ class V1UserFeatures(object):
|
|
|
61
61
|
'deployment_alerts': 'bool',
|
|
62
62
|
'deployment_persistent_disk': 'bool',
|
|
63
63
|
'deployment_reservations': 'bool',
|
|
64
|
+
'dgx_cloud': 'bool',
|
|
64
65
|
'docs_agent': 'bool',
|
|
65
66
|
'drive_v2': 'bool',
|
|
66
67
|
'enable_crypto_crackdown': 'bool',
|
|
@@ -82,7 +83,6 @@ class V1UserFeatures(object):
|
|
|
82
83
|
'open_api_in_studio': 'bool',
|
|
83
84
|
'org_level_member_permissions': 'bool',
|
|
84
85
|
'pipelines': 'bool',
|
|
85
|
-
'plugin_biz_chat': 'bool',
|
|
86
86
|
'plugin_distributed': 'bool',
|
|
87
87
|
'plugin_fiftyone': 'bool',
|
|
88
88
|
'plugin_inference': 'bool',
|
|
@@ -139,6 +139,7 @@ class V1UserFeatures(object):
|
|
|
139
139
|
'deployment_alerts': 'deploymentAlerts',
|
|
140
140
|
'deployment_persistent_disk': 'deploymentPersistentDisk',
|
|
141
141
|
'deployment_reservations': 'deploymentReservations',
|
|
142
|
+
'dgx_cloud': 'dgxCloud',
|
|
142
143
|
'docs_agent': 'docsAgent',
|
|
143
144
|
'drive_v2': 'driveV2',
|
|
144
145
|
'enable_crypto_crackdown': 'enableCryptoCrackdown',
|
|
@@ -160,7 +161,6 @@ class V1UserFeatures(object):
|
|
|
160
161
|
'open_api_in_studio': 'openApiInStudio',
|
|
161
162
|
'org_level_member_permissions': 'orgLevelMemberPermissions',
|
|
162
163
|
'pipelines': 'pipelines',
|
|
163
|
-
'plugin_biz_chat': 'pluginBizChat',
|
|
164
164
|
'plugin_distributed': 'pluginDistributed',
|
|
165
165
|
'plugin_fiftyone': 'pluginFiftyone',
|
|
166
166
|
'plugin_inference': 'pluginInference',
|
|
@@ -196,7 +196,7 @@ class V1UserFeatures(object):
|
|
|
196
196
|
'vultr': 'vultr'
|
|
197
197
|
}
|
|
198
198
|
|
|
199
|
-
def __init__(self, affiliate_links: 'bool' =None, agents_v2: 'bool' =None, ai_hub_monetization: 'bool' =None, auto_fast_load: 'bool' =None, auto_join_orgs: 'bool' =None, b2c_experience: 'bool' =None, cap_add: 'list[str]' =None, cap_drop: 'list[str]' =None, capacity_reservation_byoc: 'bool' =None, capacity_reservation_dry_run: 'bool' =None, chat_models: 'bool' =None, code_tab: 'bool' =None, collab_screen_sharing: 'bool' =None, cost_attribution_settings: 'bool' =None, custom_app_domain: 'bool' =None, custom_instance_types: 'bool' =None, default_one_cluster: 'bool' =None, deployment_alerts: 'bool' =None, deployment_persistent_disk: 'bool' =None, deployment_reservations: 'bool' =None, docs_agent: 'bool' =None, drive_v2: 'bool' =None, enable_crypto_crackdown: 'bool' =None, enable_storage_limits: 'bool' =None, fair_share: 'bool' =None, featured_studios_admin: 'bool' =None, filestore: 'bool' =None, instant_capacity_reservation: 'bool' =None, job_artifacts_v2: 'bool' =None, jobs_v2: 'bool' =None, lambda_labs: 'bool' =None, landing_studios: 'bool' =None, lit_logger: 'bool' =None, mmt_fault_tolerance: 'bool' =None, mmt_strategy_selector: 'bool' =None, mmt_v2: 'bool' =None, multicloud_saas: 'bool' =None, multiple_studio_versions: 'bool' =None, open_api_in_studio: 'bool' =None, org_level_member_permissions: 'bool' =None, pipelines: 'bool' =None,
|
|
199
|
+
def __init__(self, affiliate_links: 'bool' =None, agents_v2: 'bool' =None, ai_hub_monetization: 'bool' =None, auto_fast_load: 'bool' =None, auto_join_orgs: 'bool' =None, b2c_experience: 'bool' =None, cap_add: 'list[str]' =None, cap_drop: 'list[str]' =None, capacity_reservation_byoc: 'bool' =None, capacity_reservation_dry_run: 'bool' =None, chat_models: 'bool' =None, code_tab: 'bool' =None, collab_screen_sharing: 'bool' =None, cost_attribution_settings: 'bool' =None, custom_app_domain: 'bool' =None, custom_instance_types: 'bool' =None, default_one_cluster: 'bool' =None, deployment_alerts: 'bool' =None, deployment_persistent_disk: 'bool' =None, deployment_reservations: 'bool' =None, dgx_cloud: 'bool' =None, docs_agent: 'bool' =None, drive_v2: 'bool' =None, enable_crypto_crackdown: 'bool' =None, enable_storage_limits: 'bool' =None, fair_share: 'bool' =None, featured_studios_admin: 'bool' =None, filestore: 'bool' =None, instant_capacity_reservation: 'bool' =None, job_artifacts_v2: 'bool' =None, jobs_v2: 'bool' =None, lambda_labs: 'bool' =None, landing_studios: 'bool' =None, lit_logger: 'bool' =None, mmt_fault_tolerance: 'bool' =None, mmt_strategy_selector: 'bool' =None, mmt_v2: 'bool' =None, multicloud_saas: 'bool' =None, multiple_studio_versions: 'bool' =None, open_api_in_studio: 'bool' =None, org_level_member_permissions: 'bool' =None, pipelines: 'bool' =None, plugin_distributed: 'bool' =None, plugin_fiftyone: 'bool' =None, plugin_inference: 'bool' =None, plugin_label_studio: 'bool' =None, plugin_langflow: 'bool' =None, plugin_lightning_apps: 'bool' =None, plugin_lightning_apps_distributed: 'bool' =None, plugin_mage_ai: 'bool' =None, plugin_milvus: 'bool' =None, plugin_python_profiler: 'bool' =None, plugin_react: 'bool' =None, plugin_service: 'bool' =None, plugin_sweeps: 'bool' =None, plugin_weviate: 'bool' =None, pricing_updates: 'bool' =None, product_generator: 'bool' =None, project_selector: 'bool' =None, restartable_jobs: 'bool' =None, runnable_public_studio_page: 'bool' =None, security_docs: 'bool' =None, show_dev_admin: 'bool' =None, slurm: 'bool' =None, slurm_machine_selector: 'bool' =None, snapshotter_service: 'bool' =None, snowflake_connection: 'bool' =None, stop_ide_container_on_shutdown: 'bool' =None, studio_config: 'bool' =None, studio_on_stop: 'bool' =None, studio_version_visibility: 'bool' =None, teamspace_storage_tab: 'bool' =None, trainium2: 'bool' =None, use_rclone_mounts_only: 'bool' =None, vultr: 'bool' =None): # noqa: E501
|
|
200
200
|
"""V1UserFeatures - a model defined in Swagger""" # noqa: E501
|
|
201
201
|
self._affiliate_links = None
|
|
202
202
|
self._agents_v2 = None
|
|
@@ -218,6 +218,7 @@ class V1UserFeatures(object):
|
|
|
218
218
|
self._deployment_alerts = None
|
|
219
219
|
self._deployment_persistent_disk = None
|
|
220
220
|
self._deployment_reservations = None
|
|
221
|
+
self._dgx_cloud = None
|
|
221
222
|
self._docs_agent = None
|
|
222
223
|
self._drive_v2 = None
|
|
223
224
|
self._enable_crypto_crackdown = None
|
|
@@ -239,7 +240,6 @@ class V1UserFeatures(object):
|
|
|
239
240
|
self._open_api_in_studio = None
|
|
240
241
|
self._org_level_member_permissions = None
|
|
241
242
|
self._pipelines = None
|
|
242
|
-
self._plugin_biz_chat = None
|
|
243
243
|
self._plugin_distributed = None
|
|
244
244
|
self._plugin_fiftyone = None
|
|
245
245
|
self._plugin_inference = None
|
|
@@ -314,6 +314,8 @@ class V1UserFeatures(object):
|
|
|
314
314
|
self.deployment_persistent_disk = deployment_persistent_disk
|
|
315
315
|
if deployment_reservations is not None:
|
|
316
316
|
self.deployment_reservations = deployment_reservations
|
|
317
|
+
if dgx_cloud is not None:
|
|
318
|
+
self.dgx_cloud = dgx_cloud
|
|
317
319
|
if docs_agent is not None:
|
|
318
320
|
self.docs_agent = docs_agent
|
|
319
321
|
if drive_v2 is not None:
|
|
@@ -356,8 +358,6 @@ class V1UserFeatures(object):
|
|
|
356
358
|
self.org_level_member_permissions = org_level_member_permissions
|
|
357
359
|
if pipelines is not None:
|
|
358
360
|
self.pipelines = pipelines
|
|
359
|
-
if plugin_biz_chat is not None:
|
|
360
|
-
self.plugin_biz_chat = plugin_biz_chat
|
|
361
361
|
if plugin_distributed is not None:
|
|
362
362
|
self.plugin_distributed = plugin_distributed
|
|
363
363
|
if plugin_fiftyone is not None:
|
|
@@ -845,6 +845,27 @@ class V1UserFeatures(object):
|
|
|
845
845
|
|
|
846
846
|
self._deployment_reservations = deployment_reservations
|
|
847
847
|
|
|
848
|
+
@property
|
|
849
|
+
def dgx_cloud(self) -> 'bool':
|
|
850
|
+
"""Gets the dgx_cloud of this V1UserFeatures. # noqa: E501
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
:return: The dgx_cloud of this V1UserFeatures. # noqa: E501
|
|
854
|
+
:rtype: bool
|
|
855
|
+
"""
|
|
856
|
+
return self._dgx_cloud
|
|
857
|
+
|
|
858
|
+
@dgx_cloud.setter
|
|
859
|
+
def dgx_cloud(self, dgx_cloud: 'bool'):
|
|
860
|
+
"""Sets the dgx_cloud of this V1UserFeatures.
|
|
861
|
+
|
|
862
|
+
|
|
863
|
+
:param dgx_cloud: The dgx_cloud of this V1UserFeatures. # noqa: E501
|
|
864
|
+
:type: bool
|
|
865
|
+
"""
|
|
866
|
+
|
|
867
|
+
self._dgx_cloud = dgx_cloud
|
|
868
|
+
|
|
848
869
|
@property
|
|
849
870
|
def docs_agent(self) -> 'bool':
|
|
850
871
|
"""Gets the docs_agent of this V1UserFeatures. # noqa: E501
|
|
@@ -1286,27 +1307,6 @@ class V1UserFeatures(object):
|
|
|
1286
1307
|
|
|
1287
1308
|
self._pipelines = pipelines
|
|
1288
1309
|
|
|
1289
|
-
@property
|
|
1290
|
-
def plugin_biz_chat(self) -> 'bool':
|
|
1291
|
-
"""Gets the plugin_biz_chat of this V1UserFeatures. # noqa: E501
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
:return: The plugin_biz_chat of this V1UserFeatures. # noqa: E501
|
|
1295
|
-
:rtype: bool
|
|
1296
|
-
"""
|
|
1297
|
-
return self._plugin_biz_chat
|
|
1298
|
-
|
|
1299
|
-
@plugin_biz_chat.setter
|
|
1300
|
-
def plugin_biz_chat(self, plugin_biz_chat: 'bool'):
|
|
1301
|
-
"""Sets the plugin_biz_chat of this V1UserFeatures.
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
:param plugin_biz_chat: The plugin_biz_chat of this V1UserFeatures. # noqa: E501
|
|
1305
|
-
:type: bool
|
|
1306
|
-
"""
|
|
1307
|
-
|
|
1308
|
-
self._plugin_biz_chat = plugin_biz_chat
|
|
1309
|
-
|
|
1310
1310
|
@property
|
|
1311
1311
|
def plugin_distributed(self) -> 'bool':
|
|
1312
1312
|
"""Gets the plugin_distributed of this V1UserFeatures. # noqa: E501
|
lightning_sdk/lit_container.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from datetime import datetime
|
|
1
2
|
from typing import Dict, List, Optional
|
|
2
3
|
|
|
3
4
|
from lightning_sdk.api.lit_container_api import LitContainerApi
|
|
@@ -29,11 +30,17 @@ class LitContainer:
|
|
|
29
30
|
repositories = self._api.list_containers(project_id)
|
|
30
31
|
table = []
|
|
31
32
|
for repo in repositories:
|
|
33
|
+
created_date = repo.creation_time
|
|
34
|
+
if isinstance(repo.creation_time, str):
|
|
35
|
+
created_date = datetime.fromisoformat(created_date)
|
|
36
|
+
|
|
37
|
+
created = created_date.strftime("%Y-%m-%d %H:%M:%S")
|
|
38
|
+
|
|
32
39
|
table.append(
|
|
33
40
|
{
|
|
34
41
|
"REPOSITORY": repo.name,
|
|
35
42
|
"IMAGE ID": repo.id,
|
|
36
|
-
"CREATED":
|
|
43
|
+
"CREATED": created,
|
|
37
44
|
}
|
|
38
45
|
)
|
|
39
46
|
return table
|
lightning_sdk/mmt/mmt.py
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
|
-
from functools import lru_cache
|
|
2
1
|
from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Union
|
|
3
2
|
|
|
4
|
-
from lightning_sdk.api.user_api import UserApi
|
|
5
3
|
from lightning_sdk.mmt.base import MMTMachine, _BaseMMT
|
|
6
4
|
from lightning_sdk.mmt.v1 import _MMTV1
|
|
7
5
|
from lightning_sdk.mmt.v2 import _MMTV2
|
|
@@ -18,21 +16,6 @@ if TYPE_CHECKING:
|
|
|
18
16
|
_logger = _setup_logger(__name__)
|
|
19
17
|
|
|
20
18
|
|
|
21
|
-
@lru_cache(maxsize=None)
|
|
22
|
-
def _has_mmt_v2() -> bool:
|
|
23
|
-
api = UserApi()
|
|
24
|
-
try:
|
|
25
|
-
feature_flags = api._get_feature_flags()
|
|
26
|
-
except Exception:
|
|
27
|
-
return False
|
|
28
|
-
|
|
29
|
-
try:
|
|
30
|
-
return feature_flags.mmt_v2
|
|
31
|
-
except AttributeError:
|
|
32
|
-
# Feature flag doesn't exist anymore, so return True
|
|
33
|
-
return True
|
|
34
|
-
|
|
35
|
-
|
|
36
19
|
class MMT(_BaseMMT):
|
|
37
20
|
"""Class to submit and manage multi-machine jobs on the Lightning AI Platform."""
|
|
38
21
|
|
|
@@ -60,7 +43,7 @@ class MMT(_BaseMMT):
|
|
|
60
43
|
"""
|
|
61
44
|
from lightning_sdk.lightning_cloud.openapi.rest import ApiException
|
|
62
45
|
|
|
63
|
-
if
|
|
46
|
+
if not self._force_v1:
|
|
64
47
|
# try with v2 and fall back to v1
|
|
65
48
|
try:
|
|
66
49
|
mmt = _MMTV2(
|
lightning_sdk/mmt/v1.py
CHANGED
|
@@ -92,34 +92,7 @@ class _MMTV1(_BaseMMT):
|
|
|
92
92
|
path_mappings: The mappings from data connection inside your container (not supported)
|
|
93
93
|
|
|
94
94
|
"""
|
|
95
|
-
|
|
96
|
-
raise ValueError("Studio is required for submitting jobs")
|
|
97
|
-
if image is not None or image_credentials is not None or cloud_account_auth or entrypoint != "sh -c":
|
|
98
|
-
raise ValueError("Image is not supported for submitting jobs")
|
|
99
|
-
|
|
100
|
-
if artifacts_local is not None or artifacts_remote is not None:
|
|
101
|
-
raise ValueError("Specifying how to persist artifacts is not yet supported with jobs")
|
|
102
|
-
|
|
103
|
-
if env is not None:
|
|
104
|
-
raise ValueError("Environment variables are not supported for submitting jobs")
|
|
105
|
-
if command is None:
|
|
106
|
-
raise ValueError("Command is required for submitting multi-machine jobs")
|
|
107
|
-
|
|
108
|
-
_submitted = self._job_api.submit_job(
|
|
109
|
-
name=self._name,
|
|
110
|
-
num_machines=num_machines,
|
|
111
|
-
command=command,
|
|
112
|
-
studio_id=studio._studio.id,
|
|
113
|
-
teamspace_id=self._teamspace.id,
|
|
114
|
-
cloud_account=cloud_account or "",
|
|
115
|
-
machine=machine,
|
|
116
|
-
interruptible=interruptible,
|
|
117
|
-
strategy="parallel",
|
|
118
|
-
)
|
|
119
|
-
|
|
120
|
-
self._name = _submitted.name
|
|
121
|
-
self._job = _submitted
|
|
122
|
-
return self
|
|
95
|
+
raise NotImplementedError("Cannot submit new mmts with MMTV1!")
|
|
123
96
|
|
|
124
97
|
def _update_internal_job(self) -> None:
|
|
125
98
|
try:
|
lightning_sdk/models.py
CHANGED
|
@@ -72,6 +72,19 @@ def _get_teamspace(name: str, organization: str) -> "Teamspace":
|
|
|
72
72
|
raise RuntimeError(f"Teamspace `{requested_teamspace}` not found. Available teamspaces: {os.linesep}\t{options}")
|
|
73
73
|
|
|
74
74
|
|
|
75
|
+
def _extend_model_name_with_teamspace(name: str) -> str:
|
|
76
|
+
"""Extend the model name with the teamspace if it can be determined from env. variables."""
|
|
77
|
+
if "/" in name:
|
|
78
|
+
return name
|
|
79
|
+
# do some magic if you run studio
|
|
80
|
+
teamspace = _resolve_teamspace(None, None, None)
|
|
81
|
+
if not teamspace:
|
|
82
|
+
raise ValueError(
|
|
83
|
+
f"Model name must be in the format `organization/teamspace/model_name` but you provided '{name}'."
|
|
84
|
+
)
|
|
85
|
+
return f"{teamspace.owner.name}/{teamspace.name}/{name}"
|
|
86
|
+
|
|
87
|
+
|
|
75
88
|
def _parse_model_name_and_version(name: str) -> Tuple[str, str, str, str]:
|
|
76
89
|
"""Parse the name argument into its components."""
|
|
77
90
|
try:
|
|
@@ -105,9 +118,7 @@ def download_model(
|
|
|
105
118
|
download_dir: The directory where the Model should be downloaded.
|
|
106
119
|
progress_bar: Whether to show a progress bar when downloading.
|
|
107
120
|
"""
|
|
108
|
-
|
|
109
|
-
teamspace = _resolve_teamspace(None, None, None)
|
|
110
|
-
name = f"{teamspace.owner.name}/{teamspace.name}/{name}"
|
|
121
|
+
name = _extend_model_name_with_teamspace(name)
|
|
111
122
|
teamspace_owner_name, teamspace_name, model_name, version = _parse_model_name_and_version(name)
|
|
112
123
|
|
|
113
124
|
download_dir = Path(download_dir)
|
|
@@ -147,9 +158,7 @@ def upload_model(
|
|
|
147
158
|
If not provided, the default cloud account for the Teamspace will be used.
|
|
148
159
|
progress_bar: Whether to show a progress bar for the upload.
|
|
149
160
|
"""
|
|
150
|
-
|
|
151
|
-
teamspace = _resolve_teamspace(None, None, None)
|
|
152
|
-
name = f"{teamspace.owner.name}/{teamspace.name}/{name}"
|
|
161
|
+
name = _extend_model_name_with_teamspace(name)
|
|
153
162
|
org_name, teamspace_name, model_name, _ = _parse_model_name_and_version(name)
|
|
154
163
|
teamspace = _get_teamspace(name=teamspace_name, organization=org_name)
|
|
155
164
|
return teamspace.upload_model(
|
lightning_sdk/serve.py
CHANGED
|
@@ -1,13 +1,38 @@
|
|
|
1
1
|
import os
|
|
2
|
-
import
|
|
2
|
+
import shlex
|
|
3
|
+
import subprocess
|
|
3
4
|
from pathlib import Path
|
|
5
|
+
from typing import Generator, Optional
|
|
6
|
+
from urllib.parse import urlencode
|
|
4
7
|
|
|
5
8
|
import docker
|
|
6
9
|
from rich.console import Console
|
|
7
10
|
from rich.progress import Progress
|
|
8
11
|
|
|
9
|
-
from lightning_sdk import Teamspace
|
|
12
|
+
from lightning_sdk import Deployment, Machine, Teamspace
|
|
13
|
+
from lightning_sdk.api.deployment_api import AutoScaleConfig
|
|
10
14
|
from lightning_sdk.api.lit_container_api import LitContainerApi
|
|
15
|
+
from lightning_sdk.api.utils import _get_cloud_url
|
|
16
|
+
from lightning_sdk.lightning_cloud import env
|
|
17
|
+
from lightning_sdk.lightning_cloud.login import Auth, AuthServer
|
|
18
|
+
|
|
19
|
+
_DOCKER_NOT_RUNNING_MSG = (
|
|
20
|
+
"Deploying LitServe requires Docker to be running on the machine. "
|
|
21
|
+
"If Docker is not installed, please install it from https://docs.docker.com/get-docker/ "
|
|
22
|
+
"and start the Docker daemon before running this command."
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
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)
|
|
11
36
|
|
|
12
37
|
|
|
13
38
|
class _LitServeDeployer:
|
|
@@ -15,36 +40,49 @@ class _LitServeDeployer:
|
|
|
15
40
|
self._console = Console()
|
|
16
41
|
self._client = None
|
|
17
42
|
|
|
43
|
+
@staticmethod
|
|
44
|
+
def authenticate() -> None:
|
|
45
|
+
auth = _Auth()
|
|
46
|
+
auth.authenticate()
|
|
47
|
+
|
|
18
48
|
@property
|
|
19
49
|
def client(self) -> docker.DockerClient:
|
|
20
50
|
if self._client is None:
|
|
21
51
|
try:
|
|
52
|
+
os.environ["DOCKER_BUILDKIT"] = "1"
|
|
22
53
|
self._client = docker.from_env()
|
|
23
54
|
self._client.ping()
|
|
24
|
-
except docker.errors.DockerException
|
|
25
|
-
raise RuntimeError(
|
|
55
|
+
except docker.errors.DockerException:
|
|
56
|
+
raise RuntimeError(_DOCKER_NOT_RUNNING_MSG) from None
|
|
26
57
|
return self._client
|
|
27
58
|
|
|
28
59
|
def dockerize_api(
|
|
29
|
-
self,
|
|
60
|
+
self,
|
|
61
|
+
server_filename: str,
|
|
62
|
+
port: int = 8000,
|
|
63
|
+
gpu: bool = False,
|
|
64
|
+
tag: str = "litserve-model",
|
|
65
|
+
print_success: bool = True,
|
|
30
66
|
) -> str:
|
|
31
67
|
import litserve as ls
|
|
32
68
|
from litserve import docker_builder
|
|
33
69
|
|
|
34
70
|
console = self._console
|
|
35
|
-
if os.path.exists("Dockerfile"):
|
|
36
|
-
console.print("Dockerfile already exists. Skipping generation.")
|
|
37
|
-
return os.path.abspath("Dockerfile")
|
|
38
|
-
|
|
39
71
|
requirements = ""
|
|
40
72
|
if os.path.exists("requirements.txt"):
|
|
41
73
|
requirements = "-r requirements.txt"
|
|
42
74
|
else:
|
|
43
|
-
|
|
75
|
+
console.print(
|
|
44
76
|
f"requirements.txt not found at {os.getcwd()}. "
|
|
45
77
|
f"Make sure to install the required packages in the Dockerfile.",
|
|
46
|
-
|
|
78
|
+
style="yellow",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
if os.path.exists("Dockerfile"):
|
|
82
|
+
console.print(
|
|
83
|
+
"Dockerfile already exists in the current directory, we will use it for building the container."
|
|
47
84
|
)
|
|
85
|
+
return os.path.abspath("Dockerfile")
|
|
48
86
|
current_dir = Path.cwd()
|
|
49
87
|
if not (current_dir / server_filename).is_file():
|
|
50
88
|
raise FileNotFoundError(f"Server file `{server_filename}` must be in the current directory: {os.getcwd()}")
|
|
@@ -65,7 +103,8 @@ class _LitServeDeployer:
|
|
|
65
103
|
with open("Dockerfile", "w") as f:
|
|
66
104
|
f.write(dockerfile_content)
|
|
67
105
|
|
|
68
|
-
|
|
106
|
+
if print_success:
|
|
107
|
+
success_msg = f"""[bold]Dockerfile created successfully[/bold]
|
|
69
108
|
Update [underline]{os.path.abspath("Dockerfile")}[/underline] to add any additional dependencies or commands.
|
|
70
109
|
|
|
71
110
|
[bold]Build the container with:[/bold]
|
|
@@ -77,11 +116,14 @@ Update [underline]{os.path.abspath("Dockerfile")}[/underline] to add any additio
|
|
|
77
116
|
[bold]To push the container to a registry:[/bold]
|
|
78
117
|
> [underline]docker push {tag}[/underline]
|
|
79
118
|
"""
|
|
80
|
-
|
|
119
|
+
console.print(success_msg)
|
|
81
120
|
return os.path.abspath("Dockerfile")
|
|
82
121
|
|
|
83
|
-
|
|
84
|
-
|
|
122
|
+
@staticmethod
|
|
123
|
+
def generate_client() -> None:
|
|
124
|
+
from rich.console import Console
|
|
125
|
+
|
|
126
|
+
console = Console()
|
|
85
127
|
try:
|
|
86
128
|
from litserve.python_client import client_template
|
|
87
129
|
except ImportError:
|
|
@@ -99,36 +141,114 @@ Update [underline]{os.path.abspath("Dockerfile")}[/underline] to add any additio
|
|
|
99
141
|
except OSError as e:
|
|
100
142
|
raise OSError(f"Failed to generate client.py: {e!s}") from None
|
|
101
143
|
|
|
102
|
-
def
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
144
|
+
def _docker_build_with_logs(
|
|
145
|
+
self, path: str, repository: str, tag: str, platform: str = "linux/amd64"
|
|
146
|
+
) -> Generator[str, None, None]:
|
|
147
|
+
"""Build Docker image using CLI with real-time log streaming.
|
|
148
|
+
|
|
149
|
+
Returns:
|
|
150
|
+
Tuple: (image_id, logs generator)
|
|
151
|
+
|
|
152
|
+
Raises:
|
|
153
|
+
RuntimeError: On build failure
|
|
154
|
+
"""
|
|
155
|
+
cmd = f"docker build --platform {platform} -t {repository}:{tag} ."
|
|
156
|
+
proc = subprocess.Popen(
|
|
157
|
+
shlex.split(cmd),
|
|
158
|
+
stdout=subprocess.PIPE,
|
|
159
|
+
stderr=subprocess.STDOUT,
|
|
160
|
+
text=True,
|
|
161
|
+
bufsize=1, # Line buffered
|
|
106
162
|
)
|
|
107
|
-
|
|
163
|
+
|
|
164
|
+
def log_generator() -> Generator[str, None, None]:
|
|
165
|
+
while True:
|
|
166
|
+
line = proc.stdout.readline()
|
|
167
|
+
if not line and proc.poll() is not None:
|
|
168
|
+
break
|
|
169
|
+
yield line.strip()
|
|
170
|
+
if "error" in line.lower():
|
|
171
|
+
proc.terminate()
|
|
172
|
+
raise RuntimeError(f"Build failed: {line.strip()}")
|
|
173
|
+
|
|
174
|
+
if proc.returncode != 0:
|
|
175
|
+
raise RuntimeError(f"Build failed with exit code {proc.returncode}")
|
|
176
|
+
|
|
177
|
+
return log_generator()
|
|
178
|
+
|
|
179
|
+
def build_container(self, path: str, repository: str, tag: str, console: Console, progress: Progress) -> None:
|
|
180
|
+
build_task = progress.add_task("Building Docker image", total=None)
|
|
181
|
+
build_logs = self._docker_build_with_logs(path, repository, tag=tag)
|
|
182
|
+
|
|
183
|
+
for line in build_logs:
|
|
108
184
|
if "error" in line:
|
|
109
185
|
progress.stop()
|
|
110
186
|
console.print(f"\n[red]{line}[/red]")
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
console.print(
|
|
187
|
+
raise RuntimeError(f"Failed to build image: {line}")
|
|
188
|
+
else:
|
|
189
|
+
console.print(
|
|
190
|
+
line.strip(),
|
|
191
|
+
)
|
|
114
192
|
progress.update(build_task, description="Building Docker image")
|
|
115
193
|
|
|
116
194
|
progress.update(build_task, description="[green]Build completed![/green]")
|
|
117
195
|
|
|
118
|
-
def
|
|
196
|
+
def push_container(
|
|
119
197
|
self, repository: str, tag: str, teamspace: Teamspace, lit_cr: LitContainerApi, progress: Progress
|
|
120
|
-
) ->
|
|
198
|
+
) -> dict:
|
|
121
199
|
console = self._console
|
|
122
200
|
push_task = progress.add_task("Pushing to registry", total=None)
|
|
123
201
|
console.print("\nPushing image...", style="bold blue")
|
|
124
202
|
lit_cr.authenticate()
|
|
125
203
|
push_status = lit_cr.upload_container(repository, teamspace, tag=tag)
|
|
204
|
+
last_status = {}
|
|
126
205
|
for line in push_status:
|
|
206
|
+
last_status = line
|
|
127
207
|
if "error" in line:
|
|
128
208
|
progress.stop()
|
|
129
209
|
console.print(f"\n[red]{line}[/red]")
|
|
130
|
-
|
|
210
|
+
raise RuntimeError(f"Failed to push image: {line}")
|
|
131
211
|
if "status" in line:
|
|
132
|
-
console.print(line["status"]
|
|
212
|
+
console.print(line["status"].strip())
|
|
133
213
|
progress.update(push_task, description="Pushing to registry")
|
|
134
214
|
progress.update(push_task, description="[green]Push completed![/green]")
|
|
215
|
+
return last_status
|
|
216
|
+
|
|
217
|
+
def _run_on_cloud(
|
|
218
|
+
self,
|
|
219
|
+
deployment_name: str,
|
|
220
|
+
teamspace: Teamspace,
|
|
221
|
+
image: str,
|
|
222
|
+
metric: Optional[str] = None,
|
|
223
|
+
machine: Optional[Machine] = None,
|
|
224
|
+
min_replica: Optional[int] = 0,
|
|
225
|
+
max_replica: Optional[int] = 1,
|
|
226
|
+
spot: Optional[bool] = None,
|
|
227
|
+
replicas: Optional[int] = 1,
|
|
228
|
+
cloud_account: Optional[str] = None,
|
|
229
|
+
port: Optional[int] = 8000,
|
|
230
|
+
include_credentials: Optional[bool] = True,
|
|
231
|
+
) -> dict:
|
|
232
|
+
url = f"{_get_cloud_url()}/{teamspace.owner.name}/{teamspace.name}/jobs/{deployment_name}"
|
|
233
|
+
machine = machine or Machine.CPU
|
|
234
|
+
metric = metric or ("CPU" if machine.is_cpu() else "GPU")
|
|
235
|
+
deployment = Deployment(deployment_name, teamspace)
|
|
236
|
+
if deployment.is_started:
|
|
237
|
+
raise RuntimeError(
|
|
238
|
+
f"Deployment with name {deployment_name} already running. "
|
|
239
|
+
"Please stop the deployment before starting a new one.\n"
|
|
240
|
+
f"You can access the deployment at {url}"
|
|
241
|
+
)
|
|
242
|
+
autoscale = AutoScaleConfig(min_replicas=min_replica, max_replicas=max_replica, metric=metric, threshold=0.95)
|
|
243
|
+
deployment.start(
|
|
244
|
+
machine=machine,
|
|
245
|
+
image=image,
|
|
246
|
+
autoscale=autoscale,
|
|
247
|
+
spot=spot,
|
|
248
|
+
replicas=replicas,
|
|
249
|
+
cloud_account=cloud_account,
|
|
250
|
+
ports=[port],
|
|
251
|
+
include_credentials=include_credentials,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
return {"deployment": deployment, "url": url}
|
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
docs/source/conf.py,sha256=r8yX20eC-4mHhMTd0SbQb5TlSWHhO6wnJ0VJ_FBFpag,13249
|
|
2
|
-
lightning_sdk/__init__.py,sha256=
|
|
2
|
+
lightning_sdk/__init__.py,sha256=0M0hCXuVUl2Lxnfphl1qbZNnMBde0xhorpbackzGcHk,1104
|
|
3
3
|
lightning_sdk/agents.py,sha256=ly6Ma1j0ZgGPFyvPvMN28JWiB9dATIstFa5XM8pMi6I,1577
|
|
4
|
-
lightning_sdk/ai_hub.py,sha256=
|
|
4
|
+
lightning_sdk/ai_hub.py,sha256=Zsc8GhFuRQIAYk3J3znUYDfyVhe3sd0WYLk2bFcoxVI,6803
|
|
5
5
|
lightning_sdk/constants.py,sha256=ztl1PTUBULnqTf3DyKUSJaV_O20hNtUYT6XvAYIrmIk,749
|
|
6
6
|
lightning_sdk/helpers.py,sha256=KWMWnORHItIIA3PGn71YPs-7RjzGi8IXa2kQ5Qo4U8M,2459
|
|
7
|
-
lightning_sdk/lit_container.py,sha256=
|
|
7
|
+
lightning_sdk/lit_container.py,sha256=2A_7Hc_tX2ilj6UZmdLm-us144_KIcnWB0qT96sJGQM,4000
|
|
8
8
|
lightning_sdk/machine.py,sha256=EMr-ulyYEYhIkKFnGBOofnFf4asndTSeoOQxlyd3xW4,3632
|
|
9
|
-
lightning_sdk/models.py,sha256=
|
|
9
|
+
lightning_sdk/models.py,sha256=0aJ4OXNY0MYUCfsYI6i18fV61DU-HGKIKEHFD99r1zk,6231
|
|
10
10
|
lightning_sdk/organization.py,sha256=WCfzdgjtvY1_A07DnxOpp74V2JR2gQwtXbIEcFDnoVU,1232
|
|
11
11
|
lightning_sdk/owner.py,sha256=t5svD2it4C9pbSpVuG9WJL46CYi37JXNziwnXxhiU5U,1361
|
|
12
12
|
lightning_sdk/plugin.py,sha256=TGxCa-xsOpv_kaKiVnNBNG3jLe9MweSS5Cnz9PcxQfY,14860
|
|
13
|
-
lightning_sdk/serve.py,sha256=
|
|
13
|
+
lightning_sdk/serve.py,sha256=3nE_0TkTOtiCpdieQ01Pkyi-rb1rIkBJiF_g_fGIos8,9456
|
|
14
14
|
lightning_sdk/status.py,sha256=lLGAuSvXBoXQFEEsEYwdCi0RcSNatUn5OPjJVjDtoM0,386
|
|
15
15
|
lightning_sdk/studio.py,sha256=hyvAiVhkETAtbu0RRF1Aw6F8Y__E1SSAmsB8PHfmqHo,19935
|
|
16
16
|
lightning_sdk/teamspace.py,sha256=CzpQJ6Uj_at0W-HIzYd-Y3gN9qBotUbAV3YBWjNT9U8,14364
|
|
17
17
|
lightning_sdk/user.py,sha256=vdn8pZqkAZO0-LoRsBdg0TckRKtd_H3QF4gpiZcl4iY,1130
|
|
18
18
|
lightning_sdk/api/__init__.py,sha256=Qn2VVRvir_gO7w4yxGLkZY-R3T7kdiTPKgQ57BhIA9k,413
|
|
19
19
|
lightning_sdk/api/agents_api.py,sha256=G47TbFo9kYqnBMqdw2RW-lfS1VAUBSXDmzs6fpIEMUs,4059
|
|
20
|
-
lightning_sdk/api/ai_hub_api.py,sha256=
|
|
21
|
-
lightning_sdk/api/deployment_api.py,sha256=
|
|
20
|
+
lightning_sdk/api/ai_hub_api.py,sha256=IaA57Cqu_zdj5zRwKkx9LJQIGFFo2mHmPOYekKxbZ9A,6999
|
|
21
|
+
lightning_sdk/api/deployment_api.py,sha256=z_D7ZZAnsVe3Q_ZVx1azcLK2_pP5xj63tmU97qhgyOw,22583
|
|
22
22
|
lightning_sdk/api/job_api.py,sha256=_mMAI_BG_48i-BLwCP_U72zgmM5zYa2KUZ7u66HWkIc,13568
|
|
23
|
-
lightning_sdk/api/lit_container_api.py,sha256=
|
|
23
|
+
lightning_sdk/api/lit_container_api.py,sha256=m-3qZIIpZ24Z2Z8z9x8Di6cOSjkZmBFcf9R2yVMpA_4,7648
|
|
24
24
|
lightning_sdk/api/mmt_api.py,sha256=-v7ATab-ThAM-HRClS92Ehxuu9MlBfdKWWFCGvVUHiM,8962
|
|
25
25
|
lightning_sdk/api/org_api.py,sha256=Ze3z_ATVrukobujV5YdC42DKj45Vuwl7X52q_Vr-o3U,803
|
|
26
26
|
lightning_sdk/api/pipeline_api.py,sha256=P5P9C6qOpyBGU0t5N68h1LuFAsAKmPPgkac6uObrYKw,1676
|
|
@@ -47,7 +47,7 @@ lightning_sdk/cli/list.py,sha256=dAZ94QPvE4IkH6GfL7171TMrMfcuWB53cvW6wk2eL4w,101
|
|
|
47
47
|
lightning_sdk/cli/mmts_menu.py,sha256=HUXo3ZoZ3fWOCNWTQWoJgUlFXYq5uVm_6uFjAq7BDe8,2219
|
|
48
48
|
lightning_sdk/cli/open.py,sha256=sgMLWBnkXdIq8H9XK_rph2bye3b07AKTJBQIk9fCGVc,1937
|
|
49
49
|
lightning_sdk/cli/run.py,sha256=8JZiDrKwDhlaTOJd6qq2mCWJRqKm6shCWLzpbmFYIkE,13929
|
|
50
|
-
lightning_sdk/cli/serve.py,sha256=
|
|
50
|
+
lightning_sdk/cli/serve.py,sha256=IHQneUUWhyYQHP_ChQafm7vyfb5-9crIH_CmjKUy7TM,9445
|
|
51
51
|
lightning_sdk/cli/start.py,sha256=jUk52lkEFC_fqiEPkwM8GwE68WMNEtzBuzjkvr3POd0,1840
|
|
52
52
|
lightning_sdk/cli/stop.py,sha256=5nCrUe1BONpX1nKNhbSFqLaXXKaRhSO7PvM1BVYLgn4,2864
|
|
53
53
|
lightning_sdk/cli/studios_menu.py,sha256=TA9rO6_fFHGMz0Nt4rJ6iV80X5pZE4xShrSiyXoU-oQ,4129
|
|
@@ -55,7 +55,7 @@ lightning_sdk/cli/switch.py,sha256=qLvDoQRldCNgO1XvIGg-i-GyqnkHVb_U1-X4svqeNNU,1
|
|
|
55
55
|
lightning_sdk/cli/teamspace_menu.py,sha256=C3g3spTKgtMwoK7pnooy0MBPz4AKhFjcObkvZyZ4v04,3797
|
|
56
56
|
lightning_sdk/cli/upload.py,sha256=nZ7hVKccis4Fnx2ae3Y2utUWTlKH_9N-xjRFG9Xh3q4,12486
|
|
57
57
|
lightning_sdk/deployment/__init__.py,sha256=dXsa4psDzFYFklsq3JC-2V_L4FQjGZnQAf-ZiVlqG9c,545
|
|
58
|
-
lightning_sdk/deployment/deployment.py,sha256=
|
|
58
|
+
lightning_sdk/deployment/deployment.py,sha256=EbFoDLdj1UubXyOTSp1VTlpDTgwdczfyusdLStqD7WA,17218
|
|
59
59
|
lightning_sdk/job/__init__.py,sha256=1MxjQ6rHkyUHCypSW9RuXuVMVH11WiqhIXcU2LCFMwE,64
|
|
60
60
|
lightning_sdk/job/base.py,sha256=TS5KavtfBAFbLbNqqumEizY9edjO1joSmtUdcO5CThQ,17748
|
|
61
61
|
lightning_sdk/job/job.py,sha256=1Xf0ne4wwXpkb_GJgWD8mfueJZoKR8G4lC5T6OXijlw,13075
|
|
@@ -207,7 +207,7 @@ lightning_sdk/lightning_cloud/openapi/models/profiler_captures_body.py,sha256=xT
|
|
|
207
207
|
lightning_sdk/lightning_cloud/openapi/models/profiler_enabled_body.py,sha256=4YnP4KmUlXM5Brm5Sj5WnF82MTPr48FqH0C4R5YZRVY,4703
|
|
208
208
|
lightning_sdk/lightning_cloud/openapi/models/project_id_agentmanagedendpoints_body.py,sha256=pK1HjgWsXhU7OOKwYdt-SwklzF39N3Ap7Lyrk75T43k,4673
|
|
209
209
|
lightning_sdk/lightning_cloud/openapi/models/project_id_agents_body.py,sha256=oE3OGIhX0FfiKqE96YJmHnrRRAXgqrFsIAw840ib64Q,16193
|
|
210
|
-
lightning_sdk/lightning_cloud/openapi/models/project_id_cloudspaces_body.py,sha256=
|
|
210
|
+
lightning_sdk/lightning_cloud/openapi/models/project_id_cloudspaces_body.py,sha256=kroaORLZVLUQIXO-jQrlZvmVXXJF9t6CFM09jTFRBeQ,16188
|
|
211
211
|
lightning_sdk/lightning_cloud/openapi/models/project_id_clusters_body.py,sha256=ewyBgf8oHlhgXKmn4YdjuyG84iONOeNXiWtbRQJfj4U,4348
|
|
212
212
|
lightning_sdk/lightning_cloud/openapi/models/project_id_datasets_body.py,sha256=NkuH_enmOZ0MnYJ_hH2iVP1-Y1a9XinHR_c-5DaJxqU,15094
|
|
213
213
|
lightning_sdk/lightning_cloud/openapi/models/project_id_endpoints_body.py,sha256=bnCgn3Jz3KE7blDn7chONuxJuc7Wg1eYM1QG0hxGR9U,7507
|
|
@@ -876,7 +876,7 @@ lightning_sdk/lightning_cloud/openapi/models/v1_upstream_open_ai.py,sha256=jt1qQ
|
|
|
876
876
|
lightning_sdk/lightning_cloud/openapi/models/v1_usage.py,sha256=RhhnH9ygScZyExg06WhvMNPPRLSe8FYkIftqF-D9NIU,13408
|
|
877
877
|
lightning_sdk/lightning_cloud/openapi/models/v1_usage_details.py,sha256=U7qC698Xj5tb3D93ZskG6sDf3lTXE13UTlGeDTvtRU4,14062
|
|
878
878
|
lightning_sdk/lightning_cloud/openapi/models/v1_usage_report.py,sha256=iH67BcONBSLYzcZpGpKWSOzJTCpuqYt7FU4OUs8BJ9k,6076
|
|
879
|
-
lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py,sha256=
|
|
879
|
+
lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py,sha256=_I8vCY0BOl6yv-f_ZaqOmBhogr1SPhAz3JZZ7Veoq94,67885
|
|
880
880
|
lightning_sdk/lightning_cloud/openapi/models/v1_user_requested_compute_config.py,sha256=3jeJfpbBpYY2B2Ao2j2N93CMO2CnvmPqndE4Lw3ZfMA,13056
|
|
881
881
|
lightning_sdk/lightning_cloud/openapi/models/v1_user_requested_flow_compute_config.py,sha256=3WpZ-lf7xPwuYyQDMdP7Uc6-dh3vf5TaaUlcMfesfMk,5208
|
|
882
882
|
lightning_sdk/lightning_cloud/openapi/models/v1_user_slurm_job_action_response.py,sha256=BdNzXH8Vsf5PHjl9Rd-TVkpAgx1YC9rf8LD0js-ba20,3058
|
|
@@ -913,11 +913,11 @@ lightning_sdk/lightning_cloud/utils/name_generator.py,sha256=MkciuA10332V0mcE2Px
|
|
|
913
913
|
lightning_sdk/lightning_cloud/utils/network.py,sha256=axPgl8rhyPcPjxiztDxyksfxax3VNg2OXL5F5Uc81b4,406
|
|
914
914
|
lightning_sdk/mmt/__init__.py,sha256=ExMu90-96bGBnyp5h0CErQszUGB1-PcjC4-R8_NYbeY,117
|
|
915
915
|
lightning_sdk/mmt/base.py,sha256=B3HC-c82bPHprEZh1mhLCPCrCE8BOKqwIhY7xCF9CXg,15152
|
|
916
|
-
lightning_sdk/mmt/mmt.py,sha256=
|
|
917
|
-
lightning_sdk/mmt/v1.py,sha256=
|
|
916
|
+
lightning_sdk/mmt/mmt.py,sha256=swdGP1DOM42a_QmmY1vg3--6ZBDiC4zToAzU9Eycv4U,13446
|
|
917
|
+
lightning_sdk/mmt/v1.py,sha256=TxLtL0ssDoP8eyleDaFyYr4evkOKbLJcckLVIfalOno,8429
|
|
918
918
|
lightning_sdk/mmt/v2.py,sha256=Em1XBoqViqUMKm-sshzdMcSH5UTtZZwbJcsgqY6-mw0,9625
|
|
919
919
|
lightning_sdk/pipeline/__init__.py,sha256=uZS2v8HPGfNz2SrCCVrZ9EhihS3kd6aGtwJLbuaI3dA,167
|
|
920
|
-
lightning_sdk/pipeline/pipeline.py,sha256=
|
|
920
|
+
lightning_sdk/pipeline/pipeline.py,sha256=CCRcHUjL24yKm24NMB5xzK1sre6bQ5sY07tMifmpqI0,3762
|
|
921
921
|
lightning_sdk/pipeline/types.py,sha256=dZYNrWpuytmyjBHh8OUYT8BRwMe1_5PDDSl0AWsXkyA,9662
|
|
922
922
|
lightning_sdk/pipeline/utils.py,sha256=gccFmSx3VrkDpWtq26eRCnvWT8l4hKdYLntDMYlB5jw,2627
|
|
923
923
|
lightning_sdk/services/__init__.py,sha256=gSWUjccEhMI9CIWL_nbrFHUK2S6TM2725mEzrLMfK1Y,225
|
|
@@ -928,9 +928,9 @@ lightning_sdk/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSu
|
|
|
928
928
|
lightning_sdk/utils/dynamic.py,sha256=glUTO1JC9APtQ6Gr9SO02a3zr56-sPAXM5C3NrTpgyQ,1959
|
|
929
929
|
lightning_sdk/utils/enum.py,sha256=h2JRzqoBcSlUdanFHmkj_j5DleBHAu1esQYUsdNI-hU,4106
|
|
930
930
|
lightning_sdk/utils/resolve.py,sha256=MALzFO5iVlkZpnEiC9QkyWcTTYksCeHsloG6MCSJl48,6461
|
|
931
|
-
lightning_sdk-0.2.
|
|
932
|
-
lightning_sdk-0.2.
|
|
933
|
-
lightning_sdk-0.2.
|
|
934
|
-
lightning_sdk-0.2.
|
|
935
|
-
lightning_sdk-0.2.
|
|
936
|
-
lightning_sdk-0.2.
|
|
931
|
+
lightning_sdk-0.2.4.dist-info/LICENSE,sha256=uFIuZwj5z-4TeF2UuacPZ1o17HkvKObT8fY50qN84sg,1064
|
|
932
|
+
lightning_sdk-0.2.4.dist-info/METADATA,sha256=H8Yv9rh5KKFyNlVa6zU8k8Frb6C4ZeWoaauSIO2DUnk,3991
|
|
933
|
+
lightning_sdk-0.2.4.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
|
|
934
|
+
lightning_sdk-0.2.4.dist-info/entry_points.txt,sha256=msB9PJWIJ784dX-OP8by51d4IbKYH3Fj1vCuA9oXjHY,68
|
|
935
|
+
lightning_sdk-0.2.4.dist-info/top_level.txt,sha256=ps8doKILFXmN7F1mHncShmnQoTxKBRPIcchC8TpoBw4,19
|
|
936
|
+
lightning_sdk-0.2.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|