zenml-nightly 0.70.0.dev20241201__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/artifacts/artifact_config.py +8 -5
- zenml/artifacts/utils.py +3 -1
- zenml/cli/__init__.py +4 -4
- zenml/cli/base.py +1 -1
- zenml/cli/pipeline.py +48 -79
- zenml/cli/server.py +19 -19
- zenml/client.py +54 -2
- zenml/config/secret_reference_mixin.py +1 -1
- zenml/config/server_config.py +4 -0
- zenml/constants.py +10 -0
- zenml/image_builders/base_image_builder.py +5 -2
- zenml/image_builders/build_context.py +7 -16
- 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/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/step_operators/kubernetes_step_operator.py +0 -1
- zenml/integrations/lightning/flavors/lightning_orchestrator_flavor.py +11 -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/materializers/built_in_materializer.py +1 -1
- zenml/model/model.py +12 -16
- zenml/model/utils.py +3 -1
- zenml/models/v2/base/filter.py +26 -30
- zenml/models/v2/base/scoped.py +258 -5
- zenml/models/v2/core/artifact_version.py +15 -26
- zenml/models/v2/core/code_repository.py +1 -12
- zenml/models/v2/core/component.py +5 -46
- zenml/models/v2/core/flavor.py +1 -11
- zenml/models/v2/core/model.py +1 -57
- zenml/models/v2/core/model_version.py +5 -33
- 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 +91 -29
- 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 +18 -14
- zenml/models/v2/core/trigger.py +19 -3
- zenml/orchestrators/base_orchestrator.py +13 -1
- zenml/orchestrators/output_utils.py +5 -1
- zenml/orchestrators/step_launcher.py +9 -13
- zenml/orchestrators/step_run_utils.py +8 -204
- zenml/orchestrators/utils.py +55 -27
- zenml/pipelines/build_utils.py +12 -0
- 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/utils/archivable.py +65 -36
- zenml/utils/code_utils.py +8 -4
- zenml/utils/docker_utils.py +9 -0
- zenml/zen_server/auth.py +9 -10
- 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/rbac_sql_zen_store.py +173 -0
- zenml/zen_server/routers/auth_endpoints.py +22 -11
- zenml/zen_server/routers/steps_endpoints.py +7 -1
- zenml/zen_server/template_execution/utils.py +3 -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/rest_zen_store.py +76 -43
- zenml/zen_stores/schemas/model_schemas.py +42 -6
- zenml/zen_stores/schemas/pipeline_deployment_schemas.py +7 -7
- zenml/zen_stores/schemas/pipeline_run_schemas.py +12 -6
- zenml/zen_stores/schemas/pipeline_schemas.py +5 -0
- zenml/zen_stores/schemas/step_run_schemas.py +8 -1
- zenml/zen_stores/sql_zen_store.py +332 -100
- {zenml_nightly-0.70.0.dev20241201.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/METADATA +5 -5
- {zenml_nightly-0.70.0.dev20241201.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/RECORD +179 -164
- 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.dev20241201.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.70.0.dev20241201.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.70.0.dev20241201.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/entry_points.txt +0 -0
@@ -36,6 +36,7 @@ from zenml.constants import (
|
|
36
36
|
DEFAULT_STACK_AND_COMPONENT_NAME,
|
37
37
|
DEFAULT_WORKSPACE_NAME,
|
38
38
|
ENV_ZENML_DEFAULT_WORKSPACE_NAME,
|
39
|
+
ENV_ZENML_SERVER,
|
39
40
|
IS_DEBUG_ENV,
|
40
41
|
)
|
41
42
|
from zenml.enums import (
|
@@ -155,9 +156,16 @@ class BaseZenStore(
|
|
155
156
|
TypeError: If the store type is unsupported.
|
156
157
|
"""
|
157
158
|
if store_type == StoreType.SQL:
|
158
|
-
|
159
|
+
if os.environ.get(ENV_ZENML_SERVER):
|
160
|
+
from zenml.zen_server.rbac.rbac_sql_zen_store import (
|
161
|
+
RBACSqlZenStore,
|
162
|
+
)
|
163
|
+
|
164
|
+
return RBACSqlZenStore
|
165
|
+
else:
|
166
|
+
from zenml.zen_stores.sql_zen_store import SqlZenStore
|
159
167
|
|
160
|
-
|
168
|
+
return SqlZenStore
|
161
169
|
elif store_type == StoreType.REST:
|
162
170
|
from zenml.zen_stores.rest_zen_store import RestZenStore
|
163
171
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
"""Release [0.71.0].
|
2
|
+
|
3
|
+
Revision ID: 0.71.0
|
4
|
+
Revises: cc269488e5a9
|
5
|
+
Create Date: 2024-12-04 20:44:19.316139
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
# revision identifiers, used by Alembic.
|
10
|
+
revision = "0.71.0"
|
11
|
+
down_revision = "cc269488e5a9"
|
12
|
+
branch_labels = None
|
13
|
+
depends_on = None
|
14
|
+
|
15
|
+
|
16
|
+
def upgrade() -> None:
|
17
|
+
"""Upgrade database schema and/or data, creating a new revision."""
|
18
|
+
pass
|
19
|
+
|
20
|
+
|
21
|
+
def downgrade() -> None:
|
22
|
+
"""Downgrade database schema and/or data back to the previous revision."""
|
23
|
+
pass
|
@@ -0,0 +1,37 @@
|
|
1
|
+
"""Add step run unique constraint [26351d482b9e].
|
2
|
+
|
3
|
+
Revision ID: 26351d482b9e
|
4
|
+
Revises: 0.71.0
|
5
|
+
Create Date: 2024-12-03 11:46:57.541578
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
from alembic import op
|
10
|
+
|
11
|
+
# revision identifiers, used by Alembic.
|
12
|
+
revision = "26351d482b9e"
|
13
|
+
down_revision = "0.71.0"
|
14
|
+
branch_labels = None
|
15
|
+
depends_on = None
|
16
|
+
|
17
|
+
|
18
|
+
def upgrade() -> None:
|
19
|
+
"""Upgrade database schema and/or data, creating a new revision."""
|
20
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
21
|
+
with op.batch_alter_table("step_run", schema=None) as batch_op:
|
22
|
+
batch_op.create_unique_constraint(
|
23
|
+
"unique_step_name_for_pipeline_run", ["name", "pipeline_run_id"]
|
24
|
+
)
|
25
|
+
|
26
|
+
# ### end Alembic commands ###
|
27
|
+
|
28
|
+
|
29
|
+
def downgrade() -> None:
|
30
|
+
"""Downgrade database schema and/or data back to the previous revision."""
|
31
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
32
|
+
with op.batch_alter_table("step_run", schema=None) as batch_op:
|
33
|
+
batch_op.drop_constraint(
|
34
|
+
"unique_step_name_for_pipeline_run", type_="unique"
|
35
|
+
)
|
36
|
+
|
37
|
+
# ### end Alembic commands ###
|
@@ -0,0 +1,68 @@
|
|
1
|
+
"""Add model version producer run unique constraint [a1237ba94fd8].
|
2
|
+
|
3
|
+
Revision ID: a1237ba94fd8
|
4
|
+
Revises: 26351d482b9e
|
5
|
+
Create Date: 2024-12-13 10:28:55.432414
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
import sqlalchemy as sa
|
10
|
+
import sqlmodel
|
11
|
+
from alembic import op
|
12
|
+
|
13
|
+
# revision identifiers, used by Alembic.
|
14
|
+
revision = "a1237ba94fd8"
|
15
|
+
down_revision = "26351d482b9e"
|
16
|
+
branch_labels = None
|
17
|
+
depends_on = None
|
18
|
+
|
19
|
+
|
20
|
+
def upgrade() -> None:
|
21
|
+
"""Upgrade database schema and/or data, creating a new revision."""
|
22
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
23
|
+
with op.batch_alter_table("model_version", schema=None) as batch_op:
|
24
|
+
batch_op.add_column(
|
25
|
+
sa.Column(
|
26
|
+
"producer_run_id_if_numeric",
|
27
|
+
sqlmodel.sql.sqltypes.GUID(),
|
28
|
+
nullable=True,
|
29
|
+
)
|
30
|
+
)
|
31
|
+
|
32
|
+
# Set the producer_run_id_if_numeric column to the model version ID for
|
33
|
+
# existing rows
|
34
|
+
connection = op.get_bind()
|
35
|
+
metadata = sa.MetaData()
|
36
|
+
metadata.reflect(only=("model_version",), bind=connection)
|
37
|
+
model_version_table = sa.Table("model_version", metadata)
|
38
|
+
|
39
|
+
connection.execute(
|
40
|
+
model_version_table.update().values(
|
41
|
+
producer_run_id_if_numeric=model_version_table.c.id
|
42
|
+
)
|
43
|
+
)
|
44
|
+
|
45
|
+
with op.batch_alter_table("model_version", schema=None) as batch_op:
|
46
|
+
batch_op.alter_column(
|
47
|
+
"producer_run_id_if_numeric",
|
48
|
+
existing_type=sqlmodel.sql.sqltypes.GUID(),
|
49
|
+
nullable=False,
|
50
|
+
)
|
51
|
+
batch_op.create_unique_constraint(
|
52
|
+
"unique_numeric_version_for_pipeline_run",
|
53
|
+
["model_id", "producer_run_id_if_numeric"],
|
54
|
+
)
|
55
|
+
|
56
|
+
# ### end Alembic commands ###
|
57
|
+
|
58
|
+
|
59
|
+
def downgrade() -> None:
|
60
|
+
"""Downgrade database schema and/or data back to the previous revision."""
|
61
|
+
# ### commands auto generated by Alembic - please adjust! ###
|
62
|
+
with op.batch_alter_table("model_version", schema=None) as batch_op:
|
63
|
+
batch_op.drop_constraint(
|
64
|
+
"unique_numeric_version_for_pipeline_run", type_="unique"
|
65
|
+
)
|
66
|
+
batch_op.drop_column("producer_run_id_if_numeric")
|
67
|
+
|
68
|
+
# ### end Alembic commands ###
|
@@ -3873,13 +3873,17 @@ class RestZenStore(BaseZenStore):
|
|
3873
3873
|
|
3874
3874
|
def get_api_token(
|
3875
3875
|
self,
|
3876
|
+
token_type: APITokenType = APITokenType.WORKLOAD,
|
3877
|
+
expires_in: Optional[int] = None,
|
3876
3878
|
schedule_id: Optional[UUID] = None,
|
3877
3879
|
pipeline_run_id: Optional[UUID] = None,
|
3878
3880
|
step_run_id: Optional[UUID] = None,
|
3879
3881
|
) -> str:
|
3880
|
-
"""Get an API token
|
3882
|
+
"""Get an API token.
|
3881
3883
|
|
3882
3884
|
Args:
|
3885
|
+
token_type: The type of the token to get.
|
3886
|
+
expires_in: The time in seconds until the token expires.
|
3883
3887
|
schedule_id: The ID of the schedule to get a token for.
|
3884
3888
|
pipeline_run_id: The ID of the pipeline run to get a token for.
|
3885
3889
|
step_run_id: The ID of the step run to get a token for.
|
@@ -3891,9 +3895,10 @@ class RestZenStore(BaseZenStore):
|
|
3891
3895
|
ValueError: if the server response is not valid.
|
3892
3896
|
"""
|
3893
3897
|
params: Dict[str, Any] = {
|
3894
|
-
|
3895
|
-
"token_type": APITokenType.WORKLOAD.value,
|
3898
|
+
"token_type": token_type.value,
|
3896
3899
|
}
|
3900
|
+
if expires_in:
|
3901
|
+
params["expires_in"] = expires_in
|
3897
3902
|
if schedule_id:
|
3898
3903
|
params["schedule_id"] = schedule_id
|
3899
3904
|
if pipeline_run_id:
|
@@ -4062,7 +4067,7 @@ class RestZenStore(BaseZenStore):
|
|
4062
4067
|
"you should use a service account API key to authenticate to "
|
4063
4068
|
"the server instead of temporary CLI login credentials. For "
|
4064
4069
|
"more information, see "
|
4065
|
-
"https://docs.zenml.io/how-to/connecting-to-zenml/connect-with-a-service-account"
|
4070
|
+
"https://docs.zenml.io/how-to/project-setup-and-management/connecting-to-zenml/connect-with-a-service-account"
|
4066
4071
|
)
|
4067
4072
|
|
4068
4073
|
if api_key is not None:
|
@@ -4344,46 +4349,74 @@ class RestZenStore(BaseZenStore):
|
|
4344
4349
|
{source_context.name: source_context.get().value}
|
4345
4350
|
)
|
4346
4351
|
|
4347
|
-
|
4348
|
-
|
4349
|
-
|
4350
|
-
|
4351
|
-
|
4352
|
-
|
4353
|
-
|
4354
|
-
|
4355
|
-
|
4356
|
-
|
4357
|
-
|
4358
|
-
|
4359
|
-
|
4360
|
-
|
4361
|
-
|
4362
|
-
|
4363
|
-
|
4364
|
-
|
4365
|
-
|
4366
|
-
|
4367
|
-
|
4368
|
-
|
4369
|
-
|
4370
|
-
|
4371
|
-
|
4372
|
-
|
4373
|
-
self.session.request(
|
4374
|
-
method,
|
4375
|
-
url,
|
4376
|
-
params=params,
|
4377
|
-
verify=self.config.verify_ssl,
|
4378
|
-
timeout=self.config.http_timeout,
|
4379
|
-
**kwargs,
|
4352
|
+
# If the server replies with a credentials validation (401 Unauthorized)
|
4353
|
+
# error, we (re-)authenticate and retry the request here in the
|
4354
|
+
# following cases:
|
4355
|
+
#
|
4356
|
+
# 1. initial authentication: the last request was not authenticated
|
4357
|
+
# with an API token.
|
4358
|
+
# 2. re-authentication: the last request was authenticated with an API
|
4359
|
+
# token that was rejected by the server. This is to cover the case
|
4360
|
+
# of expired tokens that can be refreshed by the client automatically
|
4361
|
+
# without user intervention from other sources (e.g. API keys).
|
4362
|
+
#
|
4363
|
+
# NOTE: it can happen that the same request is retried here for up to
|
4364
|
+
# two times: once after initial authentication and once after
|
4365
|
+
# re-authentication.
|
4366
|
+
re_authenticated = False
|
4367
|
+
while True:
|
4368
|
+
try:
|
4369
|
+
return self._handle_response(
|
4370
|
+
self.session.request(
|
4371
|
+
method,
|
4372
|
+
url,
|
4373
|
+
params=params,
|
4374
|
+
verify=self.config.verify_ssl,
|
4375
|
+
timeout=timeout or self.config.http_timeout,
|
4376
|
+
**kwargs,
|
4377
|
+
)
|
4380
4378
|
)
|
4381
|
-
|
4382
|
-
|
4383
|
-
|
4384
|
-
|
4385
|
-
|
4386
|
-
|
4379
|
+
except CredentialsNotValid as e:
|
4380
|
+
# NOTE: CredentialsNotValid is raised only when the server
|
4381
|
+
# explicitly indicates that the credentials are not valid and
|
4382
|
+
# they can be thrown away or when the request is not
|
4383
|
+
# authenticated at all.
|
4384
|
+
|
4385
|
+
if self._api_token is None:
|
4386
|
+
# The last request was not authenticated with an API
|
4387
|
+
# token at all. We authenticate here and then try the
|
4388
|
+
# request again, this time with a valid API token in the
|
4389
|
+
# header.
|
4390
|
+
logger.debug(
|
4391
|
+
f"The last request was not authenticated: {e}\n"
|
4392
|
+
"Re-authenticating and retrying..."
|
4393
|
+
)
|
4394
|
+
self.authenticate()
|
4395
|
+
elif not re_authenticated:
|
4396
|
+
# The last request was authenticated with an API token
|
4397
|
+
# that was rejected by the server. We attempt a
|
4398
|
+
# re-authentication here and then retry the request.
|
4399
|
+
logger.debug(
|
4400
|
+
"The last request was authenticated with an API token "
|
4401
|
+
f"that was rejected by the server: {e}\n"
|
4402
|
+
"Re-authenticating and retrying..."
|
4403
|
+
)
|
4404
|
+
re_authenticated = True
|
4405
|
+
self.authenticate(
|
4406
|
+
# Ignore the current token and force a re-authentication
|
4407
|
+
force=True
|
4408
|
+
)
|
4409
|
+
else:
|
4410
|
+
# The last request was made after re-authenticating but
|
4411
|
+
# still failed. Bailing out.
|
4412
|
+
logger.debug(
|
4413
|
+
f"The last request failed after re-authenticating: {e}\n"
|
4414
|
+
"Bailing out..."
|
4415
|
+
)
|
4416
|
+
raise CredentialsNotValid(
|
4417
|
+
"The current credentials are no longer valid. Please "
|
4418
|
+
"log in again using 'zenml login'."
|
4419
|
+
) from e
|
4387
4420
|
|
4388
4421
|
def get(
|
4389
4422
|
self,
|
@@ -15,10 +15,16 @@
|
|
15
15
|
|
16
16
|
from datetime import datetime
|
17
17
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional, cast
|
18
|
-
from uuid import UUID
|
18
|
+
from uuid import UUID, uuid4
|
19
19
|
|
20
20
|
from pydantic import ConfigDict
|
21
|
-
from sqlalchemy import
|
21
|
+
from sqlalchemy import (
|
22
|
+
BOOLEAN,
|
23
|
+
INTEGER,
|
24
|
+
TEXT,
|
25
|
+
Column,
|
26
|
+
UniqueConstraint,
|
27
|
+
)
|
22
28
|
from sqlmodel import Field, Relationship
|
23
29
|
|
24
30
|
from zenml.enums import (
|
@@ -228,11 +234,13 @@ class ModelVersionSchema(NamedSchema, RunMetadataInterface, table=True):
|
|
228
234
|
|
229
235
|
__tablename__ = MODEL_VERSION_TABLENAME
|
230
236
|
__table_args__ = (
|
231
|
-
# We need
|
237
|
+
# We need three unique constraints here:
|
232
238
|
# - The first to ensure that each model version for a
|
233
239
|
# model has a unique version number
|
234
240
|
# - The second one to ensure that explicit names given by
|
235
241
|
# users are unique
|
242
|
+
# - The third one to ensure that a pipeline run only produces a single
|
243
|
+
# auto-incremented version per model
|
236
244
|
UniqueConstraint(
|
237
245
|
"number",
|
238
246
|
"model_id",
|
@@ -243,6 +251,11 @@ class ModelVersionSchema(NamedSchema, RunMetadataInterface, table=True):
|
|
243
251
|
"model_id",
|
244
252
|
name="unique_version_for_model_id",
|
245
253
|
),
|
254
|
+
UniqueConstraint(
|
255
|
+
"model_id",
|
256
|
+
"producer_run_id_if_numeric",
|
257
|
+
name="unique_numeric_version_for_pipeline_run",
|
258
|
+
),
|
246
259
|
)
|
247
260
|
|
248
261
|
workspace_id: UUID = build_foreign_key_field(
|
@@ -312,12 +325,23 @@ class ModelVersionSchema(NamedSchema, RunMetadataInterface, table=True):
|
|
312
325
|
),
|
313
326
|
)
|
314
327
|
pipeline_runs: List["PipelineRunSchema"] = Relationship(
|
315
|
-
back_populates="model_version"
|
328
|
+
back_populates="model_version",
|
316
329
|
)
|
317
330
|
step_runs: List["StepRunSchema"] = Relationship(
|
318
331
|
back_populates="model_version"
|
319
332
|
)
|
320
333
|
|
334
|
+
# We want to make sure each pipeline run only creates a single numeric
|
335
|
+
# version for each model. To solve this, we need to add a unique constraint.
|
336
|
+
# If a value of a unique constraint is NULL it is ignored and the
|
337
|
+
# remaining values in the unique constraint have to be unique. In
|
338
|
+
# our case however, we only want the unique constraint applied in
|
339
|
+
# case there is a producer run and only for numeric versions. To solve this,
|
340
|
+
# we fall back to the model version ID (which is the primary key and
|
341
|
+
# therefore unique) in case there is no producer run or the version is not
|
342
|
+
# numeric.
|
343
|
+
producer_run_id_if_numeric: UUID
|
344
|
+
|
321
345
|
# TODO: In Pydantic v2, the `model_` is a protected namespaces for all
|
322
346
|
# fields defined under base models. If not handled, this raises a warning.
|
323
347
|
# It is possible to suppress this warning message with the following
|
@@ -328,24 +352,36 @@ class ModelVersionSchema(NamedSchema, RunMetadataInterface, table=True):
|
|
328
352
|
|
329
353
|
@classmethod
|
330
354
|
def from_request(
|
331
|
-
cls,
|
355
|
+
cls,
|
356
|
+
model_version_request: ModelVersionRequest,
|
357
|
+
model_version_number: int,
|
358
|
+
producer_run_id: Optional[UUID] = None,
|
332
359
|
) -> "ModelVersionSchema":
|
333
360
|
"""Convert an `ModelVersionRequest` to an `ModelVersionSchema`.
|
334
361
|
|
335
362
|
Args:
|
336
363
|
model_version_request: The request model version to convert.
|
364
|
+
model_version_number: The model version number.
|
365
|
+
producer_run_id: The ID of the producer run.
|
337
366
|
|
338
367
|
Returns:
|
339
368
|
The converted schema.
|
340
369
|
"""
|
370
|
+
id_ = uuid4()
|
371
|
+
is_numeric = str(model_version_number) == model_version_request.name
|
372
|
+
|
341
373
|
return cls(
|
374
|
+
id=id_,
|
342
375
|
workspace_id=model_version_request.workspace,
|
343
376
|
user_id=model_version_request.user,
|
344
377
|
model_id=model_version_request.model,
|
345
378
|
name=model_version_request.name,
|
346
|
-
number=
|
379
|
+
number=model_version_number,
|
347
380
|
description=model_version_request.description,
|
348
381
|
stage=model_version_request.stage,
|
382
|
+
producer_run_id_if_numeric=producer_run_id
|
383
|
+
if (producer_run_id and is_numeric)
|
384
|
+
else id_,
|
349
385
|
)
|
350
386
|
|
351
387
|
def to_model(
|
@@ -228,13 +228,6 @@ class PipelineDeploymentSchema(BaseSchema, table=True):
|
|
228
228
|
Returns:
|
229
229
|
The created `PipelineDeploymentResponse`.
|
230
230
|
"""
|
231
|
-
pipeline_configuration = PipelineConfiguration.model_validate_json(
|
232
|
-
self.pipeline_configuration
|
233
|
-
)
|
234
|
-
step_configurations = json.loads(self.step_configurations)
|
235
|
-
for s, c in step_configurations.items():
|
236
|
-
step_configurations[s] = Step.model_validate(c)
|
237
|
-
|
238
231
|
body = PipelineDeploymentResponseBody(
|
239
232
|
user=self.user.to_model() if self.user else None,
|
240
233
|
created=self.created,
|
@@ -242,6 +235,13 @@ class PipelineDeploymentSchema(BaseSchema, table=True):
|
|
242
235
|
)
|
243
236
|
metadata = None
|
244
237
|
if include_metadata:
|
238
|
+
pipeline_configuration = PipelineConfiguration.model_validate_json(
|
239
|
+
self.pipeline_configuration
|
240
|
+
)
|
241
|
+
step_configurations = json.loads(self.step_configurations)
|
242
|
+
for s, c in step_configurations.items():
|
243
|
+
step_configurations[s] = Step.model_validate(c)
|
244
|
+
|
245
245
|
metadata = PipelineDeploymentResponseMetadata(
|
246
246
|
workspace=self.workspace.to_model(),
|
247
247
|
run_name_template=self.run_name_template,
|
@@ -298,7 +298,7 @@ class PipelineRunSchema(NamedSchema, RunMetadataInterface, table=True):
|
|
298
298
|
)
|
299
299
|
|
300
300
|
if self.deployment is not None:
|
301
|
-
deployment = self.deployment.to_model()
|
301
|
+
deployment = self.deployment.to_model(include_metadata=True)
|
302
302
|
|
303
303
|
config = deployment.pipeline_configuration
|
304
304
|
new_substitutions = config._get_full_substitutions(self.start_time)
|
@@ -365,12 +365,18 @@ class PipelineRunSchema(NamedSchema, RunMetadataInterface, table=True):
|
|
365
365
|
):
|
366
366
|
is_templatable = True
|
367
367
|
|
368
|
-
steps = {
|
369
|
-
|
370
|
-
|
371
|
-
step_name: step.config.substitutions
|
372
|
-
for step_name, step in steps.items()
|
368
|
+
steps = {
|
369
|
+
step.name: step.to_model(include_metadata=True)
|
370
|
+
for step in self.step_runs
|
373
371
|
}
|
372
|
+
|
373
|
+
step_substitutions = {}
|
374
|
+
for step_name, step in steps.items():
|
375
|
+
step_substitutions[step_name] = step.config.substitutions
|
376
|
+
# We fetch the steps hydrated before, but want them unhydrated
|
377
|
+
# in the response -> We need to reset the metadata here
|
378
|
+
step.metadata = None
|
379
|
+
|
374
380
|
metadata = PipelineRunResponseMetadata(
|
375
381
|
workspace=self.workspace.to_model(),
|
376
382
|
run_metadata=self.fetch_metadata(),
|
@@ -156,7 +156,12 @@ class PipelineSchema(NamedSchema, table=True):
|
|
156
156
|
|
157
157
|
resources = None
|
158
158
|
if include_resources:
|
159
|
+
latest_run_user = self.runs[-1].user if self.runs else None
|
160
|
+
|
159
161
|
resources = PipelineResponseResources(
|
162
|
+
latest_run_user=latest_run_user.to_model()
|
163
|
+
if latest_run_user
|
164
|
+
else None,
|
160
165
|
tags=[t.tag.to_model() for t in self.tags],
|
161
166
|
)
|
162
167
|
|
@@ -19,7 +19,7 @@ from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
|
19
19
|
from uuid import UUID
|
20
20
|
|
21
21
|
from pydantic import ConfigDict
|
22
|
-
from sqlalchemy import TEXT, Column, String
|
22
|
+
from sqlalchemy import TEXT, Column, String, UniqueConstraint
|
23
23
|
from sqlalchemy.dialects.mysql import MEDIUMTEXT
|
24
24
|
from sqlmodel import Field, Relationship, SQLModel
|
25
25
|
|
@@ -67,6 +67,13 @@ class StepRunSchema(NamedSchema, RunMetadataInterface, table=True):
|
|
67
67
|
"""SQL Model for steps of pipeline runs."""
|
68
68
|
|
69
69
|
__tablename__ = "step_run"
|
70
|
+
__table_args__ = (
|
71
|
+
UniqueConstraint(
|
72
|
+
"name",
|
73
|
+
"pipeline_run_id",
|
74
|
+
name="unique_step_name_for_pipeline_run",
|
75
|
+
),
|
76
|
+
)
|
70
77
|
|
71
78
|
# Fields
|
72
79
|
start_time: Optional[datetime] = Field(nullable=True)
|