lightning-sdk 0.2.11__py3-none-any.whl → 0.2.12__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 +35 -3
- lightning_sdk/api/lit_container_api.py +13 -7
- lightning_sdk/api/llm_api.py +34 -0
- lightning_sdk/cli/serve.py +72 -23
- lightning_sdk/deployment/deployment.py +51 -5
- lightning_sdk/lightning_cloud/openapi/__init__.py +5 -1
- lightning_sdk/lightning_cloud/openapi/api/cloud_space_environment_template_service_api.py +4 -4
- lightning_sdk/lightning_cloud/openapi/api/cluster_service_api.py +13 -1
- lightning_sdk/lightning_cloud/openapi/api/data_connection_service_api.py +4 -4
- lightning_sdk/lightning_cloud/openapi/api/jobs_service_api.py +115 -0
- lightning_sdk/lightning_cloud/openapi/models/__init__.py +5 -1
- lightning_sdk/lightning_cloud/openapi/models/assistant_id_conversations_body.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/create_deployment_request_defines_a_spec_for_the_job_that_allows_for_autoscaling_jobs.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/orgs_id_body.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/projects_id_body.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/update.py +65 -195
- lightning_sdk/lightning_cloud/openapi/models/update1.py +357 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_provider.py +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_environment_template.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_create_deployment_request.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_delete_cloud_space_environment_template_response.py +1 -53
- lightning_sdk/lightning_cloud/openapi/models/v1_get_user_response.py +1 -27
- lightning_sdk/lightning_cloud/openapi/models/v1_job_resource.py +279 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_job_type.py +108 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_job_resources_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_organization.py +55 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_project_settings.py +29 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_reservation_billing_session.py +279 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_resources.py +55 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_update_user_request.py +1 -27
- lightning_sdk/lightning_cloud/openapi/models/v1_usage.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +105 -1
- lightning_sdk/llm/__init__.py +3 -0
- lightning_sdk/llm/llm.py +56 -0
- lightning_sdk/serve.py +4 -6
- {lightning_sdk-0.2.11.dist-info → lightning_sdk-0.2.12.dist-info}/METADATA +1 -1
- {lightning_sdk-0.2.11.dist-info → lightning_sdk-0.2.12.dist-info}/RECORD +42 -35
- lightning_sdk/lightning_cloud/openapi/models/environmenttemplates_id_body.py +0 -253
- {lightning_sdk-0.2.11.dist-info → lightning_sdk-0.2.12.dist-info}/LICENSE +0 -0
- {lightning_sdk-0.2.11.dist-info → lightning_sdk-0.2.12.dist-info}/WHEEL +0 -0
- {lightning_sdk-0.2.11.dist-info → lightning_sdk-0.2.12.dist-info}/entry_points.txt +0 -0
- {lightning_sdk-0.2.11.dist-info → lightning_sdk-0.2.12.dist-info}/top_level.txt +0 -0
lightning_sdk/__init__.py
CHANGED
|
@@ -460,7 +460,7 @@ def to_autoscaling(
|
|
|
460
460
|
if target_metrics is None and (threshold < 0 or threshold > 100):
|
|
461
461
|
raise ValueError("The autoscaling threshold should be defined between 0 and 100.")
|
|
462
462
|
|
|
463
|
-
if target_metrics is not None and len(target_metrics) == 0:
|
|
463
|
+
if target_metrics is not None and len(target_metrics) == 0 and metric is None:
|
|
464
464
|
raise ValueError("The target_metrics must be provided.")
|
|
465
465
|
|
|
466
466
|
if target_metrics is not None:
|
|
@@ -524,8 +524,14 @@ def to_endpoint(
|
|
|
524
524
|
def to_health_check(
|
|
525
525
|
health_check: Optional[Union[HttpHealthCheck, ExecHealthCheck]] = None
|
|
526
526
|
) -> Optional[V1JobHealthCheckConfig]:
|
|
527
|
+
# Use Default health check if none is provided
|
|
527
528
|
if not health_check:
|
|
528
|
-
return
|
|
529
|
+
return V1JobHealthCheckConfig(
|
|
530
|
+
failure_threshold=600,
|
|
531
|
+
initial_delay_seconds=0,
|
|
532
|
+
interval_seconds=1,
|
|
533
|
+
timeout_seconds=600,
|
|
534
|
+
)
|
|
529
535
|
|
|
530
536
|
health_check_config = V1JobHealthCheckConfig(
|
|
531
537
|
failure_threshold=health_check.failure_threshold,
|
|
@@ -555,6 +561,7 @@ def to_spec(
|
|
|
555
561
|
health_check: Optional[Union[HttpHealthCheck, ExecHealthCheck]] = None,
|
|
556
562
|
quantity: Optional[int] = None,
|
|
557
563
|
include_credentials: Optional[bool] = None,
|
|
564
|
+
cloudspace_id: Optional[None] = None,
|
|
558
565
|
) -> V1JobSpec:
|
|
559
566
|
if cloud_account is None:
|
|
560
567
|
raise ValueError("The cloud account should be defined.")
|
|
@@ -562,9 +569,15 @@ def to_spec(
|
|
|
562
569
|
if machine is None:
|
|
563
570
|
raise ValueError("The machine should be defined.")
|
|
564
571
|
|
|
565
|
-
if image is None:
|
|
572
|
+
if image is None and cloudspace_id is None:
|
|
566
573
|
raise ValueError("The image should be defined.")
|
|
567
574
|
|
|
575
|
+
if entrypoint is not None and cloudspace_id is not None:
|
|
576
|
+
raise ValueError("The entrypoint shouldn't be defined when a Studio is provided.")
|
|
577
|
+
|
|
578
|
+
if command is None and cloudspace_id is not None:
|
|
579
|
+
raise ValueError("The command should be defined.")
|
|
580
|
+
|
|
568
581
|
return V1JobSpec(
|
|
569
582
|
cluster_id=cloud_account,
|
|
570
583
|
command=command,
|
|
@@ -576,6 +589,7 @@ def to_spec(
|
|
|
576
589
|
readiness_probe=to_health_check(health_check),
|
|
577
590
|
quantity=quantity,
|
|
578
591
|
include_credentials=include_credentials,
|
|
592
|
+
cloudspace_id=cloudspace_id,
|
|
579
593
|
)
|
|
580
594
|
|
|
581
595
|
|
|
@@ -600,3 +614,21 @@ def apply_change(spec: Any, key: str, value: Any) -> bool:
|
|
|
600
614
|
return True
|
|
601
615
|
|
|
602
616
|
return False
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
def compose_commands(commands: List[str]) -> str:
|
|
620
|
+
composite_command = []
|
|
621
|
+
|
|
622
|
+
for command in commands:
|
|
623
|
+
command = command.strip()
|
|
624
|
+
|
|
625
|
+
# Check if the command already has '&'
|
|
626
|
+
if command.endswith("&"):
|
|
627
|
+
# It's a background command, add it as a subshell without further adjustment
|
|
628
|
+
composite_command.append(f"( {command} )")
|
|
629
|
+
else:
|
|
630
|
+
# Sequential execution, add as-is and use `&&` to connect if followed by another command
|
|
631
|
+
composite_command.append(command)
|
|
632
|
+
|
|
633
|
+
# Joining commands, using `&&` between sequential parts and respecting subshell backgrounds
|
|
634
|
+
return " && ".join(composite_command)
|
|
@@ -125,6 +125,17 @@ class LitContainerApi:
|
|
|
125
125
|
except Exception as e:
|
|
126
126
|
raise ValueError(f"Could not delete container {container} from project {project_id}: {e!s}") from e
|
|
127
127
|
|
|
128
|
+
def get_container_url(
|
|
129
|
+
self, repository: str, tag: str, teamspace: Teamspace, cloud_account: Optional[str] = None
|
|
130
|
+
) -> str:
|
|
131
|
+
"""Docker container will be pushed to the URL returned from this function."""
|
|
132
|
+
registry_url = _get_registry_url()
|
|
133
|
+
container_basename = repository.split("/")[-1]
|
|
134
|
+
return (
|
|
135
|
+
f"{registry_url}/lit-container{f'-{cloud_account}' if cloud_account is not None else ''}/"
|
|
136
|
+
f"{teamspace.owner.name}/{teamspace.name}/{container_basename}"
|
|
137
|
+
)
|
|
138
|
+
|
|
128
139
|
@retry_on_lcr_auth_failure
|
|
129
140
|
def upload_container(
|
|
130
141
|
self,
|
|
@@ -147,7 +158,6 @@ class LitContainerApi:
|
|
|
147
158
|
Named cloud-account in the CLI options.
|
|
148
159
|
:param platform: If empty will be linux/amd64. This is important because our entire deployment infra runs on
|
|
149
160
|
linux/amd64. Will show user a warning otherwise.
|
|
150
|
-
:return_final_dict: Controls whether we respond with the dictionary containing metadata about container upload
|
|
151
161
|
:return: Generator[dict, None, dict]
|
|
152
162
|
"""
|
|
153
163
|
try:
|
|
@@ -163,18 +173,14 @@ class LitContainerApi:
|
|
|
163
173
|
except Exception as e:
|
|
164
174
|
raise ValueError(f"Unable to upload {container}:{tag}") from e
|
|
165
175
|
|
|
166
|
-
|
|
167
|
-
container_basename = container.split("/")[-1]
|
|
168
|
-
repository = (
|
|
169
|
-
f"{registry_url}/lit-container{f'-{cloud_account}' if cloud_account is not None else ''}/"
|
|
170
|
-
f"{teamspace.owner.name}/{teamspace.name}/{container_basename}"
|
|
171
|
-
)
|
|
176
|
+
repository = self.get_container_url(container, tag, teamspace, cloud_account)
|
|
172
177
|
tagged = self._docker_client.api.tag(f"{container}:{tag}", repository, tag)
|
|
173
178
|
if not tagged:
|
|
174
179
|
raise ValueError(f"Could not tag container {container}:{tag} with {repository}:{tag}")
|
|
175
180
|
yield from self._push_with_retry(repository, tag=tag)
|
|
176
181
|
|
|
177
182
|
if return_final_dict:
|
|
183
|
+
container_basename = repository.split("/")[-1]
|
|
178
184
|
yield {
|
|
179
185
|
"finish": True,
|
|
180
186
|
"url": f"{LIGHTNING_CLOUD_URL}/{teamspace.owner.name}/{teamspace.name}/containers/"
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
|
|
3
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_conversation_response_chunk import V1ConversationResponseChunk
|
|
4
|
+
from lightning_sdk.lightning_cloud.rest_client import LightningClient
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class LLMApi:
|
|
8
|
+
def __init__(self) -> None:
|
|
9
|
+
self._client = LightningClient(retry=False, max_tries=0)
|
|
10
|
+
|
|
11
|
+
def list_models(self) -> List[str]:
|
|
12
|
+
result = self._client.assistants_service_list_assistant_managed_endpoints()
|
|
13
|
+
return result.endpoints
|
|
14
|
+
|
|
15
|
+
def get_public_models(self) -> List[str]:
|
|
16
|
+
result = self._client.assistants_service_list_assistants(published=True)
|
|
17
|
+
return result.assistants
|
|
18
|
+
|
|
19
|
+
def start_conversation(
|
|
20
|
+
self, prompt: str, system_prompt: Optional[str], assistant_id: str
|
|
21
|
+
) -> V1ConversationResponseChunk:
|
|
22
|
+
body = {
|
|
23
|
+
"message": {
|
|
24
|
+
"author": {"role": "user"},
|
|
25
|
+
"content": [
|
|
26
|
+
{
|
|
27
|
+
"contentType": "text",
|
|
28
|
+
"parts": [prompt],
|
|
29
|
+
}
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
result = self._client.assistants_service_start_conversation(body, assistant_id)
|
|
34
|
+
return result.result
|
lightning_sdk/cli/serve.py
CHANGED
|
@@ -6,6 +6,7 @@ import webbrowser
|
|
|
6
6
|
from datetime import datetime
|
|
7
7
|
from enum import Enum
|
|
8
8
|
from pathlib import Path
|
|
9
|
+
from threading import Thread
|
|
9
10
|
from typing import List, Optional, TypedDict, Union
|
|
10
11
|
from urllib.parse import urlencode
|
|
11
12
|
|
|
@@ -17,6 +18,7 @@ from rich.prompt import Confirm
|
|
|
17
18
|
from lightning_sdk import Machine, Teamspace
|
|
18
19
|
from lightning_sdk.api import UserApi
|
|
19
20
|
from lightning_sdk.api.lit_container_api import LitContainerApi
|
|
21
|
+
from lightning_sdk.api.utils import _get_registry_url
|
|
20
22
|
from lightning_sdk.cli.teamspace_menu import _TeamspacesMenu
|
|
21
23
|
from lightning_sdk.lightning_cloud import env
|
|
22
24
|
from lightning_sdk.lightning_cloud.login import Auth, AuthServer
|
|
@@ -27,6 +29,7 @@ from lightning_sdk.utils.resolve import _get_authed_user, _resolve_teamspace
|
|
|
27
29
|
|
|
28
30
|
_MACHINE_VALUES = tuple([machine.name for machine in Machine.__dict__.values() if isinstance(machine, Machine)])
|
|
29
31
|
_POLL_TIMEOUT = 600
|
|
32
|
+
LITSERVE_CODE = os.environ.get("LITSERVE_CODE", "j39bzk903h")
|
|
30
33
|
|
|
31
34
|
|
|
32
35
|
class _ServeGroup(click.Group):
|
|
@@ -233,7 +236,7 @@ def api_impl(
|
|
|
233
236
|
class _AuthServer(AuthServer):
|
|
234
237
|
def get_auth_url(self, port: int) -> str:
|
|
235
238
|
redirect_uri = f"http://localhost:{port}/login-complete"
|
|
236
|
-
params = urlencode({"redirectTo": redirect_uri, "
|
|
239
|
+
params = urlencode({"redirectTo": redirect_uri, "okbhrt": LITSERVE_CODE})
|
|
237
240
|
return f"{env.LIGHTNING_CLOUD_URL}/sign-in?{params}"
|
|
238
241
|
|
|
239
242
|
|
|
@@ -398,6 +401,38 @@ def is_connected(host: str = "8.8.8.8", port: int = 53, timeout: int = 10) -> bo
|
|
|
398
401
|
return False
|
|
399
402
|
|
|
400
403
|
|
|
404
|
+
def _upload_container(
|
|
405
|
+
console: Console,
|
|
406
|
+
ls_deployer: _LitServeDeployer,
|
|
407
|
+
repository: str,
|
|
408
|
+
tag: str,
|
|
409
|
+
resolved_teamspace: Teamspace,
|
|
410
|
+
lit_cr: LitContainerApi,
|
|
411
|
+
cloud_account: Optional[str],
|
|
412
|
+
) -> bool:
|
|
413
|
+
with Progress(
|
|
414
|
+
SpinnerColumn(),
|
|
415
|
+
TextColumn("[progress.description]{task.description}"),
|
|
416
|
+
TimeElapsedColumn(),
|
|
417
|
+
console=console,
|
|
418
|
+
transient=True,
|
|
419
|
+
) as progress:
|
|
420
|
+
try:
|
|
421
|
+
push_task = progress.add_task("Uploading container to Lightning registry", total=None)
|
|
422
|
+
for line in ls_deployer.push_container(
|
|
423
|
+
repository, tag, resolved_teamspace, lit_cr, cloud_account=cloud_account
|
|
424
|
+
):
|
|
425
|
+
progress.update(push_task, advance=1)
|
|
426
|
+
if not ("Pushing" in line["status"] or "Waiting" in line["status"]):
|
|
427
|
+
console.print(line["status"])
|
|
428
|
+
progress.update(push_task, description="[green]Push completed![/green]")
|
|
429
|
+
except Exception as e:
|
|
430
|
+
console.print(f"❌ Deployment failed: {e}", style="red")
|
|
431
|
+
return False
|
|
432
|
+
console.print(f"\n✅ Image pushed to {repository}:{tag}")
|
|
433
|
+
return True
|
|
434
|
+
|
|
435
|
+
|
|
401
436
|
def _handle_cloud(
|
|
402
437
|
script_path: Union[str, Path],
|
|
403
438
|
console: Console,
|
|
@@ -480,29 +515,43 @@ def _handle_cloud(
|
|
|
480
515
|
lit_cr = LitContainerApi()
|
|
481
516
|
lit_cr.list_containers(resolved_teamspace.id, cloud_account=cloud_account)
|
|
482
517
|
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
518
|
+
registry_url = _get_registry_url()
|
|
519
|
+
container_basename = repository.split("/")[-1]
|
|
520
|
+
image = (
|
|
521
|
+
f"{registry_url}/lit-container{f'-{cloud_account}' if cloud_account is not None else ''}/"
|
|
522
|
+
f"{resolved_teamspace.owner.name}/{resolved_teamspace.name}/{container_basename}"
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
if from_onboarding:
|
|
526
|
+
thread = Thread(
|
|
527
|
+
target=ls_deployer.run_on_cloud,
|
|
528
|
+
kwargs={
|
|
529
|
+
"deployment_name": deployment_name,
|
|
530
|
+
"image": image,
|
|
531
|
+
"teamspace": resolved_teamspace,
|
|
532
|
+
"metric": None,
|
|
533
|
+
"machine": machine,
|
|
534
|
+
"spot": interruptible,
|
|
535
|
+
"cloud_account": cloud_account,
|
|
536
|
+
"port": port,
|
|
537
|
+
"min_replica": min_replica,
|
|
538
|
+
"max_replica": max_replica,
|
|
539
|
+
"replicas": replicas,
|
|
540
|
+
"include_credentials": include_credentials,
|
|
541
|
+
"cloudspace_id": cloudspace_id,
|
|
542
|
+
"from_onboarding": from_onboarding,
|
|
543
|
+
},
|
|
544
|
+
)
|
|
545
|
+
thread.start()
|
|
546
|
+
console.print("🚀 Deployment started")
|
|
547
|
+
if not _upload_container(console, ls_deployer, repository, tag, resolved_teamspace, lit_cr, cloud_account):
|
|
548
|
+
thread.join()
|
|
503
549
|
return
|
|
504
|
-
|
|
505
|
-
|
|
550
|
+
thread.join()
|
|
551
|
+
return
|
|
552
|
+
|
|
553
|
+
if not _upload_container(console, ls_deployer, repository, tag, resolved_teamspace, lit_cr, cloud_account):
|
|
554
|
+
return
|
|
506
555
|
|
|
507
556
|
deployment_status = ls_deployer.run_on_cloud(
|
|
508
557
|
deployment_name=deployment_name,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from datetime import datetime
|
|
2
3
|
from typing import Any, Dict, List, Optional, Union
|
|
3
4
|
|
|
4
5
|
import requests
|
|
@@ -15,6 +16,7 @@ from lightning_sdk.api.deployment_api import (
|
|
|
15
16
|
ReleaseStrategy,
|
|
16
17
|
Secret,
|
|
17
18
|
TokenAuth,
|
|
19
|
+
compose_commands,
|
|
18
20
|
restore_auth,
|
|
19
21
|
restore_autoscale,
|
|
20
22
|
restore_env,
|
|
@@ -30,6 +32,7 @@ from lightning_sdk.lightning_cloud.openapi import V1Deployment
|
|
|
30
32
|
from lightning_sdk.machine import Machine
|
|
31
33
|
from lightning_sdk.organization import Organization
|
|
32
34
|
from lightning_sdk.services.utilities import _get_cluster
|
|
35
|
+
from lightning_sdk.studio import Studio
|
|
33
36
|
from lightning_sdk.teamspace import Teamspace
|
|
34
37
|
from lightning_sdk.user import User
|
|
35
38
|
from lightning_sdk.utils.resolve import _resolve_deprecated_cluster, _resolve_org, _resolve_teamspace, _resolve_user
|
|
@@ -55,7 +58,7 @@ class Deployment:
|
|
|
55
58
|
|
|
56
59
|
def __init__(
|
|
57
60
|
self,
|
|
58
|
-
name: str,
|
|
61
|
+
name: Optional[str] = None,
|
|
59
62
|
teamspace: Optional[Union[str, Teamspace]] = None,
|
|
60
63
|
org: Optional[Union[str, Organization]] = None,
|
|
61
64
|
user: Optional[Union[str, User]] = None,
|
|
@@ -72,6 +75,9 @@ class Deployment:
|
|
|
72
75
|
except ConnectionError as e:
|
|
73
76
|
raise e
|
|
74
77
|
|
|
78
|
+
if name is None:
|
|
79
|
+
name = "dep_" + datetime.now().strftime("%m-%d_%H:%M:%S")
|
|
80
|
+
|
|
75
81
|
self._name = name
|
|
76
82
|
self._user = _resolve_user(self._user or user)
|
|
77
83
|
self._org = _resolve_org(org)
|
|
@@ -102,13 +108,15 @@ class Deployment:
|
|
|
102
108
|
|
|
103
109
|
def start(
|
|
104
110
|
self,
|
|
111
|
+
studio: Optional[Union[str, Studio]] = None,
|
|
105
112
|
machine: Optional[Machine] = None,
|
|
106
113
|
image: Optional[str] = None,
|
|
107
114
|
autoscale: Optional[AutoScaleConfig] = None,
|
|
108
|
-
ports: Optional[List[float]] = None,
|
|
115
|
+
ports: Optional[Union[float, List[float]]] = None,
|
|
109
116
|
release_strategy: Optional[ReleaseStrategy] = None,
|
|
110
117
|
entrypoint: Optional[str] = None,
|
|
111
118
|
command: Optional[str] = None,
|
|
119
|
+
commands: Optional[List[str]] = None,
|
|
112
120
|
env: Union[List[Union[Secret, Env]], Dict[str, str], None] = None,
|
|
113
121
|
spot: Optional[bool] = None,
|
|
114
122
|
replicas: Optional[int] = None,
|
|
@@ -157,12 +165,45 @@ class Deployment:
|
|
|
157
165
|
if self._is_created:
|
|
158
166
|
raise RuntimeError("This deployment has already been started.")
|
|
159
167
|
|
|
160
|
-
|
|
168
|
+
if isinstance(studio, Studio):
|
|
169
|
+
cloudspace_id = studio._studio.id
|
|
170
|
+
cloud_account = studio._studio.cluster_id
|
|
171
|
+
|
|
172
|
+
if isinstance(studio, str):
|
|
173
|
+
studio = Studio(studio)
|
|
174
|
+
cloudspace_id = studio._studio.id
|
|
175
|
+
cloud_account = studio._studio.cluster_id
|
|
176
|
+
|
|
177
|
+
if cloud_account is None:
|
|
178
|
+
cloud_account = _resolve_deprecated_cluster(cloud_account, cluster)
|
|
161
179
|
|
|
162
180
|
if cloud_account is None and self._cloud_account is not None:
|
|
163
181
|
print(f"No cloud account was provided, defaulting to {self._cloud_account.cluster_id}")
|
|
164
182
|
cloud_account = os.getenv("LIGHTNING_CLUSTER_ID") or self._cloud_account.cluster_id
|
|
165
183
|
|
|
184
|
+
if isinstance(ports, float):
|
|
185
|
+
ports = [ports]
|
|
186
|
+
|
|
187
|
+
if replicas is None and autoscale is None:
|
|
188
|
+
replicas = 1
|
|
189
|
+
|
|
190
|
+
if machine is None:
|
|
191
|
+
machine = Machine.CPU
|
|
192
|
+
|
|
193
|
+
if commands is not None and command is not None:
|
|
194
|
+
raise ValueError("Commands and command are mutually exclusive")
|
|
195
|
+
|
|
196
|
+
if commands is not None:
|
|
197
|
+
command = compose_commands(commands)
|
|
198
|
+
|
|
199
|
+
if autoscale is None:
|
|
200
|
+
autoscale = AutoScaleConfig(
|
|
201
|
+
min_replicas=0,
|
|
202
|
+
max_replicas=1,
|
|
203
|
+
metric="CPU" if machine.is_cpu() else "GPU",
|
|
204
|
+
threshold=90,
|
|
205
|
+
)
|
|
206
|
+
|
|
166
207
|
self._deployment = self._deployment_api.create_deployment(
|
|
167
208
|
V1Deployment(
|
|
168
209
|
autoscaling=to_autoscaling(autoscale, replicas),
|
|
@@ -182,6 +223,7 @@ class Deployment:
|
|
|
182
223
|
health_check=health_check,
|
|
183
224
|
quantity=quantity,
|
|
184
225
|
include_credentials=include_credentials if include_credentials is not None else True,
|
|
226
|
+
cloudspace_id=cloudspace_id,
|
|
185
227
|
),
|
|
186
228
|
strategy=to_strategy(release_strategy),
|
|
187
229
|
),
|
|
@@ -199,6 +241,7 @@ class Deployment:
|
|
|
199
241
|
image: Optional[str] = None,
|
|
200
242
|
entrypoint: Optional[str] = None,
|
|
201
243
|
command: Optional[str] = None,
|
|
244
|
+
commands: Optional[List[str]] = None,
|
|
202
245
|
env: Optional[List[Union[Env, Secret]]] = None,
|
|
203
246
|
spot: Optional[bool] = None,
|
|
204
247
|
cloud_account: Optional[str] = None,
|
|
@@ -218,6 +261,9 @@ class Deployment:
|
|
|
218
261
|
) -> None:
|
|
219
262
|
cloud_account = _resolve_deprecated_cluster(cloud_account, cluster)
|
|
220
263
|
|
|
264
|
+
if command is None and commands is not None:
|
|
265
|
+
command = compose_commands(commands)
|
|
266
|
+
|
|
221
267
|
self._deployment = self._deployment_api.update_deployment(
|
|
222
268
|
self._deployment,
|
|
223
269
|
name=name or self._name,
|
|
@@ -228,8 +274,8 @@ class Deployment:
|
|
|
228
274
|
cloud_account=cloud_account,
|
|
229
275
|
machine=machine,
|
|
230
276
|
image=image,
|
|
231
|
-
entrypoint=entrypoint,
|
|
232
|
-
command=command,
|
|
277
|
+
entrypoint=entrypoint or "",
|
|
278
|
+
command=command or "",
|
|
233
279
|
ports=ports,
|
|
234
280
|
custom_domain=custom_domain,
|
|
235
281
|
auth=auth,
|
|
@@ -104,7 +104,6 @@ from lightning_sdk.lightning_cloud.openapi.models.datasets_id_body import Datase
|
|
|
104
104
|
from lightning_sdk.lightning_cloud.openapi.models.deployments_id_body import DeploymentsIdBody
|
|
105
105
|
from lightning_sdk.lightning_cloud.openapi.models.deploymenttemplates_id_body import DeploymenttemplatesIdBody
|
|
106
106
|
from lightning_sdk.lightning_cloud.openapi.models.endpoints_id_body import EndpointsIdBody
|
|
107
|
-
from lightning_sdk.lightning_cloud.openapi.models.environmenttemplates_id_body import EnvironmenttemplatesIdBody
|
|
108
107
|
from lightning_sdk.lightning_cloud.openapi.models.experiment_name_variant_name_body import ExperimentNameVariantNameBody
|
|
109
108
|
from lightning_sdk.lightning_cloud.openapi.models.externalv1_cloud_space_instance_status import Externalv1CloudSpaceInstanceStatus
|
|
110
109
|
from lightning_sdk.lightning_cloud.openapi.models.externalv1_cluster import Externalv1Cluster
|
|
@@ -214,6 +213,7 @@ from lightning_sdk.lightning_cloud.openapi.models.stream_result_of_v1_conversati
|
|
|
214
213
|
from lightning_sdk.lightning_cloud.openapi.models.stream_result_of_v1_get_long_running_command_in_cloud_space_response import StreamResultOfV1GetLongRunningCommandInCloudSpaceResponse
|
|
215
214
|
from lightning_sdk.lightning_cloud.openapi.models.studioapp_jobs_body import StudioappJobsBody
|
|
216
215
|
from lightning_sdk.lightning_cloud.openapi.models.update import Update
|
|
216
|
+
from lightning_sdk.lightning_cloud.openapi.models.update1 import Update1
|
|
217
217
|
from lightning_sdk.lightning_cloud.openapi.models.upload_id_complete_body import UploadIdCompleteBody
|
|
218
218
|
from lightning_sdk.lightning_cloud.openapi.models.upload_id_complete_body1 import UploadIdCompleteBody1
|
|
219
219
|
from lightning_sdk.lightning_cloud.openapi.models.upload_id_parts_body import UploadIdPartsBody
|
|
@@ -556,8 +556,10 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_job_health_check_config imp
|
|
|
556
556
|
from lightning_sdk.lightning_cloud.openapi.models.v1_job_log_entry import V1JobLogEntry
|
|
557
557
|
from lightning_sdk.lightning_cloud.openapi.models.v1_job_logs_page import V1JobLogsPage
|
|
558
558
|
from lightning_sdk.lightning_cloud.openapi.models.v1_job_logs_response import V1JobLogsResponse
|
|
559
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_job_resource import V1JobResource
|
|
559
560
|
from lightning_sdk.lightning_cloud.openapi.models.v1_job_spec import V1JobSpec
|
|
560
561
|
from lightning_sdk.lightning_cloud.openapi.models.v1_job_timing import V1JobTiming
|
|
562
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_job_type import V1JobType
|
|
561
563
|
from lightning_sdk.lightning_cloud.openapi.models.v1_joinable_organization import V1JoinableOrganization
|
|
562
564
|
from lightning_sdk.lightning_cloud.openapi.models.v1_keep_alive_cloud_space_instance_response import V1KeepAliveCloudSpaceInstanceResponse
|
|
563
565
|
from lightning_sdk.lightning_cloud.openapi.models.v1_knowledge_configuration import V1KnowledgeConfiguration
|
|
@@ -624,6 +626,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_list_filesystem_snowflake_r
|
|
|
624
626
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_gallery_components_response import V1ListGalleryComponentsResponse
|
|
625
627
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_gallery_lightningapps_response import V1ListGalleryLightningappsResponse
|
|
626
628
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_job_files_response import V1ListJobFilesResponse
|
|
629
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_list_job_resources_response import V1ListJobResourcesResponse
|
|
627
630
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_jobs_response import V1ListJobsResponse
|
|
628
631
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_joinable_organizations_response import V1ListJoinableOrganizationsResponse
|
|
629
632
|
from lightning_sdk.lightning_cloud.openapi.models.v1_list_lightning_run_response import V1ListLightningRunResponse
|
|
@@ -788,6 +791,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_report_restart_timings_resp
|
|
|
788
791
|
from lightning_sdk.lightning_cloud.openapi.models.v1_request_cluster_access_request import V1RequestClusterAccessRequest
|
|
789
792
|
from lightning_sdk.lightning_cloud.openapi.models.v1_request_cluster_access_response import V1RequestClusterAccessResponse
|
|
790
793
|
from lightning_sdk.lightning_cloud.openapi.models.v1_request_verification_code_response import V1RequestVerificationCodeResponse
|
|
794
|
+
from lightning_sdk.lightning_cloud.openapi.models.v1_reservation_billing_session import V1ReservationBillingSession
|
|
791
795
|
from lightning_sdk.lightning_cloud.openapi.models.v1_reservation_details import V1ReservationDetails
|
|
792
796
|
from lightning_sdk.lightning_cloud.openapi.models.v1_resource_tag import V1ResourceTag
|
|
793
797
|
from lightning_sdk.lightning_cloud.openapi.models.v1_resource_visibility import V1ResourceVisibility
|
|
@@ -431,7 +431,7 @@ class CloudSpaceEnvironmentTemplateServiceApi(object):
|
|
|
431
431
|
_request_timeout=params.get('_request_timeout'),
|
|
432
432
|
collection_formats=collection_formats)
|
|
433
433
|
|
|
434
|
-
def cloud_space_environment_template_service_update_cloud_space_environment_template(self, body: '
|
|
434
|
+
def cloud_space_environment_template_service_update_cloud_space_environment_template(self, body: 'Update', id: 'str', **kwargs) -> 'V1CloudSpaceEnvironmentTemplate': # noqa: E501
|
|
435
435
|
"""cloud_space_environment_template_service_update_cloud_space_environment_template # noqa: E501
|
|
436
436
|
|
|
437
437
|
This method makes a synchronous HTTP request by default. To make an
|
|
@@ -440,7 +440,7 @@ class CloudSpaceEnvironmentTemplateServiceApi(object):
|
|
|
440
440
|
>>> result = thread.get()
|
|
441
441
|
|
|
442
442
|
:param async_req bool
|
|
443
|
-
:param
|
|
443
|
+
:param Update body: (required)
|
|
444
444
|
:param str id: (required)
|
|
445
445
|
:return: V1CloudSpaceEnvironmentTemplate
|
|
446
446
|
If the method is called asynchronously,
|
|
@@ -453,7 +453,7 @@ class CloudSpaceEnvironmentTemplateServiceApi(object):
|
|
|
453
453
|
(data) = self.cloud_space_environment_template_service_update_cloud_space_environment_template_with_http_info(body, id, **kwargs) # noqa: E501
|
|
454
454
|
return data
|
|
455
455
|
|
|
456
|
-
def cloud_space_environment_template_service_update_cloud_space_environment_template_with_http_info(self, body: '
|
|
456
|
+
def cloud_space_environment_template_service_update_cloud_space_environment_template_with_http_info(self, body: 'Update', id: 'str', **kwargs) -> 'V1CloudSpaceEnvironmentTemplate': # noqa: E501
|
|
457
457
|
"""cloud_space_environment_template_service_update_cloud_space_environment_template # noqa: E501
|
|
458
458
|
|
|
459
459
|
This method makes a synchronous HTTP request by default. To make an
|
|
@@ -462,7 +462,7 @@ class CloudSpaceEnvironmentTemplateServiceApi(object):
|
|
|
462
462
|
>>> result = thread.get()
|
|
463
463
|
|
|
464
464
|
:param async_req bool
|
|
465
|
-
:param
|
|
465
|
+
:param Update body: (required)
|
|
466
466
|
:param str id: (required)
|
|
467
467
|
:return: V1CloudSpaceEnvironmentTemplate
|
|
468
468
|
If the method is called asynchronously,
|
|
@@ -2328,6 +2328,9 @@ class ClusterServiceApi(object):
|
|
|
2328
2328
|
:param async_req bool
|
|
2329
2329
|
:param str project_id: (required)
|
|
2330
2330
|
:param str cluster_id: (required)
|
|
2331
|
+
:param datetime start_time:
|
|
2332
|
+
:param datetime end_time:
|
|
2333
|
+
:param bool available_only:
|
|
2331
2334
|
:return: V1ListClusterCapacityReservationsResponse
|
|
2332
2335
|
If the method is called asynchronously,
|
|
2333
2336
|
returns the request thread.
|
|
@@ -2350,12 +2353,15 @@ class ClusterServiceApi(object):
|
|
|
2350
2353
|
:param async_req bool
|
|
2351
2354
|
:param str project_id: (required)
|
|
2352
2355
|
:param str cluster_id: (required)
|
|
2356
|
+
:param datetime start_time:
|
|
2357
|
+
:param datetime end_time:
|
|
2358
|
+
:param bool available_only:
|
|
2353
2359
|
:return: V1ListClusterCapacityReservationsResponse
|
|
2354
2360
|
If the method is called asynchronously,
|
|
2355
2361
|
returns the request thread.
|
|
2356
2362
|
"""
|
|
2357
2363
|
|
|
2358
|
-
all_params = ['project_id', 'cluster_id'] # noqa: E501
|
|
2364
|
+
all_params = ['project_id', 'cluster_id', 'start_time', 'end_time', 'available_only'] # noqa: E501
|
|
2359
2365
|
all_params.append('async_req')
|
|
2360
2366
|
all_params.append('_return_http_data_only')
|
|
2361
2367
|
all_params.append('_preload_content')
|
|
@@ -2388,6 +2394,12 @@ class ClusterServiceApi(object):
|
|
|
2388
2394
|
path_params['clusterId'] = params['cluster_id'] # noqa: E501
|
|
2389
2395
|
|
|
2390
2396
|
query_params = []
|
|
2397
|
+
if 'start_time' in params:
|
|
2398
|
+
query_params.append(('startTime', params['start_time'])) # noqa: E501
|
|
2399
|
+
if 'end_time' in params:
|
|
2400
|
+
query_params.append(('endTime', params['end_time'])) # noqa: E501
|
|
2401
|
+
if 'available_only' in params:
|
|
2402
|
+
query_params.append(('availableOnly', params['available_only'])) # noqa: E501
|
|
2391
2403
|
|
|
2392
2404
|
header_params = {}
|
|
2393
2405
|
|
|
@@ -1025,7 +1025,7 @@ class DataConnectionServiceApi(object):
|
|
|
1025
1025
|
_request_timeout=params.get('_request_timeout'),
|
|
1026
1026
|
collection_formats=collection_formats)
|
|
1027
1027
|
|
|
1028
|
-
def data_connection_service_update_data_connection(self, body: '
|
|
1028
|
+
def data_connection_service_update_data_connection(self, body: 'Update1', project_id: 'str', id: 'str', **kwargs) -> 'V1DataConnection': # noqa: E501
|
|
1029
1029
|
"""data_connection_service_update_data_connection # noqa: E501
|
|
1030
1030
|
|
|
1031
1031
|
This method makes a synchronous HTTP request by default. To make an
|
|
@@ -1034,7 +1034,7 @@ class DataConnectionServiceApi(object):
|
|
|
1034
1034
|
>>> result = thread.get()
|
|
1035
1035
|
|
|
1036
1036
|
:param async_req bool
|
|
1037
|
-
:param
|
|
1037
|
+
:param Update1 body: (required)
|
|
1038
1038
|
:param str project_id: (required)
|
|
1039
1039
|
:param str id: (required)
|
|
1040
1040
|
:return: V1DataConnection
|
|
@@ -1048,7 +1048,7 @@ class DataConnectionServiceApi(object):
|
|
|
1048
1048
|
(data) = self.data_connection_service_update_data_connection_with_http_info(body, project_id, id, **kwargs) # noqa: E501
|
|
1049
1049
|
return data
|
|
1050
1050
|
|
|
1051
|
-
def data_connection_service_update_data_connection_with_http_info(self, body: '
|
|
1051
|
+
def data_connection_service_update_data_connection_with_http_info(self, body: 'Update1', project_id: 'str', id: 'str', **kwargs) -> 'V1DataConnection': # noqa: E501
|
|
1052
1052
|
"""data_connection_service_update_data_connection # noqa: E501
|
|
1053
1053
|
|
|
1054
1054
|
This method makes a synchronous HTTP request by default. To make an
|
|
@@ -1057,7 +1057,7 @@ class DataConnectionServiceApi(object):
|
|
|
1057
1057
|
>>> result = thread.get()
|
|
1058
1058
|
|
|
1059
1059
|
:param async_req bool
|
|
1060
|
-
:param
|
|
1060
|
+
:param Update1 body: (required)
|
|
1061
1061
|
:param str project_id: (required)
|
|
1062
1062
|
:param str id: (required)
|
|
1063
1063
|
:return: V1DataConnection
|