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.
- lightning_sdk/__init__.py +1 -1
- lightning_sdk/api/deployment_api.py +7 -2
- lightning_sdk/api/license_api.py +28 -6
- lightning_sdk/api/llm_api.py +118 -26
- lightning_sdk/api/studio_api.py +5 -0
- lightning_sdk/cli/configure.py +34 -27
- lightning_sdk/cli/connect.py +2 -2
- lightning_sdk/cli/deploy/_auth.py +11 -19
- lightning_sdk/cli/entrypoint.py +20 -2
- lightning_sdk/deployment/deployment.py +17 -3
- lightning_sdk/lightning_cloud/login.py +2 -2
- lightning_sdk/lightning_cloud/openapi/__init__.py +2 -3
- lightning_sdk/lightning_cloud/openapi/api/cluster_service_api.py +1 -5
- lightning_sdk/lightning_cloud/openapi/api/endpoint_service_api.py +11 -1
- lightning_sdk/lightning_cloud/openapi/api/jobs_service_api.py +121 -0
- lightning_sdk/lightning_cloud/openapi/api/user_service_api.py +0 -85
- lightning_sdk/lightning_cloud/openapi/models/__init__.py +2 -3
- lightning_sdk/lightning_cloud/openapi/models/alertingevents_id_body.py +409 -0
- lightning_sdk/lightning_cloud/openapi/models/id_codeconfig_body.py +29 -3
- lightning_sdk/lightning_cloud/openapi/models/update.py +105 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_author.py +201 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_blog_post.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_environment_template.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_environment_template_config.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_deletion_options.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_create_cloud_space_environment_template_request.py +105 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_create_project_request.py +79 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_data_connection.py +79 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_deployment_alerting_policy_type.py +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_get_organization_storage_metadata_response.py +79 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_get_project_storage_metadata_response.py +105 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_get_user_response.py +79 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_get_user_storage_breakdown_response.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_membership.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_message.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_notification_type.py +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_organization.py +105 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_project_storage.py +131 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_routing_telemetry.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_storage_asset.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_storage_asset_type.py +2 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_transaction.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_update_user_request.py +79 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_usage.py +27 -27
- lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +79 -443
- lightning_sdk/lightning_cloud/openapi/models/v1_volume.py +499 -31
- lightning_sdk/lightning_cloud/rest_client.py +13 -11
- lightning_sdk/lightning_cloud/source_code/logs_socket_api.py +8 -3
- lightning_sdk/llm/llm.py +52 -1
- lightning_sdk/pipeline/pipeline.py +1 -1
- lightning_sdk/services/license.py +78 -22
- lightning_sdk/services/utilities.py +15 -1
- {lightning_sdk-0.2.19.dist-info → lightning_sdk-0.2.21.dist-info}/METADATA +1 -1
- {lightning_sdk-0.2.19.dist-info → lightning_sdk-0.2.21.dist-info}/RECORD +59 -60
- lightning_sdk/lightning_cloud/openapi/models/v1_ebs.py +0 -279
- lightning_sdk/lightning_cloud/openapi/models/v1_get_user_storage_response.py +0 -201
- lightning_sdk/lightning_cloud/openapi/models/v1_reservation_billing_session.py +0 -279
- {lightning_sdk-0.2.19.dist-info → lightning_sdk-0.2.21.dist-info}/LICENSE +0 -0
- {lightning_sdk-0.2.19.dist-info → lightning_sdk-0.2.21.dist-info}/WHEEL +0 -0
- {lightning_sdk-0.2.19.dist-info → lightning_sdk-0.2.21.dist-info}/entry_points.txt +0 -0
- {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(
|
|
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
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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(
|
|
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]
|