zenml-nightly 0.58.2.dev20240618__py3-none-any.whl → 0.58.2.dev20240619__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.
Files changed (300) hide show
  1. zenml/VERSION +1 -1
  2. zenml/_hub/client.py +8 -5
  3. zenml/actions/base_action.py +8 -10
  4. zenml/artifact_stores/base_artifact_store.py +20 -15
  5. zenml/artifact_stores/local_artifact_store.py +3 -2
  6. zenml/artifacts/artifact_config.py +34 -19
  7. zenml/artifacts/external_artifact.py +18 -8
  8. zenml/artifacts/external_artifact_config.py +14 -6
  9. zenml/artifacts/unmaterialized_artifact.py +2 -11
  10. zenml/cli/__init__.py +6 -0
  11. zenml/cli/artifact.py +20 -2
  12. zenml/cli/served_model.py +0 -1
  13. zenml/cli/server.py +3 -3
  14. zenml/cli/utils.py +36 -40
  15. zenml/cli/web_login.py +2 -2
  16. zenml/client.py +198 -24
  17. zenml/client_lazy_loader.py +20 -14
  18. zenml/config/base_settings.py +5 -6
  19. zenml/config/build_configuration.py +1 -1
  20. zenml/config/compiler.py +3 -3
  21. zenml/config/docker_settings.py +27 -28
  22. zenml/config/global_config.py +33 -37
  23. zenml/config/pipeline_configurations.py +8 -11
  24. zenml/config/pipeline_run_configuration.py +6 -2
  25. zenml/config/pipeline_spec.py +3 -4
  26. zenml/config/resource_settings.py +8 -9
  27. zenml/config/schedule.py +16 -20
  28. zenml/config/secret_reference_mixin.py +6 -3
  29. zenml/config/secrets_store_config.py +16 -23
  30. zenml/config/server_config.py +50 -46
  31. zenml/config/settings_resolver.py +1 -1
  32. zenml/config/source.py +45 -35
  33. zenml/config/step_configurations.py +53 -31
  34. zenml/config/store_config.py +20 -19
  35. zenml/config/strict_base_model.py +2 -6
  36. zenml/constants.py +26 -2
  37. zenml/container_registries/base_container_registry.py +3 -2
  38. zenml/container_registries/default_container_registry.py +3 -3
  39. zenml/event_hub/base_event_hub.py +1 -1
  40. zenml/event_sources/base_event_source.py +11 -16
  41. zenml/exceptions.py +4 -0
  42. zenml/integrations/airflow/__init__.py +2 -10
  43. zenml/integrations/airflow/flavors/airflow_orchestrator_flavor.py +6 -7
  44. zenml/integrations/airflow/orchestrators/airflow_orchestrator.py +13 -249
  45. zenml/integrations/airflow/orchestrators/dag_generator.py +5 -3
  46. zenml/integrations/argilla/flavors/argilla_annotator_flavor.py +5 -4
  47. zenml/integrations/aws/__init__.py +1 -1
  48. zenml/integrations/aws/flavors/aws_container_registry_flavor.py +3 -2
  49. zenml/integrations/aws/flavors/sagemaker_orchestrator_flavor.py +11 -5
  50. zenml/integrations/aws/flavors/sagemaker_step_operator_flavor.py +6 -2
  51. zenml/integrations/aws/service_connectors/aws_service_connector.py +5 -4
  52. zenml/integrations/azure/flavors/azureml_step_operator_flavor.py +4 -4
  53. zenml/integrations/azure/service_connectors/azure_service_connector.py +4 -3
  54. zenml/integrations/azure/step_operators/azureml_step_operator.py +1 -1
  55. zenml/integrations/bentoml/steps/bentoml_deployer.py +1 -1
  56. zenml/integrations/bitbucket/plugins/event_sources/bitbucket_webhook_event_source.py +8 -12
  57. zenml/integrations/comet/flavors/comet_experiment_tracker_flavor.py +1 -1
  58. zenml/integrations/evidently/__init__.py +3 -4
  59. zenml/integrations/evidently/column_mapping.py +11 -3
  60. zenml/integrations/evidently/data_validators/evidently_data_validator.py +21 -3
  61. zenml/integrations/evidently/metrics.py +5 -6
  62. zenml/integrations/evidently/tests.py +5 -6
  63. zenml/integrations/facets/models.py +2 -6
  64. zenml/integrations/feast/__init__.py +3 -1
  65. zenml/integrations/feast/feature_stores/feast_feature_store.py +0 -23
  66. zenml/integrations/gcp/__init__.py +1 -1
  67. zenml/integrations/gcp/flavors/vertex_orchestrator_flavor.py +1 -1
  68. zenml/integrations/gcp/flavors/vertex_step_operator_flavor.py +1 -1
  69. zenml/integrations/gcp/orchestrators/vertex_orchestrator.py +234 -103
  70. zenml/integrations/gcp/service_connectors/gcp_service_connector.py +57 -42
  71. zenml/integrations/github/code_repositories/github_code_repository.py +1 -1
  72. zenml/integrations/github/plugins/event_sources/github_webhook_event_source.py +9 -13
  73. zenml/integrations/great_expectations/__init__.py +1 -1
  74. zenml/integrations/great_expectations/data_validators/ge_data_validator.py +44 -44
  75. zenml/integrations/great_expectations/flavors/great_expectations_data_validator_flavor.py +35 -2
  76. zenml/integrations/great_expectations/ge_store_backend.py +24 -11
  77. zenml/integrations/great_expectations/materializers/ge_materializer.py +3 -3
  78. zenml/integrations/great_expectations/utils.py +5 -5
  79. zenml/integrations/huggingface/__init__.py +3 -0
  80. zenml/integrations/huggingface/flavors/huggingface_model_deployer_flavor.py +1 -1
  81. zenml/integrations/huggingface/steps/__init__.py +3 -0
  82. zenml/integrations/huggingface/steps/accelerate_runner.py +149 -0
  83. zenml/integrations/huggingface/steps/huggingface_deployer.py +2 -2
  84. zenml/integrations/hyperai/flavors/hyperai_orchestrator_flavor.py +1 -1
  85. zenml/integrations/hyperai/service_connectors/hyperai_service_connector.py +4 -3
  86. zenml/integrations/kubeflow/__init__.py +1 -1
  87. zenml/integrations/kubeflow/flavors/kubeflow_orchestrator_flavor.py +48 -81
  88. zenml/integrations/kubeflow/orchestrators/kubeflow_orchestrator.py +295 -245
  89. zenml/integrations/kubernetes/flavors/kubernetes_orchestrator_flavor.py +1 -1
  90. zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint.py +11 -2
  91. zenml/integrations/kubernetes/pod_settings.py +17 -31
  92. zenml/integrations/kubernetes/service_connectors/kubernetes_service_connector.py +8 -7
  93. zenml/integrations/label_studio/__init__.py +1 -3
  94. zenml/integrations/label_studio/annotators/label_studio_annotator.py +3 -4
  95. zenml/integrations/label_studio/flavors/label_studio_annotator_flavor.py +2 -2
  96. zenml/integrations/langchain/materializers/document_materializer.py +44 -8
  97. zenml/integrations/mlflow/__init__.py +9 -3
  98. zenml/integrations/mlflow/experiment_trackers/mlflow_experiment_tracker.py +1 -1
  99. zenml/integrations/mlflow/flavors/mlflow_experiment_tracker_flavor.py +29 -37
  100. zenml/integrations/mlflow/model_registries/mlflow_model_registry.py +4 -4
  101. zenml/integrations/mlflow/steps/mlflow_deployer.py +1 -1
  102. zenml/integrations/neptune/flavors/neptune_experiment_tracker_flavor.py +1 -1
  103. zenml/integrations/pigeon/flavors/pigeon_annotator_flavor.py +1 -1
  104. zenml/integrations/s3/flavors/s3_artifact_store_flavor.py +9 -8
  105. zenml/integrations/seldon/seldon_client.py +52 -67
  106. zenml/integrations/seldon/services/seldon_deployment.py +3 -3
  107. zenml/integrations/seldon/steps/seldon_deployer.py +4 -4
  108. zenml/integrations/skypilot/flavors/skypilot_orchestrator_base_vm_config.py +15 -5
  109. zenml/integrations/skypilot_aws/__init__.py +1 -1
  110. zenml/integrations/skypilot_aws/flavors/skypilot_orchestrator_aws_vm_flavor.py +1 -1
  111. zenml/integrations/skypilot_azure/__init__.py +1 -1
  112. zenml/integrations/skypilot_azure/flavors/skypilot_orchestrator_azure_vm_flavor.py +1 -1
  113. zenml/integrations/skypilot_gcp/__init__.py +2 -1
  114. zenml/integrations/skypilot_gcp/flavors/skypilot_orchestrator_gcp_vm_flavor.py +1 -1
  115. zenml/integrations/skypilot_lambda/flavors/skypilot_orchestrator_lambda_vm_flavor.py +2 -2
  116. zenml/integrations/spark/flavors/spark_step_operator_flavor.py +1 -1
  117. zenml/integrations/tekton/__init__.py +1 -1
  118. zenml/integrations/tekton/flavors/tekton_orchestrator_flavor.py +66 -23
  119. zenml/integrations/tekton/orchestrators/tekton_orchestrator.py +547 -233
  120. zenml/integrations/tensorboard/__init__.py +1 -12
  121. zenml/integrations/tensorboard/services/tensorboard_service.py +3 -5
  122. zenml/integrations/tensorboard/visualizers/tensorboard_visualizer.py +6 -6
  123. zenml/integrations/tensorflow/__init__.py +2 -10
  124. zenml/integrations/tensorflow/materializers/keras_materializer.py +17 -9
  125. zenml/integrations/wandb/flavors/wandb_experiment_tracker_flavor.py +9 -14
  126. zenml/integrations/whylogs/flavors/whylogs_data_validator_flavor.py +1 -1
  127. zenml/lineage_graph/lineage_graph.py +1 -1
  128. zenml/materializers/built_in_materializer.py +3 -3
  129. zenml/materializers/pydantic_materializer.py +2 -2
  130. zenml/metadata/lazy_load.py +4 -4
  131. zenml/metadata/metadata_types.py +64 -4
  132. zenml/model/model.py +79 -54
  133. zenml/model_deployers/base_model_deployer.py +14 -12
  134. zenml/model_registries/base_model_registry.py +17 -15
  135. zenml/models/__init__.py +79 -206
  136. zenml/models/v2/base/base.py +54 -41
  137. zenml/models/v2/base/base_plugin_flavor.py +2 -6
  138. zenml/models/v2/base/filter.py +91 -76
  139. zenml/models/v2/base/page.py +2 -12
  140. zenml/models/v2/base/scoped.py +4 -7
  141. zenml/models/v2/core/api_key.py +22 -8
  142. zenml/models/v2/core/artifact.py +2 -2
  143. zenml/models/v2/core/artifact_version.py +74 -40
  144. zenml/models/v2/core/code_repository.py +37 -10
  145. zenml/models/v2/core/component.py +65 -16
  146. zenml/models/v2/core/device.py +14 -4
  147. zenml/models/v2/core/event_source.py +1 -2
  148. zenml/models/v2/core/flavor.py +74 -8
  149. zenml/models/v2/core/logs.py +68 -8
  150. zenml/models/v2/core/model.py +8 -4
  151. zenml/models/v2/core/model_version.py +25 -6
  152. zenml/models/v2/core/model_version_artifact.py +51 -21
  153. zenml/models/v2/core/model_version_pipeline_run.py +45 -13
  154. zenml/models/v2/core/pipeline.py +37 -72
  155. zenml/models/v2/core/pipeline_build.py +29 -17
  156. zenml/models/v2/core/pipeline_deployment.py +18 -6
  157. zenml/models/v2/core/pipeline_namespace.py +113 -0
  158. zenml/models/v2/core/pipeline_run.py +50 -22
  159. zenml/models/v2/core/run_metadata.py +59 -36
  160. zenml/models/v2/core/schedule.py +37 -24
  161. zenml/models/v2/core/secret.py +31 -12
  162. zenml/models/v2/core/service.py +64 -36
  163. zenml/models/v2/core/service_account.py +24 -11
  164. zenml/models/v2/core/service_connector.py +219 -44
  165. zenml/models/v2/core/stack.py +45 -17
  166. zenml/models/v2/core/step_run.py +28 -8
  167. zenml/models/v2/core/tag.py +8 -4
  168. zenml/models/v2/core/trigger.py +2 -2
  169. zenml/models/v2/core/trigger_execution.py +1 -0
  170. zenml/models/v2/core/user.py +18 -21
  171. zenml/models/v2/core/workspace.py +13 -3
  172. zenml/models/v2/misc/build_item.py +3 -3
  173. zenml/models/v2/misc/external_user.py +2 -6
  174. zenml/models/v2/misc/hub_plugin_models.py +9 -9
  175. zenml/models/v2/misc/loaded_visualization.py +2 -2
  176. zenml/models/v2/misc/service_connector_type.py +8 -17
  177. zenml/models/v2/misc/user_auth.py +7 -2
  178. zenml/new/pipelines/build_utils.py +3 -3
  179. zenml/new/pipelines/pipeline.py +17 -13
  180. zenml/new/pipelines/run_utils.py +103 -1
  181. zenml/orchestrators/base_orchestrator.py +10 -7
  182. zenml/orchestrators/local_docker/local_docker_orchestrator.py +1 -1
  183. zenml/orchestrators/step_runner.py +3 -6
  184. zenml/orchestrators/utils.py +1 -1
  185. zenml/plugins/base_plugin_flavor.py +6 -10
  186. zenml/plugins/plugin_flavor_registry.py +3 -7
  187. zenml/secret/base_secret.py +7 -8
  188. zenml/service_connectors/docker_service_connector.py +4 -3
  189. zenml/service_connectors/service_connector.py +5 -12
  190. zenml/service_connectors/service_connector_registry.py +2 -4
  191. zenml/services/container/container_service.py +1 -1
  192. zenml/services/container/container_service_endpoint.py +1 -1
  193. zenml/services/local/local_service.py +1 -1
  194. zenml/services/local/local_service_endpoint.py +1 -1
  195. zenml/services/service.py +16 -10
  196. zenml/services/service_type.py +4 -5
  197. zenml/services/terraform/terraform_service.py +1 -1
  198. zenml/stack/flavor.py +1 -5
  199. zenml/stack/flavor_registry.py +4 -4
  200. zenml/stack/stack.py +4 -1
  201. zenml/stack/stack_component.py +55 -31
  202. zenml/steps/base_step.py +34 -28
  203. zenml/steps/entrypoint_function_utils.py +3 -5
  204. zenml/steps/utils.py +12 -14
  205. zenml/utils/cuda_utils.py +50 -0
  206. zenml/utils/deprecation_utils.py +18 -20
  207. zenml/utils/dict_utils.py +1 -1
  208. zenml/utils/filesync_model.py +65 -28
  209. zenml/utils/function_utils.py +260 -0
  210. zenml/utils/json_utils.py +131 -0
  211. zenml/utils/mlstacks_utils.py +2 -2
  212. zenml/utils/pydantic_utils.py +270 -62
  213. zenml/utils/secret_utils.py +65 -12
  214. zenml/utils/source_utils.py +2 -2
  215. zenml/utils/typed_model.py +5 -3
  216. zenml/utils/typing_utils.py +243 -0
  217. zenml/utils/yaml_utils.py +1 -1
  218. zenml/zen_server/auth.py +2 -2
  219. zenml/zen_server/cloud_utils.py +6 -6
  220. zenml/zen_server/deploy/base_provider.py +1 -1
  221. zenml/zen_server/deploy/deployment.py +6 -8
  222. zenml/zen_server/deploy/docker/docker_zen_server.py +3 -4
  223. zenml/zen_server/deploy/local/local_provider.py +0 -1
  224. zenml/zen_server/deploy/local/local_zen_server.py +6 -6
  225. zenml/zen_server/deploy/terraform/terraform_zen_server.py +4 -6
  226. zenml/zen_server/exceptions.py +4 -1
  227. zenml/zen_server/feature_gate/zenml_cloud_feature_gate.py +1 -1
  228. zenml/zen_server/pipeline_deployment/utils.py +48 -68
  229. zenml/zen_server/rbac/models.py +2 -5
  230. zenml/zen_server/rbac/utils.py +11 -14
  231. zenml/zen_server/routers/auth_endpoints.py +2 -2
  232. zenml/zen_server/routers/pipeline_builds_endpoints.py +1 -1
  233. zenml/zen_server/routers/runs_endpoints.py +1 -1
  234. zenml/zen_server/routers/secrets_endpoints.py +3 -2
  235. zenml/zen_server/routers/server_endpoints.py +1 -1
  236. zenml/zen_server/routers/steps_endpoints.py +1 -1
  237. zenml/zen_server/routers/workspaces_endpoints.py +1 -1
  238. zenml/zen_stores/base_zen_store.py +46 -9
  239. zenml/zen_stores/migrations/utils.py +42 -46
  240. zenml/zen_stores/migrations/versions/0701da9951a0_added_service_table.py +1 -1
  241. zenml/zen_stores/migrations/versions/1041bc644e0d_remove_secrets_manager.py +5 -3
  242. zenml/zen_stores/migrations/versions/10a907dad202_delete_mlmd_tables.py +1 -1
  243. zenml/zen_stores/migrations/versions/26b776ad583e_redesign_artifacts.py +8 -10
  244. zenml/zen_stores/migrations/versions/37835ce041d2_optimizing_database.py +3 -3
  245. zenml/zen_stores/migrations/versions/46506f72f0ed_add_server_settings.py +10 -12
  246. zenml/zen_stores/migrations/versions/5994f9ad0489_introduce_role_permissions.py +3 -2
  247. zenml/zen_stores/migrations/versions/6917bce75069_add_pipeline_run_unique_constraint.py +4 -4
  248. zenml/zen_stores/migrations/versions/728c6369cfaa_add_name_column_to_input_artifact_pk.py +3 -2
  249. zenml/zen_stores/migrations/versions/743ec82b1b3c_update_size_of_build_images.py +2 -2
  250. zenml/zen_stores/migrations/versions/7500f434b71c_remove_shared_columns.py +3 -2
  251. zenml/zen_stores/migrations/versions/7834208cc3f6_artifact_project_scoping.py +8 -7
  252. zenml/zen_stores/migrations/versions/7b651bf6822e_track_secrets_in_db.py +6 -4
  253. zenml/zen_stores/migrations/versions/7e4a481d17f7_add_identity_table.py +2 -2
  254. zenml/zen_stores/migrations/versions/7f603e583dd7_fixed_migration.py +1 -1
  255. zenml/zen_stores/migrations/versions/a39c4184c8ce_remove_secrets_manager_flavors.py +2 -2
  256. zenml/zen_stores/migrations/versions/a91762e6be36_artifact_version_table.py +4 -4
  257. zenml/zen_stores/migrations/versions/alembic_start.py +1 -1
  258. zenml/zen_stores/migrations/versions/fbd7f18ced1e_increase_step_run_field_lengths.py +4 -4
  259. zenml/zen_stores/rest_zen_store.py +109 -49
  260. zenml/zen_stores/schemas/api_key_schemas.py +1 -1
  261. zenml/zen_stores/schemas/artifact_schemas.py +8 -8
  262. zenml/zen_stores/schemas/artifact_visualization_schemas.py +3 -3
  263. zenml/zen_stores/schemas/code_repository_schemas.py +1 -1
  264. zenml/zen_stores/schemas/component_schemas.py +8 -3
  265. zenml/zen_stores/schemas/device_schemas.py +8 -6
  266. zenml/zen_stores/schemas/event_source_schemas.py +3 -4
  267. zenml/zen_stores/schemas/flavor_schemas.py +5 -3
  268. zenml/zen_stores/schemas/model_schemas.py +26 -1
  269. zenml/zen_stores/schemas/pipeline_build_schemas.py +1 -1
  270. zenml/zen_stores/schemas/pipeline_deployment_schemas.py +4 -4
  271. zenml/zen_stores/schemas/pipeline_run_schemas.py +6 -6
  272. zenml/zen_stores/schemas/pipeline_schemas.py +5 -2
  273. zenml/zen_stores/schemas/run_metadata_schemas.py +2 -2
  274. zenml/zen_stores/schemas/secret_schemas.py +8 -5
  275. zenml/zen_stores/schemas/server_settings_schemas.py +3 -1
  276. zenml/zen_stores/schemas/service_connector_schemas.py +1 -1
  277. zenml/zen_stores/schemas/service_schemas.py +11 -2
  278. zenml/zen_stores/schemas/stack_schemas.py +1 -1
  279. zenml/zen_stores/schemas/step_run_schemas.py +11 -11
  280. zenml/zen_stores/schemas/tag_schemas.py +6 -2
  281. zenml/zen_stores/schemas/trigger_schemas.py +2 -2
  282. zenml/zen_stores/schemas/user_schemas.py +2 -2
  283. zenml/zen_stores/schemas/workspace_schemas.py +3 -1
  284. zenml/zen_stores/secrets_stores/aws_secrets_store.py +19 -20
  285. zenml/zen_stores/secrets_stores/azure_secrets_store.py +17 -20
  286. zenml/zen_stores/secrets_stores/base_secrets_store.py +79 -12
  287. zenml/zen_stores/secrets_stores/gcp_secrets_store.py +17 -20
  288. zenml/zen_stores/secrets_stores/hashicorp_secrets_store.py +4 -8
  289. zenml/zen_stores/secrets_stores/service_connector_secrets_store.py +10 -7
  290. zenml/zen_stores/secrets_stores/sql_secrets_store.py +5 -6
  291. zenml/zen_stores/sql_zen_store.py +196 -120
  292. zenml/zen_stores/zen_store_interface.py +33 -0
  293. {zenml_nightly-0.58.2.dev20240618.dist-info → zenml_nightly-0.58.2.dev20240619.dist-info}/METADATA +8 -7
  294. {zenml_nightly-0.58.2.dev20240618.dist-info → zenml_nightly-0.58.2.dev20240619.dist-info}/RECORD +297 -294
  295. zenml/integrations/kubeflow/utils.py +0 -95
  296. zenml/models/v2/base/internal.py +0 -37
  297. zenml/models/v2/base/update.py +0 -44
  298. {zenml_nightly-0.58.2.dev20240618.dist-info → zenml_nightly-0.58.2.dev20240619.dist-info}/LICENSE +0 -0
  299. {zenml_nightly-0.58.2.dev20240618.dist-info → zenml_nightly-0.58.2.dev20240619.dist-info}/WHEEL +0 -0
  300. {zenml_nightly-0.58.2.dev20240618.dist-info → zenml_nightly-0.58.2.dev20240619.dist-info}/entry_points.txt +0 -0
