lightning-sdk 0.2.22__py3-none-any.whl → 0.2.23__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 (57) hide show
  1. lightning_sdk/__init__.py +1 -1
  2. lightning_sdk/api/license_api.py +2 -2
  3. lightning_sdk/api/llm_api.py +2 -0
  4. lightning_sdk/api/pipeline_api.py +1 -0
  5. lightning_sdk/api/studio_api.py +2 -0
  6. lightning_sdk/cli/entrypoint.py +15 -13
  7. lightning_sdk/cli/start.py +5 -2
  8. lightning_sdk/lightning_cloud/openapi/__init__.py +8 -0
  9. lightning_sdk/lightning_cloud/openapi/api/cloud_space_service_api.py +206 -0
  10. lightning_sdk/lightning_cloud/openapi/api/cluster_service_api.py +97 -0
  11. lightning_sdk/lightning_cloud/openapi/api/pipelines_service_api.py +118 -1
  12. lightning_sdk/lightning_cloud/openapi/configuration.py +1 -1
  13. lightning_sdk/lightning_cloud/openapi/models/__init__.py +8 -0
  14. lightning_sdk/lightning_cloud/openapi/models/assistant_id_conversations_body.py +29 -3
  15. lightning_sdk/lightning_cloud/openapi/models/cloudspace_id_visibility_body.py +123 -0
  16. lightning_sdk/lightning_cloud/openapi/models/create_deployment_request_defines_a_spec_for_the_job_that_allows_for_autoscaling_jobs.py +27 -1
  17. lightning_sdk/lightning_cloud/openapi/models/metricsstream_create_body.py +27 -1
  18. lightning_sdk/lightning_cloud/openapi/models/pipelines_id_body.py +27 -1
  19. lightning_sdk/lightning_cloud/openapi/models/project_id_cloudspaces_body.py +27 -1
  20. lightning_sdk/lightning_cloud/openapi/models/v1_check_cluster_name_availability_request.py +123 -0
  21. lightning_sdk/lightning_cloud/openapi/models/v1_check_cluster_name_availability_response.py +123 -0
  22. lightning_sdk/lightning_cloud/openapi/models/v1_cloud_provider.py +1 -0
  23. lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space.py +53 -1
  24. lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_session.py +29 -3
  25. lightning_sdk/lightning_cloud/openapi/models/v1_cluster_security_options.py +27 -1
  26. lightning_sdk/lightning_cloud/openapi/models/v1_cluster_spec.py +79 -1
  27. lightning_sdk/lightning_cloud/openapi/models/v1_create_deployment_request.py +27 -1
  28. lightning_sdk/lightning_cloud/openapi/models/v1_external_cluster.py +253 -0
  29. lightning_sdk/lightning_cloud/openapi/models/v1_external_cluster_spec.py +827 -0
  30. lightning_sdk/lightning_cloud/openapi/models/v1_get_user_response.py +27 -1
  31. lightning_sdk/lightning_cloud/openapi/models/v1_instance_overprovisioning_spec.py +29 -1
  32. lightning_sdk/lightning_cloud/openapi/models/v1_lightning_run.py +53 -1
  33. lightning_sdk/lightning_cloud/openapi/models/v1_list_clusters_response.py +6 -6
  34. lightning_sdk/lightning_cloud/openapi/models/v1_list_project_clusters_response.py +6 -6
  35. lightning_sdk/lightning_cloud/openapi/models/v1_lite_published_cloud_space_response.py +513 -0
  36. lightning_sdk/lightning_cloud/openapi/models/v1_metrics_stream.py +27 -1
  37. lightning_sdk/lightning_cloud/openapi/models/v1_pipeline.py +27 -1
  38. lightning_sdk/lightning_cloud/openapi/models/v1_shared_filesystem.py +131 -1
  39. lightning_sdk/lightning_cloud/openapi/models/v1_update_cloud_space_visibility_response.py +97 -0
  40. lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +53 -79
  41. lightning_sdk/lightning_cloud/openapi/models/v1_volume.py +78 -104
  42. lightning_sdk/lightning_cloud/openapi/models/v1_volume_state.py +104 -0
  43. lightning_sdk/pipeline/__init__.py +11 -2
  44. lightning_sdk/pipeline/pipeline.py +38 -13
  45. lightning_sdk/pipeline/printer.py +29 -10
  46. lightning_sdk/pipeline/schedule.py +2 -1
  47. lightning_sdk/pipeline/{types.py → steps.py} +74 -56
  48. lightning_sdk/pipeline/utils.py +39 -2
  49. lightning_sdk/sandbox.py +157 -0
  50. lightning_sdk/services/license.py +12 -6
  51. lightning_sdk/studio.py +7 -1
  52. {lightning_sdk-0.2.22.dist-info → lightning_sdk-0.2.23.dist-info}/METADATA +1 -1
  53. {lightning_sdk-0.2.22.dist-info → lightning_sdk-0.2.23.dist-info}/RECORD +57 -48
  54. {lightning_sdk-0.2.22.dist-info → lightning_sdk-0.2.23.dist-info}/LICENSE +0 -0
  55. {lightning_sdk-0.2.22.dist-info → lightning_sdk-0.2.23.dist-info}/WHEEL +0 -0
  56. {lightning_sdk-0.2.22.dist-info → lightning_sdk-0.2.23.dist-info}/entry_points.txt +0 -0
  57. {lightning_sdk-0.2.22.dist-info → lightning_sdk-0.2.23.dist-info}/top_level.txt +0 -0
