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.
Files changed (93) hide show
  1. lightning_sdk/__init__.py +3 -2
  2. lightning_sdk/api/cloud_account_api.py +154 -0
  3. lightning_sdk/api/deployment_api.py +11 -0
  4. lightning_sdk/api/job_api.py +9 -0
  5. lightning_sdk/api/llm_api.py +11 -6
  6. lightning_sdk/api/mmt_api.py +9 -0
  7. lightning_sdk/api/pipeline_api.py +4 -3
  8. lightning_sdk/api/studio_api.py +19 -5
  9. lightning_sdk/cli/clusters_menu.py +3 -3
  10. lightning_sdk/cli/create.py +22 -10
  11. lightning_sdk/cli/deploy/_auth.py +19 -3
  12. lightning_sdk/cli/deploy/serve.py +18 -4
  13. lightning_sdk/cli/entrypoint.py +1 -1
  14. lightning_sdk/cli/start.py +37 -7
  15. lightning_sdk/deployment/deployment.py +8 -0
  16. lightning_sdk/job/base.py +37 -5
  17. lightning_sdk/job/job.py +28 -4
  18. lightning_sdk/job/v1.py +10 -1
  19. lightning_sdk/job/v2.py +15 -1
  20. lightning_sdk/lightning_cloud/openapi/__init__.py +15 -1
  21. lightning_sdk/lightning_cloud/openapi/api/assistants_service_api.py +335 -0
  22. lightning_sdk/lightning_cloud/openapi/api/billing_service_api.py +214 -0
  23. lightning_sdk/lightning_cloud/openapi/api/cluster_service_api.py +5 -1
  24. lightning_sdk/lightning_cloud/openapi/api/user_service_api.py +11 -11
  25. lightning_sdk/lightning_cloud/openapi/models/__init__.py +15 -1
  26. lightning_sdk/lightning_cloud/openapi/models/assistant_id_conversations_body.py +29 -3
  27. lightning_sdk/lightning_cloud/openapi/models/blogposts_id_body.py +53 -1
  28. lightning_sdk/lightning_cloud/openapi/models/{v1_list_new_features_for_user_response.py → conversations_id_body1.py} +23 -23
  29. lightning_sdk/lightning_cloud/openapi/models/messages_id_body.py +123 -0
  30. lightning_sdk/lightning_cloud/openapi/models/project_id_schedules_body.py +29 -3
  31. lightning_sdk/lightning_cloud/openapi/models/project_id_storage_body.py +1 -27
  32. lightning_sdk/lightning_cloud/openapi/models/protobuf_null_value.py +102 -0
  33. lightning_sdk/lightning_cloud/openapi/models/schedules_id_body.py +27 -1
  34. lightning_sdk/lightning_cloud/openapi/models/storage_complete_body.py +1 -27
  35. lightning_sdk/lightning_cloud/openapi/models/uploads_upload_id_body1.py +3 -55
  36. lightning_sdk/lightning_cloud/openapi/models/user_id_upgradetrigger_body.py +175 -0
  37. lightning_sdk/lightning_cloud/openapi/models/v1_ai_pod_v1.py +123 -0
  38. lightning_sdk/lightning_cloud/openapi/models/v1_artifact.py +27 -1
  39. lightning_sdk/lightning_cloud/openapi/models/v1_assistant_session_daily_aggregated.py +357 -0
  40. lightning_sdk/lightning_cloud/openapi/models/v1_blog_post.py +53 -1
  41. lightning_sdk/lightning_cloud/openapi/models/v1_cloud_provider.py +1 -0
  42. lightning_sdk/lightning_cloud/openapi/models/v1_cluster_spec.py +27 -1
  43. lightning_sdk/lightning_cloud/openapi/models/v1_cluster_type.py +1 -0
  44. lightning_sdk/lightning_cloud/openapi/models/v1_complete_upload.py +3 -55
  45. lightning_sdk/lightning_cloud/openapi/models/v1_conversation.py +27 -1
  46. lightning_sdk/lightning_cloud/openapi/models/v1_create_billing_upgrade_trigger_record_response.py +97 -0
  47. lightning_sdk/lightning_cloud/openapi/models/v1_create_blog_post_request.py +53 -1
  48. lightning_sdk/lightning_cloud/openapi/models/v1_create_checkout_session_request.py +27 -1
  49. lightning_sdk/lightning_cloud/openapi/models/v1_create_subscription_checkout_session_request.py +29 -3
  50. lightning_sdk/lightning_cloud/openapi/models/v1_external_cluster_spec.py +27 -1
  51. lightning_sdk/lightning_cloud/openapi/models/v1_function_tool.py +175 -0
  52. lightning_sdk/lightning_cloud/openapi/models/v1_get_artifacts_page_response.py +29 -3
  53. lightning_sdk/lightning_cloud/openapi/models/v1_get_assistant_session_daily_aggregated_response.py +201 -0
  54. lightning_sdk/lightning_cloud/openapi/models/v1_kubernetes_direct_v1.py +79 -1
  55. lightning_sdk/lightning_cloud/openapi/models/v1_lightningapp_instance_artifact.py +27 -1
  56. lightning_sdk/lightning_cloud/openapi/models/v1_like_status.py +104 -0
  57. lightning_sdk/lightning_cloud/openapi/models/v1_list_notification_dialogs_response.py +149 -0
  58. lightning_sdk/lightning_cloud/openapi/models/v1_list_published_managed_endpoint_models_response.py +123 -0
  59. lightning_sdk/lightning_cloud/openapi/models/v1_message.py +53 -1
  60. lightning_sdk/lightning_cloud/openapi/models/v1_presigned_url.py +1 -53
  61. lightning_sdk/lightning_cloud/openapi/models/v1_project.py +27 -1
  62. lightning_sdk/lightning_cloud/openapi/models/v1_quote_subscription_response.py +27 -1
  63. lightning_sdk/lightning_cloud/openapi/models/v1_schedule.py +27 -1
  64. lightning_sdk/lightning_cloud/openapi/models/v1_tool.py +149 -0
  65. lightning_sdk/lightning_cloud/openapi/models/v1_update_conversation_like_response.py +149 -0
  66. lightning_sdk/lightning_cloud/openapi/models/v1_update_conversation_message_like_response.py +149 -0
  67. lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +105 -261
  68. lightning_sdk/lightning_cloud/openapi/models/v1_volume.py +27 -1
  69. lightning_sdk/llm/llm.py +32 -5
  70. lightning_sdk/llm/public_assistants.json +3 -1
  71. lightning_sdk/machine.py +24 -1
  72. lightning_sdk/mmt/base.py +20 -2
  73. lightning_sdk/mmt/mmt.py +25 -3
  74. lightning_sdk/mmt/v1.py +7 -1
  75. lightning_sdk/mmt/v2.py +21 -2
  76. lightning_sdk/organization.py +4 -0
  77. lightning_sdk/pipeline/pipeline.py +16 -5
  78. lightning_sdk/pipeline/printer.py +5 -3
  79. lightning_sdk/pipeline/schedule.py +844 -1
  80. lightning_sdk/pipeline/steps.py +19 -4
  81. lightning_sdk/sandbox.py +4 -1
  82. lightning_sdk/serve.py +2 -0
  83. lightning_sdk/studio.py +79 -39
  84. lightning_sdk/teamspace.py +14 -8
  85. lightning_sdk/utils/resolve.py +29 -2
  86. {lightning_sdk-2025.7.10.dist-info → lightning_sdk-2025.7.22.dist-info}/METADATA +1 -1
  87. {lightning_sdk-2025.7.10.dist-info → lightning_sdk-2025.7.22.dist-info}/RECORD +92 -78
  88. lightning_sdk/api/cluster_api.py +0 -119
  89. /lightning_sdk/cli/{inspect.py → inspection.py} +0 -0
  90. {lightning_sdk-2025.7.10.dist-info → lightning_sdk-2025.7.22.dist-info}/LICENSE +0 -0
  91. {lightning_sdk-2025.7.10.dist-info → lightning_sdk-2025.7.22.dist-info}/WHEEL +0 -0
  92. {lightning_sdk-2025.7.10.dist-info → lightning_sdk-2025.7.22.dist-info}/entry_points.txt +0 -0
  93. {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
- # TODO support user input teamspace
58
- self._get_auth_info()
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 = os.environ.get("LIGHTNING_TEAMSPACE", None)
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="p5e.48xlarge")
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 will fall back to the teamspaces default cloud account.
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 will fall back to the teamspaces default cloud account.
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 will fall back to the teamspaces default cloud account.
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 will fall back to the teamspaces default cloud account.
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
@@ -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 TYPE_CHECKING, List, Optional, Union
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._cloud_account = cloud_account
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[Any],
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(f" - '{schedule.name}' runs on cron schedule: `{schedule.cron_expression}`")
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."""