zenml-nightly 0.58.2.dev20240626__py3-none-any.whl → 0.62.0.dev20240726__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 +31 -10
- RELEASE_NOTES.md +280 -0
- zenml/VERSION +1 -1
- zenml/__init__.py +2 -0
- zenml/analytics/enums.py +3 -0
- zenml/cli/__init__.py +28 -0
- zenml/cli/artifact.py +1 -2
- zenml/cli/integration.py +9 -8
- zenml/cli/server.py +6 -0
- zenml/cli/stack.py +812 -39
- zenml/cli/stack_components.py +9 -0
- zenml/cli/text_utils.py +35 -1
- zenml/cli/utils.py +127 -10
- zenml/client.py +23 -14
- zenml/config/docker_settings.py +8 -5
- zenml/constants.py +13 -1
- zenml/container_registries/base_container_registry.py +1 -0
- zenml/enums.py +23 -0
- zenml/event_hub/event_hub.py +5 -8
- zenml/integrations/__init__.py +1 -0
- zenml/integrations/aws/__init__.py +1 -0
- zenml/integrations/azure/__init__.py +3 -2
- zenml/integrations/constants.py +1 -0
- zenml/integrations/databricks/__init__.py +52 -0
- zenml/integrations/databricks/flavors/__init__.py +30 -0
- zenml/integrations/databricks/flavors/databricks_model_deployer_flavor.py +118 -0
- zenml/integrations/databricks/flavors/databricks_orchestrator_flavor.py +147 -0
- zenml/integrations/databricks/model_deployers/__init__.py +20 -0
- zenml/integrations/databricks/model_deployers/databricks_model_deployer.py +249 -0
- zenml/integrations/databricks/orchestrators/__init__.py +20 -0
- zenml/integrations/databricks/orchestrators/databricks_orchestrator.py +497 -0
- zenml/integrations/databricks/orchestrators/databricks_orchestrator_entrypoint_config.py +97 -0
- zenml/integrations/databricks/services/__init__.py +19 -0
- zenml/integrations/databricks/services/databricks_deployment.py +407 -0
- zenml/integrations/databricks/utils/__init__.py +14 -0
- zenml/integrations/databricks/utils/databricks_utils.py +87 -0
- zenml/integrations/deepchecks/__init__.py +1 -0
- zenml/integrations/discord/__init__.py +1 -0
- zenml/integrations/evidently/__init__.py +1 -0
- zenml/integrations/facets/__init__.py +1 -0
- zenml/integrations/feast/__init__.py +1 -0
- zenml/integrations/gcp/__init__.py +3 -1
- zenml/integrations/gcp/google_credentials_mixin.py +1 -1
- zenml/integrations/gcp/service_connectors/gcp_service_connector.py +320 -64
- zenml/integrations/great_expectations/data_validators/ge_data_validator.py +12 -8
- zenml/integrations/huggingface/__init__.py +1 -0
- zenml/integrations/huggingface/materializers/huggingface_datasets_materializer.py +88 -3
- zenml/integrations/huggingface/steps/accelerate_runner.py +1 -7
- zenml/integrations/integration.py +24 -0
- zenml/integrations/kubeflow/__init__.py +3 -0
- zenml/integrations/kubeflow/flavors/kubeflow_orchestrator_flavor.py +1 -1
- zenml/integrations/kubeflow/orchestrators/kubeflow_orchestrator.py +0 -1
- zenml/integrations/kubernetes/__init__.py +3 -1
- zenml/integrations/kubernetes/orchestrators/kube_utils.py +4 -1
- zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py +1 -13
- zenml/integrations/kubernetes/orchestrators/manifest_utils.py +22 -4
- zenml/integrations/kubernetes/pod_settings.py +4 -0
- zenml/integrations/label_studio/annotators/label_studio_annotator.py +1 -0
- zenml/integrations/langchain/__init__.py +1 -0
- zenml/integrations/lightgbm/__init__.py +1 -0
- zenml/integrations/mlflow/__init__.py +4 -2
- zenml/integrations/mlflow/model_registries/mlflow_model_registry.py +6 -2
- zenml/integrations/mlflow/services/mlflow_deployment.py +1 -1
- zenml/integrations/neural_prophet/__init__.py +1 -0
- zenml/integrations/polars/__init__.py +1 -0
- zenml/integrations/prodigy/__init__.py +1 -0
- zenml/integrations/pycaret/__init__.py +6 -0
- zenml/integrations/registry.py +37 -0
- zenml/integrations/s3/artifact_stores/s3_artifact_store.py +17 -6
- zenml/integrations/seldon/__init__.py +1 -0
- zenml/integrations/seldon/model_deployers/seldon_model_deployer.py +1 -0
- zenml/integrations/skypilot/flavors/skypilot_orchestrator_base_vm_config.py +2 -2
- zenml/integrations/skypilot/orchestrators/skypilot_base_vm_orchestrator.py +1 -1
- zenml/integrations/skypilot/orchestrators/skypilot_orchestrator_entrypoint.py +2 -2
- zenml/integrations/skypilot_aws/__init__.py +2 -1
- zenml/integrations/skypilot_azure/__init__.py +1 -3
- zenml/integrations/skypilot_gcp/__init__.py +1 -1
- zenml/integrations/skypilot_lambda/__init__.py +1 -1
- zenml/integrations/skypilot_lambda/flavors/skypilot_orchestrator_lambda_vm_flavor.py +1 -1
- zenml/integrations/slack/__init__.py +1 -0
- zenml/integrations/tekton/__init__.py +1 -0
- zenml/integrations/tensorboard/__init__.py +0 -1
- zenml/integrations/tensorflow/__init__.py +18 -6
- zenml/integrations/wandb/__init__.py +1 -0
- zenml/logging/step_logging.py +34 -35
- zenml/materializers/built_in_materializer.py +1 -1
- zenml/materializers/cloudpickle_materializer.py +1 -1
- zenml/model/model.py +1 -1
- zenml/models/__init__.py +11 -0
- zenml/models/v2/core/component.py +47 -0
- zenml/models/v2/core/model.py +1 -2
- zenml/models/v2/core/server_settings.py +0 -20
- zenml/models/v2/core/service_connector.py +17 -0
- zenml/models/v2/core/stack.py +31 -0
- zenml/models/v2/misc/full_stack.py +129 -0
- zenml/models/v2/misc/stack_deployment.py +91 -0
- zenml/new/pipelines/pipeline.py +1 -1
- zenml/new/pipelines/run_utils.py +1 -1
- zenml/orchestrators/__init__.py +4 -0
- zenml/orchestrators/input_utils.py +3 -6
- zenml/orchestrators/step_launcher.py +1 -0
- zenml/orchestrators/wheeled_orchestrator.py +147 -0
- zenml/service_connectors/service_connector_utils.py +408 -0
- zenml/stack/stack.py +3 -6
- zenml/stack_deployments/__init__.py +14 -0
- zenml/stack_deployments/aws_stack_deployment.py +254 -0
- zenml/stack_deployments/azure_stack_deployment.py +179 -0
- zenml/stack_deployments/gcp_stack_deployment.py +269 -0
- zenml/stack_deployments/stack_deployment.py +218 -0
- zenml/stack_deployments/utils.py +48 -0
- zenml/steps/base_step.py +7 -5
- zenml/utils/function_utils.py +2 -2
- zenml/utils/pagination_utils.py +7 -5
- zenml/utils/pipeline_docker_image_builder.py +105 -68
- zenml/utils/pydantic_utils.py +6 -5
- zenml/utils/source_utils.py +4 -1
- zenml/zen_server/cloud_utils.py +18 -3
- zenml/zen_server/dashboard/assets/{404-CDPQCl4D.js → 404-B_YdvmwS.js} +1 -1
- zenml/zen_server/dashboard/assets/@radix-CFOkMR_E.js +85 -0
- zenml/zen_server/dashboard/assets/{@react-router-DYovave8.js → @react-router-CO-OsFwI.js} +2 -2
- zenml/zen_server/dashboard/assets/{@reactflow-CHBapDaj.js → @reactflow-l_1hUr1S.js} +2 -2
- zenml/zen_server/dashboard/assets/@tanstack-DYiOyJUL.js +22 -0
- zenml/zen_server/dashboard/assets/AwarenessChannel-CFg5iX4Z.js +1 -0
- zenml/zen_server/dashboard/assets/{CodeSnippet-BidtnWOi.js → CodeSnippet-Dvkx_82E.js} +2 -2
- zenml/zen_server/dashboard/assets/CollapsibleCard-opiuBHHc.js +1 -0
- zenml/zen_server/dashboard/assets/Commands-DoN1xrEq.js +1 -0
- zenml/zen_server/dashboard/assets/CopyButton-Cr7xYEPb.js +2 -0
- zenml/zen_server/dashboard/assets/{CsvVizualization-BOuez-fG.js → CsvVizualization-Ck-nZ43m.js} +7 -7
- zenml/zen_server/dashboard/assets/DisplayDate-DYgIjlDF.js +1 -0
- zenml/zen_server/dashboard/assets/EmptyState-BMLnFVlB.js +1 -0
- zenml/zen_server/dashboard/assets/Error-kLtljEOM.js +1 -0
- zenml/zen_server/dashboard/assets/ExecutionStatus-DguLLgTK.js +1 -0
- zenml/zen_server/dashboard/assets/Helpbox-BXUMP21n.js +1 -0
- zenml/zen_server/dashboard/assets/Infobox-DSt0O-dm.js +1 -0
- zenml/zen_server/dashboard/assets/InlineAvatar-xsrsIGE-.js +1 -0
- zenml/zen_server/dashboard/assets/{MarkdownVisualization-DsB2QZiK.js → MarkdownVisualization-xp3hhULl.js} +2 -2
- zenml/zen_server/dashboard/assets/Pagination-C6X-mifw.js +1 -0
- zenml/zen_server/dashboard/assets/PasswordChecker-DUveqlva.js +1 -0
- zenml/zen_server/dashboard/assets/SetPassword-BXGTWiwj.js +1 -0
- zenml/zen_server/dashboard/assets/SuccessStep-DZC60t0x.js +1 -0
- zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-DnM-c11H.js → UpdatePasswordSchemas-DGvwFWO1.js} +1 -1
- zenml/zen_server/dashboard/assets/{aws-t0gKCj_R.js → aws-BgKTfTfx.js} +1 -1
- zenml/zen_server/dashboard/assets/{check-circle-BVvhm5dy.js → check-circle-i56092KI.js} +1 -1
- zenml/zen_server/dashboard/assets/{chevron-right-double-CJ50E9Gr.js → chevron-right-double-CZBOf6JM.js} +1 -1
- zenml/zen_server/dashboard/assets/cloud-only-C_yFCAkP.js +1 -0
- zenml/zen_server/dashboard/assets/{copy-BRhQz3j-.js → copy-BXNk6BjL.js} +1 -1
- zenml/zen_server/dashboard/assets/{database-CRRnyFWh.js → database-1xWSgZfO.js} +1 -1
- zenml/zen_server/dashboard/assets/{docker-BAonhm6G.js → docker-CQMVm_4d.js} +1 -1
- zenml/zen_server/dashboard/assets/{file-text-CbVERUON.js → file-text-CqD_iu6l.js} +1 -1
- zenml/zen_server/dashboard/assets/{help-B8rqCvqn.js → help-bu_DgLKI.js} +1 -1
- zenml/zen_server/dashboard/assets/index-BczVOqUf.js +55 -0
- zenml/zen_server/dashboard/assets/index-EpMIKgrI.css +1 -0
- zenml/zen_server/dashboard/assets/index-rK_Wuy2W.js +1 -0
- zenml/zen_server/dashboard/assets/index.esm-Corw4lXQ.js +1 -0
- zenml/zen_server/dashboard/assets/{login-mutation-wzzl23C6.js → login-mutation-CrHrndTI.js} +1 -1
- zenml/zen_server/dashboard/assets/logs-D8k8BVFf.js +1 -0
- zenml/zen_server/dashboard/assets/not-found-DYa4pC-C.js +1 -0
- zenml/zen_server/dashboard/assets/package-B3fWP-Dh.js +1 -0
- zenml/zen_server/dashboard/assets/page-1h_sD1jz.js +1 -0
- zenml/zen_server/dashboard/assets/{page-yN4rZ-ZS.js → page-1iL8aMqs.js} +1 -1
- zenml/zen_server/dashboard/assets/{page-Bi5AI0S7.js → page-2grKx_MY.js} +1 -1
- zenml/zen_server/dashboard/assets/page-5NCOHOsy.js +1 -0
- zenml/zen_server/dashboard/assets/page-8a4UMKXZ.js +1 -0
- zenml/zen_server/dashboard/assets/{page-AQKopn_4.js → page-B6h3iaHJ.js} +1 -1
- zenml/zen_server/dashboard/assets/page-BDns21Iz.js +1 -0
- zenml/zen_server/dashboard/assets/{page-BmkSiYeQ.js → page-BhgCDInH.js} +2 -2
- zenml/zen_server/dashboard/assets/{page-BzVZGExK.js → page-Bi-wtWiO.js} +2 -2
- zenml/zen_server/dashboard/assets/page-BkeAAYwp.js +1 -0
- zenml/zen_server/dashboard/assets/page-BkuQDIf-.js +1 -0
- zenml/zen_server/dashboard/assets/page-BnaevhnB.js +1 -0
- zenml/zen_server/dashboard/assets/page-Bq0YxkLV.js +1 -0
- zenml/zen_server/dashboard/assets/page-Bs2F4eoD.js +2 -0
- zenml/zen_server/dashboard/assets/page-C6-UGEbH.js +1 -0
- zenml/zen_server/dashboard/assets/page-CCNRIt_f.js +1 -0
- zenml/zen_server/dashboard/assets/page-CHNxpz3n.js +1 -0
- zenml/zen_server/dashboard/assets/page-DgorQFqi.js +1 -0
- zenml/zen_server/dashboard/assets/page-K8ebxVIs.js +1 -0
- zenml/zen_server/dashboard/assets/{page-CuT1SUik.js → page-MFQyIJd3.js} +1 -1
- zenml/zen_server/dashboard/assets/page-TgCF0P_U.js +1 -0
- zenml/zen_server/dashboard/assets/page-ZnCEe-eK.js +9 -0
- zenml/zen_server/dashboard/assets/{page-BW6Ket3a.js → page-uA5prJGY.js} +1 -1
- zenml/zen_server/dashboard/assets/persist-D7HJNBWx.js +1 -0
- zenml/zen_server/dashboard/assets/{play-circle-DK5QMJyp.js → play-circle-CNtZKDnW.js} +1 -1
- zenml/zen_server/dashboard/assets/plus-C8WOyCzt.js +1 -0
- zenml/zen_server/dashboard/assets/stack-detail-query-Cficsl6d.js +1 -0
- zenml/zen_server/dashboard/assets/{terminal-B2ovgWuz.js → terminal-By9cErXc.js} +1 -1
- zenml/zen_server/dashboard/assets/update-server-settings-mutation-7d8xi1tS.js +1 -0
- zenml/zen_server/dashboard/assets/{url-6_xv0WJS.js → url-D7mAQGUM.js} +1 -1
- zenml/zen_server/dashboard/assets/{zod-DrZvVLjd.js → zod-BhoGpZ63.js} +1 -1
- zenml/zen_server/dashboard/index.html +7 -7
- zenml/zen_server/dashboard_legacy/asset-manifest.json +4 -4
- zenml/zen_server/dashboard_legacy/index.html +1 -1
- zenml/zen_server/dashboard_legacy/{precache-manifest.f4abc5b7cfa7d90c1caf5521918e29a8.js → precache-manifest.12246c7548e71e2c4438e496360de80c.js} +4 -4
- zenml/zen_server/dashboard_legacy/service-worker.js +1 -1
- zenml/zen_server/dashboard_legacy/static/js/main.3b27024b.chunk.js +2 -0
- zenml/zen_server/dashboard_legacy/static/js/{main.ac2f17d0.chunk.js.map → main.3b27024b.chunk.js.map} +1 -1
- zenml/zen_server/deploy/helm/Chart.yaml +1 -1
- zenml/zen_server/deploy/helm/README.md +2 -2
- zenml/zen_server/feature_gate/zenml_cloud_feature_gate.py +11 -5
- zenml/zen_server/pipeline_deployment/utils.py +57 -44
- zenml/zen_server/rbac/utils.py +10 -2
- zenml/zen_server/rbac/zenml_cloud_rbac.py +11 -5
- zenml/zen_server/routers/devices_endpoints.py +4 -1
- zenml/zen_server/routers/server_endpoints.py +29 -2
- zenml/zen_server/routers/service_connectors_endpoints.py +57 -0
- zenml/zen_server/routers/stack_deployment_endpoints.py +158 -0
- zenml/zen_server/routers/steps_endpoints.py +2 -1
- zenml/zen_server/routers/workspaces_endpoints.py +64 -0
- zenml/zen_server/zen_server_api.py +2 -0
- zenml/zen_stores/migrations/utils.py +1 -1
- zenml/zen_stores/migrations/versions/0.60.0_release.py +23 -0
- zenml/zen_stores/migrations/versions/0.61.0_release.py +23 -0
- zenml/zen_stores/migrations/versions/0.62.0_release.py +23 -0
- zenml/zen_stores/migrations/versions/0d707865f404_adding_labels_to_stacks.py +30 -0
- zenml/zen_stores/migrations/versions/b4fca5241eea_migrate_onboarding_state.py +167 -0
- zenml/zen_stores/rest_zen_store.py +149 -4
- zenml/zen_stores/schemas/component_schemas.py +14 -0
- zenml/zen_stores/schemas/server_settings_schemas.py +23 -11
- zenml/zen_stores/schemas/stack_schemas.py +10 -0
- zenml/zen_stores/schemas/step_run_schemas.py +27 -11
- zenml/zen_stores/sql_zen_store.py +450 -6
- zenml/zen_stores/zen_store_interface.py +80 -0
- {zenml_nightly-0.58.2.dev20240626.dist-info → zenml_nightly-0.62.0.dev20240726.dist-info}/METADATA +35 -13
- {zenml_nightly-0.58.2.dev20240626.dist-info → zenml_nightly-0.62.0.dev20240726.dist-info}/RECORD +227 -191
- zenml/zen_server/dashboard/assets/@radix-C9DBgJhe.js +0 -77
- zenml/zen_server/dashboard/assets/@tanstack-CEbkxrhX.js +0 -30
- zenml/zen_server/dashboard/assets/AwarenessChannel-nXGpmj_f.js +0 -1
- zenml/zen_server/dashboard/assets/Cards-nwsvQLVS.js +0 -1
- zenml/zen_server/dashboard/assets/Commands-DuIWKg_Q.js +0 -1
- zenml/zen_server/dashboard/assets/CopyButton-B_YSm-Ds.js +0 -2
- zenml/zen_server/dashboard/assets/DisplayDate-BdguISQF.js +0 -1
- zenml/zen_server/dashboard/assets/EmptyState-BkooiGtL.js +0 -1
- zenml/zen_server/dashboard/assets/Error-B6M0dPph.js +0 -1
- zenml/zen_server/dashboard/assets/Helpbox-BQoqCm04.js +0 -1
- zenml/zen_server/dashboard/assets/Infobox-Ce9mefqU.js +0 -1
- zenml/zen_server/dashboard/assets/InlineAvatar-DGf3dVhV.js +0 -1
- zenml/zen_server/dashboard/assets/PageHeader-DGaemzjc.js +0 -1
- zenml/zen_server/dashboard/assets/Pagination-DVYfBCCc.js +0 -1
- zenml/zen_server/dashboard/assets/PasswordChecker-DSLBp7Vl.js +0 -1
- zenml/zen_server/dashboard/assets/SetPassword-B5s7DJug.js +0 -1
- zenml/zen_server/dashboard/assets/SuccessStep-ZzczaM7g.js +0 -1
- zenml/zen_server/dashboard/assets/chevron-down-zcvCWmyP.js +0 -1
- zenml/zen_server/dashboard/assets/cloud-only-Ba_ShBR5.js +0 -1
- zenml/zen_server/dashboard/assets/index-CWJ3xbIf.css +0 -1
- zenml/zen_server/dashboard/assets/index-QORVVTMN.js +0 -55
- zenml/zen_server/dashboard/assets/index.esm-F7nqy9zY.js +0 -1
- zenml/zen_server/dashboard/assets/not-found-Dh2la7kh.js +0 -1
- zenml/zen_server/dashboard/assets/page-B-5jAKoO.js +0 -1
- zenml/zen_server/dashboard/assets/page-B-vWk8a6.js +0 -1
- zenml/zen_server/dashboard/assets/page-B0BrqfS8.js +0 -1
- zenml/zen_server/dashboard/assets/page-BQxVFlUl.js +0 -1
- zenml/zen_server/dashboard/assets/page-ByrHy6Ss.js +0 -1
- zenml/zen_server/dashboard/assets/page-CPtY4Kv_.js +0 -1
- zenml/zen_server/dashboard/assets/page-CmmukLsl.js +0 -1
- zenml/zen_server/dashboard/assets/page-D2D-7qyr.js +0 -9
- zenml/zen_server/dashboard/assets/page-DAQQyLxT.js +0 -1
- zenml/zen_server/dashboard/assets/page-DHkUMl_E.js +0 -1
- zenml/zen_server/dashboard/assets/page-DZCbwOEs.js +0 -2
- zenml/zen_server/dashboard/assets/page-DdaIt20-.js +0 -1
- zenml/zen_server/dashboard/assets/page-LqLs24Ot.js +0 -1
- zenml/zen_server/dashboard/assets/page-lebv0c7C.js +0 -1
- zenml/zen_server/dashboard/assets/update-server-settings-mutation-0Wgz8pUE.js +0 -1
- zenml/zen_server/dashboard_legacy/static/js/main.ac2f17d0.chunk.js +0 -2
- {zenml_nightly-0.58.2.dev20240626.dist-info → zenml_nightly-0.62.0.dev20240726.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.58.2.dev20240626.dist-info → zenml_nightly-0.62.0.dev20240726.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.58.2.dev20240626.dist-info → zenml_nightly-0.62.0.dev20240726.dist-info}/entry_points.txt +0 -0
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (c) ZenML GmbH
|
1
|
+
# Copyright (c) ZenML GmbH 2024. All Rights Reserved.
|
2
2
|
#
|
3
3
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
4
|
# you may not use this file except in compliance with the License.
|
@@ -16,12 +16,21 @@
|
|
16
16
|
import os
|
17
17
|
from collections import defaultdict
|
18
18
|
from tempfile import TemporaryDirectory, mkdtemp
|
19
|
-
from typing import
|
19
|
+
from typing import (
|
20
|
+
TYPE_CHECKING,
|
21
|
+
Any,
|
22
|
+
ClassVar,
|
23
|
+
Dict,
|
24
|
+
Optional,
|
25
|
+
Tuple,
|
26
|
+
Type,
|
27
|
+
Union,
|
28
|
+
)
|
20
29
|
|
21
30
|
from datasets import Dataset, load_from_disk
|
22
31
|
from datasets.dataset_dict import DatasetDict
|
23
32
|
|
24
|
-
from zenml.enums import ArtifactType
|
33
|
+
from zenml.enums import ArtifactType, VisualizationType
|
25
34
|
from zenml.io import fileio
|
26
35
|
from zenml.materializers.base_materializer import BaseMaterializer
|
27
36
|
from zenml.materializers.pandas_materializer import PandasMaterializer
|
@@ -33,6 +42,31 @@ if TYPE_CHECKING:
|
|
33
42
|
DEFAULT_DATASET_DIR = "hf_datasets"
|
34
43
|
|
35
44
|
|
45
|
+
def extract_repo_name(checksum_str: str) -> Optional[str]:
|
46
|
+
"""Extracts the repo name from the checksum string.
|
47
|
+
|
48
|
+
An example of a checksum_str is:
|
49
|
+
"hf://datasets/nyu-mll/glue@bcdcba79d07bc864c1c254ccfcedcce55bcc9a8c/mrpc/train-00000-of-00001.parquet"
|
50
|
+
and the expected output is "nyu-mll/glue".
|
51
|
+
|
52
|
+
Args:
|
53
|
+
checksum_str: The checksum_str to extract the repo name from.
|
54
|
+
|
55
|
+
Returns:
|
56
|
+
str: The extracted repo name.
|
57
|
+
"""
|
58
|
+
dataset = None
|
59
|
+
try:
|
60
|
+
parts = checksum_str.split("/")
|
61
|
+
if len(parts) >= 4:
|
62
|
+
# Case: nyu-mll/glue
|
63
|
+
dataset = f"{parts[3]}/{parts[4].split('@')[0]}"
|
64
|
+
except Exception: # pylint: disable=broad-except
|
65
|
+
pass
|
66
|
+
|
67
|
+
return dataset
|
68
|
+
|
69
|
+
|
36
70
|
class HFDatasetMaterializer(BaseMaterializer):
|
37
71
|
"""Materializer to read data to and from huggingface datasets."""
|
38
72
|
|
@@ -103,3 +137,54 @@ class HFDatasetMaterializer(BaseMaterializer):
|
|
103
137
|
metadata[key][dataset_name] = value
|
104
138
|
return dict(metadata)
|
105
139
|
raise ValueError(f"Unsupported type {type(ds)}")
|
140
|
+
|
141
|
+
def save_visualizations(
|
142
|
+
self, ds: Union[Dataset, DatasetDict]
|
143
|
+
) -> Dict[str, VisualizationType]:
|
144
|
+
"""Save visualizations for the dataset.
|
145
|
+
|
146
|
+
Args:
|
147
|
+
ds: The Dataset or DatasetDict to visualize.
|
148
|
+
|
149
|
+
Returns:
|
150
|
+
A dictionary mapping visualization paths to their types.
|
151
|
+
|
152
|
+
Raises:
|
153
|
+
ValueError: If the given object is not a `Dataset` or `DatasetDict`.
|
154
|
+
"""
|
155
|
+
visualizations = {}
|
156
|
+
|
157
|
+
if isinstance(ds, Dataset):
|
158
|
+
datasets = {"default": ds}
|
159
|
+
elif isinstance(ds, DatasetDict):
|
160
|
+
datasets = ds
|
161
|
+
else:
|
162
|
+
raise ValueError(f"Unsupported type {type(ds)}")
|
163
|
+
|
164
|
+
for name, dataset in datasets.items():
|
165
|
+
# Generate a unique identifier for the dataset
|
166
|
+
if dataset.info.download_checksums:
|
167
|
+
dataset_id = extract_repo_name(
|
168
|
+
[x for x in dataset.info.download_checksums.keys()][0]
|
169
|
+
)
|
170
|
+
if dataset_id:
|
171
|
+
# Create the iframe HTML
|
172
|
+
html = f"""
|
173
|
+
<iframe
|
174
|
+
src="https://huggingface.co/datasets/{dataset_id}/embed/viewer"
|
175
|
+
frameborder="0"
|
176
|
+
width="100%"
|
177
|
+
height="560px"
|
178
|
+
></iframe>
|
179
|
+
"""
|
180
|
+
|
181
|
+
# Save the HTML to a file
|
182
|
+
visualization_path = os.path.join(
|
183
|
+
self.uri, f"{name}_viewer.html"
|
184
|
+
)
|
185
|
+
with fileio.open(visualization_path, "w") as f:
|
186
|
+
f.write(html)
|
187
|
+
|
188
|
+
visualizations[visualization_path] = VisualizationType.HTML
|
189
|
+
|
190
|
+
return visualizations
|
@@ -111,15 +111,9 @@ def run_with_accelerate(
|
|
111
111
|
if isinstance(v, bool):
|
112
112
|
if v:
|
113
113
|
commands.append(f"--{k}")
|
114
|
-
elif isinstance(v, str):
|
115
|
-
commands += [f"--{k}", '"{v}"']
|
116
114
|
elif type(v) in (list, tuple, set):
|
117
115
|
for each in v:
|
118
|
-
commands
|
119
|
-
if isinstance(each, str):
|
120
|
-
commands.append(f'"{each}"')
|
121
|
-
else:
|
122
|
-
commands.append(f"{each}")
|
116
|
+
commands += [f"--{k}", f"{each}"]
|
123
117
|
else:
|
124
118
|
commands += [f"--{k}", f"{v}"]
|
125
119
|
|
@@ -60,6 +60,7 @@ class Integration(metaclass=IntegrationMeta):
|
|
60
60
|
|
61
61
|
REQUIREMENTS: List[str] = []
|
62
62
|
APT_PACKAGES: List[str] = []
|
63
|
+
REQUIREMENTS_IGNORED_ON_UNINSTALL: List[str] = []
|
63
64
|
|
64
65
|
@classmethod
|
65
66
|
def check_installation(cls) -> bool:
|
@@ -143,6 +144,29 @@ class Integration(metaclass=IntegrationMeta):
|
|
143
144
|
"""
|
144
145
|
return cls.REQUIREMENTS
|
145
146
|
|
147
|
+
@classmethod
|
148
|
+
def get_uninstall_requirements(
|
149
|
+
cls, target_os: Optional[str] = None
|
150
|
+
) -> List[str]:
|
151
|
+
"""Method to get the uninstall requirements for the integration.
|
152
|
+
|
153
|
+
Args:
|
154
|
+
target_os: The target operating system to get the requirements for.
|
155
|
+
|
156
|
+
Returns:
|
157
|
+
A list of requirements.
|
158
|
+
"""
|
159
|
+
ret = []
|
160
|
+
for each in cls.get_requirements(target_os=target_os):
|
161
|
+
is_ignored = False
|
162
|
+
for ignored in cls.REQUIREMENTS_IGNORED_ON_UNINSTALL:
|
163
|
+
if each.startswith(ignored):
|
164
|
+
is_ignored = True
|
165
|
+
break
|
166
|
+
if not is_ignored:
|
167
|
+
ret.append(each)
|
168
|
+
return ret
|
169
|
+
|
146
170
|
@classmethod
|
147
171
|
def activate(cls) -> None:
|
148
172
|
"""Abstract method to activate the integration."""
|
@@ -31,6 +31,9 @@ class KubeflowIntegration(Integration):
|
|
31
31
|
|
32
32
|
NAME = KUBEFLOW
|
33
33
|
REQUIREMENTS = ["kfp>=2.6.0", "kfp-kubernetes>=1.1.0"] # Only 1.x version that supports pyyaml 6
|
34
|
+
REQUIREMENTS_IGNORED_ON_UNINSTALL = [
|
35
|
+
"kfp", # it is used by GCP as well
|
36
|
+
]
|
34
37
|
|
35
38
|
@classmethod
|
36
39
|
def flavors(cls) -> List[Type[Flavor]]:
|
@@ -149,7 +149,7 @@ class KubeflowOrchestratorConfig(
|
|
149
149
|
|
150
150
|
kubeflow_hostname: Optional[str] = None
|
151
151
|
kubeflow_namespace: str = "kubeflow"
|
152
|
-
kubernetes_context: Optional[str] # TODO: Potential setting
|
152
|
+
kubernetes_context: Optional[str] = None # TODO: Potential setting
|
153
153
|
|
154
154
|
@model_validator(mode="before")
|
155
155
|
@classmethod
|
@@ -31,7 +31,9 @@ class KubernetesIntegration(Integration):
|
|
31
31
|
|
32
32
|
NAME = KUBERNETES
|
33
33
|
REQUIREMENTS = ["kubernetes>=21.7,<26"]
|
34
|
-
|
34
|
+
REQUIREMENTS_IGNORED_ON_UNINSTALL = [
|
35
|
+
"kfp", # it is used by many others
|
36
|
+
]
|
35
37
|
@classmethod
|
36
38
|
def flavors(cls) -> List[Type[Flavor]]:
|
37
39
|
"""Declare the stack component flavors for the Kubernetes integration.
|
@@ -225,8 +225,11 @@ def wait_pod(
|
|
225
225
|
response = core_api.read_namespaced_pod_log(
|
226
226
|
name=pod_name,
|
227
227
|
namespace=namespace,
|
228
|
+
_preload_content=False,
|
228
229
|
)
|
229
|
-
|
230
|
+
raw_data = response.data
|
231
|
+
decoded_log = raw_data.decode("utf-8", errors="replace")
|
232
|
+
logs = decoded_log.splitlines()
|
230
233
|
if len(logs) > logged_lines:
|
231
234
|
for line in logs[logged_lines:]:
|
232
235
|
logger.info(line)
|
@@ -38,7 +38,6 @@ from kubernetes import config as k8s_config
|
|
38
38
|
|
39
39
|
from zenml.config.base_settings import BaseSettings
|
40
40
|
from zenml.enums import StackComponentType
|
41
|
-
from zenml.environment import Environment
|
42
41
|
from zenml.integrations.kubernetes.flavors.kubernetes_orchestrator_flavor import (
|
43
42
|
KubernetesOrchestratorConfig,
|
44
43
|
KubernetesOrchestratorSettings,
|
@@ -335,19 +334,8 @@ class KubernetesOrchestrator(ContainerizedOrchestrator):
|
|
335
334
|
environment.
|
336
335
|
|
337
336
|
Raises:
|
338
|
-
RuntimeError: If
|
337
|
+
RuntimeError: If the Kubernetes orchestrator is not configured.
|
339
338
|
"""
|
340
|
-
# First check whether the code is running in a notebook.
|
341
|
-
if Environment.in_notebook():
|
342
|
-
raise RuntimeError(
|
343
|
-
"The Kubernetes orchestrator cannot run pipelines in a notebook "
|
344
|
-
"environment. The reason is that it is non-trivial to create "
|
345
|
-
"a Docker image of a notebook. Please consider refactoring "
|
346
|
-
"your notebook cells into separate scripts in a Python module "
|
347
|
-
"and run the code outside of a notebook when using this "
|
348
|
-
"orchestrator."
|
349
|
-
)
|
350
|
-
|
351
339
|
for step_name, step in deployment.step_configurations.items():
|
352
340
|
if self.requires_resources_in_orchestration_environment(step):
|
353
341
|
logger.warning(
|
@@ -139,24 +139,42 @@ def build_pod_manifest(
|
|
139
139
|
],
|
140
140
|
security_context=security_context,
|
141
141
|
)
|
142
|
+
image_pull_secrets = []
|
143
|
+
if pod_settings:
|
144
|
+
image_pull_secrets = [
|
145
|
+
k8s_client.V1LocalObjectReference(name=name)
|
146
|
+
for name in pod_settings.image_pull_secrets
|
147
|
+
]
|
142
148
|
|
143
149
|
pod_spec = k8s_client.V1PodSpec(
|
144
150
|
containers=[container_spec],
|
145
151
|
restart_policy="Never",
|
152
|
+
image_pull_secrets=image_pull_secrets,
|
146
153
|
)
|
147
154
|
|
148
155
|
if service_account_name is not None:
|
149
156
|
pod_spec.service_account_name = service_account_name
|
150
157
|
|
158
|
+
labels = {}
|
159
|
+
|
151
160
|
if pod_settings:
|
152
161
|
add_pod_settings(pod_spec, pod_settings)
|
153
162
|
|
154
|
-
|
155
|
-
|
156
|
-
|
163
|
+
# Add pod_settings.labels to the labels
|
164
|
+
if pod_settings.labels:
|
165
|
+
labels.update(pod_settings.labels)
|
166
|
+
|
167
|
+
# Add run_name and pipeline_name to the labels
|
168
|
+
labels.update(
|
169
|
+
{
|
157
170
|
"run": run_name,
|
158
171
|
"pipeline": pipeline_name,
|
159
|
-
}
|
172
|
+
}
|
173
|
+
)
|
174
|
+
|
175
|
+
pod_metadata = k8s_client.V1ObjectMeta(
|
176
|
+
name=pod_name,
|
177
|
+
labels=labels,
|
160
178
|
)
|
161
179
|
|
162
180
|
if pod_settings and pod_settings.annotations:
|
@@ -33,6 +33,8 @@ class KubernetesPodSettings(BaseSettings):
|
|
33
33
|
volumes: Volumes to mount in the pod.
|
34
34
|
volume_mounts: Volume mounts to apply to the pod containers.
|
35
35
|
host_ipc: Whether to enable host IPC for the pod.
|
36
|
+
image_pull_secrets: Image pull secrets to use for the pod.
|
37
|
+
labels: Labels to apply to the pod.
|
36
38
|
"""
|
37
39
|
|
38
40
|
node_selectors: Dict[str, str] = {}
|
@@ -43,6 +45,8 @@ class KubernetesPodSettings(BaseSettings):
|
|
43
45
|
volumes: List[Dict[str, Any]] = []
|
44
46
|
volume_mounts: List[Dict[str, Any]] = []
|
45
47
|
host_ipc: bool = False
|
48
|
+
image_pull_secrets: List[str] = []
|
49
|
+
labels: Dict[str, str] = {}
|
46
50
|
|
47
51
|
@field_validator("volumes", mode="before")
|
48
52
|
@classmethod
|
@@ -33,7 +33,7 @@ class MlflowIntegration(Integration):
|
|
33
33
|
NAME = MLFLOW
|
34
34
|
|
35
35
|
REQUIREMENTS = [
|
36
|
-
"mlflow>=2.1.1,<=2.14.
|
36
|
+
"mlflow>=2.1.1,<=2.14.2",
|
37
37
|
"mlserver>=1.3.3",
|
38
38
|
"mlserver-mlflow>=1.3.3",
|
39
39
|
# TODO: remove this requirement once rapidjson is fixed
|
@@ -45,9 +45,11 @@ class MlflowIntegration(Integration):
|
|
45
45
|
# This downgrades pydantic to v1 even though mlflow does not have
|
46
46
|
# any issues with v2. This is why we have to pin it here so a downgrade
|
47
47
|
# will not happen.
|
48
|
-
"pydantic>=2.7.0,<2.8.0"
|
48
|
+
"pydantic>=2.7.0,<2.8.0",
|
49
49
|
]
|
50
50
|
|
51
|
+
REQUIREMENTS_IGNORED_ON_UNINSTALL = ["python-rapidjson", "pydantic"]
|
52
|
+
|
51
53
|
@classmethod
|
52
54
|
def activate(cls) -> None:
|
53
55
|
"""Activate the MLflow integration."""
|
@@ -371,7 +371,7 @@ class MLFlowModelRegistry(BaseModelRegistry):
|
|
371
371
|
self.get_model(name=name)
|
372
372
|
except KeyError:
|
373
373
|
logger.info(
|
374
|
-
f"No registered model with name {name} found. Creating a new"
|
374
|
+
f"No registered model with name {name} found. Creating a new "
|
375
375
|
"registered model."
|
376
376
|
)
|
377
377
|
self.register_model(
|
@@ -615,7 +615,11 @@ class MLFlowModelRegistry(BaseModelRegistry):
|
|
615
615
|
for mlflow_model_version in mlflow_model_versions:
|
616
616
|
# check if given MlFlow model version matches the given request
|
617
617
|
# before casting it
|
618
|
-
if
|
618
|
+
if (
|
619
|
+
stage
|
620
|
+
and not ModelVersionStage(mlflow_model_version.current_stage)
|
621
|
+
== stage
|
622
|
+
):
|
619
623
|
continue
|
620
624
|
if created_after and not (
|
621
625
|
mlflow_model_version.creation_timestamp
|
@@ -260,7 +260,7 @@ class MLFlowDeploymentService(LocalDaemonService, BaseDeploymentService):
|
|
260
260
|
)
|
261
261
|
|
262
262
|
if self.endpoint.prediction_url is not None:
|
263
|
-
if type(request)
|
263
|
+
if type(request) is pd.DataFrame:
|
264
264
|
response = requests.post( # nosec
|
265
265
|
self.endpoint.prediction_url,
|
266
266
|
json={"instances": request.to_dict("records")},
|
zenml/integrations/registry.py
CHANGED
@@ -124,6 +124,43 @@ class IntegrationRegistry(object):
|
|
124
124
|
)
|
125
125
|
]
|
126
126
|
|
127
|
+
def select_uninstall_requirements(
|
128
|
+
self,
|
129
|
+
integration_name: Optional[str] = None,
|
130
|
+
target_os: Optional[str] = None,
|
131
|
+
) -> List[str]:
|
132
|
+
"""Select the uninstall requirements for a given integration or all integrations.
|
133
|
+
|
134
|
+
Args:
|
135
|
+
integration_name: Name of the integration to check.
|
136
|
+
target_os: Target OS for the requirements.
|
137
|
+
|
138
|
+
Returns:
|
139
|
+
List of requirements for the integration uninstall.
|
140
|
+
|
141
|
+
Raises:
|
142
|
+
KeyError: If the integration is not found.
|
143
|
+
"""
|
144
|
+
if integration_name:
|
145
|
+
if integration_name in self.list_integration_names:
|
146
|
+
return self._integrations[
|
147
|
+
integration_name
|
148
|
+
].get_uninstall_requirements(target_os=target_os)
|
149
|
+
else:
|
150
|
+
raise KeyError(
|
151
|
+
f"Version {integration_name} does not exist. "
|
152
|
+
f"Currently the following integrations are implemented. "
|
153
|
+
f"{self.list_integration_names}"
|
154
|
+
)
|
155
|
+
else:
|
156
|
+
return [
|
157
|
+
requirement
|
158
|
+
for name in self.list_integration_names
|
159
|
+
for requirement in self._integrations[
|
160
|
+
name
|
161
|
+
].get_uninstall_requirements(target_os=target_os)
|
162
|
+
]
|
163
|
+
|
127
164
|
def is_installed(self, integration_name: Optional[str] = None) -> bool:
|
128
165
|
"""Checks if all requirements for an integration are installed.
|
129
166
|
|
@@ -122,7 +122,7 @@ class S3ArtifactStore(BaseArtifactStore, AuthenticationMixin):
|
|
122
122
|
|
123
123
|
def get_credentials(
|
124
124
|
self,
|
125
|
-
) -> Tuple[Optional[str], Optional[str], Optional[str]]:
|
125
|
+
) -> Tuple[Optional[str], Optional[str], Optional[str], Optional[str]]:
|
126
126
|
"""Gets authentication credentials.
|
127
127
|
|
128
128
|
If an authentication secret is configured, the secret values are
|
@@ -130,8 +130,8 @@ class S3ArtifactStore(BaseArtifactStore, AuthenticationMixin):
|
|
130
130
|
attributes.
|
131
131
|
|
132
132
|
Returns:
|
133
|
-
Tuple (key, secret, token) of credentials used to
|
134
|
-
the S3 filesystem.
|
133
|
+
Tuple (key, secret, token, region) of credentials used to
|
134
|
+
authenticate with the S3 filesystem.
|
135
135
|
|
136
136
|
Raises:
|
137
137
|
RuntimeError: If the AWS connector behaves unexpectedly.
|
@@ -151,6 +151,7 @@ class S3ArtifactStore(BaseArtifactStore, AuthenticationMixin):
|
|
151
151
|
credentials.access_key,
|
152
152
|
credentials.secret_key,
|
153
153
|
credentials.token,
|
154
|
+
client.meta.region_name,
|
154
155
|
)
|
155
156
|
|
156
157
|
secret = self.get_typed_authentication_secret(
|
@@ -161,9 +162,10 @@ class S3ArtifactStore(BaseArtifactStore, AuthenticationMixin):
|
|
161
162
|
secret.aws_access_key_id,
|
162
163
|
secret.aws_secret_access_key,
|
163
164
|
secret.aws_session_token,
|
165
|
+
None,
|
164
166
|
)
|
165
167
|
else:
|
166
|
-
return self.config.key, self.config.secret, self.config.token
|
168
|
+
return self.config.key, self.config.secret, self.config.token, None
|
167
169
|
|
168
170
|
@property
|
169
171
|
def filesystem(self) -> ZenMLS3Filesystem:
|
@@ -176,13 +178,22 @@ class S3ArtifactStore(BaseArtifactStore, AuthenticationMixin):
|
|
176
178
|
if self._filesystem and not self.connector_has_expired():
|
177
179
|
return self._filesystem
|
178
180
|
|
179
|
-
key, secret, token = self.get_credentials()
|
181
|
+
key, secret, token, region = self.get_credentials()
|
182
|
+
|
183
|
+
# Use the region from the connector if available, otherwise some
|
184
|
+
# remote workloads (e.g. Sagemaker) might not work correctly because
|
185
|
+
# they look for the bucket in the wrong region
|
186
|
+
client_kwargs = {}
|
187
|
+
if region:
|
188
|
+
client_kwargs["region_name"] = region
|
189
|
+
if self.config.client_kwargs:
|
190
|
+
client_kwargs.update(self.config.client_kwargs)
|
180
191
|
|
181
192
|
self._filesystem = ZenMLS3Filesystem(
|
182
193
|
key=key,
|
183
194
|
secret=secret,
|
184
195
|
token=token,
|
185
|
-
client_kwargs=
|
196
|
+
client_kwargs=client_kwargs,
|
186
197
|
config_kwargs=self.config.config_kwargs,
|
187
198
|
s3_additional_kwargs=self.config.s3_additional_kwargs,
|
188
199
|
)
|
@@ -43,7 +43,7 @@ class SkypilotBaseOrchestratorSettings(BaseSettings):
|
|
43
43
|
`{'tpu_vm': True, 'runtime_version': 'tpu-vm-base'}` for TPUs.
|
44
44
|
use_spot: whether to use spot instances. If None, defaults to
|
45
45
|
False.
|
46
|
-
|
46
|
+
job_recovery: the spot recovery strategy to use for the managed
|
47
47
|
spot to recover the cluster from preemption. Refer to
|
48
48
|
`recovery_strategy module <https://github.com/skypilot-org/skypilot/blob/master/sky/spot/recovery_strategy.py>`__ # pylint: disable=line-too-long
|
49
49
|
for more details.
|
@@ -103,7 +103,7 @@ class SkypilotBaseOrchestratorSettings(BaseSettings):
|
|
103
103
|
)
|
104
104
|
accelerator_args: Optional[Dict[str, str]] = None
|
105
105
|
use_spot: Optional[bool] = None
|
106
|
-
|
106
|
+
job_recovery: Optional[str] = None
|
107
107
|
region: Optional[str] = None
|
108
108
|
zone: Optional[str] = None
|
109
109
|
image_id: Union[Dict[str, str], str, None] = Field(
|
@@ -303,7 +303,7 @@ class SkypilotBaseOrchestrator(ContainerizedOrchestrator):
|
|
303
303
|
accelerators=settings.accelerators,
|
304
304
|
accelerator_args=settings.accelerator_args,
|
305
305
|
use_spot=settings.use_spot,
|
306
|
-
|
306
|
+
job_recovery=settings.job_recovery,
|
307
307
|
region=settings.region,
|
308
308
|
zone=settings.zone,
|
309
309
|
image_id=settings.image_id,
|
@@ -136,7 +136,7 @@ def main() -> None:
|
|
136
136
|
settings.disk_size, # Assuming disk_size is part of the settings
|
137
137
|
settings.disk_tier, # Assuming disk_tier is part of the settings
|
138
138
|
settings.use_spot,
|
139
|
-
settings.
|
139
|
+
settings.job_recovery,
|
140
140
|
settings.region,
|
141
141
|
settings.zone,
|
142
142
|
accelerators_hashable,
|
@@ -213,7 +213,7 @@ def main() -> None:
|
|
213
213
|
accelerators=settings.accelerators,
|
214
214
|
accelerator_args=settings.accelerator_args,
|
215
215
|
use_spot=settings.use_spot,
|
216
|
-
|
216
|
+
job_recovery=settings.job_recovery,
|
217
217
|
region=settings.region,
|
218
218
|
zone=settings.zone,
|
219
219
|
image_id=settings.image_id,
|
@@ -31,7 +31,8 @@ class SkypilotAWSIntegration(Integration):
|
|
31
31
|
"""Definition of Skypilot AWS Integration for ZenML."""
|
32
32
|
|
33
33
|
NAME = SKYPILOT_AWS
|
34
|
-
|
34
|
+
# all 0.6.x versions of skypilot[aws] are compatible
|
35
|
+
REQUIREMENTS = ["skypilot[aws]~=0.6.0"]
|
35
36
|
APT_PACKAGES = ["openssh-client", "rsync"]
|
36
37
|
|
37
38
|
@classmethod
|
@@ -19,13 +19,11 @@ orchestrator for a remote orchestration of ZenML pipelines on VMs.
|
|
19
19
|
from typing import List, Type
|
20
20
|
|
21
21
|
from zenml.integrations.constants import (
|
22
|
-
|
23
22
|
SKYPILOT_AZURE,
|
24
23
|
)
|
25
24
|
from zenml.integrations.integration import Integration
|
26
25
|
from zenml.stack import Flavor
|
27
26
|
|
28
|
-
|
29
27
|
SKYPILOT_AZURE_ORCHESTRATOR_FLAVOR = "vm_azure"
|
30
28
|
|
31
29
|
|
@@ -33,7 +31,7 @@ class SkypilotAzureIntegration(Integration):
|
|
33
31
|
"""Definition of Skypilot (Azure) Integration for ZenML."""
|
34
32
|
|
35
33
|
NAME = SKYPILOT_AZURE
|
36
|
-
REQUIREMENTS = ["skypilot[azure]
|
34
|
+
REQUIREMENTS = ["skypilot-nightly[azure]==1.0.0.dev20240716"]
|
37
35
|
APT_PACKAGES = ["openssh-client", "rsync"]
|
38
36
|
|
39
37
|
@classmethod
|