lightning-sdk 0.2.11__py3-none-any.whl → 0.2.13__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.
Files changed (61) hide show
  1. lightning_sdk/__init__.py +1 -1
  2. lightning_sdk/api/deployment_api.py +35 -3
  3. lightning_sdk/api/lit_container_api.py +13 -7
  4. lightning_sdk/api/llm_api.py +46 -0
  5. lightning_sdk/api/studio_api.py +17 -0
  6. lightning_sdk/cli/entrypoint.py +1 -1
  7. lightning_sdk/cli/serve.py +221 -62
  8. lightning_sdk/deployment/deployment.py +53 -7
  9. lightning_sdk/lightning_cloud/openapi/__init__.py +11 -1
  10. lightning_sdk/lightning_cloud/openapi/api/__init__.py +1 -0
  11. lightning_sdk/lightning_cloud/openapi/api/cloud_space_environment_template_service_api.py +4 -4
  12. lightning_sdk/lightning_cloud/openapi/api/cluster_service_api.py +13 -1
  13. lightning_sdk/lightning_cloud/openapi/api/data_connection_service_api.py +4 -4
  14. lightning_sdk/lightning_cloud/openapi/api/git_credentials_service_api.py +497 -0
  15. lightning_sdk/lightning_cloud/openapi/api/jobs_service_api.py +124 -0
  16. lightning_sdk/lightning_cloud/openapi/models/__init__.py +10 -1
  17. lightning_sdk/lightning_cloud/openapi/models/assistant_id_conversations_body.py +27 -1
  18. lightning_sdk/lightning_cloud/openapi/models/create_deployment_request_defines_a_spec_for_the_job_that_allows_for_autoscaling_jobs.py +27 -1
  19. lightning_sdk/lightning_cloud/openapi/models/deployments_id_body.py +27 -1
  20. lightning_sdk/lightning_cloud/openapi/models/orgs_id_body.py +27 -1
  21. lightning_sdk/lightning_cloud/openapi/models/projects_id_body.py +27 -1
  22. lightning_sdk/lightning_cloud/openapi/models/update.py +65 -195
  23. lightning_sdk/lightning_cloud/openapi/models/update1.py +357 -0
  24. lightning_sdk/lightning_cloud/openapi/models/v1_cloud_provider.py +1 -0
  25. lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_environment_template.py +27 -1
  26. lightning_sdk/lightning_cloud/openapi/models/v1_cluster_accelerator.py +27 -1
  27. lightning_sdk/lightning_cloud/openapi/models/v1_cluster_security_options.py +27 -1
  28. lightning_sdk/lightning_cloud/openapi/models/v1_cluster_spec.py +79 -1
  29. lightning_sdk/lightning_cloud/openapi/models/v1_create_deployment_request.py +27 -1
  30. lightning_sdk/lightning_cloud/openapi/models/v1_create_git_credentials_request.py +175 -0
  31. lightning_sdk/lightning_cloud/openapi/models/v1_delete_cloud_space_environment_template_response.py +1 -53
  32. lightning_sdk/lightning_cloud/openapi/models/v1_delete_git_credentials_response.py +97 -0
  33. lightning_sdk/lightning_cloud/openapi/models/v1_deployment.py +27 -1
  34. lightning_sdk/lightning_cloud/openapi/models/v1_deployment_state.py +2 -0
  35. lightning_sdk/lightning_cloud/openapi/models/v1_get_user_response.py +1 -27
  36. lightning_sdk/lightning_cloud/openapi/models/v1_git_credentials.py +227 -0
  37. lightning_sdk/lightning_cloud/openapi/models/v1_job_resource.py +279 -0
  38. lightning_sdk/lightning_cloud/openapi/models/v1_job_type.py +108 -0
  39. lightning_sdk/lightning_cloud/openapi/models/v1_list_git_credentials_response.py +123 -0
  40. lightning_sdk/lightning_cloud/openapi/models/v1_list_job_resources_response.py +123 -0
  41. lightning_sdk/lightning_cloud/openapi/models/v1_nebius_direct_v1.py +149 -0
  42. lightning_sdk/lightning_cloud/openapi/models/v1_organization.py +55 -1
  43. lightning_sdk/lightning_cloud/openapi/models/v1_project_settings.py +29 -1
  44. lightning_sdk/lightning_cloud/openapi/models/v1_reservation_billing_session.py +279 -0
  45. lightning_sdk/lightning_cloud/openapi/models/v1_resources.py +55 -3
  46. lightning_sdk/lightning_cloud/openapi/models/v1_update_user_request.py +1 -27
  47. lightning_sdk/lightning_cloud/openapi/models/v1_usage.py +27 -1
  48. lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +157 -1
  49. lightning_sdk/llm/__init__.py +3 -0
  50. lightning_sdk/llm/llm.py +118 -0
  51. lightning_sdk/plugin.py +19 -0
  52. lightning_sdk/serve.py +4 -6
  53. lightning_sdk/studio.py +33 -0
  54. {lightning_sdk-0.2.11.dist-info → lightning_sdk-0.2.13.dist-info}/METADATA +1 -1
  55. {lightning_sdk-0.2.11.dist-info → lightning_sdk-0.2.13.dist-info}/RECORD +60 -47
  56. lightning_sdk/lightning_cloud/openapi/models/environmenttemplates_id_body.py +0 -253
  57. /lightning_sdk/cli/{docker.py → docker_cli.py} +0 -0
  58. {lightning_sdk-0.2.11.dist-info → lightning_sdk-0.2.13.dist-info}/LICENSE +0 -0
  59. {lightning_sdk-0.2.11.dist-info → lightning_sdk-0.2.13.dist-info}/WHEEL +0 -0
  60. {lightning_sdk-0.2.11.dist-info → lightning_sdk-0.2.13.dist-info}/entry_points.txt +0 -0
  61. {lightning_sdk-0.2.11.dist-info → lightning_sdk-0.2.13.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,118 @@
1
+ from typing import Dict, List, Optional, Set, Tuple, Union
2
+
3
+ from lightning_sdk.api import UserApi
4
+ from lightning_sdk.api.llm_api import LLMApi
5
+ from lightning_sdk.lightning_cloud.login import Auth
6
+ from lightning_sdk.lightning_cloud.openapi import V1Assistant
7
+ from lightning_sdk.lightning_cloud.openapi.rest import ApiException
8
+ from lightning_sdk.organization import Organization
9
+ from lightning_sdk.user import User
10
+ from lightning_sdk.utils.resolve import _resolve_org, _resolve_user
11
+
12
+
13
+ class LLM:
14
+ def __init__(
15
+ self,
16
+ name: str,
17
+ user: Union[str, "User", None] = None,
18
+ org: Union[str, "Organization", None] = None,
19
+ ) -> None:
20
+ self._auth = Auth()
21
+ self._user = None
22
+
23
+ try:
24
+ self._auth.authenticate()
25
+ self._user = User(name=UserApi()._get_user_by_id(self._auth.user_id).username)
26
+ except ConnectionError as e:
27
+ raise e
28
+
29
+ self._name = name
30
+ try:
31
+ self._user = _resolve_user(self._user or user)
32
+ except ValueError:
33
+ self._user = None
34
+
35
+ self._name = name
36
+ self._org, self._model_name = self._parse_model_name(name)
37
+ try:
38
+ # check if it is a org model
39
+ self._org = _resolve_org(self._org or org)
40
+ except ApiException:
41
+ self._org = None
42
+
43
+ self._llm_api = LLMApi()
44
+ self._public_models = self._build_model_lookup(self._get_public_models())
45
+ self._org_models = self._build_model_lookup(self._get_org_models())
46
+ self._user_models = self._build_model_lookup(self._get_user_models())
47
+ self._model = self._get_model()
48
+ self._conversations = {}
49
+
50
+ def _parse_model_name(self, name: str) -> Tuple[str, str]:
51
+ parts = name.split("/")
52
+ if len(parts) == 1:
53
+ # a user model or a org model
54
+ return None, parts[0]
55
+ if len(parts) == 2:
56
+ return parts[0], parts[1]
57
+ raise ValueError(
58
+ f"Model name must be in the format `organization/model_name` or `model_name`, but got '{name}'."
59
+ )
60
+
61
+ def _build_model_lookup(self, endpoints: List[str]) -> Dict[str, Set[str]]:
62
+ result = {}
63
+ for endpoint in endpoints:
64
+ result.setdefault(endpoint.model, []).append(endpoint)
65
+ return result
66
+
67
+ def _get_public_models(self) -> List[str]:
68
+ return self._llm_api.get_public_models()
69
+
70
+ def _get_org_models(self) -> List[str]:
71
+ return self._llm_api.get_org_models(self._org.id) if self._org else []
72
+
73
+ def _get_user_models(self) -> List[str]:
74
+ return self._llm_api.get_user_models(self._user.id) if self._user else []
75
+
76
+ def _get_model(self) -> V1Assistant:
77
+ # TODO how to handle multiple models with same model type? For now, just use the first one
78
+ if self._model_name in self._public_models:
79
+ return self._public_models.get(self._model_name)[0]
80
+ if self._model_name in self._org_models:
81
+ return self._org_models.get(self._model_name)[0]
82
+ if self._model_name in self._user_models:
83
+ return self._user_models.get(self._model_name)[0]
84
+
85
+ available_models = []
86
+ if self._public_models:
87
+ available_models.append(f"Public Models: {', '.join(self._public_models.keys())}")
88
+
89
+ if self._org and self._org_models:
90
+ available_models.append(f"Org ({self._org.name}) Models: {', '.join(self._org_models.keys())}")
91
+
92
+ if self._user and self._user_models:
93
+ available_models.append(f"User ({self._user.name}) Models: {', '.join(self._user_models.keys())}")
94
+
95
+ available_models_str = "\n".join(available_models)
96
+ raise ValueError(f"Model '{self._model_name}' not found. \nAvailable models: \n{available_models_str}")
97
+
98
+ def chat(
99
+ self,
100
+ prompt: str,
101
+ system_prompt: Optional[str] = None,
102
+ max_completion_tokens: Optional[int] = 500,
103
+ conversation: Optional[str] = None,
104
+ ) -> str:
105
+ conversation_id = self._conversations.get(conversation) if conversation else None
106
+ output = self._llm_api.start_conversation(
107
+ prompt=prompt,
108
+ system_prompt=system_prompt,
109
+ max_completion_tokens=max_completion_tokens,
110
+ assistant_id=self._model.id,
111
+ conversation_id=conversation_id,
112
+ )
113
+ if conversation and not conversation_id:
114
+ self._conversations[conversation] = output.conversation_id
115
+ return output.choices[0].delta.content
116
+
117
+ def list_conversations(self) -> List[Dict]:
118
+ return list(self._conversations.keys())
lightning_sdk/plugin.py CHANGED
@@ -395,6 +395,25 @@ class SlurmJobsPlugin(_Plugin):
395
395
  return resp
396
396
 
397
397
 
398
+ class CustomPortPlugin(_Plugin):
399
+ """Plugin handling the port of a given service."""
400
+
401
+ _plugin_run_name = "Custom Port"
402
+ _slug_name = "custom-port"
403
+
404
+ def run(self, name: Optional[str] = None, port: int = 8000) -> str:
405
+ """Starts a new port to the given Studio."""
406
+ if name is None:
407
+ name = _run_name("port")
408
+
409
+ return self._studio._studio_api.start_new_port(
410
+ teamspace_id=self._studio._teamspace.id,
411
+ studio_id=self._studio._studio.id,
412
+ name=name,
413
+ port=port,
414
+ )
415
+
416
+
398
417
  @runtime_checkable
399
418
  class _RunnablePlugin(Protocol):
400
419
  _plugin_run_name: str
lightning_sdk/serve.py CHANGED
@@ -10,7 +10,7 @@ from rich.console import Console
10
10
  from lightning_sdk import Deployment, Machine, Teamspace
11
11
  from lightning_sdk.api.deployment_api import AutoScaleConfig, DeploymentApi, Env, Secret
12
12
  from lightning_sdk.api.lit_container_api import LitContainerApi
13
- from lightning_sdk.api.utils import _get_cloud_url, _get_registry_url
13
+ from lightning_sdk.api.utils import _get_cloud_url
14
14
  from lightning_sdk.lightning_cloud.env import LIGHTNING_CLOUD_URL
15
15
 
16
16
  _DOCKER_NOT_RUNNING_MSG = (
@@ -189,12 +189,9 @@ Update [underline]{os.path.abspath("Dockerfile")}[/underline] to add any additio
189
189
  if "status" in line:
190
190
  yield {"status": line["status"].strip()}
191
191
 
192
- registry_url = _get_registry_url()
193
192
  container_basename = repository.split("/")[-1]
194
- repository = (
195
- f"{registry_url}/lit-container{f'-{cloud_account}' if cloud_account is not None else ''}/"
196
- f"{teamspace.owner.name}/{teamspace.name}/{container_basename}"
197
- )
193
+ repository = lit_cr.get_container_url(repository, tag, teamspace, cloud_account)
194
+
198
195
  yield {
199
196
  "finish": True,
200
197
  "status": "Container pushed successfully",
@@ -303,6 +300,7 @@ Update [underline]{os.path.abspath("Dockerfile")}[/underline] to add any additio
303
300
  include_credentials=include_credentials,
304
301
  cloudspace_id=cloudspace_id,
305
302
  from_onboarding=from_onboarding,
303
+ command="",
306
304
  )
307
305
 
308
306
  return {"deployment": deployment, "url": url}
lightning_sdk/studio.py CHANGED
@@ -1,7 +1,10 @@
1
+ import glob
1
2
  import os
2
3
  import warnings
3
4
  from typing import TYPE_CHECKING, Any, Dict, Mapping, Optional, Tuple, Union
4
5
 
6
+ from tqdm.auto import tqdm
7
+
5
8
  from lightning_sdk.api.studio_api import StudioApi
6
9
  from lightning_sdk.api.utils import _machine_to_compute_name
7
10
  from lightning_sdk.constants import _LIGHTNING_DEBUG
@@ -265,6 +268,34 @@ class Studio:
265
268
  progress_bar=progress_bar,
266
269
  )
267
270
 
271
+ def upload_folder(self, folder_path: str, remote_path: Optional[str] = None, progress_bar: bool = True) -> None:
272
+ """Uploads a given folder to a remote path on the Studio."""
273
+ if folder_path is None:
274
+ raise ValueError("Cannot upload a folder that is None.")
275
+ folder_path = os.path.normpath(folder_path)
276
+ if os.path.isfile(folder_path):
277
+ raise NotADirectoryError(f"Cannot upload a file as a folder. '{folder_path}' is a file.")
278
+ if not os.path.exists(folder_path):
279
+ raise NotADirectoryError(f"Cannot upload a folder that does not exist. '{folder_path}' is not a directory.")
280
+ all_files = []
281
+ for fp in glob.glob(os.path.join(folder_path, "**"), recursive=True):
282
+ if not os.path.isfile(fp):
283
+ continue
284
+ rel_path = os.path.relpath(fp, folder_path)
285
+ remote_file = os.path.join(remote_path, rel_path) if remote_path else rel_path
286
+ all_files.append((fp, remote_file))
287
+
288
+ if progress_bar:
289
+ progress_bar = tqdm(total=len(all_files), desc="Uploading files", unit="file")
290
+ for local_file, remote_path in sorted(all_files, key=lambda p: p[1]):
291
+ if progress_bar:
292
+ progress_bar.set_description(f"Uploading {local_file}")
293
+ self.upload_file(local_file, remote_path=remote_path, progress_bar=False)
294
+ if progress_bar:
295
+ progress_bar.update(1)
296
+ if progress_bar:
297
+ progress_bar.close()
298
+
268
299
  def download_file(self, remote_path: str, file_path: Optional[str] = None) -> None:
269
300
  """Downloads a file from the Studio to a given target path."""
270
301
  if file_path is None:
@@ -445,6 +476,7 @@ class Studio:
445
476
  def _add_plugin(self, plugin_name: str) -> None:
446
477
  """Adds the just installed plugin to the internal list of plugins."""
447
478
  from lightning_sdk.plugin import (
479
+ CustomPortPlugin,
448
480
  InferenceServerPlugin,
449
481
  JobsPlugin,
450
482
  MultiMachineTrainingPlugin,
@@ -458,6 +490,7 @@ class Studio:
458
490
  "jobs": JobsPlugin,
459
491
  "multi-machine-training": MultiMachineTrainingPlugin,
460
492
  "inference-server": InferenceServerPlugin,
493
+ "custom-port": CustomPortPlugin,
461
494
  }.get(plugin_name, Plugin)
462
495
 
463
496
  description = self._list_installed_plugins()[plugin_name]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lightning_sdk
3
- Version: 0.2.11
3
+ Version: 0.2.13
4
4
  Summary: SDK to develop using Lightning AI Studios
5
5
  Author-email: Lightning-AI <justus@lightning.ai>
6
6
  License: MIT License