zenml-nightly 0.83.1.dev20250709__py3-none-any.whl → 0.83.1.dev20250711__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 (83) hide show
  1. zenml/VERSION +1 -1
  2. zenml/artifact_stores/base_artifact_store.py +51 -23
  3. zenml/artifacts/utils.py +3 -1
  4. zenml/cli/login.py +141 -18
  5. zenml/cli/pipeline.py +13 -2
  6. zenml/cli/project.py +8 -6
  7. zenml/cli/utils.py +63 -16
  8. zenml/client.py +4 -1
  9. zenml/config/compiler.py +1 -0
  10. zenml/config/retry_config.py +5 -3
  11. zenml/config/step_configurations.py +7 -1
  12. zenml/console.py +4 -1
  13. zenml/constants.py +3 -1
  14. zenml/container_registries/base_container_registry.py +17 -5
  15. zenml/enums.py +13 -4
  16. zenml/integrations/aws/flavors/sagemaker_orchestrator_flavor.py +150 -117
  17. zenml/integrations/aws/flavors/sagemaker_step_operator_flavor.py +43 -42
  18. zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +16 -7
  19. zenml/integrations/azure/orchestrators/azureml_orchestrator.py +18 -12
  20. zenml/integrations/bentoml/flavors/bentoml_model_deployer_flavor.py +7 -1
  21. zenml/integrations/databricks/flavors/databricks_orchestrator_flavor.py +58 -23
  22. zenml/integrations/feast/flavors/feast_feature_store_flavor.py +18 -5
  23. zenml/integrations/gcp/flavors/vertex_experiment_tracker_flavor.py +10 -42
  24. zenml/integrations/gcp/flavors/vertex_orchestrator_flavor.py +99 -92
  25. zenml/integrations/gcp/google_credentials_mixin.py +13 -8
  26. zenml/integrations/gcp/orchestrators/vertex_orchestrator.py +18 -9
  27. zenml/integrations/huggingface/__init__.py +1 -1
  28. zenml/integrations/hyperai/flavors/hyperai_orchestrator_flavor.py +28 -30
  29. zenml/integrations/kaniko/flavors/kaniko_image_builder_flavor.py +56 -40
  30. zenml/integrations/kubeflow/flavors/kubeflow_orchestrator_flavor.py +59 -48
  31. zenml/integrations/kubernetes/flavors/kubernetes_orchestrator_flavor.py +189 -97
  32. zenml/integrations/kubernetes/flavors/kubernetes_step_operator_flavor.py +48 -33
  33. zenml/integrations/kubernetes/orchestrators/kube_utils.py +172 -0
  34. zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py +219 -24
  35. zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint.py +98 -24
  36. zenml/integrations/kubernetes/orchestrators/manifest_utils.py +59 -0
  37. zenml/integrations/lightning/flavors/lightning_orchestrator_flavor.py +41 -25
  38. zenml/integrations/mlflow/flavors/mlflow_experiment_tracker_flavor.py +51 -44
  39. zenml/integrations/mlflow/flavors/mlflow_model_deployer_flavor.py +9 -4
  40. zenml/integrations/neptune/flavors/neptune_experiment_tracker_flavor.py +13 -12
  41. zenml/integrations/s3/flavors/s3_artifact_store_flavor.py +32 -7
  42. zenml/integrations/vllm/flavors/vllm_model_deployer_flavor.py +7 -1
  43. zenml/integrations/wandb/flavors/wandb_experiment_tracker_flavor.py +34 -25
  44. zenml/integrations/whylogs/flavors/whylogs_data_validator_flavor.py +14 -11
  45. zenml/logger.py +6 -4
  46. zenml/logging/step_logging.py +8 -7
  47. zenml/login/web_login.py +13 -6
  48. zenml/models/v2/core/model_version.py +9 -1
  49. zenml/models/v2/core/pipeline_run.py +1 -59
  50. zenml/models/v2/core/step_run.py +35 -1
  51. zenml/orchestrators/base_orchestrator.py +70 -9
  52. zenml/orchestrators/dag_runner.py +3 -1
  53. zenml/orchestrators/publish_utils.py +4 -1
  54. zenml/orchestrators/step_launcher.py +77 -139
  55. zenml/orchestrators/step_run_utils.py +16 -0
  56. zenml/orchestrators/step_runner.py +1 -4
  57. zenml/pipelines/build_utils.py +2 -1
  58. zenml/pipelines/pipeline_decorator.py +6 -1
  59. zenml/pipelines/pipeline_definition.py +7 -0
  60. zenml/stack/authentication_mixin.py +6 -5
  61. zenml/stack/flavor.py +5 -1
  62. zenml/utils/code_utils.py +2 -1
  63. zenml/utils/docker_utils.py +22 -0
  64. zenml/utils/io_utils.py +18 -0
  65. zenml/utils/pipeline_docker_image_builder.py +4 -1
  66. zenml/utils/run_utils.py +101 -8
  67. zenml/zen_server/auth.py +0 -1
  68. zenml/zen_server/deploy/daemon/daemon_zen_server.py +4 -0
  69. zenml/zen_server/deploy/docker/docker_zen_server.py +2 -0
  70. zenml/zen_server/routers/runs_endpoints.py +20 -28
  71. zenml/zen_stores/migrations/versions/360fa84718bf_step_run_versioning.py +64 -0
  72. zenml/zen_stores/migrations/versions/85289fea86ff_adding_source_to_logs.py +1 -1
  73. zenml/zen_stores/schemas/pipeline_deployment_schemas.py +21 -0
  74. zenml/zen_stores/schemas/pipeline_run_schemas.py +31 -2
  75. zenml/zen_stores/schemas/step_run_schemas.py +41 -17
  76. zenml/zen_stores/sql_zen_store.py +152 -32
  77. zenml/zen_stores/template_utils.py +29 -9
  78. zenml_nightly-0.83.1.dev20250711.dist-info/METADATA +486 -0
  79. {zenml_nightly-0.83.1.dev20250709.dist-info → zenml_nightly-0.83.1.dev20250711.dist-info}/RECORD +82 -81
  80. zenml_nightly-0.83.1.dev20250709.dist-info/METADATA +0 -538
  81. {zenml_nightly-0.83.1.dev20250709.dist-info → zenml_nightly-0.83.1.dev20250711.dist-info}/LICENSE +0 -0
  82. {zenml_nightly-0.83.1.dev20250709.dist-info → zenml_nightly-0.83.1.dev20250711.dist-info}/WHEEL +0 -0
  83. {zenml_nightly-0.83.1.dev20250709.dist-info → zenml_nightly-0.83.1.dev20250711.dist-info}/entry_points.txt +0 -0
