lightning-sdk 2025.12.9__py3-none-any.whl → 2025.12.17__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 (78) hide show
  1. lightning_sdk/__version__.py +1 -1
  2. lightning_sdk/api/job_api.py +16 -0
  3. lightning_sdk/api/org_api.py +7 -0
  4. lightning_sdk/api/studio_api.py +28 -3
  5. lightning_sdk/api/teamspace_api.py +42 -5
  6. lightning_sdk/api/user_api.py +5 -0
  7. lightning_sdk/cli/legacy/download.py +2 -1
  8. lightning_sdk/cli/legacy/studios_menu.py +8 -1
  9. lightning_sdk/job/base.py +26 -4
  10. lightning_sdk/job/job.py +16 -5
  11. lightning_sdk/job/v1.py +7 -2
  12. lightning_sdk/job/v2.py +41 -1
  13. lightning_sdk/lightning_cloud/openapi/__init__.py +26 -1
  14. lightning_sdk/lightning_cloud/openapi/api/__init__.py +1 -0
  15. lightning_sdk/lightning_cloud/openapi/api/cloud_space_service_api.py +5 -1
  16. lightning_sdk/lightning_cloud/openapi/api/cluster_service_api.py +517 -0
  17. lightning_sdk/lightning_cloud/openapi/api/data_connection_service_api.py +5 -1
  18. lightning_sdk/lightning_cloud/openapi/api/file_system_service_api.py +11 -11
  19. lightning_sdk/lightning_cloud/openapi/api/kubernetes_virtual_machine_service_api.py +557 -0
  20. lightning_sdk/lightning_cloud/openapi/api/storage_service_api.py +5 -1
  21. lightning_sdk/lightning_cloud/openapi/models/__init__.py +25 -1
  22. lightning_sdk/lightning_cloud/openapi/models/cloud_space_environment_template_service_update_cloud_space_environment_template_body.py +27 -1
  23. lightning_sdk/lightning_cloud/openapi/models/cluster_service_add_container_registry_body.py +123 -0
  24. lightning_sdk/lightning_cloud/openapi/models/cluster_service_create_machine_body.py +27 -1
  25. lightning_sdk/lightning_cloud/openapi/models/cluster_service_refresh_container_registry_credentials_body.py +97 -0
  26. lightning_sdk/lightning_cloud/openapi/models/cluster_service_validate_container_registry_body.py +97 -0
  27. lightning_sdk/lightning_cloud/openapi/models/kubernetes_virtual_machine_service_create_kubernetes_virtual_machine_body.py +513 -0
  28. lightning_sdk/lightning_cloud/openapi/models/kubernetes_virtual_machine_service_update_kubernetes_virtual_machine_body.py +97 -0
  29. lightning_sdk/lightning_cloud/openapi/models/v1_add_container_registry_response.py +123 -0
  30. lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_environment_template_config.py +27 -1
  31. lightning_sdk/lightning_cloud/openapi/models/v1_cloud_space_specialized_view.py +1 -0
  32. lightning_sdk/lightning_cloud/openapi/models/v1_container_registry.py +253 -0
  33. lightning_sdk/lightning_cloud/openapi/models/v1_container_registry_info.py +281 -0
  34. lightning_sdk/lightning_cloud/openapi/models/v1_container_registry_integration.py +123 -0
  35. lightning_sdk/lightning_cloud/openapi/models/v1_container_registry_status.py +105 -0
  36. lightning_sdk/lightning_cloud/openapi/models/v1_create_cloud_space_environment_template_request.py +27 -1
  37. lightning_sdk/lightning_cloud/openapi/models/v1_delete_container_registry_response.py +97 -0
  38. lightning_sdk/lightning_cloud/openapi/models/v1_delete_kubernetes_virtual_machine_response.py +97 -0
  39. lightning_sdk/lightning_cloud/openapi/models/v1_ecr_registry_config.py +175 -0
  40. lightning_sdk/lightning_cloud/openapi/models/v1_ecr_registry_config_input.py +123 -0
  41. lightning_sdk/lightning_cloud/openapi/models/v1_ecr_registry_details.py +201 -0
  42. lightning_sdk/lightning_cloud/openapi/models/v1_get_artifacts_page_response.py +29 -3
  43. lightning_sdk/lightning_cloud/openapi/models/v1_get_user_response.py +27 -1
  44. lightning_sdk/lightning_cloud/openapi/models/v1_kubernetes_direct_v1.py +53 -1
  45. lightning_sdk/lightning_cloud/openapi/models/v1_kubernetes_virtual_machine.py +383 -0
  46. lightning_sdk/lightning_cloud/openapi/models/v1_kubevirt_config.py +305 -0
  47. lightning_sdk/lightning_cloud/openapi/models/v1_kubevirt_provider_configuration.py +227 -0
  48. lightning_sdk/lightning_cloud/openapi/models/v1_kubevirt_vm_configuration.py +149 -0
  49. lightning_sdk/lightning_cloud/openapi/models/v1_kubevirt_vm_resources.py +201 -0
  50. lightning_sdk/lightning_cloud/openapi/models/v1_list_container_registries_response.py +123 -0
  51. lightning_sdk/lightning_cloud/openapi/models/v1_list_kubernetes_virtual_machines_response.py +123 -0
  52. lightning_sdk/lightning_cloud/openapi/models/v1_machine.py +27 -1
  53. lightning_sdk/lightning_cloud/openapi/models/v1_node_metrics.py +27 -1
  54. lightning_sdk/lightning_cloud/openapi/models/v1_refresh_container_registry_credentials_response.py +123 -0
  55. lightning_sdk/lightning_cloud/openapi/models/v1_search_user.py +27 -1
  56. lightning_sdk/lightning_cloud/openapi/models/v1_user_features.py +79 -1
  57. lightning_sdk/lightning_cloud/openapi/models/v1_validate_container_registry_response.py +149 -0
  58. lightning_sdk/lightning_cloud/openapi/rest.py +2 -2
  59. lightning_sdk/machine.py +1 -0
  60. lightning_sdk/mmt/base.py +26 -7
  61. lightning_sdk/mmt/mmt.py +11 -6
  62. lightning_sdk/mmt/v1.py +5 -2
  63. lightning_sdk/mmt/v2.py +5 -2
  64. lightning_sdk/organization.py +10 -1
  65. lightning_sdk/owner.py +4 -0
  66. lightning_sdk/pipeline/steps.py +3 -0
  67. lightning_sdk/plugin.py +2 -2
  68. lightning_sdk/studio.py +33 -2
  69. lightning_sdk/teamspace.py +44 -4
  70. lightning_sdk/user.py +22 -2
  71. lightning_sdk/utils/resolve.py +9 -7
  72. {lightning_sdk-2025.12.9.dist-info → lightning_sdk-2025.12.17.dist-info}/METADATA +1 -1
  73. {lightning_sdk-2025.12.9.dist-info → lightning_sdk-2025.12.17.dist-info}/RECORD +78 -53
  74. /lightning_sdk/lightning_cloud/openapi/models/{v1_list_filesystem_mm_ts_response.py → v1_list_filesystem_mmts_response.py} +0 -0
  75. {lightning_sdk-2025.12.9.dist-info → lightning_sdk-2025.12.17.dist-info}/LICENSE +0 -0
  76. {lightning_sdk-2025.12.9.dist-info → lightning_sdk-2025.12.17.dist-info}/WHEEL +0 -0
  77. {lightning_sdk-2025.12.9.dist-info → lightning_sdk-2025.12.17.dist-info}/entry_points.txt +0 -0
  78. {lightning_sdk-2025.12.9.dist-info → lightning_sdk-2025.12.17.dist-info}/top_level.txt +0 -0
