zenml-nightly 0.80.2.dev20250414__py3-none-any.whl → 0.80.2.dev20250415__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 (27) hide show
  1. zenml/VERSION +1 -1
  2. zenml/artifacts/utils.py +7 -2
  3. zenml/config/server_config.py +7 -0
  4. zenml/constants.py +5 -0
  5. zenml/integrations/kubernetes/flavors/kubernetes_step_operator_flavor.py +12 -0
  6. zenml/integrations/kubernetes/orchestrators/kube_utils.py +92 -0
  7. zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py +13 -3
  8. zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint.py +12 -65
  9. zenml/integrations/kubernetes/step_operators/kubernetes_step_operator.py +14 -3
  10. zenml/materializers/path_materializer.py +17 -2
  11. zenml/utils/io_utils.py +23 -0
  12. zenml/zen_server/auth.py +52 -0
  13. zenml/zen_server/cloud_utils.py +7 -1
  14. zenml/zen_server/download_utils.py +126 -0
  15. zenml/zen_server/rbac/rbac_interface.py +10 -3
  16. zenml/zen_server/rbac/utils.py +13 -3
  17. zenml/zen_server/rbac/zenml_cloud_rbac.py +14 -8
  18. zenml/zen_server/routers/artifact_version_endpoints.py +86 -3
  19. zenml/zen_server/routers/users_endpoints.py +13 -8
  20. zenml/zen_server/template_execution/utils.py +2 -2
  21. zenml/zen_stores/migrations/versions/ff538a321a92_migrate_onboarding_state.py +123 -0
  22. zenml/zen_stores/schemas/server_settings_schemas.py +4 -1
  23. {zenml_nightly-0.80.2.dev20250414.dist-info → zenml_nightly-0.80.2.dev20250415.dist-info}/METADATA +1 -1
  24. {zenml_nightly-0.80.2.dev20250414.dist-info → zenml_nightly-0.80.2.dev20250415.dist-info}/RECORD +27 -25
  25. {zenml_nightly-0.80.2.dev20250414.dist-info → zenml_nightly-0.80.2.dev20250415.dist-info}/LICENSE +0 -0
  26. {zenml_nightly-0.80.2.dev20250414.dist-info → zenml_nightly-0.80.2.dev20250415.dist-info}/WHEEL +0 -0
  27. {zenml_nightly-0.80.2.dev20250414.dist-info → zenml_nightly-0.80.2.dev20250415.dist-info}/entry_points.txt +0 -0
zenml/VERSION CHANGED
@@ -1 +1 @@
1
- 0.80.2.dev20250414
1
+ 0.80.2.dev20250415
zenml/artifacts/utils.py CHANGED
@@ -35,7 +35,9 @@ from zenml.artifacts.preexisting_data_materializer import (
35
35
  PreexistingDataMaterializer,
36
36
  )
37
37
  from zenml.client import Client
