lightning-sdk 2025.7.22__py3-none-any.whl → 2025.7.31__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 (64) hide show
  1. lightning_sdk/__init__.py +1 -1
  2. lightning_sdk/api/cloud_account_api.py +58 -8
  3. lightning_sdk/api/job_api.py +74 -11
  4. lightning_sdk/api/llm_api.py +1 -1
  5. lightning_sdk/api/mmt_api.py +35 -5
  6. lightning_sdk/api/studio_api.py +32 -3
  7. lightning_sdk/api/utils.py +6 -2
  8. lightning_sdk/cli/create.py +3 -1
  9. lightning_sdk/cli/deploy/serve.py +3 -1
  10. lightning_sdk/cli/download.py +25 -1
  11. lightning_sdk/cli/entrypoint.py +3 -1
  12. lightning_sdk/cli/list.py +5 -1
  13. lightning_sdk/cli/run.py +3 -1
  14. lightning_sdk/cli/start.py +3 -1
  15. lightning_sdk/cli/switch.py +3 -1
  16. lightning_sdk/job/v2.py +7 -1
  17. lightning_sdk/job/work.py +5 -1
  18. lightning_sdk/lightning_cloud/openapi/__init__.py +6 -1
  19. lightning_sdk/lightning_cloud/openapi/api/assistants_service_api.py +106 -13
  20. lightning_sdk/lightning_cloud/openapi/api/cloudy_service_api.py +295 -0
  21. lightning_sdk/lightning_cloud/openapi/api/cluster_service_api.py +93 -0
  22. lightning_sdk/lightning_cloud/openapi/models/__init__.py +6 -1
  23. lightning_sdk/lightning_cloud/openapi/models/agentmanagedendpoints_id_body.py +27 -1
  24. lightning_sdk/lightning_cloud/openapi/models/metricsstream_id_body.py +27 -1
  25. lightning_sdk/lightning_cloud/openapi/models/project_id_schedules_body.py +53 -1
  26. lightning_sdk/lightning_cloud/openapi/models/schedules_id_body.py +53 -1
  27. lightning_sdk/lightning_cloud/openapi/models/user_id_upgradetrigger_body.py +29 -3
  28. lightning_sdk/lightning_cloud/openapi/models/user_user_id_body.py +201 -0
  29. lightning_sdk/lightning_cloud/openapi/models/v1_billing_subscription.py +27 -1
  30. lightning_sdk/lightning_cloud/openapi/models/v1_cloudy_settings.py +227 -0
  31. lightning_sdk/lightning_cloud/openapi/models/v1_cluster_spec.py +27 -1
  32. lightning_sdk/lightning_cloud/openapi/models/v1_conversation_response_chunk.py +27 -1
  33. lightning_sdk/lightning_cloud/openapi/models/v1_create_subscription_checkout_session_request.py +27 -1
  34. lightning_sdk/lightning_cloud/openapi/models/v1_function_call.py +149 -0
  35. lightning_sdk/lightning_cloud/openapi/models/v1_get_cluster_health_response.py +149 -0
  36. lightning_sdk/lightning_cloud/openapi/models/v1_get_user_response.py +27 -1
  37. lightning_sdk/lightning_cloud/openapi/models/v1_job_spec.py +27 -1
  38. lightning_sdk/lightning_cloud/openapi/models/v1_kubernetes_direct_v1.py +27 -1
  39. lightning_sdk/lightning_cloud/openapi/models/{v1_list_published_managed_endpoint_models_response.py → v1_list_published_managed_endpoints_response.py} +23 -23
  40. lightning_sdk/lightning_cloud/openapi/models/v1_managed_endpoint.py +27 -1
  41. lightning_sdk/lightning_cloud/openapi/models/v1_managed_model.py +95 -17
  42. lightning_sdk/lightning_cloud/openapi/models/v1_resource_visibility.py +27 -1
  43. lightning_sdk/lightning_cloud/openapi/models/v1_response_choice.py +29 -3
  44. lightning_sdk/lightning_cloud/openapi/models/v1_schedule.py +53 -1
  45. lightning_sdk/lightning_cloud/openapi/models/v1_service_health.py +27 -1
  46. lightning_sdk/lightning_cloud/openapi/models/v1_slurm_v1.py +79 -1
  47. lightning_sdk/lightning_cloud/openapi/models/v1_slurm_v1_status.py +79 -1
  48. lightning_sdk/lightning_cloud/openapi/models/v1_tool_call.py +175 -0
  49. lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +79 -53
  50. lightning_sdk/lightning_cloud/openapi/models/v1_volume_state.py +1 -0
  51. lightning_sdk/llm/llm.py +41 -7
  52. lightning_sdk/llm/public_assistants.json +32 -8
  53. lightning_sdk/machine.py +139 -43
  54. lightning_sdk/mmt/v2.py +6 -1
  55. lightning_sdk/models.py +1 -1
  56. lightning_sdk/studio.py +12 -5
  57. lightning_sdk/teamspace.py +5 -2
  58. lightning_sdk/utils/resolve.py +8 -0
  59. {lightning_sdk-2025.7.22.dist-info → lightning_sdk-2025.7.31.dist-info}/METADATA +7 -5
  60. {lightning_sdk-2025.7.22.dist-info → lightning_sdk-2025.7.31.dist-info}/RECORD +64 -59
  61. {lightning_sdk-2025.7.22.dist-info → lightning_sdk-2025.7.31.dist-info}/LICENSE +0 -0
  62. {lightning_sdk-2025.7.22.dist-info → lightning_sdk-2025.7.31.dist-info}/WHEEL +0 -0
  63. {lightning_sdk-2025.7.22.dist-info → lightning_sdk-2025.7.31.dist-info}/entry_points.txt +0 -0
  64. {lightning_sdk-2025.7.22.dist-info → lightning_sdk-2025.7.31.dist-info}/top_level.txt +0 -0