@@ -1,9 +1,12 @@
1
- from typing import Optional
1
+ from typing import TYPE_CHECKING, Optional
2
2
 
3
3
  from lightning_sdk.api import OrgApi
4
4
  from lightning_sdk.owner import Owner
5
5
  from lightning_sdk.utils.resolve import _resolve_org_name
6
6
 
7
+ if TYPE_CHECKING:
8
+ from lightning_sdk.teamspace import Teamspace
9
+
7
10
 
8
11
  class Organization(Owner):
9
12
  """Represents an organization owner of teamspaces and studios.
@@ -44,6 +47,12 @@ class Organization(Owner):
44
47
  def default_cloud_account(self) -> Optional[str]:
45
48
  return self._org.preferred_cluster or None
46
49
 
50
+ def create_teamspace(self, name: str) -> "Teamspace":
51
+ from lightning_sdk.teamspace import Teamspace
52
+
53
+ self._org_api.create_teamspace(name, self.id)
54
+ return Teamspace(name=name, org=self)
55
+
47
56
  def __repr__(self) -> str:
48
57
  """Returns reader friendly representation."""
49
58
  return f"Organization(name={self.name})"
lightning_sdk/owner.py CHANGED
@@ -24,6 +24,10 @@ class Owner(ABC, metaclass=TrackCallsABCMeta):
24
24
  def id(self) -> str:
25
25
  """The owner's ID."""