@@ -31,7 +31,7 @@
31
31
  """Implementation of the Kubeflow orchestrator."""
32
32
 
33
33
  import os
34
- import sys
34
+ import types
35
35
  from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Type, cast
36
36
  from uuid import UUID
37
37
 
@@ -39,15 +39,15 @@ import kfp
39
39
  import requests
40
40
  import urllib3
41
41
  from kfp import dsl
42
- from kfp.compiler import Compiler as KFPCompiler
42
+ from kfp.client import Client as KFPClient
43
+ from kfp.compiler import Compiler
43
44
  from kfp_server_api.exceptions import ApiException
44
45
  from kubernetes import client as k8s_client
45
46
  from kubernetes import config as k8s_config
46
47
 
47
48
  from zenml.client import Client
48
- from zenml.config.global_config import GlobalConfiguration
49
+ from zenml.config.resource_settings import ResourceSettings
49
50
  from zenml.constants import (
50
- ENV_ZENML_LOCAL_STORES_PATH,
51
51
  METADATA_ORCHESTRATOR_URL,
52
52
  )
53
53
  from zenml.entrypoints import StepEntrypointConfiguration
@@ -57,19 +57,17 @@ from zenml.integrations.kubeflow.flavors.kubeflow_orchestrator_flavor import (
57
57
  KubeflowOrchestratorConfig,
58
58
  KubeflowOrchestratorSettings,
59
59
  )
