lightning-sdk 0.2.19__py3-none-any.whl → 0.2.21__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 (62) hide show
  1. lightning_sdk/__init__.py +1 -1
  2. lightning_sdk/api/deployment_api.py +7 -2
  3. lightning_sdk/api/license_api.py +28 -6
  4. lightning_sdk/api/llm_api.py +118 -26
  5. lightning_sdk/api/studio_api.py +5 -0
  6. lightning_sdk/cli/configure.py +34 -27
  7. lightning_sdk/cli/connect.py +2 -2
  8. lightning_sdk/cli/deploy/_auth.py +11 -19
  9. lightning_sdk/cli/entrypoint.py +20 -2
  10. lightning_sdk/deployment/deployment.py +17 -3
  11. lightning_sdk/lightning_cloud/login.py +2 -2
  12. lightning_sdk/lightning_cloud/openapi/__init__.py +2 -3
  13. lightning_sdk/lightning_cloud/openapi/api/cluster_service_api.py +1 -5
  14. lightning_sdk/lightning_cloud/openapi/api/endpoint_service_api.py +11 -1
  15. lightning_sdk/lightning_cloud/openapi/api/jobs_service_api.py +121 -0
  16. lightning_sdk/lightning_cloud/openapi/api/user_service_api.py +0 -85
  17. lightning_sdk/lightning_cloud/openapi/models/__init__.py +2 -3
  18. lightning_sdk/lightning_cloud/openapi/models/alertingevents_id_body.py +409 -0
  19. lightning_sdk/lightning_cloud/openapi/models/id_codeconfig_body.py +29 -3
  20. lightning_sdk/lightning_cloud/openapi/models/update.py +105 -1
  21. lightning_sdk/lightning_cloud/openapi/models/v1_author.py +201 -0
  22. lightning_sdk/lightning_cloud/openapi/models/v1_blog_post.py +53 -1
  23. lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space.py +27 -1
  24. lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_environment_template.py +53 -1
  25. lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_environment_template_config.py +27 -1
  26. lightning_sdk/lightning_cloud/openapi/models/v1_cluster_deletion_options.py +27 -1
  27. lightning_sdk/lightning_cloud/openapi/models/v1_create_cloud_space_environment_template_request.py +105 -1
  28. lightning_sdk/lightning_cloud/openapi/models/v1_create_project_request.py +79 -1
  29. lightning_sdk/lightning_cloud/openapi/models/v1_data_connection.py +79 -1
  30. lightning_sdk/lightning_cloud/openapi/models/v1_deployment_alerting_policy_type.py +1 -0
  31. lightning_sdk/lightning_cloud/openapi/models/v1_get_organization_storage_metadata_response.py +79 -1
  32. lightning_sdk/lightning_cloud/openapi/models/v1_get_project_storage_metadata_response.py +105 -1
  33. lightning_sdk/lightning_cloud/openapi/models/v1_get_user_response.py +79 -1
  34. lightning_sdk/lightning_cloud/openapi/models/v1_get_user_storage_breakdown_response.py +27 -1
  35. lightning_sdk/lightning_cloud/openapi/models/v1_membership.py +27 -1
  36. lightning_sdk/lightning_cloud/openapi/models/v1_message.py +53 -1
  37. lightning_sdk/lightning_cloud/openapi/models/v1_notification_type.py +1 -0
  38. lightning_sdk/lightning_cloud/openapi/models/v1_organization.py +105 -1
  39. lightning_sdk/lightning_cloud/openapi/models/v1_project_storage.py +131 -1
  40. lightning_sdk/lightning_cloud/openapi/models/v1_routing_telemetry.py +27 -1
  41. lightning_sdk/lightning_cloud/openapi/models/v1_storage_asset.py +27 -1
  42. lightning_sdk/lightning_cloud/openapi/models/v1_storage_asset_type.py +2 -0
  43. lightning_sdk/lightning_cloud/openapi/models/v1_transaction.py +27 -1
  44. lightning_sdk/lightning_cloud/openapi/models/v1_update_user_request.py +79 -1
  45. lightning_sdk/lightning_cloud/openapi/models/v1_usage.py +27 -27
  46. lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +79 -443
  47. lightning_sdk/lightning_cloud/openapi/models/v1_volume.py +499 -31
  48. lightning_sdk/lightning_cloud/rest_client.py +13 -11
  49. lightning_sdk/lightning_cloud/source_code/logs_socket_api.py +8 -3
  50. lightning_sdk/llm/llm.py +52 -1
  51. lightning_sdk/pipeline/pipeline.py +1 -1
  52. lightning_sdk/services/license.py +78 -22
  53. lightning_sdk/services/utilities.py +15 -1
  54. {lightning_sdk-0.2.19.dist-info → lightning_sdk-0.2.21.dist-info}/METADATA +1 -1
  55. {lightning_sdk-0.2.19.dist-info → lightning_sdk-0.2.21.dist-info}/RECORD +59 -60
  56. lightning_sdk/lightning_cloud/openapi/models/v1_ebs.py +0 -279
  57. lightning_sdk/lightning_cloud/openapi/models/v1_get_user_storage_response.py +0 -201
  58. lightning_sdk/lightning_cloud/openapi/models/v1_reservation_billing_session.py +0 -279
  59. {lightning_sdk-0.2.19.dist-info → lightning_sdk-0.2.21.dist-info}/LICENSE +0 -0
  60. {lightning_sdk-0.2.19.dist-info → lightning_sdk-0.2.21.dist-info}/WHEEL +0 -0
  61. {lightning_sdk-0.2.19.dist-info → lightning_sdk-0.2.21.dist-info}/entry_points.txt +0 -0
  62. {lightning_sdk-0.2.19.dist-info → lightning_sdk-0.2.21.dist-info}/top_level.txt +0 -0
