lightning-sdk 2025.11.5__py3-none-any.whl → 2025.11.13__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.
@@ -1,3 +1,3 @@
1
1
  """Version information for lightning_sdk."""
2
2
 
3
- __version__ = "2025.11.05"
3
+ __version__ = "2025.11.13"
@@ -4,7 +4,8 @@ import math
4
4
  import os
5
5
  import re
6
6
  from concurrent.futures import ThreadPoolExecutor
7
- from functools import partial
7
+ from enum import Enum
8
+ from functools import lru_cache, partial
8
9
  from pathlib import Path
9
10
  from typing import Any, Callable, Dict, List, Optional, Tuple, TypedDict, Union
10
11
 
@@ -747,3 +748,59 @@ def resolve_path_mappings(
747
748
  )
748
749
 
749
750
  return path_mappings_list
751
+
752
+
753
+ class AccessibleResource(Enum):
754
+ Studios = "studio"
755
+ Drive = "drive"
756
+ Jobs = "jobs"
757
+ Deployments = "deployments"
758
+ Pipelines = "pipelines"
759
+ Models = "models"
760
+ Containers = "containers"
761
+ Settings = "settings"
762
+
763
+ def __str__(self) -> str:
764
+ """Return the string representation of the resource type."""
765
+ return self.value
766
+
767
+ def __repr__(self) -> str:
768
+ """Return the string representation of the resource type."""
769
+ return self.value
770
+
771
+ def __eq__(self, other: object) -> bool:
772
+ """Return True if the resource type is equal to the other resource type."""
773
+ if isinstance(other, AccessibleResource):
774
+ return self.value == other.value
775
+ return str(other) == self.value
776
+
777
+ def __hash__(self) -> int:
778
+ """Return the hash of the resource type."""
779
+ return hash(self.value)
780
+
781
+
782
+ @lru_cache
783
+ def allowed_resource_access(resource_type: AccessibleResource, teamspace_id: str) -> bool:
784
+ # TODO: change this to proper API
785
+ from lightning_sdk.api.teamspace_api import TeamspaceApi
786
+
787
+ teamspace_api = TeamspaceApi()
788
+ teamspace = teamspace_api._get_teamspace_by_id(teamspace_id=teamspace_id)
789
+
790
+ # when we find the tab, check if it is enabled
791
+ if teamspace.layout_config:
792
+ for tab in teamspace.layout_config:
793
+ if tab.slug == resource_type:
794
+ return tab.is_enabled
795
+
796
+ # tab isn't found, allow access by default for backwards compatibility
797
+ # TODO: add additional checks here if required
798
+ return True
799
+
800
+
801
+ def raise_access_error_if_not_allowed(resource_type: AccessibleResource, teamspace_id: str) -> None:
802
+ if not allowed_resource_access(resource_type, teamspace_id):
803
+ raise PermissionError(
804
+ f"Access to {resource_type.name} has been disabled for this teamspace. "
805
+ "Contact a teamspace administrator to enable it."
806
+ )
@@ -12,7 +12,7 @@ from rich.console import Console
12
12
  from rich.progress import Progress, SpinnerColumn, TextColumn, TimeElapsedColumn
13
13
  from rich.prompt import Confirm
14
14
 
15
- from lightning_sdk import Machine, Teamspace
15
+ from lightning_sdk import CloudProvider, Machine, Teamspace
16
16
  from lightning_sdk.api.lit_container_api import LitContainerApi
17
17
  from lightning_sdk.api.utils import _get_registry_url
18
18
  from lightning_sdk.cli.legacy.clusters_menu import _ClustersMenu
@@ -122,6 +122,12 @@ def deploy() -> None:
122
122
  "If not provided will fall back to the teamspaces default cloud account."
123
123
  ),
124
124
  )
125
+ @click.option(
126
+ "--cloud-provider",
127
+ "--cloud_provider",
128
+ default=None,
129
+ help="The provider to create the studio on. If --cloud-account is specified, this option is prioritized.",
130
+ )
125
131
  @click.option("--port", default=8000, help="The port to expose the API on.")
126
132
  @click.option("--min_replica", "--min-replica", default=0, help="Number of replicas to start with.")
127
133
  @click.option("--max_replica", "--max-replica", default=1, help="Number of replicas to scale up to.")
@@ -152,6 +158,7 @@ def api(
152
158
  max_replica: Optional[int],
153
159
  replicas: Optional[int],
154
160
  no_credentials: Optional[bool],
161
+ cloud_provider: Optional[str],
155
162
  ) -> None:
156
163
  """Deploy a LitServe model script."""