38
- from zenml.constants import MODEL_METADATA_YAML_FILE_NAME
38
+ from zenml.constants import (
39
+ MODEL_METADATA_YAML_FILE_NAME,
40
+ )
39
41
  from zenml.enums import (
40
42
  ArtifactSaveType,
41
43
  ArtifactType,
@@ -43,7 +45,10 @@ from zenml.enums import (
43
45
  StackComponentType,
44
46
  VisualizationType,
45
47
  )
46
- from zenml.exceptions import DoesNotExistException, StepContextError
48
+ from zenml.exceptions import (
49
+ DoesNotExistException,
50
+ StepContextError,
51
+ )
47
52
  from zenml.io import fileio
48
53
  from zenml.logger import get_logger
49
54
  from zenml.metadata.metadata_types import validate_metadata
@@ -34,6 +34,7 @@ from zenml.constants import (
34
34
  DEFAULT_ZENML_JWT_TOKEN_LEEWAY,
35
35
  DEFAULT_ZENML_SERVER_DEVICE_AUTH_POLLING,
36
36
  DEFAULT_ZENML_SERVER_DEVICE_AUTH_TIMEOUT,
37
+ DEFAULT_ZENML_SERVER_FILE_DOWNLOAD_SIZE_LIMIT,
37
38
  DEFAULT_ZENML_SERVER_GENERIC_API_TOKEN_LIFETIME,
38
39
  DEFAULT_ZENML_SERVER_GENERIC_API_TOKEN_MAX_LIFETIME,
39
40
  DEFAULT_ZENML_SERVER_LOGIN_RATE_LIMIT_DAY,
@@ -245,6 +246,8 @@ class ServerConfiguration(BaseModel):
245
246
  memcache_default_expiry: The default expiry time in seconds for cache
246
247
  entries. If not specified, the default value of 30 seconds will be
247
248
  used.
249
+ file_download_size_limit: The maximum size of the file download in
250
+ bytes. If not specified, the default value of 2GB will be used.
248
251
  """
249
252
 
250
253
  deployment_type: ServerDeploymentType = ServerDeploymentType.OTHER
@@ -346,6 +349,10 @@ class ServerConfiguration(BaseModel):
346
349
  memcache_max_capacity: int = 1000
347
350
  memcache_default_expiry: int = 30
348
351
 
352
+ file_download_size_limit: int = (
353
+ DEFAULT_ZENML_SERVER_FILE_DOWNLOAD_SIZE_LIMIT
354
+ )
355
+
349
356
  _deployment_id: Optional[UUID] = None
350
357
 
351
358
  @model_validator(mode="before")
zenml/constants.py CHANGED
@@ -192,12 +192,14 @@ ENV_ZENML_SERVER_PRO_PREFIX = "ZENML_SERVER_PRO_"
192
192
  ENV_ZENML_SERVER_DEPLOYMENT_TYPE = f"{ENV_ZENML_SERVER_PREFIX}DEPLOYMENT_TYPE"
193
193
  ENV_ZENML_SERVER_AUTH_SCHEME = f"{ENV_ZENML_SERVER_PREFIX}AUTH_SCHEME"
194
194
  ENV_ZENML_SERVER_AUTO_ACTIVATE = f"{ENV_ZENML_SERVER_PREFIX}AUTO_ACTIVATE"
195
+
195
196
  ENV_ZENML_RUN_SINGLE_STEPS_WITHOUT_STACK = (
196
197
  "ZENML_RUN_SINGLE_STEPS_WITHOUT_STACK"
197
198
  )
198
199
  ENV_ZENML_PREVENT_CLIENT_SIDE_CACHING = "ZENML_PREVENT_CLIENT_SIDE_CACHING"
199
200
  ENV_ZENML_DISABLE_CREDENTIALS_DISK_CACHING = "DISABLE_CREDENTIALS_DISK_CACHING"
200
201
  ENV_ZENML_RUNNER_IMAGE_DISABLE_UV = "ZENML_RUNNER_IMAGE_DISABLE_UV"
202
+
201
203
  # Logging variables
202
204
  IS_DEBUG_ENV: bool = handle_bool_env_var(ENV_ZENML_DEBUG, default=False)
203
205
 
@@ -284,6 +286,7 @@ DEFAULT_ZENML_SERVER_GENERIC_API_TOKEN_LIFETIME = 60 * 60 # 1 hour
284
286
  DEFAULT_ZENML_SERVER_GENERIC_API_TOKEN_MAX_LIFETIME = (
285
287
  60 * 60 * 24 * 7
286
288
  ) # 7 days
289
+ DEFAULT_ZENML_SERVER_FILE_DOWNLOAD_SIZE_LIMIT = 2 * 1024 * 1024 * 1024 # 20 GB
287
290
 
288
291
  DEFAULT_ZENML_SERVER_SECURE_HEADERS_HSTS = (
289
292
  "max-age=63072000; includeSubdomains"
@@ -350,10 +353,12 @@ CODE_REPOSITORIES = "/code_repositories"
350
353
  COMPONENT_TYPES = "/component-types"
351
354
  CONFIG = "/config"
352
355
  CURRENT_USER = "/current-user"
356
+ DATA = "/data"
353
357
  DEACTIVATE = "/deactivate"
354
358
  DEVICES = "/devices"
355
359
  DEVICE_AUTHORIZATION = "/device_authorization"
356
360
  DEVICE_VERIFY = "/verify"
361
+ DOWNLOAD_TOKEN = "/download-token"
357
362
  EMAIL_ANALYTICS = "/email-opt-in"
358
363
  EVENT_FLAVORS = "/event-flavors"
359
364
  EVENT_SOURCES = "/event-sources"
@@ -35,11 +35,23 @@ class KubernetesStepOperatorSettings(BaseSettings):
35
35
  pod_settings: Pod settings to apply to pods executing the steps.
36
36
  service_account_name: Name of the service account to use for the pod.
37
37
  privileged: If the container should be run in privileged mode.
38
+ pod_startup_timeout: The maximum time to wait for a pending step pod to
39
+ start (in seconds).
40
+ pod_failure_max_retries: The maximum number of times to retry a step
41
+ pod if the step Kubernetes pod fails to start
42
+ pod_failure_retry_delay: The delay in seconds between pod
43
+ failure retries and pod startup retries (in seconds)
44
+ pod_failure_backoff: The backoff factor for pod failure retries and
45
+ pod startup retries.
38
46
  """
39
47
 
40
48
  pod_settings: Optional[KubernetesPodSettings] = None
41
49
  service_account_name: Optional[str] = None
42
50
  privileged: bool = False
51
+ pod_startup_timeout: int = 60 * 10 # Default 10 minutes
52
+ pod_failure_max_retries: int = 3
53
+ pod_failure_retry_delay: int = 10
54
+ pod_failure_backoff: float = 1.0
43
55
 
44
56
 
45
57
  class KubernetesStepOperatorConfig(
@@ -462,3 +462,95 @@ def delete_secret(
462
462
  name=secret_name,
463
463
  namespace=namespace,
464
464
  )
465
+
466
+
467
+ def create_and_wait_for_pod_to_start(
468
+ core_api: k8s_client.CoreV1Api,
469
+ pod_display_name: str,
470
+ pod_name: str,
471
+ pod_manifest: k8s_client.V1Pod,
472
+ namespace: str,
473
+ startup_max_retries: int,
474
+ startup_failure_delay: float,
475
+ startup_failure_backoff: float,
476
+ startup_timeout: float,
477
+ ) -> None:
478
+ """Create a pod and wait for it to reach a desired state.
479
+
480
+ Args:
481
+ core_api: Client of Core V1 API of Kubernetes API.
482
+ pod_display_name: The display name of the pod to use in logs.
483
+ pod_name: The name of the pod to create.
484
+ pod_manifest: The manifest of the pod to create.
485
+ namespace: The namespace in which to create the pod.
486
+ startup_max_retries: The maximum number of retries for the pod startup.
487
+ startup_failure_delay: The delay between retries for the pod startup.
488
+ startup_failure_backoff: The backoff factor for the pod startup.
489
+ startup_timeout: The maximum time to wait for the pod to start.
490
+
491
+ Raises:
492
+ TimeoutError: If the pod is still in a pending state after the maximum
493
+ wait time has elapsed.
494
+ Exception: If the pod fails to start after the maximum number of
495
+ retries.
496
+ """
497
+ retries = 0
498
+
499
+ while retries < startup_max_retries:
500
+ try:
501
+ # Create and run pod.
502
+ core_api.create_namespaced_pod(
503
+ namespace=namespace,
504
+ body=pod_manifest,
505
+ )
506
+ break
507
+ except Exception as e:
508
+ retries += 1
509
+ if retries < startup_max_retries:
510
+ logger.debug(f"The {pod_display_name} failed to start: {e}")
511
+ logger.error(
512
+ f"Failed to create {pod_display_name}. "
513
+ f"Retrying in {startup_failure_delay} seconds..."
514
+ )
515
+ time.sleep(startup_failure_delay)
516
+ startup_failure_delay *= startup_failure_backoff
517
+ else:
518
+ logger.error(
519
+ f"Failed to create {pod_display_name} after "
520
+ f"{startup_max_retries} retries. Exiting."
521
+ )
522
+ raise
523
+
524
+ # Wait for pod to start
525
+ logger.info(f"Waiting for {pod_display_name} to start...")
526
+ max_wait = startup_timeout
527
+ total_wait: float = 0
528
+ delay = startup_failure_delay
529
+ while True:
530
+ pod = get_pod(
531
+ core_api=core_api,
532
+ pod_name=pod_name,
533
+ namespace=namespace,
534
+ )
535
+ if not pod or pod_is_not_pending(pod):
536
+ break
537
+ if total_wait >= max_wait:
538
+ # Have to delete the pending pod so it doesn't start running
539
+ # later on.
540
+ try:
541
+ core_api.delete_namespaced_pod(
542
+ name=pod_name,
543
+ namespace=namespace,
544
+ )
545
+ except Exception:
546
+ pass
547
+ raise TimeoutError(
548
+ f"The {pod_display_name} is still in a pending state "
549
+ f"after {total_wait} seconds. Exiting."
550
+ )
551
+
552
+ if total_wait + delay > max_wait:
553
+ delay = max_wait - total_wait
554
+ total_wait += delay
555
+ time.sleep(delay)
556
+ delay *= startup_failure_backoff
@@ -543,14 +543,24 @@ class KubernetesOrchestrator(ContainerizedOrchestrator):
543
543
  mount_local_stores=self.config.is_local,
544
544
  )
545
545
 
546
- self._k8s_core_api.create_namespaced_pod(
546
+ logger.info("Waiting for Kubernetes orchestrator pod to start...")
547
+ kube_utils.create_and_wait_for_pod_to_start(
548
+ core_api=self._k8s_core_api,
549
+ pod_display_name="Kubernetes orchestrator pod",
550
+ pod_name=pod_name,
551
+ pod_manifest=pod_manifest,
547
552
  namespace=self.config.kubernetes_namespace,
548
- body=pod_manifest,
553
+ startup_max_retries=settings.pod_failure_max_retries,
554
+ startup_failure_delay=settings.pod_failure_retry_delay,
555
+ startup_failure_backoff=settings.pod_failure_backoff,
556
+ startup_timeout=settings.pod_startup_timeout,
549
557
  )
550
558
 
551
559
  # Wait for the orchestrator pod to finish and stream logs.
552
560
  if settings.synchronous:
553
- logger.info("Waiting for Kubernetes orchestrator pod...")
561
+ logger.info(
562
+ "Waiting for Kubernetes orchestrator pod to finish..."
563
+ )
554
564
  kube_utils.wait_pod(
555
565
  kube_client_fn=self.get_kube_client,
556
566
  pod_name=pod_name,
@@ -15,7 +15,6 @@
15
15
 
16
16
  import argparse
17
17
  import socket
18
- import time
19
18
  from typing import Any, Dict
20
19
  from uuid import UUID
21
20
 
@@ -103,8 +102,6 @@ def main() -> None:
103
102
 
104
103
  Raises:
105
104
  Exception: If the pod fails to start.
106
- TimeoutError: If the pod is still in a pending state after the
107
- maximum wait time has elapsed.
108
105
  """
109
106
  # Define Kubernetes pod name.
110
107
  pod_name = f"{orchestrator_run_id}-{step_name}"
@@ -176,68 +173,18 @@ def main() -> None:
176
173
  mount_local_stores=mount_local_stores,
177
174
  )
178
175
 
179
- retries = 0
180
- max_retries = settings.pod_failure_max_retries
181
- delay: float = settings.pod_failure_retry_delay
182
- backoff = settings.pod_failure_backoff
183
-
184
- while retries < max_retries:
185
- try:
186
- # Create and run pod.
187
- core_api.create_namespaced_pod(
188
- namespace=args.kubernetes_namespace,
189
- body=pod_manifest,
190
- )
191
- break
192
- except Exception as e:
193
- retries += 1
194
- if retries < max_retries:
195
- logger.debug(
196
- f"Pod for step `{step_name}` failed to start: {e}"
197
- )
198
- logger.error(
199
- f"Failed to create pod for step `{step_name}`. "
200
- f"Retrying in {delay} seconds..."
201
- )
202
- time.sleep(delay)
203
- delay *= backoff
204
- else:
205
- logger.error(
206
- f"Failed to create pod for step `{step_name}` after "
207
- f"{max_retries} retries. Exiting."
208
- )
209
- raise
210
-
211
- # Wait for pod to start
212
- max_wait = settings.pod_startup_timeout
213
- total_wait: float = 0
214
- delay = settings.pod_failure_retry_delay
215
- while True:
216
- pod = kube_utils.get_pod(
217
- core_api, pod_name, args.kubernetes_namespace
218
- )
219
- if not pod or kube_utils.pod_is_not_pending(pod):
220
- break
221
- if total_wait >= max_wait:
222
- # Have to delete the pending pod so it doesn't start running
223
- # later on.
224
- try:
225
- core_api.delete_namespaced_pod(
226
- name=pod_name,
227
- namespace=args.kubernetes_namespace,
228
- )
229
- except Exception:
230
- pass
231
- raise TimeoutError(
232
- f"Pod for step `{step_name}` is still in a pending state "
233
- f"after {total_wait} seconds. Exiting."
234
- )
235
-
236
- if total_wait + delay > max_wait:
237
- delay = max_wait - total_wait
238
- total_wait += delay
239
- time.sleep(delay)
240
- delay *= backoff
176
+ logger.info(f"Waiting for pod of step `{step_name}` to start...")
177
+ kube_utils.create_and_wait_for_pod_to_start(
178
+ core_api=core_api,
179
+ pod_display_name=f"pod for step `{step_name}`",
180
+ pod_name=pod_name,
181
+ pod_manifest=pod_manifest,
182
+ namespace=args.kubernetes_namespace,
183
+ startup_max_retries=settings.pod_failure_max_retries,
184
+ startup_failure_delay=settings.pod_failure_retry_delay,
185
+ startup_failure_backoff=settings.pod_failure_backoff,
186
+ startup_timeout=settings.pod_startup_timeout,
187
+ )
241
188
 
242
189
  # Wait for pod to finish.
243
190
  logger.info(f"Waiting for pod of step `{step_name}` to finish...")
@@ -218,13 +218,24 @@ class KubernetesStepOperator(BaseStepOperator):
218
218
  mount_local_stores=False,
219
219
  )
220
220
 
221
- self._k8s_core_api.create_namespaced_pod(
221
+ logger.info(
222
+ "Waiting for pod of step `%s` to start...", info.pipeline_step_name
223
+ )
224
+ kube_utils.create_and_wait_for_pod_to_start(
225
+ core_api=self._k8s_core_api,
226
+ pod_display_name=f"pod of step `{info.pipeline_step_name}`",
227
+ pod_name=pod_name,
228
+ pod_manifest=pod_manifest,
222
229
  namespace=self.config.kubernetes_namespace,
223
- body=pod_manifest,
230
+ startup_max_retries=settings.pod_failure_max_retries,
231
+ startup_failure_delay=settings.pod_failure_retry_delay,
232
+ startup_failure_backoff=settings.pod_failure_backoff,
233
+ startup_timeout=settings.pod_startup_timeout,
224
234
  )
225
235
 
226
236
  logger.info(
227
- "Waiting for pod of step `%s` to start...", info.pipeline_step_name
237
+ "Waiting for pod of step `%s` to finish...",
238
+ info.pipeline_step_name,
228
239
  )
229
240
  kube_utils.wait_pod(
230
241
  kube_client_fn=self.get_kube_client,
@@ -26,6 +26,7 @@ from zenml.constants import (
26
26
  from zenml.enums import ArtifactType
27
27
  from zenml.io import fileio
28
28
  from zenml.materializers.base_materializer import BaseMaterializer
29
+ from zenml.utils.io_utils import is_path_within_directory
29
30
 
30
31
 
31
32
  class PathMaterializer(BaseMaterializer):
@@ -71,7 +72,15 @@ class PathMaterializer(BaseMaterializer):
71
72
 
72
73
  # Extract the archive to the temporary directory
73
74
  with tarfile.open(archive_path_local, "r:gz") as tar:
74
- tar.extractall(path=directory)
75
+ # Validate archive members to prevent path traversal attacks
76
+ # Filter members to only those with safe paths
77
+ safe_members = []
78
+ for member in tar.getmembers():
79
+ if is_path_within_directory(member.name, directory):
80
+ safe_members.append(member)
81
+
82
+ # Extract only safe members
83
+ tar.extractall(path=directory, members=safe_members) # nosec B202 - members are filtered through is_path_within_directory
75
84
 
76
85
  # Clean up the archive file
77
86
  os.remove(archive_path_local)
@@ -93,8 +102,14 @@ class PathMaterializer(BaseMaterializer):
93
102
 
94
103
  Args:
95
104
  data: Path to a local directory or file to store. Must be a Path object.
105
+
106
+ Raises:
107
+ TypeError: If data is not a Path object.
96
108
  """
97
- assert isinstance(data, Path)
109
+ if not isinstance(data, Path):
110
+ raise TypeError(
111
+ f"Expected a Path object, got {type(data).__name__}"
112
+ )
98
113
 
99
114
  if data.is_dir():
100
115
  # Handle directory artifact
zenml/utils/io_utils.py CHANGED
@@ -205,6 +205,29 @@ def resolve_relative_path(path: str) -> str:
205
205
  return str(Path(path).resolve())
206
206
 
207
207
 
208
+ def is_path_within_directory(path: str, directory: str) -> bool:
209
+ """Checks if a path is contained within a given directory.
210
+
211
+ This utility function verifies that a path (absolute or relative) resolves
212
+ to a location that is within the specified directory. This is useful for
213
+ security checks such as preventing path traversal attacks when extracting
214
+ archives (CVE-2007-4559) or whenever path containment needs to be verified.
215
+
216
+ Args:
217
+ path: The path to check (can be relative or absolute).
218
+ directory: The directory that should contain the path.
219
+
220
+ Returns:
221
+ Boolean indicating whether the path is contained within the directory (True)
222
+ or not (False).
223
+ """
224
+ # Convert to absolute path, ensuring it's normalized
225
+ abs_path = os.path.abspath(os.path.join(directory, path))
226
+ # Check if the path is within the target directory
227
+ dir_abs = os.path.abspath(directory)
228
+ return abs_path.startswith(dir_abs + os.sep) or abs_path == dir_abs
229
+
230
+
208
231
  def move(source: str, destination: str, overwrite: bool = False) -> None:
209
232
  """Moves dir or file from source to destination. Can be used to rename.
210
233
 
zenml/zen_server/auth.py CHANGED
@@ -983,6 +983,58 @@ def generate_access_token(
983
983
  )
984
984
 
985
985
 
986
+ def generate_artifact_download_token(artifact_version_id: UUID) -> str:
987
+ """Generate a JWT token for artifact download.
988
+
989
+ Args:
990
+ artifact_version_id: The ID of the artifact version to download.
991
+
992
+ Returns:
993
+ The JWT token for the artifact download.
994
+ """
995
+ import jwt
996
+
997
+ config = server_config()
998
+
999
+ return jwt.encode(
1000
+ {
1001
+ "exp": utc_now() + timedelta(seconds=30),
1002
+ "artifact_version_id": str(artifact_version_id),
1003
+ },
1004
+ key=config.jwt_secret_key,
1005
+ algorithm=config.jwt_token_algorithm,
1006
+ )
1007
+
1008
+
1009
+ def verify_artifact_download_token(
1010
+ token: str, artifact_version_id: UUID
1011
+ ) -> None:
1012
+ """Verify a JWT token for artifact download.
1013
+
1014
+ Args:
1015
+ token: The JWT token to verify.
1016
+ artifact_version_id: The ID of the artifact version to download.
1017
+
1018
+ Raises:
1019
+ CredentialsNotValid: If the token is invalid or the artifact version
1020
+ ID does not match.
1021
+ """
1022
+ import jwt
1023
+
1024
+ config = server_config()
1025
+ try:
1026
+ claims = jwt.decode(
1027
+ token,
1028
+ config.jwt_secret_key,
1029
+ algorithms=[config.jwt_token_algorithm],
1030
+ )
1031
+ except jwt.PyJWTError as e:
1032
+ raise CredentialsNotValid(f"Invalid JWT token: {e}") from e
1033
+
1034
+ if claims["artifact_version_id"] != str(artifact_version_id):
1035
+ raise CredentialsNotValid("Invalid artifact version ID")
1036
+
1037
+
986
1038
  def http_authentication(
987
1039
  credentials: HTTPBasicCredentials = Depends(HTTPBasic()),
988
1040
  ) -> AuthContext:
@@ -7,7 +7,10 @@ import requests
7
7
  from requests.adapters import HTTPAdapter, Retry
8
8
 
9
9
  from zenml.config.server_config import ServerProConfiguration
10
- from zenml.exceptions import SubscriptionUpgradeRequiredError
10
+ from zenml.exceptions import (
11
+ IllegalOperationError,
12
+ SubscriptionUpgradeRequiredError,
13
+ )
11
14
  from zenml.utils.time_utils import utc_now
12
15
  from zenml.zen_server.utils import get_zenml_headers, server_config
13
16
 
@@ -43,6 +46,7 @@ class ZenMLCloudConnection:
43
46
  Raises:
44
47
  SubscriptionUpgradeRequiredError: If the current subscription tier
45
48
  is insufficient for the attempted operation.
49
+ IllegalOperationError: If the request failed with a 403 status code.
46
50
  RuntimeError: If the request failed.
47
51
 
48
52
  Returns:
@@ -65,6 +69,8 @@ class ZenMLCloudConnection:
65
69
  except requests.HTTPError as e:
66
70
  if response.status_code == 402:
67
71
  raise SubscriptionUpgradeRequiredError(response.json())
72
+ elif response.status_code == 403:
73
+ raise IllegalOperationError(response.json())
68
74
  else:
69
75
  raise RuntimeError(
70
76
  f"Failed while trying to contact the central zenml pro "
@@ -0,0 +1,126 @@
1
+ # Copyright (c) ZenML GmbH 2025. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at:
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12
+ # or implied. See the License for the specific language governing
13
+ # permissions and limitations under the License.
14
+ """Utility functions for downloading artifacts."""
15
+
16
+ import os
17
+ import tarfile
18
+ import tempfile
19
+ from typing import (
20
+ TYPE_CHECKING,
21
+ Optional,
22
+ )
23
+
24
+ from zenml.artifacts.utils import _load_artifact_store
25
+ from zenml.exceptions import (
26
+ IllegalOperationError,
27
+ )
28
+ from zenml.models import (
29
+ ArtifactVersionResponse,
30
+ )
31
+ from zenml.zen_server.utils import server_config, zen_store
32
+
33
+ if TYPE_CHECKING:
34
+ from zenml.artifact_stores.base_artifact_store import BaseArtifactStore
35
+
36
+
37
+ def verify_artifact_is_downloadable(
38
+ artifact: "ArtifactVersionResponse",
39
+ ) -> "BaseArtifactStore":
40
+ """Verify that the given artifact is downloadable.
41
+
42
+ Args:
43
+ artifact: The artifact to verify.
44
+
45
+ Raises:
46
+ IllegalOperationError: If the artifact is too large to be archived.
47
+ KeyError: If the artifact store is not found or the artifact URI does
48
+ not exist.
49
+
50
+ Returns:
51
+ The artifact store.
52
+ """
53
+ if not artifact.artifact_store_id:
54
+ raise KeyError(
55
+ f"Artifact '{artifact.id}' cannot be downloaded because the "
56
+ "underlying artifact store was deleted."
57
+ )
58
+
59
+ artifact_store = _load_artifact_store(
60
+ artifact_store_id=artifact.artifact_store_id, zen_store=zen_store()
61
+ )
62
+
63
+ if not artifact_store.exists(artifact.uri):
64
+ raise KeyError(f"The artifact URI '{artifact.uri}' does not exist.")
65
+
66
+ size = artifact_store.size(artifact.uri)
67
+ max_download_size = server_config().file_download_size_limit
68
+
69
+ if size and size > max_download_size:
70
+ raise IllegalOperationError(
71
+ f"The artifact '{artifact.id}' is too large to be downloaded. "
72
+ f"The maximum download size allowed by your ZenML server is "
73
+ f"{max_download_size} bytes."
74
+ )
75
+
76
+ return artifact_store
77
+
78
+
79
+ def create_artifact_archive(
80
+ artifact: "ArtifactVersionResponse",
81
+ archive_path: Optional[str] = None,
82
+ ) -> str:
83
+ """Create an archive of the given artifact.
84
+
85
+ Args:
86
+ artifact: The artifact to archive.
87
+ archive_path: The path to which to save the archive.
88
+
89
+ Returns:
90
+ The path to the created archive.
91
+ """
92
+ if archive_path is None:
93
+ archive_path = tempfile.mktemp()
94
+
95
+ artifact_store = verify_artifact_is_downloadable(artifact)
96
+
97
+ def _prepare_tarinfo(path: str) -> tarfile.TarInfo:
98
+ archive_path = os.path.relpath(path, artifact.uri)
99
+ tarinfo = tarfile.TarInfo(name=archive_path)
100
+ if size := artifact_store.size(path):
101
+ tarinfo.size = size
102
+ return tarinfo
103
+
104
+ with tarfile.open(name=archive_path, mode="w:gz") as tar:
105
+ if artifact_store.isdir(artifact.uri):
106
+ for dir, _, files in artifact_store.walk(artifact.uri):
107
+ dir = dir.decode() if isinstance(dir, bytes) else dir
108
+ dir_info = tarfile.TarInfo(
109
+ name=os.path.relpath(dir, artifact.uri)
110
+ )
111
+ dir_info.type = tarfile.DIRTYPE
112
+ dir_info.mode = 0o755
113
+ tar.addfile(dir_info)
114
+
115
+ for file in files:
116
+ file = file.decode() if isinstance(file, bytes) else file
117
+ path = os.path.join(dir, file)
118
+ tarinfo = _prepare_tarinfo(path)
119
+ with artifact_store.open(path, "rb") as f:
120
+ tar.addfile(tarinfo, fileobj=f)
121
+ else:
122
+ tarinfo = _prepare_tarinfo(artifact.uri)
123
+ with artifact_store.open(artifact.uri, "rb") as f:
124
+ tar.addfile(tarinfo, fileobj=f)
125
+
126
+ return archive_path
@@ -14,7 +14,7 @@
14
14
  """RBAC interface definition."""
15
15
 
16
16
  from abc import ABC, abstractmethod
17
- from typing import TYPE_CHECKING, Dict, List, Set, Tuple
17
+ from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple
18
18
 
19
19
  from zenml.zen_server.rbac.models import Action, Resource
20
20
 
@@ -63,15 +63,22 @@ class RBACInterface(ABC):
63
63
 
64
64
  @abstractmethod
65
65
  def update_resource_membership(
66
- self, user: "UserResponse", resource: Resource, actions: List[Action]
66
+ self,
67
+ sharing_user: "UserResponse",
68
+ resource: Resource,
69
+ actions: List[Action],
70
+ user_id: Optional[str] = None,
71
+ team_id: Optional[str] = None,
67
72
  ) -> None:
68
73
  """Update the resource membership of a user.
69
74
 
70
75
  Args:
71
- user: User for which the resource membership should be updated.
76
+ sharing_user: User that is sharing the resource.
72
77
  resource: The resource.
73
78
  actions: The actions that the user should be able to perform on the
74
79
  resource.
80
+ user_id: ID of the user for which to update the membership.
81
+ team_id: ID of the team for which to update the membership.
75
82
  """
76
83
 
77
84
  @abstractmethod
@@ -688,21 +688,31 @@ def get_schema_for_resource_type(
688
688
 
689
689
 
690
690
  def update_resource_membership(
691
- user: UserResponse, resource: Resource, actions: List[Action]
691
+ sharing_user: "UserResponse",
692
+ resource: Resource,
693
+ actions: List[Action],
694
+ user_id: Optional[str] = None,
695
+ team_id: Optional[str] = None,
692
696
  ) -> None:
693
697
  """Update the resource membership of a user.
694
698
 
695
699
  Args:
696
- user: User for which the resource membership should be updated.
700
+ sharing_user: User that is sharing the resource.
697
701
  resource: The resource.
698
702
  actions: The actions that the user should be able to perform on the
699
703
  resource.
704
+ user_id: ID of the user for which to update the membership.
705
+ team_id: ID of the team for which to update the membership.
700
706
  """
701
707
  if not server_config().rbac_enabled:
702
708
  return
703
709
 
704
710
  rbac().update_resource_membership(
705
- user=user, resource=resource, actions=actions
711
+ sharing_user=sharing_user,
712
+ resource=resource,
713
+ actions=actions,
714
+ user_id=user_id,
715
+ team_id=team_id,
706
716
  )
707
717
 
708
718
 
@@ -13,7 +13,7 @@
13
13
  # permissions and limitations under the License.
14
14
  """Cloud RBAC implementation."""
15
15
 
16
- from typing import TYPE_CHECKING, Dict, List, Set, Tuple
16
+ from typing import TYPE_CHECKING, Dict, List, Optional, Set, Tuple
17
17
 
18
18
  from zenml.zen_server.cloud_utils import cloud_connection
19
19
  from zenml.zen_server.rbac.models import Action, Resource
@@ -117,22 +117,28 @@ class ZenMLCloudRBAC(RBACInterface):
117
117
  return full_resource_access, allowed_ids
118
118
 
119
119
  def update_resource_membership(
120
- self, user: "UserResponse", resource: Resource, actions: List[Action]
120
+ self,
121
+ sharing_user: "UserResponse",
122
+ resource: Resource,
123
+ actions: List[Action],
124
+ user_id: Optional[str] = None,
125
+ team_id: Optional[str] = None,
121
126
  ) -> None:
122
127
  """Update the resource membership of a user.
123
128
 
124
129
  Args:
125
- user: User for which the resource membership should be updated.
130
+ sharing_user: User that is sharing the resource.
126
131
  resource: The resource.
127
132
  actions: The actions that the user should be able to perform on the
128
133
  resource.
134
+ user_id: ID of the user for which to update the membership.
135
+ team_id: ID of the team for which to update the membership.
129
136
  """
130
- if user.is_service_account:
131
- # Service accounts have full permissions for now
132
- return
133
-
137
+ assert sharing_user.external_user_id
134
138
  data = {
135
- "user_id": str(user.external_user_id),
139
+ "user_id": user_id,
140
+ "team_id": team_id,
141
+ "sharing_user_id": str(sharing_user.external_user_id),
136
142
  "resource": str(resource),
137
143
  "actions": [str(action) for action in actions],
138
144
  }
@@ -13,13 +13,26 @@
13
13
  # permissions and limitations under the License.
14
14
  """Endpoint definitions for artifact versions."""
15
15
 
16
+ import os
16
17
  from typing import List, Union
17
18
  from uuid import UUID
18
19
 
19
20
  from fastapi import APIRouter, Depends, Security
21
+ from fastapi.responses import FileResponse
22
+ from starlette.background import BackgroundTask
20
23
 
21
- from zenml.artifacts.utils import load_artifact_visualization
22
- from zenml.constants import API, ARTIFACT_VERSIONS, BATCH, VERSION_1, VISUALIZE
24
+ from zenml.artifacts.utils import (
25
+ load_artifact_visualization,
26
+ )
27
+ from zenml.constants import (
28
+ API,
29
+ ARTIFACT_VERSIONS,
30
+ BATCH,
31
+ DATA,
32
+ DOWNLOAD_TOKEN,
33
+ VERSION_1,
34
+ VISUALIZE,
35
+ )
23
36
  from zenml.models import (
24
37
  ArtifactVersionFilter,
25
38
  ArtifactVersionRequest,
@@ -28,7 +41,16 @@ from zenml.models import (
28
41
  LoadedVisualization,
29
42
  Page,
30
43
  )
31
- from zenml.zen_server.auth import AuthContext, authorize
44
+ from zenml.zen_server.auth import (
45
+ AuthContext,
46
+ authorize,
47
+ generate_artifact_download_token,
48
+ verify_artifact_download_token,
49
+ )
50
+ from zenml.zen_server.download_utils import (
51
+ create_artifact_archive,
52
+ verify_artifact_is_downloadable,
53
+ )
32
54
  from zenml.zen_server.exceptions import error_response
33
55
  from zenml.zen_server.rbac.endpoint_utils import (
34
56
  verify_permissions_and_batch_create_entity,
@@ -275,3 +297,64 @@ def get_artifact_visualization(
275
297
  return load_artifact_visualization(
276
298
  artifact=artifact, index=index, zen_store=store, encode_image=True
277
299
  )
300
+
301
+
302
+ @artifact_version_router.get(
303
+ "/{artifact_version_id}" + DOWNLOAD_TOKEN,
304
+ responses={401: error_response, 404: error_response, 422: error_response},
305
+ )
306
+ @handle_exceptions
307
+ def get_artifact_download_token(
308
+ artifact_version_id: UUID,
309
+ _: AuthContext = Security(authorize),
310
+ ) -> str:
311
+ """Get a download token for the artifact data.
312
+
313
+ Args:
314
+ artifact_version_id: ID of the artifact version for which to get the data.
315
+
316
+ Returns:
317
+ The download token for the artifact data.
318
+ """
319
+ artifact = verify_permissions_and_get_entity(
320
+ id=artifact_version_id, get_method=zen_store().get_artifact_version
321
+ )
322
+ verify_artifact_is_downloadable(artifact)
323
+
324
+ # The artifact download is handled in a separate tab by the browser. In this
325
+ # tab, we do not have the ability to set any headers and therefore cannot
326
+ # include the CSRF token in the request. To handle this, we instead generate
327
+ # a JWT token in this endpoint (which includes CSRF and RBAC checks) and
328
+ # then use that token to download the artifact data in a separate endpoint
329
+ # which only verifies this short-lived token.
330
+ return generate_artifact_download_token(artifact_version_id)
331
+
332
+
333
+ @artifact_version_router.get(
334
+ "/{artifact_version_id}" + DATA,
335
+ responses={401: error_response, 404: error_response, 422: error_response},
336
+ )
337
+ @handle_exceptions
338
+ def download_artifact_data(
339
+ artifact_version_id: UUID, token: str
340
+ ) -> FileResponse:
341
+ """Download the artifact data.
342
+
343
+ Args:
344
+ artifact_version_id: ID of the artifact version for which to get the data.
345
+ token: The token to authenticate the artifact download.
346
+
347
+ Returns:
348
+ The artifact data.
349
+ """
350
+ verify_artifact_download_token(token, artifact_version_id)
351
+
352
+ artifact = zen_store().get_artifact_version(artifact_version_id)
353
+ archive_path = create_artifact_archive(artifact)
354
+
355
+ return FileResponse(
356
+ archive_path,
357
+ media_type="application/gzip",
358
+ filename=f"{artifact.name}-{artifact.version}.tar.gz",
359
+ background=BackgroundTask(os.remove, archive_path),
360
+ )
@@ -698,7 +698,7 @@ if server_config().auth_scheme != AuthScheme.EXTERNAL:
698
698
  if server_config().rbac_enabled:
699
699
 
700
700
  @router.post(
701
- "/{user_name_or_id}/resource_membership",
701
+ "/resource_membership",
702
702
  responses={
703
703
  401: error_response,
704
704
  404: error_response,
@@ -707,16 +707,16 @@ if server_config().rbac_enabled:
707
707
  )
708
708
  @handle_exceptions
709
709
  def update_user_resource_membership(
710
- user_name_or_id: Union[str, UUID],
711
710
  resource_type: str,
712
711
  resource_id: UUID,
713
712
  actions: List[str],
713
+ user_id: Optional[str] = None,
714
+ team_id: Optional[str] = None,
714
715
  auth_context: AuthContext = Security(authorize),
715
716
  ) -> None:
716
717
  """Updates resource memberships of a user.
717
718
 
718
719
  Args:
719
- user_name_or_id: Name or ID of the user.
720
720
  resource_type: Type of the resource for which to update the
721
721
  membership.
722
722
  resource_id: ID of the resource for which to update the membership.
@@ -724,16 +724,19 @@ if server_config().rbac_enabled:
724
724
  the resource. If the user currently has permissions to perform
725
725
  actions which are not passed in this list, the permissions will
726
726
  be removed.
727
+ user_id: ID of the user for which to update the membership.
728
+ team_id: ID of the team for which to update the membership.
727
729
  auth_context: Authentication context.
728
730
 
729
731
  Raises:
730
732
  ValueError: If a user tries to update their own membership.
731
733
  KeyError: If no resource with the given type and ID exists.
732
734
  """
733
- user = zen_store().get_user(user_name_or_id)
734
- # verify_permission_for_model(user, action=Action.READ)
735
-
736
- if user.id == auth_context.user.id:
735
+ if (
736
+ user_id
737
+ and auth_context.user.external_user_id
738
+ and user_id == str(auth_context.user.external_user_id)
739
+ ):
737
740
  raise ValueError(
738
741
  "Not allowed to call endpoint with the authenticated user."
739
742
  )
@@ -765,7 +768,9 @@ if server_config().rbac_enabled:
765
768
  verify_permission_for_model(model=model, action=Action(action))
766
769
 
767
770
  update_resource_membership(
768
- user=user,
771
+ sharing_user=auth_context.user,
769
772
  resource=resource,
770
773
  actions=[Action(action) for action in actions],
774
+ user_id=user_id,
775
+ team_id=team_id,
771
776
  )
@@ -411,14 +411,14 @@ def deployment_request_from_template(
411
411
  if unknown_parameters:
412
412
  raise ValueError(
413
413
  "Run configuration contains the following unknown "
414
- f"parameters for step {step.config.name}: {unknown_parameters}."
414
+ f"parameters for step {invocation_id}: {unknown_parameters}."
415
415
  )
416
416
 
417
417
  missing_parameters = required_parameters - configured_parameters
418
418
  if missing_parameters:
419
419
  raise ValueError(
420
420
  "Run configuration is missing the following required "
421
- f"parameters for step {step.config.name}: {missing_parameters}."
421
+ f"parameters for step {invocation_id}: {missing_parameters}."
422
422
  )
423
423
 
424
424
  step_config = StepConfiguration.model_validate(step_config_dict)
@@ -0,0 +1,123 @@
1
+ """Migrate onboarding state [ff538a321a92].
2
+
3
+ Revision ID: ff538a321a92
4
+ Revises: 0.80.2
5
+ Create Date: 2025-04-11 09:30:03.324310
6
+
7
+ """
8
+
9
+ import json
10
+
11
+ import sqlalchemy as sa
12
+ from alembic import op
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision = "ff538a321a92"
16
+ down_revision = "0.80.2"
17
+ branch_labels = None
18
+ depends_on = None
19
+
20
+
21
+ def upgrade() -> None:
22
+ """Upgrade database schema and/or data, creating a new revision."""
23
+ with op.batch_alter_table("server_settings", schema=None) as batch_op:
24
+ batch_op.alter_column(
25
+ "onboarding_state",
26
+ existing_type=sa.VARCHAR(),
27
+ type_=sa.TEXT(),
28
+ existing_nullable=True,
29
+ )
30
+
31
+ connection = op.get_bind()
32
+
33
+ meta = sa.MetaData()
34
+ meta.reflect(only=("server_settings",), bind=connection)
35
+
36
+ server_settings_table = sa.Table("server_settings", meta)
37
+
38
+ existing_onboarding_state = connection.execute(
39
+ sa.select(server_settings_table.c.onboarding_state)
40
+ ).scalar_one_or_none()
41
+
42
+ if not existing_onboarding_state:
43
+ return
44
+
45
+ state = json.loads(existing_onboarding_state)
46
+
47
+ meta = sa.MetaData()
48
+ meta.reflect(
49
+ only=(
50
+ "pipeline_run",
51
+ "stack_component",
52
+ "stack",
53
+ "stack_composition",
54
+ "pipeline_deployment",
55
+ ),
56
+ bind=connection,
57
+ )
58
+
59
+ pipeline_run_table = sa.Table("pipeline_run", meta)
60
+ stack_component_table = sa.Table("stack_component", meta)
61
+ stack_table = sa.Table("stack", meta)
62
+ stack_composition_table = sa.Table("stack_composition", meta)
63
+ pipeline_deployment_table = sa.Table("pipeline_deployment", meta)
64
+
65
+ stack_with_remote_artifact_store_count = connection.execute(
66
+ sa.select(sa.func.count(stack_table.c.id))
67
+ .where(stack_composition_table.c.stack_id == stack_table.c.id)
68
+ .where(
69
+ stack_composition_table.c.component_id
70
+ == stack_component_table.c.id
71
+ )
72
+ .where(stack_component_table.c.flavor != "local")
73
+ .where(stack_component_table.c.type == "artifact_store")
74
+ ).scalar()
75
+ if (
76
+ stack_with_remote_artifact_store_count
77
+ and stack_with_remote_artifact_store_count > 0
78
+ ):
79
+ state.append("stack_with_remote_artifact_store_created")
80
+
81
+ pipeline_run_with_remote_artifact_store_count = connection.execute(
82
+ sa.select(sa.func.count(pipeline_run_table.c.id))
83
+ .where(
84
+ pipeline_run_table.c.deployment_id
85
+ == pipeline_deployment_table.c.id
86
+ )
87
+ .where(pipeline_deployment_table.c.stack_id == stack_table.c.id)
88
+ .where(stack_composition_table.c.stack_id == stack_table.c.id)
89
+ .where(
90
+ stack_composition_table.c.component_id
91
+ == stack_component_table.c.id
92
+ )
93
+ .where(stack_component_table.c.flavor != "local")
94
+ .where(stack_component_table.c.type == "artifact_store")
95
+ ).scalar()
96
+ if (
97
+ pipeline_run_with_remote_artifact_store_count
98
+ and pipeline_run_with_remote_artifact_store_count > 0
99
+ ):
100
+ state.append("pipeline_run_with_remote_artifact_store")
101
+ state.append("production_setup_completed")
102
+
103
+ # Remove duplicate keys
104
+ state = list(set(state))
105
+
106
+ connection.execute(
107
+ sa.update(server_settings_table).values(
108
+ onboarding_state=json.dumps(state)
109
+ )
110
+ )
111
+
112
+
113
+ def downgrade() -> None:
114
+ """Downgrade database schema and/or data back to the previous revision."""
115
+ # ### commands auto generated by Alembic - please adjust! ###
116
+ with op.batch_alter_table("server_settings", schema=None) as batch_op:
117
+ batch_op.alter_column(
118
+ "onboarding_state",
119
+ existing_type=sa.TEXT(),
120
+ type_=sa.VARCHAR(),
121
+ existing_nullable=True,
122
+ )
123
+ # ### end Alembic commands ###
@@ -18,6 +18,7 @@ from datetime import datetime
18
18
  from typing import Any, Optional, Set
19
19
  from uuid import UUID
20
20
 
21
+ from sqlalchemy import TEXT, Column
21
22
  from sqlmodel import Field, SQLModel
22
23
 
23
24
  from zenml.models import (
@@ -42,7 +43,9 @@ class ServerSettingsSchema(SQLModel, table=True):
42
43
  enable_analytics: bool = Field(default=False)
43
44
  display_announcements: Optional[bool] = Field(nullable=True)
44
45
  display_updates: Optional[bool] = Field(nullable=True)
45
- onboarding_state: Optional[str] = Field(nullable=True)
46
+ onboarding_state: Optional[str] = Field(
47
+ sa_column=Column(TEXT, nullable=True)
48
+ )
46
49
  last_user_activity: datetime = Field(default_factory=utc_now)
47
50
  updated: datetime = Field(default_factory=utc_now)
48
51
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: zenml-nightly
3
- Version: 0.80.2.dev20250414
3
+ Version: 0.80.2.dev20250415
4
4
  Summary: ZenML: Write production-ready ML code.
5
5
  License: Apache-2.0
6
6
  Keywords: machine learning,production,pipeline,mlops,devops
@@ -1,5 +1,5 @@
1
1
  zenml/README.md,sha256=827dekbOWAs1BpW7VF1a4d7EbwPbjwccX-2zdXBENZo,1777
2
- zenml/VERSION,sha256=-znhXr-geHTEJvYv6JS7hVfvU3lMKgRzfVE1e7Sn5v4,19
2
+ zenml/VERSION,sha256=aDZfR_uYsDKOeb5E9YyMbuM_1askN25eU7qAjXmYltU,19
3
3
  zenml/__init__.py,sha256=CKEyepFK-7akXYiMrNVh92Nb01Cjs23w4_YyI6sgdc8,2242
4
4
  zenml/actions/__init__.py,sha256=mrt6wPo73iKRxK754_NqsGyJ3buW7RnVeIGXr1xEw8Y,681
5
5
  zenml/actions/base_action.py,sha256=UcaHev6BTuLDwuswnyaPjdA8AgUqB5xPZ-lRtuvf2FU,25553
@@ -25,7 +25,7 @@ zenml/artifacts/external_artifact.py,sha256=7nLANV0vsGC36H1s_B_awX4hnZgXHCGIscQ2
25
25
  zenml/artifacts/external_artifact_config.py,sha256=P172p0JOu8Xx1F8RCQut1dnRpt5lpWXGNqMbY-V90sI,3323
26
26
  zenml/artifacts/preexisting_data_materializer.py,sha256=dcahDcHUD3Lvn0-6zE2BG84bkyo_ydAgzBWxtbyJJZQ,3325
27
27
  zenml/artifacts/unmaterialized_artifact.py,sha256=JNPKq_sNifQx5wP8jEw7TGBEi26zwKirPGlWX9uxbJI,1300
28
- zenml/artifacts/utils.py,sha256=yAtKLzbVrd9de82oVL3utifpk0ixz14RvPnUeh1QsMc,35233
28
+ zenml/artifacts/utils.py,sha256=G7V0L3Pii1b3eKznhCfGt7CJzBuhHoYwQr8Jx5VSLAI,35255
29
29
  zenml/cli/__init__.py,sha256=nxq4ifwLV5qT7Qghb42h02XUOcOihBkZp2xBqgiykM8,75670
30
30
  zenml/cli/annotator.py,sha256=JRR7_TJOWKyiKGv1kwSjG1Ay6RBWPVgm0X-D0uSBlyE,6976
31
31
  zenml/cli/artifact.py,sha256=7lsAS52DroBTFkFWxkyb-lIDOGP5jPL_Se_RDG_2jgg,9564
@@ -77,7 +77,7 @@ zenml/config/retry_config.py,sha256=4UH1xqw0G6fSEbXSNKfmiFEkwadxQef9BGMe3JBm6NI,
77
77
  zenml/config/schedule.py,sha256=qtMWa-mEo7jIKvDzQUstMwe57gdbvyWAQ7ggsoddbCA,5349
78
78
  zenml/config/secret_reference_mixin.py,sha256=YvY68MTd1gE23IVprf0BLkNn62hoxcvb5nqGgc8jMkU,5871
79
79
  zenml/config/secrets_store_config.py,sha256=y05zqyQhr_DGrs3IfBGa_FRoZ043hSYRT5wzrx-zHTU,2818
80
- zenml/config/server_config.py,sha256=x95kLkAMVb06NrF-Zu3PtNz96RvF1arhtcN7hiKDVOA,31186
80
+ zenml/config/server_config.py,sha256=Kns2ul12Zt4Y4ByKDJ4tgBrmUMvSrnxrc3bXRr5iQnw,31487
81
81
  zenml/config/settings_resolver.py,sha256=PR9BRm_x1dy7nVKa9UqpeFdck8IEATSW6aWT8FKd-DI,4278
82
82
  zenml/config/source.py,sha256=RzUw8lin8QztUjz-AdoCzVM5Om_cSSPuroaPx-qAO4w,8226
83
83
  zenml/config/step_configurations.py,sha256=mngjobhHRj88f3klMdz6iw2mOj9wzYUPIV8Rp_2hV3g,10433
@@ -85,7 +85,7 @@ zenml/config/step_run_info.py,sha256=KiVRSTtKmZ1GbvseDTap2imr7XwMHD3jSFVpyLNEK1I
85
85
  zenml/config/store_config.py,sha256=Cla5p5dTB6nNlo8_OZDs9hod5hspi64vxwtZj882XgU,3559
86
86
  zenml/config/strict_base_model.py,sha256=iHnO9qOmLUP_eiy9IjRr3JjIs1l1I_CsRQ76EyAneYU,860
87
87
  zenml/console.py,sha256=hj_KerPQKwnyKACj0ehSqUQX0mGVCJBKE1QvCt6ik3A,1160
88
- zenml/constants.py,sha256=998CPa9_XWM00TVjyAVADnLuBYc8EgI0MVQE2URyVvg,16068
88
+ zenml/constants.py,sha256=8_tNwBZkajdKjE9jttomUSLuQCsFGNTuYIsA1M7AkCA,16200
89
89
  zenml/container_registries/__init__.py,sha256=ZSPbBIOnzhg88kQSpYgKe_POLuru14m629665-kAVAA,2200
90
90
  zenml/container_registries/azure_container_registry.py,sha256=t1sfDa94Vzbyqtb1iPFNutJ2EXV5_p9CUNITasoiQ70,2667
91
91
  zenml/container_registries/base_container_registry.py,sha256=6c2e32wuqxYHJXm5OV2LY1MtX9yopB7WZtes9fmTAz0,7625
@@ -335,11 +335,11 @@ zenml/integrations/kubeflow/orchestrators/local_deployment_utils.py,sha256=qszoO
335
335
  zenml/integrations/kubernetes/__init__.py,sha256=k1bfrdI1u5RBnAh7yT4w-m-4SWgZ7b4L5uu7dPRc0SI,1809
336
336
  zenml/integrations/kubernetes/flavors/__init__.py,sha256=a5gU45qCj3FkLwl_uVjlIkL_2F5DHk-w1gdcZrvVjBI,1266
337
337
  zenml/integrations/kubernetes/flavors/kubernetes_orchestrator_flavor.py,sha256=VOfb5yOXGpQ8gcKP0nt4bYO48LqG8ZjzGhpw8XGXAzk,9253
338
- zenml/integrations/kubernetes/flavors/kubernetes_step_operator_flavor.py,sha256=ILN-H4cl7z3i4ltb4UBs55wbtIo871b4ib28pYkQoyQ,5605
338
+ zenml/integrations/kubernetes/flavors/kubernetes_step_operator_flavor.py,sha256=xFO7cSusji-mgbRrt4mU29gdyC9iEjEHKtomdFLp9mM,6265
339
339
  zenml/integrations/kubernetes/orchestrators/__init__.py,sha256=TJID3OTieZBox36WpQpzD0jdVRA_aZVcs_bNtfXS8ik,811
340
- zenml/integrations/kubernetes/orchestrators/kube_utils.py,sha256=MRLfLETrM4dTtxl4a3tU5FcIso6Gz2V5R0QWbCKl3GA,14860
341
- zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py,sha256=DaiJDiZlsypvXwBzzaq4CsHOyoHNiF1TM9sF-TR3ViI,25041
342
- zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint.py,sha256=CFB4TRZHizbvnXTdR2Xzu-j9DwCUOWK7BUJAlorrmG8,13450
340
+ zenml/integrations/kubernetes/orchestrators/kube_utils.py,sha256=oU0EYP-x35oG7PAy7NZKeFA8_89Eckgq6hibwc9v5l0,18108
341
+ zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py,sha256=v4LY2v81cWX9z-fwbSctjR1yZi8vkUckjj7TIrAqxps,25597
342
+ zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint.py,sha256=Wm_qw9oeCrJXAabTVmhpJeDWG2Jcmc2PZ8lZW68ScdI,11573
343
343
  zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint_configuration.py,sha256=KjHfQK9VQEQkkkM2i9w51AzqolgIU01M5dgb2YGamvY,2754
344
344
  zenml/integrations/kubernetes/orchestrators/manifest_utils.py,sha256=Owyuz5Iix-QvCnKObC6xhcuQtNG_ik-8Vbdmk13eWfc,12557
345
345
  zenml/integrations/kubernetes/pod_settings.py,sha256=fP2NeHf1XxT__D4g_6oHQf8pkiiamFUFYmfNor2OoP8,6386
@@ -347,7 +347,7 @@ zenml/integrations/kubernetes/serialization_utils.py,sha256=cPSe4szdBLzDnUZT9nQc
347
347
  zenml/integrations/kubernetes/service_connectors/__init__.py,sha256=Uf6zlHIapYrRDl3xOPWQ2jA7jt85SXx1U7DmSxzxTvQ,818
348
348
  zenml/integrations/kubernetes/service_connectors/kubernetes_service_connector.py,sha256=Cv4tiVxoQOz9ex0lf3JdJrooEkgMwfDfwt5GOeNRpQU,19669
349
349
  zenml/integrations/kubernetes/step_operators/__init__.py,sha256=40utDPYAezxHsFgO0UUIT_6XpCDzDapje6OH951XsTs,806
350
- zenml/integrations/kubernetes/step_operators/kubernetes_step_operator.py,sha256=3d3_gkn1LeAwiZ3XZlhKSd3Cqs3LOL38DNpTGx9qu30,8320
350
+ zenml/integrations/kubernetes/step_operators/kubernetes_step_operator.py,sha256=m8bSHskP-wS77tt1DEBoKt11pV4L6OtN0QN1AZ5Tp1g,8859
351
351
  zenml/integrations/label_studio/__init__.py,sha256=sF2c9FxTDRlbcu95OxaUNKNtIhC1LgfmBRKY4jBME38,1475
352
352
  zenml/integrations/label_studio/annotators/__init__.py,sha256=YtOtSfS1_NBoLoXIygEerElBP1-B98UU0HOAEfzdRY0,821
353
353
  zenml/integrations/label_studio/annotators/label_studio_annotator.py,sha256=VkuW4zsZhHz8__P9WTTLRTF-FOmoYB-_cFqBdu-PUyA,30498
@@ -601,7 +601,7 @@ zenml/materializers/cloudpickle_materializer.py,sha256=x8a6jEMTky6N2YVHiwrnGWSfV
601
601
  zenml/materializers/materializer_registry.py,sha256=ic-aWhJ2Ex9F_rml2dDVAxhRfW3nd71QMxzfTPP6BIM,4002
602
602
  zenml/materializers/numpy_materializer.py,sha256=OLcHF9Z0tAqQ_U8TraA0vGmZjHoT7eT_XevncIutt0M,1715
603
603
  zenml/materializers/pandas_materializer.py,sha256=c4B-ly04504gysA66iCYcmEdeh0ClePRTxRCkmHqIgE,1725
604
- zenml/materializers/path_materializer.py,sha256=L6XwjRXz9Sgiy8t7PhOKiG3l3ILMeLT2reB-yWZZqb0,4669
604
+ zenml/materializers/path_materializer.py,sha256=kY37mVwlfQndp3M7--g81dRt2qT81MJ1aURnl4uX29I,5422
605
605
  zenml/materializers/pydantic_materializer.py,sha256=eDp3eOR-X7FOHlpwALOJtVUtJti75Dsa7r0lzSjeQ-Q,2271
606
606
  zenml/materializers/service_materializer.py,sha256=OV5HFUuIc8UF8vE5y_FTWn4_mpCz0AtJPnNA2YFAjZE,2992
607
607
  zenml/materializers/structured_string_materializer.py,sha256=8LIvSH3JQn_QRAWGXK5_NhPQ4NgCt5zzz6f8GnGG9q4,4442
@@ -776,7 +776,7 @@ zenml/utils/filesync_model.py,sha256=9ibsIr2HJfkEQ41upQd4uJ79ZhzB0MHS85GOGOAQ19Y
776
776
  zenml/utils/function_utils.py,sha256=bznRrCrQIJ3Joc-QQ8ynDyn9AvyIcR61bzLnaXQV3tc,8109
777
777
  zenml/utils/git_utils.py,sha256=O2f6PUpDuMQrcmkcGXIhbJQpTzLM7p8PyiKWjcIuWqM,1831
778
778
  zenml/utils/integration_utils.py,sha256=jQVTnOjB9AQUerU7UiIXtDfcHnUrkMJDJKx5JZaPTAQ,1186
779
- zenml/utils/io_utils.py,sha256=fogsvnCODy-aAia7GLZiKL_Ts9J_KJEXzSKN_ukcmXo,6728
779
+ zenml/utils/io_utils.py,sha256=S-5dpeOCgEMV7R5y2gZxuWdAwRBunx9sBZi_WwY13Hw,7728
780
780
  zenml/utils/json_utils.py,sha256=mzZnzq5TAjZQLRPOHjRqAUTHqMfw5-lNptWSvrBICic,3668
781
781
  zenml/utils/materializer_utils.py,sha256=DNlO113FXjZ0Y67KpvDB5NiWen1tRZ0E-A_vTjE-BME,1815
782
782
  zenml/utils/metadata_utils.py,sha256=aLDLW25RKzS_uQCjfMKpqbUi4YRd85ioCPfRKsgnADc,11723
@@ -803,9 +803,9 @@ zenml/utils/uuid_utils.py,sha256=aOGQ2SdREexcVQICPU2jUAgjvAJxTmh4ESdM52PEhck,204
803
803
  zenml/utils/visualization_utils.py,sha256=ZGAnmE4s_rBSyz2FBu9FOIu054blfXuxLEz8wWqv-3s,4739
804
804
  zenml/utils/yaml_utils.py,sha256=747M_BTf3lcHFgJDF8RJxnnGIbCU8e9YxBBMmoQ5O_U,5834
805
805
  zenml/zen_server/__init__.py,sha256=WyltI9TzFW2mEHZVOs6alLWMCQrrZaFALtrQXs83STA,1355
806
- zenml/zen_server/auth.py,sha256=xr9OI0AMHo7Y7nIBO7Tg_xj0GvW8wT-XYJ1Kd2iLj78,39249
806
+ zenml/zen_server/auth.py,sha256=DxDEifWvV-mGWgyavTv6In0rypreh0qmxRLXsRLQ5oI,40646
807
807
  zenml/zen_server/cache.py,sha256=Tc4TSugmsU1bhThxlYfE8rv0KmltIX1CcVHgzrJ0Eus,6633
808
- zenml/zen_server/cloud_utils.py,sha256=2gSGSf-0zKX9m2sWCuRCbXfWgr-mjnjnrS8B-EM3JSc,9083
808
+ zenml/zen_server/cloud_utils.py,sha256=nHCJjO4jRaiZiVIMbN339xG5_uLKzkRPk-FoId0hhx8,9307
809
809
  zenml/zen_server/csrf.py,sha256=Jsbi_IKriWCOuquSYTOEWqSXiGORJATIhR5Wrkm4XzQ,2684
810
810
  zenml/zen_server/dashboard/assets/404-CYPi9d8E.js,sha256=2zavfhLSXLfKcj5vrItcAWat8SxQ2iGByKTuA5KOQE8,1033
811
811
  zenml/zen_server/dashboard/assets/@radix-C7hRs6Kx.js,sha256=cNA9UX8LGrKUQubGrls3E3Wq1fCrlK51W14yh22FE_U,296700
@@ -1010,6 +1010,7 @@ zenml/zen_server/deploy/docker/__init__.py,sha256=lNGI-Pl3PVMqR4BVajZXwe3moVNXPx
1010
1010
  zenml/zen_server/deploy/docker/docker_provider.py,sha256=18pNpxvP8xqbxS_KxvYTEIuad8Ky5mVtk5TaKVVrzcY,8371
1011
1011
  zenml/zen_server/deploy/docker/docker_zen_server.py,sha256=83-bjRmtlE-WDCavbvL9uTXOn5fMJ4yPDDb2ewSr4Do,7480
1012
1012
  zenml/zen_server/deploy/exceptions.py,sha256=tX0GNnLB_GMkeN7zGNlJRwtlrpZ5Slvyj_unVYVmGxk,1396
1013
+ zenml/zen_server/download_utils.py,sha256=zh6Hrs1E3BbzVwyvTpZyOLrZfkqCAxLm8wDcDpXFEIE,4203
1013
1014
  zenml/zen_server/exceptions.py,sha256=H4F9SbbwCiVLnJTdzpWN016Knt6zvoOVmOWxTgXEAsA,9686
1014
1015
  zenml/zen_server/feature_gate/__init__.py,sha256=yabe4fBY6NSusn-QlKQDLOvXVLERNpcAQgigsyWQIbQ,612
1015
1016
  zenml/zen_server/feature_gate/endpoint_utils.py,sha256=o6sBVlqqlc9KokMaEsRTYeMra7f2a6kCt3FrB-oHhCw,2227
@@ -1020,14 +1021,14 @@ zenml/zen_server/rate_limit.py,sha256=rOg5H_6rOGQ_qiSCtMKPREmL1LL3bZyn4czKILtImJ
1020
1021
  zenml/zen_server/rbac/__init__.py,sha256=nACbn_G7nZt_AWM3zeFL0FCmELvQnvaOFMwvTG3-6ZE,637
1021
1022
  zenml/zen_server/rbac/endpoint_utils.py,sha256=l6V6DwsPGmT6dLJmY4MHMRfRXdc0lZVibD-Aea5UAHc,11604
1022
1023
  zenml/zen_server/rbac/models.py,sha256=Xhz0XqmVqg3ji9uTwjlhQ4IuQ2ivrT4gVdHi5ZkjFC4,4933
1023
- zenml/zen_server/rbac/rbac_interface.py,sha256=APsP5qd8iT3pg6lvvuDDyWkifnXHSg2sbgNfuV8wlg0,3181
1024
+ zenml/zen_server/rbac/rbac_interface.py,sha256=VPMNnUIPcqJWDngITX6cfaPlM6zJwzCApXAnT_oaxSQ,3431
1024
1025
  zenml/zen_server/rbac/rbac_sql_zen_store.py,sha256=djGycBkciChk48q8oL7mHM0c-cHJM-S7inv5f3gcDgg,5942
1025
- zenml/zen_server/rbac/utils.py,sha256=FBaduqwErT9gDqwKOjN9zvtBdWm7dkrArGyBS0uUjIY,24036
1026
- zenml/zen_server/rbac/zenml_cloud_rbac.py,sha256=mDI6eeTksKvOi3lgwyMgEMTgUHP0OJ8GjSUFqnqqcy8,5797
1026
+ zenml/zen_server/rbac/utils.py,sha256=8hBvCGWnz517hL5gn53dcjcrKePE2OGqZV_ojJ8XZEw,24329
1027
+ zenml/zen_server/rbac/zenml_cloud_rbac.py,sha256=L1Tio4IcNNxNWnVpAqBWYgdVfrqR20ddvdhWAo1i3f4,6055
1027
1028
  zenml/zen_server/routers/__init__.py,sha256=ViyAhWL-ogHxE9wBvB_iMcur5H1NRVrzXkpogVY7FBA,641
1028
1029
  zenml/zen_server/routers/actions_endpoints.py,sha256=MFausi27i5QBB5s4vbjQQTVqFiRlk5jF5lRM6vsRM2o,9422
1029
1030
  zenml/zen_server/routers/artifact_endpoint.py,sha256=huFufMN-hHzBUKqYmn1WeWVaJic1ypvqSpTWFxklUDQ,4976
1030
- zenml/zen_server/routers/artifact_version_endpoints.py,sha256=WkZ3C4kxZXJ6zvUdbqYDR3nNtgOV96r7IZxVFfVkmN8,8784
1031
+ zenml/zen_server/routers/artifact_version_endpoints.py,sha256=zda_iINad3VnKcyGgCW2ZveblEzZa5YjwrCAsuEN13s,11254
1031
1032
  zenml/zen_server/routers/auth_endpoints.py,sha256=CZB5ZeuWy1lhP7-O7Be5dGzlr51YUR0Mtf4kMkj96Rw,23634
1032
1033
  zenml/zen_server/routers/code_repositories_endpoints.py,sha256=_D1896lrEgYD516y69iiFrPn5b62MxlrTcmDt1AyqBA,6476
1033
1034
  zenml/zen_server/routers/devices_endpoints.py,sha256=GluKziLikUn_IoflcBwV5wB8ui71STjDsg2Mjgn43MU,10551
@@ -1057,12 +1058,12 @@ zenml/zen_server/routers/steps_endpoints.py,sha256=By-9_VKrJeSo6UUqrggDhGKtrnhxi
1057
1058
  zenml/zen_server/routers/tag_resource_endpoints.py,sha256=AdmbujrwKK6arnSx2VHpJWbZXnlviGlay7LCov4x9Fc,3203
1058
1059
  zenml/zen_server/routers/tags_endpoints.py,sha256=lRjnMaJOdTnooMHoQYIViKfgu32cMq7rI3Lyeaxq4QI,4467
1059
1060
  zenml/zen_server/routers/triggers_endpoints.py,sha256=5fb0EITa3bs--AtKQRQZ5QswHu4pb37OrG5y0OlnGXE,9983
1060
- zenml/zen_server/routers/users_endpoints.py,sha256=b1zZRbVJF8MT3XjwAwj7FJ0scplnQuNT4AzmbCU8PcU,24344
1061
+ zenml/zen_server/routers/users_endpoints.py,sha256=2SfqHutGp0OqwYVytx3WeIwF51nZ3f874uCiCc_m_7g,24531
1061
1062
  zenml/zen_server/routers/webhook_endpoints.py,sha256=KOJsuykv_TMjL3oEItpC4OWWP75p-OEvVjRSUBd5Mro,3983
1062
1063
  zenml/zen_server/secure_headers.py,sha256=glh6QujnjyeoH1_FK-tAS-105G-qKS_34AqSzqJ6TRc,4182
1063
1064
  zenml/zen_server/template_execution/__init__.py,sha256=79knXLKfegsvVSVSWecpqrepq6iAavTUA4hKuiDk-WE,613
1064
1065
  zenml/zen_server/template_execution/runner_entrypoint_configuration.py,sha256=Y8aYJhqqs8Kv8I1q-dM1WemS5VBIfyoaaYH_YkzC7iY,1541
1065
- zenml/zen_server/template_execution/utils.py,sha256=r85hjV_W3ToFLjyTwBrf4hJIAGOM2GpvadgIKnuo2U4,17336
1066
+ zenml/zen_server/template_execution/utils.py,sha256=qL62UDH4FwXhpLYwGp-VxF3mttOEr8HO8O7utvyHRfM,17330
1066
1067
  zenml/zen_server/template_execution/workload_manager_interface.py,sha256=CL9c7z8ajuZE01DaHmdCDCZmsroDcTarvN-nE8jv6qQ,2590
1067
1068
  zenml/zen_server/utils.py,sha256=Jc2Q4UBaYG2ruHdsN9JmbOWfWU_eWD9wTBBEgcGAbqg,17439
1068
1069
  zenml/zen_server/zen_server_api.py,sha256=ALyv5096frXXRNySegEtsmkDbHLLqHd402bbQI7RnII,17941
@@ -1267,6 +1268,7 @@ zenml/zen_stores/migrations/versions/f3b3964e3a0f_add_oauth_devices.py,sha256=2C
1267
1268
  zenml/zen_stores/migrations/versions/f49904a80aa7_increase_length_of_artifact_table_sources.py,sha256=kLgfDUnQdAb5_SyFx3VKXDLC0YbuBKf9iXRDNeBin7Q,1618
1268
1269
  zenml/zen_stores/migrations/versions/f76a368a25a5_add_stack_description.py,sha256=u8fRomaasFeGhxvM2zU-Ab-AEpVsWm5zRcixxKFXdRw,904
1269
1270
  zenml/zen_stores/migrations/versions/fbd7f18ced1e_increase_step_run_field_lengths.py,sha256=kn-ng5EHe_mmLfffIFbz7T59z-to3oMx8III_4wOsz4,1956
1271
+ zenml/zen_stores/migrations/versions/ff538a321a92_migrate_onboarding_state.py,sha256=gsUFLJQ32_o9U35JCVqkqJVVk-zfq3yel25hXhzVFm4,3829
1270
1272
  zenml/zen_stores/rest_zen_store.py,sha256=zo0OnNZuiC8EWR-P9-oz7VdEFYPimnikERtvpEJjEgs,158091
1271
1273
  zenml/zen_stores/schemas/__init__.py,sha256=4EXqExiVyxdnGxhQ_Hz79mOdRuMD0LsGlw0PaP2Ef6o,4333
1272
1274
  zenml/zen_stores/schemas/action_schemas.py,sha256=2OiUiskFSg5qXGxA6AFq71bWzUczxA563LGFokLZmac,6456
@@ -1292,7 +1294,7 @@ zenml/zen_stores/schemas/run_template_schemas.py,sha256=sM3pcegx7qOs5tlwqVnFvc7J
1292
1294
  zenml/zen_stores/schemas/schedule_schema.py,sha256=E9hmq8-PBRHxEH5UYEpD_Oc2cHkNp4FG0sTvhiLZ6D4,7653
1293
1295
  zenml/zen_stores/schemas/schema_utils.py,sha256=Xahifq2fJ5szXCM00ZZ6461Div9Suatzl6sy9hVhPkk,3612
1294
1296
  zenml/zen_stores/schemas/secret_schemas.py,sha256=HrihLSUPJKmWbQHogGmi6e0lYPoQHUGWGRl69sLnZMk,9430
1295
- zenml/zen_stores/schemas/server_settings_schemas.py,sha256=jhvc-WYmKUlHU4SDdPjm1BLbm2D_Rug-rgXuyY4kzeo,4201
1297
+ zenml/zen_stores/schemas/server_settings_schemas.py,sha256=usBE-idRrmK-LeLN0zDtCRCGP51YTnyKfIx5GZ0_ATg,4275
1296
1298
  zenml/zen_stores/schemas/service_connector_schemas.py,sha256=kNJ6TmM2RA16wc73zusosHxWUm6gVpyowlY_OJv-Oww,9709
1297
1299
  zenml/zen_stores/schemas/service_schemas.py,sha256=yg9OJ2QWa1qQy_gqraGvN3JQrOJDLjhkSL-UVSFJy-s,9517
1298
1300
  zenml/zen_stores/schemas/stack_schemas.py,sha256=SkMspxsnR420KU4mZlulPbEOR4DMSRyrcspe3HSYjHc,5393
@@ -1313,8 +1315,8 @@ zenml/zen_stores/secrets_stores/sql_secrets_store.py,sha256=nEO0bAPlULBLxLVk-UTR
1313
1315
  zenml/zen_stores/sql_zen_store.py,sha256=ndfeiZSIsilv6aEmTsYVEIYL9eMRUKVQTVYXDTt9xXI,441016
1314
1316
  zenml/zen_stores/template_utils.py,sha256=GWBP5QEOyvhzndS_MLPmvh28sQaOPpPoZFXCIX9CRL4,9065
1315
1317
  zenml/zen_stores/zen_store_interface.py,sha256=fF_uL_FplnvGvM5o3jOQ8i1zHXhuhKLL2n4nvIKSR7E,92090
1316
- zenml_nightly-0.80.2.dev20250414.dist-info/LICENSE,sha256=wbnfEnXnafPbqwANHkV6LUsPKOtdpsd-SNw37rogLtc,11359
1317
- zenml_nightly-0.80.2.dev20250414.dist-info/METADATA,sha256=y0D8vRfzPVSqxhsYe2AVGg4Ti0F027IHgo7cNrSERLM,24233
1318
- zenml_nightly-0.80.2.dev20250414.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
1319
- zenml_nightly-0.80.2.dev20250414.dist-info/entry_points.txt,sha256=QK3ETQE0YswAM2mWypNMOv8TLtr7EjnqAFq1br_jEFE,43
1320
- zenml_nightly-0.80.2.dev20250414.dist-info/RECORD,,
1318
+ zenml_nightly-0.80.2.dev20250415.dist-info/LICENSE,sha256=wbnfEnXnafPbqwANHkV6LUsPKOtdpsd-SNw37rogLtc,11359
1319
+ zenml_nightly-0.80.2.dev20250415.dist-info/METADATA,sha256=uqsecSjp8pzIGZ_ho7pXxHqjQB4M--7DaJ5IpPOD6_g,24233
1320
+ zenml_nightly-0.80.2.dev20250415.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
1321
+ zenml_nightly-0.80.2.dev20250415.dist-info/entry_points.txt,sha256=QK3ETQE0YswAM2mWypNMOv8TLtr7EjnqAFq1br_jEFE,43
1322
+ zenml_nightly-0.80.2.dev20250415.dist-info/RECORD,,