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.
- zenml/VERSION +1 -1
- zenml/artifact_stores/base_artifact_store.py +51 -23
- zenml/artifacts/utils.py +3 -1
- zenml/cli/login.py +141 -18
- zenml/cli/pipeline.py +13 -2
- zenml/cli/project.py +8 -6
- zenml/cli/utils.py +63 -16
- zenml/client.py +4 -1
- zenml/config/compiler.py +1 -0
- zenml/config/retry_config.py +5 -3
- zenml/config/step_configurations.py +7 -1
- zenml/console.py +4 -1
- zenml/constants.py +3 -1
- zenml/container_registries/base_container_registry.py +17 -5
- zenml/enums.py +13 -4
- zenml/integrations/aws/flavors/sagemaker_orchestrator_flavor.py +150 -117
- zenml/integrations/aws/flavors/sagemaker_step_operator_flavor.py +43 -42
- zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +16 -7
- zenml/integrations/azure/orchestrators/azureml_orchestrator.py +18 -12
- zenml/integrations/bentoml/flavors/bentoml_model_deployer_flavor.py +7 -1
- zenml/integrations/databricks/flavors/databricks_orchestrator_flavor.py +58 -23
- zenml/integrations/feast/flavors/feast_feature_store_flavor.py +18 -5
- zenml/integrations/gcp/flavors/vertex_experiment_tracker_flavor.py +10 -42
- zenml/integrations/gcp/flavors/vertex_orchestrator_flavor.py +99 -92
- zenml/integrations/gcp/google_credentials_mixin.py +13 -8
- zenml/integrations/gcp/orchestrators/vertex_orchestrator.py +18 -9
- zenml/integrations/huggingface/__init__.py +1 -1
- zenml/integrations/hyperai/flavors/hyperai_orchestrator_flavor.py +28 -30
- zenml/integrations/kaniko/flavors/kaniko_image_builder_flavor.py +56 -40
- zenml/integrations/kubeflow/flavors/kubeflow_orchestrator_flavor.py +59 -48
- zenml/integrations/kubernetes/flavors/kubernetes_orchestrator_flavor.py +189 -97
- zenml/integrations/kubernetes/flavors/kubernetes_step_operator_flavor.py +48 -33
- zenml/integrations/kubernetes/orchestrators/kube_utils.py +172 -0
- zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py +219 -24
- zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint.py +98 -24
- zenml/integrations/kubernetes/orchestrators/manifest_utils.py +59 -0
- zenml/integrations/lightning/flavors/lightning_orchestrator_flavor.py +41 -25
- zenml/integrations/mlflow/flavors/mlflow_experiment_tracker_flavor.py +51 -44
- zenml/integrations/mlflow/flavors/mlflow_model_deployer_flavor.py +9 -4
- zenml/integrations/neptune/flavors/neptune_experiment_tracker_flavor.py +13 -12
- zenml/integrations/s3/flavors/s3_artifact_store_flavor.py +32 -7
- zenml/integrations/vllm/flavors/vllm_model_deployer_flavor.py +7 -1
- zenml/integrations/wandb/flavors/wandb_experiment_tracker_flavor.py +34 -25
- zenml/integrations/whylogs/flavors/whylogs_data_validator_flavor.py +14 -11
- zenml/logger.py +6 -4
- zenml/logging/step_logging.py +8 -7
- zenml/login/web_login.py +13 -6
- zenml/models/v2/core/model_version.py +9 -1
- zenml/models/v2/core/pipeline_run.py +1 -59
- zenml/models/v2/core/step_run.py +35 -1
- zenml/orchestrators/base_orchestrator.py +70 -9
- zenml/orchestrators/dag_runner.py +3 -1
- zenml/orchestrators/publish_utils.py +4 -1
- zenml/orchestrators/step_launcher.py +77 -139
- zenml/orchestrators/step_run_utils.py +16 -0
- zenml/orchestrators/step_runner.py +1 -4
- zenml/pipelines/build_utils.py +2 -1
- zenml/pipelines/pipeline_decorator.py +6 -1
- zenml/pipelines/pipeline_definition.py +7 -0
- zenml/stack/authentication_mixin.py +6 -5
- zenml/stack/flavor.py +5 -1
- zenml/utils/code_utils.py +2 -1
- zenml/utils/docker_utils.py +22 -0
- zenml/utils/io_utils.py +18 -0
- zenml/utils/pipeline_docker_image_builder.py +4 -1
- zenml/utils/run_utils.py +101 -8
- zenml/zen_server/auth.py +0 -1
- zenml/zen_server/deploy/daemon/daemon_zen_server.py +4 -0
- zenml/zen_server/deploy/docker/docker_zen_server.py +2 -0
- zenml/zen_server/routers/runs_endpoints.py +20 -28
- zenml/zen_stores/migrations/versions/360fa84718bf_step_run_versioning.py +64 -0
- zenml/zen_stores/migrations/versions/85289fea86ff_adding_source_to_logs.py +1 -1
- zenml/zen_stores/schemas/pipeline_deployment_schemas.py +21 -0
- zenml/zen_stores/schemas/pipeline_run_schemas.py +31 -2
- zenml/zen_stores/schemas/step_run_schemas.py +41 -17
- zenml/zen_stores/sql_zen_store.py +152 -32
- zenml/zen_stores/template_utils.py +29 -9
- zenml_nightly-0.83.1.dev20250711.dist-info/METADATA +486 -0
- {zenml_nightly-0.83.1.dev20250709.dist-info → zenml_nightly-0.83.1.dev20250711.dist-info}/RECORD +82 -81
- zenml_nightly-0.83.1.dev20250709.dist-info/METADATA +0 -538
- {zenml_nightly-0.83.1.dev20250709.dist-info → zenml_nightly-0.83.1.dev20250711.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.83.1.dev20250709.dist-info → zenml_nightly-0.83.1.dev20250711.dist-info}/WHEEL +0 -0
- {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.
|
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
|
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
|
-
|
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.
|
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
|
527
|
-
|
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,
|
899
|
-
|
900
|
-
|
901
|
-
|
902
|
-
|
903
|
-
|
904
|
-
|
905
|
-
|
906
|
-
|
907
|
-
|
908
|
-
|
909
|
-
|
910
|
-
|
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
|
-
|
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
|
-
|
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.
|
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.
|
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.
|
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.
|
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
|
-
|
166
|
+
StyledClickException: when called.
|
166
167
|
"""
|
167
|
-
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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=
|
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,
|