zenml/VERSION CHANGED
@@ -1 +1 @@
1
- 0.83.1.dev20250709
1
+ 0.83.1.dev20250711
@@ -33,11 +33,15 @@ from typing import (
33
33
  cast,
34
34
  )
35
35
 
36
- from pydantic import model_validator
36
+ from pydantic import Field, model_validator
37
37
 
38
- from zenml.constants import ENV_ZENML_SERVER
38
+ from zenml.constants import (
39
+ ENV_ZENML_SERVER,
40
+ ENV_ZENML_SERVER_ALLOW_LOCAL_FILE_ACCESS,
41
+ handle_bool_env_var,
42
+ )
39
43
  from zenml.enums import StackComponentType
40
- from zenml.exceptions import ArtifactStoreInterfaceError
44
+ from zenml.exceptions import ArtifactStoreInterfaceError, IllegalOperationError
41
45
  from zenml.io import fileio
42
46
  from zenml.logger import get_logger
43
47
  from zenml.stack import Flavor, StackComponent, StackComponentConfig
@@ -73,6 +77,12 @@ class _sanitize_paths:
73
77
  """
74
78
  self.func = func
75
79
  self.fixed_root_path = fixed_root_path
80
+ if ENV_ZENML_SERVER in os.environ:
81
+ self.allow_local_file_access = handle_bool_env_var(
82
+ ENV_ZENML_SERVER_ALLOW_LOCAL_FILE_ACCESS, False
83
+ )
84
+ else:
85
+ self.allow_local_file_access = True
76
86
 
77
87
  self.path_args: List[int] = []
78
88
  self.path_kwargs: List[str] = []
@@ -93,7 +103,15 @@ class _sanitize_paths:
93
103
  Raises:
94
104
  FileNotFoundError: If the path is outside of the artifact store
95
105
  bounds.
106
+ IllegalOperationError: If the path is a local file and the server
107
+ is not configured to allow local file access.
96
108
  """
109
+ if not self.allow_local_file_access and not io_utils.is_remote(path):
110
+ raise IllegalOperationError(
111
+ "Files in a local artifact store cannot be accessed from the "
112
+ "server."
113
+ )
114
+
97
115
  if not path.startswith(self.fixed_root_path):