157
164
  return api_impl(
@@ -172,6 +179,7 @@ def api(
172
179
  min_replica=min_replica,
173
180
  max_replica=max_replica,
174
181
  include_credentials=not no_credentials,
182
+ cloud_provider=cloud_provider,
175
183
  )
176
184
 
177
185
 
@@ -194,6 +202,7 @@ def api_impl(
194
202
  max_replica: Optional[int] = 1,
195
203
  replicas: Optional[int] = 1,
196
204
  include_credentials: Optional[bool] = True,
205
+ cloud_provider: Optional[str] = None,
197
206
  ) -> None:
198
207
  """Deploy a LitServe model script."""
199
208
  console = Console()
@@ -226,6 +235,7 @@ def api_impl(
226
235
  return _handle_devbox(name, script_path, console, non_interactive, machine, interruptible, teamspace, org, user)
227
236
 
228
237
  machine = Machine.from_str(machine)
238
+ cloud_provider = CloudProvider.from_str(cloud_provider) if cloud_provider else None
229
239
  return _handle_cloud(
230
240
  script_path,
231
241
  console,
@@ -243,6 +253,7 @@ def api_impl(
243
253
  max_replica=max_replica,
244
254
  replicas=replicas,
245
255
  include_credentials=include_credentials,
256
+ cloud_provider=cloud_provider,
246
257
  )
247
258
 
248
259
 
@@ -304,6 +315,7 @@ def _handle_cloud(
304
315
  max_replica: Optional[int] = 1,
305
316
  replicas: Optional[int] = 1,
306
317
  include_credentials: Optional[bool] = True,
318
+ cloud_provider: Optional[CloudProvider] = None,
307
319
  ) -> None:
308
320
  if not is_connected():
309
321
  console.print("❌ Internet connection required to deploy to the cloud.", style="red")
@@ -379,7 +391,7 @@ def _handle_cloud(
379
391
  resolved_teamspace = select_teamspace(teamspace, org, user)
380
392
 
381
393
  lightning_containers_cloud_account = cloud_account
382
- if not cloud_account:
394
+ if not cloud_account and not cloud_provider:
383
395
  clusters_menu = _ClustersMenu()
384
396
  lightning_containers_cloud_account = clusters_menu._resolve_cluster(resolved_teamspace)
385
397
  cloud_account = resolved_teamspace.default_cloud_account
@@ -414,6 +426,7 @@ def _handle_cloud(
414
426
  "include_credentials": include_credentials,
415
427
  "cloudspace_id": cloudspace_id,
416
428
  "from_onboarding": from_onboarding,
429
+ "cloud_provider": cloud_provider,
417
430
  },
418
431
  )
419
432
  thread.start()
@@ -446,6 +459,7 @@ def _handle_cloud(
446
459
  include_credentials=include_credentials,
447
460
  cloudspace_id=cloudspace_id,
448
461
  from_onboarding=from_onboarding,
462
+ cloud_provider=cloud_provider,
449
463
  )
450
464
  console.print(f"🚀 Deployment started, access at [i]{deployment_status.get('url')}[/i]")
451
465
  if user_status["onboarded"]:
@@ -6,6 +6,7 @@ import click
6
6
  def register_commands(group: click.Group) -> None:
7
7
  """Register studio commands with the given group."""
8
8
  from lightning_sdk.cli.studio.connect import connect_studio
9
+ from lightning_sdk.cli.studio.cp import cp_studio_file
9
10
  from lightning_sdk.cli.studio.create import create_studio
10
11
  from lightning_sdk.cli.studio.delete import delete_studio
11
12
  from lightning_sdk.cli.studio.list import list_studios
@@ -22,3 +23,4 @@ def register_commands(group: click.Group) -> None:
22
23
  group.add_command(stop_studio)
23
24
  group.add_command(switch_studio)
24
25
  group.add_command(connect_studio)
26
+ group.add_command(cp_studio_file)
@@ -0,0 +1,138 @@
1
+ """Studio cp command."""
2
+
3
+ from pathlib import Path
4
+ from typing import Optional, TypedDict
5
+
6
+ import click
7
+ from rich.console import Console
8
+
9
+ from lightning_sdk.api.utils import _get_cloud_url
10
+ from lightning_sdk.cli.legacy.exceptions import StudioCliError
11
+ from lightning_sdk.cli.utils.owner_selection import OwnerMenu
12
+ from lightning_sdk.cli.utils.studio_selection import StudiosMenu
13
+ from lightning_sdk.cli.utils.teamspace_selection import TeamspacesMenu
14
+ from lightning_sdk.studio import Studio
15
+
16
+
17
+ @click.command("cp")
18
+ @click.argument("source", nargs=1)
19
+ @click.argument("destination", nargs=1)
20
+ def cp_studio_file(source: str, destination: str, teamspace: Optional[str] = None) -> None:
21
+ """Copy a Studio file.
22
+
23
+ SOURCE: Source file to copy from. For Studio files, use the format lit://<owner>/<my-teamspace>/studios/<my-studio>/<filepath>.
24
+
25
+ DESTINATION: Destination file to copy to. For Studio files, use the format lit://<owner>/<my-teamspace>/studios/<my-studio>/<filepath>.
26
+
27
+ Example:
28
+ lightning studio cp source.txt lit://<owner>/<my-teamspace>/studios/<my-studio>/destination.txt
29
+
30
+ """
31
+ return cp_impl(source=source, destination=destination)
32
+
33
+
34
+ def cp_impl(source: str, destination: str) -> None:
35
+ if "lit://" in source and "lit://" in destination:
36
+ raise ValueError("Both source and destination cannot be Studio files.")
37
+ elif "lit://" not in source and "lit://" not in destination:
38
+ raise ValueError("Either source or destination must be a Studio file.")
39
+ elif "lit://" in source:
40
+ # Download from Studio to local
41
+ cp_download(studio_file_path=source, local_file_path=destination)
42
+ else:
43
+ # Upload from local to Studio
44
+ cp_upload(local_file_path=source, studio_file_path=destination)
45
+
46
+
47
+ class StudioPathResult(TypedDict):
48
+ owner: Optional[str]
49
+ teamspace: Optional[str]
50
+ studio: Optional[str]
51
+ destination: Optional[str]
52
+
53
+
54
+ def parse_studio_path(studio_path: str) -> StudioPathResult:
55
+ path_string = studio_path.removeprefix("lit://")
56
+ if not path_string:
57
+ raise ValueError("Studio path cannot be empty after prefix")
58
+
59
+ result: StudioPathResult = {"owner": None, "teamspace": None, "studio": None, "destination": None}
60
+
61
+ if "/studios/" in path_string:
62
+ prefix_part, suffix_part = path_string.split("/studios/", 1)
63
+
64
+ # org and teamspace
65
+ if prefix_part:
66
+ org_ts_components = prefix_part.split("/")
67
+ if len(org_ts_components) == 2:
68
+ result["owner"], result["teamspace"] = org_ts_components
69
+ elif len(org_ts_components) == 1:
70
+ result["teamspace"] = org_ts_components[0]
71
+ else:
72
+ raise ValueError(f"Invalid format: '{prefix_part}'")
73
+
74
+ # studio and destination
75
+ path_parts = suffix_part.split("/")
76
+
77
+ else:
78
+ # studio and destination
79
+ path_parts = path_string.split("/")
80
+
81
+ if not path_parts or len(path_parts) < 2:
82
+ raise ValueError("Invalid: Missing studio name.")
83
+
84
+ result["studio"] = path_parts[0]
85
+ result["destination"] = "/".join(path_parts[1:])
86
+
87
+ return result
88
+
89
+
90
+ def cp_upload(
91
+ local_file_path: str,
92
+ studio_file_path: str,
93
+ ) -> None:
94
+ console = Console()
95
+ if Path(local_file_path).is_dir():
96
+ raise StudioCliError(
97
+ f"The provided path is a folder: {local_file_path}. Use `lightning upload folder` instead."
98
+ )
99
+ if not Path(local_file_path).exists():
100
+ raise FileNotFoundError(f"The provided path does not exist: {local_file_path}.")
101
+
102
+ studio_path_result = parse_studio_path(studio_file_path)
103
+
104
+ selected_studio = resolve_studio(
105
+ studio_path_result["studio"], studio_path_result["teamspace"], studio_path_result["owner"]
106
+ )
107
+ console.print(f"Uploading to {selected_studio.teamspace.name}/{selected_studio.name}")
108
+
109
+ selected_studio.upload_file(local_file_path, studio_file_path)
110
+
111
+ studio_url = (
112
+ _get_cloud_url().replace(":443", "")
113
+ + "/"
114
+ + selected_studio.owner.name
115
+ + "/"
116
+ + selected_studio.teamspace.name
117
+ + "/studios/"
118
+ + selected_studio.name
119
+ )
120
+ console.print(f"See your file at {studio_url}")
121
+
122
+
123
+ def cp_download(
124
+ studio_file_path: str,
125
+ local_file_path: str,
126
+ ) -> None:
127
+ raise NotImplementedError("Download functionality is not implemented yet.")
128
+
129
+
130
+ def resolve_studio(studio_name: Optional[str], teamspace: Optional[str], owner: Optional[str]) -> Studio:
131
+ owner_menu = OwnerMenu()
132
+ resolved_owner = owner_menu(owner=owner)
133
+
134
+ teamspace_menu = TeamspacesMenu(resolved_owner)
135
+ resolved_teamspace = teamspace_menu(teamspace=teamspace)
136
+
137
+ studio_menu = StudiosMenu(resolved_teamspace)
138
+ return studio_menu(studio=studio_name)
@@ -4,7 +4,7 @@ from typing import Any, Dict, List, Optional, Union
4
4
 
5
5
  import requests
6
6
 
7
- from lightning_sdk.api import UserApi
7
+ from lightning_sdk.api import CloudAccountApi, UserApi
8
8
  from lightning_sdk.api.deployment_api import (
9
9
  ApiKeyAuth,
10
10
  Auth,
@@ -28,9 +28,10 @@ from lightning_sdk.api.deployment_api import (
28
28
  to_spec,
29
29
  to_strategy,
30
30
  )
31
+ from lightning_sdk.api.utils import AccessibleResource, raise_access_error_if_not_allowed
31
32
  from lightning_sdk.lightning_cloud import login
32
33
  from lightning_sdk.lightning_cloud.openapi import V1Deployment
33
- from lightning_sdk.machine import Machine
34
+ from lightning_sdk.machine import CloudProvider, Machine
34
35
  from lightning_sdk.organization import Organization
35
36
  from lightning_sdk.services.utilities import _get_cluster
36
37
  from lightning_sdk.studio import Studio
@@ -66,6 +67,7 @@ class Deployment(metaclass=TrackCallsMeta):
66
67
  user: Optional[Union[str, User]] = None,
67
68
  ) -> None:
68
69
  self._request_session = None
70
+ self._cloud_account_api = CloudAccountApi()
69
71
 
70
72
  self._auth = login.Auth()
71
73
  self._user = None
@@ -92,6 +94,8 @@ class Deployment(metaclass=TrackCallsMeta):
92
94
  if self._teamspace is None:
93
95
  raise ValueError("You need to pass a teamspace or an org for your deployment.")
94
96
 
97
+ raise_access_error_if_not_allowed(AccessibleResource.Deployments, self._teamspace.id)
98
+
95
99
  self._deployment_api = DeploymentApi()
96
100
  self._cloud_account = _get_cluster(client=self._deployment_api._client, project_id=self._teamspace.id)
97
101
  self._is_created = False
@@ -134,6 +138,7 @@ class Deployment(metaclass=TrackCallsMeta):
134
138
  from_litserve: Optional[bool] = None,
135
139
  max_runtime: Optional[int] = None,
136
140
  path_mappings: Optional[Dict[str, str]] = None,
141
+ cloud_provider: Optional[CloudProvider] = None,
137
142
  ) -> None:
138
143
  """The Lightning AI Deployment.
139
144
 
@@ -180,6 +185,7 @@ class Deployment(metaclass=TrackCallsMeta):
180
185
  only one of the arguments can be provided.
181
186
 
182
187
  """
188
+ raise_access_error_if_not_allowed(AccessibleResource.Deployments, self._teamspace.id)
183
189
  if self._is_created:
184
190
  raise RuntimeError("This deployment has already been started.")
185
191
 
@@ -199,10 +205,17 @@ class Deployment(metaclass=TrackCallsMeta):
199
205
  if cloud_account is None:
200
206
  cloud_account = _resolve_deprecated_cluster(cloud_account, cluster)
201
207
 
202
- if cloud_account is None and self._cloud_account is not None:
208
+ if cloud_account is None and self._cloud_account is not None and cloud_provider is None:
203
209
  print(f"No cloud account was provided, defaulting to {self._cloud_account.cluster_id}")
204
210
  cloud_account = os.getenv("LIGHTNING_CLUSTER_ID") or self._cloud_account.cluster_id
205
211
 
212
+ _cloud_account = self._cloud_account_api.resolve_cloud_account(
213
+ self.teamspace.id,
214
+ cloud_account=cloud_account,
215
+ cloud_provider=cloud_provider,
216
+ default_cloud_account=self._teamspace.default_cloud_account,
217
+ )
218
+
206
219
  if isinstance(ports, float):
207
220
  ports = [ports]
208
221
 
@@ -237,7 +250,7 @@ class Deployment(metaclass=TrackCallsMeta):
237
250
  replicas=replicas,
238
251
  cloudspace_id=cloudspace_id,
239
252
  spec=to_spec(
240
- cloud_account=cloud_account,
253
+ cloud_account=_cloud_account,
241
254
  command=command,
242
255
  entrypoint=entrypoint,
243
256
  env=env,
@@ -289,6 +302,7 @@ class Deployment(metaclass=TrackCallsMeta):
289
302
  max_runtime: Optional[int] = None,
290
303
  path_mappings: Optional[Dict[str, str]] = None,
291
304
  ) -> None:
305
+ raise_access_error_if_not_allowed(AccessibleResource.Deployments, self._teamspace.id)
292
306
  cloud_account = _resolve_deprecated_cluster(cloud_account, cluster)
293
307
 
294
308
  if command is None and commands is not None:
lightning_sdk/job/job.py CHANGED
@@ -1,12 +1,14 @@
1
1
  from typing import TYPE_CHECKING, Any, Dict, Optional, Union
2
2
 
3
+ from lightning_sdk.api.utils import AccessibleResource, raise_access_error_if_not_allowed
3
4
  from lightning_sdk.job.base import _BaseJob
4
5
  from lightning_sdk.job.v1 import _JobV1
5
6
  from lightning_sdk.job.v2 import _JobV2
6
- from lightning_sdk.utils.resolve import _setup_logger
7
+ from lightning_sdk.utils.resolve import _resolve_teamspace, _setup_logger
7
8
 
8
9
  _logger = _setup_logger(__name__)
9
10
 
11
+
10
12
  if TYPE_CHECKING:
11
13
  from lightning_sdk.machine import CloudProvider, Machine
12
14
  from lightning_sdk.organization import Organization
@@ -39,6 +41,10 @@ class Job(_BaseJob):
39
41
  user: the name of the user owning the :param`teamspace`
40
42
  in case it is owned directly by a user instead of an org.
41
43
  """
44
+ teamspace = _resolve_teamspace(teamspace=teamspace, org=org, user=user)
45
+
46
+ raise_access_error_if_not_allowed(AccessibleResource.Jobs, teamspace.id)
47
+
42
48
  from lightning_sdk.lightning_cloud.openapi.rest import ApiException
43
49
 
44
50
  if not self._force_v1:
@@ -4,6 +4,7 @@ from typing import Dict, List, Optional
4
4
  from rich.console import Console
5
5
 
6
6
  from lightning_sdk.api.lit_container_api import LitContainerApi
7
+ from lightning_sdk.api.utils import AccessibleResource, raise_access_error_if_not_allowed
7
8
  from lightning_sdk.utils.resolve import _resolve_teamspace
8
9
 
9
10
 
@@ -31,6 +32,8 @@ class LitContainer:
31
32
  except Exception:
32
33
  console.print(f"[bold red]Could not resolve teamspace: {teamspace}[/bold red]")
33
34
  return []
35
+
36
+ raise_access_error_if_not_allowed(AccessibleResource.Containers, teamspace.id)
34
37
  project_id = teamspace.id
35
38
  repositories = self._api.list_containers(project_id, cloud_account=cloud_account)
36
39
  table = []
@@ -68,6 +71,8 @@ class LitContainer:
68
71
  teamspace = _resolve_teamspace(teamspace=teamspace, org=org, user=user)
69
72
  except Exception as e:
70
73
  raise ValueError("Could not resolve teamspace") from e
74
+
75
+ raise_access_error_if_not_allowed(AccessibleResource.Containers, teamspace.id)
71
76
  project_id = teamspace.id
72
77
  return self._api.delete_container(project_id, container)
73
78
 
@@ -99,6 +104,8 @@ class LitContainer:
99
104
  except Exception as e:
100
105
  raise ValueError(f"Could not resolve teamspace: {e}") from e
101
106
 
107
+ raise_access_error_if_not_allowed(AccessibleResource.Containers, teamspace.id)
108
+
102
109
  resp = self._api.upload_container(
103
110
  container, teamspace, tag, cloud_account, platform=platform, return_final_dict=return_final_dict
104
111
  )
@@ -134,4 +141,6 @@ class LitContainer:
134
141
  except Exception as e:
135
142
  raise ValueError(f"Could not resolve teamspace: {e}") from e
136
143
 
144
+ raise_access_error_if_not_allowed(AccessibleResource.Containers, teamspace.id)
145
+
137
146
  return self._api.download_container(container, teamspace, tag, cloud_account)
lightning_sdk/machine.py CHANGED
@@ -16,6 +16,14 @@ class CloudProvider(Enum):
16
16
  """Converts the CloudProvider to a str."""
17
17
  return self.value
18
18
 
19
+ @classmethod
20
+ def from_str(cls, provider: str) -> "CloudProvider":
21
+ """Converts a string to a CloudProvider enum member."""
22
+ for cp in cls:
23
+ if cp.value.lower() == provider.lower():
24
+ return cp
25
+ raise ValueError(f"Unknown CloudProvider: {provider}")
26
+
19
27
 
20
28
  @dataclass(frozen=True)
21
29
  class Machine:
lightning_sdk/mmt/mmt.py CHANGED
@@ -1,10 +1,11 @@
1
1
  from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Union
2
2
 
3
3
  from lightning_sdk.api.cloud_account_api import CloudAccountApi
4
+ from lightning_sdk.api.utils import AccessibleResource, raise_access_error_if_not_allowed
4
5
  from lightning_sdk.mmt.base import MMTMachine, _BaseMMT
5
6
  from lightning_sdk.mmt.v1 import _MMTV1
6
7
  from lightning_sdk.mmt.v2 import _MMTV2
7
- from lightning_sdk.utils.resolve import _setup_logger
8
+ from lightning_sdk.utils.resolve import _resolve_teamspace, _setup_logger
8
9
 
9
10
  if TYPE_CHECKING:
10
11
  from lightning_sdk.machine import CloudProvider, Machine
@@ -42,6 +43,9 @@ class MMT(_BaseMMT):
42
43
  user: the name of the user owning the :param`teamspace`
43
44
  in case it is owned directly by a user instead of an org.
44
45
  """
46
+ teamspace = _resolve_teamspace(teamspace=teamspace, org=org, user=user)
47
+ raise_access_error_if_not_allowed(AccessibleResource.Jobs, teamspace_id=teamspace.id)
48
+
45
49
  from lightning_sdk.lightning_cloud.openapi.rest import ApiException
46
50
 
47
51
  if not self._force_v1:
lightning_sdk/models.py CHANGED
@@ -4,6 +4,7 @@ from pathlib import Path
4
4
  from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
5
5
 
6
6
  from lightning_sdk.api import OrgApi, TeamspaceApi, UserApi
7
+ from lightning_sdk.api.utils import AccessibleResource, raise_access_error_if_not_allowed
7
8
  from lightning_sdk.lightning_cloud.openapi import V1ModelVersionArchive
8
9
  from lightning_sdk.lightning_cloud.openapi.models import V1Membership, V1OwnerType
9
10
  from lightning_sdk.lightning_cloud.openapi.rest import ApiException
@@ -121,6 +122,10 @@ def download_model(
121
122
  """
122
123
  name = _extend_model_name_with_teamspace(name)
123
124
  teamspace_owner_name, teamspace_name, model_name, version = _parse_org_teamspace_model_version(name)
125
+
126
+ teamspace = _get_teamspace(name=teamspace_name, organization=teamspace_owner_name)
127
+ raise_access_error_if_not_allowed(AccessibleResource.Models, teamspace.id)
128
+
124
129
  api = TeamspaceApi()
125
130
 
126
131
  try:
@@ -162,6 +167,7 @@ def upload_model(
162
167
  name = _extend_model_name_with_teamspace(name)
163
168
  org_name, teamspace_name, model_name, version = _parse_org_teamspace_model_version(name)
164
169
  teamspace = _get_teamspace(name=teamspace_name, organization=org_name)
170
+ raise_access_error_if_not_allowed(AccessibleResource.Models, teamspace.id)
165
171
  return teamspace.upload_model(
166
172
  path=path,
167
173
  name=model_name,
@@ -185,6 +191,7 @@ def delete_model(
185
191
  name = _extend_model_name_with_teamspace(name)
186
192
  org_name, teamspace_name, model_name, version = _parse_org_teamspace_model_version(name)
187
193
  teamspace = _get_teamspace(name=teamspace_name, organization=org_name)
194
+ raise_access_error_if_not_allowed(AccessibleResource.Models, teamspace.id)
188
195
  teamspace.delete_model(name=f"{model_name}:{version}" if version else model_name)
189
196
 
190
197
 
@@ -200,4 +207,5 @@ def list_model_versions(
200
207
  name = _extend_model_name_with_teamspace(name)
201
208
  org_name, teamspace_name, model_name, _ = _parse_org_teamspace_model_version(name)
202
209
  teamspace = _get_teamspace(name=teamspace_name, organization=org_name)
210
+ raise_access_error_if_not_allowed(AccessibleResource.Models, teamspace.id)
203
211
  return teamspace.list_model_versions(name=model_name)
@@ -3,6 +3,7 @@ from typing import List, Optional, Union
3
3
 
4
4
  from lightning_sdk.api.cloud_account_api import CloudAccountApi
5
5
  from lightning_sdk.api.pipeline_api import PipelineApi
6
+ from lightning_sdk.api.utils import AccessibleResource, raise_access_error_if_not_allowed
6
7
  from lightning_sdk.machine import CloudProvider
7
8
  from lightning_sdk.organization import Organization
8
9
  from lightning_sdk.pipeline.printer import PipelinePrinter
@@ -49,6 +50,8 @@ class Pipeline:
49
50
  if self._teamspace is None:
50
51
  raise RuntimeError("Could not resolve teamspace")
51
52
 
53
+ raise_access_error_if_not_allowed(AccessibleResource.Pipelines, self._teamspace.id)
54
+
52
55
  self._pipeline_api = PipelineApi()
53
56
  self._cloud_account_api = CloudAccountApi()
54
57
  self._cloud_account = self._cloud_account_api.resolve_cloud_account(
lightning_sdk/serve.py CHANGED
@@ -7,7 +7,7 @@ from typing import Generator, List, Optional, Union
7
7
  import docker
8
8
  from rich.console import Console
9
9
 
10
- from lightning_sdk import Deployment, Machine, Teamspace
10
+ from lightning_sdk import CloudProvider, Deployment, Machine, Teamspace
11
11
  from lightning_sdk.api.deployment_api import AutoScaleConfig, DeploymentApi, Env, Secret
12
12
  from lightning_sdk.api.lit_container_api import LitContainerApi
13
13
  from lightning_sdk.api.utils import _get_cloud_url
@@ -249,6 +249,7 @@ Check out [blue][link=https://lightning.ai/docs/litserve/features]the docs[/link
249
249
  include_credentials: Optional[bool] = True,
250
250
  cloudspace_id: Optional[str] = None,
251
251
  from_onboarding: bool = False,
252
+ cloud_provider: Optional[CloudProvider] = None,
252
253
  ) -> dict:
253
254
  """Run a deployment on the cloud. If the deployment already exists, it will be updated.
254
255
 
@@ -304,6 +305,7 @@ Check out [blue][link=https://lightning.ai/docs/litserve/features]the docs[/link
304
305
  from_litserve=True,
305
306
  from_onboarding=from_onboarding,
306
307
  command="",
308
+ cloud_provider=cloud_provider,
307
309
  )
308
310
 
309
311
  return {"deployment": deployment, "url": url}
lightning_sdk/studio.py CHANGED
@@ -8,6 +8,7 @@ from tqdm.auto import tqdm
8
8
 
9
9
  from lightning_sdk.api.cloud_account_api import CloudAccountApi
10
10
  from lightning_sdk.api.studio_api import StudioApi
11
+ from lightning_sdk.api.utils import AccessibleResource, raise_access_error_if_not_allowed
11
12
  from lightning_sdk.base_studio import BaseStudio
12
13
  from lightning_sdk.constants import _LIGHTNING_DEBUG
13
14
  from lightning_sdk.lightning_cloud.openapi import V1ClusterType
@@ -97,6 +98,7 @@ class Studio(metaclass=TrackCallsMeta):
97
98
  raise ValueError("Couldn't resolve teamspace from the provided name, org, or user")
98
99
 
99
100
  self._teamspace = _teamspace
101
+ raise_access_error_if_not_allowed(AccessibleResource.Studios, self._teamspace.id)
100
102
 
101
103
  self._setup_done = getattr(self._skip_setup, "value", False)
102
104
  self._disable_secrets = disable_secrets
@@ -10,6 +10,7 @@ from tqdm.auto import tqdm
10
10
  import lightning_sdk
11
11
  from lightning_sdk.agents import Agent
12
12
  from lightning_sdk.api import CloudAccountApi, TeamspaceApi
13
+ from lightning_sdk.api.utils import AccessibleResource, raise_access_error_if_not_allowed
13
14
  from lightning_sdk.lightning_cloud.openapi import V1ClusterType, V1Model, V1ModelVersionArchive, V1ProjectClusterBinding
14
15
  from lightning_sdk.machine import CloudProvider, Machine
15
16
  from lightning_sdk.models import UploadedModelInfo
@@ -143,12 +144,17 @@ class Teamspace(metaclass=TrackCallsMeta):
143
144
  @property
144
145
  def studios(self) -> List["Studio"]:
145
146
  """All studios within that teamspace."""
147
+ raise_access_error_if_not_allowed(AccessibleResource.Studios, self.id)
146
148
  from lightning_sdk.studio import Studio
147
149
 
148
150
  return self._get_studios(Studio)
149
151
 
150
152
  @property
151
153
  def vms(self) -> List["VM"]:
154
+ try:
155
+ raise_access_error_if_not_allowed(AccessibleResource.Studios, self.id)
156
+ except PermissionError as e:
157
+ raise PermissionError(str(e).replace("Studios", "VMs")) from e
152
158
  from lightning_sdk.studio import VM
153
159
 
154
160
  return [x for x in self._get_studios(VM) if isinstance(x, VM)]
@@ -204,6 +210,8 @@ class Teamspace(metaclass=TrackCallsMeta):
204
210
  from lightning_sdk.job import Job
205
211
  from lightning_sdk.plugin import forced_v1
206
212
 
213
+ raise_access_error_if_not_allowed(AccessibleResource.Jobs, self.id)
214
+
207
215
  jobsv1, jobsv2 = self._teamspace_api.list_jobs(teamspace_id=self.id)
208
216
 
209
217
  jobs = []
@@ -228,6 +236,8 @@ class Teamspace(metaclass=TrackCallsMeta):
228
236
  from lightning_sdk.mmt import MMT
229
237
  from lightning_sdk.plugin import forced_v1
230
238
 
239
+ raise_access_error_if_not_allowed(AccessibleResource.Jobs, self.id)
240
+
231
241
  mmtsv1, mmtsv2 = self._teamspace_api.list_mmts(teamspace_id=self.id)
232
242
 
233
243
  mmts = []
@@ -351,6 +361,7 @@ class Teamspace(metaclass=TrackCallsMeta):
351
361
  progress_bar: Whether to show a progress bar for the upload.
352
362
  metadata: Metadata to attach to the model. Can be a dictionary.
353
363
  """
364
+ raise_access_error_if_not_allowed(AccessibleResource.Models, self.id)
354
365
  if not path:
355
366
  raise ValueError("No path provided to upload")
356
367
  if not name:
@@ -430,6 +441,7 @@ class Teamspace(metaclass=TrackCallsMeta):
430
441
  The absolute path to the downloaded model file or folder.
431
442
 
432
443
  """
444
+ raise_access_error_if_not_allowed(AccessibleResource.Models, self.id)
433
445
  if not name:
434
446
  raise ValueError("No name provided for the model")
435
447
  if download_dir is None:
@@ -468,15 +480,18 @@ class Teamspace(metaclass=TrackCallsMeta):
468
480
  e.g. 'entity/modelname:v1'.
469
481
 
470
482
  """
483
+ raise_access_error_if_not_allowed(AccessibleResource.Models, self.id)
471
484
  name, version = _parse_model_and_version(name)
472
485
  self._teamspace_api.delete_model(name=name, version=version, teamspace_id=self.id)
473
486
 
474
487
  def list_models(self) -> List[V1Model]:
475
488
  """List all models in the model store."""
489
+ raise_access_error_if_not_allowed(AccessibleResource.Models, self.id)
476
490
  return self._teamspace_api.list_models(teamspace_id=self.id)
477
491
 
478
492
  def list_model_versions(self, name: str) -> List[V1ModelVersionArchive]:
479
493
  """List all versions of a model in the model store."""
494
+ raise_access_error_if_not_allowed(AccessibleResource.Models, self.id)
480
495
  if ":" in name:
481
496
  raise ValueError(
482
497
  "Model name should not contain a version tag. Please provide the model name without a version."
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: lightning_sdk
3
- Version: 2025.11.5
3
+ Version: 2025.11.13
4
4
  Summary: SDK to develop using Lightning AI Studios
5
5
  Author-email: Lightning-AI <justus@lightning.ai>
6
6
  License: MIT License
@@ -1,22 +1,22 @@
1
1
  docs/source/conf.py,sha256=r8yX20eC-4mHhMTd0SbQb5TlSWHhO6wnJ0VJ_FBFpag,13249
2
2
  lightning_sdk/__init__.py,sha256=aTu3zCrxj6rhgvc17ditLe71LTGvyU27Fg2GwOR4gUY,1301
3
- lightning_sdk/__version__.py,sha256=2ZSNbneC7ZSnz2YuQSFdnEZDjYLwTi8hkwfzgq0NAOI,73
3
+ lightning_sdk/__version__.py,sha256=hqNBnV5LTBUIhMeuGeJkdLaMqcLZJZ6Gbyid4dzeywE,73
4
4
  lightning_sdk/agents.py,sha256=Uqsdu4PgswhpeXZzDSbp3hu30MMOhSlCnp8D56MaS_c,1658
5
5
  lightning_sdk/ai_hub.py,sha256=QzCfmX0h2CLn7r0DQyjWg6RnNw26Ey6akiP2Wffl9Ls,7006
6
6
  lightning_sdk/base_studio.py,sha256=yMvZ1dDq_avW3iu98CLEo9W9UL54mx4C6FC43IKLWAE,4691
7
7
  lightning_sdk/constants.py,sha256=Lr6MRnBieAbuT94w9KnbueuU15qz4N9Kgu1um7yH2nw,839
8
8
  lightning_sdk/helpers.py,sha256=WvsetkrXC8n4iQUFKpwQOKo7oaaOxgfWJm6KcMeVaTE,3508
9
- lightning_sdk/lit_container.py,sha256=8ys49TXI9MO89jLTA7MwDrKrssTRARAIF9OVmolDLq0,5273
10
- lightning_sdk/machine.py,sha256=GBx4w8qbx_V7ji4-f-uiZ04Z202muORW83Ku0nIbLfA,9614
11
- lightning_sdk/models.py,sha256=ur1My3oivAe0p2tAYtOAGM_1tPBht3Oh-xLun-oUK08,7861
9
+ lightning_sdk/lit_container.py,sha256=9gAGoilwn6x0PF-FxcRm9wyJG64mqOs7HH76g8ksxsk,5715
10
+ lightning_sdk/machine.py,sha256=ezAu7CnZi7TLJo9yGElRmt9j6RNrRGrBBJdr6giLx40,9918
11
+ lightning_sdk/models.py,sha256=p0JZaInjFbxiUx96ZmfNQfr8VDL8n-ZekusqVmWlYHQ,8356
12
12
  lightning_sdk/organization.py,sha256=jizjG4oW_oxkP2Qz4vL5cle4Cg12xB_vf4eT-bnvLP0,1352
13
13
  lightning_sdk/owner.py,sha256=k0Hh0BQNxc6Jajzqt6jUgApM0IxHDNPIe_X1ocCI2Rk,1448
14
14
  lightning_sdk/plugin.py,sha256=yaQrIx2Tt18R0CoarJQAtB_AwaVa436f-eC5rb9yWe4,15579
15
15
  lightning_sdk/sandbox.py,sha256=_NvnWotEXW2rBiVFZZ4krKXxVjuAqfNh04qELSM0-Pg,5786
16
- lightning_sdk/serve.py,sha256=uW7zLhQ3X90ifetpxzTb8FNxifv5vIs7qZlgfEjVKzk,11794
16
+ lightning_sdk/serve.py,sha256=XbllHwmOAOHjQJ0AQukwYe3OtspEwhmf5-_HXhL-mpc,11908
17
17
  lightning_sdk/status.py,sha256=lLGAuSvXBoXQFEEsEYwdCi0RcSNatUn5OPjJVjDtoM0,386
18
- lightning_sdk/studio.py,sha256=lTIZ7OHcv3Y5HchCO4pvl2u20KY0PryhAIM_krM3KaA,36975
19
- lightning_sdk/teamspace.py,sha256=9nXLkyHH52yeUu2W6TCF34AOlfzf_C2mFTs6EJn0pjc,26637
18
+ lightning_sdk/studio.py,sha256=LVpyH58HaDcm5mNFrIYueoiERvUzO7Ed535aWTKs7-k,37159
19
+ lightning_sdk/teamspace.py,sha256=pskDJWe3OGKFeXCKr-QZbzgor2AOQwdzgxqlS8Ahj_o,27558
20
20
  lightning_sdk/user.py,sha256=TSYh38rxoi7qKOfrK2JYh_Nknya2Kbz2ngDIY85fFOY,1778
21
21
  lightning_sdk/api/__init__.py,sha256=xrp_RNECJGQtL5rZHF69WOzEuEIbWSLtjWAJAz4R5K4,500
22
22
  lightning_sdk/api/agents_api.py,sha256=G47TbFo9kYqnBMqdw2RW-lfS1VAUBSXDmzs6fpIEMUs,4059
@@ -34,7 +34,7 @@ lightning_sdk/api/pipeline_api.py,sha256=rJYp_FN7uUjC5xbc6K67l2eRSmVuOkijd5i8Nm5
34
34
  lightning_sdk/api/studio_api.py,sha256=itQqpC9Be2O69CX7eful3Q2t66NrtMPAizhxUDImpKA,39927
35
35
  lightning_sdk/api/teamspace_api.py,sha256=wuwW4YbhloFqI9rS_5mbgsS6Dk3zYONLv9M6OoqyDg8,19980
36
36
  lightning_sdk/api/user_api.py,sha256=o9gZmtvqNfj4kdSpo2fyyRuFAP7bM5w7mx0Oj9__Ads,4702
37
- lightning_sdk/api/utils.py,sha256=1NJgW4HCfzMqgnAqbMA7RRr2v3iM3KTuPIUQK5klDeQ,27127
37
+ lightning_sdk/api/utils.py,sha256=FvkpPt3-5IPfPW5QQsWoYlCC2SQXeQ-PB68gtICrWvM,29051
38
38
  lightning_sdk/cli/__init__.py,sha256=lksw08t_ZIuHOH47LCIqSVHeZ8cUXI2aJWHYhyujYHM,32
39
39
  lightning_sdk/cli/entrypoint.py,sha256=WsHbYB-rGIbXwz_Un6UiuOFdVPwXGTOs7GC5Zh4Y_B4,3467
40
40
  lightning_sdk/cli/groups.py,sha256=umtnKZm4xrIEtY-SWrC1KcKzYGvArr08qeP2zGPkyTk,1609
@@ -73,14 +73,15 @@ lightning_sdk/cli/legacy/upload.py,sha256=fFJct9F_1vLqTMLtSjuv4BN3AbUpeGebwv3ZXH
73
73
  lightning_sdk/cli/legacy/deploy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
74
74
  lightning_sdk/cli/legacy/deploy/_auth.py,sha256=nUe9Lqx154qapHRPdZCWqMICCEPWGjofnikYBdYcBBM,7680
75
75
  lightning_sdk/cli/legacy/deploy/devbox.py,sha256=SdOtYdGefzldn-WCa6sz31zysyf2nP5D7O2x1DE9z_E,6839
76
- lightning_sdk/cli/legacy/deploy/serve.py,sha256=bxN04mHlArjHtxR_QkCM0S_RPmL3WIPsgnPgFadSXwI,15478
76
+ lightning_sdk/cli/legacy/deploy/serve.py,sha256=qzuJ6IRYaIEP9P_A6fGX1gRo2FX2i8gJC3vwX4vbFZs,16094
77
77
  lightning_sdk/cli/license/__init__.py,sha256=UmtIevklzc_2j4fZlrE9RyONHVs2E1AdZD66nWz0OPY,446
78
78
  lightning_sdk/cli/license/get.py,sha256=XkZvsuWwD5OdsAymzNggHJBuNEkZEhz0ZVl7lIWit6g,604
79
79
  lightning_sdk/cli/license/list.py,sha256=RUaR271kdHgTbFIagjOagopkOI8vmIHf3guaup0rzs8,1342
80
80
  lightning_sdk/cli/license/set.py,sha256=uGjFgq-3jkMF0IstMIIRjKn8GDzg9TIe1OZcRkDTrqg,515
81
81
  lightning_sdk/cli/mmt/__init__.py,sha256=CLgr-ZHHLS6Db_JGqpxbn4G2pYrKi4Qn-uhi8e0kFNc,145
82
- lightning_sdk/cli/studio/__init__.py,sha256=jqUuEniYlf3fwJni-MGLII3oprpsBSQEFqzBJwPIxMk,925
82
+ lightning_sdk/cli/studio/__init__.py,sha256=wREPAmPJdN4b51r6RRD1o4aE212KN7xI88Z0O6HTzEU,1022
83
83
  lightning_sdk/cli/studio/connect.py,sha256=RLi5VumsVlVDlyiGbST7lDuHAntOj_-99KsNr8J3knE,5057
84
+ lightning_sdk/cli/studio/cp.py,sha256=u1GM58i4YO70GqbgnNAIkG-HnIOW5b5y1Mby3SsQKRk,4748
84
85
  lightning_sdk/cli/studio/create.py,sha256=eVkuP1qg1aQSe2jyDgbIg-si5xPl8CwDIPcyvkUEFnQ,3179
85
86
  lightning_sdk/cli/studio/delete.py,sha256=Gi1KLCrSgjTiyoQe9NRvF4ZpsNyeM07KHnBxfdJg2FE,1483
86
87
  lightning_sdk/cli/studio/list.py,sha256=ieLiHRIfAb7E5e_r48BeTC03ib8RY6maeF7nkmrqaek,3301
@@ -110,10 +111,10 @@ lightning_sdk/cli/vm/start.py,sha256=v0FI482lscAN9S-XEW1IJ_sHiu45xSxCZJMHgPa8zNI
110
111
  lightning_sdk/cli/vm/stop.py,sha256=rZ-aWCE95n4wPe9edevXI0xq5RL5gxGeG8hCzvCyQCs,671
111
112
  lightning_sdk/cli/vm/switch.py,sha256=7jcKbTnqRv6JjOcDDVRDy8OP9xCxJ3ctLqBJViNAxbc,1174
112
113
  lightning_sdk/deployment/__init__.py,sha256=TRkd1xGCuYcsPzZwvq-vm_Qwlu3-htp8je-PYMcqjFc,579
113
- lightning_sdk/deployment/deployment.py,sha256=ODWC66iculfBYPpqLDIoCvFIWmOvDzg0LmwbHc5x3A4,22714
114
+ lightning_sdk/deployment/deployment.py,sha256=iI2B9dIAQjknqzIcWNP2MsHr-l0K2E9SW1EJp-H24rs,23526
114
115
  lightning_sdk/job/__init__.py,sha256=1MxjQ6rHkyUHCypSW9RuXuVMVH11WiqhIXcU2LCFMwE,64
115
116
  lightning_sdk/job/base.py,sha256=4_hdUOqorLaaym10ccoB9TvWZM_btEJkVI5_wE9K0oM,21744
116
- lightning_sdk/job/job.py,sha256=mFmNv9zXYp1GCHno0Ii2myftG1CBt7TT3u8maw8fcd4,15551
117
+ lightning_sdk/job/job.py,sha256=9RTMSf2czZbrrjrGZ-2fOZAWMIGomZMG2UE5D_6pgPs,15825
117
118
  lightning_sdk/job/v1.py,sha256=32mzDi4y2zNMYk9ZCMQS1c_yJfbnKrAjwKnJmo6HMCU,9977
118
119
  lightning_sdk/job/v2.py,sha256=VAArSIEDH05H3gnALVVPLNRrKm42cxG8No6i9jT_NzY,11710
119
120
  lightning_sdk/job/work.py,sha256=aRknNDja-96qQaYw0fNboEGtOZCmvztYEzUPxLNAt8g,2456
@@ -1240,11 +1241,11 @@ lightning_sdk/llm/llm.py,sha256=pjBs8fbGZjs0tQRKHx-WdfTmeECKAwI_T1ShVBgdyO4,2083
1240
1241
  lightning_sdk/llm/public_assistants.py,sha256=k0yc41AyrKImnRa8Fv-ow9javnlrRQeP63507p2VybA,1579
1241
1242
  lightning_sdk/mmt/__init__.py,sha256=ExMu90-96bGBnyp5h0CErQszUGB1-PcjC4-R8_NYbeY,117
1242
1243
  lightning_sdk/mmt/base.py,sha256=fP5bcZewLgUCLTH82CTauKSH6J4994pS8ZuFnzzN9BI,16991
1243
- lightning_sdk/mmt/mmt.py,sha256=zPqPXgToOzwX_hzPTIuv7W_be8tvaQV700Vq1nFzOT0,15582
1244
+ lightning_sdk/mmt/mmt.py,sha256=LDXiPskJoZslEXapKE1WPIeczGAaOTP-RXZ1AgF68zw,15867
1244
1245
  lightning_sdk/mmt/v1.py,sha256=PRyqgoHUlzZWsjUv2q8vHT00FxyCSqqVt5p5R9tu36s,9114
1245
1246
  lightning_sdk/mmt/v2.py,sha256=iQOXb631M1zk63nJzomG-7fh0BLScZz98ndKjn4NMYo,11169
1246
1247
  lightning_sdk/pipeline/__init__.py,sha256=Sja_0NJ8vgh-2ThSVP3WDI9a9qghrWd21LkaQp4Zsp8,378
1247
- lightning_sdk/pipeline/pipeline.py,sha256=A11Q_bRhiQm89n-VcdaXBJsvK8Yb_EY_IijOGnBmPoI,6163
1248
+ lightning_sdk/pipeline/pipeline.py,sha256=l9yZit7HVj8JyVFRWFGjeU4n6oG6J2f7MNEev650ONw,6346
1248
1249
  lightning_sdk/pipeline/printer.py,sha256=fsewFE_nnI-x5KscviYvQbKgNcgMyn-uT0UWlArGn-8,4828
1249
1250
  lightning_sdk/pipeline/schedule.py,sha256=J6lfjeuUMqGhX6xo6-qIliyi7KmB4e8BimTKJh7cJXY,20059
1250
1251
  lightning_sdk/pipeline/steps.py,sha256=vo7pC4gmcx_xMfWwAIctBk39QCxzmbjvDVwWSnTswI4,14424
@@ -1262,9 +1263,9 @@ lightning_sdk/utils/logging.py,sha256=WPyOx7KAn8OpRKqoDDlB7MkORtLDryJsj1WVXLYNqy
1262
1263
  lightning_sdk/utils/names.py,sha256=1EuXbIh7wldkDp1FG10oz9vIOyWrpGWeFFVy-DQBgzA,18162
1263
1264
  lightning_sdk/utils/progress.py,sha256=bLWw39fzq29PMWoFXaPIVfoS3Ug245950oWOFJ2ZaiU,12596
1264
1265
  lightning_sdk/utils/resolve.py,sha256=ukC-Zn35gNZV-fQ-ZyUgZkRPFb8nwsFh_aN7YcJ0sl8,10613
1265
- lightning_sdk-2025.11.5.dist-info/LICENSE,sha256=uFIuZwj5z-4TeF2UuacPZ1o17HkvKObT8fY50qN84sg,1064
1266
- lightning_sdk-2025.11.5.dist-info/METADATA,sha256=agAOAZJ6zSWJuOywfVIGN9BEYQdPzDPQvTP44ZxqWBY,4130
1267
- lightning_sdk-2025.11.5.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
1268
- lightning_sdk-2025.11.5.dist-info/entry_points.txt,sha256=OoZa4Fc8NMs6GSN0cdA1J8e6couzAcL82CbM1yo4f_M,122
1269
- lightning_sdk-2025.11.5.dist-info/top_level.txt,sha256=ps8doKILFXmN7F1mHncShmnQoTxKBRPIcchC8TpoBw4,19
1270
- lightning_sdk-2025.11.5.dist-info/RECORD,,
1266
+ lightning_sdk-2025.11.13.dist-info/LICENSE,sha256=uFIuZwj5z-4TeF2UuacPZ1o17HkvKObT8fY50qN84sg,1064
1267
+ lightning_sdk-2025.11.13.dist-info/METADATA,sha256=drF7S545973JTcSaLYX2usrmASxaJih4AB61RJGwsn8,4131
1268
+ lightning_sdk-2025.11.13.dist-info/WHEEL,sha256=iAkIy5fosb7FzIOwONchHf19Qu7_1wCWyFNR5gu9nU0,91
1269
+ lightning_sdk-2025.11.13.dist-info/entry_points.txt,sha256=OoZa4Fc8NMs6GSN0cdA1J8e6couzAcL82CbM1yo4f_M,122
1270
+ lightning_sdk-2025.11.13.dist-info/top_level.txt,sha256=ps8doKILFXmN7F1mHncShmnQoTxKBRPIcchC8TpoBw4,19
1271
+ lightning_sdk-2025.11.13.dist-info/RECORD,,