zenml-nightly 0.84.0.dev20250728__py3-none-any.whl → 0.84.0.dev20250729__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.
zenml/VERSION CHANGED
@@ -1 +1 @@
1
- 0.84.0.dev20250728
1
+ 0.84.0.dev20250729
@@ -13,7 +13,7 @@
13
13
  # permissions and limitations under the License.
14
14
  """Implementation of the Local git repository context."""
15
15
 
16
- from typing import TYPE_CHECKING, Callable, Optional, cast
16
+ from typing import TYPE_CHECKING, Callable, Optional
17
17
 
18
18
  from zenml.code_repositories import (
19
19
  LocalRepositoryContext,
@@ -25,7 +25,6 @@ from zenml.constants import (
25
25
  from zenml.logger import get_logger
26
26
 
27
27
  if TYPE_CHECKING:
28
- from git.objects import Commit
29
28
  from git.remote import Remote
30
29
  from git.repo.base import Repo
31
30
 
@@ -180,7 +179,7 @@ class LocalGitRepositoryContext(LocalRepositoryContext):
180
179
  # Branch doesn't exist on remote
181
180
  return True
182
181
 
183
- return cast("Commit", remote_commit_object) != local_commit_object
182
+ return remote_commit_object != local_commit_object
184
183
 
185
184
  @property
186
185
  def current_commit(self) -> str:
@@ -189,4 +188,4 @@ class LocalGitRepositoryContext(LocalRepositoryContext):
189
188
  Returns:
190
189
  The current commit sha.
191
190
  """
192
- return cast(str, self.git_repo.head.object.hexsha)
191
+ return self.git_repo.head.object.hexsha
zenml/constants.py CHANGED
@@ -181,6 +181,9 @@ ENV_ZENML_PIPELINE_RUN_API_TOKEN_EXPIRATION = (
181
181
  ENV_ZENML_CODE_REPOSITORY_IGNORE_UNTRACKED_FILES = (
182
182
  "ZENML_CODE_REPOSITORY_IGNORE_UNTRACKED_FILES"
183
183
  )
184
+ # Environment variable that indicates whether the current environment is running
185
+ # a step operator.
186
+ ENV_ZENML_STEP_OPERATOR = "ZENML_STEP_OPERATOR"
184
187
 
185
188
  # Materializer environment variables
186
189
  ENV_ZENML_MATERIALIZER_ALLOW_NON_ASCII_JSON_DUMPS = (
@@ -1490,7 +1490,8 @@ class AzureServiceConnector(ServiceConnector):
1490
1490
  aks_clusters = [
1491
1491
  cluster
1492
1492
  for cluster in aks_clusters
1493
- if self._get_resource_group(cluster.id)
1493
+ if cluster.id
1494
+ and self._get_resource_group(cluster.id)
1494
1495
  == self.config.resource_group
1495
1496
  ]
1496
1497
 
@@ -1507,7 +1508,8 @@ class AzureServiceConnector(ServiceConnector):
1507
1508
  clusters = [
1508
1509
  (cluster.name, self._get_resource_group(cluster.id))
1509
1510
  for cluster in aks_clusters
1510
- if cluster.name
1511
+ if cluster.id
1512
+ and cluster.name
1511
1513
  and (not cluster_name or cluster.name == cluster_name)
1512
1514
  ]
1513
1515
 
@@ -1809,16 +1811,25 @@ class AzureServiceConnector(ServiceConnector):
1809
1811
  cluster_name, resource_group = clusters[0]
1810
1812
 
1811
1813
  try:
1812
- client = ContainerServiceClient(credential, subscription_id)
1814
+ cs_client = ContainerServiceClient(credential, subscription_id)
1813
1815
 
1814
- creds = client.managed_clusters.list_cluster_admin_credentials(
1815
- resource_group_name=resource_group,
1816
- resource_name=cluster_name,
1816
+ creds = (
1817
+ cs_client.managed_clusters.list_cluster_admin_credentials(
1818
+ resource_group_name=resource_group,
1819
+ resource_name=cluster_name,
1820
+ )
1817
1821
  )
1818
1822
 
1819
- kubeconfig_yaml = creds.kubeconfigs[0].value.decode(
1820
- encoding="UTF-8"
1821
- )
1823
+ if creds.kubeconfigs and creds.kubeconfigs[0].value:
1824
+ kubeconfig_yaml = creds.kubeconfigs[0].value.decode(
1825
+ encoding="UTF-8"
1826
+ )
1827
+ else:
1828
+ raise AuthorizationException(
1829
+ f"failed to list credentials for Azure Kubernetes "
1830
+ f"Service cluster '{cluster_name}' in resource group "
1831
+ f"'{resource_group}': no kubeconfig found"
1832
+ )
1822
1833
  except AzureError as e:
1823
1834
  raise AuthorizationException(
1824
1835
  f"failed to list credentials for Azure Kubernetes "
@@ -13,7 +13,7 @@
13
13
  # permissions and limitations under the License.
14
14
  """Kubernetes orchestrator flavor."""
15
15
 
16
- from typing import TYPE_CHECKING, Any, Dict, Optional, Type
16
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Type
17
17
 
18
18
  from pydantic import Field, NonNegativeInt, PositiveInt, field_validator
19
19
 
@@ -131,6 +131,25 @@ class KubernetesOrchestratorSettings(BaseSettings):
131
131
  "the chance of the server receiving the maximum amount of retry "
132
132
  "requests.",
133
133
  )
134
+ fail_on_container_waiting_reasons: Optional[List[str]] = Field(
135
+ default=[
136
+ "InvalidImageName",
137
+ "ErrImagePull",
138
+ "ImagePullBackOff",
139
+ "CreateContainerConfigError",
140
+ ],
141
+ description="List of container waiting reasons that should cause the "
142
+ "job to fail immediately. This should be set to a list of "
143
+ "nonrecoverable reasons, which if found in any "
144
+ "`pod.status.containerStatuses[*].state.waiting.reason` of a job pod, "
145
+ "should cause the job to fail immediately.",
146
+ )
147
+ job_monitoring_interval: int = Field(
148
+ default=3,
149
+ description="The interval in seconds to monitor the job. Each interval "
150
+ "is used to check for container issues and streaming logs for the "
151
+ "job pods.",
152
+ )
134
153
  pod_failure_policy: Optional[Dict[str, Any]] = Field(
135
154
  default=None,
136
155
  description="The pod failure policy to use for the job that is "
@@ -37,7 +37,7 @@ import json
37
37
  import re
38
38
  import time
39
39
  from collections import defaultdict
40
- from typing import Any, Callable, Dict, List, Optional, TypeVar, cast
40
+ from typing import Any, Callable, Dict, List, Optional, Tuple, TypeVar, cast
41
41
 
42
42
  from kubernetes import client as k8s_client
43
43
  from kubernetes import config as k8s_config
@@ -657,6 +657,50 @@ def create_job(
657
657
  )
658
658
 
659
659
 
660
+ def get_container_status(
661
+ pod: k8s_client.V1Pod, container_name: str
662
+ ) -> Optional[k8s_client.V1ContainerState]:
663
+ """Get the status of a container.
664
+
665
+ Args:
666
+ pod: The pod to get the container status for.
667
+ container_name: The container name.
668
+
669
+ Returns:
670
+ The container status.
671
+ """
672
+ if not pod.status or not pod.status.container_statuses:
673
+ return None
674
+
675
+ for container_status in pod.status.container_statuses:
676
+ if container_status.name == container_name:
677
+ return container_status.state
678
+
679
+ return None
680
+
681
+
682
+ def get_container_termination_reason(
683
+ pod: k8s_client.V1Pod, container_name: str
684
+ ) -> Optional[Tuple[int, str]]:
685
+ """Get the termination reason for a container.
686
+
687
+ Args:
688
+ pod: The pod to get the termination reason for.
689
+ container_name: The container name.
690
+
691
+ Returns:
692
+ The exit code and termination reason for the container.
693
+ """
694
+ container_state = get_container_status(pod, container_name)
695
+ if not container_state or not container_state.terminated:
696
+ return None
697
+
698
+ return (
699
+ container_state.terminated.exit_code,
700
+ container_state.terminated.reason or "Unknown",
701
+ )
702
+
703
+
660
704
  def wait_for_job_to_finish(
661
705
  batch_api: k8s_client.BatchV1Api,
662
706
  core_api: k8s_client.CoreV1Api,
@@ -665,8 +709,9 @@ def wait_for_job_to_finish(
665
709
  backoff_interval: float = 1,
666
710
  maximum_backoff: float = 32,
667
711
  exponential_backoff: bool = False,
668
- container_name: Optional[str] = None,
712
+ fail_on_container_waiting_reasons: Optional[List[str]] = None,
669
713
  stream_logs: bool = True,
714
+ container_name: Optional[str] = None,
670
715
  ) -> None:
671
716
  """Wait for a job to finish.
672
717
 
@@ -679,6 +724,8 @@ def wait_for_job_to_finish(
679
724
  maximum_backoff: The maximum interval to wait between polling the job
680
725
  status.
681
726
  exponential_backoff: Whether to use exponential backoff.
727
+ fail_on_container_waiting_reasons: List of container waiting reasons
728
+ that will cause the job to fail.
682
729
  stream_logs: Whether to stream the job logs.
683
730
  container_name: Name of the container to stream logs from.
684
731
 
@@ -703,9 +750,39 @@ def wait_for_job_to_finish(
703
750
  f"{condition.message}"
704
751
  )
705
752
 
753
+ if fail_on_container_waiting_reasons:
754
+ pod_list: k8s_client.V1PodList = retry_on_api_exception(
755
+ core_api.list_namespaced_pod
756
+ )(
757
+ namespace=namespace,
758
+ label_selector=f"job-name={job_name}",
759
+ field_selector="status.phase=Pending",
760
+ )
761
+ for pod in pod_list.items:
762
+ container_state = get_container_status(
763
+ pod, container_name or "main"
764
+ )
765
+
766
+ if (
767
+ container_state
768
+ and (waiting_state := container_state.waiting)
769
+ and waiting_state.reason
770
+ in fail_on_container_waiting_reasons
771
+ ):
772
+ retry_on_api_exception(batch_api.delete_namespaced_job)(
773
+ name=job_name,
774
+ namespace=namespace,
775
+ propagation_policy="Foreground",
776
+ )
777
+ raise RuntimeError(
778
+ f"Job `{namespace}:{job_name}` failed: "
779
+ f"Detected container in state "
780
+ f"{waiting_state.reason}"
781
+ )
782
+
706
783
  if stream_logs:
707
784
  try:
708
- pod_list: k8s_client.V1PodList = core_api.list_namespaced_pod(
785
+ pod_list = core_api.list_namespaced_pod(
709
786
  namespace=namespace,
710
787
  label_selector=f"job-name={job_name}",
711
788
  )
@@ -367,33 +367,37 @@ def main() -> None:
367
367
  core_api=core_api,
368
368
  namespace=namespace,
369
369
  job_name=job_name,
370
+ fail_on_container_waiting_reasons=settings.fail_on_container_waiting_reasons,
370
371
  stream_logs=pipeline_settings.stream_step_logs,
372
+ backoff_interval=settings.job_monitoring_interval,
371
373
  )
372
374
 
373
375
  logger.info(f"Job for step `{step_name}` completed.")
374
376
  except Exception:
375
- reason = ""
377
+ reason = "Unknown"
376
378
  try:
377
- pods_by_job = core_api.list_namespaced_pod(
379
+ pods = core_api.list_namespaced_pod(
378
380
  label_selector=f"job-name={job_name}",
379
381
  namespace=namespace,
380
382
  ).items
381
- first_pod = pods_by_job[0]
382
- if status := first_pod.status:
383
- if container_statuses := status.container_statuses:
384
- for cs in container_statuses:
385
- if cs.name == "main":
386
- if state := cs.state:
387
- if terminated := state.terminated:
388
- if terminated.exit_code != 0:
389
- reason = f"{terminated.reason}(exit_code={terminated.exit_code})"
390
- break
383
+ # Sort pods by creation timestamp, oldest first
384
+ pods.sort(
385
+ key=lambda pod: pod.metadata.creation_timestamp,
386
+ )
387
+ if pods:
388
+ if (
389
+ termination_reason
390
+ := kube_utils.get_container_termination_reason(
391
+ pods[-1], "main"
392
+ )
393
+ ):
394
+ exit_code, reason = termination_reason
395
+ if exit_code != 0:
396
+ reason = f"{reason} (exit_code={exit_code})"
391
397
  except Exception:
392
398
  pass
393
399
  logger.error(
394
400
  f"Job for step `{step_name}` failed. Reason: {reason}"
395
- if reason
396
- else ""
397
401
  )
398
402
 
399
403
  raise
zenml/models/__init__.py CHANGED
@@ -435,6 +435,7 @@ from zenml.models.v2.misc.info_models import (
435
435
  ServiceConnectorResourcesInfo,
436
436
  ResourcesInfo,
437
437
  )
438
+ from zenml.models.v2.misc.exception_info import ExceptionInfo
438
439
 
439
440
  # ----------------------------- Forward References -----------------------------
440
441
 
@@ -840,4 +841,5 @@ __all__ = [
840
841
  "RunMetadataResource",
841
842
  "ProjectStatistics",
842
843
  "PipelineRunDAG",
844
+ "ExceptionInfo",
843
845
  ]
@@ -48,6 +48,7 @@ from zenml.models.v2.base.scoped import (
48
48
  )
49
49
  from zenml.models.v2.core.artifact_version import ArtifactVersionResponse
50
50
  from zenml.models.v2.core.model_version import ModelVersionResponse
51
+ from zenml.models.v2.misc.exception_info import ExceptionInfo
51
52
 
52
53
  if TYPE_CHECKING:
53
54
  from sqlalchemy.sql.elements import ColumnElement
@@ -143,6 +144,10 @@ class StepRunRequest(ProjectScopedRequest):
143
144
  title="Logs associated with this step run.",
144
145
  default=None,
145
146
  )
147
+ exception_info: Optional[ExceptionInfo] = Field(
148
+ default=None,
149
+ title="The exception information of the step run.",
150
+ )
146
151
 
147
152
  model_config = ConfigDict(protected_namespaces=())
148
153
 
@@ -169,6 +174,10 @@ class StepRunUpdate(BaseUpdate):
169
174
  title="The end time of the step run.",
170
175
  default=None,
171
176
  )
177
+ exception_info: Optional[ExceptionInfo] = Field(
178
+ default=None,
179
+ title="The exception information of the step run.",
180
+ )
172
181
  model_config = ConfigDict(protected_namespaces=())
173
182
 
174
183
 
@@ -237,6 +246,10 @@ class StepRunResponseMetadata(ProjectScopedResponseMetadata):
237
246
  default=None,
238
247
  max_length=TEXT_FIELD_MAX_LENGTH,
239
248
  )
249
+ exception_info: Optional[ExceptionInfo] = Field(
250
+ default=None,
251
+ title="The exception information of the step run.",
252
+ )
240
253
 
241
254
  # References
242
255
  logs: Optional["LogsResponse"] = Field(
@@ -0,0 +1,30 @@
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
+ """Exception information models."""
15
+
16
+ from typing import Optional
17
+
18
+ from pydantic import BaseModel, Field
19
+
20
+
21
+ class ExceptionInfo(BaseModel):
22
+ """Exception information."""
23
+
24
+ traceback: str = Field(
25
+ title="The traceback of the exception.",
26
+ )
27
+ step_code_line: Optional[int] = Field(
28
+ default=None,
29
+ title="The line number of the step code that raised the exception.",
30
+ )
@@ -13,12 +13,14 @@
13
13
  # permissions and limitations under the License.
14
14
  """Utilities to publish pipeline and step runs."""
15
15
 
16
+ from contextvars import ContextVar
16
17
  from datetime import datetime
17
18
  from typing import TYPE_CHECKING, Dict, List, Optional
18
19
 
19
20
  from zenml.client import Client
20
21
  from zenml.enums import ExecutionStatus, MetadataResourceTypes
21
22
  from zenml.models import (
23
+ ExceptionInfo,
22
24
  PipelineRunResponse,
23
25
  PipelineRunUpdate,
24
26
  RunMetadataResource,
@@ -32,6 +34,10 @@ if TYPE_CHECKING:
32
34
 
33
35
  from zenml.metadata.metadata_types import MetadataType
34
36
 
37
+ step_exception_info: ContextVar[Optional[ExceptionInfo]] = ContextVar(
38
+ "step_exception_info", default=None
39
+ )
40
+
35
41
 
36
42
  def publish_successful_step_run(
37
43
  step_run_id: "UUID", output_artifact_ids: Dict[str, List["UUID"]]
@@ -59,6 +65,7 @@ def publish_step_run_status_update(
59
65
  step_run_id: "UUID",
60
66
  status: "ExecutionStatus",
61
67
  end_time: Optional[datetime] = None,
68
+ exception_info: Optional[ExceptionInfo] = None,
62
69
  ) -> "StepRunResponse":
63
70
  """Publishes a step run update.
64
71
 
@@ -66,6 +73,7 @@ def publish_step_run_status_update(
66
73
  step_run_id: ID of the step run.
67
74
  status: New status of the step run.
68
75
  end_time: New end time of the step run.
76
+ exception_info: Exception information of the step run.
69
77
 
70
78
  Returns:
71
79
  The updated step run.
@@ -83,6 +91,7 @@ def publish_step_run_status_update(
83
91
  step_run_update=StepRunUpdate(
84
92
  status=status,
85
93
  end_time=end_time,
94
+ exception_info=exception_info,
86
95
  ),
87
96
  )
88
97
 
@@ -102,6 +111,7 @@ def publish_failed_step_run(step_run_id: "UUID") -> "StepRunResponse":
102
111
  step_run_id=step_run_id,
103
112
  status=ExecutionStatus.FAILED,
104
113
  end_time=utc_now(),
114
+ exception_info=step_exception_info.get(),
105
115
  )
106
116
 
107
117
 
@@ -24,6 +24,7 @@ from zenml.config.step_configurations import Step
24
24
  from zenml.config.step_run_info import StepRunInfo
25
25
  from zenml.constants import (
26
26
  ENV_ZENML_DISABLE_STEP_LOGS_STORAGE,
27
+ ENV_ZENML_STEP_OPERATOR,
27
28
  handle_bool_env_var,
28
29
  )
29
30
  from zenml.enums import ExecutionStatus
@@ -43,7 +44,7 @@ from zenml.orchestrators import output_utils, publish_utils, step_run_utils
43
44
  from zenml.orchestrators import utils as orchestrator_utils
44
45
  from zenml.orchestrators.step_runner import StepRunner
45
46
  from zenml.stack import Stack
46
- from zenml.utils import string_utils
47
+ from zenml.utils import exception_utils, string_utils
47
48
  from zenml.utils.time_utils import utc_now
48
49
 
49
50
  if TYPE_CHECKING:
@@ -211,7 +212,9 @@ class StepLauncher:
211
212
 
212
213
  Raises:
213
214
  RunStoppedException: If the pipeline run is stopped by the user.
215
+ BaseException: If the step preparation or execution fails.
214
216
  """
217
+ publish_utils.step_exception_info.set(None)
215
218
  pipeline_run, run_was_created = self._create_or_reuse_run()
216
219
 
217
220
  # Enable or disable step logs storage
@@ -269,10 +272,13 @@ class StepLauncher:
269
272
 
270
273
  try:
271
274
  request_factory.populate_request(request=step_run_request)
272
- except:
275
+ except BaseException as e:
273
276
  logger.exception(f"Failed preparing step `{self._step_name}`.")
274
277
  step_run_request.status = ExecutionStatus.FAILED
275
278
  step_run_request.end_time = utc_now()
279
+ step_run_request.exception_info = (
280
+ exception_utils.collect_exception_information(e)
281
+ )
276
282
  raise
277
283
  finally:
278
284
  step_run = Client().zen_store.create_run_step(step_run_request)
@@ -453,6 +459,7 @@ class StepLauncher:
453
459
  environment = orchestrator_utils.get_config_environment_vars(
454
460
  pipeline_run_id=step_run_info.run_id,
455
461
  )
462
+ environment[ENV_ZENML_STEP_OPERATOR] = "True"
456
463
  logger.info(
457
464
  "Using step operator `%s` to run step `%s`.",
458
465
  step_operator.name,
@@ -16,6 +16,7 @@
16
16
 
17
17
  import copy
18
18
  import inspect
19
+ import os
19
20
  from contextlib import nullcontext
20
21
  from typing import (
21
22
  TYPE_CHECKING,
@@ -34,6 +35,7 @@ from zenml.config.step_configurations import StepConfiguration
34
35
  from zenml.config.step_run_info import StepRunInfo
35
36
  from zenml.constants import (
36
37
  ENV_ZENML_DISABLE_STEP_LOGS_STORAGE,
38
+ ENV_ZENML_STEP_OPERATOR,
37
39
  handle_bool_env_var,
38
40
  )
39
41
  from zenml.enums import ArtifactSaveType
@@ -41,10 +43,14 @@ from zenml.exceptions import StepInterfaceError
41
43
  from zenml.logger import get_logger
42
44
  from zenml.logging.step_logging import PipelineLogsStorageContext, redirected
43
45
  from zenml.materializers.base_materializer import BaseMaterializer
44
- from zenml.models.v2.core.step_run import StepRunInputResponse
46
+ from zenml.models.v2.core.step_run import (
47
+ StepRunInputResponse,
48
+ StepRunUpdate,
49
+ )
45
50
  from zenml.orchestrators.publish_utils import (
46
51
  publish_step_run_metadata,
47
52
  publish_successful_step_run,
53
+ step_exception_info,
48
54
  )
49
55
  from zenml.orchestrators.utils import (
50
56
  is_setting_enabled,
@@ -56,6 +62,7 @@ from zenml.steps.utils import (
56
62
  resolve_type_annotation,
57
63
  )
58
64
  from zenml.utils import (
65
+ exception_utils,
59
66
  materializer_utils,
60
67
  source_utils,
61
68
  string_utils,
@@ -193,6 +200,24 @@ class StepRunner:
193
200
  )
194
201
  except BaseException as step_exception: # noqa: E722
195
202
  step_failed = True
203
+
204
+ exception_info = exception_utils.collect_exception_information(
205
+ step_exception, step_instance
206
+ )
207
+
208
+ if ENV_ZENML_STEP_OPERATOR in os.environ:
209
+ # We're running in a step operator environment, so we can't
210
+ # depend on the step launcher to publish the exception info
211
+ Client().zen_store.update_run_step(
212
+ step_run_id=step_run_info.step_run_id,
213
+ step_run_update=StepRunUpdate(
214
+ exception_info=exception_info,
215
+ ),
216
+ )
217
+ else:
218
+ # This will be published by the step launcher
219
+ step_exception_info.set(exception_info)
220
+
196
221
  if not step_run.is_retriable:
197
222
  if (
198
223
  failure_hook_source
@@ -0,0 +1,90 @@
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
+ """Exception utilities."""
15
+
16
+ import inspect
17
+ import os
18
+ import re
19
+ import traceback
20
+ from typing import TYPE_CHECKING, Optional
21
+
22
+ from zenml.constants import MEDIUMTEXT_MAX_LENGTH
23
+ from zenml.logger import get_logger
24
+ from zenml.models import (
25
+ ExceptionInfo,
26
+ )
27
+
28
+ if TYPE_CHECKING:
29
+ from zenml.steps import BaseStep
30
+
31
+ logger = get_logger(__name__)
32
+
33
+
34
+ def collect_exception_information(
35
+ exception: BaseException, step_instance: Optional["BaseStep"] = None
36
+ ) -> ExceptionInfo:
37
+ """Collects the exception information.
38
+
39
+ Args:
40
+ exception: The exception to collect information from.
41
+ step_instance: The step instance that is currently running.
42
+
43
+ Returns:
44
+ The exception information.
45
+ """
46
+ tb = traceback.format_tb(exception.__traceback__)
47
+ line_number = None
48
+ start_index = None
49
+
50
+ if step_instance and (
51
+ source_file := inspect.getsourcefile(step_instance.entrypoint)
52
+ ):
53
+ try:
54
+ source_file = os.path.abspath(source_file)
55
+
56
+ lines, start_line = inspect.getsourcelines(
57
+ step_instance.entrypoint
58
+ )
59
+ end_line = start_line + len(lines)
60
+
61
+ line_pattern = re.compile(f'File "{source_file}", line (\d+),')
62
+
63
+ for index, line in enumerate(tb):
64
+ match = line_pattern.search(line)
65
+ if match:
66
+ potential_line_number = int(match.group(1))
67
+ if (
68
+ potential_line_number >= start_line
69
+ and potential_line_number <= end_line
70
+ ):
71
+ line_number = potential_line_number - start_line
72
+ start_index = index
73
+ break
74
+ except Exception as e:
75
+ logger.debug("Failed to detect step code line: %s", e)
76
+
77
+ if start_index is not None:
78
+ # If the code failed while executing user code, we remove the initial
79
+ # part of the traceback that is happening in the ZenML code.
80
+ tb = tb[start_index:]
81
+
82
+ tb_bytes = "\n".join(tb).encode()
83
+ tb_bytes = tb_bytes[:MEDIUMTEXT_MAX_LENGTH]
84
+
85
+ return ExceptionInfo(
86
+ # Ignore errors when decoding in case we cut off in the middle of an
87
+ # encoded character.
88
+ traceback=tb_bytes.decode(errors="ignore"),
89
+ step_code_line=line_number,
90
+ )
@@ -0,0 +1,43 @@
1
+ """Step exception info [d4591f95ac07].
2
+
3
+ Revision ID: d4591f95ac07
4
+ Revises: 0.84.0
5
+ Create Date: 2025-07-17 14:13:54.731842
6
+
7
+ """
8
+
9
+ import sqlalchemy as sa
10
+ from alembic import op
11
+ from sqlalchemy.dialects import mysql
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision = "d4591f95ac07"
15
+ down_revision = "0.84.0"
16
+ branch_labels = None
17
+ depends_on = None
18
+
19
+
20
+ def upgrade() -> None:
21
+ """Upgrade database schema and/or data, creating a new revision."""
22
+ # ### commands auto generated by Alembic - please adjust! ###
23
+ with op.batch_alter_table("step_run", schema=None) as batch_op:
24
+ batch_op.add_column(
25
+ sa.Column(
26
+ "exception_info",
27
+ sa.String(length=16777215).with_variant(
28
+ mysql.MEDIUMTEXT, "mysql"
29
+ ),
30
+ nullable=True,
31
+ )
32
+ )
33
+
34
+ # ### end Alembic commands ###
35
+
36
+
37
+ def downgrade() -> None:
38
+ """Downgrade database schema and/or data back to the previous revision."""
39
+ # ### commands auto generated by Alembic - please adjust! ###
40
+ with op.batch_alter_table("step_run", schema=None) as batch_op:
41
+ batch_op.drop_column("exception_info")
42
+
43
+ # ### end Alembic commands ###
@@ -34,6 +34,7 @@ from zenml.enums import (
34
34
  StepRunInputArtifactType,
35
35
  )
36
36
  from zenml.models import (
37
+ ExceptionInfo,
37
38
  StepRunRequest,
38
39
  StepRunResponse,
39
40
  StepRunResponseBody,
@@ -101,6 +102,14 @@ class StepRunSchema(NamedSchema, RunMetadataInterface, table=True):
101
102
  nullable=True,
102
103
  )
103
104
  )
105
+ exception_info: Optional[str] = Field(
106
+ sa_column=Column(
107
+ String(length=MEDIUMTEXT_MAX_LENGTH).with_variant(
108
+ MEDIUMTEXT, "mysql"
109
+ ),
110
+ nullable=True,
111
+ )
112
+ )
104
113
 
105
114
  # Foreign keys
106
115
  original_step_run_id: Optional[UUID] = build_foreign_key_field(
@@ -310,6 +319,9 @@ class StepRunSchema(NamedSchema, RunMetadataInterface, table=True):
310
319
  source_code=request.source_code,
311
320
  version=version,
312
321
  is_retriable=is_retriable,
322
+ exception_info=json.dumps(request.exception_info)
323
+ if request.exception_info
324
+ else None,
313
325
  )
314
326
 
315
327
  def get_step_configuration(self) -> Step:
@@ -395,6 +407,11 @@ class StepRunSchema(NamedSchema, RunMetadataInterface, table=True):
395
407
  code_hash=self.code_hash,
396
408
  docstring=self.docstring,
397
409
  source_code=self.source_code,
410
+ exception_info=ExceptionInfo.model_validate_json(
411
+ self.exception_info
412
+ )
413
+ if self.exception_info
414
+ else None,
398
415
  logs=self.logs.to_model() if self.logs else None,
399
416
  deployment_id=self.deployment_id,
400
417
  pipeline_run_id=self.pipeline_run_id,
@@ -458,6 +475,8 @@ class StepRunSchema(NamedSchema, RunMetadataInterface, table=True):
458
475
  self.status = value.value
459
476
  if key == "end_time":
460
477
  self.end_time = value
478
+ if key == "exception_info":
479
+ self.exception_info = json.dumps(value)
461
480
 
462
481
  self.updated = utc_now()
463
482
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: zenml-nightly
3
- Version: 0.84.0.dev20250728
3
+ Version: 0.84.0.dev20250729
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=WRM_9Y6VMs8f5Mlv18OC4qR1DoA4AV-1RBz6HZkQ-fs,19
2
+ zenml/VERSION,sha256=ig64qkkuwpgtDDUBRHxcjuv0CoEUTgcfKVFR9WA0Wt8,19
3
3
  zenml/__init__.py,sha256=r7JUg2SVDf_dPhS7iU6vudKusEqK4ics7_jFMZhq0o4,2731
4
4
  zenml/actions/__init__.py,sha256=mrt6wPo73iKRxK754_NqsGyJ3buW7RnVeIGXr1xEw8Y,681
5
5
  zenml/actions/base_action.py,sha256=UcaHev6BTuLDwuswnyaPjdA8AgUqB5xPZ-lRtuvf2FU,25553
@@ -60,7 +60,7 @@ zenml/client_lazy_loader.py,sha256=oyxKvBWVB7k2pHMavdhNEOfR2Vk4IS3XUu43SBzDPsI,7
60
60
  zenml/code_repositories/__init__.py,sha256=W5bDfzAG8OXIKZSV1L-VHuzMcSCYa9qzTdPb3jqfyYw,920
61
61
  zenml/code_repositories/base_code_repository.py,sha256=Id6VjbUu8N3ZpNvBGhOgbahtoMiCAtYXed3G7YQ_iAc,5225
62
62
  zenml/code_repositories/git/__init__.py,sha256=vU8UzMp8sv9n-R2r7VKa9LdQcYER6BhO4O-z8Ppa3kM,824
63
- zenml/code_repositories/git/local_git_repository_context.py,sha256=hfX7zVDQ27Le0qb4HKoJMcRB8YWfK8_yHPPgfh1SAS0,5848
63
+ zenml/code_repositories/git/local_git_repository_context.py,sha256=PuD4GOnxBNtkSddc5QwMnnx3F2t4gClUeGmhfoLQHfs,5780
64
64
  zenml/code_repositories/local_repository_context.py,sha256=1VyiYkJBDVg0iGusgRQDToGRPJuu9lx7jTBDpplukDg,2816
65
65
  zenml/config/__init__.py,sha256=NIMS7QQo3-uVkNlzPvR5FbtgPdRibF2xFJB7Vr0fhCM,1826
66
66
  zenml/config/base_settings.py,sha256=itoLqc1cOwEYhgSGdZmSKSaBevQkvYH7NQh7PUamazc,1700
@@ -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=t_ULrtJF2eW7TgyYBRobl1fscwwIZXATYky8ER97ev4,860
87
87
  zenml/console.py,sha256=A54wgNv_ntZkU4uGLox00IPVskDapH31xhtgAV5x4-E,1299
88
- zenml/constants.py,sha256=cHkG6v45NwK0XFblrS_Eoczfj80iVckI1W-PcsFqktU,16802
88
+ zenml/constants.py,sha256=olos1s4CGUzUpTTkcEmwS4iR7GENdgA-B-9pQWsS8gk,16950
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=5e-49puqeNgGdsLbfALoV-J8AgnriSvD-DDwBCPCiCs,9709
@@ -166,7 +166,7 @@ zenml/integrations/azure/orchestrators/__init__.py,sha256=q4rBPIJHcuUr6dLUBdrTkQ
166
166
  zenml/integrations/azure/orchestrators/azureml_orchestrator.py,sha256=zWtRz_ymW6CZfwgcLYUGLCDvWF_SUvgB9jBPkc192A0,21072
167
167
  zenml/integrations/azure/orchestrators/azureml_orchestrator_entrypoint_config.py,sha256=lacOyorsHa-HuD_kxN9M6BUiZDvs5jL9AnJWwrFCtp4,3104
168
168
  zenml/integrations/azure/service_connectors/__init__.py,sha256=yMz6bTCtIZqZwfEM6h7-PSWsd_DB8l0OD9z_bdzomZw,793
169
- zenml/integrations/azure/service_connectors/azure_service_connector.py,sha256=4Qma8FyKpZ5GujIa-RrGU2Lh80j9GHEyBwwDfyK7aAI,81344
169
+ zenml/integrations/azure/service_connectors/azure_service_connector.py,sha256=B9YBr-5j2D3UZ6Lo4e7aNMy5Os35W7dR-bno0QUMfUE,81865
170
170
  zenml/integrations/azure/step_operators/__init__.py,sha256=fV4_nAO0cH53x6_-F8-CbDEZwb_Vv64oq1r0-vtigEU,819
171
171
  zenml/integrations/azure/step_operators/azureml_step_operator.py,sha256=bi6cl1PimxZF6D7PVZepC6OqHdGHTcsNGBe0njuiBCM,7397
172
172
  zenml/integrations/bentoml/__init__.py,sha256=vOlziqJ038cS7_ZtbGojWpefdQDigq8mLDALVC_lgbY,1835
@@ -334,12 +334,12 @@ zenml/integrations/kubeflow/orchestrators/kubeflow_orchestrator.py,sha256=lPQSQn
334
334
  zenml/integrations/kubeflow/orchestrators/local_deployment_utils.py,sha256=qszoOdvBpgIp40XkncphXAr9dRKnyZzGiz2mJ56bYmw,15448
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
- zenml/integrations/kubernetes/flavors/kubernetes_orchestrator_flavor.py,sha256=MqPOFJpxQ1hfzF3c_FU_431OJGHOVKT_VNwdAzNu1lc,13595
337
+ zenml/integrations/kubernetes/flavors/kubernetes_orchestrator_flavor.py,sha256=zhcn5d3N4VmlVq-cxtyXtp63AKcwVfKWg-jAlkx2B10,14420
338
338
  zenml/integrations/kubernetes/flavors/kubernetes_step_operator_flavor.py,sha256=Vp7kY9t2TWdWRBNSf_sJKPL5dZnhuHnskPhQ7KBuGPY,6384
339
339
  zenml/integrations/kubernetes/orchestrators/__init__.py,sha256=TJID3OTieZBox36WpQpzD0jdVRA_aZVcs_bNtfXS8ik,811
340
- zenml/integrations/kubernetes/orchestrators/kube_utils.py,sha256=9oPdp1jREPOn2NAu_mmdxuFeYjMartj3cWYqzb808qw,25112
340
+ zenml/integrations/kubernetes/orchestrators/kube_utils.py,sha256=Gbw1mT0z-F_t2iQKPmLntGhdNTT-CZNHO5sDb-XxE68,27709
341
341
  zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py,sha256=Agx_Thj6nByAd5p61HTf0p6SRDPOcYKSSl-_Km2EdfU,41316
342
- zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint.py,sha256=8XZZMJB8E4HnXSUC436VRKMRJHCI57YK7a892xYGTSo,20675
342
+ zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint.py,sha256=jgxnJdBVFVn6l6gSoygIoScQxyKh5cVbgXF10qFA9Nk,20809
343
343
  zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint_configuration.py,sha256=QOwQnWCfB-t_BQ2eOZN0SakurGUd0GTMCSdUlREhk6I,2324
344
344
  zenml/integrations/kubernetes/orchestrators/manifest_utils.py,sha256=52Ba6MZ4lCo6CpnW0IjnFOJQNpPoVgbvIqR-x32ubew,16085
345
345
  zenml/integrations/kubernetes/pod_settings.py,sha256=s6I0sALXB7PnRqJ-nlU71o3cTm2GM1TfqsE44qhiDJY,8022
@@ -618,7 +618,7 @@ zenml/model_deployers/__init__.py,sha256=oVBLtTfrNenl5OI1iqtQUvJ0vpocRVUN_HIt8qp
618
618
  zenml/model_deployers/base_model_deployer.py,sha256=Z5-E_zC0Ss5OQhUBz9YKeVgS2MJOzuNz2aBzqI5eo4U,24610
619
619
  zenml/model_registries/__init__.py,sha256=wA9Vzo0w_e9zuXOVURB9w8oMLSnTaimXcxg_Nb7O3b0,1238
620
620
  zenml/model_registries/base_model_registry.py,sha256=NXviKlREKv56JJaS1skG0dXgP0YOzZJN7kTxqw-cw7U,17524
621
- zenml/models/__init__.py,sha256=pxwOM1cJS3N5pmCPt3P_LzjStOBI0WDwmdzEo7numqM,24238
621
+ zenml/models/__init__.py,sha256=I-5VZaOFp4-30q1kmOvIo6WUkl6iwkT_DGrvHZ8HtSk,24321
622
622
  zenml/models/v2/__init__.py,sha256=LGMIUJi19sOsvo54roZSQwDp_czNQYtenqd_frTLIhU,613
623
623
  zenml/models/v2/base/__init__.py,sha256=knhroJ2h0uHBCGzAiBBGJEiuhEA3cwI6XYBRIyXdbkQ,613
624
624
  zenml/models/v2/base/base.py,sha256=Oz6iWCaJw1ykoYe2tTvGJqalGmDp0tRS9r1NpBx8WsM,16222
@@ -660,7 +660,7 @@ zenml/models/v2/core/service.py,sha256=PeI036PIVG0zX5EiYPsx7h5LTRC8hlmfdeClKff-I
660
660
  zenml/models/v2/core/service_account.py,sha256=-1c9Et9Ma4_OHOZxIHjVnLJAaLLxhpoLEyGKkwc1Yx8,6619
661
661
  zenml/models/v2/core/service_connector.py,sha256=V1zWxTpo9ABCxRfK327w7CV21n5XWaPHgIR6CE2zHNk,38628
662
662
  zenml/models/v2/core/stack.py,sha256=3IKofEaKprAtYNbIrKvcYs5IABTbCqxcfJSXC3Q0fYM,13332
663
- zenml/models/v2/core/step_run.py,sha256=wM91dxaSTkOFVX9vRl4ooTlyrxmrxWrDszJydhTqjgs,22046
663
+ zenml/models/v2/core/step_run.py,sha256=KnwZMfqxPo0xJSoRv1pkdinze5nagBhcWKcCG6cyEt4,22531
664
664
  zenml/models/v2/core/tag.py,sha256=60hCZuHAHy7lsYFkTDw7hK0zZz2eG7jx8l9VY7Hh-LQ,7100
665
665
  zenml/models/v2/core/tag_resource.py,sha256=H7CPj9oaUpym8vSkwuYSJ6_rWfHMEKVRedPw2kyxmII,2640
666
666
  zenml/models/v2/core/trigger.py,sha256=L6-pzWPM2HFsfA6-6rM1XeHx4UChZrIY58G6ZG1PA10,12782
@@ -669,6 +669,7 @@ zenml/models/v2/core/user.py,sha256=jw8HRKhZKJ3ljAbNMiFga-IV04zXhiRS33jLm13b8RA,
669
669
  zenml/models/v2/misc/__init__.py,sha256=knhroJ2h0uHBCGzAiBBGJEiuhEA3cwI6XYBRIyXdbkQ,613
670
670
  zenml/models/v2/misc/auth_models.py,sha256=j31CQt9qfH20tyCjY0depqbKP0YZEewcgGKrixqBUbg,3688
671
671
  zenml/models/v2/misc/build_item.py,sha256=66Fywatv8bDY86pf0k530fsHTk_L4IkHL6uJL7-6dAo,1938
672
+ zenml/models/v2/misc/exception_info.py,sha256=CCTO7HYqsa3poi5hL4HK3ZS67xmkQNYQRHgLzqpUTHs,1016
672
673
  zenml/models/v2/misc/external_user.py,sha256=_prXznyVA3Dc5pCK7nVE72K3tjxslG_e3JxIXLIikBo,944
673
674
  zenml/models/v2/misc/info_models.py,sha256=3X6HFIlFcz5uuT7qgU18HqZBPkgrGZQ7QcM2yBT0qeU,2645
674
675
  zenml/models/v2/misc/loaded_visualization.py,sha256=u6lapDNZDtU9eS-_EMzl00urj0yPNhiqhZcvjIz7DSg,946
@@ -692,10 +693,10 @@ zenml/orchestrators/local/local_orchestrator.py,sha256=KCzc901_wrb1DPTDu_IY6HFxT
692
693
  zenml/orchestrators/local_docker/__init__.py,sha256=k8J68ydy6HmmvE9tWo32g761H8P_Dw4AxWNf4UMpsbs,669
693
694
  zenml/orchestrators/local_docker/local_docker_orchestrator.py,sha256=dEc1R2_xzFIzvSwPirwtH2noYzPyw0gbdDhpsqAgFMM,10032
694
695
  zenml/orchestrators/output_utils.py,sha256=01vqke1ZfmfuLpgxNerF-QL2wA0VPv1zUdvlMw0OwUY,3508
695
- zenml/orchestrators/publish_utils.py,sha256=nCUgjR8hl3H0sudPqojnENmALNwlFjtaZ1XKFzvPNHA,8109
696
- zenml/orchestrators/step_launcher.py,sha256=SIiWMgzqhJdwYNqbPgvoOxoxlM0SUg8oYEI9Gyh9wYQ,18460
696
+ zenml/orchestrators/publish_utils.py,sha256=u0jk9Q57fLQE1QDJjrydy5KQ9xtYKUvn3CXBirDH2u8,8485
697
+ zenml/orchestrators/step_launcher.py,sha256=ipFy7-pGKSmAUySRuBPddSusQ5LaLS0g5t49UlU77YY,18841
697
698
  zenml/orchestrators/step_run_utils.py,sha256=JXlYomV_PmC-cmjk7VWAGauKUXdijXU0ao8HBTZobmY,16802
698
- zenml/orchestrators/step_runner.py,sha256=sV0IkS9Rsa_TzSJNHRqwXKI0qXzMNZKRRn6Klfn-cEw,26523
699
+ zenml/orchestrators/step_runner.py,sha256=X3sX7f5NBYxT3utFGKcOJlkvCauYyEoo0LB2TB2Pp9U,27435
699
700
  zenml/orchestrators/topsort.py,sha256=D8evz3X47zwpXd90NMLsJD-_uCeXtV6ClzNfDUrq7cM,5784
700
701
  zenml/orchestrators/utils.py,sha256=6bqLc1fmdJTXg8JUwUKs8YNbmxTuMIfWmUbUpg-7hx0,12956
701
702
  zenml/orchestrators/wheeled_orchestrator.py,sha256=eOnMcnd3sCzfhA2l6qRAzF0rOXzaojbjvvYvTkqixQo,4791
@@ -775,6 +776,7 @@ zenml/utils/docker_utils.py,sha256=occ364GUasOyiEipCcBHdK1WSvh1oVJIVnc-XXNsrcE,1
775
776
  zenml/utils/downloaded_repository_context.py,sha256=GyPOzC8M3SaEGnnbPgEgJhUW-m5hx9rLScnCiGw6_PY,1504
776
777
  zenml/utils/enum_utils.py,sha256=0fA0B9v9Wjutuqlu_owUoOit1utIw2UH5J6YHXSqhLU,1368
777
778
  zenml/utils/env_utils.py,sha256=2--2DDUt3gPOvCNVyobBtAciikQ0OEXs5WWp7NvYuKo,5276
779
+ zenml/utils/exception_utils.py,sha256=7X--e7ERx2PYzhNwyhFMzKyriBpPPLybS5blpdZe1PE,2965
778
780
  zenml/utils/filesync_model.py,sha256=9ibsIr2HJfkEQ41upQd4uJ79ZhzB0MHS85GOGOAQ19Y,5470
779
781
  zenml/utils/function_utils.py,sha256=bznRrCrQIJ3Joc-QQ8ynDyn9AvyIcR61bzLnaXQV3tc,8109
780
782
  zenml/utils/git_utils.py,sha256=O2f6PUpDuMQrcmkcGXIhbJQpTzLM7p8PyiKWjcIuWqM,1831
@@ -1288,6 +1290,7 @@ zenml/zen_stores/migrations/versions/cc9894cb58aa_add_user_metadata.py,sha256=68
1288
1290
  zenml/zen_stores/migrations/versions/ccd68b7825ae_add_status_to_pipeline_and_step_run.py,sha256=MUvxaUyzcLtNOVgCgM6nBE3UTPKz5sVKsz-k071zCes,1824
1289
1291
  zenml/zen_stores/migrations/versions/d02b3d3464cf_add_orchestrator_run_id_column.py,sha256=b5KNEC9vWdbWkiMRpPKJDK0xE1HiOdVJT_ab5kynA7M,1157
1290
1292
  zenml/zen_stores/migrations/versions/d26471b6fe8f_update_build_filtering.py,sha256=VVDG2lhPzhejGm4Phzxw8sRCZd-vFXU4J3BtR96aRL0,1993
1293
+ zenml/zen_stores/migrations/versions/d4591f95ac07_step_exception_info.py,sha256=LAWlgcCgWpo4yz54JIm7ys_DdK8Jd0mOpyhUKsHNjf8,1198
1291
1294
  zenml/zen_stores/migrations/versions/d7b3acf9aa46_create_schedule_table.py,sha256=ntxrMe__pAjvepVi0hznANE3tIHgrfJ1Ml4gtxmmSLU,3329
1292
1295
  zenml/zen_stores/migrations/versions/e1d66d91a099_add_stack_and_component_spec_paths_to_.py,sha256=zAwzy9_n8s2WZ08vVyDG1WihowKpHDf-EOs9dbUmgPg,1543
1293
1296
  zenml/zen_stores/migrations/versions/e5225281b4d3_add_connector_skew_tolerance.py,sha256=nR2h9X_cnbJ7OUwANJuzazhN90_anXF3YRABQPofqX0,1048
@@ -1330,7 +1333,7 @@ zenml/zen_stores/schemas/server_settings_schemas.py,sha256=usBE-idRrmK-LeLN0zDtC
1330
1333
  zenml/zen_stores/schemas/service_connector_schemas.py,sha256=SpzFZTbfxypFtMLBujPSlGOpt-kzjcHfq3IoBghPizI,11137
1331
1334
  zenml/zen_stores/schemas/service_schemas.py,sha256=Fn_DyGm-PF-qN1TPZA3Q8AhrnUOTSIX0V_MD0laZ2Bk,10685
1332
1335
  zenml/zen_stores/schemas/stack_schemas.py,sha256=DOPoNwblTqN83RpnD6cjyF80VeplA1BGUX8yuulpzKk,6829
1333
- zenml/zen_stores/schemas/step_run_schemas.py,sha256=nqAX5y_rlnvqXH1MBMCtvxFNebfs00iu5RtqtiCAj64,20086
1336
+ zenml/zen_stores/schemas/step_run_schemas.py,sha256=lzisqMKUsKmzfl6AiagFlmVADOZowFvyZamltgjXxaE,20740
1334
1337
  zenml/zen_stores/schemas/tag_schemas.py,sha256=pwQ5ogZovtkUuRtAkHakOfMh6ixw6qofvxB4i_Gd4qA,8352
1335
1338
  zenml/zen_stores/schemas/trigger_schemas.py,sha256=LokauFEHdN5UuYeC2kf5PtJ60WMz-E82Ngcwxcv6tDA,11936
1336
1339
  zenml/zen_stores/schemas/user_schemas.py,sha256=wLSHtfjjrYBWnX_PXv5KPz_DRdheTxZVjcgBe-cP__c,11497
@@ -1347,8 +1350,8 @@ zenml/zen_stores/secrets_stores/sql_secrets_store.py,sha256=LPFW757WCJLP1S8vrvjs
1347
1350
  zenml/zen_stores/sql_zen_store.py,sha256=cXcKBBRKiwM2GtwHzmJb6tiN5NhGWo9n8SWnL1b4_WE,491150
1348
1351
  zenml/zen_stores/template_utils.py,sha256=iCXrXpqzVTY7roqop4Eh9J7DmLW6PQeILZexmw_l3b8,10074
1349
1352
  zenml/zen_stores/zen_store_interface.py,sha256=weiSULdI9AsbCE10a5TcwtybX-BJs9hKhjPJnTapWv4,93023
1350
- zenml_nightly-0.84.0.dev20250728.dist-info/LICENSE,sha256=wbnfEnXnafPbqwANHkV6LUsPKOtdpsd-SNw37rogLtc,11359
1351
- zenml_nightly-0.84.0.dev20250728.dist-info/METADATA,sha256=nng2oUmzUZy3Tl0YEHaj5PH63qN78QIGAYaO1wZm_z8,24296
1352
- zenml_nightly-0.84.0.dev20250728.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
1353
- zenml_nightly-0.84.0.dev20250728.dist-info/entry_points.txt,sha256=QK3ETQE0YswAM2mWypNMOv8TLtr7EjnqAFq1br_jEFE,43
1354
- zenml_nightly-0.84.0.dev20250728.dist-info/RECORD,,
1353
+ zenml_nightly-0.84.0.dev20250729.dist-info/LICENSE,sha256=wbnfEnXnafPbqwANHkV6LUsPKOtdpsd-SNw37rogLtc,11359
1354
+ zenml_nightly-0.84.0.dev20250729.dist-info/METADATA,sha256=0K0W8CmHG1-krP1zg_a1s4uvVVAsJy2TjFAvCo-cG9k,24296
1355
+ zenml_nightly-0.84.0.dev20250729.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
1356
+ zenml_nightly-0.84.0.dev20250729.dist-info/entry_points.txt,sha256=QK3ETQE0YswAM2mWypNMOv8TLtr7EjnqAFq1br_jEFE,43
1357
+ zenml_nightly-0.84.0.dev20250729.dist-info/RECORD,,