60
- from zenml.integrations.kubeflow.utils import apply_pod_settings
61
60
  from zenml.io import fileio
62
61
  from zenml.logger import get_logger
63
62
  from zenml.metadata.metadata_types import MetadataType, Uri
64
63
  from zenml.orchestrators import ContainerizedOrchestrator
65
64
  from zenml.orchestrators.utils import get_orchestrator_run_name
66
65
  from zenml.stack import StackValidator
67
- from zenml.utils import io_utils, settings_utils
66
+ from zenml.utils import io_utils, settings_utils, yaml_utils
68
67
 
69
68
  if TYPE_CHECKING:
70
69
  from zenml.models import PipelineDeploymentResponse
71
70
  from zenml.stack import Stack
72
- from zenml.steps import ResourceSettings
73
71
 
74
72
 
75
73
  logger = get_logger(__name__)
@@ -80,6 +78,7 @@ KFP_POD_LABELS = {
80
78
  }
81
79
 
82
80
  ENV_KFP_RUN_ID = "KFP_RUN_ID"
81
+ KFP_ACCELERATOR_NODE_SELECTOR_CONSTRAINT_LABEL = "accelerator"
83
82
 
84
83
 
85
84
  class KubeClientKFPClient(kfp.Client): # type: ignore[misc]