98
116
  raise FileNotFoundError(
99
117
  f"File `{path}` is outside of "
@@ -169,9 +187,18 @@ class _sanitize_paths:
169
187
 
170
188
 
171
189
  class BaseArtifactStoreConfig(StackComponentConfig):
172
- """Config class for `BaseArtifactStore`."""
190
+ """Config class for `BaseArtifactStore`.
173
191
 
174
- path: str
192
+ Base configuration for artifact storage backends.
193
+ Field descriptions are defined inline using Field() descriptors.
194
+ """
195
+
196
+ path: str = Field(
197
+ description="Root path for artifact storage. Must be a valid URI supported by the "
198
+ "specific artifact store implementation. Examples: 's3://my-bucket/artifacts', "
199
+ "'/local/storage/path', 'gs://bucket-name/zenml-artifacts', 'azure://container/path'. "
200
+ "Path must be accessible with the configured credentials and permissions"
201
+ )
175
202
 
176
203
  SUPPORTED_SCHEMES: ClassVar[Set[str]]
177
204
  IS_IMMUTABLE_FILESYSTEM: ClassVar[bool] = False
@@ -435,40 +462,41 @@ class BaseArtifactStore(StackComponent):
435
462
  **kwargs: The keyword arguments to pass to the Pydantic object.
436
463
  """
437
464
  super(BaseArtifactStore, self).__init__(*args, **kwargs)
465
+ self._add_path_sanitization()
438
466
 
439
467
  # If running in a ZenML server environment, we don't register
440
468
  # the filesystems. We always use the artifact stores directly.
441
469
  if ENV_ZENML_SERVER not in os.environ:
442
470
  self._register()
443
471
 
472
+ def _add_path_sanitization(self) -> None:
473
+ """Add path sanitization to the artifact store."""
474
+ for method_name, method in inspect.getmembers(BaseArtifactStore):
475
+ if getattr(method, "__isabstractmethod__", False):
476
+ method_implementation = getattr(self, method_name)
477
+ sanitized_method = _sanitize_paths(
478
+ method_implementation, self.path
479
+ )
480
+ setattr(self, method_name, sanitized_method)
481
+
444
482
  def _register(self) -> None:
445
483
  """Create and register a filesystem within the filesystem registry."""
446
484
  from zenml.io.filesystem import BaseFilesystem
447
485
  from zenml.io.filesystem_registry import default_filesystem_registry
448
486
  from zenml.io.local_filesystem import LocalFilesystem
449
487
 
450
- overloads: Dict[str, Any] = {
451
- "SUPPORTED_SCHEMES": self.config.SUPPORTED_SCHEMES,
452
- }
453
- for abc_method in inspect.getmembers(BaseArtifactStore):
454
- if getattr(abc_method[1], "__isabstractmethod__", False):
455
- sanitized_method = _sanitize_paths(
456
- getattr(self, abc_method[0]), self.path
457
- )
458
- # prepare overloads for filesystem methods
459
- overloads[abc_method[0]] = staticmethod(sanitized_method)
460
-
461
- # decorate artifact store methods
462
- setattr(
463
- self,
464
- abc_method[0],
465
- sanitized_method,
466
- )
467
-
468
488
  # Local filesystem is always registered, no point in doing it again.
469
489
  if isinstance(self, LocalFilesystem):
470
490
  return
471
491
 
492
+ overloads: Dict[str, Any] = {
493
+ "SUPPORTED_SCHEMES": self.config.SUPPORTED_SCHEMES,
494
+ }
495
+ for method_name, method in inspect.getmembers(BaseArtifactStore):
496
+ if getattr(method, "__isabstractmethod__", False):
497
+ method_implementation = getattr(self, method_name)
498
+ overloads[method_name] = staticmethod(method_implementation)
499
+
472
500
  filesystem_class = type(
473
501
  self.__class__.__name__, (BaseFilesystem,), overloads
474
502
  )
zenml/artifacts/utils.py CHANGED
@@ -49,6 +49,7 @@ from zenml.enums import (
49
49
  )
50
50
  from zenml.exceptions import (
51
51
  DoesNotExistException,
52
+ IllegalOperationError,
52
53
  StepContextError,
53
54
  )
54
55
  from zenml.io import fileio
@@ -925,6 +926,7 @@ def _load_file_from_artifact_store(
925
926
  DoesNotExistException: If the file does not exist in the artifact store.
926
927
  NotImplementedError: If the artifact store cannot open the file.
927
928
  IOError: If the artifact store rejects the request.
929
+ IllegalOperationError: If the artifact store rejects the request.
928
930
  """
929
931
  try:
930
932
  with artifact_store.open(uri, mode) as text_file:
@@ -946,7 +948,7 @@ def _load_file_from_artifact_store(
946
948
  f"File '{uri}' does not exist in artifact store "
947
949
  f"'{artifact_store.name}'."
948
950
  )
949
- except IOError as e:
951
+ except (IOError, IllegalOperationError) as e:
950
952
  raise e
951
953
  except Exception as e:
952
954
  logger.exception(e)
zenml/cli/login.py CHANGED
@@ -22,6 +22,10 @@ from typing import Any, Dict, Optional, Tuple, Union
22
22
  from uuid import UUID
23
23
 
24
24
  import click
25
+ from pydantic import BaseModel
26
+ from rich.panel import Panel
27
+ from rich.prompt import Prompt
28
+ from rich.text import Text
25
29
 
26
30
  from zenml.cli import utils as cli_utils
27
31
  from zenml.cli.cli import cli
@@ -46,6 +50,77 @@ from zenml.utils.server_utils import (
46
50
  logger = get_logger(__name__)
47
51
 
48
52
 
53
+ class LoginMethod(BaseModel):
54
+ """Login method class."""
55
+
56
+ name: str
57
+ description: str
58
+ help: str
59
+
60
+
61
+ possible_login_methods = [
62
+ LoginMethod(
63
+ name="local",
64
+ description="Login to a local server",
65
+ help="(zenml login --local)",
66
+ ),
67
+ LoginMethod(
68
+ name="pro",
69
+ description="Login to ZenML Pro",
70
+ help="(https://cloud.zenml.io)",
71
+ ),
72
+ LoginMethod(
73
+ name="cloud",
74
+ description="Login to a cloud server",
75
+ help="(custom URL)",
76
+ ),
77
+ ]
78
+
79
+
80
+ def _display_login_menu() -> LoginMethod:
81
+ """Display an interactive login menu and return the user's choice.
82
+
83
+ Returns:
84
+ The selected login method enum value.
85
+ """
86
+ title_text = Text("ZenML Login", style="bold cyan")
87
+
88
+ options_text = Text()
89
+ options_text.append("Choose your login method:\n\n", style="dim")
90
+
91
+ for i, login_method in enumerate(possible_login_methods):
92
+ options_text.append(f"{i + 1}. ", style="bold purple")
93
+ options_text.append(
94
+ f"{login_method.description}"
95
+ + " " * (25 - len(login_method.description)),
96
+ style="white",
97
+ )
98
+ options_text.append(f"{login_method.help}", style="dim")
99
+ if i < len(possible_login_methods) - 1:
100
+ options_text.append("\n")
101
+
102
+ panel = Panel(
103
+ options_text,
104
+ title=title_text,
105
+ border_style="white dim",
106
+ padding=(1, 2),
107
+ width=60,
108
+ )
109
+
110
+ console.print(panel)
111
+
112
+ # Get user choice with validation
113
+ while True:
114
+ choice = Prompt.ask(
115
+ "[bold]Enter your choice[/bold]",
116
+ choices=list(
117
+ str(i) for i in range(1, len(possible_login_methods) + 1)
118
+ ),
119
+ default="2",
120
+ )
121
+ return possible_login_methods[int(choice) - 1]
122
+
123
+
49
124
  def start_local_server(
50
125
  docker: bool = False,
51
126
  ip_address: Union[
@@ -424,7 +499,7 @@ def connect_to_pro_server(
424
499
  )
425
500
 
426
501
  cli_utils.declare(
427
- f"Connecting to ZenML Pro server: {server.name} [{str(server.id)}] "
502
+ f"Connecting to ZenML Pro server: '{server.name}' [{str(server.id)}] "
428
503
  )
429
504
 
430
505
  connect_to_server(
@@ -435,7 +510,7 @@ def connect_to_pro_server(
435
510
  # ZenML Pro workspace object.
436
511
  credentials_store.update_server_info(server.url, server)
437
512
 
438
- cli_utils.declare(f"Connected to ZenML Pro server: {server.name}.")
513
+ cli_utils.success(f"Connected to ZenML Pro server: {server.name}.")
439
514
 
440
515
 
441
516
  def is_pro_server(
@@ -523,8 +598,10 @@ def _fail_if_authentication_environment_variables_set() -> None:
523
598
  on the current client state:
524
599
 
525
600
  * if the client is not connected to a non-local ZenML server, the command
526
- will take the user to the ZenML Pro login / signup page to authenticate
527
- and connect to a ZenML Pro server.
601
+ will display an interactive menu with three login options:
602
+ 1. Login to a local server (equivalent to `zenml login --local`)
603
+ 2. Login to ZenML Pro (https://cloud.zenml.io)
604
+ 3. Login to a custom cloud server (you'll be prompted for the URL)
528
605
 
529
606
  * if the client is already connected to a non-local ZenML server, the
530
607
  command triggers a new web login flow with the same server. This allows
@@ -895,20 +972,66 @@ def login(
895
972
  )
896
973
  else:
897
974
  # If no server argument is provided, and the client is not currently
898
- # connected to any non-local server, we default to logging in to ZenML
899
- # Pro.
900
- cli_utils.declare(
901
- "No server argument was provided. Logging to ZenML Pro...\n"
902
- "Hint: You can run 'zenml login --local' to start a local ZenML "
903
- "server and connect to it or 'zenml login <server-url>' to connect "
904
- "to a specific ZenML server. If you wish to login to a ZenML Pro "
905
- "server, you can run 'zenml login --pro'."
906
- )
907
- connect_to_pro_server(
908
- api_key=api_key_value,
909
- pro_api_url=pro_api_url,
910
- verify_ssl=verify_ssl,
911
- )
975
+ # connected to any non-local server, show the interactive login menu.
976
+ login_method = _display_login_menu()
977
+
978
+ if login_method.name == "local":
979
+ # Start a local ZenML server and connect to it
980
+ start_local_server(
981
+ docker=docker,
982
+ ip_address=ip_address,
983
+ port=port,
984
+ blocking=blocking,
985
+ image=image,
986
+ ngrok_token=ngrok_token,
987
+ restart=restart,
988
+ )
989
+ elif login_method.name == "pro":
990
+ # Connect to ZenML Pro
991
+ connect_to_pro_server(
992
+ api_key=api_key_value,
993
+ pro_api_url=pro_api_url,
994
+ verify_ssl=verify_ssl,
995
+ )
996
+ elif login_method.name == "cloud":
997
+ # Get custom server URL from user
998
+ console.print()
999
+ server_url = Prompt.ask(
1000
+ "[bold]Enter the ZenML server URL[/bold]", default="http/https"
1001
+ )
1002
+
1003
+ if not server_url.strip():
1004
+ cli_utils.error("Server URL cannot be empty.")
1005
+ return
1006
+
1007
+ # Validate URL format
1008
+ if not re.match(r"^https?://", server_url):
1009
+ cli_utils.error(
1010
+ "Invalid URL format. Please provide a URL starting with "
1011
+ "http:// or https://"
1012
+ )
1013
+ return
1014
+
1015
+ # Connect to the custom server
1016
+ # First, try to discover if the server is a ZenML Pro server or not
1017
+ server_is_pro, server_pro_api_url = is_pro_server(server_url)
1018
+ if server_is_pro:
1019
+ connect_to_pro_server(
1020
+ pro_server=server_url,
1021
+ api_key=api_key_value,
1022
+ refresh=True, # Force refresh for manually entered URLs
1023
+ # Prefer the pro API URL extracted from the server info if
1024
+ # available
1025
+ pro_api_url=server_pro_api_url or pro_api_url,
1026
+ verify_ssl=verify_ssl,
1027
+ )
1028
+ else:
1029
+ connect_to_server(
1030
+ url=server_url,
1031
+ api_key=api_key_value,
1032
+ verify_ssl=verify_ssl,
1033
+ refresh=True, # Force refresh for manually entered URLs
1034
+ )
912
1035
 
913
1036
 
914
1037
  @cli.command(
zenml/cli/pipeline.py CHANGED
@@ -604,16 +604,27 @@ def delete_pipeline_run(
604
604
 
605
605
  @runs.command("refresh")
606
606
  @click.argument("run_name_or_id", type=str, required=True)
607
- def refresh_pipeline_run(run_name_or_id: str) -> None:
607
+ @click.option(
608
+ "--include-steps",
609
+ is_flag=True,
610
+ default=False,
611
+ help="Also refresh the status of individual steps.",
612
+ )
613
+ def refresh_pipeline_run(
614
+ run_name_or_id: str, include_steps: bool = False
615
+ ) -> None:
608
616
  """Refresh the status of a pipeline run.
609
617
 
610
618
  Args:
611
619
  run_name_or_id: The name or ID of the pipeline run to refresh.
620
+ include_steps: If True, also refresh the status of individual steps.
612
621
  """
613
622
  try:
614
623
  # Fetch and update the run
615
624
  run = Client().get_pipeline_run(name_id_or_prefix=run_name_or_id)
616
- run.refresh_run_status()
625
+ run_utils.refresh_run_status(
626
+ run=run, include_step_updates=include_steps
627
+ )
617
628
 
618
629
  except KeyError as e:
619
630
  cli_utils.error(str(e))
zenml/cli/project.py CHANGED
@@ -109,21 +109,23 @@ def register_project(
109
109
  description="",
110
110
  display_name=display_name,
111
111
  )
112
- cli_utils.declare("Project created successfully.")
112
+ cli_utils.success("Project created successfully.")
113
113
  except Exception as e:
114
114
  cli_utils.error(str(e))
115
115
 
116
116
  if set_project:
117
117
  client.set_active_project(project_name)
118
- cli_utils.declare(f"The active project has been set to {project_name}")
118
+ cli_utils.success(
119
+ f"✔ The active project has been set to {project_name}"
120
+ )
119
121
 
120
122
  if set_default:
121
123
  client.update_user(
122
124
  name_id_or_prefix=client.active_user.id,
123
125
  updated_default_project_id=project.id,
124
126
  )
125
- cli_utils.declare(
126
- f"The default project has been set to {project.name}"
127
+ cli_utils.success(
128
+ f"The default project has been set to {project.name}"
127
129
  )
128
130
 
129
131
 
@@ -147,8 +149,8 @@ def set_project(project_name_or_id: str, default: bool = False) -> None:
147
149
  with console.status("Setting project...\n"):
148
150
  try:
149
151
  project = client.set_active_project(project_name_or_id)
150
- cli_utils.declare(
151
- f"The active project has been set to {project_name_or_id}"
152
+ cli_utils.success(
153
+ f"The active project has been set to {project_name_or_id}"
152
154
  )
153
155
  except Exception as e:
154
156
  cli_utils.error(str(e))
zenml/cli/utils.py CHANGED
@@ -22,6 +22,7 @@ import shutil
22
22
  import subprocess
23
23
  import sys
24
24
  from typing import (
25
+ IO,
25
26
  TYPE_CHECKING,
26
27
  AbstractSet,
27
28
  Any,
@@ -162,9 +163,20 @@ def error(text: str) -> NoReturn:
162
163
  text: Input text string.
163
164
 
164
165
  Raises:
165
- ClickException: when called.
166
+ StyledClickException: when called.
166
167
  """
167
- raise click.ClickException(message=click.style(text, fg="red", bold=True))
168
+ error_prefix = click.style("Error: ", fg="red", bold=True)
169
+ error_message = click.style(text, fg="red", bold=False)
170
+
171
+ # Create a custom ClickException that bypasses Click's default "Error: " prefix
172
+ class StyledClickException(click.ClickException):
173
+ def show(self, file: Optional[IO[Any]] = None) -> None:
174
+ if file is None:
175
+ file = click.get_text_stream("stderr")
176
+ # Print our custom styled message directly without Click's prefix
177
+ click.echo(self.message, file=file)
178
+
179
+ raise StyledClickException(message=error_prefix + error_message)
168
180
 
169
181
 
170
182
  def warning(
@@ -186,6 +198,25 @@ def warning(
186
198
  console.print(text, style=style, **kwargs)
187
199
 
188
200
 
201
+ def success(
202
+ text: str,
203
+ bold: Optional[bool] = None,
204
+ italic: Optional[bool] = None,
205
+ **kwargs: Any,
206
+ ) -> None:
207
+ """Echo a success string on the CLI.
208
+
209
+ Args:
210
+ text: Input text string.
211
+ bold: Optional boolean to bold the text.
212
+ italic: Optional boolean to italicize the text.
213
+ **kwargs: Optional kwargs to be passed to console.print().
214
+ """
215
+ base_style = zenml_style_defaults["success"]
216
+ style = Style.chain(base_style, Style(bold=bold, italic=italic))
217
+ console.print(text, style=style, **kwargs)
218
+
219
+
189
220
  def print_markdown(text: str) -> None:
190
221
  """Prints a string as markdown.
191
222
 
@@ -230,7 +261,11 @@ def print_table(
230
261
  column_keys = {key: None for dict_ in obj for key in dict_}
231
262
  column_names = [columns.get(key, key.upper()) for key in column_keys]
232
263
  rich_table = table.Table(
233
- box=box.HEAVY_EDGE, show_lines=True, title=title, caption=caption
264
+ box=box.ROUNDED,
265
+ show_lines=True,
266
+ title=title,
267
+ caption=caption,
268
+ border_style="dim",
234
269
  )
235
270
  for col_name in column_names:
236
271
  if isinstance(col_name, str):
@@ -434,9 +469,10 @@ def print_pydantic_model(
434
469
  columns: Optionally specify subset and order of columns to display.
435
470
  """
436
471
  rich_table = table.Table(
437
- box=box.HEAVY_EDGE,
472
+ box=box.ROUNDED,
438
473
  title=title,
439
474
  show_lines=True,
475
+ border_style="dim",
440
476
  )
441
477
  rich_table.add_column("PROPERTY", overflow="fold")
442
478
  rich_table.add_column("VALUE", overflow="fold")
@@ -552,10 +588,11 @@ def print_stack_configuration(stack: "StackResponse", active: bool) -> None:
552
588
  if active:
553
589
  stack_caption += " (ACTIVE)"
554
590
  rich_table = table.Table(
555
- box=box.HEAVY_EDGE,
591
+ box=box.ROUNDED,
556
592
  title="Stack Configuration",
557
593
  caption=stack_caption,
558
594
  show_lines=True,
595
+ border_style="dim",
559
596
  )
560
597
  rich_table.add_column("COMPONENT_TYPE", overflow="fold")
561
598
  rich_table.add_column("COMPONENT_NAME", overflow="fold")
@@ -573,9 +610,10 @@ def print_stack_configuration(stack: "StackResponse", active: bool) -> None:
573
610
  declare("No labels are set for this stack.")
574
611
  else:
575
612
  rich_table = table.Table(
576
- box=box.HEAVY_EDGE,
613
+ box=box.ROUNDED,
577
614
  title="Labels",
578
615
  show_lines=True,
616
+ border_style="dim",
579
617
  )
580
618
  rich_table.add_column("LABEL")
581
619
  rich_table.add_column("VALUE", overflow="fold")
@@ -649,9 +687,10 @@ def print_stack_component_configuration(
649
687
  if active_status:
650
688
  title_ += " (ACTIVE)"
651
689
  rich_table = table.Table(
652
- box=box.HEAVY_EDGE,
690
+ box=box.ROUNDED,
653
691
  title=title_,
654
692
  show_lines=True,
693
+ border_style="dim",
655
694
  )
656
695
  rich_table.add_column("COMPONENT_PROPERTY")
657
696
  rich_table.add_column("VALUE", overflow="fold")
@@ -672,9 +711,10 @@ def print_stack_component_configuration(
672
711
  declare("No labels are set for this component.")
673
712
  else:
674
713
  rich_table = table.Table(
675
- box=box.HEAVY_EDGE,
714
+ box=box.ROUNDED,
676
715
  title="Labels",
677
716
  show_lines=True,
717
+ border_style="dim",
678
718
  )
679
719
  rich_table.add_column("LABEL")
680
720
  rich_table.add_column("VALUE", overflow="fold")
@@ -688,9 +728,10 @@ def print_stack_component_configuration(
688
728
  declare("No connector is set for this component.")
689
729
  else:
690
730
  rich_table = table.Table(
691
- box=box.HEAVY_EDGE,
731
+ box=box.ROUNDED,
692
732
  title="Service Connector",
693
733
  show_lines=True,
734
+ border_style="dim",
694
735
  )
695
736
  rich_table.add_column("PROPERTY")
696
737
  rich_table.add_column("VALUE", overflow="fold")
@@ -1165,8 +1206,9 @@ def print_list_items(list_items: List[str], column_title: str) -> None:
1165
1206
  column_title: Title of the column
1166
1207
  """
1167
1208
  rich_table = table.Table(
1168
- box=box.HEAVY_EDGE,
1209
+ box=box.ROUNDED,
1169
1210
  show_lines=True,
1211
+ border_style="dim",
1170
1212
  )
1171
1213
  rich_table.add_column(column_title.upper(), overflow="fold")
1172
1214
  list_items.sort()
@@ -1291,9 +1333,10 @@ def pretty_print_model_version_details(
1291
1333
  title_ = f"Properties of model `{model_version.registered_model.name}` version `{model_version.version}`"
1292
1334
 
1293
1335
  rich_table = table.Table(
1294
- box=box.HEAVY_EDGE,
1336
+ box=box.ROUNDED,
1295
1337
  title=title_,
1296
1338
  show_lines=True,
1339
+ border_style="dim",
1297
1340
  )
1298
1341
  rich_table.add_column("MODEL VERSION PROPERTY", overflow="fold")
1299
1342
  rich_table.add_column("VALUE", overflow="fold")
@@ -1343,9 +1386,10 @@ def print_served_model_configuration(
1343
1386
  title_ = f"Properties of Served Model {model_service.uuid}"
1344
1387
 
1345
1388
  rich_table = table.Table(
1346
- box=box.HEAVY_EDGE,
1389
+ box=box.ROUNDED,
1347
1390
  title=title_,
1348
1391
  show_lines=True,
1392
+ border_style="dim",
1349
1393
  )
1350
1394
  rich_table.add_column("MODEL SERVICE PROPERTY", overflow="fold")
1351
1395
  rich_table.add_column("VALUE", overflow="fold")
@@ -1745,9 +1789,10 @@ def print_service_connector_configuration(
1745
1789
  if active_status:
1746
1790
  title_ += " (ACTIVE)"
1747
1791
  rich_table = table.Table(
1748
- box=box.HEAVY_EDGE,
1792
+ box=box.ROUNDED,
1749
1793
  title=title_,
1750
1794
  show_lines=True,
1795
+ border_style="dim",
1751
1796
  )
1752
1797
  rich_table.add_column("PROPERTY")
1753
1798
  rich_table.add_column("VALUE", overflow="fold")
@@ -1819,9 +1864,10 @@ def print_service_connector_configuration(
1819
1864
 
1820
1865
  else:
1821
1866
  rich_table = table.Table(
1822
- box=box.HEAVY_EDGE,
1867
+ box=box.ROUNDED,
1823
1868
  title="Configuration",
1824
1869
  show_lines=True,
1870
+ border_style="dim",
1825
1871
  )
1826
1872
  rich_table.add_column("PROPERTY")
1827
1873
  rich_table.add_column("VALUE", overflow="fold")
@@ -1847,9 +1893,10 @@ def print_service_connector_configuration(
1847
1893
  return
1848
1894
 
1849
1895
  rich_table = table.Table(
1850
- box=box.HEAVY_EDGE,
1896
+ box=box.ROUNDED,
1851
1897
  title="Labels",
1852
1898
  show_lines=True,
1899
+ border_style="dim",
1853
1900
  )
1854
1901
  rich_table.add_column("LABEL")
1855
1902
  rich_table.add_column("VALUE", overflow="fold")
@@ -2609,7 +2656,7 @@ def multi_choice_prompt(
2609
2656
  table = Table(
2610
2657
  title=f"Available {object_type}",
2611
2658
  show_header=True,
2612
- border_style=None,
2659
+ border_style="dim",
2613
2660
  expand=True,
2614
2661
  show_lines=True,
2615
2662
  )
zenml/client.py CHANGED
@@ -4051,6 +4051,7 @@ class Client(metaclass=ClientMetaClass):
4051
4051
  model_version_id: Optional[Union[str, UUID]] = None,
4052
4052
  model: Optional[Union[UUID, str]] = None,
4053
4053
  run_metadata: Optional[List[str]] = None,
4054
+ exclude_retried: Optional[bool] = None,
4054
4055
  hydrate: bool = False,
4055
4056
  ) -> Page[StepRunResponse]:
4056
4057
  """List all pipelines.
@@ -4077,6 +4078,7 @@ class Client(metaclass=ClientMetaClass):
4077
4078
  code_hash: The code hash of the step run to filter by.
4078
4079
  status: The name of the run to filter by.
4079
4080
  run_metadata: Filter by run metadata.
4081
+ exclude_retried: Whether to exclude retried step runs.
4080
4082
  hydrate: Flag deciding whether to hydrate the output model(s)
4081
4083
  by including metadata fields in the response.
4082
4084
 
@@ -4105,6 +4107,7 @@ class Client(metaclass=ClientMetaClass):
4105
4107
  model_version_id=model_version_id,
4106
4108
  model=model,
4107
4109
  run_metadata=run_metadata,
4110
+ exclude_retried=exclude_retried,
4108
4111
  )
4109
4112
  return self.zen_store.list_run_steps(
4110
4113
  step_run_filter_model=step_run_filter_model,
@@ -4352,7 +4355,7 @@ class Client(metaclass=ClientMetaClass):
4352
4355
  version: Optional[Union[str, int]] = None,
4353
4356
  version_number: Optional[int] = None,
4354
4357
  artifact_store_id: Optional[Union[str, UUID]] = None,
4355
- type: Optional[ArtifactType] = None,
4358
+ type: Optional[Union[ArtifactType, str]] = None,
4356
4359
  data_type: Optional[str] = None,
4357
4360
  uri: Optional[str] = None,
4358
4361
  materializer: Optional[str] = None,
zenml/config/compiler.py CHANGED
@@ -206,6 +206,7 @@ class Compiler:
206
206
  tags=config.tags,
207
207
  extra=config.extra,
208
208
  model=config.model,
209
+ retry=config.retry,
209
210
  parameters=config.parameters,
210
211
  )
211
212