lightning_sdk/__init__.py CHANGED
@@ -32,6 +32,6 @@ __all__ = [
32
32
  "User",
33
33
  ]
34
34
 
35
- __version__ = "2025.07.22"
35
+ __version__ = "2025.07.31"
36
36
  _check_version_and_prompt_upgrade(__version__)
37
37
  _set_tqdm_envvars_noninteractive()
@@ -1,3 +1,4 @@
1
+ from functools import lru_cache
1
2
  from typing import TYPE_CHECKING, Dict, List, Optional, Union
2
3
 
3
4
  from lightning_sdk.lightning_cloud.openapi import (
@@ -6,6 +7,7 @@ from lightning_sdk.lightning_cloud.openapi import (
6
7
  V1ClusterType,
7
8
  V1ExternalCluster,
8
9
  V1ListClusterAcceleratorsResponse,
10
+ V1ListDefaultClusterAcceleratorsResponse,
9
11
  )
10
12
  from lightning_sdk.lightning_cloud.rest_client import LightningClient
11
13
 
@@ -33,6 +35,7 @@ class CloudAccountApi:
33
35
  raise ValueError(f"CloudAccount {cloud_account_id} does not exist")
34
36
  return res
35
37
 
38
+ @lru_cache(maxsize=None) # noqa: B019
36
39
  def list_cloud_accounts(self, teamspace_id: str) -> List[V1ExternalCluster]:
37
40
  """Lists the cloud accounts for a given teamspace.
38
41
 
@@ -42,10 +45,22 @@ class CloudAccountApi:
42
45
  Returns:
43
46
  A list of cloud accounts
44
47
  """
45
- res = self._client.cluster_service_list_project_clusters(
48
+ res_project = self._client.cluster_service_list_project_clusters(
46
49
  project_id=teamspace_id,
47
50
  )
48
- return res.clusters
51
+ res_global = self._client.cluster_service_list_clusters(
52
+ project_id=teamspace_id,
53
+ )
54
+
55
+ # can't use set here because the cloud_accounts are not hashable
56
+ cloud_accounts = []
57
+ cloud_account_ids = []
58
+ for cloud_account in res_project.clusters + res_global.clusters:
59
+ if cloud_account.id not in cloud_account_ids:
60
+ cloud_accounts.append(cloud_account)
61
+ cloud_account_ids.append(cloud_account.id)
62
+
63
+ return cloud_accounts
49
64
 
50
65
  def get_cloud_account_non_org(self, teamspace_id: str, cloud_account_id: str) -> Optional[V1ExternalCluster]:
51
66
  for cluster in self.list_cloud_accounts(teamspace_id=teamspace_id):
@@ -54,21 +69,52 @@ class CloudAccountApi:
54
69
 
55
70
  return None
56
71
 
57
- def list_cloud_account_accelerators(self, cloud_account_id: str, org_id: str) -> V1ListClusterAcceleratorsResponse:
72
+ @lru_cache(maxsize=None) # noqa: B019
73
+ def list_cloud_account_accelerators(
74
+ self,
75
+ teamspace_id: str,
76
+ cloud_account_id: str,
77
+ org_id: str,
78
+ ) -> Union[V1ListClusterAcceleratorsResponse, V1ListDefaultClusterAcceleratorsResponse]:
58
79
  """Lists the accelerators for a given cloud account.
59
80
 
60
81
  Args:
61
82
  cloud_account_id: cluster ID to list accelerators for
62
- org_id: The owning org of this project
63
83
  """
64
- res = self._client.cluster_service_list_cluster_accelerators(
65
- id=cloud_account_id,
66
- org_id=org_id,
67
- )
84
+ # map cloud_account to provider
85
+ cloud_provider = None
86
+ is_default = True
87
+ for cloud_account in self.list_cloud_accounts(teamspace_id=teamspace_id):
88
+ if cloud_account.id == cloud_account_id:
89
+ is_default = cloud_account.spec.cluster_type == V1ClusterType.GLOBAL
90
+ cloud_provider = self._get_cloud_account_provider(cloud_account)
91
+ break
92
+
93
+ if cloud_provider is None:
94
+ raise ValueError(
95
+ f"Cloud Account {cloud_account_id} is not a default cloud account. Are you in the correct teamspace?"
96
+ )
97
+
98
+ if is_default:
99
+ res = self._list_default_cluster_accelerators(teamspace_id=teamspace_id, cloud_provider=cloud_provider)
100
+ else:
101
+ res = self._client.cluster_service_list_cluster_accelerators(
102
+ id=cloud_account_id,
103
+ org_id=org_id,
104
+ )
105
+
68
106
  if not res:
69
107
  raise ValueError(f"CloudAccount {cloud_account_id} does not exist")
70
108
  return res
71
109
 
110
+ def _list_default_cluster_accelerators(
111
+ self, teamspace_id: str, cloud_provider: Union[str, "CloudProvider"]
112
+ ) -> V1ListDefaultClusterAcceleratorsResponse:
113
+ return self._client.cluster_service_list_default_cluster_accelerators(
114
+ project_id=teamspace_id, cloud_provider=str(cloud_provider)
115
+ )
116
+
117
+ @lru_cache(maxsize=None) # noqa: B019
72
118
  def list_global_cloud_accounts(self, teamspace_id: str) -> List[V1ExternalCluster]:
73
119
  """Lists the accelerators for a given teamspace.
74
120
 
@@ -79,6 +125,10 @@ class CloudAccountApi:
79
125
  if not cloud_accounts:
80
126
  raise ValueError(f"Teamspace {teamspace_id} does not exist")
81
127
  filtered_cloud_accounts = filter(lambda x: x.spec.cluster_type == V1ClusterType.GLOBAL, cloud_accounts)
128
+ # TODO: remove aggregate filter once finished
129
+ filtered_cloud_accounts = filter(
130
+ lambda x: x.spec.driver != V1CloudProvider.LIGHTNING_AGGREGATE, filtered_cloud_accounts
131
+ )
82
132
  return list(filtered_cloud_accounts)
83
133
 
84
134
  def get_cloud_account_provider_mapping(self, teamspace_id: str) -> Dict["CloudProvider", str]:
@@ -17,7 +17,7 @@ from lightning_sdk.lightning_cloud.openapi import (
17
17
  JobsIdBody1,
18
18
  ProjectIdJobsBody,
19
19
  V1CloudSpace,
20
- V1ComputeConfig,
20
+ V1ClusterAccelerator,
21
21
  V1DownloadJobLogsResponse,
22
22
  V1DownloadLightningappInstanceLogsResponse,
23
23
  V1EnvVar,
@@ -94,15 +94,48 @@ class JobApiV1:
94
94
  def get_work(self, job_id: str, teamspace_id: str, work_id: str) -> Externalv1Lightningwork:
95
95
  return self._client.lightningwork_service_get_lightningwork(project_id=teamspace_id, app_id=job_id, id=work_id)
96
96
 
97
- def get_machine_from_work(self, work: Externalv1Lightningwork) -> Machine:
97
+ def get_machine_from_work(self, work: Externalv1Lightningwork, org_id: str) -> Machine:
98
98
  spec: V1LightningworkSpec = work.spec
99
99
  # prefer user-requested config if specified
100
100
  user_requested_compute_config: V1UserRequestedComputeConfig = spec.user_requested_compute_config
101
- if user_requested_compute_config.name:
102
- return Machine.from_str(user_requested_compute_config.name)
103
- compute_config: V1ComputeConfig = spec.compute_config
101
+ accelerators = self._get_machines_for_cloud_account(
102
+ teamspace_id=work.project_id,
103
+ cloud_account_id=spec.cluster_id,
104
+ org_id=org_id,
105
+ )
106
+
107
+ identifier = None
108
+
109
+ if user_requested_compute_config and user_requested_compute_config.name:
110
+ identifier = user_requested_compute_config.name
111
+ else:
112
+ identifier = spec.compute_config.instance_type
113
+
114
+ for accelerator in accelerators:
115
+ if identifier in (
116
+ accelerator.slug,
117
+ accelerator.slug_multi_cloud,
118
+ accelerator.instance_id,
119
+ ):
120
+ return Machine.from_str(accelerator.slug_multi_cloud)
121
+
122
+ return Machine.from_str(identifier)
104
123
 
105
- return Machine.from_str(compute_config.instance_type)
124
+ def _get_machines_for_cloud_account(
125
+ self, teamspace_id: str, cloud_account_id: str, org_id: str
126
+ ) -> List[V1ClusterAccelerator]:
127
+ from lightning_sdk.api.cloud_account_api import CloudAccountApi
128
+
129
+ cloud_account_api = CloudAccountApi()
130
+ accelerators = cloud_account_api.list_cloud_account_accelerators(
131
+ teamspace_id=teamspace_id,
132
+ cloud_account_id=cloud_account_id,
133
+ org_id=org_id,
134
+ )
135
+ if not accelerators:
136
+ return []
137
+
138
+ return list(filter(lambda acc: acc.enabled, accelerators.accelerator))
106
139
 
107
140
  def get_studio_name(self, job: Externalv1LightningappInstance) -> str:
108
141
  cs: V1CloudSpace = self._client.cloud_space_service_get_cloud_space(
@@ -215,7 +248,7 @@ class JobApiV2:
215
248
  path_mappings: Optional[Dict[str, str]],
216
249
  artifacts_local: Optional[str], # deprecated in favor of path_mappings
217
250
  artifacts_remote: Optional[str], # deprecated in favor of path_mappings
218
- max_runtime: Optional[str] = None,
251
+ max_runtime: Optional[int] = None,
219
252
  ) -> V1Job:
220
253
  body = self._create_job_body(
221
254
  name=name,
@@ -380,11 +413,41 @@ class JobApiV2:
380
413
  return Status.Stopping
381
414
  return Status.Pending
382
415
 
383
- def _get_job_machine_from_spec(self, spec: V1JobSpec) -> "Machine":
384
- instance_name = spec.instance_name
385
- instance_type = spec.instance_type
416
+ def _get_job_machine_from_spec(self, spec: V1JobSpec, teamspace_id: str, org_id: str) -> "Machine":
417
+ accelerators = self._get_machines_for_cloud_account(
418
+ teamspace_id=teamspace_id,
419
+ cloud_account_id=spec.cluster_id,
420
+ org_id=org_id,
421
+ )
422
+
423
+ for accelerator in accelerators:
424
+ possible_identifiers = (
425
+ accelerator.slug,
426
+ accelerator.slug_multi_cloud,
427
+ accelerator.instance_id,
428
+ )
429
+ if (spec.instance_name and spec.instance_name in possible_identifiers) or (
430
+ spec.instance_type and spec.instance_type in possible_identifiers
431
+ ):
432
+ return Machine.from_str(accelerator.slug_multi_cloud)
433
+
434
+ return Machine.from_str(spec.instance_name or spec.instance_type)
435
+
436
+ def _get_machines_for_cloud_account(
437
+ self, teamspace_id: str, cloud_account_id: str, org_id: str
438
+ ) -> List[V1ClusterAccelerator]:
439
+ from lightning_sdk.api.cloud_account_api import CloudAccountApi
440
+
441
+ cloud_account_api = CloudAccountApi()
442
+ accelerators = cloud_account_api.list_cloud_account_accelerators(
443
+ teamspace_id=teamspace_id,
444
+ cloud_account_id=cloud_account_id,
445
+ org_id=org_id,
446
+ )
447
+ if not accelerators:
448
+ return []
386
449
 
387
- return Machine.from_str(instance_name, instance_type or instance_name)
450
+ return list(filter(lambda acc: acc.enabled, accelerators.accelerator))
388
451
 
389
452
  def get_total_cost(self, job: V1Job) -> float:
390
453
  return job.total_cost
@@ -98,7 +98,7 @@ class LLMApi:
98
98
  {"contentType": "text", "parts": [prompt]},
99
99
  ],
100
100
  },
101
- "max_completion_tokens": max_completion_tokens,
101
+ "max_tokens": max_completion_tokens,
102
102
  "conversation_id": conversation_id,
103
103
  "billing_project_id": billing_project_id,
104
104
  "name": name,
@@ -2,7 +2,7 @@ import json
2
2
  import time
3
3
  from typing import TYPE_CHECKING, Dict, List, Optional, Union
4
4
 
5
- from lightning_sdk.api.job_api import JobApiV1
5
+ from lightning_sdk.api.job_api import JobApiV1, V1ClusterAccelerator
6
6
  from lightning_sdk.api.utils import (
7
7
  _create_app,
8
8
  _machine_to_compute_name,
@@ -247,11 +247,41 @@ class MMTApiV2:
247
247
  def get_command(self, job: V1MultiMachineJob) -> str:
248
248
  return job.spec.command
249
249
 
250
- def _get_job_machine_from_spec(self, spec: V1JobSpec) -> "Machine":
251
- instance_name = spec.instance_name
252
- instance_type = spec.instance_type
250
+ def _get_job_machine_from_spec(self, spec: V1JobSpec, teamspace_id: str, org_id: str) -> "Machine":
251
+ accelerators = self._get_machines_for_cloud_account(
252
+ teamspace_id=teamspace_id,
253
+ cloud_account_id=spec.cluster_id,
254
+ org_id=org_id,
255
+ )
256
+
257
+ for accelerator in accelerators:
258
+ possible_identifiers = (
259
+ accelerator.slug,
260
+ accelerator.slug_multi_cloud,
261
+ accelerator.instance_id,
262
+ )
263
+ if (spec.instance_name and spec.instance_name in possible_identifiers) or (
264
+ spec.instance_type and spec.instance_type in possible_identifiers
265
+ ):
266
+ return Machine.from_str(accelerator.slug_multi_cloud)
267
+
268
+ return Machine.from_str(spec.instance_name or spec.instance_type)
269
+
270
+ def _get_machines_for_cloud_account(
271
+ self, teamspace_id: str, cloud_account_id: str, org_id: str
272
+ ) -> List[V1ClusterAccelerator]:
273
+ from lightning_sdk.api.cloud_account_api import CloudAccountApi
274
+
275
+ cloud_account_api = CloudAccountApi()
276
+ accelerators = cloud_account_api.list_cloud_account_accelerators(
277
+ teamspace_id=teamspace_id,
278
+ cloud_account_id=cloud_account_id,
279
+ org_id=org_id,
280
+ )
281
+ if not accelerators:
282
+ return []
253
283
 
254
- return Machine.from_str(instance_name, instance_type or instance_name)
284
+ return list(filter(lambda acc: acc.enabled, accelerators.accelerator))
255
285
 
256
286
  def get_total_cost(self, job: V1MultiMachineJob) -> float:
257
287
  return job.total_cost
@@ -4,7 +4,7 @@ import tempfile
4
4
  import time
5
5
  import zipfile
6
6
  from threading import Event, Thread
7
- from typing import Any, Dict, Generator, Mapping, Optional, Tuple, Union
7
+ from typing import Any, Dict, Generator, List, Mapping, Optional, Tuple, Union
8
8
 
9
9
  import backoff
10
10
  import requests
@@ -37,6 +37,7 @@ from lightning_sdk.lightning_cloud.openapi import (
37
37
  V1CloudSpaceSeedFile,
38
38
  V1CloudSpaceSourceType,
39
39
  V1CloudSpaceState,
40
+ V1ClusterAccelerator,
40
41
  V1EndpointType,
41
42
  V1GetCloudSpaceInstanceStatusResponse,
42
43
  V1GetLongRunningCommandInCloudSpaceResponse,
@@ -267,12 +268,24 @@ class StudioApi:
267
268
  break
268
269
  time.sleep(1)
269
270
 
270
- def get_machine(self, studio_id: str, teamspace_id: str) -> Machine:
271
+ def get_machine(self, studio_id: str, teamspace_id: str, cloud_account_id: str, org_id: str) -> Machine:
271
272
  """Get the current machine type the given Studio is running on."""
272
273
  response: V1CloudSpaceInstanceConfig = self._client.cloud_space_service_get_cloud_space_instance_config(
273
274
  project_id=teamspace_id, id=studio_id
274
275
  )
275
- return Machine(response.compute_config.name, response.compute_config.name)
276
+ accelerators = self._get_machines_for_cloud_account(
277
+ teamspace_id=teamspace_id, cloud_account_id=cloud_account_id, org_id=org_id
278
+ )
279
+
280
+ for accelerator in accelerators:
281
+ if response.compute_config.name in (
282
+ accelerator.slug,
283
+ accelerator.slug_multi_cloud,
284
+ accelerator.instance_id,
285
+ ):
286
+ return Machine.from_str(accelerator.slug_multi_cloud)
287
+
288
+ return Machine.from_str(response.compute_config.name)
276
289
 
277
290
  def get_interruptible(self, studio_id: str, teamspace_id: str) -> bool:
278
291
  """Get whether the Studio is running on a interruptible instance."""
@@ -282,6 +295,22 @@ class StudioApi:
282
295
 
283
296
  return response.compute_config.spot
284
297
 
298
+ def _get_machines_for_cloud_account(
299
+ self, teamspace_id: str, cloud_account_id: str, org_id: str
300
+ ) -> List[V1ClusterAccelerator]:
301
+ from lightning_sdk.api.cloud_account_api import CloudAccountApi
302
+
303
+ cloud_account_api = CloudAccountApi()
304
+ accelerators = cloud_account_api.list_cloud_account_accelerators(
305
+ teamspace_id=teamspace_id,
306
+ cloud_account_id=cloud_account_id,
307
+ org_id=org_id,
308
+ )
309
+ if not accelerators:
310
+ return []
311
+
312
+ return list(filter(lambda acc: acc.enabled, accelerators.accelerator))
313
+
285
314
  def _get_detached_command_status(
286
315
  self, studio_id: str, teamspace_id: str, session_id: str
287
316
  ) -> V1GetLongRunningCommandInCloudSpaceResponse:
@@ -331,7 +331,9 @@ class _DummyResponse:
331
331
 
332
332
  def _machine_to_compute_name(machine: Union[Machine, str]) -> str:
333
333
  if isinstance(machine, Machine):
334
- return machine.instance_type
334
+ if machine.instance_type is not None:
335
+ return machine.instance_type
336
+ return machine.slug
335
337
  return machine
336
338
 
337
339
 
@@ -352,7 +354,9 @@ def _get_registry_url() -> str:
352
354
 
353
355
 
354
356
  def _sanitize_studio_remote_path(path: str, studio_id: str) -> str:
355
- return f"/cloudspaces/{studio_id}/code/content/{path.replace('/teamspace/studios/this_studio/', '')}"
357
+ path = path.replace("/teamspace/studios/this_studio/", "")
358
+ root = f"/cloudspaces/{studio_id}/code/content/"
359
+ return os.path.join(root, path)
356
360
 
357
361
 
358
362
  def _resolve_teamspace_remote_path(path: str) -> str:
@@ -12,7 +12,9 @@ from lightning_sdk.cli.teamspace_menu import _TeamspacesMenu
12
12
  from lightning_sdk.machine import CloudProvider
13
13
  from lightning_sdk.utils.resolve import _resolve_deprecated_provider
14
14
 
15
- _MACHINE_VALUES = tuple([machine.name for machine in Machine.__dict__.values() if isinstance(machine, Machine)])
15
+ _MACHINE_VALUES = tuple(
16
+ [machine.name for machine in Machine.__dict__.values() if isinstance(machine, Machine) and machine._include_in_cli]
17
+ )
16
18
  _PROVIDER_VALUES = tuple([provider.value for provider in CloudProvider])
17
19
 
18
20
 
@@ -25,7 +25,9 @@ from lightning_sdk.cli.deploy._auth import (
25
25
  from lightning_sdk.cli.deploy.devbox import _handle_devbox
26
26
  from lightning_sdk.serve import _LitServeDeployer
27
27
 
28
- _MACHINE_VALUES = tuple([machine.name for machine in Machine.__dict__.values() if isinstance(machine, Machine)])
28
+ _MACHINE_VALUES = tuple(
29
+ [machine.name for machine in Machine.__dict__.values() if isinstance(machine, Machine) and machine._include_in_cli]
30
+ )
29
31
 
30
32
 
31
33
  class _ServeGroup(click.Group):
@@ -17,6 +17,27 @@ from lightning_sdk.studio import Studio
17
17
  from lightning_sdk.utils.resolve import _get_authed_user, skip_studio_init
18
18
 
19
19
 
20
+ def _expand_remote_path(path: str) -> str:
21
+ """Expand and normalize remote CLI paths.
22
+
23
+ - Strips leading `~/` or `~`
24
+ - Expands `~` to the user's home but returns relative to it
25
+ - Returns an empty string if path is empty or `~`
26
+ """
27
+ if not path:
28
+ return ""
29
+
30
+ local_home = os.path.expanduser("~")
31
+
32
+ # Expand to absolute path and remove the local home prefix if present
33
+ path = os.path.expanduser(path)
34
+ if path.startswith(local_home):
35
+ path = path[len(local_home) :]
36
+
37
+ # Remove any leading "/" or "~" remnants
38
+ return path.lstrip("/~")
39
+
40
+
20
41
  @click.group(name="download")
21
42
  def download() -> None:
22
43
  """Download resources from Lightning AI."""
@@ -86,17 +107,20 @@ def folder(
86
107
  raise ValueError("Either --studio or --teamspace must be provided, not both")
87
108
 
88
109
  if studio:
110
+ path = _expand_remote_path(path)
89
111
  resolved_downloader = _resolve_studio(studio)
90
112
  elif teamspace:
91
113
  menu = _TeamspacesMenu()
92
114
  resolved_downloader = menu._resolve_teamspace(teamspace)
115
+ else:
116
+ raise ValueError("Either --studio or --teamspace must be provided")
93
117
 
94
118
  if not path:
95
119
  local_path /= resolved_downloader.name
96
120
  path = ""
97
121
 
98
122
  try:
99
- if not path:
123
+ if not path and teamspace:
100
124
  raise FileNotFoundError()
101
125
  resolved_downloader.download_folder(remote_path=path, target_path=str(local_path))
102
126
  except Exception as e:
@@ -50,7 +50,9 @@ def _notify_exception(exception_type: Type[BaseException], value: BaseException,
50
50
  renderables.append(Text("\n\nFull traceback:\n", style="bold yellow"))
51
51
  renderables.append(Syntax(tb_text, "python", theme="monokai", line_numbers=False, word_wrap=True))
52
52
  else:
53
- renderables.append(Text("\n\nTo see the full traceback, set the LIGHTNING_DEBUG environment variable to 1."))
53
+ renderables.append(Text("\n\n🐞 To view the full traceback, set: LIGHTNING_DEBUG=1"))
54
+
55
+ renderables.append(Text("\n📘 Need help? Run: lightning <command> --help", style="cyan"))
54
56
 
55
57
  console.print(Panel(Group(*renderables), title="⚡ Lightning CLI Error", border_style="red"))
56
58
 
lightning_sdk/cli/list.py CHANGED
@@ -274,7 +274,11 @@ def machines() -> None:
274
274
  table.add_column("Name")
275
275
 
276
276
  # Get all machine types from the enum
277
- machine_types = [name for name in dir(Machine) if isinstance(getattr(Machine, name), Machine)]
277
+ machine_types = [
278
+ name
279
+ for name in dir(Machine)
280
+ if isinstance(getattr(Machine, name), Machine) and getattr(Machine, name)._include_in_cli
281
+ ]
278
282
 
279
283
  # Add rows to table
280
284
  for name in sorted(machine_types):
lightning_sdk/cli/run.py CHANGED
@@ -8,7 +8,9 @@ from lightning_sdk.machine import Machine
8
8
  from lightning_sdk.mmt import MMT
9
9
  from lightning_sdk.teamspace import Teamspace
10
10
 
11
- _MACHINE_VALUES = tuple([machine.name for machine in Machine.__dict__.values() if isinstance(machine, Machine)])
11
+ _MACHINE_VALUES = tuple(
12
+ [machine.name for machine in Machine.__dict__.values() if isinstance(machine, Machine) and machine._include_in_cli]
13
+ )
12
14
 
13
15
 
14
16
  @click.group(name="run")
@@ -6,7 +6,9 @@ from lightning_sdk import Machine, Studio
6
6
  from lightning_sdk.lightning_cloud.openapi.rest import ApiException
7
7
  from lightning_sdk.machine import CloudProvider
8
8
 
9
- _MACHINE_VALUES = tuple([machine.name for machine in Machine.__dict__.values() if isinstance(machine, Machine)])
9
+ _MACHINE_VALUES = tuple(
10
+ [machine.name for machine in Machine.__dict__.values() if isinstance(machine, Machine) and machine._include_in_cli]
11
+ )
10
12
  _PROVIDER_VALUES = tuple([provider.value for provider in CloudProvider])
11
13
 
12
14
 
@@ -5,7 +5,9 @@ import click
5
5
  from lightning_sdk import Machine, Studio
6
6
  from lightning_sdk.lightning_cloud.openapi.rest import ApiException
7
7
 
8
- _MACHINE_VALUES = tuple([machine.name for machine in Machine.__dict__.values() if isinstance(machine, Machine)])
8
+ _MACHINE_VALUES = tuple(
9
+ [machine.name for machine in Machine.__dict__.values() if isinstance(machine, Machine) and machine._include_in_cli]
10
+ )
9
11
 
10
12
 
11
13
  @click.group("switch")
lightning_sdk/job/v2.py CHANGED
@@ -4,6 +4,7 @@ from lightning_sdk.api.job_api import JobApiV2
4
4
  from lightning_sdk.api.utils import _get_cloud_url
5
5
  from lightning_sdk.job.base import _BaseJob
6
6
  from lightning_sdk.status import Status
7
+ from lightning_sdk.utils.resolve import _get_org_id
7
8
 
8
9
  if TYPE_CHECKING:
9
10
  from lightning_sdk.machine import CloudProvider, Machine
@@ -165,7 +166,12 @@ class _JobV2(_BaseJob):
165
166
  def machine(self) -> Union["Machine", str]:
166
167
  """The machine type the job is running on."""
167
168
  # only fetch the job it it hasn't been fetched yet as machine cannot change over time
168
- return self._job_api._get_job_machine_from_spec(self._guaranteed_job.spec)
169
+
170
+ return self._job_api._get_job_machine_from_spec(
171
+ self._guaranteed_job.spec,
172
+ self.teamspace.id,
173
+ _get_org_id(self.teamspace),
174
+ )
169
175
 
170
176
  @property
171
177
  def artifact_path(self) -> Optional[str]:
lightning_sdk/job/work.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from typing import TYPE_CHECKING, Any, Optional, Protocol, Union
2
2
 
3
3
  from lightning_sdk.api.job_api import JobApiV1
4
+ from lightning_sdk.utils.resolve import _get_org_id
4
5
 
5
6
  if TYPE_CHECKING:
6
7
  from lightning_sdk.job.base import MachineDict
@@ -52,7 +53,10 @@ class Work:
52
53
 
53
54
  @property
54
55
  def machine(self) -> Union["Machine", str]:
55
- return self._job_api.get_machine_from_work(self._guaranteed_work)
56
+ return self._job_api.get_machine_from_work(
57
+ self._guaranteed_work,
58
+ org_id=_get_org_id(self._teamspace),
59
+ )
56
60
 
57
61
  @property
58
62
  def artifact_path(self) -> Optional[str]:
@@ -243,6 +243,7 @@ from lightning_sdk.lightning_cloud.openapi.models.user_id_affiliatelinks_body im
243
243
  from lightning_sdk.lightning_cloud.openapi.models.user_id_membershiprolebindings_body import UserIdMembershiprolebindingsBody
244
244
  from lightning_sdk.lightning_cloud.openapi.models.user_id_membershiprolebindings_body1 import UserIdMembershiprolebindingsBody1
245
245
  from lightning_sdk.lightning_cloud.openapi.models.user_id_upgradetrigger_body import UserIdUpgradetriggerBody
246
+ from lightning_sdk.lightning_cloud.openapi.models.user_user_id_body import UserUserIdBody
246
247
  from lightning_sdk.lightning_cloud.openapi.models.v1_aws_cluster_credentials import V1AWSClusterCredentials
247
248
  from lightning_sdk.lightning_cloud.openapi.models.v1_aws_direct_v1 import V1AWSDirectV1
248
249
  from lightning_sdk.lightning_cloud.openapi.models.v1_aws_direct_v1_status import V1AWSDirectV1Status
@@ -325,6 +326,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_cloud_space_version import
325
326
  from lightning_sdk.lightning_cloud.openapi.models.v1_cloud_space_version_publication import V1CloudSpaceVersionPublication
326
327
  from lightning_sdk.lightning_cloud.openapi.models.v1_cloudflare_v1 import V1CloudflareV1
327
328
  from lightning_sdk.lightning_cloud.openapi.models.v1_cloudy_expert import V1CloudyExpert
329
+ from lightning_sdk.lightning_cloud.openapi.models.v1_cloudy_settings import V1CloudySettings
328
330
  from lightning_sdk.lightning_cloud.openapi.models.v1_cluster_accelerator import V1ClusterAccelerator
329
331
  from lightning_sdk.lightning_cloud.openapi.models.v1_cluster_availability import V1ClusterAvailability
330
332
  from lightning_sdk.lightning_cloud.openapi.models.v1_cluster_capacity_reservation import V1ClusterCapacityReservation
@@ -533,6 +535,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_filesystem_work import V1Fi
533
535
  from lightning_sdk.lightning_cloud.openapi.models.v1_find_capacity_block_offering_response import V1FindCapacityBlockOfferingResponse
534
536
  from lightning_sdk.lightning_cloud.openapi.models.v1_flowserver import V1Flowserver
535
537
  from lightning_sdk.lightning_cloud.openapi.models.v1_folder_index_status import V1FolderIndexStatus
538
+ from lightning_sdk.lightning_cloud.openapi.models.v1_function_call import V1FunctionCall
536
539
  from lightning_sdk.lightning_cloud.openapi.models.v1_function_tool import V1FunctionTool
537
540
  from lightning_sdk.lightning_cloud.openapi.models.v1_gcp_direct_vpc import V1GCPDirectVPC
538
541
  from lightning_sdk.lightning_cloud.openapi.models.v1_gcs_folder_data_connection import V1GCSFolderDataConnection
@@ -555,6 +558,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_get_cloud_space_instance_sy
555
558
  from lightning_sdk.lightning_cloud.openapi.models.v1_get_cloud_space_size_response import V1GetCloudSpaceSizeResponse
556
559
  from lightning_sdk.lightning_cloud.openapi.models.v1_get_cluster_accelerator_demand_response import V1GetClusterAcceleratorDemandResponse
557
560
  from lightning_sdk.lightning_cloud.openapi.models.v1_get_cluster_credentials_response import V1GetClusterCredentialsResponse
561
+ from lightning_sdk.lightning_cloud.openapi.models.v1_get_cluster_health_response import V1GetClusterHealthResponse
558
562
  from lightning_sdk.lightning_cloud.openapi.models.v1_get_deployment_routing_telemetry_aggregated_response import V1GetDeploymentRoutingTelemetryAggregatedResponse
559
563
  from lightning_sdk.lightning_cloud.openapi.models.v1_get_deployment_routing_telemetry_content_response import V1GetDeploymentRoutingTelemetryContentResponse
560
564
  from lightning_sdk.lightning_cloud.openapi.models.v1_get_deployment_routing_telemetry_response import V1GetDeploymentRoutingTelemetryResponse
@@ -729,7 +733,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_list_project_memberships_re
729
733
  from lightning_sdk.lightning_cloud.openapi.models.v1_list_project_roles_response import V1ListProjectRolesResponse
730
734
  from lightning_sdk.lightning_cloud.openapi.models.v1_list_published_cloud_spaces_response import V1ListPublishedCloudSpacesResponse
731
735
  from lightning_sdk.lightning_cloud.openapi.models.v1_list_published_deployment_templates_response import V1ListPublishedDeploymentTemplatesResponse
732
- from lightning_sdk.lightning_cloud.openapi.models.v1_list_published_managed_endpoint_models_response import V1ListPublishedManagedEndpointModelsResponse
736
+ from lightning_sdk.lightning_cloud.openapi.models.v1_list_published_managed_endpoints_response import V1ListPublishedManagedEndpointsResponse
733
737
  from lightning_sdk.lightning_cloud.openapi.models.v1_list_quests_response import V1ListQuestsResponse
734
738
  from lightning_sdk.lightning_cloud.openapi.models.v1_list_slurm_cluster_users_response import V1ListSLURMClusterUsersResponse
735
739
  from lightning_sdk.lightning_cloud.openapi.models.v1_list_ssh_public_keys_response import V1ListSSHPublicKeysResponse
@@ -930,6 +934,7 @@ from lightning_sdk.lightning_cloud.openapi.models.v1_telemetry import V1Telemetr
930
934
  from lightning_sdk.lightning_cloud.openapi.models.v1_timestamp_code_telemetry import V1TimestampCodeTelemetry
931
935
  from lightning_sdk.lightning_cloud.openapi.models.v1_token_usage import V1TokenUsage
932
936
  from lightning_sdk.lightning_cloud.openapi.models.v1_tool import V1Tool
937
+ from lightning_sdk.lightning_cloud.openapi.models.v1_tool_call import V1ToolCall
933
938
  from lightning_sdk.lightning_cloud.openapi.models.v1_transaction import V1Transaction
934
939
  from lightning_sdk.lightning_cloud.openapi.models.v1_transfer_cloud_space_response import V1TransferCloudSpaceResponse
935
940
  from lightning_sdk.lightning_cloud.openapi.models.v1_transfer_org_balance_response import V1TransferOrgBalanceResponse