lightning_sdk/llm/llm.py CHANGED
@@ -1,6 +1,6 @@
1
1
  import os
2
2
  import warnings
3
- from typing import Dict, Generator, List, Optional, Set, Tuple, Union
3
+ from typing import AsyncGenerator, Dict, Generator, List, Optional, Set, Tuple, Union
4
4
 
5
5
  from lightning_sdk.api.llm_api import LLMApi
6
6
  from lightning_sdk.cli.teamspace_menu import _TeamspacesMenu
@@ -18,6 +18,7 @@ class LLM:
18
18
  self,
19
19
  name: str,
20
20
  teamspace: Optional[str] = None,
21
+ enable_async: Optional[bool] = False,
21
22
  ) -> None:
22
23
  """Initializes the LLM instance with teamspace information, which is required for billing purposes.
23
24
 
@@ -30,6 +31,7 @@ class LLM:
30
31
  name (str): The name of the model or resource.
31
32
  teamspace (Optional[str]): The specified teamspace for billing. If not provided, it will be resolved
32
33
  through the above methods.
34
+ enable_async (Optional[bool]): Enable async requests
33
35
 
34
36
  Raises:
35
37
  ValueError: If teamspace information cannot be resolved.
@@ -67,6 +69,7 @@ class LLM:
67
69
  self._model_provider, self._model_name = self._parse_model_name(name)
68
70
 
69
71
  self._llm_api = LLMApi()
72
+ self._enable_async = enable_async
70
73
 
71
74
  try:
72
75
  # check if it is a org model
@@ -174,6 +177,41 @@ class LLM:
174
177
  for line in result:
175
178
  yield line.choices[0].delta.content
176
179
 
180
+ async def _async_stream_text(self, output: str) -> AsyncGenerator[str, None]:
181
+ async for chunk in output:
182
+ if chunk.choices and chunk.choices[0].delta:
183
+ yield chunk.choices[0].delta.content
184
+
185
+ async def _async_chat(
186
+ self,
187
+ prompt: str,
188
+ system_prompt: Optional[str] = None,
189
+ max_completion_tokens: Optional[int] = 500,
190
+ images: Optional[Union[List[str], str]] = None,
191
+ conversation: Optional[str] = None,
192
+ metadata: Optional[Dict[str, str]] = None,
193
+ stream: bool = False,
194
+ upload_local_images: bool = False,
195
+ ) -> Union[str, AsyncGenerator[str, None]]:
196
+ conversation_id = self._conversations.get(conversation) if conversation else None
197
+ output = await self._llm_api.async_start_conversation(
198
+ prompt=prompt,
199
+ system_prompt=system_prompt,
200
+ max_completion_tokens=max_completion_tokens,
201
+ images=images,
202
+ assistant_id=self._model.id,
203
+ conversation_id=conversation_id,
204
+ billing_project_id=self._teamspace.id,
205
+ metadata=metadata,
206
+ name=conversation,
207
+ stream=stream,
208
+ )
209
+ if not stream:
210
+ if conversation and not conversation_id:
211
+ self._conversations[conversation] = output.conversation_id
212
+ return output.choices[0].delta.content
213
+ return self._async_stream_text(output)
214
+
177
215
  def chat(
178
216
  self,
179
217
  prompt: str,
@@ -198,6 +236,19 @@ class LLM:
198
236
  self._teamspace.upload_file(file_path=image, remote_path=f"images/{os.path.basename(image)}")
199
237
 
200
238
  conversation_id = self._conversations.get(conversation) if conversation else None
239
+
240
+ if self._enable_async:
241
+ return self._async_chat(
242
+ prompt,
243
+ system_prompt,
244
+ max_completion_tokens,
245
+ images,
246
+ conversation,
247
+ metadata,
248
+ stream,
249
+ upload_local_images,
250
+ )
251
+
201
252
  output = self._llm_api.start_conversation(
202
253
  prompt=prompt,
203
254
  system_prompt=system_prompt,
@@ -63,7 +63,7 @@ class Pipeline:
63
63
  pipeline = None
64
64
 
65
65
  if name.startswith("pip_"):
66
- pipeline = self._pipeline_api.get_pipeline_by_id(name, self._teamspace.id)
66
+ pipeline = self._pipeline_api.get_pipeline_by_id(self._teamspace.id, name)
67
67
 
68
68
  if pipeline:
69
69
  self._name = pipeline.name
@@ -9,9 +9,74 @@ from importlib import metadata
9
9
  from pathlib import Path
10
10
  from typing import Optional, Tuple
11
11
 
12
- from lightning_sdk.api.license_api import LicenseApi
12
+ from lightning_sdk.api.license_api import LICENSE_SIGNING_URL, LicenseApi, generate_url_user_settings
13
13
  from lightning_sdk.lightning_cloud.login import Auth
14
14
 
15
+ MESSAGE_NOT_AUTHENTICATED = (
16
+ "┌─────────────────────────────────────────────────────────────────────────┐\n"
17
+ "│ ⚠️ No authenticated user found or license API is not available. │\n"
18
+ "│ │\n"
19
+ "│ Please make sure you are logged in and have a valid license. │\n"
20
+ "│ If you're not logged in, you can use the following command: │\n"
21
+ "│ │\n"
22
+ "│ lightning login │\n"
23
+ "└─────────────────────────────────────────────────────────────────────────┘"
24
+ )
25
+ MESSAGE_AUTH_NO_LICENSE = (
26
+ "┌──────────────────────────────────────────────────────────────────────────────────────────────┐\n"
27
+ "│ ⚠️ No valid license found for the authenticated user for product '{self.product_name}'. │\n"
28
+ "│ │\n"
29
+ "│ Please ensure you have an approved license for this product. │\n"
30
+ "│ If you believe this is an error, please contact support. │\n"
31
+ "│ │\n"
32
+ "│ You can review or update your license settings here: │\n"
33
+ f"│ {LICENSE_SIGNING_URL:<89}│\n"
34
+ "└──────────────────────────────────────────────────────────────────────────────────────────────┘"
35
+ )
36
+ MESSAGE_GUIDE_SIGN_LICENSE = (
37
+ "┌───────────────────────────────────────────────────────────────────────────────────────────────────────────┐\n"
38
+ "│ ⚠️ {reason} │\n"
39
+ "│ Details: license key ({license_strats}...{license_ends}) for package {package_name:<56} │\n"
40
+ "│ Please make sure you have signed the license agreement and set the license key. │\n"
41
+ "│ │\n"
42
+ f"│ Sign the license agreement here (if you dont have Lightning account, you will asked to create one): │\n"
43
+ f"│ {generate_url_user_settings():<102}\n"
44
+ "│ │\n"
45
+ "│ Once you have the license key, you may need to reinstall this package to activate it. Use the commands: │\n"
46
+ "│ │\n"
47
+ "│ export LIGHTNING_LICENSE_KEY=<your_license_key> │\n"
48
+ "│ pip install --force-reinstall --no-deps {package_name:<63} │\n"
49
+ "│ │\n"
50
+ "│ For more information, please refer to the documentation. │\n"
51
+ "└───────────────────────────────────────────────────────────────────────────────────────────────────────────┘"
52
+ )
53
+
54
+
55
+ def generate_message_guide_sign_license(package_name: str, reason: str, license_key: str = "") -> str:
56
+ """Generate a default message for signing the license agreement."""
57
+ if not license_key:
58
+ license_key = "." * 64
59
+ return MESSAGE_GUIDE_SIGN_LICENSE.format(
60
+ package_name=package_name,
61
+ reason=reason.ljust(102, " "),
62
+ license_strats=license_key[:5],
63
+ license_ends=license_key[-5:],
64
+ )
65
+
66
+
67
+ MESSAGE_WITH_KEY_OFFLINE = (
68
+ "┌──────────────────────────────────────────────────────────────────────────────────────┐\n"
69
+ "│ ⚠️ License key ({license_strats}...{license_ends}) is set, but the system is offline. │\n"
70
+ "│ │\n"
71
+ "│ Please ensure your license key is valid and that the system is connected online. │\n"
72
+ "└──────────────────────────────────────────────────────────────────────────────────────┘"
73
+ )
74
+
75
+
76
+ def generate_message_with_key_offline(license_key: str) -> str:
77
+ """Generate a message for when the license key is set but the system is offline."""
78
+ return MESSAGE_WITH_KEY_OFFLINE.format(license_strats=license_key[:5], license_ends=license_key[-5:])
79
+
15
80
 
16
81
  class LightningLicense:
17
82
  """This class is used to manage the license for the Lightning SDK."""
@@ -48,9 +113,6 @@ class LightningLicense:
48
113
  """Validate the license key."""
49
114
  if not self.is_online():
50
115
  raise ConnectionError("No internet connection.")
51
- if not self.license_api:
52
- # todo: this need to be exposed with public API
53
- raise RuntimeError("License validation is allowed only for authenticated users.")
54
116
 
55
117
  return self.license_api.valid_license(
56
118
  license_key=self.license_key,
@@ -76,10 +138,7 @@ class LightningLicense:
76
138
  if not user_id:
77
139
  return False
78
140
  if not self.license_api:
79
- self._stream_messages(
80
- "No authenticated user found or license API is not available."
81
- " Please make sure you are logged in and have a valid license."
82
- )
141
+ self._stream_messages(MESSAGE_NOT_AUTHENTICATED)
83
142
  return False
84
143
 
85
144
  licenses = self.license_api.list_user_licenses(user_id=user_id)
@@ -90,10 +149,7 @@ class LightningLicense:
90
149
  and license_info.is_valid
91
150
  ):
92
151
  return True
93
- self._stream_messages(
94
- f"No valid license found for authenticated user for product {self.product_name}."
95
- " Please make sure you have a approved product license or contact support."
96
- )
152
+ self._stream_messages(MESSAGE_AUTH_NO_LICENSE)
97
153
  return False
98
154
 
99
155
  @staticmethod
@@ -143,15 +199,13 @@ class LightningLicense:
143
199
  else:
144
200
  self._is_valid = self._check_user_license(user_id=user_id)
145
201
  elif self.license_key:
146
- self._stream_messages(
147
- f"License key ({self.license_key[:5]}...{self.license_key[-5:]}) is set but the system is offline."
148
- " Please make sure you have a valid license key and the system is online."
149
- )
202
+ self._stream_messages(generate_message_with_key_offline(self.license_key))
150
203
  else:
151
204
  self._stream_messages(
152
- "License key is not set neither cannot be found in the package root or user home."
153
- " Please make sure you have signed the license agreement and set the license key."
154
- " For more information, please refer to the documentation.",
205
+ generate_message_guide_sign_license(
206
+ package_name=self.product_name,
207
+ reason="License key is not set neither cannot be found in the package root or user home.",
208
+ )
155
209
  )
156
210
 
157
211
  return self._is_valid
@@ -265,9 +319,11 @@ def check_license(
265
319
  )
266
320
  if lit_license.is_valid is False:
267
321
  stream_messages(
268
- f"License key for `{lit_license.product_name}` is not valid.\n"
269
- f" Key: {lit_license.license_key}\n"
270
- " Please make sure you have a valid license key."
322
+ generate_message_guide_sign_license(
323
+ package_name=lit_license.product_name,
324
+ reason="License key is not valid or not set.",
325
+ license_key=lit_license.license_key,
326
+ )
271
327
  )
272
328
 
273
329
 
@@ -35,7 +35,9 @@ def _get_project(client: LightningClient, project_name: Optional[str] = None) ->
35
35
  raise ValueError("No valid projects found. Please reach out to lightning.ai team to create a project")
36
36
 
37
37
 
38
- def _get_cluster(client: LightningClient, project_id: str, cluster_id: Optional[str] = None) -> V1ProjectClusterBinding:
38
+ def _get_cluster(
39
+ client: LightningClient, project_id: str, cluster_id: Optional[str] = None, allow_neoclouds: bool = False
40
+ ) -> V1ProjectClusterBinding:
39
41
  """Get a project membership for the user from the backend."""
40
42
  clusters = client.projects_service_list_project_cluster_bindings(project_id=project_id)
41
43
  if cluster_id:
@@ -47,6 +49,18 @@ def _get_cluster(client: LightningClient, project_id: str, cluster_id: Optional[
47
49
  f"Found {[c.cluster_id for c in clusters.clusters]}."
48
50
  )
49
51
 
52
+ # filter neoclouds out
53
+ if not allow_neoclouds:
54
+ cluster_objs = client.cluster_service_list_clusters(project_id=project_id)
55
+ # filter for aws or gcp cluster
56
+ valid_clusters = filter(
57
+ lambda c: c.spec.aws_v1 is not None or c.spec.google_cloud_v1 is not None, cluster_objs.clusters
58
+ )
59
+ valid_clusters = {c.id for c in valid_clusters}
60
+
61
+ if len(valid_clusters):
62
+ clusters.clusters = list(filter(lambda c: c.cluster_id in valid_clusters, clusters.clusters))
63
+
50
64
  clusters = sorted(clusters.clusters, key=lambda x: x.created_at)
51
65
  if len(clusters):
52
66
  return clusters[0]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lightning_sdk
3
- Version: 0.2.19
3
+ Version: 0.2.21
4
4
  Summary: SDK to develop using Lightning AI Studios
5
5
  Author-email: Lightning-AI <justus@lightning.ai>
6
6
  License: MIT License