lightning-sdk 0.1.54__py3-none-any.whl → 0.1.56__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 +22 -0
- lightning_sdk/api/ai_hub_api.py +21 -2
- lightning_sdk/api/deployment_api.py +4 -3
- lightning_sdk/api/job_api.py +5 -10
- lightning_sdk/api/lit_container_api.py +27 -7
- lightning_sdk/api/mmt_api.py +1 -4
- lightning_sdk/api/studio_api.py +5 -7
- lightning_sdk/api/teamspace_api.py +7 -0
- lightning_sdk/api/utils.py +1 -27
- lightning_sdk/cli/configure.py +92 -0
- lightning_sdk/cli/connect.py +31 -0
- lightning_sdk/cli/delete.py +32 -3
- lightning_sdk/cli/download.py +1 -1
- lightning_sdk/cli/entrypoint.py +14 -1
- lightning_sdk/cli/generate.py +35 -0
- lightning_sdk/cli/inspect.py +4 -2
- lightning_sdk/cli/jobs_menu.py +2 -1
- lightning_sdk/cli/list.py +49 -6
- lightning_sdk/cli/mmts_menu.py +2 -1
- lightning_sdk/cli/run.py +3 -3
- lightning_sdk/cli/serve.py +1 -2
- lightning_sdk/cli/start.py +43 -0
- lightning_sdk/cli/stop.py +30 -2
- lightning_sdk/cli/studios_menu.py +24 -1
- lightning_sdk/cli/switch.py +43 -0
- lightning_sdk/cli/teamspace_menu.py +2 -1
- lightning_sdk/cli/upload.py +6 -4
- lightning_sdk/lightning_cloud/openapi/__init__.py +6 -0
- lightning_sdk/lightning_cloud/openapi/api/cluster_service_api.py +115 -2
- lightning_sdk/lightning_cloud/openapi/api/jobs_service_api.py +113 -0
- lightning_sdk/lightning_cloud/openapi/api/lit_registry_service_api.py +216 -2
- lightning_sdk/lightning_cloud/openapi/api/projects_service_api.py +1 -5
- lightning_sdk/lightning_cloud/openapi/models/__init__.py +6 -0
- lightning_sdk/lightning_cloud/openapi/models/cluster_id_usagerestrictions_body.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/id_reportrestarttimings_body.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/project_id_litregistry_body.py +2 -0
- lightning_sdk/lightning_cloud/openapi/models/usagerestrictions_id_body.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_provider.py +3 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_accelerator.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_get_cluster_accelerator_demand_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_job.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_job_spec.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_lambda_labs_direct_v1.py +55 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_list_lit_registry_repository_image_artifact_versions_response.py +231 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_lit_registry_artifact.py +279 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_lit_registry_project.py +8 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_lit_repository.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_report_restart_timings_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_restart_timing.py +175 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +27 -53
- lightning_sdk/lightning_cloud/openapi/models/v1_validate_deployment_image_request.py +27 -1
- lightning_sdk/machine.py +59 -27
- lightning_sdk/studio.py +5 -1
- lightning_sdk/teamspace.py +25 -0
- {lightning_sdk-0.1.54.dist-info → lightning_sdk-0.1.56.dist-info}/METADATA +2 -1
- {lightning_sdk-0.1.54.dist-info → lightning_sdk-0.1.56.dist-info}/RECORD +61 -50
- {lightning_sdk-0.1.54.dist-info → lightning_sdk-0.1.56.dist-info}/LICENSE +0 -0
- {lightning_sdk-0.1.54.dist-info → lightning_sdk-0.1.56.dist-info}/WHEEL +0 -0
- {lightning_sdk-0.1.54.dist-info → lightning_sdk-0.1.56.dist-info}/entry_points.txt +0 -0
- {lightning_sdk-0.1.54.dist-info → lightning_sdk-0.1.56.dist-info}/top_level.txt +0 -0
lightning_sdk/cli/inspect.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
from typing import Optional
|
|
2
2
|
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
|
|
3
5
|
from lightning_sdk.cli.job_and_mmt_action import _JobAndMMTAction
|
|
4
6
|
|
|
5
7
|
|
|
@@ -16,7 +18,7 @@ class _Inspect(_JobAndMMTAction):
|
|
|
16
18
|
If not specified can be selected interactively.
|
|
17
19
|
|
|
18
20
|
"""
|
|
19
|
-
|
|
21
|
+
(super().job(name=name, teamspace=teamspace).json())
|
|
20
22
|
|
|
21
23
|
def mmt(self, name: Optional[str] = None, teamspace: Optional[str] = None) -> None:
|
|
22
24
|
"""Inspect a multi-machine job for further details as JSON.
|
|
@@ -28,4 +30,4 @@ class _Inspect(_JobAndMMTAction):
|
|
|
28
30
|
If not specified can be selected interactively.
|
|
29
31
|
|
|
30
32
|
"""
|
|
31
|
-
print(super().mmt(name=name, teamspace=teamspace).json())
|
|
33
|
+
Console().print(super().mmt(name=name, teamspace=teamspace).json())
|
lightning_sdk/cli/jobs_menu.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from typing import Dict, List, Optional
|
|
2
2
|
|
|
3
|
+
from rich.console import Console
|
|
3
4
|
from simple_term_menu import TerminalMenu
|
|
4
5
|
|
|
5
6
|
from lightning_sdk.cli.exceptions import StudioCliError
|
|
@@ -20,7 +21,7 @@ class _JobsMenu:
|
|
|
20
21
|
if j.name == job:
|
|
21
22
|
return j
|
|
22
23
|
|
|
23
|
-
print("Could not find Job {job}, please select it from the list:")
|
|
24
|
+
Console().print("Could not find Job {job}, please select it from the list:")
|
|
24
25
|
return self._get_job_from_interactive_menu(possible_jobs)
|
|
25
26
|
|
|
26
27
|
@staticmethod
|
lightning_sdk/cli/list.py
CHANGED
|
@@ -3,6 +3,7 @@ from typing import Optional
|
|
|
3
3
|
from rich.console import Console
|
|
4
4
|
from rich.table import Table
|
|
5
5
|
|
|
6
|
+
from lightning_sdk import Machine
|
|
6
7
|
from lightning_sdk.cli.teamspace_menu import _TeamspacesMenu
|
|
7
8
|
from lightning_sdk.lit_container import LitContainer
|
|
8
9
|
|
|
@@ -10,6 +11,37 @@ from lightning_sdk.lit_container import LitContainer
|
|
|
10
11
|
class _List(_TeamspacesMenu):
|
|
11
12
|
"""List resources on the Lightning AI platform."""
|
|
12
13
|
|
|
14
|
+
def studios(self, teamspace: Optional[str] = None) -> None:
|
|
15
|
+
"""List studios for a given teamspace.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
teamspace: the teamspace to list studios from. Should be specified as {owner}/{name}
|
|
19
|
+
If not provided, can be selected in an interactive menu.
|
|
20
|
+
|
|
21
|
+
"""
|
|
22
|
+
resolved_teamspace = self._resolve_teamspace(teamspace=teamspace)
|
|
23
|
+
|
|
24
|
+
studios = resolved_teamspace.studios
|
|
25
|
+
|
|
26
|
+
table = Table(
|
|
27
|
+
pad_edge=True,
|
|
28
|
+
)
|
|
29
|
+
table.add_column("Name")
|
|
30
|
+
table.add_column("Teamspace")
|
|
31
|
+
table.add_column("Status")
|
|
32
|
+
table.add_column("Machine")
|
|
33
|
+
table.add_column("Cloud account")
|
|
34
|
+
for studio in studios:
|
|
35
|
+
table.add_row(
|
|
36
|
+
studio.name,
|
|
37
|
+
f"{studio.teamspace.owner.name}/{studio.teamspace.name}",
|
|
38
|
+
str(studio.status),
|
|
39
|
+
str(studio.machine) if studio.machine is not None else None,
|
|
40
|
+
str(studio.cloud_account),
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
Console().print(table)
|
|
44
|
+
|
|
13
45
|
def jobs(self, teamspace: Optional[str] = None) -> None:
|
|
14
46
|
"""List jobs for a given teamspace.
|
|
15
47
|
|
|
@@ -48,8 +80,7 @@ class _List(_TeamspacesMenu):
|
|
|
48
80
|
f"{j.total_cost:.3f}",
|
|
49
81
|
)
|
|
50
82
|
|
|
51
|
-
|
|
52
|
-
console.print(table)
|
|
83
|
+
Console().print(table)
|
|
53
84
|
|
|
54
85
|
def mmts(self, teamspace: Optional[str] = None) -> None:
|
|
55
86
|
"""List multi-machine jobs for a given teamspace.
|
|
@@ -89,8 +120,7 @@ class _List(_TeamspacesMenu):
|
|
|
89
120
|
str(j.total_cost),
|
|
90
121
|
)
|
|
91
122
|
|
|
92
|
-
|
|
93
|
-
console.print(table)
|
|
123
|
+
Console().print(table)
|
|
94
124
|
|
|
95
125
|
def containers(self, teamspace: Optional[str] = None) -> None:
|
|
96
126
|
"""Display the list of available containers.
|
|
@@ -108,5 +138,18 @@ class _List(_TeamspacesMenu):
|
|
|
108
138
|
table.add_column("CREATED")
|
|
109
139
|
for repo in result:
|
|
110
140
|
table.add_row(repo["REPOSITORY"], repo["IMAGE ID"], repo["CREATED"])
|
|
111
|
-
|
|
112
|
-
|
|
141
|
+
Console().print(table)
|
|
142
|
+
|
|
143
|
+
def machines(self) -> None:
|
|
144
|
+
"""Display the list of available machines."""
|
|
145
|
+
table = Table(pad_edge=True)
|
|
146
|
+
table.add_column("Name")
|
|
147
|
+
|
|
148
|
+
# Get all machine types from the enum
|
|
149
|
+
machine_types = [name for name in dir(Machine) if not name.startswith("_")]
|
|
150
|
+
|
|
151
|
+
# Add rows to table
|
|
152
|
+
for name in sorted(machine_types):
|
|
153
|
+
table.add_row(name)
|
|
154
|
+
|
|
155
|
+
Console().print(table)
|
lightning_sdk/cli/mmts_menu.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from typing import Dict, List, Optional
|
|
2
2
|
|
|
3
|
+
from rich.console import Console
|
|
3
4
|
from simple_term_menu import TerminalMenu
|
|
4
5
|
|
|
5
6
|
from lightning_sdk.cli.exceptions import StudioCliError
|
|
@@ -20,7 +21,7 @@ class _MMTsMenu:
|
|
|
20
21
|
if j.name == mmt:
|
|
21
22
|
return j
|
|
22
23
|
|
|
23
|
-
print("Could not find Multi-Machine Job {mmt}, please select it from the list:")
|
|
24
|
+
Console().print("Could not find Multi-Machine Job {mmt}, please select it from the list:")
|
|
24
25
|
return self._get_mmt_from_interactive_menu(possible_mmts)
|
|
25
26
|
|
|
26
27
|
@staticmethod
|
lightning_sdk/cli/run.py
CHANGED
|
@@ -8,7 +8,7 @@ from lightning_sdk.teamspace import Teamspace
|
|
|
8
8
|
if TYPE_CHECKING:
|
|
9
9
|
from lightning_sdk.cli.legacy import _LegacyLightningCLI
|
|
10
10
|
|
|
11
|
-
_MACHINE_VALUES = tuple([machine.
|
|
11
|
+
_MACHINE_VALUES = tuple([machine.name for machine in Machine.__dict__.values() if isinstance(machine, Machine)])
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class _Run:
|
|
@@ -156,7 +156,7 @@ class _Run:
|
|
|
156
156
|
machine = "CPU"
|
|
157
157
|
machine_enum: Union[str, Machine]
|
|
158
158
|
try:
|
|
159
|
-
machine_enum = Machine
|
|
159
|
+
machine_enum = getattr(Machine, machine.upper(), Machine(machine, machine))
|
|
160
160
|
except KeyError:
|
|
161
161
|
machine_enum = machine
|
|
162
162
|
|
|
@@ -222,7 +222,7 @@ class _Run:
|
|
|
222
222
|
machine = "CPU"
|
|
223
223
|
machine_enum: Union[str, Machine]
|
|
224
224
|
try:
|
|
225
|
-
machine_enum = Machine
|
|
225
|
+
machine_enum = getattr(Machine, machine.upper(), Machine(machine, machine))
|
|
226
226
|
except KeyError:
|
|
227
227
|
machine_enum = machine
|
|
228
228
|
|
lightning_sdk/cli/serve.py
CHANGED
|
@@ -167,7 +167,6 @@ class _Docker:
|
|
|
167
167
|
import litserve as ls
|
|
168
168
|
from litserve import docker_builder
|
|
169
169
|
|
|
170
|
-
console = Console()
|
|
171
170
|
requirements = ""
|
|
172
171
|
if os.path.exists("requirements.txt"):
|
|
173
172
|
requirements = "-r requirements.txt"
|
|
@@ -210,5 +209,5 @@ Update [underline]{os.path.abspath("Dockerfile")}[/underline] to add any additio
|
|
|
210
209
|
[bold]To push the container to a registry:[/bold]
|
|
211
210
|
> [underline]docker push {tag}[/underline]
|
|
212
211
|
"""
|
|
213
|
-
|
|
212
|
+
Console().print(success_msg)
|
|
214
213
|
return os.path.abspath("Dockerfile")
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from lightning_sdk import Machine, Studio
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class _Start:
|
|
7
|
+
"""Start resources on the Lightning AI platform."""
|
|
8
|
+
|
|
9
|
+
def __init__(self) -> None:
|
|
10
|
+
_machine_values = tuple([machine.name for machine in Machine.__dict__.values() if isinstance(machine, Machine)])
|
|
11
|
+
|
|
12
|
+
docstr_studio = f"""Start a studio on a given machine.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
name: The name of the studio to start.
|
|
16
|
+
If not specified, tries to infer from the environment (e.g. when run from within a Studio.)
|
|
17
|
+
teamspace: The teamspace the studio is part of. Should be of format <OWNER>/<TEAMSPACE_NAME>.
|
|
18
|
+
If not specified, tries to infer from the environment (e.g. when run from within a Studio.)
|
|
19
|
+
machine: The machine type to start the studio on. One of {", ".join(_machine_values)}.
|
|
20
|
+
Defaults to the CPU Machine.
|
|
21
|
+
"""
|
|
22
|
+
self.studio.__func__.__doc__ = docstr_studio
|
|
23
|
+
|
|
24
|
+
def studio(self, name: Optional[str] = None, teamspace: Optional[str] = None, machine: str = "CPU") -> None:
|
|
25
|
+
if teamspace is not None:
|
|
26
|
+
ts_splits = teamspace.split("/")
|
|
27
|
+
if len(ts_splits) != 2:
|
|
28
|
+
raise ValueError(f"Teamspace should be of format <OWNER>/<TEAMSPACE_NAME> but got {teamspace}")
|
|
29
|
+
owner, teamspace = ts_splits
|
|
30
|
+
else:
|
|
31
|
+
owner, teamspace = None, None
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
studio = Studio(name=name, teamspace=teamspace, org=owner, user=None, create_ok=False)
|
|
35
|
+
except (RuntimeError, ValueError):
|
|
36
|
+
studio = Studio(name=name, teamspace=teamspace, org=None, user=owner, create_ok=False)
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
resolved_machine = getattr(Machine, machine.upper(), Machine(machine, machine))
|
|
40
|
+
except KeyError:
|
|
41
|
+
resolved_machine = machine
|
|
42
|
+
|
|
43
|
+
studio.start(resolved_machine)
|
lightning_sdk/cli/stop.py
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
from typing import Optional
|
|
2
2
|
|
|
3
|
+
from rich.console import Console
|
|
4
|
+
|
|
3
5
|
from lightning_sdk.cli.job_and_mmt_action import _JobAndMMTAction
|
|
6
|
+
from lightning_sdk.studio import Studio
|
|
4
7
|
|
|
5
8
|
|
|
6
9
|
class _Stop(_JobAndMMTAction):
|
|
@@ -19,7 +22,7 @@ class _Stop(_JobAndMMTAction):
|
|
|
19
22
|
job = super().job(name=name, teamspace=teamspace)
|
|
20
23
|
|
|
21
24
|
job.stop()
|
|
22
|
-
print(f"Successfully stopped {job.name}!")
|
|
25
|
+
Console().print(f"Successfully stopped {job.name}!")
|
|
23
26
|
|
|
24
27
|
def mmt(self, name: Optional[str] = None, teamspace: Optional[str] = None) -> None:
|
|
25
28
|
"""Stop a multi-machine job.
|
|
@@ -34,4 +37,29 @@ class _Stop(_JobAndMMTAction):
|
|
|
34
37
|
mmt = super().mmt(name=name, teamspace=teamspace)
|
|
35
38
|
|
|
36
39
|
mmt.stop()
|
|
37
|
-
print(f"Successfully stopped {mmt.name}!")
|
|
40
|
+
Console().print(f"Successfully stopped {mmt.name}!")
|
|
41
|
+
|
|
42
|
+
def studio(self, name: Optional[str] = None, teamspace: Optional[str] = None) -> None:
|
|
43
|
+
"""Stop a running studio.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
name: The name of the studio to stop.
|
|
47
|
+
If not specified, tries to infer from the environment (e.g. when run from within a Studio.)
|
|
48
|
+
teamspace: The teamspace the studio is part of. Should be of format <OWNER>/<TEAMSPACE_NAME>.
|
|
49
|
+
If not specified, tries to infer from the environment (e.g. when run from within a Studio.)
|
|
50
|
+
"""
|
|
51
|
+
if teamspace is not None:
|
|
52
|
+
ts_splits = teamspace.split("/")
|
|
53
|
+
if len(ts_splits) != 2:
|
|
54
|
+
raise ValueError(f"Teamspace should be of format <OWNER>/<TEAMSPACE_NAME> but got {teamspace}")
|
|
55
|
+
owner, teamspace = ts_splits
|
|
56
|
+
else:
|
|
57
|
+
owner, teamspace = None, None
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
studio = Studio(name=name, teamspace=teamspace, org=owner, user=None, create_ok=False)
|
|
61
|
+
except (RuntimeError, ValueError):
|
|
62
|
+
studio = Studio(name=name, teamspace=teamspace, org=None, user=owner, create_ok=False)
|
|
63
|
+
|
|
64
|
+
studio.stop()
|
|
65
|
+
Console().print("Studio successfully stopped")
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
from typing import Dict, List, Optional
|
|
2
2
|
|
|
3
|
+
from rich.console import Console
|
|
3
4
|
from simple_term_menu import TerminalMenu
|
|
4
5
|
|
|
6
|
+
from lightning_sdk import Studio
|
|
5
7
|
from lightning_sdk.api import OrgApi, TeamspaceApi
|
|
6
8
|
from lightning_sdk.user import User
|
|
7
9
|
|
|
@@ -18,7 +20,7 @@ class _StudiosMenu:
|
|
|
18
20
|
if st["teamspace"] == teamspace and name == st["name"]:
|
|
19
21
|
return st
|
|
20
22
|
|
|
21
|
-
print("Could not find Studio {studio}, please select it from the list:")
|
|
23
|
+
Console().print("Could not find Studio {studio}, please select it from the list:")
|
|
22
24
|
return self._get_studio_from_interactive_menu(possible_studios)
|
|
23
25
|
|
|
24
26
|
@staticmethod
|
|
@@ -76,3 +78,24 @@ class _StudiosMenu:
|
|
|
76
78
|
possible_studios.append({"name": st.name, **teamspaces[teamspace_id]})
|
|
77
79
|
|
|
78
80
|
return possible_studios
|
|
81
|
+
|
|
82
|
+
def _get_studio(self, name: str, teamspace: str) -> Studio:
|
|
83
|
+
"""Get studio object from name and teamspace.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
name: Name of the studio
|
|
87
|
+
teamspace: Name of the teamspace
|
|
88
|
+
"""
|
|
89
|
+
if teamspace:
|
|
90
|
+
ts_splits = teamspace.split("/")
|
|
91
|
+
if len(ts_splits) != 2:
|
|
92
|
+
raise ValueError(f"Teamspace should be of format <OWNER>/<TEAMSPACE_NAME> but got {teamspace}")
|
|
93
|
+
owner, teamspace = ts_splits
|
|
94
|
+
else:
|
|
95
|
+
owner, teamspace = None, None
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
studio = Studio(name=name, teamspace=teamspace, org=owner, user=None, create_ok=False)
|
|
99
|
+
except (RuntimeError, ValueError):
|
|
100
|
+
studio = Studio(name=name, teamspace=teamspace, org=None, user=owner, create_ok=False)
|
|
101
|
+
return studio
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
from lightning_sdk import Machine, Studio
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class _Switch:
|
|
7
|
+
"""Switch machines for resources on the Lightning AI platform."""
|
|
8
|
+
|
|
9
|
+
def __init__(self) -> None:
|
|
10
|
+
_machine_values = tuple([machine.name for machine in Machine.__dict__.values() if isinstance(machine, Machine)])
|
|
11
|
+
|
|
12
|
+
docstr_studio = f"""Switch a studio to a given machine.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
name: The name of the studio to start.
|
|
16
|
+
If not specified, tries to infer from the environment (e.g. when run from within a Studio.)
|
|
17
|
+
teamspace: The teamspace the studio is part of. Should be of format <OWNER>/<TEAMSPACE_NAME>.
|
|
18
|
+
If not specified, tries to infer from the environment (e.g. when run from within a Studio.)
|
|
19
|
+
machine: The machine type to switch to. One of {", ".join(_machine_values)}.
|
|
20
|
+
Defaults to the CPU Machine.
|
|
21
|
+
"""
|
|
22
|
+
self.studio.__func__.__doc__ = docstr_studio
|
|
23
|
+
|
|
24
|
+
def studio(self, name: Optional[str] = None, teamspace: Optional[str] = None, machine: str = "CPU") -> None:
|
|
25
|
+
if teamspace is not None:
|
|
26
|
+
ts_splits = teamspace.split("/")
|
|
27
|
+
if len(ts_splits) != 2:
|
|
28
|
+
raise ValueError(f"Teamspace should be of format <OWNER>/<TEAMSPACE_NAME> but got {teamspace}")
|
|
29
|
+
owner, teamspace = ts_splits
|
|
30
|
+
else:
|
|
31
|
+
owner, teamspace = None, None
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
studio = Studio(name=name, teamspace=teamspace, org=owner, user=None, create_ok=False)
|
|
35
|
+
except (RuntimeError, ValueError):
|
|
36
|
+
studio = Studio(name=name, teamspace=teamspace, org=None, user=owner, create_ok=False)
|
|
37
|
+
|
|
38
|
+
try:
|
|
39
|
+
resolved_machine = getattr(Machine, machine.upper(), Machine(machine, machine))
|
|
40
|
+
except KeyError:
|
|
41
|
+
resolved_machine = machine
|
|
42
|
+
|
|
43
|
+
studio.switch_machine(resolved_machine)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from typing import Dict, List, Optional
|
|
2
2
|
|
|
3
|
+
from rich.console import Console
|
|
3
4
|
from simple_term_menu import TerminalMenu
|
|
4
5
|
|
|
5
6
|
from lightning_sdk.api import OrgApi
|
|
@@ -26,7 +27,7 @@ class _TeamspacesMenu:
|
|
|
26
27
|
if ts["name"] == name and (ts["user"] == owner or ts["org"] == owner):
|
|
27
28
|
return ts
|
|
28
29
|
|
|
29
|
-
print("Could not find Teamspace {teamspace}, please select it from the list:")
|
|
30
|
+
Console().print("Could not find Teamspace {teamspace}, please select it from the list:")
|
|
30
31
|
return self._get_teamspace_from_interactive_menu(possible_teamspaces)
|
|
31
32
|
|
|
32
33
|
@staticmethod
|
lightning_sdk/cli/upload.py
CHANGED
|
@@ -71,6 +71,7 @@ class _Uploads(_StudiosMenu, _TeamspacesMenu):
|
|
|
71
71
|
If not specified, will use the file or directory name of the path you want to upload
|
|
72
72
|
and place it in your home directory.
|
|
73
73
|
"""
|
|
74
|
+
console = Console()
|
|
74
75
|
if remote_path is None:
|
|
75
76
|
remote_path = os.path.basename(path)
|
|
76
77
|
|
|
@@ -81,7 +82,7 @@ class _Uploads(_StudiosMenu, _TeamspacesMenu):
|
|
|
81
82
|
|
|
82
83
|
selected_studio = self._resolve_studio(studio)
|
|
83
84
|
|
|
84
|
-
print(f"Uploading to {selected_studio.teamspace.name}/{selected_studio.name}")
|
|
85
|
+
console.print(f"Uploading to {selected_studio.teamspace.name}/{selected_studio.name}")
|
|
85
86
|
|
|
86
87
|
pairs = {}
|
|
87
88
|
for root, _, files in os.walk(path):
|
|
@@ -112,7 +113,7 @@ class _Uploads(_StudiosMenu, _TeamspacesMenu):
|
|
|
112
113
|
+ "/studios/"
|
|
113
114
|
+ selected_studio.name
|
|
114
115
|
)
|
|
115
|
-
print(f"See your files at {studio_url}")
|
|
116
|
+
console.print(f"See your files at {studio_url}")
|
|
116
117
|
|
|
117
118
|
def file(self, path: str, studio: Optional[str] = None, remote_path: Optional[str] = None) -> None:
|
|
118
119
|
"""Upload a file to a Studio.
|
|
@@ -126,6 +127,7 @@ class _Uploads(_StudiosMenu, _TeamspacesMenu):
|
|
|
126
127
|
If not specified, will use the name of the file you want to upload
|
|
127
128
|
and place it in your home directory.
|
|
128
129
|
"""
|
|
130
|
+
console = Console()
|
|
129
131
|
if remote_path is None:
|
|
130
132
|
remote_path = os.path.basename(path)
|
|
131
133
|
|
|
@@ -136,7 +138,7 @@ class _Uploads(_StudiosMenu, _TeamspacesMenu):
|
|
|
136
138
|
|
|
137
139
|
selected_studio = self._resolve_studio(studio)
|
|
138
140
|
|
|
139
|
-
print(f"Uploading to {selected_studio.teamspace.name}/{selected_studio.name}")
|
|
141
|
+
console.print(f"Uploading to {selected_studio.teamspace.name}/{selected_studio.name}")
|
|
140
142
|
|
|
141
143
|
self._single_file_upload(selected_studio, path, remote_path, True)
|
|
142
144
|
|
|
@@ -149,7 +151,7 @@ class _Uploads(_StudiosMenu, _TeamspacesMenu):
|
|
|
149
151
|
+ "/studios/"
|
|
150
152
|
+ selected_studio.name
|
|
151
153
|
)
|
|
152
|
-
print(f"See your file at {studio_url}")
|
|
154
|
+
console.print(f"See your file at {studio_url}")
|
|
153
155
|
|
|
154
156
|
def container(self, container: str, tag: str = "latest", teamspace: Optional[str] = None) -> None:
|
|
155
157
|
teamspace = self._resolve_teamspace(teamspace)
|
|
@@ -124,6 +124,7 @@ from lightning_sdk.lightning_cloud.openapi.models.id_publications_body import Id
|
|
|
124
124
|
from lightning_sdk.lightning_cloud.openapi.models.id_publications_body1 import IdPublicationsBody1
|
|
125
125
|
from lightning_sdk.lightning_cloud.openapi.models.id_release_body import IdReleaseBody
|
|
126
126
|
from lightning_sdk.lightning_cloud.openapi.models.id_reportlogsactivity_body import IdReportlogsactivityBody
|
|
127
|
+
from lightning_sdk.lightning_cloud.openapi.models.id_reportrestarttimings_body import IdReportrestarttimingsBody
|
|
127
128
|
from lightning_sdk.lightning_cloud.openapi.models.id_start_body import IdStartBody
|
|
128
129
|
from lightning_sdk.lightning_cloud.openapi.models.id_storage_body import IdStorageBody
|
|
129
130
|
from lightning_sdk.lightning_cloud.openapi.models.id_uploads_body import IdUploadsBody
|
|
@@ -446,6 +447,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_get_agent_job_logs_metadata
|
|
|
446
447
|
from lightning_sdk.lightning_cloud.openapi.models.v1_get_artifacts_page_response import V1GetArtifactsPageResponse
|
|
447
448
|
from lightning_sdk.lightning_cloud.openapi.models.v1_get_cloud_space_instance_status_response import V1GetCloudSpaceInstanceStatusResponse
|
|
448
449
|
from lightning_sdk.lightning_cloud.openapi.models.v1_get_cloud_space_size_response import V1GetCloudSpaceSizeResponse
|
|
450
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_get_cluster_accelerator_demand_response import V1GetClusterAcceleratorDemandResponse
|
|
449
451
|
from lightning_sdk.lightning_cloud.openapi.models.v1_get_cluster_credentials_response import V1GetClusterCredentialsResponse
|
|
450
452
|
from lightning_sdk.lightning_cloud.openapi.models.v1_get_deployment_routing_telemetry_aggregated_response import V1GetDeploymentRoutingTelemetryAggregatedResponse
|
|
451
453
|
from lightning_sdk.lightning_cloud.openapi.models.v1_get_deployment_routing_telemetry_response import V1GetDeploymentRoutingTelemetryResponse
|
|
@@ -571,6 +573,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_list_lightningapp_instances
|
|
|
571
573
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_lightningwork_events_response import V1ListLightningworkEventsResponse
|
|
572
574
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_lightningwork_response import V1ListLightningworkResponse
|
|
573
575
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_lit_pages_response import V1ListLitPagesResponse
|
|
576
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_list_lit_registry_repository_image_artifact_versions_response import V1ListLitRegistryRepositoryImageArtifactVersionsResponse
|
|
574
577
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_logger_artifact_response import V1ListLoggerArtifactResponse
|
|
575
578
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_managed_endpoints_response import V1ListManagedEndpointsResponse
|
|
576
579
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_memberships_response import V1ListMembershipsResponse
|
|
@@ -606,6 +609,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_list_studio_jobs_response i
|
|
|
606
609
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_user_slurm_jobs_response import V1ListUserSLURMJobsResponse
|
|
607
610
|
from lightning_sdk.lightning_cloud.openapi.models.v1_lit_page import V1LitPage
|
|
608
611
|
from lightning_sdk.lightning_cloud.openapi.models.v1_lit_page_type import V1LitPageType
|
|
612
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_lit_registry_artifact import V1LitRegistryArtifact
|
|
609
613
|
from lightning_sdk.lightning_cloud.openapi.models.v1_lit_registry_project import V1LitRegistryProject
|
|
610
614
|
from lightning_sdk.lightning_cloud.openapi.models.v1_lit_repository import V1LitRepository
|
|
611
615
|
from lightning_sdk.lightning_cloud.openapi.models.v1_locked_resource import V1LockedResource
|
|
@@ -699,6 +703,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_refresh_response import V1R
|
|
|
699
703
|
from lightning_sdk.lightning_cloud.openapi.models.v1_region_state import V1RegionState
|
|
700
704
|
from lightning_sdk.lightning_cloud.openapi.models.v1_regional_load_balancer import V1RegionalLoadBalancer
|
|
701
705
|
from lightning_sdk.lightning_cloud.openapi.models.v1_report_logs_activity_response import V1ReportLogsActivityResponse
|
|
706
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_report_restart_timings_response import V1ReportRestartTimingsResponse
|
|
702
707
|
from lightning_sdk.lightning_cloud.openapi.models.v1_request_cluster_access_request import V1RequestClusterAccessRequest
|
|
703
708
|
from lightning_sdk.lightning_cloud.openapi.models.v1_request_cluster_access_response import V1RequestClusterAccessResponse
|
|
704
709
|
from lightning_sdk.lightning_cloud.openapi.models.v1_request_verification_code_response import V1RequestVerificationCodeResponse
|
|
@@ -708,6 +713,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_resources import V1Resource
|
|
|
708
713
|
from lightning_sdk.lightning_cloud.openapi.models.v1_response_choice import V1ResponseChoice
|
|
709
714
|
from lightning_sdk.lightning_cloud.openapi.models.v1_response_choice_delta import V1ResponseChoiceDelta
|
|
710
715
|
from lightning_sdk.lightning_cloud.openapi.models.v1_restart_cloud_space_instance_response import V1RestartCloudSpaceInstanceResponse
|
|
716
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_restart_timing import V1RestartTiming
|
|
711
717
|
from lightning_sdk.lightning_cloud.openapi.models.v1_restore_deployment_release_response import V1RestoreDeploymentReleaseResponse
|
|
712
718
|
from lightning_sdk.lightning_cloud.openapi.models.v1_role import V1Role
|
|
713
719
|
from lightning_sdk.lightning_cloud.openapi.models.v1_rolling_update_strategy import V1RollingUpdateStrategy
|
|
@@ -1096,6 +1096,7 @@ class ClusterServiceApi(object):
|
|
|
1096
1096
|
:param async_req bool
|
|
1097
1097
|
:param str cluster_id: (required)
|
|
1098
1098
|
:param str id: (required)
|
|
1099
|
+
:param str org_id:
|
|
1099
1100
|
:return: V1DeleteClusterUsageRestrictionResponse
|
|
1100
1101
|
If the method is called asynchronously,
|
|
1101
1102
|
returns the request thread.
|
|
@@ -1118,12 +1119,13 @@ class ClusterServiceApi(object):
|
|
|
1118
1119
|
:param async_req bool
|
|
1119
1120
|
:param str cluster_id: (required)
|
|
1120
1121
|
:param str id: (required)
|
|
1122
|
+
:param str org_id:
|
|
1121
1123
|
:return: V1DeleteClusterUsageRestrictionResponse
|
|
1122
1124
|
If the method is called asynchronously,
|
|
1123
1125
|
returns the request thread.
|
|
1124
1126
|
"""
|
|
1125
1127
|
|
|
1126
|
-
all_params = ['cluster_id', 'id'] # noqa: E501
|
|
1128
|
+
all_params = ['cluster_id', 'id', 'org_id'] # noqa: E501
|
|
1127
1129
|
all_params.append('async_req')
|
|
1128
1130
|
all_params.append('_return_http_data_only')
|
|
1129
1131
|
all_params.append('_preload_content')
|
|
@@ -1156,6 +1158,8 @@ class ClusterServiceApi(object):
|
|
|
1156
1158
|
path_params['id'] = params['id'] # noqa: E501
|
|
1157
1159
|
|
|
1158
1160
|
query_params = []
|
|
1161
|
+
if 'org_id' in params:
|
|
1162
|
+
query_params.append(('orgId', params['org_id'])) # noqa: E501
|
|
1159
1163
|
|
|
1160
1164
|
header_params = {}
|
|
1161
1165
|
|
|
@@ -1525,6 +1529,111 @@ class ClusterServiceApi(object):
|
|
|
1525
1529
|
_request_timeout=params.get('_request_timeout'),
|
|
1526
1530
|
collection_formats=collection_formats)
|
|
1527
1531
|
|
|
1532
|
+
def cluster_service_get_cluster_accelerator_demand(self, cluster_id: 'str', id: 'str', **kwargs) -> 'V1GetClusterAcceleratorDemandResponse': # noqa: E501
|
|
1533
|
+
"""cluster_service_get_cluster_accelerator_demand # noqa: E501
|
|
1534
|
+
|
|
1535
|
+
This method makes a synchronous HTTP request by default. To make an
|
|
1536
|
+
asynchronous HTTP request, please pass async_req=True
|
|
1537
|
+
>>> thread = api.cluster_service_get_cluster_accelerator_demand(cluster_id, id, async_req=True)
|
|
1538
|
+
>>> result = thread.get()
|
|
1539
|
+
|
|
1540
|
+
:param async_req bool
|
|
1541
|
+
:param str cluster_id: (required)
|
|
1542
|
+
:param str id: (required)
|
|
1543
|
+
:param bool spot:
|
|
1544
|
+
:return: V1GetClusterAcceleratorDemandResponse
|
|
1545
|
+
If the method is called asynchronously,
|
|
1546
|
+
returns the request thread.
|
|
1547
|
+
"""
|
|
1548
|
+
kwargs['_return_http_data_only'] = True
|
|
1549
|
+
if kwargs.get('async_req'):
|
|
1550
|
+
return self.cluster_service_get_cluster_accelerator_demand_with_http_info(cluster_id, id, **kwargs) # noqa: E501
|
|
1551
|
+
else:
|
|
1552
|
+
(data) = self.cluster_service_get_cluster_accelerator_demand_with_http_info(cluster_id, id, **kwargs) # noqa: E501
|
|
1553
|
+
return data
|
|
1554
|
+
|
|
1555
|
+
def cluster_service_get_cluster_accelerator_demand_with_http_info(self, cluster_id: 'str', id: 'str', **kwargs) -> 'V1GetClusterAcceleratorDemandResponse': # noqa: E501
|
|
1556
|
+
"""cluster_service_get_cluster_accelerator_demand # noqa: E501
|
|
1557
|
+
|
|
1558
|
+
This method makes a synchronous HTTP request by default. To make an
|
|
1559
|
+
asynchronous HTTP request, please pass async_req=True
|
|
1560
|
+
>>> thread = api.cluster_service_get_cluster_accelerator_demand_with_http_info(cluster_id, id, async_req=True)
|
|
1561
|
+
>>> result = thread.get()
|
|
1562
|
+
|
|
1563
|
+
:param async_req bool
|
|
1564
|
+
:param str cluster_id: (required)
|
|
1565
|
+
:param str id: (required)
|
|
1566
|
+
:param bool spot:
|
|
1567
|
+
:return: V1GetClusterAcceleratorDemandResponse
|
|
1568
|
+
If the method is called asynchronously,
|
|
1569
|
+
returns the request thread.
|
|
1570
|
+
"""
|
|
1571
|
+
|
|
1572
|
+
all_params = ['cluster_id', 'id', 'spot'] # noqa: E501
|
|
1573
|
+
all_params.append('async_req')
|
|
1574
|
+
all_params.append('_return_http_data_only')
|
|
1575
|
+
all_params.append('_preload_content')
|
|
1576
|
+
all_params.append('_request_timeout')
|
|
1577
|
+
|
|
1578
|
+
params = locals()
|
|
1579
|
+
for key, val in six.iteritems(params['kwargs']):
|
|
1580
|
+
if key not in all_params:
|
|
1581
|
+
raise TypeError(
|
|
1582
|
+
"Got an unexpected keyword argument '%s'"
|
|
1583
|
+
" to method cluster_service_get_cluster_accelerator_demand" % key
|
|
1584
|
+
)
|
|
1585
|
+
params[key] = val
|
|
1586
|
+
del params['kwargs']
|
|
1587
|
+
# verify the required parameter 'cluster_id' is set
|
|
1588
|
+
if ('cluster_id' not in params or
|
|
1589
|
+
params['cluster_id'] is None):
|
|
1590
|
+
raise ValueError("Missing the required parameter `cluster_id` when calling `cluster_service_get_cluster_accelerator_demand`") # noqa: E501
|
|
1591
|
+
# verify the required parameter 'id' is set
|
|
1592
|
+
if ('id' not in params or
|
|
1593
|
+
params['id'] is None):
|
|
1594
|
+
raise ValueError("Missing the required parameter `id` when calling `cluster_service_get_cluster_accelerator_demand`") # noqa: E501
|
|
1595
|
+
|
|
1596
|
+
collection_formats = {}
|
|
1597
|
+
|
|
1598
|
+
path_params = {}
|
|
1599
|
+
if 'cluster_id' in params:
|
|
1600
|
+
path_params['clusterId'] = params['cluster_id'] # noqa: E501
|
|
1601
|
+
if 'id' in params:
|
|
1602
|
+
path_params['id'] = params['id'] # noqa: E501
|
|
1603
|
+
|
|
1604
|
+
query_params = []
|
|
1605
|
+
if 'spot' in params:
|
|
1606
|
+
query_params.append(('spot', params['spot'])) # noqa: E501
|
|
1607
|
+
|
|
1608
|
+
header_params = {}
|
|
1609
|
+
|
|
1610
|
+
form_params = []
|
|
1611
|
+
local_var_files = {}
|
|
1612
|
+
|
|
1613
|
+
body_params = None
|
|
1614
|
+
# HTTP header `Accept`
|
|
1615
|
+
header_params['Accept'] = self.api_client.select_header_accept(
|
|
1616
|
+
['application/json']) # noqa: E501
|
|
1617
|
+
|
|
1618
|
+
# Authentication setting
|
|
1619
|
+
auth_settings = [] # noqa: E501
|
|
1620
|
+
|
|
1621
|
+
return self.api_client.call_api(
|
|
1622
|
+
'/v1/core/clusters/{clusterId}/accelerator/{id}/demand', 'GET',
|
|
1623
|
+
path_params,
|
|
1624
|
+
query_params,
|
|
1625
|
+
header_params,
|
|
1626
|
+
body=body_params,
|
|
1627
|
+
post_params=form_params,
|
|
1628
|
+
files=local_var_files,
|
|
1629
|
+
response_type='V1GetClusterAcceleratorDemandResponse', # noqa: E501
|
|
1630
|
+
auth_settings=auth_settings,
|
|
1631
|
+
async_req=params.get('async_req'),
|
|
1632
|
+
_return_http_data_only=params.get('_return_http_data_only'),
|
|
1633
|
+
_preload_content=params.get('_preload_content', True),
|
|
1634
|
+
_request_timeout=params.get('_request_timeout'),
|
|
1635
|
+
collection_formats=collection_formats)
|
|
1636
|
+
|
|
1528
1637
|
def cluster_service_get_cluster_availability(self, **kwargs) -> 'V1ClusterAvailability': # noqa: E501
|
|
1529
1638
|
"""cluster_service_get_cluster_availability # noqa: E501
|
|
1530
1639
|
|
|
@@ -2311,6 +2420,7 @@ class ClusterServiceApi(object):
|
|
|
2311
2420
|
|
|
2312
2421
|
:param async_req bool
|
|
2313
2422
|
:param str cluster_id: (required)
|
|
2423
|
+
:param str org_id:
|
|
2314
2424
|
:return: V1ListClusterUsageRestrictionsResponse
|
|
2315
2425
|
If the method is called asynchronously,
|
|
2316
2426
|
returns the request thread.
|
|
@@ -2332,12 +2442,13 @@ class ClusterServiceApi(object):
|
|
|
2332
2442
|
|
|
2333
2443
|
:param async_req bool
|
|
2334
2444
|
:param str cluster_id: (required)
|
|
2445
|
+
:param str org_id:
|
|
2335
2446
|
:return: V1ListClusterUsageRestrictionsResponse
|
|
2336
2447
|
If the method is called asynchronously,
|
|
2337
2448
|
returns the request thread.
|
|
2338
2449
|
"""
|
|
2339
2450
|
|
|
2340
|
-
all_params = ['cluster_id'] # noqa: E501
|
|
2451
|
+
all_params = ['cluster_id', 'org_id'] # noqa: E501
|
|
2341
2452
|
all_params.append('async_req')
|
|
2342
2453
|
all_params.append('_return_http_data_only')
|
|
2343
2454
|
all_params.append('_preload_content')
|
|
@@ -2364,6 +2475,8 @@ class ClusterServiceApi(object):
|
|
|
2364
2475
|
path_params['clusterId'] = params['cluster_id'] # noqa: E501
|
|
2365
2476
|
|
|
2366
2477
|
query_params = []
|
|
2478
|
+
if 'org_id' in params:
|
|
2479
|
+
query_params.append(('orgId', params['org_id'])) # noqa: E501
|
|
2367
2480
|
|
|
2368
2481
|
header_params = {}
|
|
2369
2482
|
|