lightning-sdk 0.2.3__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 CHANGED
@@ -31,6 +31,6 @@ __all__ = [
31
31
  "User",
32
32
  ]
33
33
 
34
- __version__ = "0.2.3"
34
+ __version__ = "0.2.4"
35
35
  _check_version_and_prompt_upgrade(__version__)
36
36
  _set_tqdm_envvars_noninteractive()
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
- ) -> Dict[str, Union[str, bool]]:
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 dictionary containing the name of the deployed API,
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: Dict[str, Union[str, bool]]) -> None:
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 dictionary returned by the run method.
185
+ deployment: The deployment object returned by the run method.
192
186
  """
193
- if "teamspace id" not in deployment or "id" not in deployment:
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)
@@ -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 "True")
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, "")
@@ -4,17 +4,19 @@ from pathlib import Path
4
4
  from typing import Optional, Union
5
5
 
6
6
  import click
7
- import docker
8
7
  from rich.console import Console
9
8
  from rich.progress import Progress, SpinnerColumn, TextColumn, TimeElapsedColumn
10
9
  from rich.prompt import Confirm
11
10
 
11
+ from lightning_sdk import Machine, Teamspace
12
12
  from lightning_sdk.api.deployment_api import DeploymentApi
13
13
  from lightning_sdk.api.lit_container_api import LitContainerApi
14
14
  from lightning_sdk.cli.exceptions import StudioCliError
15
15
  from lightning_sdk.cli.teamspace_menu import _TeamspacesMenu
16
16
  from lightning_sdk.serve import _LitServeDeployer
17
17
 
18
+ _MACHINE_VALUES = tuple([machine.name for machine in Machine.__dict__.values() if isinstance(machine, Machine)])
19
+
18
20
 
19
21
  @click.group("serve")
20
22
  def serve() -> None:
@@ -24,11 +26,10 @@ def serve() -> None:
24
26
  lightning serve api server.py # serve locally
25
27
 
26
28
  Example:
27
- lightning serve api server.py --cloud # deploy to the cloud
29
+ lightning serve api server.py --cloud --name litserve-api # deploy to the cloud
28
30
 
29
31
  You can deploy the API to the cloud by running `lightning serve api server.py --cloud`.
30
- This will generate a Dockerfile, build the image, and push it to the image registry.
31
- 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.
32
33
  """
33
34
 
34
35
 
@@ -48,7 +49,6 @@ def serve() -> None:
48
49
  flag_value=True,
49
50
  help="Deploy the model to the Lightning AI platform",
50
51
  )
51
- @click.option("--gpu", is_flag=True, default=False, flag_value=True, help="Use GPU for serving")
52
52
  @click.option("--name", default=None, help="Name of the deployed API (e.g., 'classification-api', 'Llama-api')")
53
53
  @click.option(
54
54
  "--non-interactive",
@@ -58,17 +58,88 @@ def serve() -> None:
58
58
  flag_value=True,
59
59
  help="Do not prompt for confirmation",
60
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
+ )
61
108
  def api(
62
109
  script_path: str,
63
110
  easy: bool,
64
111
  cloud: bool,
65
- gpu: bool,
66
112
  name: str,
67
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],
68
125
  ) -> None:
69
126
  """Deploy a LitServe model script."""
70
127
  return api_impl(
71
- script_path=script_path, easy=easy, cloud=cloud, gpu=gpu, repository=name, non_interactive=non_interactive
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,
72
143
  )
73
144
 
74
145
 
@@ -76,10 +147,20 @@ def api_impl(
76
147
  script_path: Union[str, Path],
77
148
  easy: bool = False,
78
149
  cloud: bool = False,
79
- gpu: bool = False,
80
150
  repository: [str] = None,
81
151
  tag: Optional[str] = None,
82
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,
83
164
  ) -> None:
84
165
  """Deploy a LitServe model script."""
85
166
  console = Console()