@@ -121,7 +120,7 @@ class KubeClientKFPClient(kfp.Client): # type: ignore[misc]
121
120
  host = (
122
121
  kube_config.host
123
122
  + "/"
124
- + self.KUBE_PROXY_PATH.format(kwargs.get("namespace", "kubeflow"))
123
+ + self._KUBE_PROXY_PATH.format(kwargs.get("namespace", "kubeflow"))
125
124
  )
126
125
 
127
126
  config = Configuration(
@@ -145,6 +144,70 @@ class KubeClientKFPClient(kfp.Client): # type: ignore[misc]
145
144
  class KubeflowOrchestrator(ContainerizedOrchestrator):
146
145
  """Orchestrator responsible for running pipelines using Kubeflow."""
147
146
 
147
+ _k8s_client: Optional[k8s_client.ApiClient] = None
148
+
149
+ def _get_kfp_client(
150
+ self,
151
+ settings: KubeflowOrchestratorSettings,
152
+ ) -> kfp.Client:
153
+ """Creates a KFP client instance.
154
+
155
+ Args:
156
+ settings: Settings which can be used to
157
+ configure the client instance.
158
+
159
+ Returns:
160
+ A KFP client instance.
161
+
162
+ Raises:
163
+ RuntimeError: If the linked Kubernetes connector behaves
164
+ unexpectedly.
165
+ """
166
+ connector = self.get_connector()
167
+ client_args = settings.client_args.copy()
168
+
169
+ # The kube_context, host and namespace are stack component
170
+ # configurations that refer to the Kubeflow deployment. We don't want
171
+ # these overwritten on a run by run basis by user settings
172
+ client_args["namespace"] = self.config.kubeflow_namespace
173
+
174
+ if connector:
175
+ client = connector.connect()
176
+ if not isinstance(client, k8s_client.ApiClient):
177
+ raise RuntimeError(
178
+ f"Expected a k8s_client.ApiClient while trying to use the "
179
+ f"linked connector, but got {type(client)}."
180
+ )
181
+ return KubeClientKFPClient(
182
+ client=client,
183
+ **client_args,
184
+ )
185
+
186
+ elif self.config.kubernetes_context:
187
+ client_args["kube_context"] = self.config.kubernetes_context
188
+
189
+ elif self.config.kubeflow_hostname:
190
+ client_args["host"] = self.config.kubeflow_hostname
191
+
192
+ # Handle username and password, ignore the case if one is passed and
193
+ # not the other. Also do not attempt to get cookie if cookie is
194
+ # already passed in client_args
195
+ if settings.client_username and settings.client_password:
196
+ # If cookie is already set, then ignore
197
+ if "cookie" in client_args:
198
+ logger.warning(
199
+ "Cookie already set in `client_args`, ignoring "
200
+ "`client_username` and `client_password`..."
201
+ )
202
+ else:
203
+ session_cookie = self._get_session_cookie(
204
+ username=settings.client_username,
205
+ password=settings.client_password,
206
+ )
207
+
208
+ client_args["cookies"] = session_cookie
209
+ return KFPClient(**client_args)
210
+
148
211
  @property
149
212
  def config(self) -> KubeflowOrchestratorConfig:
150
213
  """Returns the `KubeflowOrchestratorConfig` config.
@@ -272,10 +335,7 @@ class KubeflowOrchestrator(ContainerizedOrchestrator):
272
335
  f"--skip_local_validations=True'\n"
273
336
  )
274
337
 
275
- if (
276
- not self.config.skip_local_validations
277
- and not self.config.is_local
278
- ):
338
+ if not self.config.is_local:
279
339
  # if the orchestrator is not running in a local k3d cluster,
280
340
  # we cannot have any other local components in our stack,
281
341
  # because we cannot mount the local path into the container.
@@ -354,114 +414,49 @@ class KubeflowOrchestrator(ContainerizedOrchestrator):
354
414
  """
355
415
  return os.path.join(self.root_directory, "pipelines")
356
416
 
357
- def _configure_container_op(
417
+ def _create_dynamic_component(
358
418
  self,
359
- container_op: dsl.ContainerOp,
360
- settings: KubeflowOrchestratorSettings,
361
- ) -> None:
362
- """Makes changes in place to the configuration of the container op.
363
-
364
- Configures persistent mounted volumes for each stack component that
365
- writes to a local path. Adds some labels to the container_op and applies
366
- some functions to ir.
419
+ image: str,
420
+ command: List[str],
421
+ arguments: List[str],
422
+ component_name: str,
423
+ ) -> dsl.PipelineTask:
424
+ """Creates a dynamic container component for a Kubeflow pipeline.
367
425
 
368
426
  Args:
369
- container_op: The kubeflow container operation to configure.
370
- settings: Orchestrator settings for this step.
371
- """
372
- volumes: Dict[str, k8s_client.V1Volume] = {}
373
-
374
- stack = Client().active_stack
375
-
376
- if self.config.is_local:
377
- stack.check_local_paths()
378
-
379
- local_stores_path = GlobalConfiguration().local_stores_path
427
+ image: The image to use for the component.
428
+ command: The command to use for the component.
429
+ arguments: The arguments to use for the component.
430
+ component_name: The name of the component.
380
431
 
381
- host_path = k8s_client.V1HostPathVolumeSource(
382
- path=local_stores_path, type="Directory"
383
- )
384
-
385
- volumes[local_stores_path] = k8s_client.V1Volume(
386
- name="local-stores",
387
- host_path=host_path,
388
- )
389
- logger.debug(
390
- "Adding host path volume for the local ZenML stores (path: %s) "
391
- "in kubeflow pipelines container.",
392
- local_stores_path,
393
- )
394
-
395
- if sys.platform == "win32":
396
- # File permissions are not checked on Windows. This if clause
397
- # prevents mypy from complaining about unused 'type: ignore'
398
- # statements
399
- pass
400
- else:
401
- # Run KFP containers in the context of the local UID/GID
402
- # to ensure that the local stores can be shared
403
- # with the local pipeline runs.
404
- container_op.container.security_context = (
405
- k8s_client.V1SecurityContext(
406
- run_as_user=os.getuid(),
407
- run_as_group=os.getgid(),
408
- )
409
- )
410
- logger.debug(
411
- "Setting security context UID and GID to local user/group "
412
- "in kubeflow pipelines container."
413
- )
414
-
415
- container_op.container.add_env_variable(
416
- k8s_client.V1EnvVar(
417
- name=ENV_ZENML_LOCAL_STORES_PATH,
418
- value=local_stores_path,
419
- )
420
- )
421
-
422
- container_op.add_pvolumes(volumes)
432
+ Returns:
433
+ The dynamic container component.
434
+ """
423
435
 
424
- # Add some pod labels to the container_op
425
- for k, v in KFP_POD_LABELS.items():
426
- container_op.add_pod_label(k, v)
436
+ @dsl.container_component # type: ignore[misc]
437
+ def dynamic_container_component() -> dsl.ContainerSpec:
438
+ """Dynamic container component.
427
439
 
428
- if settings.pod_settings:
429
- apply_pod_settings(
430
- container_op=container_op, settings=settings.pod_settings
440
+ Returns:
441
+ The dynamic container component.
442
+ """
443
+ return dsl.ContainerSpec(
444
+ image=image,
445
+ command=command,
446
+ args=arguments,
431
447
  )
432
448
 
433
- # Disable caching in KFP v1 only works like this, replace by the second
434
- # line in the future
435
- container_op.execution_options.caching_strategy.max_cache_staleness = (
436
- "P0D"
449
+ # Change the name of the function
450
+ new_container_spec_func = types.FunctionType(
451
+ dynamic_container_component.__code__,
452
+ dynamic_container_component.__globals__,
453
+ name=component_name,
454
+ argdefs=dynamic_container_component.__defaults__,
455
+ closure=dynamic_container_component.__closure__,
437
456
  )
438
- # container_op.set_caching_options(enable_caching=False)
439
-
440
- @staticmethod
441
- def _configure_container_resources(
442
- container_op: dsl.ContainerOp,
443
- resource_settings: "ResourceSettings",
444
- ) -> None:
445
- """Adds resource requirements to the container.
457
+ pipeline_task = dsl.container_component(new_container_spec_func)
446
458
 
447
- Args:
448
- container_op: The kubeflow container operation to configure.
449
- resource_settings: The resource settings to use for this
450
- container.
451
- """
452
- if resource_settings.cpu_count is not None:
453
- container_op = container_op.set_cpu_limit(
454
- str(resource_settings.cpu_count)
455
- )
456
-
457
- if resource_settings.gpu_count is not None:
458
- container_op = container_op.set_gpu_limit(
459
- resource_settings.gpu_count
460
- )
461
-
462
- if resource_settings.memory is not None:
463
- memory_limit = resource_settings.memory[:-1]
464
- container_op = container_op.set_memory_limit(memory_limit)
459
+ return pipeline_task
465
460
 
466
461
  def prepare_or_run_pipeline(
467
462
  self,
@@ -517,89 +512,149 @@ class KubeflowOrchestrator(ContainerizedOrchestrator):
517
512
  assert stack.container_registry
518
513
 
519
514
  # Create a callable for future compilation into a dsl.Pipeline.
520
- def _construct_kfp_pipeline() -> None:
521
- """Create a container_op for each step.
522
-
523
- This should contain the name of the docker image and configures the
524
- entrypoint of the docker image to run the step.
515
+ orchestrator_run_name = get_orchestrator_run_name(
516
+ pipeline_name=deployment.pipeline_configuration.name
517
+ ).replace("_", "-")
525
518
 
526
- Additionally, this gives each container_op information about its
527
- direct downstream steps.
519
+ def _create_dynamic_pipeline() -> Any:
520
+ """Create a dynamic pipeline including each step.
528
521
 
529
- If this callable is passed to the `_create_and_write_workflow()`
530
- method of a KFPCompiler all dsl.ContainerOp instances will be
531
- automatically added to a singular dsl.Pipeline instance.
522
+ Returns:
523
+ pipeline_func
532
524
  """
533
- # Dictionary of container_ops index by the associated step name
534
- step_name_to_container_op: Dict[str, dsl.ContainerOp] = {}
525
+ step_name_to_dynamic_component: Dict[str, Any] = {}
526
+ node_selector_constraint: Optional[Tuple[str, str]] = None
535
527
 
536
528
  for step_name, step in deployment.step_configurations.items():
537
529
  image = self.get_image(
538
- deployment=deployment, step_name=step_name
530
+ deployment=deployment,
531
+ step_name=step_name,
539
532
  )
540
-
541
- # The command will be needed to eventually call the python step
542
- # within the docker container
543
533
  command = StepEntrypointConfiguration.get_entrypoint_command()
544
-
545
- # The arguments are passed to configure the entrypoint of the
546
- # docker container when the step is called.
547
534
  arguments = (
548
535
  StepEntrypointConfiguration.get_entrypoint_arguments(
549
- step_name=step_name, deployment_id=deployment.id
536
+ step_name=step_name,
537
+ deployment_id=deployment.id,
550
538
  )
551
539
  )
552
-
553
- # Create a container_op - the kubeflow equivalent of a step. It
554
- # contains the name of the step, the name of the docker image,
555
- # the command to use to run the step entrypoint
556
- # (e.g. `python -m zenml.entrypoints.step_entrypoint`)
557
- # and the arguments to be passed along with the command. Find
558
- # out more about how these arguments are parsed and used
559
- # in the base entrypoint `run()` method.
560
- container_op = dsl.ContainerOp(
561
- name=step_name,
562
- image=image,
563
- command=command,
564
- arguments=arguments,
540
+ dynamic_component = self._create_dynamic_component(
541
+ image, command, arguments, step_name
565
542
  )
566
-
567
- settings = cast(
543
+ step_settings = cast(
568
544
  KubeflowOrchestratorSettings, self.get_settings(step)
569
545
  )
570
- self._configure_container_op(
571
- container_op=container_op,
572
- settings=settings,
573
- )
546
+ pod_settings = step_settings.pod_settings
547
+ if pod_settings:
548
+ if pod_settings.host_ipc:
549
+ logger.warning(
550
+ "Host IPC is set to `True` but not supported in "
551
+ "this orchestrator. Ignoring..."
552
+ )
553
+ if pod_settings.affinity:
554
+ logger.warning(
555
+ "Affinity is set but not supported in Kubeflow with "
556
+ "Kubeflow Pipelines 2.x. Ignoring..."
557
+ )
558
+ if pod_settings.tolerations:
559
+ logger.warning(
560
+ "Tolerations are set but not supported in "
561
+ "Kubeflow with Kubeflow Pipelines 2.x. Ignoring..."
562
+ )
563
+ if pod_settings.volumes:
564
+ logger.warning(
565
+ "Volumes are set but not supported in Kubeflow with "
566
+ "Kubeflow Pipelines 2.x. Ignoring..."
567
+ )
568
+ if pod_settings.volume_mounts:
569
+ logger.warning(
570
+ "Volume mounts are set but not supported in "
571
+ "Kubeflow with Kubeflow Pipelines 2.x. Ignoring..."
572
+ )
574
573
 
575
- if self.requires_resources_in_orchestration_environment(step):
576
- self._configure_container_resources(
577
- container_op=container_op,
578
- resource_settings=step.config.resource_settings,
579
- )
574
+ # apply pod settings
575
+ if (
576
+ KFP_ACCELERATOR_NODE_SELECTOR_CONSTRAINT_LABEL
577
+ in pod_settings.node_selectors.keys()
578
+ ):
579
+ node_selector_constraint = (
580
+ KFP_ACCELERATOR_NODE_SELECTOR_CONSTRAINT_LABEL,
581
+ pod_settings.node_selectors[
582
+ KFP_ACCELERATOR_NODE_SELECTOR_CONSTRAINT_LABEL
583
+ ],
584
+ )
580
585
 
581
- for key, value in environment.items():
582
- container_op.container.add_env_variable(
583
- k8s_client.V1EnvVar(
584
- name=key,
585
- value=value,
586
+ step_name_to_dynamic_component[step_name] = dynamic_component
587
+
588
+ @dsl.pipeline( # type: ignore[misc]
589
+ display_name=orchestrator_run_name,
590
+ )
591
+ def dynamic_pipeline() -> None:
592
+ """Dynamic pipeline."""
593
+ # iterate through the components one by one
594
+ # (from step_name_to_dynamic_component)
595
+ for (
596
+ component_name,
597
+ component,
598
+ ) in step_name_to_dynamic_component.items():
599
+ # for each component, check to see what other steps are
600
+ # upstream of it
601
+ step = deployment.step_configurations[component_name]
602
+ upstream_step_components = [
603
+ step_name_to_dynamic_component[upstream_step_name]
604
+ for upstream_step_name in step.spec.upstream_steps
605
+ ]
606
+ task = (
607
+ component()
608
+ .set_display_name(
609
+ name=component_name,
586
610
  )
611
+ .set_caching_options(enable_caching=False)
612
+ .set_env_variable(
613
+ name=ENV_KFP_RUN_ID,
614
+ value=dsl.PIPELINE_JOB_NAME_PLACEHOLDER,
615
+ )
616
+ .after(*upstream_step_components)
617
+ )
618
+ self._configure_container_resources(
619
+ task,
620
+ step.config.resource_settings,
621
+ node_selector_constraint,
587
622
  )
588
623
 
589
- # Find the upstream container ops of the current step and
590
- # configure the current container op to run after them
591
- for upstream_step_name in step.spec.upstream_steps:
592
- upstream_container_op = step_name_to_container_op[
593
- upstream_step_name
594
- ]
595
- container_op.after(upstream_container_op)
624
+ return dynamic_pipeline
596
625
 
597
- # Update dictionary of container ops with the current one
598
- step_name_to_container_op[step_name] = container_op
626
+ def _update_yaml_with_environment(
627
+ yaml_file_path: str, environment: Dict[str, str]
628
+ ) -> None:
629
+ """Updates the env section of the steps in the YAML file with the given environment variables.
599
630
 
600
- orchestrator_run_name = get_orchestrator_run_name(
601
- pipeline_name=deployment.pipeline_configuration.name
602
- )
631
+ Args:
632
+ yaml_file_path: The path to the YAML file to update.
633
+ environment: A dictionary of environment variables to add.
634
+ """
635
+ pipeline_definition = yaml_utils.read_yaml(pipeline_file_path)
636
+
637
+ # Iterate through each component and add the environment variables
638
+ for executor in pipeline_definition["deploymentSpec"]["executors"]:
639
+ if (
640
+ "container"
641
+ in pipeline_definition["deploymentSpec"]["executors"][
642
+ executor
643
+ ]
644
+ ):
645
+ container = pipeline_definition["deploymentSpec"][
646
+ "executors"
647
+ ][executor]["container"]
648
+ if "env" not in container:
649
+ container["env"] = []
650
+ for key, value in environment.items():
651
+ container["env"].append({"name": key, "value": value})
652
+
653
+ yaml_utils.write_yaml(pipeline_file_path, pipeline_definition)
654
+
655
+ print(
656
+ f"Updated YAML file with environment variables at {yaml_file_path}"
657
+ )
603
658
 
604
659
  # Get a filepath to use to save the finished yaml to
605
660
  fileio.makedirs(self.pipeline_directory)
@@ -608,11 +663,15 @@ class KubeflowOrchestrator(ContainerizedOrchestrator):
608
663
  )
609
664
 
610
665
  # write the argo pipeline yaml
611
- KFPCompiler()._create_and_write_workflow(
612
- pipeline_func=_construct_kfp_pipeline,
613
- pipeline_name=deployment.pipeline_configuration.name,
666
+ Compiler().compile(
667
+ pipeline_func=_create_dynamic_pipeline(),
614
668
  package_path=pipeline_file_path,
669
+ pipeline_name=orchestrator_run_name,
615
670
  )
671
+
672
+ # Let's update the YAML file with the environment variables
673
+ _update_yaml_with_environment(pipeline_file_path, environment)
674
+
616
675
  logger.info(
617
676
  "Writing Kubeflow workflow definition to `%s`.", pipeline_file_path
618
677
  )
@@ -696,7 +755,7 @@ class KubeflowOrchestrator(ContainerizedOrchestrator):
696
755
  else None
697
756
  )
698
757
  result = client.create_recurring_run(
699
- experiment_id=experiment.id,
758
+ experiment_id=experiment.experiment_id,
700
759
  job_name=run_name,
701
760
  pipeline_package_path=pipeline_file_path,
702
761
  enable_caching=False,
@@ -707,7 +766,10 @@ class KubeflowOrchestrator(ContainerizedOrchestrator):
707
766
  no_catchup=not deployment.schedule.catchup,
708
767
  )
709
768
 
710
- logger.info("Started recurring run with ID '%s'.", result.id)
769
+ logger.info(
770
+ "Started recurring run with ID '%s'.",
771
+ result.recurring_run_id,
772
+ )
711
773
  else:
712
774
  logger.info(
713
775
  "No schedule detected. Creating a one-off pipeline run.."
@@ -773,72 +835,6 @@ class KubeflowOrchestrator(ContainerizedOrchestrator):
773
835
  f"{ENV_KFP_RUN_ID}."
774
836
  )
775
837
 
776
- def _get_kfp_client(
777
- self,
778
- settings: KubeflowOrchestratorSettings,
779
- ) -> kfp.Client:
780
- """Creates a KFP client instance.
781
-
782
- Args:
783
- settings: Settings which can be used to
784
- configure the client instance.
785
-
786
- Returns:
787
- A KFP client instance.
788
-
789
- Raises:
790
- RuntimeError: If the linked Kubernetes connector behaves
791
- unexpectedly.
792
- """
793
- connector = self.get_connector()
794
- client_args = settings.client_args.copy()
795
-
796
- # The kube_context, host and namespace are stack component
797
- # configurations that refer to the Kubeflow deployment. We don't want
798
- # these overwritten on a run by run basis by user settings
799
- client_args["namespace"] = self.config.kubeflow_namespace
800
-
801
- if connector:
802
- client = connector.connect()
803
- if not isinstance(client, k8s_client.ApiClient):
804
- raise RuntimeError(
805
- f"Expected a k8s_client.ApiClient while trying to use the "
806
- f"linked connector, but got {type(client)}."
807
- )
808
-
809
- kfp_client = KubeClientKFPClient(
810
- client=client,
811
- **client_args,
812
- )
813
-
814
- return kfp_client
815
-
816
- elif self.config.kubernetes_context:
817
- client_args["kube_context"] = self.config.kubernetes_context
818
-
819
- elif self.config.kubeflow_hostname:
820
- client_args["host"] = self.config.kubeflow_hostname
821
-
822
- # Handle username and password, ignore the case if one is passed and
823
- # not the other. Also do not attempt to get cookie if cookie is
824
- # already passed in client_args
825
- if settings.client_username and settings.client_password:
826
- # If cookie is already set, then ignore
827
- if "cookie" in client_args:
828
- logger.warning(
829
- "Cookie already set in `client_args`, ignoring "
830
- "`client_username` and `client_password`..."
831
- )
832
- else:
833
- session_cookie = self._get_session_cookie(
834
- username=settings.client_username,
835
- password=settings.client_password,
836
- )
837
-
838
- client_args["cookies"] = session_cookie
839
-
840
- return kfp.Client(**client_args)
841
-
842
838
  def _get_session_cookie(self, username: str, password: str) -> str:
843
839
  """Gets session cookie from username and password.
844
840
 
@@ -892,7 +888,8 @@ class KubeflowOrchestrator(ContainerizedOrchestrator):
892
888
  raise RuntimeError(
893
889
  f"Error while trying to fetch kubeflow cookie: {errh}"
894
890
  )
895
- cookie_dict = session.cookies.get_dict() # type: ignore[no-untyped-call]
891
+
892
+ cookie_dict: Dict[str, str] = session.cookies.get_dict() # type: ignore[no-untyped-call]
896
893
 
897
894
  if "authservice_session" not in cookie_dict:
898
895
  raise RuntimeError("Invalid username and/or password!")
@@ -924,8 +921,8 @@ class KubeflowOrchestrator(ContainerizedOrchestrator):
924
921
  run = Client().get_pipeline_run(run_id)
925
922
 
926
923
  settings_key = settings_utils.get_stack_component_setting_key(self)
927
- run_settings = self.settings_class.parse_obj(
928
- run.config.dict().get(settings_key, self.config)
924
+ run_settings = self.settings_class.model_validate(
925
+ run.config.model_dump().get(settings_key, self.config)
929
926
  )
930
927
  user_namespace = run_settings.user_namespace
931
928
 
@@ -941,3 +938,56 @@ class KubeflowOrchestrator(ContainerizedOrchestrator):
941
938
  return {
942
939
  METADATA_ORCHESTRATOR_URL: Uri(f"{hostname}"),
943
940
  }
941
+
942
+ def _configure_container_resources(
943
+ self,
944
+ dynamic_component: dsl.PipelineTask,
945
+ resource_settings: "ResourceSettings",
946
+ node_selector_constraint: Optional[Tuple[str, str]] = None,
947
+ ) -> dsl.PipelineTask:
948
+ """Adds resource requirements to the container.
949
+
950
+ Args:
951
+ dynamic_component: The dynamic component to add the resource
952
+ settings to.
953
+ resource_settings: The resource settings to use for this
954
+ container.
955
+ node_selector_constraint: Node selector constraint to apply to
956
+ the container.
957
+
958
+ Returns:
959
+ The dynamic component with the resource settings applied.
960
+ """
961
+ # Set optional CPU, RAM and GPU constraints for the pipeline
962
+ if resource_settings:
963
+ cpu_limit = resource_settings.cpu_count or None
964
+
965
+ if cpu_limit is not None:
966
+ dynamic_component = dynamic_component.set_cpu_limit(str(cpu_limit))
967
+
968
+ memory_limit = resource_settings.get_memory() or None
969
+ if memory_limit is not None:
970
+ dynamic_component = dynamic_component.set_memory_limit(
971
+ memory_limit
972
+ )
973
+
974
+ gpu_limit = (
975
+ resource_settings.gpu_count
976
+ if resource_settings.gpu_count is not None
977
+ else 0
978
+ )
979
+
980
+ if node_selector_constraint:
981
+ (constraint_label, value) = node_selector_constraint
982
+ if gpu_limit is not None and gpu_limit > 0:
983
+ dynamic_component = (
984
+ dynamic_component.set_accelerator_type(value)
985
+ .set_accelerator_limit(gpu_limit)
986
+ .set_gpu_limit(gpu_limit)
987
+ )
988
+ elif constraint_label == "accelerator" and gpu_limit == 0:
989
+ logger.warning(
990
+ "GPU limit is set to 0 but a GPU type is specified. Ignoring GPU settings."
991
+ )
992
+
993
+ return dynamic_component