lightning-sdk 2025.7.10__py3-none-any.whl → 2025.7.22__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 +3 -2
- lightning_sdk/api/cloud_account_api.py +154 -0
- lightning_sdk/api/deployment_api.py +11 -0
- lightning_sdk/api/job_api.py +9 -0
- lightning_sdk/api/llm_api.py +11 -6
- lightning_sdk/api/mmt_api.py +9 -0
- lightning_sdk/api/pipeline_api.py +4 -3
- lightning_sdk/api/studio_api.py +19 -5
- lightning_sdk/cli/clusters_menu.py +3 -3
- lightning_sdk/cli/create.py +22 -10
- lightning_sdk/cli/deploy/_auth.py +19 -3
- lightning_sdk/cli/deploy/serve.py +18 -4
- lightning_sdk/cli/entrypoint.py +1 -1
- lightning_sdk/cli/start.py +37 -7
- lightning_sdk/deployment/deployment.py +8 -0
- lightning_sdk/job/base.py +37 -5
- lightning_sdk/job/job.py +28 -4
- lightning_sdk/job/v1.py +10 -1
- lightning_sdk/job/v2.py +15 -1
- lightning_sdk/lightning_cloud/openapi/__init__.py +15 -1
- lightning_sdk/lightning_cloud/openapi/api/assistants_service_api.py +335 -0
- lightning_sdk/lightning_cloud/openapi/api/billing_service_api.py +214 -0
- lightning_sdk/lightning_cloud/openapi/api/cluster_service_api.py +5 -1
- lightning_sdk/lightning_cloud/openapi/api/user_service_api.py +11 -11
- lightning_sdk/lightning_cloud/openapi/models/__init__.py +15 -1
- lightning_sdk/lightning_cloud/openapi/models/assistant_id_conversations_body.py +29 -3
- lightning_sdk/lightning_cloud/openapi/models/blogposts_id_body.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/{v1_list_new_features_for_user_response.py → conversations_id_body1.py} +23 -23
- lightning_sdk/lightning_cloud/openapi/models/messages_id_body.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/project_id_schedules_body.py +29 -3
- lightning_sdk/lightning_cloud/openapi/models/project_id_storage_body.py +1 -27
- lightning_sdk/lightning_cloud/openapi/models/protobuf_null_value.py +102 -0
- lightning_sdk/lightning_cloud/openapi/models/schedules_id_body.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/storage_complete_body.py +1 -27
- lightning_sdk/lightning_cloud/openapi/models/uploads_upload_id_body1.py +3 -55
- lightning_sdk/lightning_cloud/openapi/models/user_id_upgradetrigger_body.py +175 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_ai_pod_v1.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_artifact.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_assistant_session_daily_aggregated.py +357 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_blog_post.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cloud_provider.py +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_spec.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_cluster_type.py +1 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_complete_upload.py +3 -55
- lightning_sdk/lightning_cloud/openapi/models/v1_conversation.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_create_billing_upgrade_trigger_record_response.py +97 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_create_blog_post_request.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_create_checkout_session_request.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_create_subscription_checkout_session_request.py +29 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_external_cluster_spec.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_function_tool.py +175 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_get_artifacts_page_response.py +29 -3
- lightning_sdk/lightning_cloud/openapi/models/v1_get_assistant_session_daily_aggregated_response.py +201 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_kubernetes_direct_v1.py +79 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_lightningapp_instance_artifact.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_like_status.py +104 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_notification_dialogs_response.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_list_published_managed_endpoint_models_response.py +123 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_message.py +53 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_presigned_url.py +1 -53
- lightning_sdk/lightning_cloud/openapi/models/v1_project.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_quote_subscription_response.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_schedule.py +27 -1
- lightning_sdk/lightning_cloud/openapi/models/v1_tool.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_update_conversation_like_response.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_update_conversation_message_like_response.py +149 -0
- lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +105 -261
- lightning_sdk/lightning_cloud/openapi/models/v1_volume.py +27 -1
- lightning_sdk/llm/llm.py +32 -5
- lightning_sdk/llm/public_assistants.json +3 -1
- lightning_sdk/machine.py +24 -1
- lightning_sdk/mmt/base.py +20 -2
- lightning_sdk/mmt/mmt.py +25 -3
- lightning_sdk/mmt/v1.py +7 -1
- lightning_sdk/mmt/v2.py +21 -2
- lightning_sdk/organization.py +4 -0
- lightning_sdk/pipeline/pipeline.py +16 -5
- lightning_sdk/pipeline/printer.py +5 -3
- lightning_sdk/pipeline/schedule.py +844 -1
- lightning_sdk/pipeline/steps.py +19 -4
- lightning_sdk/sandbox.py +4 -1
- lightning_sdk/serve.py +2 -0
- lightning_sdk/studio.py +79 -39
- lightning_sdk/teamspace.py +14 -8
- lightning_sdk/utils/resolve.py +29 -2
- {lightning_sdk-2025.7.10.dist-info → lightning_sdk-2025.7.22.dist-info}/METADATA +1 -1
- {lightning_sdk-2025.7.10.dist-info → lightning_sdk-2025.7.22.dist-info}/RECORD +92 -78
- lightning_sdk/api/cluster_api.py +0 -119
- /lightning_sdk/cli/{inspect.py → inspection.py} +0 -0
- {lightning_sdk-2025.7.10.dist-info → lightning_sdk-2025.7.22.dist-info}/LICENSE +0 -0
- {lightning_sdk-2025.7.10.dist-info → lightning_sdk-2025.7.22.dist-info}/WHEEL +0 -0
- {lightning_sdk-2025.7.10.dist-info → lightning_sdk-2025.7.22.dist-info}/entry_points.txt +0 -0
- {lightning_sdk-2025.7.10.dist-info → lightning_sdk-2025.7.22.dist-info}/top_level.txt +0 -0
|
@@ -57,6 +57,7 @@ class V1Volume(object):
|
|
|
57
57
|
'server_id': 'str',
|
|
58
58
|
'size_gb': 'str',
|
|
59
59
|
'state': 'V1VolumeState',
|
|
60
|
+
'subnet_id': 'str',
|
|
60
61
|
'throughput': 'str',
|
|
61
62
|
'type': 'str',
|
|
62
63
|
'updated_at': 'datetime',
|
|
@@ -80,13 +81,14 @@ class V1Volume(object):
|
|
|
80
81
|
'server_id': 'serverId',
|
|
81
82
|
'size_gb': 'sizeGb',
|
|
82
83
|
'state': 'state',
|
|
84
|
+
'subnet_id': 'subnetId',
|
|
83
85
|
'throughput': 'throughput',
|
|
84
86
|
'type': 'type',
|
|
85
87
|
'updated_at': 'updatedAt',
|
|
86
88
|
'user_id': 'userId'
|
|
87
89
|
}
|
|
88
90
|
|
|
89
|
-
def __init__(self, attached_at: 'datetime' =None, availability_zone: 'str' =None, detached_at: 'datetime' =None, encrypted: 'bool' =None, iops: 'str' =None, metadata: 'V1Metadata' =None, path: 'str' =None, provider: 'str' =None, provider_id: 'str' =None, region: 'str' =None, resource_id: 'str' =None, resource_type: 'str' =None, retention_period: 'str' =None, server_id: 'str' =None, size_gb: 'str' =None, state: 'V1VolumeState' =None, throughput: 'str' =None, type: 'str' =None, updated_at: 'datetime' =None, user_id: 'str' =None): # noqa: E501
|
|
91
|
+
def __init__(self, attached_at: 'datetime' =None, availability_zone: 'str' =None, detached_at: 'datetime' =None, encrypted: 'bool' =None, iops: 'str' =None, metadata: 'V1Metadata' =None, path: 'str' =None, provider: 'str' =None, provider_id: 'str' =None, region: 'str' =None, resource_id: 'str' =None, resource_type: 'str' =None, retention_period: 'str' =None, server_id: 'str' =None, size_gb: 'str' =None, state: 'V1VolumeState' =None, subnet_id: 'str' =None, throughput: 'str' =None, type: 'str' =None, updated_at: 'datetime' =None, user_id: 'str' =None): # noqa: E501
|
|
90
92
|
"""V1Volume - a model defined in Swagger""" # noqa: E501
|
|
91
93
|
self._attached_at = None
|
|
92
94
|
self._availability_zone = None
|
|
@@ -104,6 +106,7 @@ class V1Volume(object):
|
|
|
104
106
|
self._server_id = None
|
|
105
107
|
self._size_gb = None
|
|
106
108
|
self._state = None
|
|
109
|
+
self._subnet_id = None
|
|
107
110
|
self._throughput = None
|
|
108
111
|
self._type = None
|
|
109
112
|
self._updated_at = None
|
|
@@ -141,6 +144,8 @@ class V1Volume(object):
|
|
|
141
144
|
self.size_gb = size_gb
|
|
142
145
|
if state is not None:
|
|
143
146
|
self.state = state
|
|
147
|
+
if subnet_id is not None:
|
|
148
|
+
self.subnet_id = subnet_id
|
|
144
149
|
if throughput is not None:
|
|
145
150
|
self.throughput = throughput
|
|
146
151
|
if type is not None:
|
|
@@ -486,6 +491,27 @@ class V1Volume(object):
|
|
|
486
491
|
|
|
487
492
|
self._state = state
|
|
488
493
|
|
|
494
|
+
@property
|
|
495
|
+
def subnet_id(self) -> 'str':
|
|
496
|
+
"""Gets the subnet_id of this V1Volume. # noqa: E501
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
:return: The subnet_id of this V1Volume. # noqa: E501
|
|
500
|
+
:rtype: str
|
|
501
|
+
"""
|
|
502
|
+
return self._subnet_id
|
|
503
|
+
|
|
504
|
+
@subnet_id.setter
|
|
505
|
+
def subnet_id(self, subnet_id: 'str'):
|
|
506
|
+
"""Sets the subnet_id of this V1Volume.
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
:param subnet_id: The subnet_id of this V1Volume. # noqa: E501
|
|
510
|
+
:type: str
|
|
511
|
+
"""
|
|
512
|
+
|
|
513
|
+
self._subnet_id = subnet_id
|
|
514
|
+
|
|
489
515
|
@property
|
|
490
516
|
def throughput(self) -> 'str':
|
|
491
517
|
"""Gets the throughput of this V1Volume. # noqa: E501
|
lightning_sdk/llm/llm.py
CHANGED
|
@@ -9,6 +9,7 @@ PUBLIC_MODEL_PROVIDERS: Dict[str, str] = {
|
|
|
9
9
|
"openai": "OpenAI",
|
|
10
10
|
"anthropic": "Anthropic",
|
|
11
11
|
"google": "Google",
|
|
12
|
+
"lightning-ai": "lightning-ai",
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
|
|
@@ -54,8 +55,18 @@ class LLM:
|
|
|
54
55
|
Raises:
|
|
55
56
|
ValueError: If teamspace information cannot be resolved.
|
|
56
57
|
"""
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
teamspace_name = None
|
|
59
|
+
if teamspace:
|
|
60
|
+
try:
|
|
61
|
+
owner, teamspace_name = teamspace.split("/", maxsplit=1)
|
|
62
|
+
except ValueError as e:
|
|
63
|
+
raise ValueError(
|
|
64
|
+
f"Invalid teamspace format: '{teamspace}'. "
|
|
65
|
+
"Teamspace should be specified as '{teamspace_owner}/{teamspace_name}' "
|
|
66
|
+
"(e.g., 'my-org/my-teamspace')."
|
|
67
|
+
) from e
|
|
68
|
+
|
|
69
|
+
self._get_auth_info(teamspace_name)
|
|
59
70
|
|
|
60
71
|
self._model_provider, self._model_name = self._parse_model_name(name)
|
|
61
72
|
self._enable_async = enable_async
|
|
@@ -76,13 +87,15 @@ class LLM:
|
|
|
76
87
|
def provider(self) -> str:
|
|
77
88
|
return self._model_provider
|
|
78
89
|
|
|
79
|
-
def _get_auth_info(self) -> None:
|
|
90
|
+
def _get_auth_info(self, teamspace_name: Optional[str] = None) -> None:
|
|
91
|
+
# TODO: Validate user input teamspace name
|
|
80
92
|
if not LLM._auth_info_cached:
|
|
81
|
-
teamspace_name
|
|
93
|
+
if teamspace_name is None:
|
|
94
|
+
teamspace_name = os.environ.get("LIGHTNING_TEAMSPACE", None)
|
|
82
95
|
if teamspace_name is None:
|
|
83
96
|
raise ValueError(
|
|
84
97
|
"Teamspace name must be provided either through "
|
|
85
|
-
"the environment variable LIGHTNING_TEAMSPACE or as an argument
|
|
98
|
+
"the environment variable LIGHTNING_TEAMSPACE or as an argument - LLM(..., teamspace=...)"
|
|
86
99
|
)
|
|
87
100
|
LLM._cached_auth_info = {
|
|
88
101
|
"teamspace_name": teamspace_name,
|
|
@@ -138,6 +151,18 @@ class LLM:
|
|
|
138
151
|
"Please check the model name or provider."
|
|
139
152
|
) from e
|
|
140
153
|
|
|
154
|
+
if self._model_provider == "lightning-ai":
|
|
155
|
+
# Try model provider model
|
|
156
|
+
try:
|
|
157
|
+
return self._llm_api.get_assistant(
|
|
158
|
+
model_provider=self._model_provider,
|
|
159
|
+
model_name=self._model_name,
|
|
160
|
+
user_name="",
|
|
161
|
+
org_name="",
|
|
162
|
+
)
|
|
163
|
+
except Exception:
|
|
164
|
+
pass
|
|
165
|
+
|
|
141
166
|
# Try organization model
|
|
142
167
|
try:
|
|
143
168
|
return self._llm_api.get_assistant(
|
|
@@ -241,6 +266,7 @@ class LLM:
|
|
|
241
266
|
metadata: Optional[Dict[str, str]] = None,
|
|
242
267
|
stream: bool = False,
|
|
243
268
|
full_response: bool = False,
|
|
269
|
+
tools: Optional[List[Dict[str, Any]]] = None,
|
|
244
270
|
**kwargs: Any,
|
|
245
271
|
) -> Union[
|
|
246
272
|
V1ConversationResponseChunk, Generator[V1ConversationResponseChunk, None, None], str, Generator[str, None, None]
|
|
@@ -281,6 +307,7 @@ class LLM:
|
|
|
281
307
|
metadata=metadata,
|
|
282
308
|
name=conversation,
|
|
283
309
|
stream=stream,
|
|
310
|
+
tools=tools,
|
|
284
311
|
**kwargs,
|
|
285
312
|
)
|
|
286
313
|
if not stream:
|
|
@@ -4,5 +4,7 @@
|
|
|
4
4
|
"openai/o3-mini": "ast_01jz3t13fhnjhh11t1k8b5gyp1",
|
|
5
5
|
"anthropic/claude-3-5-sonnet-20240620": "ast_01jd3923a6p98rqwh3dpj686pq",
|
|
6
6
|
"google/gemini-2.5-pro": "ast_01jz3tdb1fhey798k95pv61v57",
|
|
7
|
-
"google/gemini-2.5-flash": "ast_01jz3thxskg4fcdk4xhkjkym5a"
|
|
7
|
+
"google/gemini-2.5-flash": "ast_01jz3thxskg4fcdk4xhkjkym5a",
|
|
8
|
+
"google/gemini-2.5-flash-lite-preview-06-17": "ast_01jz3thxskg4fcdk4xhkjkym5b",
|
|
9
|
+
"lightning-ai/llama4-maverick": "ast_01k073vsqs66tenpns02cf5jnq"
|
|
8
10
|
}
|
lightning_sdk/machine.py
CHANGED
|
@@ -1,17 +1,31 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
|
+
from enum import Enum
|
|
2
3
|
from typing import Any, ClassVar, Optional, Tuple
|
|
3
4
|
|
|
4
5
|
|
|
6
|
+
class CloudProvider(Enum):
|
|
7
|
+
AWS = "AWS"
|
|
8
|
+
GCP = "GCP"
|
|
9
|
+
VULTR = "VULTR"
|
|
10
|
+
LAMBDA_LABS = "LAMBDA_LABS"
|
|
11
|
+
DGX = "DGX"
|
|
12
|
+
VOLTAGE_PARK = "VOLTAGE_PARK"
|
|
13
|
+
NEBIUS = "NEBIUS"
|
|
14
|
+
LIGHTNING = "LIGHTNING"
|
|
15
|
+
|
|
16
|
+
|
|
5
17
|
@dataclass(frozen=True)
|
|
6
18
|
class Machine:
|
|
7
19
|
# Default Machines
|
|
8
20
|
CPU: ClassVar["Machine"]
|
|
21
|
+
CPU_SMALL: ClassVar["Machine"]
|
|
9
22
|
DATA_PREP: ClassVar["Machine"]
|
|
10
23
|
DATA_PREP_MAX: ClassVar["Machine"]
|
|
11
24
|
DATA_PREP_ULTRA: ClassVar["Machine"]
|
|
12
25
|
T4: ClassVar["Machine"]
|
|
13
26
|
T4_X_4: ClassVar["Machine"]
|
|
14
27
|
L4: ClassVar["Machine"]
|
|
28
|
+
L4_X_2: ClassVar["Machine"]
|
|
15
29
|
L4_X_4: ClassVar["Machine"]
|
|
16
30
|
L4_X_8: ClassVar["Machine"]
|
|
17
31
|
A10G: ClassVar["Machine"]
|
|
@@ -20,7 +34,10 @@ class Machine:
|
|
|
20
34
|
L40S: ClassVar["Machine"]
|
|
21
35
|
L40S_X_4: ClassVar["Machine"]
|
|
22
36
|
L40S_X_8: ClassVar["Machine"]
|
|
37
|
+
A100_X_2: ClassVar["Machine"]
|
|
38
|
+
A100_X_4: ClassVar["Machine"]
|
|
23
39
|
A100_X_8: ClassVar["Machine"]
|
|
40
|
+
B200_X_8: ClassVar["Machine"]
|
|
24
41
|
H100_X_8: ClassVar["Machine"]
|
|
25
42
|
H200_X_8: ClassVar["Machine"]
|
|
26
43
|
|
|
@@ -45,6 +62,7 @@ class Machine:
|
|
|
45
62
|
"""Whether the machine is a CPU."""
|
|
46
63
|
return (
|
|
47
64
|
self == Machine.CPU
|
|
65
|
+
or self == Machine.CPU_SMALL
|
|
48
66
|
or self == Machine.DATA_PREP
|
|
49
67
|
or self == Machine.DATA_PREP_MAX
|
|
50
68
|
or self == Machine.DATA_PREP_ULTRA
|
|
@@ -66,12 +84,14 @@ class Machine:
|
|
|
66
84
|
|
|
67
85
|
|
|
68
86
|
Machine.CPU = Machine(name="CPU", instance_type="cpu-4")
|
|
87
|
+
Machine.CPU_SMALL = Machine(name="CPU_SMALL", instance_type="n2d-standard-2") # GCP
|
|
69
88
|
Machine.DATA_PREP = Machine(name="DATA_PREP", instance_type="data-large")
|
|
70
89
|
Machine.DATA_PREP_MAX = Machine(name="DATA_PREP_MAX", instance_type="data-max")
|
|
71
90
|
Machine.DATA_PREP_ULTRA = Machine(name="DATA_PREP_ULTRA", instance_type="data-ultra")
|
|
72
91
|
Machine.T4 = Machine(name="T4", instance_type="g4dn.2xlarge")
|
|
73
92
|
Machine.T4_X_4 = Machine(name="T4_X_4", instance_type="g4dn.12xlarge")
|
|
74
93
|
Machine.L4 = Machine(name="L4", instance_type="g6.4xlarge")
|
|
94
|
+
Machine.L4_X_2 = Machine(name="L4_X_2", instance_type="g2-standard-24") # GCP
|
|
75
95
|
Machine.L4_X_4 = Machine(name="L4_X_4", instance_type="g6.12xlarge")
|
|
76
96
|
Machine.L4_X_8 = Machine(name="L4_X_8", instance_type="g6.48xlarge")
|
|
77
97
|
Machine.A10G = Machine(name="A10G", instance_type="g5.8xlarge")
|
|
@@ -80,6 +100,9 @@ Machine.A10G_X_8 = Machine(name="A10G_X_8", instance_type="g5.48xlarge")
|
|
|
80
100
|
Machine.L40S = Machine(name="L40S", instance_type="g6e.4xlarge")
|
|
81
101
|
Machine.L40S_X_4 = Machine(name="L40S_X_4", instance_type="g6e.12xlarge")
|
|
82
102
|
Machine.L40S_X_8 = Machine(name="L40S_X_8", instance_type="g6e.48xlarge")
|
|
103
|
+
Machine.A100_X_2 = Machine(name="A100_X_2", instance_type="a2-ultragpu-2g") # GCP
|
|
104
|
+
Machine.A100_X_4 = Machine(name="A100_X_4", instance_type="a2-ultragpu-4g") # GCP
|
|
83
105
|
Machine.A100_X_8 = Machine(name="A100_X_8", instance_type="p4d.24xlarge")
|
|
106
|
+
Machine.B200_X_8 = Machine(name="B200_X_8", instance_type="a4-highgpu-8g") # GCP
|
|
84
107
|
Machine.H100_X_8 = Machine(name="H100_X_8", instance_type="p5.48xlarge")
|
|
85
|
-
Machine.H200_X_8 = Machine(name="H200_X_8", instance_type="
|
|
108
|
+
Machine.H200_X_8 = Machine(name="H200_X_8", instance_type="p5en.48xlarge")
|
lightning_sdk/mmt/base.py
CHANGED
|
@@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Dict, List, Optional, Protocol, Tuple, Union
|
|
|
4
4
|
|
|
5
5
|
if TYPE_CHECKING:
|
|
6
6
|
from lightning_sdk.job.base import MachineDict
|
|
7
|
-
from lightning_sdk.machine import Machine
|
|
7
|
+
from lightning_sdk.machine import CloudProvider, Machine
|
|
8
8
|
from lightning_sdk.organization import Organization
|
|
9
9
|
from lightning_sdk.status import Status
|
|
10
10
|
from lightning_sdk.studio import Studio
|
|
@@ -64,12 +64,14 @@ class _BaseMMT(_BaseJob):
|
|
|
64
64
|
org: Union[str, "Organization", None] = None,
|
|
65
65
|
user: Union[str, "User", None] = None,
|
|
66
66
|
cloud_account: Optional[str] = None,
|
|
67
|
+
cloud_provider: Optional[Union["CloudProvider", str]] = None,
|
|
67
68
|
env: Optional[Dict[str, str]] = None,
|
|
68
69
|
interruptible: bool = False,
|
|
69
70
|
image_credentials: Optional[str] = None,
|
|
70
71
|
cloud_account_auth: bool = False,
|
|
71
72
|
entrypoint: str = "sh -c",
|
|
72
73
|
path_mappings: Optional[Dict[str, str]] = None,
|
|
74
|
+
max_runtime: Optional[int] = None,
|
|
73
75
|
artifacts_local: Optional[str] = None, # deprecated in favor of path_mappings
|
|
74
76
|
artifacts_remote: Optional[str] = None, # deprecated in favor of path_mappings
|
|
75
77
|
cluster: Optional[str] = None, # deprecated in favor of cloud_account
|
|
@@ -89,7 +91,11 @@ class _BaseMMT(_BaseJob):
|
|
|
89
91
|
user: The user owning the teamspace (if any). Defaults to the current user.
|
|
90
92
|
cloud_account: The cloud account to run the job on.
|
|
91
93
|
Defaults to the studio cloud account if running with studio compute env.
|
|
92
|
-
If not provided
|
|
94
|
+
If not provided and `cloud_account_provider` is set, will resolve cluster from this, else
|
|
95
|
+
will fall back to the teamspaces default cloud account.
|
|
96
|
+
cloud_account_provider: The provider to select the cloud-account from.
|
|
97
|
+
If set, must be in agreement with the provider from the cloud_account (if specified).
|
|
98
|
+
If not specified, falls backto the teamspace default cloud account.
|
|
93
99
|
env: Environment variables to set inside the job.
|
|
94
100
|
interruptible: Whether the job should run on interruptible instances. They are cheaper but can be preempted.
|
|
95
101
|
image_credentials: The credentials used to pull the image. Required if the image is private.
|
|
@@ -109,6 +115,10 @@ class _BaseMMT(_BaseJob):
|
|
|
109
115
|
}
|
|
110
116
|
If the path inside the connection is omitted it's assumed to be the root path of that connection.
|
|
111
117
|
Only applicable when submitting docker jobs.
|
|
118
|
+
max_runtime: the duration (in seconds) for which to allocate the machine.
|
|
119
|
+
Irrelevant for most machines, required for some of the top-end machines on GCP.
|
|
120
|
+
If in doubt, set it. Won't have an effect on machines not requiring it.
|
|
121
|
+
Defaults to 3h
|
|
112
122
|
"""
|
|
113
123
|
from lightning_sdk.lightning_cloud.openapi.rest import ApiException
|
|
114
124
|
from lightning_sdk.studio import Studio
|
|
@@ -191,6 +201,7 @@ class _BaseMMT(_BaseJob):
|
|
|
191
201
|
num_machines=num_machines,
|
|
192
202
|
machine=machine,
|
|
193
203
|
cloud_account=cloud_account,
|
|
204
|
+
cloud_provider=cloud_provider,
|
|
194
205
|
command=command,
|
|
195
206
|
studio=studio,
|
|
196
207
|
image=image,
|
|
@@ -202,6 +213,7 @@ class _BaseMMT(_BaseJob):
|
|
|
202
213
|
path_mappings=path_mappings,
|
|
203
214
|
artifacts_local=artifacts_local,
|
|
204
215
|
artifacts_remote=artifacts_remote,
|
|
216
|
+
max_runtime=max_runtime,
|
|
205
217
|
)
|
|
206
218
|
return inst
|
|
207
219
|
|
|
@@ -216,12 +228,14 @@ class _BaseMMT(_BaseJob):
|
|
|
216
228
|
env: Optional[Dict[str, str]] = None,
|
|
217
229
|
interruptible: bool = False,
|
|
218
230
|
cloud_account: Optional[str] = None,
|
|
231
|
+
cloud_provider: Optional[Union["CloudProvider", str]] = None,
|
|
219
232
|
image_credentials: Optional[str] = None,
|
|
220
233
|
cloud_account_auth: bool = False,
|
|
221
234
|
entrypoint: str = "sh -c",
|
|
222
235
|
path_mappings: Optional[Dict[str, str]] = None,
|
|
223
236
|
artifacts_local: Optional[str] = None, # deprecated in favor of path_mappings
|
|
224
237
|
artifacts_remote: Optional[str] = None, # deprecated in favor of path_mappings
|
|
238
|
+
max_runtime: Optional[int] = None,
|
|
225
239
|
) -> None:
|
|
226
240
|
"""Submit a new multi-machine job to the Lightning AI platform.
|
|
227
241
|
|
|
@@ -253,6 +267,10 @@ class _BaseMMT(_BaseJob):
|
|
|
253
267
|
}
|
|
254
268
|
If the path inside the connection is omitted it's assumed to be the root path of that connection.
|
|
255
269
|
Only applicable when submitting docker jobs.
|
|
270
|
+
max_runtime: the duration (in seconds) for which to allocate the machine.
|
|
271
|
+
Irrelevant for most machines, required for some of the top-end machines on GCP.
|
|
272
|
+
If in doubt, set it. Won't have an effect on machines not requiring it.
|
|
273
|
+
Defaults to 3h
|
|
256
274
|
"""
|
|
257
275
|
|
|
258
276
|
@property
|
lightning_sdk/mmt/mmt.py
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Union
|
|
2
2
|
|
|
3
|
+
from lightning_sdk.api.cloud_account_api import CloudAccountApi
|
|
3
4
|
from lightning_sdk.mmt.base import MMTMachine, _BaseMMT
|
|
4
5
|
from lightning_sdk.mmt.v1 import _MMTV1
|
|
5
6
|
from lightning_sdk.mmt.v2 import _MMTV2
|
|
6
7
|
from lightning_sdk.utils.resolve import _setup_logger
|
|
7
8
|
|
|
8
9
|
if TYPE_CHECKING:
|
|
9
|
-
from lightning_sdk.machine import Machine
|
|
10
|
+
from lightning_sdk.machine import CloudProvider, Machine
|
|
10
11
|
from lightning_sdk.organization import Organization
|
|
11
12
|
from lightning_sdk.status import Status
|
|
12
13
|
from lightning_sdk.studio import Studio
|
|
@@ -75,6 +76,7 @@ class MMT(_BaseMMT):
|
|
|
75
76
|
)
|
|
76
77
|
|
|
77
78
|
self._internal_mmt = mmt
|
|
79
|
+
self._cloud_account_api = CloudAccountApi()
|
|
78
80
|
|
|
79
81
|
@classmethod
|
|
80
82
|
def run(
|
|
@@ -89,12 +91,14 @@ class MMT(_BaseMMT):
|
|
|
89
91
|
org: Union[str, "Organization", None] = None,
|
|
90
92
|
user: Union[str, "User", None] = None,
|
|
91
93
|
cloud_account: Optional[str] = None,
|
|
94
|
+
cloud_provider: Optional[Union["CloudProvider", str]] = None,
|
|
92
95
|
env: Optional[Dict[str, str]] = None,
|
|
93
96
|
interruptible: bool = False,
|
|
94
97
|
image_credentials: Optional[str] = None,
|
|
95
98
|
cloud_account_auth: bool = False,
|
|
96
99
|
entrypoint: str = "sh -c",
|
|
97
100
|
path_mappings: Optional[Dict[str, str]] = None,
|
|
101
|
+
max_runtime: Optional[int] = None,
|
|
98
102
|
artifacts_local: Optional[str] = None,
|
|
99
103
|
artifacts_remote: Optional[str] = None,
|
|
100
104
|
cluster: Optional[str] = None, # deprecated in favor of cloud_account
|
|
@@ -114,7 +118,11 @@ class MMT(_BaseMMT):
|
|
|
114
118
|
user: The user owning the teamspace (if any). Defaults to the current user.
|
|
115
119
|
cloud_account: The cloud account to run the job on.
|
|
116
120
|
Defaults to the studio cloud account if running with studio compute env.
|
|
117
|
-
If not provided
|
|
121
|
+
If not provided and `cloud_account_provider` is set, will resolve cluster from this, else
|
|
122
|
+
will fall back to the teamspaces default cloud account.
|
|
123
|
+
cloud_account_provider: The provider to select the cloud-account from.
|
|
124
|
+
If set, must be in agreement with the provider from the cloud_account (if specified).
|
|
125
|
+
If not specified, falls backto the teamspace default cloud account.
|
|
118
126
|
env: Environment variables to set inside the job.
|
|
119
127
|
interruptible: Whether the job should run on interruptible instances. They are cheaper but can be preempted.
|
|
120
128
|
image_credentials: The credentials used to pull the image. Required if the image is private.
|
|
@@ -145,6 +153,7 @@ class MMT(_BaseMMT):
|
|
|
145
153
|
org=org,
|
|
146
154
|
user=user,
|
|
147
155
|
cloud_account=cloud_account,
|
|
156
|
+
cloud_provider=cloud_provider,
|
|
148
157
|
env=env,
|
|
149
158
|
interruptible=interruptible,
|
|
150
159
|
image_credentials=image_credentials,
|
|
@@ -154,6 +163,7 @@ class MMT(_BaseMMT):
|
|
|
154
163
|
artifacts_local=artifacts_local,
|
|
155
164
|
artifacts_remote=artifacts_remote,
|
|
156
165
|
cluster=cluster, # deprecated in favor of cloud_account
|
|
166
|
+
max_runtime=max_runtime,
|
|
157
167
|
)
|
|
158
168
|
# required for typing with "MMT"
|
|
159
169
|
assert isinstance(ret_val, cls)
|
|
@@ -173,10 +183,12 @@ class MMT(_BaseMMT):
|
|
|
173
183
|
env: Optional[Dict[str, str]] = None,
|
|
174
184
|
interruptible: bool = False,
|
|
175
185
|
cloud_account: Optional[str] = None,
|
|
186
|
+
cloud_provider: Optional[Union["CloudProvider", str]] = None,
|
|
176
187
|
image_credentials: Optional[str] = None,
|
|
177
188
|
cloud_account_auth: bool = False,
|
|
178
189
|
entrypoint: str = "sh -c",
|
|
179
190
|
path_mappings: Optional[Dict[str, str]] = None,
|
|
191
|
+
max_runtime: Optional[int] = None,
|
|
180
192
|
artifacts_local: Optional[str] = None, # deprecated in favor of path_mappings
|
|
181
193
|
artifacts_remote: Optional[str] = None, # deprecated in favor of path_mappings
|
|
182
194
|
) -> "MMT":
|
|
@@ -193,7 +205,11 @@ class MMT(_BaseMMT):
|
|
|
193
205
|
interruptible: Whether the job should run on interruptible instances. They are cheaper but can be preempted.
|
|
194
206
|
cloud_account: The cloud account to run the job on.
|
|
195
207
|
Defaults to the studio cloud account if running with studio compute env.
|
|
196
|
-
If not provided
|
|
208
|
+
If not provided and `cloud_account_provider` is set, will resolve cluster from this, else
|
|
209
|
+
will fall back to the teamspaces default cloud account.
|
|
210
|
+
cloud_account_provider: The provider to select the cloud-account from.
|
|
211
|
+
If set, must be in agreement with the provider from the cloud_account (if specified).
|
|
212
|
+
If not specified, falls backto the teamspace default cloud account.
|
|
197
213
|
image_credentials: The credentials used to pull the image. Required if the image is private.
|
|
198
214
|
This should be the name of the respective credentials secret created on the Lightning AI platform.
|
|
199
215
|
cloud_account_auth: Whether to authenticate with the cloud account to pull the image.
|
|
@@ -211,11 +227,16 @@ class MMT(_BaseMMT):
|
|
|
211
227
|
}
|
|
212
228
|
If the path inside the connection is omitted it's assumed to be the root path of that connection.
|
|
213
229
|
Only applicable when submitting docker jobs.
|
|
230
|
+
max_runtime: the duration (in seconds) for which to allocate the machine.
|
|
231
|
+
Irrelevant for most machines, required for some of the top-end machines on GCP.
|
|
232
|
+
If in doubt, set it. Won't have an effect on machines not requiring it.
|
|
233
|
+
Defaults to 3h
|
|
214
234
|
"""
|
|
215
235
|
self._job = self._internal_mmt._submit(
|
|
216
236
|
num_machines=num_machines,
|
|
217
237
|
machine=machine,
|
|
218
238
|
cloud_account=cloud_account,
|
|
239
|
+
cloud_provider=cloud_provider,
|
|
219
240
|
command=command,
|
|
220
241
|
studio=studio,
|
|
221
242
|
image=image,
|
|
@@ -227,6 +248,7 @@ class MMT(_BaseMMT):
|
|
|
227
248
|
path_mappings=path_mappings,
|
|
228
249
|
artifacts_local=artifacts_local,
|
|
229
250
|
artifacts_remote=artifacts_remote,
|
|
251
|
+
max_runtime=max_runtime,
|
|
230
252
|
)
|
|
231
253
|
return self
|
|
232
254
|
|
lightning_sdk/mmt/v1.py
CHANGED
|
@@ -7,7 +7,7 @@ from lightning_sdk.job.work import Work
|
|
|
7
7
|
from lightning_sdk.status import Status
|
|
8
8
|
|
|
9
9
|
if TYPE_CHECKING:
|
|
10
|
-
from lightning_sdk.machine import Machine
|
|
10
|
+
from lightning_sdk.machine import CloudProvider, Machine
|
|
11
11
|
from lightning_sdk.organization import Organization
|
|
12
12
|
from lightning_sdk.studio import Studio
|
|
13
13
|
from lightning_sdk.teamspace import Teamspace
|
|
@@ -50,10 +50,12 @@ class _MMTV1(_BaseMMT):
|
|
|
50
50
|
env: Optional[Dict[str, str]] = None,
|
|
51
51
|
interruptible: bool = False,
|
|
52
52
|
cloud_account: Optional[str] = None,
|
|
53
|
+
cloud_provider: Optional[Union["CloudProvider", str]] = None,
|
|
53
54
|
image_credentials: Optional[str] = None,
|
|
54
55
|
cloud_account_auth: bool = False,
|
|
55
56
|
entrypoint: str = "sh -c",
|
|
56
57
|
path_mappings: Optional[Dict[str, str]] = None,
|
|
58
|
+
max_runtime: Optional[int] = None,
|
|
57
59
|
artifacts_local: Optional[str] = None,
|
|
58
60
|
artifacts_remote: Optional[str] = None,
|
|
59
61
|
) -> "_MMTV1":
|
|
@@ -90,6 +92,10 @@ class _MMTV1(_BaseMMT):
|
|
|
90
92
|
To use the pre-defined entrypoint of the provided image, set this to an empty string.
|
|
91
93
|
Only applicable when submitting docker jobs.
|
|
92
94
|
path_mappings: The mappings from data connection inside your container (not supported)
|
|
95
|
+
max_runtime: the duration (in seconds) for which to allocate the machine.
|
|
96
|
+
Irrelevant for most machines, required for some of the top-end machines on GCP.
|
|
97
|
+
If in doubt, set it. Won't have an effect on machines not requiring it.
|
|
98
|
+
Defaults to 3h
|
|
93
99
|
|
|
94
100
|
"""
|
|
95
101
|
raise NotImplementedError("Cannot submit new mmts with MMTV1!")
|
lightning_sdk/mmt/v2.py
CHANGED
|
@@ -6,7 +6,7 @@ from lightning_sdk.status import Status
|
|
|
6
6
|
|
|
7
7
|
if TYPE_CHECKING:
|
|
8
8
|
from lightning_sdk.job.job import Job
|
|
9
|
-
from lightning_sdk.machine import Machine
|
|
9
|
+
from lightning_sdk.machine import CloudProvider, Machine
|
|
10
10
|
from lightning_sdk.organization import Organization
|
|
11
11
|
from lightning_sdk.studio import Studio
|
|
12
12
|
from lightning_sdk.teamspace import Teamspace
|
|
@@ -49,10 +49,12 @@ class _MMTV2(_BaseMMT):
|
|
|
49
49
|
env: Optional[Dict[str, str]] = None,
|
|
50
50
|
interruptible: bool = False,
|
|
51
51
|
cloud_account: Optional[str] = None,
|
|
52
|
+
cloud_provider: Optional[Union["CloudProvider", str]] = None,
|
|
52
53
|
image_credentials: Optional[str] = None,
|
|
53
54
|
cloud_account_auth: bool = False,
|
|
54
55
|
entrypoint: str = "sh -c",
|
|
55
56
|
path_mappings: Optional[Dict[str, str]] = None,
|
|
57
|
+
max_runtime: Optional[int] = None,
|
|
56
58
|
artifacts_local: Optional[str] = None, # deprecated in favor of path_mappings
|
|
57
59
|
artifacts_remote: Optional[str] = None, # deprecated in favor of path_mappings
|
|
58
60
|
) -> "_MMTV2":
|
|
@@ -69,7 +71,11 @@ class _MMTV2(_BaseMMT):
|
|
|
69
71
|
interruptible: Whether the job should run on interruptible instances. They are cheaper but can be preempted.
|
|
70
72
|
cloud_account: The cloud account to run the job on.
|
|
71
73
|
Defaults to the studio cloud account if running with studio compute env.
|
|
72
|
-
If not provided
|
|
74
|
+
If not provided and `cloud_account_provider` is set, will resolve cluster from this, else
|
|
75
|
+
will fall back to the teamspaces default cloud account.
|
|
76
|
+
cloud_account_provider: The provider to select the cloud-account from.
|
|
77
|
+
If set, must be in agreement with the provider from the cloud_account (if specified).
|
|
78
|
+
If not specified, falls backto the teamspace default cloud account.
|
|
73
79
|
image_credentials: The credentials used to pull the image. Required if the image is private.
|
|
74
80
|
This should be the name of the respective credentials secret created on the Lightning AI platform.
|
|
75
81
|
cloud_account_auth: Whether to authenticate with the cloud account to pull the image.
|
|
@@ -87,6 +93,10 @@ class _MMTV2(_BaseMMT):
|
|
|
87
93
|
}
|
|
88
94
|
If the path inside the connection is omitted it's assumed to be the root path of that connection.
|
|
89
95
|
Only applicable when submitting docker jobs.
|
|
96
|
+
max_runtime: the duration (in seconds) for which to allocate the machine.
|
|
97
|
+
Irrelevant for most machines, required for some of the top-end machines on GCP.
|
|
98
|
+
If in doubt, set it. Won't have an effect on machines not requiring it.
|
|
99
|
+
Defaults to 3h
|
|
90
100
|
"""
|
|
91
101
|
# Command is required if Studio is provided to know what to run
|
|
92
102
|
# Image is mutually exclusive with Studio
|
|
@@ -104,6 +114,14 @@ class _MMTV2(_BaseMMT):
|
|
|
104
114
|
studio_id = None
|
|
105
115
|
if image is None:
|
|
106
116
|
raise ValueError("either image or studio must be provided")
|
|
117
|
+
|
|
118
|
+
cloud_account = self._cloud_account_api.resolve_cloud_account(
|
|
119
|
+
self._teamspace.id,
|
|
120
|
+
cloud_account=cloud_account,
|
|
121
|
+
cloud_provider=cloud_provider,
|
|
122
|
+
default_cloud_account=self._teamspace.default_cloud_account,
|
|
123
|
+
)
|
|
124
|
+
|
|
107
125
|
submitted = self._job_api.submit_job(
|
|
108
126
|
name=self.name,
|
|
109
127
|
num_machines=num_machines,
|
|
@@ -121,6 +139,7 @@ class _MMTV2(_BaseMMT):
|
|
|
121
139
|
path_mappings=path_mappings,
|
|
122
140
|
artifacts_local=artifacts_local,
|
|
123
141
|
artifacts_remote=artifacts_remote,
|
|
142
|
+
max_runtime=max_runtime,
|
|
124
143
|
)
|
|
125
144
|
self._job = submitted
|
|
126
145
|
self._name = submitted.name
|
lightning_sdk/organization.py
CHANGED
|
@@ -40,6 +40,10 @@ class Organization(Owner):
|
|
|
40
40
|
"""The organization's ID."""
|
|
41
41
|
return self._org.id
|
|
42
42
|
|
|
43
|
+
@property
|
|
44
|
+
def default_cloud_account(self) -> Optional[str]:
|
|
45
|
+
return self._org.preferred_cluster or None
|
|
46
|
+
|
|
43
47
|
def __repr__(self) -> str:
|
|
44
48
|
"""Returns reader friendly representation."""
|
|
45
49
|
return f"Organization(name={self.name})"
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import os
|
|
2
|
-
from typing import
|
|
2
|
+
from typing import List, Optional, Union
|
|
3
3
|
|
|
4
|
+
from lightning_sdk.api.cloud_account_api import CloudAccountApi
|
|
4
5
|
from lightning_sdk.api.pipeline_api import PipelineApi
|
|
6
|
+
from lightning_sdk.machine import CloudProvider
|
|
5
7
|
from lightning_sdk.organization import Organization
|
|
6
8
|
from lightning_sdk.pipeline.printer import PipelinePrinter
|
|
9
|
+
from lightning_sdk.pipeline.schedule import _TIMEZONES, Schedule
|
|
7
10
|
from lightning_sdk.pipeline.steps import DeploymentStep, JobStep, MMTStep, _get_studio
|
|
8
11
|
from lightning_sdk.pipeline.utils import prepare_steps
|
|
9
12
|
from lightning_sdk.services.utilities import _get_cluster
|
|
@@ -12,9 +15,6 @@ from lightning_sdk.teamspace import Teamspace
|
|
|
12
15
|
from lightning_sdk.user import User
|
|
13
16
|
from lightning_sdk.utils.resolve import _resolve_teamspace
|
|
14
17
|
|
|
15
|
-
if TYPE_CHECKING:
|
|
16
|
-
from lightning_sdk.pipeline.schedule import Schedule
|
|
17
|
-
|
|
18
18
|
|
|
19
19
|
class Pipeline:
|
|
20
20
|
def __init__(
|
|
@@ -24,6 +24,7 @@ class Pipeline:
|
|
|
24
24
|
org: Union[str, "Organization", None] = None,
|
|
25
25
|
user: Union[str, "User", None] = None,
|
|
26
26
|
cloud_account: Optional[str] = None,
|
|
27
|
+
cloud_provider: Optional[Union[CloudProvider, str]] = None,
|
|
27
28
|
shared_filesystem: Optional[bool] = None,
|
|
28
29
|
studio: Optional[Union[Studio, str]] = None,
|
|
29
30
|
) -> None:
|
|
@@ -45,9 +46,14 @@ class Pipeline:
|
|
|
45
46
|
org=org,
|
|
46
47
|
user=user,
|
|
47
48
|
)
|
|
49
|
+
if self._teamspace is None:
|
|
50
|
+
raise RuntimeError("Could not resolve teamspace")
|
|
48
51
|
|
|
49
52
|
self._pipeline_api = PipelineApi()
|
|
50
|
-
self.
|
|
53
|
+
self._cloud_account_api = CloudAccountApi()
|
|
54
|
+
self._cloud_account = self._cloud_account_api.resolve_cloud_account(
|
|
55
|
+
self._teamspace.id, cloud_account, cloud_provider, self._teamspace.default_cloud_account
|
|
56
|
+
)
|
|
51
57
|
self._default_cluster = _get_cluster(
|
|
52
58
|
client=self._pipeline_api._client, project_id=self._teamspace.id, cluster_id=cloud_account
|
|
53
59
|
)
|
|
@@ -108,6 +114,11 @@ class Pipeline:
|
|
|
108
114
|
if schedule.name is None:
|
|
109
115
|
schedule.name = f"schedule-{schedule_idx}"
|
|
110
116
|
|
|
117
|
+
if schedule.timezone is not None and schedule.timezone not in _TIMEZONES:
|
|
118
|
+
raise ValueError(
|
|
119
|
+
f"The schedule {schedule.name} timezone isn't supported. Available list is {_TIMEZONES}. Found {schedule.timezone}." # noqa: E501
|
|
120
|
+
)
|
|
121
|
+
|
|
111
122
|
parent_pipeline_id = None if self._pipeline is None else self._pipeline.id
|
|
112
123
|
|
|
113
124
|
self._pipeline = self._pipeline_api.create_pipeline(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from typing import Any, ClassVar, Dict, List
|
|
3
3
|
|
|
4
|
-
from lightning_sdk.lightning_cloud.openapi.models import V1Pipeline, V1PipelineStepType
|
|
4
|
+
from lightning_sdk.lightning_cloud.openapi.models import V1Pipeline, V1PipelineStepType, V1Schedule
|
|
5
5
|
from lightning_sdk.pipeline.utils import _get_spec
|
|
6
6
|
|
|
7
7
|
|
|
@@ -21,7 +21,7 @@ class PipelinePrinter:
|
|
|
21
21
|
pipeline: V1Pipeline,
|
|
22
22
|
teamspace: Any,
|
|
23
23
|
proto_steps: List[Any],
|
|
24
|
-
schedules: List[
|
|
24
|
+
schedules: List[V1Schedule],
|
|
25
25
|
) -> None:
|
|
26
26
|
self._name = name
|
|
27
27
|
self._initial = initial
|
|
@@ -78,7 +78,9 @@ class PipelinePrinter:
|
|
|
78
78
|
return
|
|
79
79
|
|
|
80
80
|
for schedule in self._schedules:
|
|
81
|
-
self._print(
|
|
81
|
+
self._print(
|
|
82
|
+
f" - '{schedule.name}' runs on cron schedule: `{schedule.cron_expression} in timezone {schedule.timezone or 'UTC'}`" # noqa: E501
|
|
83
|
+
)
|
|
82
84
|
|
|
83
85
|
def _print_footer(self) -> None:
|
|
84
86
|
"""Prints the final link and closing message."""
|