26
26
 
27
+ @abstractmethod
28
+ def create_teamspace(self, name: str) -> "Teamspace":
29
+ """Creates a new teamspace."""
30
+
27
31
  @property
28
32
  def teamspaces(self) -> List["Teamspace"]:
29
33
  """All teamspaces by this owner."""
@@ -176,6 +176,7 @@ class JobStep:
176
176
  max_runtime: Optional[int] = None,
177
177
  wait_for: Union[str, List[str], None] = DEFAULT,
178
178
  reuse_snapshot: bool = True,
179
+ scratch_disks: Optional[Dict[str, int]] = None,
179
180
  ) -> None:
180
181
  self.name = name
181
182
  self.machine = machine or Machine.CPU
@@ -203,6 +204,7 @@ class JobStep:
203
204
  self.max_runtime = max_runtime
204
205
  self.wait_for = wait_for
205
206
  self.reuse_snapshot = reuse_snapshot
207
+ self.scratch_disks = scratch_disks
206
208
 
207
209
  def to_proto(
208
210
  self, teamspace: "Teamspace", cloud_account: str, shared_filesystem: Union[bool, V1SharedFilesystem]
@@ -242,6 +244,7 @@ class JobStep:
242
244
  max_runtime=self.max_runtime,
243
245
  machine_image_version=machine_image_version,
244
246
  reuse_snapshot=self.reuse_snapshot,
247
+ scratch_disks=self.scratch_disks,
245
248
  )
246
249
 
247
250
  return V1PipelineStep(
lightning_sdk/plugin.py CHANGED
@@ -411,12 +411,12 @@ class CustomPortPlugin(_Plugin):
411
411
  if name is None:
412
412
  name = _run_name("port")
413
413
 
414
- return self._studio._studio_api.start_new_port(
414
+ return self._studio._studio_api.add_port(
415
415
  teamspace_id=self._studio._teamspace.id,
416
416
  studio_id=self._studio._studio.id,
417
417
  name=name,
418
418
  port=port,
419
- )
419
+ ).urls[0]
420
420
 
421
421
 
422
422
  @runtime_checkable
lightning_sdk/studio.py CHANGED
@@ -2,7 +2,7 @@ import glob
2
2
  import os
3
3
  import threading
4
4
  import warnings
5
- from typing import TYPE_CHECKING, Any, Dict, Mapping, Optional, Tuple, Union
5
+ from typing import TYPE_CHECKING, Any, Dict, List, Mapping, Optional, Tuple, Union
6
6
 
7
7
  from tqdm.auto import tqdm
8
8
 
@@ -12,7 +12,7 @@ from lightning_sdk.api.utils import AccessibleResource, raise_access_error_if_no
12
12
  from lightning_sdk.base_studio import BaseStudio
13
13
  from lightning_sdk.constants import _LIGHTNING_DEBUG
14
14
  from lightning_sdk.exceptions import OutOfCapacityError
15
- from lightning_sdk.lightning_cloud.openapi import V1ClusterType
15
+ from lightning_sdk.lightning_cloud.openapi import V1ClusterType, V1Endpoint
16
16
  from lightning_sdk.machine import DEFAULT_MACHINE, CloudProvider, Machine
17
17
  from lightning_sdk.organization import Organization
18
18
  from lightning_sdk.owner import Owner
@@ -707,6 +707,37 @@ class Studio(metaclass=TrackCallsMeta):
707
707
  interruptible=interruptible,
708
708
  )
709
709
 
710
+ def add_ports(self, ports: Union[int, List[int], Dict[str, int]]) -> List[V1Endpoint]:
711
+ """Add one or more ports to the studio and return their endpoints.
712
+
713
+ Args:
714
+ ports: Port to add. Can be:
715
+ - int: Single port (e.g., 8080)
716
+ - List[int]: Multiple ports ()
717
+ - dict[str, int]: Named ports (e.g., {"web": 8080})
718
+
719
+ Returns:
720
+ List of V1Endpoint objects. Access endpoint properties like:
721
+ - endpoint.name: Port name (None for unnamed ports)
722
+ - endpoint.ports: List of port numbers
723
+ - endpoint.urls: List of accessible URLs
724
+ """
725
+ if isinstance(ports, dict):
726
+ port_items = ports.items()
727
+ elif isinstance(ports, list):
728
+ port_items = ((None, port) for port in ports)
729
+ else:
730
+ port_items = [(None, ports)]
731
+
732
+ return [
733
+ self._studio_api.add_port(self._teamspace.id, self._studio.id, name=name, port=port)
734
+ for name, port in port_items
735
+ ]
736
+
737
+ def list_ports(self) -> List[V1Endpoint]:
738
+ """List ports that are exposed in the Studio."""
739
+ return self._studio_api.list_ports(self._teamspace.id, self._studio.id)
740
+
710
741
  def create_assistant(self, name: str, port: int) -> None:
