lightning-sdk 0.2.21rc1__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 (70) 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 +69 -27
  4. lightning_sdk/api/pipeline_api.py +49 -9
  5. lightning_sdk/api/studio_api.py +2 -0
  6. lightning_sdk/cli/configure.py +34 -27
  7. lightning_sdk/cli/connect.py +2 -2
  8. lightning_sdk/cli/download.py +38 -2
  9. lightning_sdk/cli/entrypoint.py +15 -13
  10. lightning_sdk/cli/start.py +5 -2
  11. lightning_sdk/lightning_cloud/openapi/__init__.py +9 -0
  12. lightning_sdk/lightning_cloud/openapi/api/cloud_space_service_api.py +206 -0
  13. lightning_sdk/lightning_cloud/openapi/api/cluster_service_api.py +98 -5
  14. lightning_sdk/lightning_cloud/openapi/api/lit_registry_service_api.py +113 -0
  15. lightning_sdk/lightning_cloud/openapi/api/pipelines_service_api.py +118 -1
  16. lightning_sdk/lightning_cloud/openapi/api/schedules_service_api.py +5 -1
  17. lightning_sdk/lightning_cloud/openapi/models/__init__.py +9 -0
  18. lightning_sdk/lightning_cloud/openapi/models/assistant_id_conversations_body.py +29 -3
  19. lightning_sdk/lightning_cloud/openapi/models/cloudspace_id_visibility_body.py +123 -0
  20. lightning_sdk/lightning_cloud/openapi/models/create_deployment_request_defines_a_spec_for_the_job_that_allows_for_autoscaling_jobs.py +27 -1
  21. lightning_sdk/lightning_cloud/openapi/models/metricsstream_create_body.py +27 -1
  22. lightning_sdk/lightning_cloud/openapi/models/pipelines_id_body.py +27 -1
  23. lightning_sdk/lightning_cloud/openapi/models/project_id_cloudspaces_body.py +27 -1
  24. lightning_sdk/lightning_cloud/openapi/models/project_id_pipelines_body.py +27 -1
  25. lightning_sdk/lightning_cloud/openapi/models/project_id_schedules_body.py +27 -1
  26. lightning_sdk/lightning_cloud/openapi/models/schedules_id_body.py +27 -1
  27. lightning_sdk/lightning_cloud/openapi/models/v1_check_cluster_name_availability_request.py +123 -0
  28. lightning_sdk/lightning_cloud/openapi/models/v1_check_cluster_name_availability_response.py +123 -0
  29. lightning_sdk/lightning_cloud/openapi/models/v1_cloud_provider.py +1 -0
  30. lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space.py +53 -1
  31. lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_session.py +29 -3
  32. lightning_sdk/lightning_cloud/openapi/models/v1_cluster_security_options.py +27 -1
  33. lightning_sdk/lightning_cloud/openapi/models/v1_cluster_spec.py +79 -1
  34. lightning_sdk/lightning_cloud/openapi/models/v1_create_deployment_request.py +27 -1
  35. lightning_sdk/lightning_cloud/openapi/models/v1_delete_lit_registry_repository_image_artifact_version_by_digest_response.py +97 -0
  36. lightning_sdk/lightning_cloud/openapi/models/v1_external_cluster.py +253 -0
  37. lightning_sdk/lightning_cloud/openapi/models/v1_external_cluster_spec.py +827 -0
  38. lightning_sdk/lightning_cloud/openapi/models/v1_get_project_storage_metadata_response.py +27 -1
  39. lightning_sdk/lightning_cloud/openapi/models/v1_get_user_response.py +105 -1
  40. lightning_sdk/lightning_cloud/openapi/models/v1_get_user_storage_breakdown_response.py +53 -1
  41. lightning_sdk/lightning_cloud/openapi/models/v1_instance_overprovisioning_spec.py +29 -1
  42. lightning_sdk/lightning_cloud/openapi/models/v1_lightning_run.py +53 -1
  43. lightning_sdk/lightning_cloud/openapi/models/v1_list_clusters_response.py +6 -6
  44. lightning_sdk/lightning_cloud/openapi/models/v1_list_project_clusters_response.py +6 -6
  45. lightning_sdk/lightning_cloud/openapi/models/v1_lite_published_cloud_space_response.py +513 -0
  46. lightning_sdk/lightning_cloud/openapi/models/v1_metrics_stream.py +27 -1
  47. lightning_sdk/lightning_cloud/openapi/models/v1_pipeline.py +27 -1
  48. lightning_sdk/lightning_cloud/openapi/models/v1_schedule.py +27 -1
  49. lightning_sdk/lightning_cloud/openapi/models/v1_shared_filesystem.py +131 -1
  50. lightning_sdk/lightning_cloud/openapi/models/v1_update_cloud_space_visibility_response.py +97 -0
  51. lightning_sdk/lightning_cloud/openapi/models/v1_update_user_request.py +79 -1
  52. lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +53 -183
  53. lightning_sdk/lightning_cloud/openapi/models/v1_volume.py +78 -104
  54. lightning_sdk/lightning_cloud/openapi/models/v1_volume_state.py +104 -0
  55. lightning_sdk/llm/llm.py +6 -1
  56. lightning_sdk/pipeline/__init__.py +12 -2
  57. lightning_sdk/pipeline/pipeline.py +59 -17
  58. lightning_sdk/pipeline/printer.py +121 -0
  59. lightning_sdk/pipeline/schedule.py +8 -0
  60. lightning_sdk/pipeline/{types.py → steps.py} +77 -59
  61. lightning_sdk/pipeline/utils.py +39 -17
  62. lightning_sdk/sandbox.py +157 -0
  63. lightning_sdk/services/license.py +12 -6
  64. lightning_sdk/studio.py +7 -1
  65. {lightning_sdk-0.2.21rc1.dist-info → lightning_sdk-0.2.23.dist-info}/METADATA +1 -1
  66. {lightning_sdk-0.2.21rc1.dist-info → lightning_sdk-0.2.23.dist-info}/RECORD +70 -58
  67. {lightning_sdk-0.2.21rc1.dist-info → lightning_sdk-0.2.23.dist-info}/LICENSE +0 -0
  68. {lightning_sdk-0.2.21rc1.dist-info → lightning_sdk-0.2.23.dist-info}/WHEEL +0 -0
  69. {lightning_sdk-0.2.21rc1.dist-info → lightning_sdk-0.2.23.dist-info}/entry_points.txt +0 -0
  70. {lightning_sdk-0.2.21rc1.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
lightning_sdk/llm/llm.py CHANGED
@@ -177,6 +177,11 @@ class LLM:
177
177
  for line in result:
178
178
  yield line.choices[0].delta.content
179
179
 
180
+ async def _async_stream_text(self, output: str) -> AsyncGenerator[str, None]:
181
+ async for chunk in output:
182
+ if chunk.choices and chunk.choices[0].delta:
183
+ yield chunk.choices[0].delta.content
184
+
180
185
  async def _async_chat(
181
186
  self,
182
187
  prompt: str,
@@ -205,7 +210,7 @@ class LLM:
205
210
  if conversation and not conversation_id:
206
211
  self._conversations[conversation] = output.conversation_id
207
212
  return output.choices[0].delta.content
208
- raise NotImplementedError("Streaming is not supported in this client.")
213
+ return self._async_stream_text(output)
209
214
 
210
215
  def chat(
211
216
  self,
@@ -1,4 +1,14 @@
1
+ from lightning_sdk import Studio
1
2
  from lightning_sdk.pipeline.pipeline import Pipeline
2
- from lightning_sdk.pipeline.types import MMT, Deployment, Job
3
+ from lightning_sdk.pipeline.schedule import Schedule
4
+ from lightning_sdk.pipeline.steps import DeploymentReleaseStep, DeploymentStep, JobStep, MMTStep
3
5
 
4
- __all__ = ["Pipeline", "Job", "MMT", "Deployment"]
6
+ __all__ = [
7
+ "Pipeline",
8
+ "JobStep",
9
+ "MMTStep",
10
+ "DeploymentStep",
11
+ "Schedule",
12
+ "Studio",
13
+ "DeploymentReleaseStep",
14
+ ]
@@ -1,16 +1,22 @@
1
- from typing import List, Optional, Union
1
+ import os
2
+ from typing import TYPE_CHECKING, List, Optional, Union
2
3
 
3
4
  from lightning_sdk.api import UserApi
4
5
  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
- from lightning_sdk.pipeline.types import MMT, Deployment, Job
8
+ from lightning_sdk.pipeline.printer import PipelinePrinter
9
+ from lightning_sdk.pipeline.steps import DeploymentStep, JobStep, MMTStep, _get_studio
8
10
  from lightning_sdk.pipeline.utils import prepare_steps
9
11
  from lightning_sdk.services.utilities import _get_cluster
12
+ from lightning_sdk.studio import Studio
10
13
  from lightning_sdk.teamspace import Teamspace
11
14
  from lightning_sdk.user import User
12
15
  from lightning_sdk.utils.resolve import _resolve_org, _resolve_teamspace, _resolve_user
13
16
 
17
+ if TYPE_CHECKING:
18
+ from lightning_sdk.pipeline.schedule import Schedule
19
+
14
20
 
15
21
  class Pipeline:
16
22
  def __init__(
@@ -21,6 +27,7 @@ class Pipeline:
21
27
  user: Union[str, "User", None] = None,
22
28
  cloud_account: Optional[str] = None,
23
29
  shared_filesystem: Optional[bool] = None,
30
+ studio: Optional[Union[Studio, str]] = None,
24
31
  ) -> None:
25
32
  """The Lightning Pipeline can be used to create complex DAG.
26
33
 
@@ -54,42 +61,73 @@ class Pipeline:
54
61
  )
55
62
 
56
63
  self._pipeline_api = PipelineApi()
57
- self._cloud_account = _get_cluster(
64
+ self._cloud_account = cloud_account
65
+ self._default_cluster = _get_cluster(
58
66
  client=self._pipeline_api._client, project_id=self._teamspace.id, cluster_id=cloud_account
59
67
  )
60
- 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)
61
70
  self._is_created = False
62
71
 
63
72
  pipeline = None
64
73
 
65
- if name.startswith("pip_"):
66
- pipeline = self._pipeline_api.get_pipeline_by_id(self._teamspace.id, name)
74
+ pipeline = self._pipeline_api.get_pipeline_by_id(self._teamspace.id, name)
67
75
 
68
76
  if pipeline:
69
77
  self._name = pipeline.name
70
78
  self._is_created = True
71
79
  self._pipeline = pipeline
80
+ else:
81
+ self._pipeline = None
72
82
 
73
- def run(self, steps: List[Union[Job, Deployment, MMT]]) -> None:
83
+ def run(
84
+ self, steps: List[Union[JobStep, DeploymentStep, MMTStep]], schedules: Optional[List["Schedule"]] = None
85
+ ) -> None:
74
86
  if len(steps) == 0:
75
87
  raise ValueError("The provided steps is empty")
76
88
 
77
- for step_idx, step in enumerate(steps):
78
- if step.name in [None, ""]:
79
- 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
104
+
105
+ steps = [step.to_proto(self._teamspace, cloud_account, self._shared_filesystem) for step in steps]
80
106
 
81
- steps = [
82
- step.to_proto(self._teamspace, self._cloud_account.cluster_id or "", self._shared_filesystem)
83
- for step in steps
84
- ]
107
+ proto_steps = prepare_steps(steps)
108
+ schedules = schedules or []
109
+
110
+ parent_pipeline_id = None if self._pipeline is None else self._pipeline.id
85
111
 
86
112
  self._pipeline = self._pipeline_api.create_pipeline(
87
113
  self._name,
88
114
  self._teamspace.id,
89
- prepare_steps(steps),
90
- self._shared_filesystem or False,
115
+ proto_steps,
116
+ self._shared_filesystem,
117
+ schedules,
118
+ parent_pipeline_id,
91
119
  )
92
120
 
121
+ printer = PipelinePrinter(
122
+ self._name,
123
+ parent_pipeline_id is None,
124
+ self._pipeline,
125
+ self._teamspace,
126
+ proto_steps,
127
+ schedules,
128
+ )
129
+ printer.print_summary()
130
+
93
131
  def stop(self) -> None:
94
132
  if self._pipeline is None:
95
133
  return
@@ -103,7 +141,11 @@ class Pipeline:
103
141
  self._pipeline_api.delete(self._teamspace.id, self._pipeline.id)
104
142
 
105
143
  @property
106
- def name(self) -> str:
144
+ def name(self) -> Optional[str]:
107
145
  if self._pipeline:
108
146
  return self._pipeline.name
109
147
  return None
148
+
149
+ @classmethod
150
+ def from_env(cls) -> "Pipeline":
151
+ return Pipeline(name=os.getenv("LIGHTNING_PIPELINE_ID", ""))