zenml-nightly 0.70.0.dev20241125__py3-none-any.whl → 0.71.0.dev20241220__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.
- README.md +4 -4
- RELEASE_NOTES.md +112 -0
- zenml/VERSION +1 -1
- zenml/artifact_stores/base_artifact_store.py +2 -2
- zenml/artifacts/artifact_config.py +15 -6
- zenml/artifacts/utils.py +59 -32
- zenml/cli/__init__.py +22 -4
- zenml/cli/base.py +5 -5
- zenml/cli/login.py +26 -0
- zenml/cli/pipeline.py +111 -62
- zenml/cli/server.py +20 -20
- zenml/cli/service_connectors.py +3 -3
- zenml/cli/stack.py +0 -3
- zenml/cli/stack_components.py +0 -1
- zenml/cli/utils.py +0 -5
- zenml/client.py +62 -20
- zenml/config/compiler.py +12 -3
- zenml/config/pipeline_configurations.py +20 -0
- zenml/config/pipeline_run_configuration.py +1 -0
- zenml/config/secret_reference_mixin.py +1 -1
- zenml/config/server_config.py +4 -0
- zenml/config/step_configurations.py +21 -0
- zenml/constants.py +10 -0
- zenml/enums.py +1 -0
- zenml/image_builders/base_image_builder.py +5 -2
- zenml/image_builders/build_context.py +7 -16
- zenml/image_builders/local_image_builder.py +13 -3
- zenml/integrations/__init__.py +1 -0
- zenml/integrations/aws/__init__.py +3 -0
- zenml/integrations/aws/flavors/__init__.py +6 -0
- zenml/integrations/aws/flavors/aws_image_builder_flavor.py +146 -0
- zenml/integrations/aws/image_builders/__init__.py +20 -0
- zenml/integrations/aws/image_builders/aws_image_builder.py +307 -0
- zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +14 -6
- 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/gcp/orchestrators/vertex_orchestrator.py +1 -1
- zenml/integrations/kaniko/image_builders/kaniko_image_builder.py +2 -1
- zenml/integrations/kubernetes/flavors/kubernetes_orchestrator_flavor.py +11 -0
- 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 -2
- zenml/integrations/lightning/flavors/lightning_orchestrator_flavor.py +11 -0
- 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/integrations/neptune/experiment_trackers/neptune_experiment_tracker.py +7 -5
- zenml/integrations/neptune/experiment_trackers/run_state.py +69 -53
- zenml/integrations/registry.py +2 -2
- zenml/integrations/skypilot/flavors/skypilot_orchestrator_base_vm_config.py +12 -0
- zenml/integrations/wandb/flavors/wandb_experiment_tracker_flavor.py +13 -5
- zenml/io/filesystem.py +2 -2
- zenml/io/local_filesystem.py +3 -3
- zenml/materializers/built_in_materializer.py +18 -1
- zenml/materializers/structured_string_materializer.py +8 -3
- zenml/model/model.py +23 -101
- zenml/model/utils.py +21 -17
- zenml/models/__init__.py +6 -0
- zenml/models/v2/base/filter.py +26 -30
- zenml/models/v2/base/scoped.py +258 -5
- zenml/models/v2/core/artifact_version.py +21 -29
- zenml/models/v2/core/code_repository.py +1 -12
- zenml/models/v2/core/component.py +5 -68
- zenml/models/v2/core/flavor.py +1 -11
- zenml/models/v2/core/model.py +1 -57
- zenml/models/v2/core/model_version.py +11 -36
- zenml/models/v2/core/model_version_artifact.py +11 -3
- zenml/models/v2/core/model_version_pipeline_run.py +14 -3
- zenml/models/v2/core/pipeline.py +47 -55
- zenml/models/v2/core/pipeline_build.py +67 -12
- zenml/models/v2/core/pipeline_deployment.py +0 -10
- zenml/models/v2/core/pipeline_run.py +110 -32
- zenml/models/v2/core/run_metadata.py +30 -9
- zenml/models/v2/core/run_template.py +21 -29
- zenml/models/v2/core/schedule.py +0 -10
- zenml/models/v2/core/secret.py +0 -14
- zenml/models/v2/core/service.py +9 -16
- zenml/models/v2/core/service_connector.py +0 -11
- zenml/models/v2/core/stack.py +21 -30
- zenml/models/v2/core/step_run.py +24 -18
- zenml/models/v2/core/trigger.py +19 -3
- zenml/models/v2/misc/run_metadata.py +38 -0
- zenml/orchestrators/base_orchestrator.py +13 -1
- zenml/orchestrators/input_utils.py +19 -6
- zenml/orchestrators/output_utils.py +5 -1
- zenml/orchestrators/publish_utils.py +12 -5
- zenml/orchestrators/step_launcher.py +16 -16
- zenml/orchestrators/step_run_utils.py +18 -197
- zenml/orchestrators/step_runner.py +40 -3
- zenml/orchestrators/utils.py +79 -50
- zenml/pipelines/build_utils.py +12 -0
- zenml/pipelines/pipeline_decorator.py +4 -0
- zenml/pipelines/pipeline_definition.py +26 -8
- zenml/pipelines/run_utils.py +9 -5
- zenml/service_connectors/service_connector_utils.py +3 -9
- zenml/stack/stack_component.py +1 -1
- zenml/stack_deployments/aws_stack_deployment.py +22 -0
- zenml/steps/base_step.py +11 -1
- zenml/steps/entrypoint_function_utils.py +7 -3
- zenml/steps/step_decorator.py +4 -0
- zenml/steps/utils.py +23 -7
- zenml/types.py +4 -0
- zenml/utils/archivable.py +65 -36
- zenml/utils/code_utils.py +8 -4
- zenml/utils/docker_utils.py +9 -0
- zenml/utils/metadata_utils.py +186 -153
- zenml/utils/string_utils.py +41 -16
- zenml/utils/visualization_utils.py +4 -1
- zenml/zen_server/auth.py +9 -10
- zenml/zen_server/cloud_utils.py +3 -1
- zenml/zen_server/dashboard/assets/{404-NVXKFp-x.js → 404-Cqu3EDCm.js} +1 -1
- zenml/zen_server/dashboard/assets/{@reactflow-CK0KJUen.js → @reactflow-D2Y7BWwz.js} +1 -1
- zenml/zen_server/dashboard/assets/{AlertDialogDropdownItem-DezXKmDf.js → AlertDialogDropdownItem-BHd71pVS.js} +1 -1
- zenml/zen_server/dashboard/assets/{CodeSnippet-JzR8CEtw.js → CodeSnippet-DIonwetW.js} +1 -1
- zenml/zen_server/dashboard/assets/{CollapsibleCard-DQW_ktMO.js → CollapsibleCard-CDnC97pB.js} +1 -1
- zenml/zen_server/dashboard/assets/{Commands-DL2kwkRd.js → Commands-BVEXKAOj.js} +1 -1
- zenml/zen_server/dashboard/assets/{ComponentBadge-D_g62Wv8.js → ComponentBadge-CrRvovox.js} +1 -1
- zenml/zen_server/dashboard/assets/{CopyButton-LNcWaa14.js → CopyButton-B6wGAhQv.js} +1 -1
- zenml/zen_server/dashboard/assets/{CsvVizualization-DknpE5ej.js → CsvVizualization-CjcT7LMm.js} +5 -5
- zenml/zen_server/dashboard/assets/DeleteAlertDialog-D2ELtM2W.js +1 -0
- zenml/zen_server/dashboard/assets/{DialogItem-Bxf8FuAT.js → DialogItem-DXIMhBgU.js} +1 -1
- zenml/zen_server/dashboard/assets/{Error-DYflYyps.js → Error-B8uUfTpL.js} +1 -1
- zenml/zen_server/dashboard/assets/{ExecutionStatus-C7zyIQKZ.js → ExecutionStatus-ibAdY-dG.js} +1 -1
- zenml/zen_server/dashboard/assets/{Helpbox-oYSGpLqd.js → Helpbox-BfAfhKHw.js} +1 -1
- zenml/zen_server/dashboard/assets/{Infobox-Cx4xGoXR.js → Infobox-M_SMOu96.js} +1 -1
- zenml/zen_server/dashboard/assets/{InlineAvatar-DiGOWNKF.js → InlineAvatar-DBA0a0-a.js} +1 -1
- zenml/zen_server/dashboard/assets/{NestedCollapsible-DYbgyKxK.js → NestedCollapsible-DpgmEFKw.js} +1 -1
- zenml/zen_server/dashboard/assets/{Partials-03iZf8-N.js → Partials-D_ldD9if.js} +1 -1
- zenml/zen_server/dashboard/assets/{ProBadge-D_EB8HNo.js → ProBadge-DQbfFotM.js} +1 -1
- zenml/zen_server/dashboard/assets/{ProCta-DqNS4v3x.js → ProCta-Bcpb4rcY.js} +1 -1
- zenml/zen_server/dashboard/assets/{ProviderIcon-Bki2aw8w.js → ProviderIcon-BZpgPigN.js} +1 -1
- zenml/zen_server/dashboard/assets/{ProviderRadio-8f43sPD4.js → ProviderRadio-DWPnMuQ1.js} +1 -1
- zenml/zen_server/dashboard/assets/RunSelector-DgRGaAc6.js +1 -0
- zenml/zen_server/dashboard/assets/{RunsBody-07YEO7qI.js → RunsBody-KecfSkjY.js} +1 -1
- zenml/zen_server/dashboard/assets/{SearchField-lp1KgU4e.js → SearchField-n-ILHnaP.js} +1 -1
- zenml/zen_server/dashboard/assets/{SecretTooltip-CgnbyeOx.js → SecretTooltip-B8MrX5yu.js} +1 -1
- zenml/zen_server/dashboard/assets/{SetPassword-CpP418A2.js → SetPassword-B_IVq_wg.js} +1 -1
- zenml/zen_server/dashboard/assets/StackList-TWPBYnkF.js +1 -0
- zenml/zen_server/dashboard/assets/{Tabs-BktHkCJJ.js → Tabs-Rg857zmd.js} +1 -1
- zenml/zen_server/dashboard/assets/{Tick-BlMoIlJT.js → Tick-COg4A-xo.js} +1 -1
- zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-Sc0A0pP-.js → UpdatePasswordSchemas-C6Aj3hm6.js} +1 -1
- zenml/zen_server/dashboard/assets/{UsageReason-YYduL4fj.js → UsageReason-BTLbx7w4.js} +1 -1
- zenml/zen_server/dashboard/assets/{WizardFooter-dgmizSJC.js → WizardFooter-BCAj69Vj.js} +1 -1
- zenml/zen_server/dashboard/assets/{all-pipeline-runs-query-D-c2G6lV.js → all-pipeline-runs-query-DMXkDrV2.js} +1 -1
- zenml/zen_server/dashboard/assets/code-snippets-CqONne41.js +13 -0
- zenml/zen_server/dashboard/assets/{create-stack-DM_JPgef.js → create-stack-HfdbhLs4.js} +1 -1
- zenml/zen_server/dashboard/assets/dates-3pMLCNrD.js +1 -0
- zenml/zen_server/dashboard/assets/delete-run-DZ4hIXff.js +1 -0
- zenml/zen_server/dashboard/assets/{form-schemas-K6FYKjwa.js → form-schemas-B0AVEd9b.js} +1 -1
- zenml/zen_server/dashboard/assets/{index-BAkC7FXi.js → index-DPqSWjug.js} +1 -1
- zenml/zen_server/dashboard/assets/{index-CEV4Cvaf.js → index-DScjfBRb.js} +1 -1
- zenml/zen_server/dashboard/assets/index-DXvT1_Um.css +1 -0
- zenml/zen_server/dashboard/assets/{index-CCOPpudF.js → index-FO-p0GU7.js} +5 -5
- zenml/zen_server/dashboard/assets/{index-B1mVPYxf.js → index-I3bKUGUj.js} +1 -1
- zenml/zen_server/dashboard/assets/key-icon-aH-QIa5R.js +1 -0
- zenml/zen_server/dashboard/assets/login-command-CkqxPtV3.js +1 -0
- zenml/zen_server/dashboard/assets/{login-mutation-hf-lK87O.js → login-mutation-BQeo4wTY.js} +1 -1
- zenml/zen_server/dashboard/assets/{not-found-BGirLjU-.js → not-found-gAJ5aDdR.js} +1 -1
- zenml/zen_server/dashboard/assets/page-9Y9-gig0.js +1 -0
- zenml/zen_server/dashboard/assets/{page-DjRJCGb3.js → page-AUwiQ14W.js} +1 -1
- zenml/zen_server/dashboard/assets/page-B6XU7yYT.js +2 -0
- zenml/zen_server/dashboard/assets/{page-C00YAkaB.js → page-BKZYc2Zv.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-CdMWnQak.js → page-BU9FG4sR.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-D7S3aCbF.js → page-B_Apk3xg.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-Djikxq_S.js → page-BdowiCbr.js} +1 -1
- zenml/zen_server/dashboard/assets/page-Bg8OjTRe.js +1 -0
- zenml/zen_server/dashboard/assets/page-BxL4qD4_.js +1 -0
- zenml/zen_server/dashboard/assets/{page-DakHVWXF.js → page-CWxT5K5J.js} +1 -1
- zenml/zen_server/dashboard/assets/page-CXuQufSe.js +1 -0
- zenml/zen_server/dashboard/assets/{page-DLC-bNBP.js → page-CcQr8CPP.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-CD-DcWoy.js → page-Ce4Hrjnr.js} +1 -1
- zenml/zen_server/dashboard/assets/page-CiYxgZP_.js +1 -0
- zenml/zen_server/dashboard/assets/page-Cldq1mpe.js +1 -0
- zenml/zen_server/dashboard/assets/{page-BDigxVpo.js → page-D4wdonLm.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-D6uU2ax4.js → page-D8ObrbH8.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-DXSTpqRD.js → page-DFuAUGt4.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-CbpvrsDL.js → page-DGazBpuP.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-COXXJj1k.js → page-DO1UcqPX.js} +1 -1
- zenml/zen_server/dashboard/assets/page-DRYXdL5o.js +1 -0
- zenml/zen_server/dashboard/assets/{page-Df-Fw0aq.js → page-DYEquBC2.js} +1 -1
- zenml/zen_server/dashboard/assets/page-Dk32IeZm.js +1 -0
- zenml/zen_server/dashboard/assets/{page-yYC9OI-E.js → page-I3nKFGie.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-6m6yHHlE.js → page-M0w-n6vn.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-Vcxara9U.js → page-R5dx3xGF.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-BR68V0V1.js → page-bT5pOvcB.js} +1 -1
- zenml/zen_server/dashboard/assets/page-hUqK889I.js +6 -0
- zenml/zen_server/dashboard/assets/{page-CjGdWY13.js → page-h_Stveon.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-D01JhjQB.js → page-r8XK5vR7.js} +1 -1
- zenml/zen_server/dashboard/assets/page-u_-ZXBKb.js +1 -0
- zenml/zen_server/dashboard/assets/page-zaMqB_ao.js +1 -0
- zenml/zen_server/dashboard/assets/{persist-GjC8PZoC.js → persist-AppN1B0J.js} +1 -1
- zenml/zen_server/dashboard/assets/{persist-Coz7ZWvz.js → persist-DAUi_3za.js} +1 -1
- zenml/zen_server/dashboard/assets/service-BqqeXLEe.js +2 -0
- zenml/zen_server/dashboard/assets/{sharedSchema-CQb14VSr.js → sharedSchema-uXN9FLLk.js} +1 -1
- zenml/zen_server/dashboard/assets/{stack-detail-query-OPEW-cDJ.js → stack-detail-query-XfZBiBP2.js} +1 -1
- zenml/zen_server/dashboard/assets/{update-server-settings-mutation-LwuQfHYn.js → update-server-settings-mutation-BWmgVJwA.js} +1 -1
- zenml/zen_server/dashboard/assets/{url-CkvKAnwF.js → url-BLwMbzES.js} +1 -1
- zenml/zen_server/dashboard/index.html +4 -4
- zenml/zen_server/deploy/helm/Chart.yaml +1 -1
- zenml/zen_server/deploy/helm/README.md +2 -2
- zenml/zen_server/rbac/endpoint_utils.py +6 -4
- zenml/zen_server/rbac/models.py +3 -2
- zenml/zen_server/rbac/rbac_sql_zen_store.py +173 -0
- zenml/zen_server/rbac/utils.py +4 -7
- zenml/zen_server/routers/auth_endpoints.py +22 -11
- zenml/zen_server/routers/steps_endpoints.py +7 -1
- zenml/zen_server/routers/users_endpoints.py +35 -37
- zenml/zen_server/routers/workspaces_endpoints.py +44 -55
- zenml/zen_server/template_execution/utils.py +4 -1
- zenml/zen_server/utils.py +4 -3
- zenml/zen_stores/base_zen_store.py +10 -2
- zenml/zen_stores/migrations/versions/0.71.0_release.py +23 -0
- zenml/zen_stores/migrations/versions/26351d482b9e_add_step_run_unique_constraint.py +37 -0
- zenml/zen_stores/migrations/versions/a1237ba94fd8_add_model_version_producer_run_unique_.py +68 -0
- zenml/zen_stores/migrations/versions/b73bc71f1106_remove_component_spec_path.py +36 -0
- zenml/zen_stores/migrations/versions/cc269488e5a9_separate_run_metadata.py +135 -0
- zenml/zen_stores/migrations/versions/ec6307720f92_simplify_model_version_links.py +7 -6
- zenml/zen_stores/rest_zen_store.py +76 -43
- zenml/zen_stores/schemas/__init__.py +5 -1
- zenml/zen_stores/schemas/artifact_schemas.py +12 -11
- zenml/zen_stores/schemas/component_schemas.py +0 -3
- zenml/zen_stores/schemas/model_schemas.py +55 -17
- zenml/zen_stores/schemas/pipeline_deployment_schemas.py +7 -7
- zenml/zen_stores/schemas/pipeline_run_schemas.py +52 -18
- zenml/zen_stores/schemas/pipeline_schemas.py +5 -0
- zenml/zen_stores/schemas/run_metadata_schemas.py +66 -31
- zenml/zen_stores/schemas/step_run_schemas.py +40 -13
- zenml/zen_stores/schemas/utils.py +47 -3
- zenml/zen_stores/sql_zen_store.py +462 -134
- {zenml_nightly-0.70.0.dev20241125.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/METADATA +5 -5
- {zenml_nightly-0.70.0.dev20241125.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/RECORD +239 -217
- zenml/utils/cloud_utils.py +0 -40
- zenml/zen_server/dashboard/assets/RunSelector-DkPiIiNr.js +0 -1
- zenml/zen_server/dashboard/assets/StackList-WvuKQusZ.js +0 -1
- zenml/zen_server/dashboard/assets/delete-run-CJdh1P_h.js +0 -1
- zenml/zen_server/dashboard/assets/index-DlGvJQPn.css +0 -1
- zenml/zen_server/dashboard/assets/page-0JE_-Ec1.js +0 -1
- zenml/zen_server/dashboard/assets/page-BRLpxOt0.js +0 -1
- zenml/zen_server/dashboard/assets/page-BU7huvKw.js +0 -6
- zenml/zen_server/dashboard/assets/page-BvqLv2Ky.js +0 -1
- zenml/zen_server/dashboard/assets/page-CwxrFarU.js +0 -1
- zenml/zen_server/dashboard/assets/page-DfbXf_8s.js +0 -1
- zenml/zen_server/dashboard/assets/page-Dnovpa0i.js +0 -3
- zenml/zen_server/dashboard/assets/page-Dot3LPmL.js +0 -1
- zenml/zen_server/dashboard/assets/page-Xynx4btY.js +0 -14
- zenml/zen_server/dashboard/assets/page-YpKAqVSa.js +0 -1
- {zenml_nightly-0.70.0.dev20241125.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.70.0.dev20241125.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.70.0.dev20241125.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/entry_points.txt +0 -0
@@ -14,7 +14,7 @@
|
|
14
14
|
"""Utilities for creating step runs."""
|
15
15
|
|
16
16
|
from datetime import datetime
|
17
|
-
from typing import
|
17
|
+
from typing import Dict, List, Optional, Set, Tuple
|
18
18
|
|
19
19
|
from zenml.client import Client
|
20
20
|
from zenml.config.step_configurations import Step
|
@@ -24,21 +24,13 @@ from zenml.logger import get_logger
|
|
24
24
|
from zenml.model.utils import link_artifact_version_to_model_version
|
25
25
|
from zenml.models import (
|
26
26
|
ArtifactVersionResponse,
|
27
|
-
ModelVersionPipelineRunRequest,
|
28
27
|
ModelVersionResponse,
|
29
28
|
PipelineDeploymentResponse,
|
30
29
|
PipelineRunResponse,
|
31
|
-
PipelineRunUpdate,
|
32
30
|
StepRunRequest,
|
33
|
-
StepRunResponse,
|
34
|
-
StepRunUpdate,
|
35
31
|
)
|
36
32
|
from zenml.orchestrators import cache_utils, input_utils, utils
|
37
33
|
from zenml.stack import Stack
|
38
|
-
from zenml.utils import pagination_utils, string_utils
|
39
|
-
|
40
|
-
if TYPE_CHECKING:
|
41
|
-
from zenml.model.model import Model
|
42
34
|
|
43
35
|
logger = get_logger(__name__)
|
44
36
|
|
@@ -293,10 +285,6 @@ def create_cached_step_runs(
|
|
293
285
|
deployment=deployment, pipeline_run=pipeline_run, stack=stack
|
294
286
|
)
|
295
287
|
|
296
|
-
pipeline_model_version, pipeline_run = prepare_pipeline_run_model_version(
|
297
|
-
pipeline_run=pipeline_run
|
298
|
-
)
|
299
|
-
|
300
288
|
while (
|
301
289
|
cache_candidates := find_cacheable_invocation_candidates(
|
302
290
|
deployment=deployment,
|
@@ -309,6 +297,11 @@ def create_cached_step_runs(
|
|
309
297
|
for invocation_id in cache_candidates:
|
310
298
|
visited_invocations.add(invocation_id)
|
311
299
|
|
300
|
+
# Make sure the request factory has the most up to date pipeline
|
301
|
+
# run to avoid hydration calls
|
302
|
+
request_factory.pipeline_run = Client().get_pipeline_run(
|
303
|
+
pipeline_run.id
|
304
|
+
)
|
312
305
|
try:
|
313
306
|
step_run_request = request_factory.create_request(
|
314
307
|
invocation_id
|
@@ -333,15 +326,10 @@ def create_cached_step_runs(
|
|
333
326
|
|
334
327
|
step_run = Client().zen_store.create_run_step(step_run_request)
|
335
328
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
step_model_version, step_run = prepare_step_run_model_version(
|
341
|
-
step_run=step_run, pipeline_run=pipeline_run
|
342
|
-
)
|
343
|
-
|
344
|
-
if model_version := step_model_version or pipeline_model_version:
|
329
|
+
if (
|
330
|
+
model_version := step_run.model_version
|
331
|
+
or pipeline_run.model_version
|
332
|
+
):
|
345
333
|
link_output_artifacts_to_model_version(
|
346
334
|
artifacts=step_run.outputs,
|
347
335
|
model_version=model_version,
|
@@ -353,160 +341,6 @@ def create_cached_step_runs(
|
|
353
341
|
return cached_invocations
|
354
342
|
|
355
343
|
|
356
|
-
def get_or_create_model_version_for_pipeline_run(
|
357
|
-
model: "Model", pipeline_run: PipelineRunResponse
|
358
|
-
) -> Tuple[ModelVersionResponse, bool]:
|
359
|
-
"""Get or create a model version as part of a pipeline run.
|
360
|
-
|
361
|
-
Args:
|
362
|
-
model: The model to get or create.
|
363
|
-
pipeline_run: The pipeline run for which the model should be created.
|
364
|
-
|
365
|
-
Returns:
|
366
|
-
The model version and a boolean indicating whether it was newly created
|
367
|
-
or not.
|
368
|
-
"""
|
369
|
-
# Copy the model before modifying it so we don't accidently modify
|
370
|
-
# configurations in which the model object is potentially referenced
|
371
|
-
model = model.model_copy()
|
372
|
-
|
373
|
-
if model.model_version_id:
|
374
|
-
return model._get_model_version(), False
|
375
|
-
elif model.version:
|
376
|
-
if isinstance(model.version, str):
|
377
|
-
start_time = pipeline_run.start_time or datetime.utcnow()
|
378
|
-
model.version = string_utils.format_name_template(
|
379
|
-
model.version,
|
380
|
-
date=start_time.strftime("%Y_%m_%d"),
|
381
|
-
time=start_time.strftime("%H_%M_%S_%f"),
|
382
|
-
)
|
383
|
-
|
384
|
-
return (
|
385
|
-
model._get_or_create_model_version(),
|
386
|
-
model._created_model_version,
|
387
|
-
)
|
388
|
-
|
389
|
-
# The model version should be created as part of this run
|
390
|
-
# -> We first check if it was already created as part of this run, and if
|
391
|
-
# not we do create it. If this is running in two parallel steps, we might
|
392
|
-
# run into issues that this will create two versions. Ideally, all model
|
393
|
-
# versions required for a pipeline run and its steps could be created
|
394
|
-
# server-side at run creation time before the first step starts.
|
395
|
-
if model_version := get_model_version_created_by_pipeline_run(
|
396
|
-
model_name=model.name, pipeline_run=pipeline_run
|
397
|
-
):
|
398
|
-
return model_version, False
|
399
|
-
else:
|
400
|
-
return model._get_or_create_model_version(), True
|
401
|
-
|
402
|
-
|
403
|
-
def get_model_version_created_by_pipeline_run(
|
404
|
-
model_name: str, pipeline_run: PipelineRunResponse
|
405
|
-
) -> Optional[ModelVersionResponse]:
|
406
|
-
"""Get a model version that was created by a specific pipeline run.
|
407
|
-
|
408
|
-
This function does not refresh the pipeline run, so it will only try to
|
409
|
-
fetch the model version from existing steps if they're already part of the
|
410
|
-
response.
|
411
|
-
|
412
|
-
Args:
|
413
|
-
model_name: The model name for which to get the version.
|
414
|
-
pipeline_run: The pipeline run for which to get the version.
|
415
|
-
|
416
|
-
Returns:
|
417
|
-
A model version with the given name created by the run, or None if such
|
418
|
-
a model version does not exist.
|
419
|
-
"""
|
420
|
-
if pipeline_run.config.model and pipeline_run.model_version:
|
421
|
-
if (
|
422
|
-
pipeline_run.config.model.name == model_name
|
423
|
-
and pipeline_run.config.model.version is None
|
424
|
-
):
|
425
|
-
return pipeline_run.model_version
|
426
|
-
|
427
|
-
# We fetch a list of hydrated step runs here in order to avoid hydration
|
428
|
-
# calls for each step separately.
|
429
|
-
candidate_step_runs = pagination_utils.depaginate(
|
430
|
-
Client().list_run_steps,
|
431
|
-
pipeline_run_id=pipeline_run.id,
|
432
|
-
model=model_name,
|
433
|
-
hydrate=True,
|
434
|
-
)
|
435
|
-
for step_run in candidate_step_runs:
|
436
|
-
if step_run.config.model and step_run.model_version:
|
437
|
-
if (
|
438
|
-
step_run.config.model.name == model_name
|
439
|
-
and step_run.config.model.version is None
|
440
|
-
):
|
441
|
-
return step_run.model_version
|
442
|
-
|
443
|
-
return None
|
444
|
-
|
445
|
-
|
446
|
-
def prepare_pipeline_run_model_version(
|
447
|
-
pipeline_run: PipelineRunResponse,
|
448
|
-
) -> Tuple[Optional[ModelVersionResponse], PipelineRunResponse]:
|
449
|
-
"""Prepare the model version for a pipeline run.
|
450
|
-
|
451
|
-
Args:
|
452
|
-
pipeline_run: The pipeline run for which to prepare the model version.
|
453
|
-
|
454
|
-
Returns:
|
455
|
-
The prepared model version and the updated pipeline run.
|
456
|
-
"""
|
457
|
-
model_version = None
|
458
|
-
|
459
|
-
if pipeline_run.model_version:
|
460
|
-
model_version = pipeline_run.model_version
|
461
|
-
elif config_model := pipeline_run.config.model:
|
462
|
-
model_version, _ = get_or_create_model_version_for_pipeline_run(
|
463
|
-
model=config_model, pipeline_run=pipeline_run
|
464
|
-
)
|
465
|
-
pipeline_run = Client().zen_store.update_run(
|
466
|
-
run_id=pipeline_run.id,
|
467
|
-
run_update=PipelineRunUpdate(model_version_id=model_version.id),
|
468
|
-
)
|
469
|
-
link_pipeline_run_to_model_version(
|
470
|
-
pipeline_run=pipeline_run, model_version=model_version
|
471
|
-
)
|
472
|
-
log_model_version_dashboard_url(model_version)
|
473
|
-
|
474
|
-
return model_version, pipeline_run
|
475
|
-
|
476
|
-
|
477
|
-
def prepare_step_run_model_version(
|
478
|
-
step_run: StepRunResponse, pipeline_run: PipelineRunResponse
|
479
|
-
) -> Tuple[Optional[ModelVersionResponse], StepRunResponse]:
|
480
|
-
"""Prepare the model version for a step run.
|
481
|
-
|
482
|
-
Args:
|
483
|
-
step_run: The step run for which to prepare the model version.
|
484
|
-
pipeline_run: The pipeline run of the step.
|
485
|
-
|
486
|
-
Returns:
|
487
|
-
The prepared model version and the updated step run.
|
488
|
-
"""
|
489
|
-
model_version = None
|
490
|
-
|
491
|
-
if step_run.model_version:
|
492
|
-
model_version = step_run.model_version
|
493
|
-
elif config_model := step_run.config.model:
|
494
|
-
model_version, created = get_or_create_model_version_for_pipeline_run(
|
495
|
-
model=config_model, pipeline_run=pipeline_run
|
496
|
-
)
|
497
|
-
step_run = Client().zen_store.update_run_step(
|
498
|
-
step_run_id=step_run.id,
|
499
|
-
step_run_update=StepRunUpdate(model_version_id=model_version.id),
|
500
|
-
)
|
501
|
-
link_pipeline_run_to_model_version(
|
502
|
-
pipeline_run=pipeline_run, model_version=model_version
|
503
|
-
)
|
504
|
-
if created:
|
505
|
-
log_model_version_dashboard_url(model_version)
|
506
|
-
|
507
|
-
return model_version, step_run
|
508
|
-
|
509
|
-
|
510
344
|
def log_model_version_dashboard_url(
|
511
345
|
model_version: ModelVersionResponse,
|
512
346
|
) -> None:
|
@@ -518,10 +352,15 @@ def log_model_version_dashboard_url(
|
|
518
352
|
Args:
|
519
353
|
model_version: The model version for which to log the dashboard URL.
|
520
354
|
"""
|
521
|
-
from zenml.utils.
|
355
|
+
from zenml.utils.dashboard_utils import get_model_version_url
|
522
356
|
|
523
|
-
if
|
524
|
-
logger.info(
|
357
|
+
if model_version_url := get_model_version_url(model_version.id):
|
358
|
+
logger.info(
|
359
|
+
"Dashboard URL for Model Version `%s (%s)`:\n%s",
|
360
|
+
model_version.model.name,
|
361
|
+
model_version.name,
|
362
|
+
model_version_url,
|
363
|
+
)
|
525
364
|
else:
|
526
365
|
logger.info(
|
527
366
|
"Models can be viewed in the dashboard using ZenML Pro. Sign up "
|
@@ -529,24 +368,6 @@ def log_model_version_dashboard_url(
|
|
529
368
|
)
|
530
369
|
|
531
370
|
|
532
|
-
def link_pipeline_run_to_model_version(
|
533
|
-
pipeline_run: PipelineRunResponse, model_version: ModelVersionResponse
|
534
|
-
) -> None:
|
535
|
-
"""Link a pipeline run to a model version.
|
536
|
-
|
537
|
-
Args:
|
538
|
-
pipeline_run: The pipeline run to link.
|
539
|
-
model_version: The model version to link.
|
540
|
-
"""
|
541
|
-
client = Client()
|
542
|
-
client.zen_store.create_model_version_pipeline_run_link(
|
543
|
-
ModelVersionPipelineRunRequest(
|
544
|
-
pipeline_run=pipeline_run.id,
|
545
|
-
model_version=model_version.id,
|
546
|
-
)
|
547
|
-
)
|
548
|
-
|
549
|
-
|
550
371
|
def link_output_artifacts_to_model_version(
|
551
372
|
artifacts: Dict[str, List[ArtifactVersionResponse]],
|
552
373
|
model_version: ModelVersionResponse,
|
@@ -56,7 +56,7 @@ from zenml.steps.utils import (
|
|
56
56
|
parse_return_type_annotations,
|
57
57
|
resolve_type_annotation,
|
58
58
|
)
|
59
|
-
from zenml.utils import materializer_utils, source_utils
|
59
|
+
from zenml.utils import materializer_utils, source_utils, string_utils
|
60
60
|
from zenml.utils.typing_utils import get_origin, is_union
|
61
61
|
|
62
62
|
if TYPE_CHECKING:
|
@@ -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,32 @@ 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
|
+
name = k
|
296
|
+
if v.artifact_config and v.artifact_config.name:
|
297
|
+
name = string_utils.format_name_template(
|
298
|
+
v.artifact_config.name,
|
299
|
+
substitutions=step_run.config.substitutions,
|
300
|
+
)
|
301
|
+
|
302
|
+
for d in collections:
|
303
|
+
d[name] = d.pop(k)
|
304
|
+
|
268
305
|
def _load_step(self) -> "BaseStep":
|
269
306
|
"""Load the step instance.
|
270
307
|
|
@@ -400,7 +437,7 @@ class StepRunner:
|
|
400
437
|
**artifact.get_hydrated_version().model_dump()
|
401
438
|
)
|
402
439
|
|
403
|
-
if data_type
|
440
|
+
if data_type in (None, Any) or is_union(get_origin(data_type)):
|
404
441
|
# Entrypoint function does not define a specific type for the input,
|
405
442
|
# we use the datatype of the stored artifact
|
406
443
|
data_type = source_utils.load(artifact.data_type)
|
zenml/orchestrators/utils.py
CHANGED
@@ -26,13 +26,14 @@ from zenml.constants import (
|
|
26
26
|
ENV_ZENML_ACTIVE_STACK_ID,
|
27
27
|
ENV_ZENML_ACTIVE_WORKSPACE_ID,
|
28
28
|
ENV_ZENML_DISABLE_CREDENTIALS_DISK_CACHING,
|
29
|
+
ENV_ZENML_PIPELINE_RUN_API_TOKEN_EXPIRATION,
|
29
30
|
ENV_ZENML_SERVER,
|
30
31
|
ENV_ZENML_STORE_PREFIX,
|
32
|
+
ZENML_PIPELINE_RUN_API_TOKEN_EXPIRATION,
|
31
33
|
)
|
32
|
-
from zenml.enums import AuthScheme, StackComponentType, StoreType
|
34
|
+
from zenml.enums import APITokenType, AuthScheme, StackComponentType, StoreType
|
33
35
|
from zenml.logger import get_logger
|
34
36
|
from zenml.stack import StackComponent
|
35
|
-
from zenml.utils.string_utils import format_name_template
|
36
37
|
|
37
38
|
logger = get_logger(__name__)
|
38
39
|
|
@@ -40,7 +41,9 @@ if TYPE_CHECKING:
|
|
40
41
|
from zenml.artifact_stores.base_artifact_store import BaseArtifactStore
|
41
42
|
|
42
43
|
|
43
|
-
def get_orchestrator_run_name(
|
44
|
+
def get_orchestrator_run_name(
|
45
|
+
pipeline_name: str, max_length: Optional[int] = None
|
46
|
+
) -> str:
|
44
47
|
"""Gets an orchestrator run name.
|
45
48
|
|
46
49
|
This run name is not the same as the ZenML run name but can instead be
|
@@ -48,11 +51,31 @@ def get_orchestrator_run_name(pipeline_name: str) -> str:
|
|
48
51
|
|
49
52
|
Args:
|
50
53
|
pipeline_name: Name of the pipeline that will run.
|
54
|
+
max_length: Maximum length of the generated name.
|
55
|
+
|
56
|
+
Raises:
|
57
|
+
ValueError: If the max length is below 8 characters.
|
51
58
|
|
52
59
|
Returns:
|
53
60
|
The orchestrator run name.
|
54
61
|
"""
|
55
|
-
|
62
|
+
suffix_length = 32
|
63
|
+
pipeline_name = f"{pipeline_name}_"
|
64
|
+
|
65
|
+
if max_length:
|
66
|
+
if max_length < 8:
|
67
|
+
raise ValueError(
|
68
|
+
"Maximum length for orchestrator run name must be 8 or above."
|
69
|
+
)
|
70
|
+
|
71
|
+
# Make sure we always have a certain suffix to guarantee no overlap
|
72
|
+
# with other runs
|
73
|
+
suffix_length = min(32, max(8, max_length - len(pipeline_name)))
|
74
|
+
pipeline_name = pipeline_name[: (max_length - suffix_length)]
|
75
|
+
|
76
|
+
suffix = "".join(random.choices("0123456789abcdef", k=suffix_length))
|
77
|
+
|
78
|
+
return f"{pipeline_name}{suffix}"
|
56
79
|
|
57
80
|
|
58
81
|
def is_setting_enabled(
|
@@ -116,37 +139,63 @@ def get_config_environment_vars(
|
|
116
139
|
url = global_config.store_configuration.url
|
117
140
|
api_token = credentials_store.get_token(url, allow_expired=False)
|
118
141
|
if schedule_id or pipeline_run_id or step_run_id:
|
119
|
-
# When connected to an authenticated ZenML server, if a schedule ID,
|
120
|
-
# pipeline run ID or step run ID is supplied, we need to fetch a new
|
121
|
-
# workload API token scoped to the schedule, pipeline run or step
|
122
|
-
# run.
|
123
142
|
assert isinstance(global_config.zen_store, RestZenStore)
|
124
143
|
|
125
|
-
#
|
126
|
-
#
|
127
|
-
|
128
|
-
if
|
144
|
+
# The user has the option to manually set an expiration for the API
|
145
|
+
# token generated for a pipeline run. In this case, we generate a new
|
146
|
+
# generic API token that will be valid for the indicated duration.
|
147
|
+
if (
|
148
|
+
pipeline_run_id
|
149
|
+
and ZENML_PIPELINE_RUN_API_TOKEN_EXPIRATION != 0
|
150
|
+
):
|
129
151
|
logger.warning(
|
130
|
-
"An API token
|
131
|
-
"
|
132
|
-
"
|
133
|
-
"
|
134
|
-
"
|
135
|
-
"
|
136
|
-
"
|
137
|
-
"
|
138
|
-
"
|
139
|
-
"
|
152
|
+
f"An unscoped API token will be generated for this pipeline "
|
153
|
+
f"run that will expire after "
|
154
|
+
f"{ZENML_PIPELINE_RUN_API_TOKEN_EXPIRATION} "
|
155
|
+
f"seconds instead of being scoped to the pipeline run "
|
156
|
+
f"and not having an expiration time. This is more insecure "
|
157
|
+
f"because the API token will remain valid even after the "
|
158
|
+
f"pipeline run completes its execution. This option has "
|
159
|
+
"been explicitly enabled by setting the "
|
160
|
+
f"{ENV_ZENML_PIPELINE_RUN_API_TOKEN_EXPIRATION} environment "
|
161
|
+
f"variable"
|
162
|
+
)
|
163
|
+
new_api_token = global_config.zen_store.get_api_token(
|
164
|
+
token_type=APITokenType.GENERIC,
|
165
|
+
expires_in=ZENML_PIPELINE_RUN_API_TOKEN_EXPIRATION,
|
140
166
|
)
|
141
167
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
168
|
+
else:
|
169
|
+
# If a schedule ID, pipeline run ID or step run ID is supplied,
|
170
|
+
# we need to fetch a new workload API token scoped to the
|
171
|
+
# schedule, pipeline run or step run.
|
172
|
+
|
173
|
+
# If only a schedule is given, the pipeline run credentials will
|
174
|
+
# be valid for the entire duration of the schedule.
|
175
|
+
api_key = credentials_store.get_api_key(url)
|
176
|
+
if not api_key and not pipeline_run_id and not step_run_id:
|
177
|
+
logger.warning(
|
178
|
+
"An API token without an expiration time will be generated "
|
179
|
+
"and used to run this pipeline on a schedule. This is very "
|
180
|
+
"insecure because the API token will be valid for the "
|
181
|
+
"entire lifetime of the schedule and can be used to access "
|
182
|
+
"your user account if accidentally leaked. When deploying "
|
183
|
+
"a pipeline on a schedule, it is strongly advised to use a "
|
184
|
+
"service account API key to authenticate to the ZenML "
|
185
|
+
"server instead of your regular user account. For more "
|
186
|
+
"information, see "
|
187
|
+
"https://docs.zenml.io/how-to/connecting-to-zenml/connect-with-a-service-account"
|
188
|
+
)
|
189
|
+
|
190
|
+
# The schedule, pipeline run or step run credentials are scoped to
|
191
|
+
# the schedule, pipeline run or step run and will only be valid for
|
192
|
+
# the duration of the schedule/pipeline run/step run.
|
193
|
+
new_api_token = global_config.zen_store.get_api_token(
|
194
|
+
token_type=APITokenType.WORKLOAD,
|
195
|
+
schedule_id=schedule_id,
|
196
|
+
pipeline_run_id=pipeline_run_id,
|
197
|
+
step_run_id=step_run_id,
|
198
|
+
)
|
150
199
|
|
151
200
|
environment_vars[ENV_ZENML_STORE_PREFIX + "API_TOKEN"] = (
|
152
201
|
new_api_token
|
@@ -174,26 +223,6 @@ def get_config_environment_vars(
|
|
174
223
|
return environment_vars
|
175
224
|
|
176
225
|
|
177
|
-
def get_run_name(run_name_template: str) -> str:
|
178
|
-
"""Fill out the run name template to get a complete run name.
|
179
|
-
|
180
|
-
Args:
|
181
|
-
run_name_template: The run name template to fill out.
|
182
|
-
|
183
|
-
Raises:
|
184
|
-
ValueError: If the run name is empty.
|
185
|
-
|
186
|
-
Returns:
|
187
|
-
The run name derived from the template.
|
188
|
-
"""
|
189
|
-
run_name = format_name_template(run_name_template)
|
190
|
-
|
191
|
-
if run_name == "":
|
192
|
-
raise ValueError("Empty run names are not allowed.")
|
193
|
-
|
194
|
-
return run_name
|
195
|
-
|
196
|
-
|
197
226
|
class register_artifact_store_filesystem:
|
198
227
|
"""Context manager for the artifact_store/filesystem_registry dependency.
|
199
228
|
|
zenml/pipelines/build_utils.py
CHANGED
@@ -249,6 +249,11 @@ def find_existing_build(
|
|
249
249
|
client = Client()
|
250
250
|
stack = client.active_stack
|
251
251
|
|
252
|
+
if not stack.container_registry:
|
253
|
+
# There can be no non-local builds that we can reuse if there is no
|
254
|
+
# container registry in the stack.
|
255
|
+
return None
|
256
|
+
|
252
257
|
python_version_prefix = ".".join(platform.python_version_tuple()[:2])
|
253
258
|
required_builds = stack.get_docker_builds(deployment=deployment)
|
254
259
|
|
@@ -263,6 +268,13 @@ def find_existing_build(
|
|
263
268
|
sort_by="desc:created",
|
264
269
|
size=1,
|
265
270
|
stack_id=stack.id,
|
271
|
+
# Until we implement stack versioning, users can still update their
|
272
|
+
# stack to update/remove the container registry. In that case, we might
|
273
|
+
# try to pull an image from a container registry that we don't have
|
274
|
+
# access to. This is why we add an additional check for the container
|
275
|
+
# registry ID here. (This is still not perfect as users can update the
|
276
|
+
# container registry URI or config, but the best we can do)
|
277
|
+
container_registry_id=stack.container_registry.id,
|
266
278
|
# The build is local and it's not clear whether the images
|
267
279
|
# exist on the current machine or if they've been overwritten.
|
268
280
|
# TODO: Should we support this by storing the unique Docker ID for
|
@@ -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__
|