zenml-nightly 0.70.0.dev20241128__py3-none-any.whl → 0.70.0.dev20241129__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/artifacts/artifact_config.py +21 -1
- zenml/artifacts/utils.py +5 -1
- zenml/cli/pipeline.py +80 -0
- zenml/config/compiler.py +12 -3
- zenml/config/pipeline_configurations.py +20 -0
- zenml/config/pipeline_run_configuration.py +1 -0
- zenml/config/step_configurations.py +21 -0
- zenml/enums.py +1 -0
- zenml/integrations/feast/__init__.py +1 -1
- zenml/integrations/feast/feature_stores/feast_feature_store.py +13 -9
- zenml/materializers/built_in_materializer.py +18 -1
- zenml/materializers/structured_string_materializer.py +8 -3
- zenml/model/model.py +6 -2
- zenml/models/v2/core/pipeline_run.py +4 -0
- zenml/models/v2/core/step_run.py +1 -1
- zenml/orchestrators/publish_utils.py +1 -1
- zenml/orchestrators/step_launcher.py +6 -2
- zenml/orchestrators/step_run_utils.py +15 -6
- zenml/orchestrators/step_runner.py +39 -1
- zenml/orchestrators/utils.py +5 -2
- zenml/pipelines/pipeline_decorator.py +4 -0
- zenml/pipelines/pipeline_definition.py +14 -3
- zenml/pipelines/run_utils.py +8 -3
- zenml/steps/base_step.py +11 -1
- zenml/steps/entrypoint_function_utils.py +4 -2
- zenml/steps/step_decorator.py +4 -0
- zenml/steps/utils.py +17 -5
- zenml/types.py +4 -0
- zenml/utils/string_utils.py +30 -12
- zenml/utils/visualization_utils.py +4 -1
- zenml/zen_server/template_execution/utils.py +1 -0
- zenml/zen_stores/schemas/artifact_schemas.py +2 -1
- zenml/zen_stores/schemas/pipeline_run_schemas.py +14 -3
- zenml/zen_stores/schemas/step_run_schemas.py +19 -0
- zenml/zen_stores/sql_zen_store.py +15 -11
- {zenml_nightly-0.70.0.dev20241128.dist-info → zenml_nightly-0.70.0.dev20241129.dist-info}/METADATA +1 -1
- {zenml_nightly-0.70.0.dev20241128.dist-info → zenml_nightly-0.70.0.dev20241129.dist-info}/RECORD +41 -41
- {zenml_nightly-0.70.0.dev20241128.dist-info → zenml_nightly-0.70.0.dev20241129.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.70.0.dev20241128.dist-info → zenml_nightly-0.70.0.dev20241129.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.70.0.dev20241128.dist-info → zenml_nightly-0.70.0.dev20241129.dist-info}/entry_points.txt +0 -0
zenml/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.70.0.
|
1
|
+
0.70.0.dev20241129
|
@@ -21,6 +21,7 @@ from zenml.enums import ArtifactType
|
|
21
21
|
from zenml.logger import get_logger
|
22
22
|
from zenml.metadata.metadata_types import MetadataType
|
23
23
|
from zenml.utils.pydantic_utils import before_validator_handler
|
24
|
+
from zenml.utils.string_utils import format_name_template
|
24
25
|
|
25
26
|
logger = get_logger(__name__)
|
26
27
|
|
@@ -45,7 +46,13 @@ class ArtifactConfig(BaseModel):
|
|
45
46
|
```
|
46
47
|
|
47
48
|
Attributes:
|
48
|
-
name: The name of the artifact
|
49
|
+
name: The name of the artifact:
|
50
|
+
- static string e.g. "name"
|
51
|
+
- dynamic string e.g. "name_{date}_{time}_{custom_placeholder}"
|
52
|
+
If you use any placeholders besides `date` and `time`,
|
53
|
+
you need to provide the values for them in the `substitutions`
|
54
|
+
argument of the step decorator or the `substitutions` argument
|
55
|
+
of `with_options` of the step.
|
49
56
|
version: The version of the artifact.
|
50
57
|
tags: The tags of the artifact.
|
51
58
|
run_metadata: Metadata to add to the artifact.
|
@@ -111,3 +118,16 @@ class ArtifactConfig(BaseModel):
|
|
111
118
|
data.setdefault("artifact_type", ArtifactType.SERVICE)
|
112
119
|
|
113
120
|
return data
|
121
|
+
|
122
|
+
def _evaluated_name(self, substitutions: Dict[str, str]) -> Optional[str]:
|
123
|
+
"""Evaluated name of the artifact.
|
124
|
+
|
125
|
+
Args:
|
126
|
+
substitutions: Extra placeholders to use in the name template.
|
127
|
+
|
128
|
+
Returns:
|
129
|
+
The evaluated name of the artifact.
|
130
|
+
"""
|
131
|
+
if self.name:
|
132
|
+
return format_name_template(self.name, substitutions=substitutions)
|
133
|
+
return self.name
|
zenml/artifacts/utils.py
CHANGED
@@ -689,7 +689,11 @@ def _link_artifact_version_to_the_step_and_model(
|
|
689
689
|
client.zen_store.update_run_step(
|
690
690
|
step_run_id=step_run.id,
|
691
691
|
step_run_update=StepRunUpdate(
|
692
|
-
outputs={
|
692
|
+
outputs={
|
693
|
+
artifact_version.artifact.name: [
|
694
|
+
artifact_version.id,
|
695
|
+
]
|
696
|
+
}
|
693
697
|
),
|
694
698
|
)
|
695
699
|
error_message = "model"
|
zenml/cli/pipeline.py
CHANGED
@@ -315,6 +315,86 @@ def run_pipeline(
|
|
315
315
|
pipeline_instance()
|
316
316
|
|
317
317
|
|
318
|
+
@pipeline.command(
|
319
|
+
"create-run-template",
|
320
|
+
help="Create a run template for a pipeline. The SOURCE argument needs to "
|
321
|
+
"be an importable source path resolving to a ZenML pipeline instance, e.g. "
|
322
|
+
"`my_module.my_pipeline_instance`.",
|
323
|
+
)
|
324
|
+
@click.argument("source")
|
325
|
+
@click.option(
|
326
|
+
"--name",
|
327
|
+
"-n",
|
328
|
+
type=str,
|
329
|
+
required=True,
|
330
|
+
help="Name for the template",
|
331
|
+
)
|
332
|
+
@click.option(
|
333
|
+
"--config",
|
334
|
+
"-c",
|
335
|
+
"config_path",
|
336
|
+
type=click.Path(exists=True, dir_okay=False),
|
337
|
+
required=False,
|
338
|
+
help="Path to configuration file for the build.",
|
339
|
+
)
|
340
|
+
@click.option(
|
341
|
+
"--stack",
|
342
|
+
"-s",
|
343
|
+
"stack_name_or_id",
|
344
|
+
type=str,
|
345
|
+
required=False,
|
346
|
+
help="Name or ID of the stack to use for the build.",
|
347
|
+
)
|
348
|
+
def create_run_template(
|
349
|
+
source: str,
|
350
|
+
name: str,
|
351
|
+
config_path: Optional[str] = None,
|
352
|
+
stack_name_or_id: Optional[str] = None,
|
353
|
+
) -> None:
|
354
|
+
"""Create a run template for a pipeline.
|
355
|
+
|
356
|
+
Args:
|
357
|
+
source: Importable source resolving to a pipeline instance.
|
358
|
+
name: Name of the run template.
|
359
|
+
config_path: Path to pipeline configuration file.
|
360
|
+
stack_name_or_id: Name or ID of the stack for which the template should
|
361
|
+
be created.
|
362
|
+
"""
|
363
|
+
if not Client().root:
|
364
|
+
cli_utils.warning(
|
365
|
+
"You're running the `zenml pipeline create-run-template` command "
|
366
|
+
"without a ZenML repository. Your current working directory will "
|
367
|
+
"be used as the source root relative to which the registered step "
|
368
|
+
"classes will be resolved. To silence this warning, run `zenml "
|
369
|
+
"init` at your source code root."
|
370
|
+
)
|
371
|
+
|
372
|
+
try:
|
373
|
+
pipeline_instance = source_utils.load(source)
|
374
|
+
except ModuleNotFoundError as e:
|
375
|
+
source_root = source_utils.get_source_root()
|
376
|
+
cli_utils.error(
|
377
|
+
f"Unable to import module `{e.name}`. Make sure the source path is "
|
378
|
+
f"relative to your source root `{source_root}`."
|
379
|
+
)
|
380
|
+
except AttributeError as e:
|
381
|
+
cli_utils.error("Unable to load attribute from module: " + str(e))
|
382
|
+
|
383
|
+
if not isinstance(pipeline_instance, Pipeline):
|
384
|
+
cli_utils.error(
|
385
|
+
f"The given source path `{source}` does not resolve to a pipeline "
|
386
|
+
"object."
|
387
|
+
)
|
388
|
+
|
389
|
+
with cli_utils.temporary_active_stack(stack_name_or_id=stack_name_or_id):
|
390
|
+
pipeline_instance = pipeline_instance.with_options(
|
391
|
+
config_path=config_path
|
392
|
+
)
|
393
|
+
template = pipeline_instance.create_run_template(name=name)
|
394
|
+
|
395
|
+
cli_utils.declare(f"Created run template `{template.id}`.")
|
396
|
+
|
397
|
+
|
318
398
|
@pipeline.command("list", help="List all registered pipelines.")
|
319
399
|
@list_options(PipelineFilter)
|
320
400
|
def list_pipelines(**kwargs: Any) -> None:
|
zenml/config/compiler.py
CHANGED
@@ -99,7 +99,10 @@ class Compiler:
|
|
99
99
|
|
100
100
|
self._apply_stack_default_settings(pipeline=pipeline, stack=stack)
|
101
101
|
if run_configuration.run_name:
|
102
|
-
self._verify_run_name(
|
102
|
+
self._verify_run_name(
|
103
|
+
run_configuration.run_name,
|
104
|
+
pipeline.configuration.substitutions,
|
105
|
+
)
|
103
106
|
|
104
107
|
pipeline_settings = self._filter_and_validate_settings(
|
105
108
|
settings=pipeline.configuration.settings,
|
@@ -305,16 +308,22 @@ class Compiler:
|
|
305
308
|
return default_settings
|
306
309
|
|
307
310
|
@staticmethod
|
308
|
-
def _verify_run_name(
|
311
|
+
def _verify_run_name(
|
312
|
+
run_name: str,
|
313
|
+
substitutions: Dict[str, str],
|
314
|
+
) -> None:
|
309
315
|
"""Verifies that the run name contains only valid placeholders.
|
310
316
|
|
311
317
|
Args:
|
312
318
|
run_name: The run name to verify.
|
319
|
+
substitutions: The substitutions to be used in the run name.
|
313
320
|
|
314
321
|
Raises:
|
315
322
|
ValueError: If the run name contains invalid placeholders.
|
316
323
|
"""
|
317
|
-
valid_placeholder_names = {"date", "time"}
|
324
|
+
valid_placeholder_names = {"date", "time"}.union(
|
325
|
+
set(substitutions.keys())
|
326
|
+
)
|
318
327
|
placeholders = {
|
319
328
|
v[1] for v in string.Formatter().parse(run_name) if v[1]
|
320
329
|
}
|
@@ -13,6 +13,7 @@
|
|
13
13
|
# permissions and limitations under the License.
|
14
14
|
"""Pipeline configuration classes."""
|
15
15
|
|
16
|
+
from datetime import datetime
|
16
17
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
17
18
|
|
18
19
|
from pydantic import SerializeAsAny, field_validator
|
@@ -46,6 +47,25 @@ class PipelineConfigurationUpdate(StrictBaseModel):
|
|
46
47
|
model: Optional[Model] = None
|
47
48
|
parameters: Optional[Dict[str, Any]] = None
|
48
49
|
retry: Optional[StepRetryConfig] = None
|
50
|
+
substitutions: Dict[str, str] = {}
|
51
|
+
|
52
|
+
def _get_full_substitutions(
|
53
|
+
self, start_time: Optional[datetime]
|
54
|
+
) -> Dict[str, str]:
|
55
|
+
"""Returns the full substitutions dict.
|
56
|
+
|
57
|
+
Args:
|
58
|
+
start_time: Start time of the pipeline run.
|
59
|
+
|
60
|
+
Returns:
|
61
|
+
The full substitutions dict including date and time.
|
62
|
+
"""
|
63
|
+
if start_time is None:
|
64
|
+
start_time = datetime.utcnow()
|
65
|
+
ret = self.substitutions.copy()
|
66
|
+
ret.setdefault("date", start_time.strftime("%Y_%m_%d"))
|
67
|
+
ret.setdefault("time", start_time.strftime("%H_%M_%S_%f"))
|
68
|
+
return ret
|
49
69
|
|
50
70
|
|
51
71
|
class PipelineConfiguration(PipelineConfigurationUpdate):
|
@@ -13,6 +13,7 @@
|
|
13
13
|
# permissions and limitations under the License.
|
14
14
|
"""Pipeline configuration classes."""
|
15
15
|
|
16
|
+
from datetime import datetime
|
16
17
|
from typing import (
|
17
18
|
TYPE_CHECKING,
|
18
19
|
Any,
|
@@ -49,6 +50,7 @@ from zenml.utils.pydantic_utils import before_validator_handler
|
|
49
50
|
|
50
51
|
if TYPE_CHECKING:
|
51
52
|
from zenml.config import DockerSettings, ResourceSettings
|
53
|
+
from zenml.config.pipeline_configurations import PipelineConfiguration
|
52
54
|
|
53
55
|
logger = get_logger(__name__)
|
54
56
|
|
@@ -152,6 +154,7 @@ class StepConfigurationUpdate(StrictBaseModel):
|
|
152
154
|
success_hook_source: Optional[SourceWithValidator] = None
|
153
155
|
model: Optional[Model] = None
|
154
156
|
retry: Optional[StepRetryConfig] = None
|
157
|
+
substitutions: Dict[str, str] = {}
|
155
158
|
|
156
159
|
outputs: Mapping[str, PartialArtifactConfiguration] = {}
|
157
160
|
|
@@ -237,6 +240,24 @@ class StepConfiguration(PartialStepConfiguration):
|
|
237
240
|
model_or_dict = model_or_dict.model_dump()
|
238
241
|
return DockerSettings.model_validate(model_or_dict)
|
239
242
|
|
243
|
+
def _get_full_substitutions(
|
244
|
+
self,
|
245
|
+
pipeline_config: "PipelineConfiguration",
|
246
|
+
start_time: Optional[datetime],
|
247
|
+
) -> Dict[str, str]:
|
248
|
+
"""Get the full set of substitutions for this step configuration.
|
249
|
+
|
250
|
+
Args:
|
251
|
+
pipeline_config: The pipeline configuration.
|
252
|
+
start_time: The start time of the pipeline run.
|
253
|
+
|
254
|
+
Returns:
|
255
|
+
The full set of substitutions for this step configuration.
|
256
|
+
"""
|
257
|
+
ret = pipeline_config._get_full_substitutions(start_time)
|
258
|
+
ret.update(self.substitutions)
|
259
|
+
return ret
|
260
|
+
|
240
261
|
|
241
262
|
class InputSpec(StrictBaseModel):
|
242
263
|
"""Step input specification."""
|
zenml/enums.py
CHANGED
@@ -31,7 +31,7 @@ class FeastIntegration(Integration):
|
|
31
31
|
|
32
32
|
NAME = FEAST
|
33
33
|
# click is added to keep the feast click version in sync with ZenML's click
|
34
|
-
REQUIREMENTS = ["feast", "click>=8.0.1,<8.1.4"]
|
34
|
+
REQUIREMENTS = ["feast>=0.12.0", "click>=8.0.1,<8.1.4"]
|
35
35
|
REQUIREMENTS_IGNORED_ON_UNINSTALL = ["click", "pandas"]
|
36
36
|
|
37
37
|
@classmethod
|
@@ -16,7 +16,7 @@
|
|
16
16
|
from typing import Any, Dict, List, Union, cast
|
17
17
|
|
18
18
|
import pandas as pd
|
19
|
-
from feast import FeatureStore # type: ignore
|
19
|
+
from feast import FeatureService, FeatureStore # type: ignore
|
20
20
|
from feast.infra.registry.base_registry import BaseRegistry # type: ignore
|
21
21
|
|
22
22
|
from zenml.feature_stores.base_feature_store import BaseFeatureStore
|
@@ -43,14 +43,14 @@ class FeastFeatureStore(BaseFeatureStore):
|
|
43
43
|
def get_historical_features(
|
44
44
|
self,
|
45
45
|
entity_df: Union[pd.DataFrame, str],
|
46
|
-
features: List[str],
|
46
|
+
features: Union[List[str], FeatureService],
|
47
47
|
full_feature_names: bool = False,
|
48
48
|
) -> pd.DataFrame:
|
49
49
|
"""Returns the historical features for training or batch scoring.
|
50
50
|
|
51
51
|
Args:
|
52
52
|
entity_df: The entity DataFrame or entity name.
|
53
|
-
features: The features to retrieve.
|
53
|
+
features: The features to retrieve or a FeatureService.
|
54
54
|
full_feature_names: Whether to return the full feature names.
|
55
55
|
|
56
56
|
Raise:
|
@@ -70,14 +70,14 @@ class FeastFeatureStore(BaseFeatureStore):
|
|
70
70
|
def get_online_features(
|
71
71
|
self,
|
72
72
|
entity_rows: List[Dict[str, Any]],
|
73
|
-
features: List[str],
|
73
|
+
features: Union[List[str], FeatureService],
|
74
74
|
full_feature_names: bool = False,
|
75
75
|
) -> Dict[str, Any]:
|
76
76
|
"""Returns the latest online feature data.
|
77
77
|
|
78
78
|
Args:
|
79
79
|
entity_rows: The entity rows to retrieve.
|
80
|
-
features: The features to retrieve.
|
80
|
+
features: The features to retrieve or a FeatureService.
|
81
81
|
full_feature_names: Whether to return the full feature names.
|
82
82
|
|
83
83
|
Raise:
|
@@ -118,17 +118,21 @@ class FeastFeatureStore(BaseFeatureStore):
|
|
118
118
|
fs = FeatureStore(repo_path=self.config.feast_repo)
|
119
119
|
return [ds.name for ds in fs.list_entities()]
|
120
120
|
|
121
|
-
def get_feature_services(self) -> List[
|
122
|
-
"""Returns the feature
|
121
|
+
def get_feature_services(self) -> List[FeatureService]:
|
122
|
+
"""Returns the feature services.
|
123
123
|
|
124
124
|
Raise:
|
125
125
|
ConnectionError: If the online component (Redis) is not available.
|
126
126
|
|
127
127
|
Returns:
|
128
|
-
The feature
|
128
|
+
The feature services.
|
129
129
|
"""
|
130
130
|
fs = FeatureStore(repo_path=self.config.feast_repo)
|
131
|
-
|
131
|
+
feature_services: List[FeatureService] = list(
|
132
|
+
fs.list_feature_services()
|
133
|
+
)
|
134
|
+
|
135
|
+
return feature_services
|
132
136
|
|
133
137
|
def get_feature_views(self) -> List[str]:
|
134
138
|
"""Returns the feature view names.
|
@@ -28,7 +28,7 @@ from typing import (
|
|
28
28
|
)
|
29
29
|
|
30
30
|
from zenml.artifact_stores.base_artifact_store import BaseArtifactStore
|
31
|
-
from zenml.enums import ArtifactType
|
31
|
+
from zenml.enums import ArtifactType, VisualizationType
|
32
32
|
from zenml.logger import get_logger
|
33
33
|
from zenml.materializers.base_materializer import BaseMaterializer
|
34
34
|
from zenml.materializers.materializer_registry import materializer_registry
|
@@ -415,6 +415,23 @@ class BuiltInContainerMaterializer(BaseMaterializer):
|
|
415
415
|
self.artifact_store.rmtree(entry["path"])
|
416
416
|
raise e
|
417
417
|
|
418
|
+
# save dict type objects to JSON file with JSON visualization type
|
419
|
+
def save_visualizations(self, data: Any) -> Dict[str, "VisualizationType"]:
|
420
|
+
"""Save visualizations for the given data.
|
421
|
+
|
422
|
+
Args:
|
423
|
+
data: The data to save visualizations for.
|
424
|
+
|
425
|
+
Returns:
|
426
|
+
A dictionary of visualization URIs and their types.
|
427
|
+
"""
|
428
|
+
# dict/list type objects are always saved as JSON files
|
429
|
+
# doesn't work for non-serializable types as they
|
430
|
+
# are saved as list of lists in different files
|
431
|
+
if _is_serializable(data):
|
432
|
+
return {self.data_path: VisualizationType.JSON}
|
433
|
+
return {}
|
434
|
+
|
418
435
|
def extract_metadata(self, data: Any) -> Dict[str, "MetadataType"]:
|
419
436
|
"""Extract metadata from the given built-in container object.
|
420
437
|
|
@@ -19,22 +19,23 @@ from typing import Dict, Type, Union
|
|
19
19
|
from zenml.enums import ArtifactType, VisualizationType
|
20
20
|
from zenml.logger import get_logger
|
21
21
|
from zenml.materializers.base_materializer import BaseMaterializer
|
22
|
-
from zenml.types import CSVString, HTMLString, MarkdownString
|
22
|
+
from zenml.types import CSVString, HTMLString, JSONString, MarkdownString
|
23
23
|
|
24
24
|
logger = get_logger(__name__)
|
25
25
|
|
26
26
|
|
27
|
-
STRUCTURED_STRINGS = Union[CSVString, HTMLString, MarkdownString]
|
27
|
+
STRUCTURED_STRINGS = Union[CSVString, HTMLString, MarkdownString, JSONString]
|
28
28
|
|
29
29
|
HTML_FILENAME = "output.html"
|
30
30
|
MARKDOWN_FILENAME = "output.md"
|
31
31
|
CSV_FILENAME = "output.csv"
|
32
|
+
JSON_FILENAME = "output.json"
|
32
33
|
|
33
34
|
|
34
35
|
class StructuredStringMaterializer(BaseMaterializer):
|
35
36
|
"""Materializer for HTML or Markdown strings."""
|
36
37
|
|
37
|
-
ASSOCIATED_TYPES = (CSVString, HTMLString, MarkdownString)
|
38
|
+
ASSOCIATED_TYPES = (CSVString, HTMLString, MarkdownString, JSONString)
|
38
39
|
ASSOCIATED_ARTIFACT_TYPE = ArtifactType.DATA_ANALYSIS
|
39
40
|
|
40
41
|
def load(self, data_type: Type[STRUCTURED_STRINGS]) -> STRUCTURED_STRINGS:
|
@@ -94,6 +95,8 @@ class StructuredStringMaterializer(BaseMaterializer):
|
|
94
95
|
filename = HTML_FILENAME
|
95
96
|
elif issubclass(data_type, MarkdownString):
|
96
97
|
filename = MARKDOWN_FILENAME
|
98
|
+
elif issubclass(data_type, JSONString):
|
99
|
+
filename = JSON_FILENAME
|
97
100
|
else:
|
98
101
|
raise ValueError(
|
99
102
|
f"Data type {data_type} is not supported by this materializer."
|
@@ -120,6 +123,8 @@ class StructuredStringMaterializer(BaseMaterializer):
|
|
120
123
|
return VisualizationType.HTML
|
121
124
|
elif issubclass(data_type, MarkdownString):
|
122
125
|
return VisualizationType.MARKDOWN
|
126
|
+
elif issubclass(data_type, JSONString):
|
127
|
+
return VisualizationType.JSON
|
123
128
|
else:
|
124
129
|
raise ValueError(
|
125
130
|
f"Data type {data_type} is not supported by this materializer."
|
zenml/model/model.py
CHANGED
@@ -57,7 +57,9 @@ class Model(BaseModel):
|
|
57
57
|
ethics: The ethical implications of the model.
|
58
58
|
tags: Tags associated with the model.
|
59
59
|
version: The version name, version number or stage is optional and points model context
|
60
|
-
to a specific version/stage. If skipped new version will be created.
|
60
|
+
to a specific version/stage. If skipped new version will be created. `version`
|
61
|
+
also supports placeholders: standard `{date}` and `{time}` and any custom placeholders
|
62
|
+
that are passed as substitutions in the pipeline or step decorators.
|
61
63
|
save_models_to_registry: Whether to save all ModelArtifacts to Model Registry,
|
62
64
|
if available in active stack.
|
63
65
|
"""
|
@@ -534,6 +536,8 @@ class Model(BaseModel):
|
|
534
536
|
from zenml.models import ModelRequest
|
535
537
|
|
536
538
|
zenml_client = Client()
|
539
|
+
# backup logic, if the Model class is used directly from the code
|
540
|
+
self.name = format_name_template(self.name, substitutions={})
|
537
541
|
if self.model_version_id:
|
538
542
|
mv = zenml_client.get_model_version(
|
539
543
|
model_version_name_or_number_or_id=self.model_version_id,
|
@@ -663,7 +667,7 @@ class Model(BaseModel):
|
|
663
667
|
|
664
668
|
# backup logic, if the Model class is used directly from the code
|
665
669
|
if isinstance(self.version, str):
|
666
|
-
self.version = format_name_template(self.version)
|
670
|
+
self.version = format_name_template(self.version, substitutions={})
|
667
671
|
|
668
672
|
try:
|
669
673
|
if self.version or self.model_version_id:
|
@@ -237,6 +237,10 @@ class PipelineRunResponseMetadata(WorkspaceScopedResponseMetadata):
|
|
237
237
|
default=False,
|
238
238
|
description="Whether a template can be created from this run.",
|
239
239
|
)
|
240
|
+
steps_substitutions: Dict[str, Dict[str, str]] = Field(
|
241
|
+
title="Substitutions used in the step runs of this pipeline run.",
|
242
|
+
default_factory=dict,
|
243
|
+
)
|
240
244
|
|
241
245
|
|
242
246
|
class PipelineRunResponseResources(WorkspaceScopedResponseResources):
|
zenml/models/v2/core/step_run.py
CHANGED
@@ -142,7 +142,7 @@ class StepRunRequest(WorkspaceScopedRequest):
|
|
142
142
|
class StepRunUpdate(BaseModel):
|
143
143
|
"""Update model for step runs."""
|
144
144
|
|
145
|
-
outputs: Dict[str, UUID] = Field(
|
145
|
+
outputs: Dict[str, List[UUID]] = Field(
|
146
146
|
title="The IDs of the output artifact versions of the step run.",
|
147
147
|
default={},
|
148
148
|
)
|
@@ -32,7 +32,7 @@ if TYPE_CHECKING:
|
|
32
32
|
|
33
33
|
|
34
34
|
def publish_successful_step_run(
|
35
|
-
step_run_id: "UUID", output_artifact_ids: Dict[str, "UUID"]
|
35
|
+
step_run_id: "UUID", output_artifact_ids: Dict[str, List["UUID"]]
|
36
36
|
) -> "StepRunResponse":
|
37
37
|
"""Publishes a successful step run.
|
38
38
|
|
@@ -309,8 +309,12 @@ class StepLauncher:
|
|
309
309
|
The created or existing pipeline run,
|
310
310
|
and a boolean indicating whether the run was created or reused.
|
311
311
|
"""
|
312
|
+
start_time = datetime.utcnow()
|
312
313
|
run_name = orchestrator_utils.get_run_name(
|
313
|
-
run_name_template=self._deployment.run_name_template
|
314
|
+
run_name_template=self._deployment.run_name_template,
|
315
|
+
substitutions=self._deployment.pipeline_configuration._get_full_substitutions(
|
316
|
+
start_time
|
317
|
+
),
|
314
318
|
)
|
315
319
|
|
316
320
|
logger.debug("Creating pipeline run %s", run_name)
|
@@ -329,7 +333,7 @@ class StepLauncher:
|
|
329
333
|
),
|
330
334
|
status=ExecutionStatus.RUNNING,
|
331
335
|
orchestrator_environment=get_run_environment_dict(),
|
332
|
-
start_time=
|
336
|
+
start_time=start_time,
|
333
337
|
tags=self._deployment.pipeline_configuration.tags,
|
334
338
|
)
|
335
339
|
return client.zen_store.get_or_create_run(pipeline_run)
|
@@ -354,13 +354,16 @@ def create_cached_step_runs(
|
|
354
354
|
|
355
355
|
|
356
356
|
def get_or_create_model_version_for_pipeline_run(
|
357
|
-
model: "Model",
|
357
|
+
model: "Model",
|
358
|
+
pipeline_run: PipelineRunResponse,
|
359
|
+
substitutions: Dict[str, str],
|
358
360
|
) -> Tuple[ModelVersionResponse, bool]:
|
359
361
|
"""Get or create a model version as part of a pipeline run.
|
360
362
|
|
361
363
|
Args:
|
362
364
|
model: The model to get or create.
|
363
365
|
pipeline_run: The pipeline run for which the model should be created.
|
366
|
+
substitutions: Substitutions to apply to the model version name.
|
364
367
|
|
365
368
|
Returns:
|
366
369
|
The model version and a boolean indicating whether it was newly created
|
@@ -374,12 +377,14 @@ def get_or_create_model_version_for_pipeline_run(
|
|
374
377
|
return model._get_model_version(), False
|
375
378
|
elif model.version:
|
376
379
|
if isinstance(model.version, str):
|
377
|
-
start_time = pipeline_run.start_time or datetime.utcnow()
|
378
380
|
model.version = string_utils.format_name_template(
|
379
381
|
model.version,
|
380
|
-
|
381
|
-
time=start_time.strftime("%H_%M_%S_%f"),
|
382
|
+
substitutions=substitutions,
|
382
383
|
)
|
384
|
+
model.name = string_utils.format_name_template(
|
385
|
+
model.name,
|
386
|
+
substitutions=substitutions,
|
387
|
+
)
|
383
388
|
|
384
389
|
return (
|
385
390
|
model._get_or_create_model_version(),
|
@@ -460,7 +465,9 @@ def prepare_pipeline_run_model_version(
|
|
460
465
|
model_version = pipeline_run.model_version
|
461
466
|
elif config_model := pipeline_run.config.model:
|
462
467
|
model_version, _ = get_or_create_model_version_for_pipeline_run(
|
463
|
-
model=config_model,
|
468
|
+
model=config_model,
|
469
|
+
pipeline_run=pipeline_run,
|
470
|
+
substitutions=pipeline_run.config.substitutions,
|
464
471
|
)
|
465
472
|
pipeline_run = Client().zen_store.update_run(
|
466
473
|
run_id=pipeline_run.id,
|
@@ -492,7 +499,9 @@ def prepare_step_run_model_version(
|
|
492
499
|
model_version = step_run.model_version
|
493
500
|
elif config_model := step_run.config.model:
|
494
501
|
model_version, created = get_or_create_model_version_for_pipeline_run(
|
495
|
-
model=config_model,
|
502
|
+
model=config_model,
|
503
|
+
pipeline_run=pipeline_run,
|
504
|
+
substitutions=step_run.config.substitutions,
|
496
505
|
)
|
497
506
|
step_run = Client().zen_store.update_run_step(
|
498
507
|
step_run_id=step_run.id,
|
@@ -152,6 +152,15 @@ class StepRunner:
|
|
152
152
|
func=step_instance.entrypoint
|
153
153
|
)
|
154
154
|
|
155
|
+
self._evaluate_artifact_names_in_collections(
|
156
|
+
step_run,
|
157
|
+
output_annotations,
|
158
|
+
[
|
159
|
+
output_artifact_uris,
|
160
|
+
output_materializers,
|
161
|
+
],
|
162
|
+
)
|
163
|
+
|
155
164
|
self._stack.prepare_step_run(info=step_run_info)
|
156
165
|
|
157
166
|
# Initialize the step context singleton
|
@@ -257,7 +266,9 @@ class StepRunner:
|
|
257
266
|
|
258
267
|
# Update the status and output artifacts of the step run.
|
259
268
|
output_artifact_ids = {
|
260
|
-
output_name:
|
269
|
+
output_name: [
|
270
|
+
artifact.id,
|
271
|
+
]
|
261
272
|
for output_name, artifact in output_artifacts.items()
|
262
273
|
}
|
263
274
|
publish_successful_step_run(
|
@@ -265,6 +276,33 @@ class StepRunner:
|
|
265
276
|
output_artifact_ids=output_artifact_ids,
|
266
277
|
)
|
267
278
|
|
279
|
+
def _evaluate_artifact_names_in_collections(
|
280
|
+
self,
|
281
|
+
step_run: "StepRunResponse",
|
282
|
+
output_annotations: Dict[str, OutputSignature],
|
283
|
+
collections: List[Dict[str, Any]],
|
284
|
+
) -> None:
|
285
|
+
"""Evaluates the artifact names in the collections.
|
286
|
+
|
287
|
+
Args:
|
288
|
+
step_run: The step run.
|
289
|
+
output_annotations: The output annotations of the step function
|
290
|
+
(also evaluated).
|
291
|
+
collections: The collections to evaluate.
|
292
|
+
"""
|
293
|
+
collections.append(output_annotations)
|
294
|
+
for k, v in list(output_annotations.items()):
|
295
|
+
_evaluated_name = None
|
296
|
+
if v.artifact_config:
|
297
|
+
_evaluated_name = v.artifact_config._evaluated_name(
|
298
|
+
step_run.config.substitutions
|
299
|
+
)
|
300
|
+
if _evaluated_name is None:
|
301
|
+
_evaluated_name = k
|
302
|
+
|
303
|
+
for d in collections:
|
304
|
+
d[_evaluated_name] = d.pop(k)
|
305
|
+
|
268
306
|
def _load_step(self) -> "BaseStep":
|
269
307
|
"""Load the step instance.
|
270
308
|
|
zenml/orchestrators/utils.py
CHANGED
@@ -196,11 +196,12 @@ def get_config_environment_vars(
|
|
196
196
|
return environment_vars
|
197
197
|
|
198
198
|
|
199
|
-
def get_run_name(run_name_template: str) -> str:
|
199
|
+
def get_run_name(run_name_template: str, substitutions: Dict[str, str]) -> str:
|
200
200
|
"""Fill out the run name template to get a complete run name.
|
201
201
|
|
202
202
|
Args:
|
203
203
|
run_name_template: The run name template to fill out.
|
204
|
+
substitutions: The substitutions to use in the template.
|
204
205
|
|
205
206
|
Raises:
|
206
207
|
ValueError: If the run name is empty.
|
@@ -208,7 +209,9 @@ def get_run_name(run_name_template: str) -> str:
|
|
208
209
|
Returns:
|
209
210
|
The run name derived from the template.
|
210
211
|
"""
|
211
|
-
run_name = format_name_template(
|
212
|
+
run_name = format_name_template(
|
213
|
+
run_name_template, substitutions=substitutions
|
214
|
+
)
|
212
215
|
|
213
216
|
if run_name == "":
|
214
217
|
raise ValueError("Empty run names are not allowed.")
|