zenml-nightly 0.70.0.dev20241127__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/__init__.py +1 -0
- zenml/integrations/constants.py +1 -0
- zenml/integrations/feast/__init__.py +1 -1
- zenml/integrations/feast/feature_stores/feast_feature_store.py +13 -9
- zenml/integrations/kubernetes/orchestrators/kube_utils.py +46 -2
- zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py +13 -2
- zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint.py +3 -1
- zenml/integrations/kubernetes/orchestrators/manifest_utils.py +3 -2
- zenml/integrations/kubernetes/step_operators/kubernetes_step_operator.py +3 -1
- zenml/integrations/modal/__init__.py +46 -0
- zenml/integrations/modal/flavors/__init__.py +26 -0
- zenml/integrations/modal/flavors/modal_step_operator_flavor.py +125 -0
- zenml/integrations/modal/step_operators/__init__.py +22 -0
- zenml/integrations/modal/step_operators/modal_step_operator.py +242 -0
- 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 +40 -2
- zenml/orchestrators/utils.py +29 -4
- 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.dev20241127.dist-info → zenml_nightly-0.70.0.dev20241129.dist-info}/METADATA +1 -1
- {zenml_nightly-0.70.0.dev20241127.dist-info → zenml_nightly-0.70.0.dev20241129.dist-info}/RECORD +53 -48
- {zenml_nightly-0.70.0.dev20241127.dist-info → zenml_nightly-0.70.0.dev20241129.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.70.0.dev20241127.dist-info → zenml_nightly-0.70.0.dev20241129.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.70.0.dev20241127.dist-info → zenml_nightly-0.70.0.dev20241129.dist-info}/entry_points.txt +0 -0
zenml/orchestrators/utils.py
CHANGED
@@ -40,7 +40,9 @@ if TYPE_CHECKING:
|
|
40
40
|
from zenml.artifact_stores.base_artifact_store import BaseArtifactStore
|
41
41
|
|
42
42
|
|
43
|
-
def get_orchestrator_run_name(
|
43
|
+
def get_orchestrator_run_name(
|
44
|
+
pipeline_name: str, max_length: Optional[int] = None
|
45
|
+
) -> str:
|
44
46
|
"""Gets an orchestrator run name.
|
45
47
|
|
46
48
|
This run name is not the same as the ZenML run name but can instead be
|
@@ -48,11 +50,31 @@ def get_orchestrator_run_name(pipeline_name: str) -> str:
|
|
48
50
|
|
49
51
|
Args:
|
50
52
|
pipeline_name: Name of the pipeline that will run.
|
53
|
+
max_length: Maximum length of the generated name.
|
54
|
+
|
55
|
+
Raises:
|
56
|
+
ValueError: If the max length is below 8 characters.
|
51
57
|
|
52
58
|
Returns:
|
53
59
|
The orchestrator run name.
|
54
60
|
"""
|
55
|
-
|
61
|
+
suffix_length = 32
|
62
|
+
pipeline_name = f"{pipeline_name}_"
|
63
|
+
|
64
|
+
if max_length:
|
65
|
+
if max_length < 8:
|
66
|
+
raise ValueError(
|
67
|
+
"Maximum length for orchestrator run name must be 8 or above."
|
68
|
+
)
|
69
|
+
|
70
|
+
# Make sure we always have a certain suffix to guarantee no overlap
|
71
|
+
# with other runs
|
72
|
+
suffix_length = min(32, max(8, max_length - len(pipeline_name)))
|
73
|
+
pipeline_name = pipeline_name[: (max_length - suffix_length)]
|
74
|
+
|
75
|
+
suffix = "".join(random.choices("0123456789abcdef", k=suffix_length))
|
76
|
+
|
77
|
+
return f"{pipeline_name}{suffix}"
|
56
78
|
|
57
79
|
|
58
80
|
def is_setting_enabled(
|
@@ -174,11 +196,12 @@ def get_config_environment_vars(
|
|
174
196
|
return environment_vars
|
175
197
|
|
176
198
|
|
177
|
-
def get_run_name(run_name_template: str) -> str:
|
199
|
+
def get_run_name(run_name_template: str, substitutions: Dict[str, str]) -> str:
|
178
200
|
"""Fill out the run name template to get a complete run name.
|
179
201
|
|
180
202
|
Args:
|
181
203
|
run_name_template: The run name template to fill out.
|
204
|
+
substitutions: The substitutions to use in the template.
|
182
205
|
|
183
206
|
Raises:
|
184
207
|
ValueError: If the run name is empty.
|
@@ -186,7 +209,9 @@ def get_run_name(run_name_template: str) -> str:
|
|
186
209
|
Returns:
|
187
210
|
The run name derived from the template.
|
188
211
|
"""
|
189
|
-
run_name = format_name_template(
|
212
|
+
run_name = format_name_template(
|
213
|
+
run_name_template, substitutions=substitutions
|
214
|
+
)
|
190
215
|
|
191
216
|
if run_name == "":
|
192
217
|
raise ValueError("Empty run names are not allowed.")
|
@@ -55,6 +55,7 @@ def pipeline(
|
|
55
55
|
on_failure: Optional["HookSpecification"] = None,
|
56
56
|
on_success: Optional["HookSpecification"] = None,
|
57
57
|
model: Optional["Model"] = None,
|
58
|
+
substitutions: Optional[Dict[str, str]] = None,
|
58
59
|
) -> Callable[["F"], "Pipeline"]: ...
|
59
60
|
|
60
61
|
|
@@ -71,6 +72,7 @@ def pipeline(
|
|
71
72
|
on_failure: Optional["HookSpecification"] = None,
|
72
73
|
on_success: Optional["HookSpecification"] = None,
|
73
74
|
model: Optional["Model"] = None,
|
75
|
+
substitutions: Optional[Dict[str, str]] = None,
|
74
76
|
) -> Union["Pipeline", Callable[["F"], "Pipeline"]]:
|
75
77
|
"""Decorator to create a pipeline.
|
76
78
|
|
@@ -91,6 +93,7 @@ def pipeline(
|
|
91
93
|
function with no arguments, or a source path to such a function
|
92
94
|
(e.g. `module.my_function`).
|
93
95
|
model: configuration of the model in the Model Control Plane.
|
96
|
+
substitutions: Extra placeholders to use in the name templates.
|
94
97
|
|
95
98
|
Returns:
|
96
99
|
A pipeline instance.
|
@@ -111,6 +114,7 @@ def pipeline(
|
|
111
114
|
on_success=on_success,
|
112
115
|
model=model,
|
113
116
|
entrypoint=func,
|
117
|
+
substitutions=substitutions,
|
114
118
|
)
|
115
119
|
|
116
120
|
p.__doc__ = func.__doc__
|
@@ -135,6 +135,7 @@ class Pipeline:
|
|
135
135
|
on_failure: Optional["HookSpecification"] = None,
|
136
136
|
on_success: Optional["HookSpecification"] = None,
|
137
137
|
model: Optional["Model"] = None,
|
138
|
+
substitutions: Optional[Dict[str, str]] = None,
|
138
139
|
) -> None:
|
139
140
|
"""Initializes a pipeline.
|
140
141
|
|
@@ -157,6 +158,7 @@ class Pipeline:
|
|
157
158
|
be a function with no arguments, or a source path to such a
|
158
159
|
function (e.g. `module.my_function`).
|
159
160
|
model: configuration of the model in the Model Control Plane.
|
161
|
+
substitutions: Extra placeholders to use in the name templates.
|
160
162
|
"""
|
161
163
|
self._invocations: Dict[str, StepInvocation] = {}
|
162
164
|
self._run_args: Dict[str, Any] = {}
|
@@ -177,6 +179,7 @@ class Pipeline:
|
|
177
179
|
on_failure=on_failure,
|
178
180
|
on_success=on_success,
|
179
181
|
model=model,
|
182
|
+
substitutions=substitutions,
|
180
183
|
)
|
181
184
|
self.entrypoint = entrypoint
|
182
185
|
self._parameters: Dict[str, Any] = {}
|
@@ -297,6 +300,7 @@ class Pipeline:
|
|
297
300
|
model: Optional["Model"] = None,
|
298
301
|
parameters: Optional[Dict[str, Any]] = None,
|
299
302
|
merge: bool = True,
|
303
|
+
substitutions: Optional[Dict[str, str]] = None,
|
300
304
|
) -> Self:
|
301
305
|
"""Configures the pipeline.
|
302
306
|
|
@@ -333,6 +337,7 @@ class Pipeline:
|
|
333
337
|
method for an example.
|
334
338
|
model: configuration of the model version in the Model Control Plane.
|
335
339
|
parameters: input parameters for the pipeline.
|
340
|
+
substitutions: Extra placeholders to use in the name templates.
|
336
341
|
|
337
342
|
Returns:
|
338
343
|
The pipeline instance that this method was called on.
|
@@ -365,6 +370,7 @@ class Pipeline:
|
|
365
370
|
"success_hook_source": success_hook_source,
|
366
371
|
"model": model,
|
367
372
|
"parameters": parameters,
|
373
|
+
"substitutions": substitutions,
|
368
374
|
}
|
369
375
|
)
|
370
376
|
if not self.__suppress_warnings_flag__:
|
@@ -577,6 +583,7 @@ To avoid this consider setting pipeline parameters only in one place (config or
|
|
577
583
|
config_path: Optional[str] = None,
|
578
584
|
unlisted: bool = False,
|
579
585
|
prevent_build_reuse: bool = False,
|
586
|
+
skip_schedule_registration: bool = False,
|
580
587
|
) -> PipelineDeploymentResponse:
|
581
588
|
"""Create a pipeline deployment.
|
582
589
|
|
@@ -603,6 +610,7 @@ To avoid this consider setting pipeline parameters only in one place (config or
|
|
603
610
|
to any pipeline).
|
604
611
|
prevent_build_reuse: DEPRECATED: Use
|
605
612
|
`DockerSettings.prevent_build_reuse` instead.
|
613
|
+
skip_schedule_registration: Whether to skip schedule registration.
|
606
614
|
|
607
615
|
Returns:
|
608
616
|
The pipeline deployment.
|
@@ -643,7 +651,7 @@ To avoid this consider setting pipeline parameters only in one place (config or
|
|
643
651
|
stack.validate()
|
644
652
|
|
645
653
|
schedule_id = None
|
646
|
-
if schedule:
|
654
|
+
if schedule and not skip_schedule_registration:
|
647
655
|
if not stack.orchestrator.config.is_schedulable:
|
648
656
|
raise ValueError(
|
649
657
|
f"Stack {stack.name} does not support scheduling. "
|
@@ -656,7 +664,8 @@ To avoid this consider setting pipeline parameters only in one place (config or
|
|
656
664
|
schedule_name = schedule.name
|
657
665
|
else:
|
658
666
|
schedule_name = format_name_template(
|
659
|
-
deployment.run_name_template
|
667
|
+
deployment.run_name_template,
|
668
|
+
substitutions=deployment.pipeline_configuration.substitutions,
|
660
669
|
)
|
661
670
|
components = Client().active_stack_model.components
|
662
671
|
orchestrator = components[StackComponentType.ORCHESTRATOR][0]
|
@@ -1438,7 +1447,9 @@ To avoid this consider setting pipeline parameters only in one place (config or
|
|
1438
1447
|
The created run template.
|
1439
1448
|
"""
|
1440
1449
|
self._prepare_if_possible()
|
1441
|
-
deployment = self._create_deployment(
|
1450
|
+
deployment = self._create_deployment(
|
1451
|
+
**self._run_args, skip_schedule_registration=True
|
1452
|
+
)
|
1442
1453
|
|
1443
1454
|
return Client().create_run_template(
|
1444
1455
|
name=name, deployment_id=deployment.id, **kwargs
|
zenml/pipelines/run_utils.py
CHANGED
@@ -66,16 +66,21 @@ def create_placeholder_run(
|
|
66
66
|
|
67
67
|
if deployment.schedule:
|
68
68
|
return None
|
69
|
-
|
69
|
+
start_time = datetime.utcnow()
|
70
70
|
run_request = PipelineRunRequest(
|
71
|
-
name=get_run_name(
|
71
|
+
name=get_run_name(
|
72
|
+
run_name_template=deployment.run_name_template,
|
73
|
+
substitutions=deployment.pipeline_configuration._get_full_substitutions(
|
74
|
+
start_time
|
75
|
+
),
|
76
|
+
),
|
72
77
|
# We set the start time on the placeholder run already to
|
73
78
|
# make it consistent with the {time} placeholder in the
|
74
79
|
# run name. This means the placeholder run will usually
|
75
80
|
# have longer durations than scheduled runs, as for them
|
76
81
|
# the start_time is only set once the first step starts
|
77
82
|
# running.
|
78
|
-
start_time=
|
83
|
+
start_time=start_time,
|
79
84
|
orchestrator_run_id=None,
|
80
85
|
user=deployment.user.id,
|
81
86
|
workspace=deployment.workspace.id,
|
zenml/steps/base_step.py
CHANGED
@@ -116,6 +116,7 @@ class BaseStep:
|
|
116
116
|
on_success: Optional["HookSpecification"] = None,
|
117
117
|
model: Optional["Model"] = None,
|
118
118
|
retry: Optional[StepRetryConfig] = None,
|
119
|
+
substitutions: Optional[Dict[str, str]] = None,
|
119
120
|
) -> None:
|
120
121
|
"""Initializes a step.
|
121
122
|
|
@@ -144,11 +145,13 @@ class BaseStep:
|
|
144
145
|
function (e.g. `module.my_function`).
|
145
146
|
model: configuration of the model version in the Model Control Plane.
|
146
147
|
retry: Configuration for retrying the step in case of failure.
|
148
|
+
substitutions: Extra placeholders to use in the name template.
|
147
149
|
"""
|
148
150
|
from zenml.config.step_configurations import PartialStepConfiguration
|
149
151
|
|
150
152
|
self.entrypoint_definition = validate_entrypoint_function(
|
151
|
-
self.entrypoint,
|
153
|
+
self.entrypoint,
|
154
|
+
reserved_arguments=["after", "id"],
|
152
155
|
)
|
153
156
|
|
154
157
|
name = name or self.__class__.__name__
|
@@ -203,6 +206,7 @@ class BaseStep:
|
|
203
206
|
on_success=on_success,
|
204
207
|
model=model,
|
205
208
|
retry=retry,
|
209
|
+
substitutions=substitutions,
|
206
210
|
)
|
207
211
|
|
208
212
|
notebook_utils.try_to_save_notebook_cell_code(self.source_object)
|
@@ -595,6 +599,7 @@ class BaseStep:
|
|
595
599
|
model: Optional["Model"] = None,
|
596
600
|
merge: bool = True,
|
597
601
|
retry: Optional[StepRetryConfig] = None,
|
602
|
+
substitutions: Optional[Dict[str, str]] = None,
|
598
603
|
) -> T:
|
599
604
|
"""Configures the step.
|
600
605
|
|
@@ -637,6 +642,7 @@ class BaseStep:
|
|
637
642
|
overwrite all existing ones. See the general description of this
|
638
643
|
method for an example.
|
639
644
|
retry: Configuration for retrying the step in case of failure.
|
645
|
+
substitutions: Extra placeholders to use in the name template.
|
640
646
|
|
641
647
|
Returns:
|
642
648
|
The step instance that this method was called on.
|
@@ -701,6 +707,7 @@ class BaseStep:
|
|
701
707
|
"success_hook_source": success_hook_source,
|
702
708
|
"model": model,
|
703
709
|
"retry": retry,
|
710
|
+
"substitutions": substitutions,
|
704
711
|
}
|
705
712
|
)
|
706
713
|
config = StepConfigurationUpdate(**values)
|
@@ -725,6 +732,7 @@ class BaseStep:
|
|
725
732
|
on_success: Optional["HookSpecification"] = None,
|
726
733
|
model: Optional["Model"] = None,
|
727
734
|
merge: bool = True,
|
735
|
+
substitutions: Optional[Dict[str, str]] = None,
|
728
736
|
) -> "BaseStep":
|
729
737
|
"""Copies the step and applies the given configurations.
|
730
738
|
|
@@ -756,6 +764,7 @@ class BaseStep:
|
|
756
764
|
configurations. If `False` the given configurations will
|
757
765
|
overwrite all existing ones. See the general description of this
|
758
766
|
method for an example.
|
767
|
+
substitutions: Extra placeholders for the step name.
|
759
768
|
|
760
769
|
Returns:
|
761
770
|
The copied step instance.
|
@@ -776,6 +785,7 @@ class BaseStep:
|
|
776
785
|
on_success=on_success,
|
777
786
|
model=model,
|
778
787
|
merge=merge,
|
788
|
+
substitutions=substitutions,
|
779
789
|
)
|
780
790
|
return step_copy
|
781
791
|
|
@@ -211,7 +211,8 @@ class EntrypointFunctionDefinition(NamedTuple):
|
|
211
211
|
|
212
212
|
|
213
213
|
def validate_entrypoint_function(
|
214
|
-
func: Callable[..., Any],
|
214
|
+
func: Callable[..., Any],
|
215
|
+
reserved_arguments: Sequence[str] = (),
|
215
216
|
) -> EntrypointFunctionDefinition:
|
216
217
|
"""Validates a step entrypoint function.
|
217
218
|
|
@@ -258,7 +259,8 @@ def validate_entrypoint_function(
|
|
258
259
|
inputs[key] = parameter
|
259
260
|
|
260
261
|
outputs = parse_return_type_annotations(
|
261
|
-
func=func,
|
262
|
+
func=func,
|
263
|
+
enforce_type_annotations=ENFORCE_TYPE_ANNOTATIONS,
|
262
264
|
)
|
263
265
|
|
264
266
|
return EntrypointFunctionDefinition(
|
zenml/steps/step_decorator.py
CHANGED
@@ -73,6 +73,7 @@ def step(
|
|
73
73
|
on_success: Optional["HookSpecification"] = None,
|
74
74
|
model: Optional["Model"] = None,
|
75
75
|
retry: Optional["StepRetryConfig"] = None,
|
76
|
+
substitutions: Optional[Dict[str, str]] = None,
|
76
77
|
) -> Callable[["F"], "BaseStep"]: ...
|
77
78
|
|
78
79
|
|
@@ -93,6 +94,7 @@ def step(
|
|
93
94
|
on_success: Optional["HookSpecification"] = None,
|
94
95
|
model: Optional["Model"] = None,
|
95
96
|
retry: Optional["StepRetryConfig"] = None,
|
97
|
+
substitutions: Optional[Dict[str, str]] = None,
|
96
98
|
) -> Union["BaseStep", Callable[["F"], "BaseStep"]]:
|
97
99
|
"""Decorator to create a ZenML step.
|
98
100
|
|
@@ -124,6 +126,7 @@ def step(
|
|
124
126
|
(e.g. `module.my_function`).
|
125
127
|
model: configuration of the model in the Model Control Plane.
|
126
128
|
retry: configuration of step retry in case of step failure.
|
129
|
+
substitutions: Extra placeholders for the step name.
|
127
130
|
|
128
131
|
Returns:
|
129
132
|
The step instance.
|
@@ -157,6 +160,7 @@ def step(
|
|
157
160
|
on_success=on_success,
|
158
161
|
model=model,
|
159
162
|
retry=retry,
|
163
|
+
substitutions=substitutions,
|
160
164
|
)
|
161
165
|
|
162
166
|
return step_instance
|
zenml/steps/utils.py
CHANGED
@@ -18,7 +18,15 @@ import ast
|
|
18
18
|
import contextlib
|
19
19
|
import inspect
|
20
20
|
import textwrap
|
21
|
-
from typing import
|
21
|
+
from typing import (
|
22
|
+
TYPE_CHECKING,
|
23
|
+
Any,
|
24
|
+
Callable,
|
25
|
+
Dict,
|
26
|
+
Optional,
|
27
|
+
Tuple,
|
28
|
+
Union,
|
29
|
+
)
|
22
30
|
from uuid import UUID
|
23
31
|
|
24
32
|
from pydantic import BaseModel
|
@@ -94,7 +102,8 @@ def get_args(obj: Any) -> Tuple[Any, ...]:
|
|
94
102
|
|
95
103
|
|
96
104
|
def parse_return_type_annotations(
|
97
|
-
func: Callable[..., Any],
|
105
|
+
func: Callable[..., Any],
|
106
|
+
enforce_type_annotations: bool = False,
|
98
107
|
) -> Dict[str, OutputSignature]:
|
99
108
|
"""Parse the return type annotation of a step function.
|
100
109
|
|
@@ -228,9 +237,12 @@ def get_artifact_config_from_annotation_metadata(
|
|
228
237
|
|
229
238
|
error_message = (
|
230
239
|
"Artifact annotation should only contain two elements: the artifact "
|
231
|
-
"type, and
|
232
|
-
"`
|
233
|
-
"`Annotated[int,
|
240
|
+
"type, and one of the following: { a static or dynamic name || "
|
241
|
+
"an `ArtifactConfig` }, e.g.: "
|
242
|
+
"`Annotated[int, 'output_name']` || "
|
243
|
+
"`Annotated[int, 'output_{placeholder}']` || "
|
244
|
+
"`Annotated[int, ArtifactConfig(name='output_{placeholder}')]` ||"
|
245
|
+
"`Annotated[int, ArtifactConfig(name='output_name')]`."
|
234
246
|
)
|
235
247
|
|
236
248
|
if len(metadata) > 2:
|
zenml/types.py
CHANGED
zenml/utils/string_utils.py
CHANGED
@@ -14,10 +14,10 @@
|
|
14
14
|
"""Utils for strings."""
|
15
15
|
|
16
16
|
import base64
|
17
|
-
import datetime
|
18
17
|
import functools
|
19
18
|
import random
|
20
19
|
import string
|
20
|
+
from datetime import datetime
|
21
21
|
from typing import Any, Callable, Dict, TypeVar, cast
|
22
22
|
|
23
23
|
from pydantic import BaseModel
|
@@ -147,7 +147,7 @@ def validate_name(model: BaseModel) -> None:
|
|
147
147
|
|
148
148
|
def format_name_template(
|
149
149
|
name_template: str,
|
150
|
-
|
150
|
+
substitutions: Dict[str, str],
|
151
151
|
) -> str:
|
152
152
|
"""Formats a name template with the given arguments.
|
153
153
|
|
@@ -157,20 +157,38 @@ def format_name_template(
|
|
157
157
|
|
158
158
|
Args:
|
159
159
|
name_template: The name template to format.
|
160
|
-
|
160
|
+
substitutions: A dictionary of substitutions to use in the template.
|
161
161
|
|
162
162
|
Returns:
|
163
163
|
The formatted name template.
|
164
|
+
|
165
|
+
Raises:
|
166
|
+
KeyError: If a key in template is missing in the kwargs.
|
164
167
|
"""
|
165
|
-
|
166
|
-
"
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
168
|
+
if ("date" not in substitutions and "{date}" in name_template) or (
|
169
|
+
"time" not in substitutions and "{time}" in name_template
|
170
|
+
):
|
171
|
+
from zenml import get_step_context
|
172
|
+
|
173
|
+
try:
|
174
|
+
pr = get_step_context().pipeline_run
|
175
|
+
start_time = pr.start_time
|
176
|
+
substitutions.update(pr.config.substitutions)
|
177
|
+
except RuntimeError:
|
178
|
+
start_time = None
|
179
|
+
|
180
|
+
if start_time is None:
|
181
|
+
start_time = datetime.utcnow()
|
182
|
+
substitutions.setdefault("date", start_time.strftime("%Y_%m_%d"))
|
183
|
+
substitutions.setdefault("time", start_time.strftime("%H_%M_%S_%f"))
|
184
|
+
|
185
|
+
try:
|
186
|
+
return name_template.format(**substitutions)
|
187
|
+
except KeyError as e:
|
188
|
+
raise KeyError(
|
189
|
+
f"Could not format the name template `{name_template}`. "
|
190
|
+
f"Missing key: {e}"
|
191
|
+
)
|
174
192
|
|
175
193
|
|
176
194
|
def substitute_string(value: V, substitution_func: Callable[[str], str]) -> V:
|
@@ -13,9 +13,10 @@
|
|
13
13
|
# permissions and limitations under the License.
|
14
14
|
"""Utility functions for dashboard visualizations."""
|
15
15
|
|
16
|
+
import json
|
16
17
|
from typing import TYPE_CHECKING, Optional
|
17
18
|
|
18
|
-
from IPython.core.display import HTML, Image, Markdown, display
|
19
|
+
from IPython.core.display import HTML, JSON, Image, Markdown, display
|
19
20
|
|
20
21
|
from zenml.artifacts.utils import load_artifact_visualization
|
21
22
|
from zenml.enums import VisualizationType
|
@@ -63,6 +64,8 @@ def visualize_artifact(
|
|
63
64
|
assert isinstance(visualization.value, str)
|
64
65
|
table = format_csv_visualization_as_html(visualization.value)
|
65
66
|
display(HTML(table))
|
67
|
+
elif visualization.type == VisualizationType.JSON:
|
68
|
+
display(JSON(json.loads(visualization.value)))
|
66
69
|
else:
|
67
70
|
display(visualization.value)
|
68
71
|
|
@@ -352,8 +352,9 @@ class ArtifactVersionSchema(BaseSchema, table=True):
|
|
352
352
|
producer_step_run_id = step_run.original_step_run_id
|
353
353
|
|
354
354
|
# Create the body of the model
|
355
|
+
artifact = self.artifact.to_model()
|
355
356
|
body = ArtifactVersionResponseBody(
|
356
|
-
artifact=
|
357
|
+
artifact=artifact,
|
357
358
|
version=self.version or str(self.version_number),
|
358
359
|
user=self.user.to_model() if self.user else None,
|
359
360
|
uri=self.uri,
|
@@ -284,6 +284,10 @@ class PipelineRunSchema(NamedSchema, table=True):
|
|
284
284
|
deployment = self.deployment.to_model()
|
285
285
|
|
286
286
|
config = deployment.pipeline_configuration
|
287
|
+
new_substitutions = config._get_full_substitutions(self.start_time)
|
288
|
+
config = config.model_copy(
|
289
|
+
update={"substitutions": new_substitutions}
|
290
|
+
)
|
287
291
|
client_environment = deployment.client_environment
|
288
292
|
|
289
293
|
stack = deployment.stack
|
@@ -323,9 +327,11 @@ class PipelineRunSchema(NamedSchema, table=True):
|
|
323
327
|
build=build,
|
324
328
|
schedule=schedule,
|
325
329
|
code_reference=code_reference,
|
326
|
-
trigger_execution=
|
327
|
-
|
328
|
-
|
330
|
+
trigger_execution=(
|
331
|
+
self.trigger_execution.to_model()
|
332
|
+
if self.trigger_execution
|
333
|
+
else None
|
334
|
+
),
|
329
335
|
created=self.created,
|
330
336
|
updated=self.updated,
|
331
337
|
deployment_id=self.deployment_id,
|
@@ -344,6 +350,10 @@ class PipelineRunSchema(NamedSchema, table=True):
|
|
344
350
|
|
345
351
|
steps = {step.name: step.to_model() for step in self.step_runs}
|
346
352
|
|
353
|
+
steps_substitutions = {
|
354
|
+
step_name: step.config.substitutions
|
355
|
+
for step_name, step in steps.items()
|
356
|
+
}
|
347
357
|
metadata = PipelineRunResponseMetadata(
|
348
358
|
workspace=self.workspace.to_model(),
|
349
359
|
run_metadata=run_metadata,
|
@@ -361,6 +371,7 @@ class PipelineRunSchema(NamedSchema, table=True):
|
|
361
371
|
if self.deployment
|
362
372
|
else None,
|
363
373
|
is_templatable=is_templatable,
|
374
|
+
steps_substitutions=steps_substitutions,
|
364
375
|
)
|
365
376
|
|
366
377
|
resources = None
|
@@ -23,6 +23,7 @@ from sqlalchemy import TEXT, Column, String
|
|
23
23
|
from sqlalchemy.dialects.mysql import MEDIUMTEXT
|
24
24
|
from sqlmodel import Field, Relationship, SQLModel
|
25
25
|
|
26
|
+
from zenml.config.pipeline_configurations import PipelineConfiguration
|
26
27
|
from zenml.config.step_configurations import Step
|
27
28
|
from zenml.constants import MEDIUMTEXT_MAX_LENGTH
|
28
29
|
from zenml.enums import (
|
@@ -163,6 +164,9 @@ class StepRunSchema(NamedSchema, table=True):
|
|
163
164
|
"primaryjoin": "StepRunParentsSchema.child_id == StepRunSchema.id",
|
164
165
|
},
|
165
166
|
)
|
167
|
+
pipeline_run: "PipelineRunSchema" = Relationship(
|
168
|
+
back_populates="step_runs"
|
169
|
+
)
|
166
170
|
model_version: "ModelVersionSchema" = Relationship(
|
167
171
|
back_populates="step_runs",
|
168
172
|
)
|
@@ -248,6 +252,21 @@ class StepRunSchema(NamedSchema, table=True):
|
|
248
252
|
full_step_config = Step.model_validate(
|
249
253
|
step_configuration[self.name]
|
250
254
|
)
|
255
|
+
new_substitutions = (
|
256
|
+
full_step_config.config._get_full_substitutions(
|
257
|
+
PipelineConfiguration.model_validate_json(
|
258
|
+
self.deployment.pipeline_configuration
|
259
|
+
),
|
260
|
+
self.pipeline_run.start_time,
|
261
|
+
)
|
262
|
+
)
|
263
|
+
full_step_config = full_step_config.model_copy(
|
264
|
+
update={
|
265
|
+
"config": full_step_config.config.model_copy(
|
266
|
+
update={"substitutions": new_substitutions}
|
267
|
+
)
|
268
|
+
}
|
269
|
+
)
|
251
270
|
elif not self.step_configuration:
|
252
271
|
raise ValueError(
|
253
272
|
f"Unable to load the configuration for step `{self.name}` from the"
|
@@ -2726,7 +2726,9 @@ class SqlZenStore(BaseZenStore):
|
|
2726
2726
|
# -------------------- Artifact Versions --------------------
|
2727
2727
|
|
2728
2728
|
def _get_or_create_artifact_for_name(
|
2729
|
-
self,
|
2729
|
+
self,
|
2730
|
+
name: str,
|
2731
|
+
has_custom_name: bool,
|
2730
2732
|
) -> ArtifactSchema:
|
2731
2733
|
"""Get or create an artifact with a specific name.
|
2732
2734
|
|
@@ -2747,7 +2749,8 @@ class SqlZenStore(BaseZenStore):
|
|
2747
2749
|
try:
|
2748
2750
|
with session.begin_nested():
|
2749
2751
|
artifact_request = ArtifactRequest(
|
2750
|
-
name=name,
|
2752
|
+
name=name,
|
2753
|
+
has_custom_name=has_custom_name,
|
2751
2754
|
)
|
2752
2755
|
artifact = ArtifactSchema.from_request(
|
2753
2756
|
artifact_request
|
@@ -8182,12 +8185,12 @@ class SqlZenStore(BaseZenStore):
|
|
8182
8185
|
)
|
8183
8186
|
|
8184
8187
|
# Save output artifact IDs into the database.
|
8185
|
-
for
|
8188
|
+
for name, artifact_version_ids in step_run.outputs.items():
|
8186
8189
|
for artifact_version_id in artifact_version_ids:
|
8187
8190
|
self._set_run_step_output_artifact(
|
8188
8191
|
step_run_id=step_schema.id,
|
8189
8192
|
artifact_version_id=artifact_version_id,
|
8190
|
-
name=
|
8193
|
+
name=name,
|
8191
8194
|
session=session,
|
8192
8195
|
)
|
8193
8196
|
|
@@ -8291,13 +8294,14 @@ class SqlZenStore(BaseZenStore):
|
|
8291
8294
|
session.add(existing_step_run)
|
8292
8295
|
|
8293
8296
|
# Update the artifacts.
|
8294
|
-
for name,
|
8295
|
-
|
8296
|
-
|
8297
|
-
|
8298
|
-
|
8299
|
-
|
8300
|
-
|
8297
|
+
for name, artifact_version_ids in step_run_update.outputs.items():
|
8298
|
+
for artifact_version_id in artifact_version_ids:
|
8299
|
+
self._set_run_step_output_artifact(
|
8300
|
+
step_run_id=step_run_id,
|
8301
|
+
artifact_version_id=artifact_version_id,
|
8302
|
+
name=name,
|
8303
|
+
session=session,
|
8304
|
+
)
|
8301
8305
|
|
8302
8306
|
# Update loaded artifacts.
|
8303
8307
|
for (
|