@@ -43,21 +43,20 @@ class V1Volume(object):
43
43
  swagger_types = {
44
44
  'attached_at': 'datetime',
45
45
  'availability_zone': 'str',
46
- 'cluster_id': 'str',
47
- 'created_at': 'datetime',
48
46
  'detached_at': 'datetime',
49
47
  'encrypted': 'bool',
50
- 'id': 'str',
51
48
  'iops': 'str',
49
+ 'metadata': 'V1Metadata',
52
50
  'path': 'str',
53
- 'project_id': 'str',
54
51
  'provider': 'str',
55
52
  'provider_id': 'str',
56
53
  'region': 'str',
57
54
  'resource_id': 'str',
58
55
  'resource_type': 'str',
56
+ 'retention_period': 'str',
59
57
  'server_id': 'str',
60
58
  'size_gb': 'str',
59
+ 'state': 'V1VolumeState',
61
60
  'throughput': 'str',
62
61
  'type': 'str',
63
62
  'updated_at': 'datetime',
@@ -67,46 +66,44 @@ class V1Volume(object):
67
66
  attribute_map = {
68
67
  'attached_at': 'attachedAt',
69
68
  'availability_zone': 'availabilityZone',
70
- 'cluster_id': 'clusterId',
71
- 'created_at': 'createdAt',
72
69
  'detached_at': 'detachedAt',
73
70
  'encrypted': 'encrypted',
74
- 'id': 'id',
75
71
  'iops': 'iops',
72
+ 'metadata': 'metadata',
76
73
  'path': 'path',
77
- 'project_id': 'projectId',
78
74
  'provider': 'provider',
79
75
  'provider_id': 'providerId',
80
76
  'region': 'region',
81
77
  'resource_id': 'resourceId',
82
78
  'resource_type': 'resourceType',
79
+ 'retention_period': 'retentionPeriod',
83
80
  'server_id': 'serverId',
84
81
  'size_gb': 'sizeGb',
82
+ 'state': 'state',
85
83
  'throughput': 'throughput',
86
84
  'type': 'type',
87
85
  'updated_at': 'updatedAt',
88
86
  'user_id': 'userId'
89
87
  }
90
88
 
91
- def __init__(self, attached_at: 'datetime' =None, availability_zone: 'str' =None, cluster_id: 'str' =None, created_at: 'datetime' =None, detached_at: 'datetime' =None, encrypted: 'bool' =None, id: 'str' =None, iops: 'str' =None, path: 'str' =None, project_id: 'str' =None, provider: 'str' =None, provider_id: 'str' =None, region: 'str' =None, resource_id: 'str' =None, resource_type: 'str' =None, server_id: 'str' =None, size_gb: 'str' =None, throughput: 'str' =None, type: 'str' =None, updated_at: 'datetime' =None, user_id: 'str' =None): # noqa: E501
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
92
90
  """V1Volume - a model defined in Swagger""" # noqa: E501
93
91
  self._attached_at = None
94
92
  self._availability_zone = None
95
- self._cluster_id = None
96
- self._created_at = None
97
93
  self._detached_at = None
98
94
  self._encrypted = None
99
- self._id = None
100
95
  self._iops = None
96
+ self._metadata = None
101
97
  self._path = None
102
- self._project_id = None
103
98
  self._provider = None
104
99
  self._provider_id = None
105
100
  self._region = None
106
101
  self._resource_id = None
107
102
  self._resource_type = None
103
+ self._retention_period = None
108
104
  self._server_id = None
109
105
  self._size_gb = None
106
+ self._state = None
110
107
  self._throughput = None
111
108
  self._type = None
112
109
  self._updated_at = None
@@ -116,22 +113,16 @@ class V1Volume(object):
116
113
  self.attached_at = attached_at
117
114
  if availability_zone is not None:
118
115
  self.availability_zone = availability_zone
119
- if cluster_id is not None:
120
- self.cluster_id = cluster_id
121
- if created_at is not None:
122
- self.created_at = created_at
123
116
  if detached_at is not None:
124
117
  self.detached_at = detached_at
125
118
  if encrypted is not None:
126
119
  self.encrypted = encrypted
127
- if id is not None:
128
- self.id = id
129
120
  if iops is not None:
130
121
  self.iops = iops
122
+ if metadata is not None:
123
+ self.metadata = metadata
131
124
  if path is not None:
132
125
  self.path = path
133
- if project_id is not None:
134
- self.project_id = project_id
135
126
  if provider is not None:
136
127
  self.provider = provider
137
128
  if provider_id is not None:
@@ -142,10 +133,14 @@ class V1Volume(object):
142
133
  self.resource_id = resource_id
143
134
  if resource_type is not None:
144
135
  self.resource_type = resource_type
136
+ if retention_period is not None:
137
+ self.retention_period = retention_period
145
138
  if server_id is not None:
146
139
  self.server_id = server_id
147
140
  if size_gb is not None:
148
141
  self.size_gb = size_gb
142
+ if state is not None:
143
+ self.state = state
149
144
  if throughput is not None:
150
145
  self.throughput = throughput
151
146
  if type is not None:
@@ -197,48 +192,6 @@ class V1Volume(object):
197
192
 
198
193
  self._availability_zone = availability_zone
199
194
 
200
- @property
201
- def cluster_id(self) -> 'str':
202
- """Gets the cluster_id of this V1Volume. # noqa: E501
203
-
204
-
205
- :return: The cluster_id of this V1Volume. # noqa: E501
206
- :rtype: str
207
- """
208
- return self._cluster_id
209
-
210
- @cluster_id.setter
211
- def cluster_id(self, cluster_id: 'str'):
212
- """Sets the cluster_id of this V1Volume.
213
-
214
-
215
- :param cluster_id: The cluster_id of this V1Volume. # noqa: E501
216
- :type: str
217
- """
218
-
219
- self._cluster_id = cluster_id
220
-
221
- @property
222
- def created_at(self) -> 'datetime':
223
- """Gets the created_at of this V1Volume. # noqa: E501
224
-
225
-
226
- :return: The created_at of this V1Volume. # noqa: E501
227
- :rtype: datetime
228
- """
229
- return self._created_at
230
-
231
- @created_at.setter
232
- def created_at(self, created_at: 'datetime'):
233
- """Sets the created_at of this V1Volume.
234
-
235
-
236
- :param created_at: The created_at of this V1Volume. # noqa: E501
237
- :type: datetime
238
- """
239
-
240
- self._created_at = created_at
241
-
242
195
  @property
243
196
  def detached_at(self) -> 'datetime':
244
197
  """Gets the detached_at of this V1Volume. # noqa: E501
@@ -281,27 +234,6 @@ class V1Volume(object):
281
234
 
282
235
  self._encrypted = encrypted
283
236
 
284
- @property
285
- def id(self) -> 'str':
286
- """Gets the id of this V1Volume. # noqa: E501
287
-
288
-
289
- :return: The id of this V1Volume. # noqa: E501
290
- :rtype: str
291
- """
292
- return self._id
293
-
294
- @id.setter
295
- def id(self, id: 'str'):
296
- """Sets the id of this V1Volume.
297
-
298
-
299
- :param id: The id of this V1Volume. # noqa: E501
300
- :type: str
301
- """
302
-
303
- self._id = id
304
-
305
237
  @property
306
238
  def iops(self) -> 'str':
307
239
  """Gets the iops of this V1Volume. # noqa: E501
@@ -324,46 +256,46 @@ class V1Volume(object):
324
256
  self._iops = iops
325
257
 
326
258
  @property
327
- def path(self) -> 'str':
328
- """Gets the path of this V1Volume. # noqa: E501
259
+ def metadata(self) -> 'V1Metadata':
260
+ """Gets the metadata of this V1Volume. # noqa: E501
329
261
 
330
262
 
331
- :return: The path of this V1Volume. # noqa: E501
332
- :rtype: str
263
+ :return: The metadata of this V1Volume. # noqa: E501
264
+ :rtype: V1Metadata
333
265
  """
334
- return self._path
266
+ return self._metadata
335
267
 
336
- @path.setter
337
- def path(self, path: 'str'):
338
- """Sets the path of this V1Volume.
268
+ @metadata.setter
269
+ def metadata(self, metadata: 'V1Metadata'):
270
+ """Sets the metadata of this V1Volume.
339
271
 
340
272
 
341
- :param path: The path of this V1Volume. # noqa: E501
342
- :type: str
273
+ :param metadata: The metadata of this V1Volume. # noqa: E501
274
+ :type: V1Metadata
343
275
  """
344
276
 
345
- self._path = path
277
+ self._metadata = metadata
346
278
 
347
279
  @property
348
- def project_id(self) -> 'str':
349
- """Gets the project_id of this V1Volume. # noqa: E501
280
+ def path(self) -> 'str':
281
+ """Gets the path of this V1Volume. # noqa: E501
350
282
 
351
283
 
352
- :return: The project_id of this V1Volume. # noqa: E501
284
+ :return: The path of this V1Volume. # noqa: E501
353
285
  :rtype: str
354
286
  """
355
- return self._project_id
287
+ return self._path
356
288
 
357
- @project_id.setter
358
- def project_id(self, project_id: 'str'):
359
- """Sets the project_id of this V1Volume.
289
+ @path.setter
290
+ def path(self, path: 'str'):
291
+ """Sets the path of this V1Volume.
360
292
 
361
293
 
362
- :param project_id: The project_id of this V1Volume. # noqa: E501
294
+ :param path: The path of this V1Volume. # noqa: E501
363
295
  :type: str
364
296
  """
365
297
 
366
- self._project_id = project_id
298
+ self._path = path
367
299
 
368
300
  @property
369
301
  def provider(self) -> 'str':
@@ -470,6 +402,27 @@ class V1Volume(object):
470
402
 
471
403
  self._resource_type = resource_type
472
404
 
405
+ @property
406
+ def retention_period(self) -> 'str':
407
+ """Gets the retention_period of this V1Volume. # noqa: E501
408
+
409
+
410
+ :return: The retention_period of this V1Volume. # noqa: E501
411
+ :rtype: str
412
+ """
413
+ return self._retention_period
414
+
415
+ @retention_period.setter
416
+ def retention_period(self, retention_period: 'str'):
417
+ """Sets the retention_period of this V1Volume.
418
+
419
+
420
+ :param retention_period: The retention_period of this V1Volume. # noqa: E501
421
+ :type: str
422
+ """
423
+
424
+ self._retention_period = retention_period
425
+
473
426
  @property
474
427
  def server_id(self) -> 'str':
475
428
  """Gets the server_id of this V1Volume. # noqa: E501
@@ -512,6 +465,27 @@ class V1Volume(object):
512
465
 
513
466
  self._size_gb = size_gb
514
467
 
468
+ @property
469
+ def state(self) -> 'V1VolumeState':
470
+ """Gets the state of this V1Volume. # noqa: E501
471
+
472
+
473
+ :return: The state of this V1Volume. # noqa: E501
474
+ :rtype: V1VolumeState
475
+ """
476
+ return self._state
477
+
478
+ @state.setter
479
+ def state(self, state: 'V1VolumeState'):
480
+ """Sets the state of this V1Volume.
481
+
482
+
483
+ :param state: The state of this V1Volume. # noqa: E501
484
+ :type: V1VolumeState
485
+ """
486
+
487
+ self._state = state
488
+
515
489
  @property
516
490
  def throughput(self) -> 'str':
517
491
  """Gets the throughput of this V1Volume. # noqa: E501
@@ -0,0 +1,104 @@
1
+ # coding: utf-8
2
+
3
+ """
4
+ external/v1/auth_service.proto
5
+
6
+ No description provided (generated by Swagger Codegen https://github.com/swagger-api/swagger-codegen) # noqa: E501
7
+
8
+ OpenAPI spec version: version not set
9
+
10
+ Generated by: https://github.com/swagger-api/swagger-codegen.git
11
+
12
+ NOTE
13
+ ----
14
+ standard swagger-codegen-cli for this python client has been modified
15
+ by custom templates. The purpose of these templates is to include
16
+ typing information in the API and Model code. Please refer to the
17
+ main grid repository for more info
18
+ """
19
+
20
+ import pprint
21
+ import re # noqa: F401
22
+
23
+ from typing import TYPE_CHECKING
24
+
25
+ import six
26
+
27
+ if TYPE_CHECKING:
28
+ from datetime import datetime
29
+ from lightning_sdk.lightning_cloud.openapi.models import *
30
+
31
+ class V1VolumeState(object):
32
+ """NOTE: This class is auto generated by the swagger code generator program.
33
+
34
+ Do not edit the class manually.
35
+ """
36
+
37
+ """
38
+ allowed enum values
39
+ """
40
+ UNSPECIFIED = "VOLUME_STATE_UNSPECIFIED"
41
+ ATTACHED = "VOLUME_STATE_ATTACHED"
42
+ DETACHED = "VOLUME_STATE_DETACHED"
43
+ """
44
+ Attributes:
45
+ swagger_types (dict): The key is attribute name
46
+ and the value is attribute type.
47
+ attribute_map (dict): The key is attribute name
48
+ and the value is json key in definition.
49
+ """
50
+ swagger_types = {
51
+ }
52
+
53
+ attribute_map = {
54
+ }
55
+
56
+ def __init__(self): # noqa: E501
57
+ """V1VolumeState - a model defined in Swagger""" # noqa: E501
58
+ self.discriminator = None
59
+
60
+ def to_dict(self) -> dict:
61
+ """Returns the model properties as a dict"""
62
+ result = {}
63
+
64
+ for attr, _ in six.iteritems(self.swagger_types):
65
+ value = getattr(self, attr)
66
+ if isinstance(value, list):
67
+ result[attr] = list(map(
68
+ lambda x: x.to_dict() if hasattr(x, "to_dict") else x,
69
+ value
70
+ ))
71
+ elif hasattr(value, "to_dict"):
72
+ result[attr] = value.to_dict()
73
+ elif isinstance(value, dict):
74
+ result[attr] = dict(map(
75
+ lambda item: (item[0], item[1].to_dict())
76
+ if hasattr(item[1], "to_dict") else item,
77
+ value.items()
78
+ ))
79
+ else:
80
+ result[attr] = value
81
+ if issubclass(V1VolumeState, dict):
82
+ for key, value in self.items():
83
+ result[key] = value
84
+
85
+ return result
86
+
87
+ def to_str(self) -> str:
88
+ """Returns the string representation of the model"""
89
+ return pprint.pformat(self.to_dict())
90
+
91
+ def __repr__(self) -> str:
92
+ """For `print` and `pprint`"""
93
+ return self.to_str()
94
+
95
+ def __eq__(self, other: 'V1VolumeState') -> bool:
96
+ """Returns true if both objects are equal"""
97
+ if not isinstance(other, V1VolumeState):
98
+ return False
99
+
100
+ return self.__dict__ == other.__dict__
101
+
102
+ def __ne__(self, other: 'V1VolumeState') -> bool:
103
+ """Returns true if both objects are not equal"""
104
+ return not self == other
@@ -1,5 +1,14 @@
1
+ from lightning_sdk import Studio
1
2
  from lightning_sdk.pipeline.pipeline import Pipeline
2
3
  from lightning_sdk.pipeline.schedule import Schedule
3
- from lightning_sdk.pipeline.types import MMT, Deployment, Job
4
+ from lightning_sdk.pipeline.steps import DeploymentReleaseStep, DeploymentStep, JobStep, MMTStep
4
5
 
5
- __all__ = ["Pipeline", "Job", "MMT", "Deployment", "Schedule"]
6
+ __all__ = [
7
+ "Pipeline",
8
+ "JobStep",
9
+ "MMTStep",
10
+ "DeploymentStep",
11
+ "Schedule",
12
+ "Studio",
13
+ "DeploymentReleaseStep",
14
+ ]
@@ -1,3 +1,4 @@
1
+ import os
1
2
  from typing import TYPE_CHECKING, List, Optional, Union
2
3
 
3
4
  from lightning_sdk.api import UserApi
@@ -5,9 +6,10 @@ from lightning_sdk.api.pipeline_api import PipelineApi
5
6
  from lightning_sdk.lightning_cloud.login import Auth
6
7
  from lightning_sdk.organization import Organization
7
8
  from lightning_sdk.pipeline.printer import PipelinePrinter
8
- from lightning_sdk.pipeline.types import MMT, Deployment, Job
9
+ from lightning_sdk.pipeline.steps import DeploymentStep, JobStep, MMTStep, _get_studio
9
10
  from lightning_sdk.pipeline.utils import prepare_steps
10
11
  from lightning_sdk.services.utilities import _get_cluster
12
+ from lightning_sdk.studio import Studio
11
13
  from lightning_sdk.teamspace import Teamspace
12
14
  from lightning_sdk.user import User
13
15
  from lightning_sdk.utils.resolve import _resolve_org, _resolve_teamspace, _resolve_user
@@ -25,6 +27,7 @@ class Pipeline:
25
27
  user: Union[str, "User", None] = None,
26
28
  cloud_account: Optional[str] = None,
27
29
  shared_filesystem: Optional[bool] = None,
30
+ studio: Optional[Union[Studio, str]] = None,
28
31
  ) -> None:
29
32
  """The Lightning Pipeline can be used to create complex DAG.
30
33
 
@@ -58,10 +61,12 @@ class Pipeline:
58
61
  )
59
62
 
60
63
  self._pipeline_api = PipelineApi()
61
- self._cloud_account = _get_cluster(
64
+ self._cloud_account = cloud_account
65
+ self._default_cluster = _get_cluster(
62
66
  client=self._pipeline_api._client, project_id=self._teamspace.id, cluster_id=cloud_account
63
67
  )
64
- self._shared_filesystem = shared_filesystem
68
+ self._shared_filesystem = shared_filesystem if shared_filesystem is not None else True
69
+ self._studio = _get_studio(studio)
65
70
  self._is_created = False
66
71
 
67
72
  pipeline = None
@@ -75,18 +80,29 @@ class Pipeline:
75
80
  else:
76
81
  self._pipeline = None
77
82
 
78
- def run(self, steps: List[Union[Job, Deployment, MMT]], schedules: Optional[List["Schedule"]] = None) -> None:
83
+ def run(
84
+ self, steps: List[Union[JobStep, DeploymentStep, MMTStep]], schedules: Optional[List["Schedule"]] = None
85
+ ) -> None:
79
86
  if len(steps) == 0:
80
87
  raise ValueError("The provided steps is empty")
81
88
 
82
- for step_idx, step in enumerate(steps):
83
- if step.name in [None, ""]:
84
- raise ValueError(f"The step {step_idx} requires a name")
89
+ for step_idx, pipeline_step in enumerate(steps):
90
+ if pipeline_step.name in [None, ""]:
91
+ pipeline_step.name = f"step-{step_idx}"
92
+
93
+ if (
94
+ self._studio is not None
95
+ and (pipeline_step.image == "" or pipeline_step.image is None)
96
+ and pipeline_step.studio is None
97
+ ):
98
+ pipeline_step.cloud_account = self._studio.cloud_account
99
+ pipeline_step.studio = self._studio
100
+
101
+ cluster_ids = set(step.cloud_account for step in steps if step.cloud_account not in ["", None]) # noqa: C401
102
+
103
+ cloud_account = list(cluster_ids)[0] if len(cluster_ids) == 1 and self._cloud_account is None else "" # noqa: RUF015
85
104
 
86
- steps = [
87
- step.to_proto(self._teamspace, self._cloud_account.cluster_id or "", self._shared_filesystem)
88
- for step in steps
89
- ]
105
+ steps = [step.to_proto(self._teamspace, cloud_account, self._shared_filesystem) for step in steps]
90
106
 
91
107
  proto_steps = prepare_steps(steps)
92
108
  schedules = schedules or []
@@ -97,13 +113,18 @@ class Pipeline:
97
113
  self._name,
98
114
  self._teamspace.id,
99
115
  proto_steps,
100
- self._shared_filesystem or False,
116
+ self._shared_filesystem,
101
117
  schedules,
102
118
  parent_pipeline_id,
103
119
  )
104
120
 
105
121
  printer = PipelinePrinter(
106
- self._name, parent_pipeline_id is None, self._pipeline, self._teamspace, proto_steps, schedules
122
+ self._name,
123
+ parent_pipeline_id is None,
124
+ self._pipeline,
125
+ self._teamspace,
126
+ proto_steps,
127
+ schedules,
107
128
  )
108
129
  printer.print_summary()
109
130
 
@@ -124,3 +145,7 @@ class Pipeline:
124
145
  if self._pipeline:
125
146
  return self._pipeline.name
126
147
  return None
148
+
149
+ @classmethod
150
+ def from_env(cls) -> "Pipeline":
151
+ return Pipeline(name=os.getenv("LIGHTNING_PIPELINE_ID", ""))
@@ -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 V1JobSpec, V1PipelineStepType
4
+ from lightning_sdk.lightning_cloud.openapi.models import V1JobSpec, V1Pipeline, V1PipelineStepType
5
5
 
6
6
 
7
7
  class PipelinePrinter:
@@ -14,14 +14,26 @@ class PipelinePrinter:
14
14
  }
15
15
 
16
16
  def __init__(
17
- self, name: str, initial: bool, pipeline: Any, teamspace: Any, proto_steps: List[Any], schedules: List[Any]
17
+ self,
18
+ name: str,
19
+ initial: bool,
20
+ pipeline: V1Pipeline,
21
+ teamspace: Any,
22
+ proto_steps: List[Any],
23
+ schedules: List[Any],
18
24
  ) -> None:
19
25
  self._name = name
20
26
  self._initial = initial
21
27
  self._pipeline = pipeline
22
28
  self._teamspace = teamspace
23
29
  self._proto_steps = proto_steps
30
+ self._shared_filesystem = pipeline.shared_filesystem
24
31
  self._schedules = schedules
32
+ cluster_ids: set[str] = set()
33
+ for step in self._proto_steps:
34
+ job_spec = self._get_spec(step)
35
+ cluster_ids.add(job_spec.cluster_id)
36
+ self._cluster_ids = cluster_ids
25
37
 
26
38
  def print_summary(self) -> None:
27
39
  """Prints the full, formatted summary of the created pipeline."""
@@ -32,6 +44,7 @@ class PipelinePrinter:
32
44
  self._print_steps()
33
45
  self._print_schedules()
34
46
  self._print_cloud_account()
47
+ self._print_shared_filesystem()
35
48
  self._print_footer()
36
49
 
37
50
  def _print(self, value: str) -> None:
@@ -75,7 +88,7 @@ class PipelinePrinter:
75
88
  team: str = self._teamspace.name
76
89
  pipeline_name: str = self._name
77
90
 
78
- pipeline_url = f"{cloud_url}/{owner}/{team}/pipelines/{pipeline_name}?app_id=pipeline&section=Graph"
91
+ pipeline_url = f"{cloud_url}/{owner}/{team}/pipelines/{pipeline_name}?app_id=pipeline"
79
92
 
80
93
  self._print("\n" + "─" * 60)
81
94
  self._print(f"🔗 View your pipeline in the browser:\n {pipeline_url}")
@@ -85,15 +98,21 @@ class PipelinePrinter:
85
98
  if not self._proto_steps:
86
99
  return
87
100
 
88
- cluster_ids: set[str] = set()
89
- for step in self._proto_steps:
90
- job_spec = self._get_spec(step)
91
- cluster_ids.add(job_spec.cluster_id)
92
-
93
- self._print(f"\nCloud account{'s' if len(cluster_ids) > 1 else ''}:")
94
- for cluster_id in sorted(cluster_ids):
101
+ self._print(f"\nCloud account{'s' if len(self._cluster_ids) > 1 else ''}:")
102
+ for cluster_id in sorted(self._cluster_ids):
95
103
  self._print(f" - {cluster_id}")
96
104
 
105
+ def _print_shared_filesystem(self) -> None:
106
+ self._print(f"\nShared filesystem: {self._shared_filesystem.enabled}")
107
+
108
+ if self._shared_filesystem.enabled and len(self._cluster_ids) == 1:
109
+ shared_path = ""
110
+ if self._pipeline.shared_filesystem.s3_folder:
111
+ cluster_id = list(self._cluster_ids)[0] # noqa: RUF015
112
+ shared_path = f"/teamspace/s3_folders/pipelines-{cluster_id}"
113
+ if shared_path:
114
+ self._print(f" - {shared_path}")
115
+
97
116
  def _get_spec(self, step: Any) -> V1JobSpec:
98
117
  if step.type == V1PipelineStepType.DEPLOYMENT:
99
118
  return step.deployment.spec
@@ -1,7 +1,8 @@
1
1
  from dataclasses import dataclass
2
+ from typing import Optional
2
3
 
3
4
 
4
5
  @dataclass
5
6
  class Schedule:
6
- name: str
7
7
  cron_expression: str
8
+ name: Optional[str] = None