zenml-nightly 0.68.1.dev20241103__py3-none-any.whl → 0.70.0.dev20241115__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 +1 -1
- RELEASE_NOTES.md +77 -0
- zenml/VERSION +1 -1
- zenml/__init__.py +2 -0
- zenml/artifacts/external_artifact.py +2 -1
- zenml/artifacts/{load_directory_materializer.py → preexisting_data_materializer.py} +8 -9
- zenml/artifacts/utils.py +139 -80
- zenml/cli/base.py +4 -4
- zenml/cli/model.py +1 -6
- zenml/cli/stack.py +1 -0
- zenml/client.py +29 -74
- zenml/config/server_config.py +17 -1
- zenml/constants.py +2 -7
- zenml/data_validators/base_data_validator.py +2 -2
- zenml/enums.py +20 -4
- zenml/exceptions.py +4 -0
- zenml/integrations/__init__.py +3 -1
- zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +20 -18
- zenml/integrations/azure/orchestrators/azureml_orchestrator.py +1 -1
- zenml/integrations/bentoml/materializers/bentoml_bento_materializer.py +19 -31
- zenml/integrations/constants.py +1 -0
- zenml/integrations/deepchecks/data_validators/deepchecks_data_validator.py +1 -1
- zenml/integrations/evidently/__init__.py +1 -1
- zenml/integrations/gcp/orchestrators/vertex_orchestrator.py +1 -1
- zenml/integrations/huggingface/materializers/huggingface_datasets_materializer.py +8 -12
- zenml/integrations/huggingface/materializers/huggingface_pt_model_materializer.py +17 -18
- zenml/integrations/huggingface/materializers/huggingface_t5_materializer.py +2 -5
- zenml/integrations/huggingface/materializers/huggingface_tf_model_materializer.py +17 -18
- zenml/integrations/huggingface/materializers/huggingface_tokenizer_materializer.py +2 -3
- zenml/integrations/langchain/__init__.py +2 -1
- zenml/integrations/langchain/materializers/openai_embedding_materializer.py +28 -2
- zenml/integrations/lightgbm/materializers/lightgbm_booster_materializer.py +8 -15
- zenml/integrations/lightgbm/materializers/lightgbm_dataset_materializer.py +11 -16
- zenml/integrations/lightning/orchestrators/lightning_orchestrator.py +29 -9
- zenml/integrations/openai/__init__.py +1 -1
- zenml/integrations/openai/hooks/open_ai_failure_hook.py +39 -14
- zenml/integrations/pillow/materializers/pillow_image_materializer.py +17 -20
- zenml/integrations/polars/materializers/dataframe_materializer.py +26 -39
- zenml/integrations/pycaret/materializers/model_materializer.py +7 -22
- zenml/integrations/tensorboard/visualizers/tensorboard_visualizer.py +60 -54
- zenml/integrations/tensorflow/materializers/keras_materializer.py +11 -22
- zenml/integrations/tensorflow/materializers/tf_dataset_materializer.py +8 -15
- zenml/integrations/vllm/__init__.py +50 -0
- zenml/integrations/vllm/flavors/__init__.py +21 -0
- zenml/integrations/vllm/flavors/vllm_model_deployer_flavor.py +91 -0
- zenml/integrations/vllm/model_deployers/__init__.py +19 -0
- zenml/integrations/vllm/model_deployers/vllm_model_deployer.py +263 -0
- zenml/integrations/vllm/services/__init__.py +19 -0
- zenml/integrations/vllm/services/vllm_deployment.py +206 -0
- zenml/integrations/whylogs/materializers/whylogs_materializer.py +11 -18
- zenml/integrations/xgboost/materializers/xgboost_booster_materializer.py +11 -22
- zenml/integrations/xgboost/materializers/xgboost_dmatrix_materializer.py +10 -19
- zenml/materializers/base_materializer.py +68 -1
- zenml/metadata/lazy_load.py +20 -7
- zenml/model/model.py +17 -64
- zenml/model/utils.py +5 -0
- zenml/models/__init__.py +0 -12
- zenml/models/v2/base/filter.py +121 -8
- zenml/models/v2/core/artifact_version.py +42 -7
- zenml/models/v2/core/model_version.py +26 -5
- zenml/models/v2/core/pipeline_run.py +25 -6
- zenml/models/v2/core/run_metadata.py +2 -217
- zenml/models/v2/core/step_run.py +62 -24
- zenml/orchestrators/base_orchestrator.py +12 -1
- zenml/orchestrators/input_utils.py +44 -19
- zenml/orchestrators/step_launcher.py +4 -3
- zenml/orchestrators/step_run_utils.py +19 -15
- zenml/orchestrators/step_runner.py +25 -14
- zenml/orchestrators/utils.py +45 -26
- zenml/stack/flavor.py +9 -5
- zenml/stack_deployments/aws_stack_deployment.py +23 -6
- zenml/stack_deployments/azure_stack_deployment.py +28 -5
- zenml/stack_deployments/gcp_stack_deployment.py +25 -8
- zenml/stack_deployments/stack_deployment.py +3 -5
- zenml/steps/base_step.py +1 -1
- zenml/steps/entrypoint_function_utils.py +3 -5
- zenml/steps/step_context.py +5 -2
- zenml/steps/utils.py +13 -2
- zenml/utils/callback_registry.py +71 -0
- zenml/utils/metadata_utils.py +335 -0
- zenml/zen_server/auth.py +221 -3
- zenml/zen_server/cache.py +208 -0
- zenml/zen_server/dashboard/assets/{404-DT4QRUqN.js → 404-NVXKFp-x.js} +1 -1
- zenml/zen_server/dashboard/assets/{@radix-DP6vWzyx.js → @radix-DeK6qiuw.js} +1 -1
- zenml/zen_server/dashboard/assets/{@react-router-BMhZulnd.js → @react-router-B3Z5rLr2.js} +1 -1
- zenml/zen_server/dashboard/assets/{@reactflow-8U9qNlMR.js → @reactflow-CK0KJUen.js} +2 -2
- zenml/zen_server/dashboard/assets/{@tanstack-BUCbhJyH.js → @tanstack-DT5WLu9C.js} +1 -1
- zenml/zen_server/dashboard/assets/AlertDialogDropdownItem-DezXKmDf.js +1 -0
- zenml/zen_server/dashboard/assets/{CodeSnippet-CqybNv0k.js → CodeSnippet-JzR8CEtw.js} +2 -2
- zenml/zen_server/dashboard/assets/{CollapsibleCard-0r_8G2Lj.js → CollapsibleCard-DQW_ktMO.js} +1 -1
- zenml/zen_server/dashboard/assets/{Commands-BDjgBQKi.js → Commands-DL2kwkRd.js} +1 -1
- zenml/zen_server/dashboard/assets/ComponentBadge-D_g62Wv8.js +1 -0
- zenml/zen_server/dashboard/assets/{CopyButton-C745BrKi.js → CopyButton-LNcWaa14.js} +1 -1
- zenml/zen_server/dashboard/assets/{CsvVizualization-PpAq0CeZ.js → CsvVizualization-DknpE5ej.js} +5 -5
- zenml/zen_server/dashboard/assets/{DialogItem-DcVCZEew.js → DialogItem-Bxf8FuAT.js} +1 -1
- zenml/zen_server/dashboard/assets/{DisplayDate-BeXgUG_C.js → DisplayDate-CDMUcQHS.js} +1 -1
- zenml/zen_server/dashboard/assets/{EmptyState-DeK7H4pr.js → EmptyState-BzdlCwp3.js} +1 -1
- zenml/zen_server/dashboard/assets/{Error-BMlzibXj.js → Error-DYflYyps.js} +1 -1
- zenml/zen_server/dashboard/assets/ExecutionStatus-C7zyIQKZ.js +1 -0
- zenml/zen_server/dashboard/assets/{Helpbox-BLf40fLV.js → Helpbox-oYSGpLqd.js} +1 -1
- zenml/zen_server/dashboard/assets/{Infobox-BwisKifi.js → Infobox-Cx4xGoXR.js} +1 -1
- zenml/zen_server/dashboard/assets/{InlineAvatar-jEgodSgX.js → InlineAvatar-DiGOWNKF.js} +1 -1
- zenml/zen_server/dashboard/assets/{Lock-3lLt1ih0.js → Lock-CYYy18Mm.js} +1 -1
- zenml/zen_server/dashboard/assets/{MarkdownVisualization-8O9kTr-2.js → MarkdownVisualization-ylXaAxev.js} +1 -1
- zenml/zen_server/dashboard/assets/NestedCollapsible-DYbgyKxK.js +1 -0
- zenml/zen_server/dashboard/assets/{NumberBox-T9eELfLZ.js → NumberBox-Dtp3J6g5.js} +1 -1
- zenml/zen_server/dashboard/assets/Partials-03iZf8-N.js +1 -0
- zenml/zen_server/dashboard/assets/{PasswordChecker-CW0kqY0W.js → PasswordChecker-B0nadgh6.js} +1 -1
- zenml/zen_server/dashboard/assets/ProBadge-D_EB8HNo.js +1 -0
- zenml/zen_server/dashboard/assets/ProCta-DqNS4v3x.js +1 -0
- zenml/zen_server/dashboard/assets/ProviderIcon-Bki2aw8w.js +1 -0
- zenml/zen_server/dashboard/assets/{ProviderRadio-BROY1700.js → ProviderRadio-8f43sPD4.js} +1 -1
- zenml/zen_server/dashboard/assets/RunSelector-DkPiIiNr.js +1 -0
- zenml/zen_server/dashboard/assets/RunsBody-07YEO7qI.js +1 -0
- zenml/zen_server/dashboard/assets/SearchField-lp1KgU4e.js +1 -0
- zenml/zen_server/dashboard/assets/{SecretTooltip-C_qByGWB.js → SecretTooltip-CgnbyeOx.js} +1 -1
- zenml/zen_server/dashboard/assets/{SetPassword-7pRB00El.js → SetPassword-CpP418A2.js} +1 -1
- zenml/zen_server/dashboard/assets/StackList-WvuKQusZ.js +1 -0
- zenml/zen_server/dashboard/assets/Tabs-BktHkCJJ.js +1 -0
- zenml/zen_server/dashboard/assets/Tick-BlMoIlJT.js +1 -0
- zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-DckMEkFf.js → UpdatePasswordSchemas-Sc0A0pP-.js} +1 -1
- zenml/zen_server/dashboard/assets/{UsageReason-DVceN14P.js → UsageReason-YYduL4fj.js} +1 -1
- zenml/zen_server/dashboard/assets/{WizardFooter-CW0Cvd70.js → WizardFooter-dgmizSJC.js} +1 -1
- zenml/zen_server/dashboard/assets/all-pipeline-runs-query-D-c2G6lV.js +1 -0
- zenml/zen_server/dashboard/assets/check-DloQpStc.js +1 -0
- zenml/zen_server/dashboard/assets/{check-circle-Dwxliy1Z.js → check-circle-jNbX5-sR.js} +1 -1
- zenml/zen_server/dashboard/assets/{chevron-down-8wLBS5pQ.js → chevron-down-6JyMkfjR.js} +1 -1
- zenml/zen_server/dashboard/assets/{chevron-right-double-DoD8iXWM.js → chevron-right-double-D7ojK9Co.js} +1 -1
- zenml/zen_server/dashboard/assets/{code-browser-CZUQs3Wa.js → code-browser-CUFUIHfp.js} +1 -1
- zenml/zen_server/dashboard/assets/{copy-CaSMXwiU.js → copy-C8XQA2Ug.js} +1 -1
- zenml/zen_server/dashboard/assets/create-stack-DM_JPgef.js +1 -0
- zenml/zen_server/dashboard/assets/delete-run-CJdh1P_h.js +1 -0
- zenml/zen_server/dashboard/assets/{docker-BFNgg-z3.js → docker-BdA9vrnW.js} +1 -1
- zenml/zen_server/dashboard/assets/{dots-horizontal-DK5Duzx4.js → dots-horizontal-otGBOSDJ.js} +1 -1
- zenml/zen_server/dashboard/assets/{form-schemas-1AyOCx90.js → form-schemas-K6FYKjwa.js} +1 -1
- zenml/zen_server/dashboard/assets/{gcp-7M2Yf3ZK.js → gcp-CFtm4BA7.js} +1 -1
- zenml/zen_server/dashboard/assets/{help-Dam461dC.js → help-Cc9bBIJH.js} +1 -1
- zenml/zen_server/dashboard/assets/index-B1mVPYxf.js +1 -0
- zenml/zen_server/dashboard/assets/index-BAkC7FXi.js +1 -0
- zenml/zen_server/dashboard/assets/{index-QQb7wQEC.js → index-CCOPpudF.js} +8 -8
- zenml/zen_server/dashboard/assets/index-CEV4Cvaf.js +1 -0
- zenml/zen_server/dashboard/assets/index-DlGvJQPn.css +1 -0
- zenml/zen_server/dashboard/assets/{index-BVJ8n2-j.js → index-Uu49AX48.js} +1 -1
- zenml/zen_server/dashboard/assets/{index.esm-cuVep_NJ.js → index.esm-Dy6Z9Ung.js} +1 -1
- zenml/zen_server/dashboard/assets/{kubernetes--g7r02Zu.js → kubernetes-B2wmAJ1d.js} +1 -1
- zenml/zen_server/dashboard/assets/{layout-DCSYN7-C.js → layout-BtHBmE4w.js} +1 -1
- zenml/zen_server/dashboard/assets/{link-external-CBEk6kEG.js → link-external-b9AXw_sW.js} +1 -1
- zenml/zen_server/dashboard/assets/{login-mutation-DTcAFP1l.js → login-mutation-hf-lK87O.js} +1 -1
- zenml/zen_server/dashboard/assets/{logs-D5bdJGur.js → logs-WMSM52RF.js} +1 -1
- zenml/zen_server/dashboard/assets/{not-found-Cc-JkRH2.js → not-found-BGirLjU-.js} +1 -1
- zenml/zen_server/dashboard/assets/{package-Cs35Szwh.js → package-C6uypY4h.js} +1 -1
- zenml/zen_server/dashboard/assets/page-0JE_-Ec1.js +1 -0
- zenml/zen_server/dashboard/assets/{page-DH_Z7iW1.js → page-6m6yHHlE.js} +1 -1
- zenml/zen_server/dashboard/assets/page-BDigxVpo.js +1 -0
- zenml/zen_server/dashboard/assets/page-BR68V0V1.js +1 -0
- zenml/zen_server/dashboard/assets/page-BRLpxOt0.js +1 -0
- zenml/zen_server/dashboard/assets/{page-BQQKaabe.js → page-BU7huvKw.js} +3 -3
- zenml/zen_server/dashboard/assets/page-BvqLv2Ky.js +1 -0
- zenml/zen_server/dashboard/assets/page-C00YAkaB.js +1 -0
- zenml/zen_server/dashboard/assets/{page-N4qoPHKb.js → page-CD-DcWoy.js} +1 -1
- zenml/zen_server/dashboard/assets/page-COXXJj1k.js +1 -0
- zenml/zen_server/dashboard/assets/page-CbpvrsDL.js +1 -0
- zenml/zen_server/dashboard/assets/page-CdMWnQak.js +1 -0
- zenml/zen_server/dashboard/assets/{page-ClUVkl-O.js → page-CjGdWY13.js} +1 -1
- zenml/zen_server/dashboard/assets/page-CwxrFarU.js +1 -0
- zenml/zen_server/dashboard/assets/{page-DLixvR-7.js → page-D01JhjQB.js} +1 -1
- zenml/zen_server/dashboard/assets/page-D6uU2ax4.js +1 -0
- zenml/zen_server/dashboard/assets/page-D7S3aCbF.js +1 -0
- zenml/zen_server/dashboard/assets/{page-9yplj5JT.js → page-DLC-bNBP.js} +1 -1
- zenml/zen_server/dashboard/assets/page-DXSTpqRD.js +1 -0
- zenml/zen_server/dashboard/assets/{page-DzpVUZ8f.js → page-DakHVWXF.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-DIOXwhiD.js → page-Df-Fw0aq.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-B-y2XKIc.js → page-DfbXf_8s.js} +1 -1
- zenml/zen_server/dashboard/assets/page-DjRJCGb3.js +1 -0
- zenml/zen_server/dashboard/assets/{page-C0N5q3l7.js → page-Djikxq_S.js} +1 -1
- zenml/zen_server/dashboard/assets/page-Dnovpa0i.js +3 -0
- zenml/zen_server/dashboard/assets/page-Dot3LPmL.js +1 -0
- zenml/zen_server/dashboard/assets/page-Vcxara9U.js +1 -0
- zenml/zen_server/dashboard/assets/page-Xynx4btY.js +14 -0
- zenml/zen_server/dashboard/assets/page-YpKAqVSa.js +1 -0
- zenml/zen_server/dashboard/assets/page-yYC9OI-E.js +1 -0
- zenml/zen_server/dashboard/assets/{persist-DNb5cdrU.js → persist-Coz7ZWvz.js} +1 -1
- zenml/zen_server/dashboard/assets/{persist-CP0JmYZ4.js → persist-GjC8PZoC.js} +1 -1
- zenml/zen_server/dashboard/assets/{plus-C9IxgN2M.js → plus-tf1V2hTJ.js} +1 -1
- zenml/zen_server/dashboard/assets/{refresh-BVu22P_C.js → refresh-BjOeWlEq.js} +1 -1
- zenml/zen_server/dashboard/assets/{rocket-CONEmRmB.js → rocket-DjT2cDvG.js} +1 -1
- zenml/zen_server/dashboard/assets/sharedSchema-CQb14VSr.js +14 -0
- zenml/zen_server/dashboard/assets/stack-detail-query-OPEW-cDJ.js +1 -0
- zenml/zen_server/dashboard/assets/{tick-circle-CM1ZScbQ.js → tick-circle-BEX_Tp4v.js} +1 -1
- zenml/zen_server/dashboard/assets/{trash-DkJHMOg7.js → trash-arLUMWMS.js} +1 -1
- zenml/zen_server/dashboard/assets/{update-server-settings-mutation-DsU8cNVl.js → update-server-settings-mutation-LwuQfHYn.js} +1 -1
- zenml/zen_server/dashboard/assets/upgrade-form-CwRHBuXB.webp +0 -0
- zenml/zen_server/dashboard/assets/url-CkvKAnwF.js +1 -0
- zenml/zen_server/dashboard/assets/{zod-D89GC_vc.js → zod-BwEbpOxH.js} +1 -1
- zenml/zen_server/dashboard/index.html +7 -7
- zenml/zen_server/deploy/helm/Chart.yaml +1 -1
- zenml/zen_server/deploy/helm/README.md +2 -2
- zenml/zen_server/exceptions.py +2 -0
- zenml/zen_server/jwt.py +30 -13
- zenml/zen_server/rbac/endpoint_utils.py +43 -1
- zenml/zen_server/rbac/utils.py +0 -2
- zenml/zen_server/routers/artifact_version_endpoints.py +27 -1
- zenml/zen_server/routers/auth_endpoints.py +134 -102
- zenml/zen_server/routers/logs_endpoints.py +66 -0
- zenml/zen_server/routers/workspaces_endpoints.py +3 -4
- zenml/zen_server/template_execution/utils.py +14 -16
- zenml/zen_server/utils.py +27 -0
- zenml/zen_server/zen_server_api.py +6 -3
- zenml/zen_stores/migrations/versions/0.70.0_release.py +23 -0
- zenml/zen_stores/migrations/versions/1cb6477f72d6_move_artifact_save_type.py +99 -0
- zenml/zen_stores/migrations/versions/904464ea4041_add_pipeline_model_run_unique_constraints.py +192 -0
- zenml/zen_stores/migrations/versions/b557b2871693_update_step_run_input_types.py +33 -0
- zenml/zen_stores/rest_zen_store.py +68 -64
- zenml/zen_stores/schemas/artifact_schemas.py +8 -1
- zenml/zen_stores/schemas/model_schemas.py +27 -3
- zenml/zen_stores/schemas/pipeline_run_schemas.py +6 -1
- zenml/zen_stores/schemas/pipeline_schemas.py +8 -2
- zenml/zen_stores/schemas/run_metadata_schemas.py +1 -48
- zenml/zen_stores/schemas/step_run_schemas.py +18 -10
- zenml/zen_stores/sql_zen_store.py +283 -219
- zenml/zen_stores/zen_store_interface.py +15 -42
- {zenml_nightly-0.68.1.dev20241103.dist-info → zenml_nightly-0.70.0.dev20241115.dist-info}/METADATA +2 -2
- {zenml_nightly-0.68.1.dev20241103.dist-info → zenml_nightly-0.70.0.dev20241115.dist-info}/RECORD +226 -203
- zenml/zen_server/dashboard/assets/AlertDialogDropdownItem-C6N2rGrB.js +0 -1
- zenml/zen_server/dashboard/assets/ComponentBadge-DUiEYJHu.js +0 -1
- zenml/zen_server/dashboard/assets/ComponentFallbackDialog-BFoH5K4V.js +0 -1
- zenml/zen_server/dashboard/assets/ComponentIcon-CAIoUis2.js +0 -1
- zenml/zen_server/dashboard/assets/Partials-YPBB3V4q.js +0 -1
- zenml/zen_server/dashboard/assets/ProviderIcon-Bb3Xha5A.js +0 -1
- zenml/zen_server/dashboard/assets/RunSelector-DCiL3M1c.js +0 -1
- zenml/zen_server/dashboard/assets/SearchField-DfUiGFVd.js +0 -1
- zenml/zen_server/dashboard/assets/Tick-CykQFPj2.js +0 -1
- zenml/zen_server/dashboard/assets/cloud-only-B-s_HMDm.js +0 -1
- zenml/zen_server/dashboard/assets/codespaces-BitYDX9d.gif +0 -0
- zenml/zen_server/dashboard/assets/create-stack-CEmaPZ4c.js +0 -1
- zenml/zen_server/dashboard/assets/delete-run-D-LKbGyz.js +0 -1
- zenml/zen_server/dashboard/assets/index-Bpmj40BI.js +0 -1
- zenml/zen_server/dashboard/assets/index-CbU4Ln_E.css +0 -1
- zenml/zen_server/dashboard/assets/index-DKPhqP2B.js +0 -1
- zenml/zen_server/dashboard/assets/page-BBpOxVcY.js +0 -1
- zenml/zen_server/dashboard/assets/page-BRInM1Lg.js +0 -1
- zenml/zen_server/dashboard/assets/page-BjjlMk7s.js +0 -1
- zenml/zen_server/dashboard/assets/page-Bvd7YH2A.js +0 -1
- zenml/zen_server/dashboard/assets/page-CT3Nep8W.js +0 -1
- zenml/zen_server/dashboard/assets/page-C_f47pBf.js +0 -1
- zenml/zen_server/dashboard/assets/page-Cmv8C_yM.js +0 -3
- zenml/zen_server/dashboard/assets/page-CyN2bdWG.js +0 -1
- zenml/zen_server/dashboard/assets/page-CzzXH4fs.js +0 -1
- zenml/zen_server/dashboard/assets/page-DTlGjgnG.js +0 -1
- zenml/zen_server/dashboard/assets/page-Dbpl86h0.js +0 -1
- zenml/zen_server/dashboard/assets/page-Ddgy6kDS.js +0 -1
- zenml/zen_server/dashboard/assets/page-DtCAfBLy.js +0 -9
- zenml/zen_server/dashboard/assets/page-Dx16z7nA.js +0 -1
- zenml/zen_server/dashboard/assets/page-McUyYbo1.js +0 -1
- zenml/zen_server/dashboard/assets/page-T1P3RyAR.js +0 -1
- zenml/zen_server/dashboard/assets/page-bKaULTGG.js +0 -1
- zenml/zen_server/dashboard/assets/page-sbXUJy9t.js +0 -1
- zenml/zen_server/dashboard/assets/sharedSchema-TMLu-nYQ.js +0 -14
- zenml/zen_server/dashboard/assets/stack-detail-query-xmYxSsUY.js +0 -1
- zenml/zen_server/dashboard/assets/url-D5le3J4q.js +0 -1
- zenml/zen_server/routers/run_metadata_endpoints.py +0 -96
- {zenml_nightly-0.68.1.dev20241103.dist-info → zenml_nightly-0.70.0.dev20241115.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.68.1.dev20241103.dist-info → zenml_nightly-0.70.0.dev20241115.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.68.1.dev20241103.dist-info → zenml_nightly-0.70.0.dev20241115.dist-info}/entry_points.txt +0 -0
zenml/zen_server/auth.py
CHANGED
@@ -14,13 +14,13 @@
|
|
14
14
|
"""Authentication module for ZenML server."""
|
15
15
|
|
16
16
|
from contextvars import ContextVar
|
17
|
-
from datetime import datetime
|
17
|
+
from datetime import datetime, timedelta
|
18
18
|
from typing import Callable, Optional, Union
|
19
19
|
from urllib.parse import urlencode
|
20
20
|
from uuid import UUID
|
21
21
|
|
22
22
|
import requests
|
23
|
-
from fastapi import Depends
|
23
|
+
from fastapi import Depends, Response
|
24
24
|
from fastapi.security import (
|
25
25
|
HTTPBasic,
|
26
26
|
HTTPBasicCredentials,
|
@@ -37,7 +37,7 @@ from zenml.constants import (
|
|
37
37
|
LOGIN,
|
38
38
|
VERSION_1,
|
39
39
|
)
|
40
|
-
from zenml.enums import AuthScheme, OAuthDeviceStatus
|
40
|
+
from zenml.enums import AuthScheme, ExecutionStatus, OAuthDeviceStatus
|
41
41
|
from zenml.exceptions import (
|
42
42
|
AuthorizationException,
|
43
43
|
CredentialsNotValid,
|
@@ -51,11 +51,13 @@ from zenml.models import (
|
|
51
51
|
ExternalUserModel,
|
52
52
|
OAuthDeviceInternalResponse,
|
53
53
|
OAuthDeviceInternalUpdate,
|
54
|
+
OAuthTokenResponse,
|
54
55
|
UserAuthModel,
|
55
56
|
UserRequest,
|
56
57
|
UserResponse,
|
57
58
|
UserUpdate,
|
58
59
|
)
|
60
|
+
from zenml.zen_server.cache import cache_result
|
59
61
|
from zenml.zen_server.exceptions import http_exception_from_error
|
60
62
|
from zenml.zen_server.jwt import JWTToken
|
61
63
|
from zenml.zen_server.utils import server_config, zen_store
|
@@ -329,6 +331,148 @@ def authenticate_credentials(
|
|
329
331
|
),
|
330
332
|
)
|
331
333
|
|
334
|
+
if decoded_token.schedule_id:
|
335
|
+
# If the token contains a schedule ID, we need to check if the
|
336
|
+
# schedule still exists in the database. We use a cached version
|
337
|
+
# of the schedule active status to avoid unnecessary database
|
338
|
+
# queries.
|
339
|
+
|
340
|
+
@cache_result(expiry=30)
|
341
|
+
def get_schedule_active(schedule_id: UUID) -> Optional[bool]:
|
342
|
+
"""Get the active status of a schedule.
|
343
|
+
|
344
|
+
Args:
|
345
|
+
schedule_id: The schedule ID.
|
346
|
+
|
347
|
+
Returns:
|
348
|
+
The schedule active status or None if the schedule does not
|
349
|
+
exist.
|
350
|
+
"""
|
351
|
+
try:
|
352
|
+
schedule = zen_store().get_schedule(
|
353
|
+
schedule_id, hydrate=False
|
354
|
+
)
|
355
|
+
except KeyError:
|
356
|
+
return False
|
357
|
+
|
358
|
+
return schedule.active
|
359
|
+
|
360
|
+
schedule_active = get_schedule_active(decoded_token.schedule_id)
|
361
|
+
if schedule_active is None:
|
362
|
+
error = (
|
363
|
+
f"Authentication error: error retrieving token schedule "
|
364
|
+
f"{decoded_token.schedule_id}"
|
365
|
+
)
|
366
|
+
logger.error(error)
|
367
|
+
raise CredentialsNotValid(error)
|
368
|
+
|
369
|
+
if not schedule_active:
|
370
|
+
error = (
|
371
|
+
f"Authentication error: schedule {decoded_token.schedule_id} "
|
372
|
+
"is not active"
|
373
|
+
)
|
374
|
+
logger.error(error)
|
375
|
+
raise CredentialsNotValid(error)
|
376
|
+
|
377
|
+
if decoded_token.pipeline_run_id:
|
378
|
+
# If the token contains a pipeline run ID, we need to check if the
|
379
|
+
# pipeline run exists in the database and the pipeline run has
|
380
|
+
# not concluded. We use a cached version of the pipeline run status
|
381
|
+
# to avoid unnecessary database queries.
|
382
|
+
|
383
|
+
@cache_result(expiry=30)
|
384
|
+
def get_pipeline_run_status(
|
385
|
+
pipeline_run_id: UUID,
|
386
|
+
) -> Optional[ExecutionStatus]:
|
387
|
+
"""Get the status of a pipeline run.
|
388
|
+
|
389
|
+
Args:
|
390
|
+
pipeline_run_id: The pipeline run ID.
|
391
|
+
|
392
|
+
Returns:
|
393
|
+
The pipeline run status or None if the pipeline run does not
|
394
|
+
exist.
|
395
|
+
"""
|
396
|
+
try:
|
397
|
+
pipeline_run = zen_store().get_run(
|
398
|
+
pipeline_run_id, hydrate=False
|
399
|
+
)
|
400
|
+
except KeyError:
|
401
|
+
return None
|
402
|
+
|
403
|
+
return pipeline_run.status
|
404
|
+
|
405
|
+
pipeline_run_status = get_pipeline_run_status(
|
406
|
+
decoded_token.pipeline_run_id
|
407
|
+
)
|
408
|
+
if pipeline_run_status is None:
|
409
|
+
error = (
|
410
|
+
f"Authentication error: error retrieving token pipeline run "
|
411
|
+
f"{decoded_token.pipeline_run_id}"
|
412
|
+
)
|
413
|
+
logger.error(error)
|
414
|
+
raise CredentialsNotValid(error)
|
415
|
+
|
416
|
+
if pipeline_run_status in [
|
417
|
+
ExecutionStatus.FAILED,
|
418
|
+
ExecutionStatus.COMPLETED,
|
419
|
+
]:
|
420
|
+
error = (
|
421
|
+
f"The execution of pipeline run "
|
422
|
+
f"{decoded_token.pipeline_run_id} has already concluded and "
|
423
|
+
"API tokens scoped to it are no longer valid."
|
424
|
+
)
|
425
|
+
logger.error(error)
|
426
|
+
raise CredentialsNotValid(error)
|
427
|
+
|
428
|
+
if decoded_token.step_run_id:
|
429
|
+
# If the token contains a step run ID, we need to check if the
|
430
|
+
# step run exists in the database and the step run has not concluded.
|
431
|
+
# We use a cached version of the step run status to avoid unnecessary
|
432
|
+
# database queries.
|
433
|
+
|
434
|
+
@cache_result(expiry=30)
|
435
|
+
def get_step_run_status(
|
436
|
+
step_run_id: UUID,
|
437
|
+
) -> Optional[ExecutionStatus]:
|
438
|
+
"""Get the status of a step run.
|
439
|
+
|
440
|
+
Args:
|
441
|
+
step_run_id: The step run ID.
|
442
|
+
|
443
|
+
Returns:
|
444
|
+
The step run status or None if the step run does not exist.
|
445
|
+
"""
|
446
|
+
try:
|
447
|
+
step_run = zen_store().get_run_step(
|
448
|
+
step_run_id, hydrate=False
|
449
|
+
)
|
450
|
+
except KeyError:
|
451
|
+
return None
|
452
|
+
|
453
|
+
return step_run.status
|
454
|
+
|
455
|
+
step_run_status = get_step_run_status(decoded_token.step_run_id)
|
456
|
+
if step_run_status is None:
|
457
|
+
error = (
|
458
|
+
f"Authentication error: error retrieving token step run "
|
459
|
+
f"{decoded_token.step_run_id}"
|
460
|
+
)
|
461
|
+
logger.error(error)
|
462
|
+
raise CredentialsNotValid(error)
|
463
|
+
|
464
|
+
if step_run_status in [
|
465
|
+
ExecutionStatus.FAILED,
|
466
|
+
ExecutionStatus.COMPLETED,
|
467
|
+
]:
|
468
|
+
error = (
|
469
|
+
f"The execution of step run "
|
470
|
+
f"{decoded_token.step_run_id} has already concluded and "
|
471
|
+
"API tokens scoped to it are no longer valid."
|
472
|
+
)
|
473
|
+
logger.error(error)
|
474
|
+
raise CredentialsNotValid(error)
|
475
|
+
|
332
476
|
auth_context = AuthContext(
|
333
477
|
user=user_model,
|
334
478
|
access_token=decoded_token,
|
@@ -660,6 +804,80 @@ def authenticate_api_key(
|
|
660
804
|
return AuthContext(user=user_model, api_key=internal_api_key)
|
661
805
|
|
662
806
|
|
807
|
+
def generate_access_token(
|
808
|
+
user_id: UUID,
|
809
|
+
response: Optional[Response] = None,
|
810
|
+
device: Optional[OAuthDeviceInternalResponse] = None,
|
811
|
+
api_key: Optional[APIKeyInternalResponse] = None,
|
812
|
+
expires_in: Optional[int] = None,
|
813
|
+
schedule_id: Optional[UUID] = None,
|
814
|
+
pipeline_run_id: Optional[UUID] = None,
|
815
|
+
step_run_id: Optional[UUID] = None,
|
816
|
+
) -> OAuthTokenResponse:
|
817
|
+
"""Generates an access token for the given user.
|
818
|
+
|
819
|
+
Args:
|
820
|
+
user_id: The ID of the user.
|
821
|
+
response: The FastAPI response object.
|
822
|
+
device: The device used for authentication.
|
823
|
+
api_key: The service account API key used for authentication.
|
824
|
+
expires_in: The number of seconds until the token expires.
|
825
|
+
schedule_id: The ID of the schedule to scope the token to.
|
826
|
+
pipeline_run_id: The ID of the pipeline run to scope the token to.
|
827
|
+
step_run_id: The ID of the step run to scope the token to.
|
828
|
+
|
829
|
+
Returns:
|
830
|
+
An authentication response with an access token.
|
831
|
+
"""
|
832
|
+
config = server_config()
|
833
|
+
|
834
|
+
# If the expiration time is not supplied, the JWT tokens are set to expire
|
835
|
+
# according to the values configured in the server config. Device tokens are
|
836
|
+
# handled separately from regular user tokens.
|
837
|
+
expires: Optional[datetime] = None
|
838
|
+
if expires_in:
|
839
|
+
expires = datetime.utcnow() + timedelta(seconds=expires_in)
|
840
|
+
elif device:
|
841
|
+
# If a device was used for authentication, the token will expire
|
842
|
+
# at the same time as the device.
|
843
|
+
expires = device.expires
|
844
|
+
if expires:
|
845
|
+
expires_in = max(
|
846
|
+
int(expires.timestamp() - datetime.utcnow().timestamp()), 0
|
847
|
+
)
|
848
|
+
elif config.jwt_token_expire_minutes:
|
849
|
+
expires = datetime.utcnow() + timedelta(
|
850
|
+
minutes=config.jwt_token_expire_minutes
|
851
|
+
)
|
852
|
+
expires_in = config.jwt_token_expire_minutes * 60
|
853
|
+
|
854
|
+
access_token = JWTToken(
|
855
|
+
user_id=user_id,
|
856
|
+
device_id=device.id if device else None,
|
857
|
+
api_key_id=api_key.id if api_key else None,
|
858
|
+
schedule_id=schedule_id,
|
859
|
+
pipeline_run_id=pipeline_run_id,
|
860
|
+
step_run_id=step_run_id,
|
861
|
+
).encode(expires=expires)
|
862
|
+
|
863
|
+
if not device and response:
|
864
|
+
# Also set the access token as an HTTP only cookie in the response
|
865
|
+
response.set_cookie(
|
866
|
+
key=config.get_auth_cookie_name(),
|
867
|
+
value=access_token,
|
868
|
+
httponly=True,
|
869
|
+
samesite="lax",
|
870
|
+
max_age=config.jwt_token_expire_minutes * 60
|
871
|
+
if config.jwt_token_expire_minutes
|
872
|
+
else None,
|
873
|
+
domain=config.auth_cookie_domain,
|
874
|
+
)
|
875
|
+
|
876
|
+
return OAuthTokenResponse(
|
877
|
+
access_token=access_token, expires_in=expires_in, token_type="bearer"
|
878
|
+
)
|
879
|
+
|
880
|
+
|
663
881
|
def http_authentication(
|
664
882
|
credentials: HTTPBasicCredentials = Depends(HTTPBasic()),
|
665
883
|
) -> AuthContext:
|
@@ -0,0 +1,208 @@
|
|
1
|
+
# Copyright (c) ZenML GmbH 2024. All Rights Reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at:
|
6
|
+
#
|
7
|
+
# https://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
12
|
+
# or implied. See the License for the specific language governing
|
13
|
+
# permissions and limitations under the License.
|
14
|
+
"""Memory cache module for the ZenML server."""
|
15
|
+
|
16
|
+
import time
|
17
|
+
from collections import OrderedDict
|
18
|
+
from threading import Lock
|
19
|
+
from typing import Any, Callable, Optional
|
20
|
+
from typing import OrderedDict as OrderedDictType
|
21
|
+
from uuid import UUID
|
22
|
+
|
23
|
+
from zenml.logger import get_logger
|
24
|
+
from zenml.utils.singleton import SingletonMetaClass
|
25
|
+
|
26
|
+
logger = get_logger(__name__)
|
27
|
+
|
28
|
+
|
29
|
+
class MemoryCacheEntry:
|
30
|
+
"""Simple class to hold cache entry data."""
|
31
|
+
|
32
|
+
def __init__(self, value: Any, expiry: int) -> None:
|
33
|
+
"""Initialize a cache entry with value and expiry time.
|
34
|
+
|
35
|
+
Args:
|
36
|
+
value: The value to store in the cache.
|
37
|
+
expiry: The expiry time in seconds.
|
38
|
+
"""
|
39
|
+
self.value: Any = value
|
40
|
+
self.expiry: int = expiry
|
41
|
+
self.timestamp: float = time.time()
|
42
|
+
|
43
|
+
@property
|
44
|
+
def expired(self) -> bool:
|
45
|
+
"""Check if the cache entry has expired.
|
46
|
+
|
47
|
+
Returns:
|
48
|
+
True if the cache entry has expired; otherwise, False.
|
49
|
+
"""
|
50
|
+
return time.time() - self.timestamp >= self.expiry
|
51
|
+
|
52
|
+
|
53
|
+
class MemoryCache(metaclass=SingletonMetaClass):
|
54
|
+
"""Simple in-memory cache with expiry and capacity management.
|
55
|
+
|
56
|
+
This cache is thread-safe and can be used in both synchronous and
|
57
|
+
asynchronous contexts. It uses a simple LRU (Least Recently Used) eviction
|
58
|
+
strategy to manage the cache size.
|
59
|
+
|
60
|
+
Each cache entry has a key, value, timestamp, and expiry. The cache
|
61
|
+
automatically removes expired entries and evicts the oldest entry when
|
62
|
+
the cache reaches its maximum capacity.
|
63
|
+
|
64
|
+
|
65
|
+
Usage Example:
|
66
|
+
|
67
|
+
cache = MemoryCache()
|
68
|
+
uuid_key = UUID("12345678123456781234567812345678")
|
69
|
+
|
70
|
+
if not cache.get(uuid_key):
|
71
|
+
# Get the value from the database or other source
|
72
|
+
value = get_value_from_database()
|
73
|
+
cache.set(uuid_key, value, expiry=60)
|
74
|
+
|
75
|
+
Usage Example with decorator:
|
76
|
+
|
77
|
+
@cache_result(expiry=60)
|
78
|
+
def get_cached_value(key: UUID) -> Any:
|
79
|
+
return get_value_from_database(key)
|
80
|
+
|
81
|
+
uuid_key = UUID("12345678123456781234567812345678")
|
82
|
+
|
83
|
+
value = get_cached_value(uuid_key)
|
84
|
+
"""
|
85
|
+
|
86
|
+
def __init__(self, max_capacity: int, default_expiry: int) -> None:
|
87
|
+
"""Initialize the cache with a maximum capacity and default expiry time.
|
88
|
+
|
89
|
+
Args:
|
90
|
+
max_capacity: The maximum number of entries the cache can hold.
|
91
|
+
default_expiry: The default expiry time in seconds.
|
92
|
+
"""
|
93
|
+
self.cache: OrderedDictType[UUID, MemoryCacheEntry] = OrderedDict()
|
94
|
+
self.max_capacity = max_capacity
|
95
|
+
self.default_expiry = default_expiry
|
96
|
+
self._lock = Lock()
|
97
|
+
|
98
|
+
def set(self, key: UUID, value: Any, expiry: Optional[int] = None) -> None:
|
99
|
+
"""Insert value into cache with optional custom expiry time in seconds.
|
100
|
+
|
101
|
+
Args:
|
102
|
+
key: The key to insert the value with.
|
103
|
+
value: The value to insert into the cache.
|
104
|
+
expiry: The expiry time in seconds. If None, uses the default expiry.
|
105
|
+
"""
|
106
|
+
with self._lock:
|
107
|
+
self.cache[key] = MemoryCacheEntry(
|
108
|
+
value=value, expiry=expiry or self.default_expiry
|
109
|
+
)
|
110
|
+
self._cleanup()
|
111
|
+
|
112
|
+
def get(self, key: UUID) -> Optional[Any]:
|
113
|
+
"""Retrieve value if it's still valid; otherwise, return None.
|
114
|
+
|
115
|
+
Args:
|
116
|
+
key: The key to retrieve the value for.
|
117
|
+
|
118
|
+
Returns:
|
119
|
+
The value if it's still valid; otherwise, None.
|
120
|
+
"""
|
121
|
+
with self._lock:
|
122
|
+
return self._get_internal(key)
|
123
|
+
|
124
|
+
def _get_internal(self, key: UUID) -> Optional[Any]:
|
125
|
+
"""Helper to retrieve a value without lock (internal use only).
|
126
|
+
|
127
|
+
Args:
|
128
|
+
key: The key to retrieve the value for.
|
129
|
+
|
130
|
+
Returns:
|
131
|
+
The value if it's still valid; otherwise, None.
|
132
|
+
"""
|
133
|
+
entry = self.cache.get(key)
|
134
|
+
if entry and not entry.expired:
|
135
|
+
return entry.value
|
136
|
+
elif entry:
|
137
|
+
del self.cache[key] # Invalidate expired entry
|
138
|
+
return None
|
139
|
+
|
140
|
+
def _cleanup(self) -> None:
|
141
|
+
"""Remove expired or excess entries."""
|
142
|
+
# Remove expired entries
|
143
|
+
keys_to_remove = [k for k, v in self.cache.items() if v.expired]
|
144
|
+
for k in keys_to_remove:
|
145
|
+
del self.cache[k]
|
146
|
+
|
147
|
+
# Ensure we don't exceed max capacity
|
148
|
+
while len(self.cache) > self.max_capacity:
|
149
|
+
self.cache.popitem(last=False)
|
150
|
+
|
151
|
+
|
152
|
+
F = Callable[[UUID], Any]
|
153
|
+
|
154
|
+
|
155
|
+
def cache_result(
|
156
|
+
expiry: Optional[int] = None,
|
157
|
+
) -> Callable[[F], F]:
|
158
|
+
"""A decorator to cache the result of a function based on a UUID key argument.
|
159
|
+
|
160
|
+
Args:
|
161
|
+
expiry: Custom time in seconds for the cache entry to expire. If None,
|
162
|
+
uses the default expiry time.
|
163
|
+
|
164
|
+
Returns:
|
165
|
+
A decorator that wraps a function, caching its results based on a UUID
|
166
|
+
key.
|
167
|
+
"""
|
168
|
+
|
169
|
+
def decorator(func: F) -> F:
|
170
|
+
"""The actual decorator that wraps the function with caching logic.
|
171
|
+
|
172
|
+
Args:
|
173
|
+
func: The function to wrap.
|
174
|
+
|
175
|
+
Returns:
|
176
|
+
The wrapped function with caching logic.
|
177
|
+
"""
|
178
|
+
|
179
|
+
def wrapper(key: UUID) -> Any:
|
180
|
+
"""The wrapped function with caching logic.
|
181
|
+
|
182
|
+
Args:
|
183
|
+
key: The key to use for caching.
|
184
|
+
|
185
|
+
Returns:
|
186
|
+
The result of the original function, either from cache or
|
187
|
+
freshly computed.
|
188
|
+
"""
|
189
|
+
from zenml.zen_server.utils import memcache
|
190
|
+
|
191
|
+
cache = memcache()
|
192
|
+
|
193
|
+
# Attempt to retrieve the result from cache
|
194
|
+
cached_value = cache.get(key)
|
195
|
+
if cached_value is not None:
|
196
|
+
logger.debug(
|
197
|
+
f"Memory cache hit for key: {key} and func: {func.__name__}"
|
198
|
+
)
|
199
|
+
return cached_value
|
200
|
+
|
201
|
+
# Call the original function and cache its result
|
202
|
+
result = func(key)
|
203
|
+
cache.set(key, result, expiry)
|
204
|
+
return result
|
205
|
+
|
206
|
+
return wrapper
|
207
|
+
|
208
|
+
return decorator
|
@@ -1 +1 @@
|
|
1
|
-
import{j as e}from"./@radix-
|
1
|
+
import{j as e}from"./@radix-DeK6qiuw.js";import{f as s,r as t}from"./index-CCOPpudF.js";import{E as r}from"./EmptyState-BzdlCwp3.js";import{S as a}from"./help-Cc9bBIJH.js";import{L as o}from"./@react-router-B3Z5rLr2.js";import"./@tanstack-DT5WLu9C.js";import"./@reactflow-CK0KJUen.js";function d(){return e.jsx("div",{className:"flex min-h-screen w-full flex-col",children:e.jsx(r,{icon:e.jsx(a,{className:"h-[120px] w-[120px] fill-neutral-300"}),children:e.jsxs("div",{className:"text-center",children:[e.jsx("h1",{className:"mb-2 text-display-xs font-semibold",children:"We can't find the page you are looking for"}),e.jsx("p",{className:"text-lg text-theme-text-secondary",children:"You can try typing a different URL or we can bring you back to your Homepage."}),e.jsx("div",{className:"mt-5 flex justify-center",children:e.jsx(s,{size:"md",asChild:!0,children:e.jsx(o,{className:"w-min self-center whitespace-nowrap",to:t.home,children:e.jsx("span",{className:"px-0.5",children:"Go to Home"})})})})]})})})}export{d as default};
|