711
742
  assistant = self._studio_api.create_assistant(
712
743
  studio_id=self._studio.id, teamspace_id=self._teamspace.id, port=port, assistant_name=name
@@ -11,7 +11,12 @@ import lightning_sdk
11
11
  from lightning_sdk.agents import Agent
12
12
  from lightning_sdk.api import CloudAccountApi, TeamspaceApi
13
13
  from lightning_sdk.api.utils import AccessibleResource, raise_access_error_if_not_allowed
14
- from lightning_sdk.lightning_cloud.openapi import V1ClusterType, V1Model, V1ModelVersionArchive, V1ProjectClusterBinding
14
+ from lightning_sdk.lightning_cloud.openapi import (
15
+ V1ClusterType,
16
+ V1Model,
17
+ V1ModelVersionArchive,
18
+ V1ProjectClusterBinding,
19
+ )
15
20
  from lightning_sdk.machine import CloudProvider, Machine
16
21
  from lightning_sdk.models import UploadedModelInfo
17
22
  from lightning_sdk.organization import Organization
@@ -275,14 +280,48 @@ class Teamspace(metaclass=TrackCallsMeta):
275
280
 
276
281
  self._teamspace_api.set_secret(self.id, key, value)
277
282
 
278
- def list_machines(self, cloud_account: Optional[str] = None) -> List[Machine]:
283
+ def list_machines(self, cloud_account: Optional[str] = None, machine: Optional[str] = None) -> List[Machine]:
284
+ """List available machines across cloud accounts.
285
+
286
+ Args:
287
+ cloud_account: The cloud account from which to list available machines. If None, uses LIGHTNING_CLUSTER_ID
288
+ environment variable. If that's also None, queries all global cloud accounts.
289
+ machine: Specific machine name to filter by. If provided, only returns that
290
+ machine type. Must be a valid Machine enum value.
291
+
292
+ Returns:
293
+ List of available machines, excluding out-of-capacity machines.
294
+ """
279
295
  if cloud_account is None:
280
- cloud_account = os.getenv("LIGHTNING_CLUSTER_ID") or self.default_cloud_account
296
+ cloud_account = os.getenv("LIGHTNING_CLUSTER_ID", None)
281
297
 
298
+ # if cloud_account is not given as a paramter and as a env var, use global cloud_accounts
282
299
  if cloud_account is None:
300
+ global_cloud_accounts = self._cloud_account_api.list_global_cloud_accounts(teamspace_id=self.id)
301
+ cloud_accounts = [cm.id for cm in global_cloud_accounts]
302
+ else:
303
+ cloud_accounts = [cloud_account]
304
+
305
+ if cloud_accounts is None:
283
306
  raise RuntimeError("Could not resolve cloud account")
284
307
 
285
- cloud_machines = self._teamspace_api.list_machines(self.id, cloud_account=cloud_account)
308
+ if machine:
309
+ _machine_values = tuple(
310
+ [
311
+ machine.name
312
+ for machine in Machine.__dict__.values()
313
+ if isinstance(machine, Machine) and machine._include_in_cli
314
+ ]
315
+ )
316
+ if machine not in _machine_values:
317
+ raise ValueError(f"Machine '{machine}' is not valid. Valid machines are: {_machine_values}")
318
+ machine = getattr(Machine, machine.upper(), Machine(machine, machine))
319
+
320
+ cloud_machines = self._teamspace_api.list_machines(
321
+ self.id, cloud_accounts=cloud_accounts, machine=machine, org_id=self._org.id
322
+ )
323
+ # filter out of capacity machines
324
+ cloud_machines = [cm for cm in cloud_machines if not cm.out_of_capacity]
286
325
  return [
287
326
  Machine(
288
327
  name=cluster_machine.instance_id,
@@ -292,6 +331,7 @@ class Teamspace(metaclass=TrackCallsMeta):
292
331
  accelerator_count=cluster_machine.resources.gpu or cluster_machine.resources.cpu,
293
332
  cost=cluster_machine.cost,
294
333
  interruptible_cost=cluster_machine.spot_price,
334
+ provider=cluster_machine.provider,
295
335
  wait_time=float(cluster_machine.available_in_seconds) if cluster_machine.available_in_seconds else None,
296
336
  interruptible_wait_time=float(cluster_machine.available_in_seconds_spot)
297
337
  if cluster_machine.available_in_seconds_spot
lightning_sdk/user.py CHANGED
@@ -1,8 +1,12 @@
1
- from typing import Dict, Optional
1
+ from typing import TYPE_CHECKING, Dict, List, Optional
2
2
 
3
3
  from lightning_sdk.api import UserApi
4
4
  from lightning_sdk.owner import Owner
5
- from lightning_sdk.utils.resolve import _resolve_user_name
5
+ from lightning_sdk.utils.resolve import _get_authed_user, _get_organizations_for_authed_user, _resolve_user_name
6
+
7
+ if TYPE_CHECKING:
8
+ from lightning_sdk.organization import Organization
9
+ from lightning_sdk.teamspace import Teamspace
6
10
 
7
11
 
8
12
  class User(Owner):
@@ -55,6 +59,22 @@ class User(Owner):
55
59
 
56
60
  self._user_api.set_secret(key, value)
57
61
 
62
+ def create_teamspace(self, name: str) -> "Teamspace":
63
+ from lightning_sdk.teamspace import Teamspace
64
+
65
+ if not _get_authed_user().id == self.id:
66
+ raise ValueError("Can only create teamspaces for currently authenticated user")
67
+
68
+ self._user_api.create_teamspace(name)
69
+ return Teamspace(name=name, user=self)
70
+
71
+ @property
72
+ def organizations(self) -> List["Organization"]:
73
+ if not _get_authed_user().id == self.id:
74
+ raise ValueError("Can only list organizations for currently authenticated user")
75
+
76
+ return _get_organizations_for_authed_user(user_api=self._user_api)
77
+
58
78
  def __repr__(self) -> str:
59
79
  """Returns reader friendly representation."""
60
80
  return f"User(name={self.name})"
@@ -2,6 +2,7 @@ import logging
2
2
  import os
3
3
  import warnings
4
4
  from contextlib import contextmanager
5
+ from functools import lru_cache
5
6
  from typing import TYPE_CHECKING, Generator, List, Optional, Tuple, Union
6
7
 
7
8
  from lightning_sdk.api import TeamspaceApi, UserApi
@@ -222,25 +223,26 @@ def _resolve_teamspace(
222
223
  raise RuntimeError("Neither user nor org provided, but one of them needs to be provided")
223
224
 
224
225
 
225
- def _get_organizations_for_authed_user() -> List["Organization"]:
226
+ def _get_organizations_for_authed_user(user_api: Optional[UserApi] = None) -> List["Organization"]:
226
227
  """Returns Organizations the current Authed user is a member of."""
227
228
  from lightning_sdk.organization import Organization
228
229
 
229
- _orgs = UserApi()._get_organizations_for_authed_user()
230
+ _orgs = (user_api or UserApi())._get_organizations_for_authed_user()
230
231
  return [Organization(_org.name) for _org in _orgs]
231
232
 
232
233
 
233
- def _get_teamspace_names_for_authed_user() -> List[str]:
234
+ def _get_teamspace_names_for_authed_user(user_api: Optional[UserApi] = None) -> List[str]:
234
235
  """Returns Teamspace's names the current Authed user is a member of."""
235
- teamspaces = UserApi()._get_all_teamspace_memberships("")
236
+ teamspaces = (user_api or UserApi())._get_all_teamspace_memberships("")
236
237
  return sorted([ts.name for ts in teamspaces])
237
238
 
238
239
 
239
- def _get_authed_user() -> "User":
240
+ @lru_cache(maxsize=1)
241
+ def _get_authed_user(user_api: Optional[UserApi] = None, teamspace_api: Optional[TeamspaceApi] = None) -> "User":
240
242
  from lightning_sdk.user import User
241
243
 
242
- user_id = TeamspaceApi()._get_authed_user_id()
243
- _user = UserApi()._get_user_by_id(user_id)
244
+ user_id = (teamspace_api or TeamspaceApi())._get_authed_user_id()
245
+ _user = (user_api or UserApi())._get_user_by_id(user_id)
244
246
  return User(name=_user.username)
245
247
 
246
248
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lightning_sdk
3
- Version: 2025.12.9
3
+ Version: 2025.12.17
4
4
  Summary: SDK to develop using Lightning AI Studios
5
5
  Author-email: Lightning-AI <justus@lightning.ai>
6
6
  License: MIT License