@@ -93,8 +174,24 @@ def api_impl(
93
174
 
94
175
  if cloud:
95
176
  repository = repository or "litserve-model"
177
+ machine = Machine.from_str(machine)
96
178
  return _handle_cloud(
97
- script_path, console, gpu=gpu, repository=repository, tag=tag, non_interactive=non_interactive
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,
98
195
  )
99
196
 
100
197
  try:
@@ -111,22 +208,30 @@ def api_impl(
111
208
  def _handle_cloud(
112
209
  script_path: Union[str, Path],
113
210
  console: Console,
114
- gpu: bool,
115
211
  repository: str = "litserve-model",
116
212
  tag: Optional[str] = None,
117
- teamspace: Optional[str] = None,
118
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,
119
225
  ) -> None:
120
- menu = _TeamspacesMenu()
121
- teamspace = menu._resolve_teamspace(teamspace)
122
- try:
123
- client = docker.from_env()
124
- client.ping()
125
- except docker.errors.DockerException as e:
126
- raise StudioCliError(f"Failed to connect to Docker daemon: {e!s}. Is Docker running?") from None
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)
127
231
 
232
+ port = port or 8000
128
233
  ls_deployer = _LitServeDeployer()
129
- path = ls_deployer.dockerize_api(script_path, port=8000, gpu=gpu, tag=tag)
234
+ path = ls_deployer.dockerize_api(script_path, port=port, gpu=not machine.is_cpu(), tag=tag, print_success=False)
130
235
  console.clear()
131
236
  if non_interactive:
132
237
  console.print("[italic]non-interactive[/italic] mode enabled, skipping confirmation prompts", style="blue")
@@ -142,7 +247,8 @@ def _handle_cloud(
142
247
  lit_cr = LitContainerApi()
143
248
  deployment_name = os.path.basename(repository)
144
249
 
145
- if DeploymentApi().get_deployment_by_name(deployment_name, teamspace.id):
250
+ ls_deployer.authenticate()
251
+ if DeploymentApi().get_deployment_by_name(deployment_name, resolved_teamspace.id):
146
252
  raise StudioCliError(f"Deployment {deployment_name} already exists. Please choose a different name.") from None
147
253
 
148
254
  with Progress(
@@ -154,7 +260,7 @@ def _handle_cloud(
154
260
  ) as progress:
155
261
  try:
156
262
  ls_deployer.build_container(path, repository, tag, console, progress)
157
- push_status = ls_deployer.push_container(repository, tag, teamspace, lit_cr, progress)
263
+ push_status = ls_deployer.push_container(repository, tag, resolved_teamspace, lit_cr, progress)
158
264
  except Exception as e:
159
265
  console.print(f"❌ Deployment failed: {e}", style="red")
160
266
  return
@@ -163,6 +269,17 @@ def _handle_cloud(
163
269
  repository = push_status.get("repository")
164
270
 
165
271
  deployment_status = ls_deployer._run_on_cloud(
166
- deployment_name=deployment_name, image=repository, teamspace=teamspace, ports=[8000], gpu=gpu, metric=None
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,
167
284
  )
168
285
  console.print(f"🚀 Deployment started, access at [i]{deployment_status.get('url')}[/i]")
@@ -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": repo.creation_time.strftime("%Y-%m-%d %H:%M:%S"),
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 _has_mmt_v2() and not self._force_v1:
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
- if studio is None:
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
- if "/" not in name: # do some magic if you run studio
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
- if "/" not in name: # do some magic if you run studio
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(
@@ -49,8 +49,8 @@ class Pipeline:
49
49
 
50
50
  self._teamspace = _resolve_teamspace(
51
51
  teamspace=teamspace,
52
- org=self._org,
53
- user=self._user,
52
+ org=org,
53
+ user=user,
54
54
  )
55
55
 
56
56
  self._pipeline_api = PipelineApi()
lightning_sdk/serve.py CHANGED
@@ -1,9 +1,9 @@
1
1
  import os
2
2
  import shlex
3
3
  import subprocess
4
- import warnings
5
4
  from pathlib import Path
6
- from typing import Generator, List, Optional
5
+ from typing import Generator, Optional
6
+ from urllib.parse import urlencode
7
7
 
8
8
  import docker
9
9
  from rich.console import Console
@@ -13,6 +13,26 @@ from lightning_sdk import Deployment, Machine, Teamspace
13
13
  from lightning_sdk.api.deployment_api import AutoScaleConfig
14
14
  from lightning_sdk.api.lit_container_api import LitContainerApi
15
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)
16
36
 
17
37
 
18
38
  class _LitServeDeployer:
@@ -20,38 +40,49 @@ class _LitServeDeployer:
20
40
  self._console = Console()
21
41
  self._client = None
22
42
 
43
+ @staticmethod
44
+ def authenticate() -> None:
45
+ auth = _Auth()
46
+ auth.authenticate()
47
+
23
48
  @property
24
49
  def client(self) -> docker.DockerClient:
25
- os.environ["DOCKER_BUILDKIT"] = "1"
26
-
27
50
  if self._client is None:
28
51
  try:
52
+ os.environ["DOCKER_BUILDKIT"] = "1"
29
53
  self._client = docker.from_env()
30
54
  self._client.ping()
31
- except docker.errors.DockerException as e:
32
- raise RuntimeError(f"Failed to connect to Docker daemon: {e!s}. Is Docker running?") from None
55
+ except docker.errors.DockerException:
56
+ raise RuntimeError(_DOCKER_NOT_RUNNING_MSG) from None
33
57
  return self._client
34
58
 
35
59
  def dockerize_api(
36
- self, server_filename: str, port: int = 8000, gpu: bool = False, tag: str = "litserve-model"
60
+ self,
61
+ server_filename: str,
62
+ port: int = 8000,
63
+ gpu: bool = False,
64
+ tag: str = "litserve-model",
65
+ print_success: bool = True,
37
66
  ) -> str:
38
67
  import litserve as ls
39
68
  from litserve import docker_builder
40
69
 
41
70
  console = self._console
42
- if os.path.exists("Dockerfile"):
43
- console.print("Dockerfile already exists. Skipping generation.")
44
- return os.path.abspath("Dockerfile")
45
-
46
71
  requirements = ""
47
72
  if os.path.exists("requirements.txt"):
48
73
  requirements = "-r requirements.txt"
49
74
  else:
50
- warnings.warn(
75
+ console.print(
51
76
  f"requirements.txt not found at {os.getcwd()}. "
52
77
  f"Make sure to install the required packages in the Dockerfile.",
53
- UserWarning,
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."
54
84
  )
85
+ return os.path.abspath("Dockerfile")
55
86
  current_dir = Path.cwd()
56
87
  if not (current_dir / server_filename).is_file():
57
88
  raise FileNotFoundError(f"Server file `{server_filename}` must be in the current directory: {os.getcwd()}")
@@ -72,7 +103,8 @@ class _LitServeDeployer:
72
103
  with open("Dockerfile", "w") as f:
73
104
  f.write(dockerfile_content)
74
105
 
75
- success_msg = f"""[bold]Dockerfile created successfully[/bold]
106
+ if print_success:
107
+ success_msg = f"""[bold]Dockerfile created successfully[/bold]
76
108
  Update [underline]{os.path.abspath("Dockerfile")}[/underline] to add any additional dependencies or commands.
77
109
 
78
110
  [bold]Build the container with:[/bold]
@@ -84,7 +116,7 @@ Update [underline]{os.path.abspath("Dockerfile")}[/underline] to add any additio
84
116
  [bold]To push the container to a registry:[/bold]
85
117
  > [underline]docker push {tag}[/underline]
86
118
  """
87
- console.print(success_msg)
119
+ console.print(success_msg)
88
120
  return os.path.abspath("Dockerfile")
89
121
 
90
122
  @staticmethod
@@ -187,19 +219,19 @@ Update [underline]{os.path.abspath("Dockerfile")}[/underline] to add any additio
187
219
  deployment_name: str,
188
220
  teamspace: Teamspace,
189
221
  image: str,
190
- ports: List[int],
191
- gpu: bool = False,
192
222
  metric: Optional[str] = None,
193
223
  machine: Optional[Machine] = None,
194
- min_replica: Optional[int] = 1,
224
+ min_replica: Optional[int] = 0,
195
225
  max_replica: Optional[int] = 1,
196
226
  spot: Optional[bool] = None,
197
- replicas: Optional[int] = None,
227
+ replicas: Optional[int] = 1,
198
228
  cloud_account: Optional[str] = None,
229
+ port: Optional[int] = 8000,
230
+ include_credentials: Optional[bool] = True,
199
231
  ) -> dict:
200
- machine = machine or Machine.CPU
201
- metric = metric or "GPU" if gpu else "CPU"
202
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")
203
235
  deployment = Deployment(deployment_name, teamspace)
204
236
  if deployment.is_started:
205
237
  raise RuntimeError(
@@ -211,11 +243,12 @@ Update [underline]{os.path.abspath("Dockerfile")}[/underline] to add any additio
211
243
  deployment.start(
212
244
  machine=machine,
213
245
  image=image,
214
- ports=ports,
215
246
  autoscale=autoscale,
216
247
  spot=spot,
217
248
  replicas=replicas,
218
249
  cloud_account=cloud_account,
250
+ ports=[port],
251
+ include_credentials=include_credentials,
219
252
  )
220
253
 
221
254
  return {"deployment": deployment, "url": url}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lightning_sdk
3
- Version: 0.2.3
3
+ Version: 0.2.4
4
4
  Summary: SDK to develop using Lightning AI Studios
5
5
  Author-email: Lightning-AI <justus@lightning.ai>
6
6
  License: MIT License
@@ -1,23 +1,23 @@
1
1
  docs/source/conf.py,sha256=r8yX20eC-4mHhMTd0SbQb5TlSWHhO6wnJ0VJ_FBFpag,13249
2
- lightning_sdk/__init__.py,sha256=YGP-QC_WV8gn8xxxESb1X6DcaOVnY-lkbM0NHBZor_s,1104
2
+ lightning_sdk/__init__.py,sha256=0M0hCXuVUl2Lxnfphl1qbZNnMBde0xhorpbackzGcHk,1104
3
3
  lightning_sdk/agents.py,sha256=ly6Ma1j0ZgGPFyvPvMN28JWiB9dATIstFa5XM8pMi6I,1577
4
- lightning_sdk/ai_hub.py,sha256=dfw5CS93ehGmPlZCbbE2Pu3v0ANI6ZY05-ULbcHFqs4,7189
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=423XYYpzoGppv200kMbD9dB-iqP6xdHj7q5kejJLDKE,3778
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=inFyRA8aLMvrAhCeIdJaTGHjp0Qtou9G_J3gXePxU4E,5975
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=gllkjhCzG_Pdo5iV9AF78ytwgJXxjIUmb8vebi50M1o,8334
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=Yr9VvxueIqPUUeMqExbjbWD5CE_yeWjq0dXQQv47erg,6779
20
+ lightning_sdk/api/ai_hub_api.py,sha256=IaA57Cqu_zdj5zRwKkx9LJQIGFFo2mHmPOYekKxbZ9A,6999
21
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
23
  lightning_sdk/api/lit_container_api.py,sha256=m-3qZIIpZ24Z2Z8z9x8Di6cOSjkZmBFcf9R2yVMpA_4,7648
@@ -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=9g8FygBIIgT8qsL885xa-tUt4IXGSm7pNEnx23CIdOE,5729
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
@@ -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=UbbK6UFEd-S2OPjezCG7cy-7kfMbyLOg3LAzs_Zq2Ds,13872
917
- lightning_sdk/mmt/v1.py,sha256=dROOXRWLzG2kUwGtvozB9Vywt_3QK8j4XFHVZFohWUM,9527
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=L1Aj5kRffjBKLUYt6YJDerw2CwCGi8g1TTwHV3qrpfU,3774
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.3.dist-info/LICENSE,sha256=uFIuZwj5z-4TeF2UuacPZ1o17HkvKObT8fY50qN84sg,1064
932
- lightning_sdk-0.2.3.dist-info/METADATA,sha256=dqptSi26kADENeZWIzeE4uyJdaLagaxgvam8I_49_uQ,3991
933
- lightning_sdk-0.2.3.dist-info/WHEEL,sha256=P9jw-gEje8ByB7_hXoICnHtVCrEwMQh-630tKvQWehc,91
934
- lightning_sdk-0.2.3.dist-info/entry_points.txt,sha256=msB9PJWIJ784dX-OP8by51d4IbKYH3Fj1vCuA9oXjHY,68
935
- lightning_sdk-0.2.3.dist-info/top_level.txt,sha256=ps8doKILFXmN7F1mHncShmnQoTxKBRPIcchC8TpoBw4,19
936
- lightning_sdk-0.2.3.dist-info/RECORD,,
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,,