zenml-nightly 0.61.0.dev20240712__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.
Files changed (152) hide show
  1. README.md +2 -2
  2. RELEASE_NOTES.md +40 -0
  3. zenml/VERSION +1 -1
  4. zenml/__init__.py +2 -0
  5. zenml/cli/stack.py +114 -248
  6. zenml/cli/stack_components.py +5 -3
  7. zenml/constants.py +3 -0
  8. zenml/enums.py +16 -0
  9. zenml/integrations/__init__.py +1 -0
  10. zenml/integrations/azure/__init__.py +2 -2
  11. zenml/integrations/constants.py +1 -0
  12. zenml/integrations/databricks/__init__.py +52 -0
  13. zenml/integrations/databricks/flavors/__init__.py +30 -0
  14. zenml/integrations/databricks/flavors/databricks_model_deployer_flavor.py +118 -0
  15. zenml/integrations/databricks/flavors/databricks_orchestrator_flavor.py +147 -0
  16. zenml/integrations/databricks/model_deployers/__init__.py +20 -0
  17. zenml/integrations/databricks/model_deployers/databricks_model_deployer.py +249 -0
  18. zenml/integrations/databricks/orchestrators/__init__.py +20 -0
  19. zenml/integrations/databricks/orchestrators/databricks_orchestrator.py +497 -0
  20. zenml/integrations/databricks/orchestrators/databricks_orchestrator_entrypoint_config.py +97 -0
  21. zenml/integrations/databricks/services/__init__.py +19 -0
  22. zenml/integrations/databricks/services/databricks_deployment.py +407 -0
  23. zenml/integrations/databricks/utils/__init__.py +14 -0
  24. zenml/integrations/databricks/utils/databricks_utils.py +87 -0
  25. zenml/integrations/great_expectations/data_validators/ge_data_validator.py +12 -8
  26. zenml/integrations/huggingface/materializers/huggingface_datasets_materializer.py +88 -3
  27. zenml/integrations/huggingface/steps/accelerate_runner.py +1 -7
  28. zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py +1 -13
  29. zenml/integrations/kubernetes/orchestrators/manifest_utils.py +22 -4
  30. zenml/integrations/kubernetes/pod_settings.py +4 -0
  31. zenml/integrations/lightgbm/__init__.py +1 -0
  32. zenml/integrations/mlflow/__init__.py +1 -1
  33. zenml/integrations/mlflow/model_registries/mlflow_model_registry.py +6 -2
  34. zenml/integrations/mlflow/services/mlflow_deployment.py +1 -1
  35. zenml/integrations/skypilot_azure/__init__.py +1 -3
  36. zenml/integrations/skypilot_lambda/__init__.py +1 -1
  37. zenml/logging/step_logging.py +34 -35
  38. zenml/materializers/built_in_materializer.py +1 -1
  39. zenml/materializers/cloudpickle_materializer.py +1 -1
  40. zenml/model/model.py +1 -1
  41. zenml/models/v2/core/component.py +29 -0
  42. zenml/models/v2/core/server_settings.py +0 -20
  43. zenml/models/v2/misc/full_stack.py +32 -0
  44. zenml/models/v2/misc/stack_deployment.py +5 -0
  45. zenml/new/pipelines/run_utils.py +1 -1
  46. zenml/orchestrators/__init__.py +4 -0
  47. zenml/orchestrators/step_launcher.py +1 -0
  48. zenml/orchestrators/wheeled_orchestrator.py +147 -0
  49. zenml/service_connectors/service_connector_utils.py +408 -0
  50. zenml/stack_deployments/azure_stack_deployment.py +179 -0
  51. zenml/stack_deployments/gcp_stack_deployment.py +13 -4
  52. zenml/stack_deployments/stack_deployment.py +10 -0
  53. zenml/stack_deployments/utils.py +4 -0
  54. zenml/steps/base_step.py +7 -5
  55. zenml/utils/function_utils.py +1 -1
  56. zenml/utils/pipeline_docker_image_builder.py +8 -0
  57. zenml/utils/source_utils.py +4 -1
  58. zenml/zen_server/dashboard/assets/{404-DpJaNHKF.js → 404-B_YdvmwS.js} +1 -1
  59. zenml/zen_server/dashboard/assets/{@reactflow-DJfzkHO1.js → @reactflow-l_1hUr1S.js} +1 -1
  60. zenml/zen_server/dashboard/assets/{AwarenessChannel-BYDLT2xC.js → AwarenessChannel-CFg5iX4Z.js} +1 -1
  61. zenml/zen_server/dashboard/assets/{CodeSnippet-BkOuRmyq.js → CodeSnippet-Dvkx_82E.js} +1 -1
  62. zenml/zen_server/dashboard/assets/CollapsibleCard-opiuBHHc.js +1 -0
  63. zenml/zen_server/dashboard/assets/{Commands-ZvWR1BRs.js → Commands-DoN1xrEq.js} +1 -1
  64. zenml/zen_server/dashboard/assets/{CopyButton-DVwLkafa.js → CopyButton-Cr7xYEPb.js} +1 -1
  65. zenml/zen_server/dashboard/assets/{CsvVizualization-C2IiqX4I.js → CsvVizualization-Ck-nZ43m.js} +3 -3
  66. zenml/zen_server/dashboard/assets/{Error-CqX0VqW_.js → Error-kLtljEOM.js} +1 -1
  67. zenml/zen_server/dashboard/assets/{ExecutionStatus-BoLUXR9t.js → ExecutionStatus-DguLLgTK.js} +1 -1
  68. zenml/zen_server/dashboard/assets/{Helpbox-LFydyVwh.js → Helpbox-BXUMP21n.js} +1 -1
  69. zenml/zen_server/dashboard/assets/{Infobox-DnENC0sh.js → Infobox-DSt0O-dm.js} +1 -1
  70. zenml/zen_server/dashboard/assets/{InlineAvatar-CbJtYr0t.js → InlineAvatar-xsrsIGE-.js} +1 -1
  71. zenml/zen_server/dashboard/assets/Pagination-C6X-mifw.js +1 -0
  72. zenml/zen_server/dashboard/assets/{SetPassword-BYBdbQDo.js → SetPassword-BXGTWiwj.js} +1 -1
  73. zenml/zen_server/dashboard/assets/{SuccessStep-Nx743hll.js → SuccessStep-DZC60t0x.js} +1 -1
  74. zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-DF9gSzE0.js → UpdatePasswordSchemas-DGvwFWO1.js} +1 -1
  75. zenml/zen_server/dashboard/assets/{chevron-right-double-BiEMg7rd.js → chevron-right-double-CZBOf6JM.js} +1 -1
  76. zenml/zen_server/dashboard/assets/cloud-only-C_yFCAkP.js +1 -0
  77. zenml/zen_server/dashboard/assets/index-BczVOqUf.js +55 -0
  78. zenml/zen_server/dashboard/assets/index-EpMIKgrI.css +1 -0
  79. zenml/zen_server/dashboard/assets/{login-mutation-BUnVASxp.js → login-mutation-CrHrndTI.js} +1 -1
  80. zenml/zen_server/dashboard/assets/logs-D8k8BVFf.js +1 -0
  81. zenml/zen_server/dashboard/assets/{not-found-B4VnX8gK.js → not-found-DYa4pC-C.js} +1 -1
  82. zenml/zen_server/dashboard/assets/{package-CsUhPmou.js → package-B3fWP-Dh.js} +1 -1
  83. zenml/zen_server/dashboard/assets/page-1h_sD1jz.js +1 -0
  84. zenml/zen_server/dashboard/assets/{page-Sxn82W-5.js → page-1iL8aMqs.js} +1 -1
  85. zenml/zen_server/dashboard/assets/{page-DMOYZppS.js → page-2grKx_MY.js} +1 -1
  86. zenml/zen_server/dashboard/assets/page-5NCOHOsy.js +1 -0
  87. zenml/zen_server/dashboard/assets/{page-JyfeDUfu.js → page-8a4UMKXZ.js} +1 -1
  88. zenml/zen_server/dashboard/assets/{page-Bx6o0ARS.js → page-B6h3iaHJ.js} +1 -1
  89. zenml/zen_server/dashboard/assets/page-BDns21Iz.js +1 -0
  90. zenml/zen_server/dashboard/assets/{page-3efNCDeb.js → page-BhgCDInH.js} +2 -2
  91. zenml/zen_server/dashboard/assets/{page-DKlIdAe5.js → page-Bi-wtWiO.js} +2 -2
  92. zenml/zen_server/dashboard/assets/{page-7zTHbhhI.js → page-BkeAAYwp.js} +1 -1
  93. zenml/zen_server/dashboard/assets/{page-CRTJ0UuR.js → page-BkuQDIf-.js} +1 -1
  94. zenml/zen_server/dashboard/assets/page-BnaevhnB.js +1 -0
  95. zenml/zen_server/dashboard/assets/{page-BEs6jK71.js → page-Bq0YxkLV.js} +1 -1
  96. zenml/zen_server/dashboard/assets/page-Bs2F4eoD.js +2 -0
  97. zenml/zen_server/dashboard/assets/{page-CUZIGO-3.js → page-C6-UGEbH.js} +1 -1
  98. zenml/zen_server/dashboard/assets/{page-Xu8JEjSU.js → page-CCNRIt_f.js} +1 -1
  99. zenml/zen_server/dashboard/assets/{page-DvCvroOM.js → page-CHNxpz3n.js} +1 -1
  100. zenml/zen_server/dashboard/assets/{page-BpSqIf4B.js → page-DgorQFqi.js} +1 -1
  101. zenml/zen_server/dashboard/assets/page-K8ebxVIs.js +1 -0
  102. zenml/zen_server/dashboard/assets/{page-Cx67M0QT.js → page-MFQyIJd3.js} +1 -1
  103. zenml/zen_server/dashboard/assets/page-TgCF0P_U.js +1 -0
  104. zenml/zen_server/dashboard/assets/page-ZnCEe-eK.js +9 -0
  105. zenml/zen_server/dashboard/assets/{page-Dc_7KMQE.js → page-uA5prJGY.js} +1 -1
  106. zenml/zen_server/dashboard/assets/persist-D7HJNBWx.js +1 -0
  107. zenml/zen_server/dashboard/assets/plus-C8WOyCzt.js +1 -0
  108. zenml/zen_server/dashboard/assets/stack-detail-query-Cficsl6d.js +1 -0
  109. zenml/zen_server/dashboard/assets/update-server-settings-mutation-7d8xi1tS.js +1 -0
  110. zenml/zen_server/dashboard/assets/{url-DuQMeqYA.js → url-D7mAQGUM.js} +1 -1
  111. zenml/zen_server/dashboard/index.html +4 -4
  112. zenml/zen_server/dashboard_legacy/asset-manifest.json +4 -4
  113. zenml/zen_server/dashboard_legacy/index.html +1 -1
  114. zenml/zen_server/dashboard_legacy/{precache-manifest.c8c57fb0d2132b1d3c2119e776b7dfb3.js → precache-manifest.12246c7548e71e2c4438e496360de80c.js} +4 -4
  115. zenml/zen_server/dashboard_legacy/service-worker.js +1 -1
  116. zenml/zen_server/dashboard_legacy/static/js/main.3b27024b.chunk.js +2 -0
  117. zenml/zen_server/dashboard_legacy/static/js/{main.382439a7.chunk.js.map → main.3b27024b.chunk.js.map} +1 -1
  118. zenml/zen_server/deploy/helm/Chart.yaml +1 -1
  119. zenml/zen_server/deploy/helm/README.md +2 -2
  120. zenml/zen_server/rbac/utils.py +10 -2
  121. zenml/zen_server/routers/devices_endpoints.py +4 -1
  122. zenml/zen_server/routers/server_endpoints.py +29 -2
  123. zenml/zen_server/routers/service_connectors_endpoints.py +57 -0
  124. zenml/zen_server/routers/steps_endpoints.py +2 -1
  125. zenml/zen_stores/migrations/versions/0.62.0_release.py +23 -0
  126. zenml/zen_stores/migrations/versions/b4fca5241eea_migrate_onboarding_state.py +167 -0
  127. zenml/zen_stores/rest_zen_store.py +4 -0
  128. zenml/zen_stores/schemas/component_schemas.py +14 -0
  129. zenml/zen_stores/schemas/server_settings_schemas.py +23 -11
  130. zenml/zen_stores/sql_zen_store.py +151 -1
  131. {zenml_nightly-0.61.0.dev20240712.dist-info → zenml_nightly-0.62.0.dev20240726.dist-info}/METADATA +5 -5
  132. {zenml_nightly-0.61.0.dev20240712.dist-info → zenml_nightly-0.62.0.dev20240726.dist-info}/RECORD +135 -115
  133. zenml/zen_server/dashboard/assets/Pagination-DEbVUupy.js +0 -1
  134. zenml/zen_server/dashboard/assets/chevron-down-D_ZlKMqH.js +0 -1
  135. zenml/zen_server/dashboard/assets/cloud-only-DVbIeckv.js +0 -1
  136. zenml/zen_server/dashboard/assets/index-C_CrU4vI.js +0 -1
  137. zenml/zen_server/dashboard/assets/index-DK1ynKjA.js +0 -55
  138. zenml/zen_server/dashboard/assets/index-inApY3KQ.css +0 -1
  139. zenml/zen_server/dashboard/assets/page-C43QGHTt.js +0 -9
  140. zenml/zen_server/dashboard/assets/page-CR0OG7ss.js +0 -1
  141. zenml/zen_server/dashboard/assets/page-CaopxiU1.js +0 -1
  142. zenml/zen_server/dashboard/assets/page-D7Z399xy.js +0 -1
  143. zenml/zen_server/dashboard/assets/page-D93kd7Xj.js +0 -1
  144. zenml/zen_server/dashboard/assets/page-DMsSn3dv.js +0 -2
  145. zenml/zen_server/dashboard/assets/page-Hus2pr9T.js +0 -1
  146. zenml/zen_server/dashboard/assets/page-TKXERe16.js +0 -1
  147. zenml/zen_server/dashboard/assets/plus-DOeLmm7C.js +0 -1
  148. zenml/zen_server/dashboard/assets/update-server-settings-mutation-CR8e3Sir.js +0 -1
  149. zenml/zen_server/dashboard_legacy/static/js/main.382439a7.chunk.js +0 -2
  150. {zenml_nightly-0.61.0.dev20240712.dist-info → zenml_nightly-0.62.0.dev20240726.dist-info}/LICENSE +0 -0
  151. {zenml_nightly-0.61.0.dev20240712.dist-info → zenml_nightly-0.62.0.dev20240726.dist-info}/WHEEL +0 -0
  152. {zenml_nightly-0.61.0.dev20240712.dist-info → zenml_nightly-0.62.0.dev20240726.dist-info}/entry_points.txt +0 -0
@@ -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 trying to run from a Jupyter notebook.
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
- pod_metadata = k8s_client.V1ObjectMeta(
155
- name=pod_name,
156
- labels={
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
@@ -22,6 +22,7 @@ class LightGBMIntegration(Integration):
22
22
 
23
23
  NAME = LIGHTGBM
24
24
  REQUIREMENTS = ["lightgbm>=1.0.0"]
25
+ APT_PACKAGES = ["libgomp1"]
25
26
 
26
27
  @classmethod
27
28
  def activate(cls) -> None:
@@ -33,7 +33,7 @@ class MlflowIntegration(Integration):
33
33
  NAME = MLFLOW
34
34
 
35
35
  REQUIREMENTS = [
36
- "mlflow>=2.1.1,<=2.14.1",
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
@@ -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 stage and not mlflow_model_version.current_stage == str(stage):
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) == pd.DataFrame:
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")},
@@ -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]~=0.6.0"]
34
+ REQUIREMENTS = ["skypilot-nightly[azure]==1.0.0.dev20240716"]
37
35
  APT_PACKAGES = ["openssh-client", "rsync"]
38
36
 
39
37
  @classmethod
@@ -31,7 +31,7 @@ class SkypilotLambdaIntegration(Integration):
31
31
  """Definition of Skypilot Lambda Integration for ZenML."""
32
32
 
33
33
  NAME = SKYPILOT_LAMBDA
34
- REQUIREMENTS = ["skypilot[lambda]<=0.6.0"]
34
+ REQUIREMENTS = ["skypilot[lambda]~=0.6.0"]
35
35
 
36
36
  @classmethod
37
37
  def flavors(cls) -> List[Type[Flavor]]:
@@ -240,8 +240,6 @@ class StepLogsStorage:
240
240
 
241
241
  # Immutable filesystems state
242
242
  self.last_merge_time = time.time()
243
- self.log_files_not_merged: List[str] = []
244
- self.next_merged_file_name: str = self._get_timestamped_filename()
245
243
 
246
244
  @property
247
245
  def artifact_store(self) -> "BaseArtifactStore":
@@ -279,13 +277,16 @@ class StepLogsStorage:
279
277
  or time.time() - self.last_save_time >= self.time_interval
280
278
  )
281
279
 
282
- def _get_timestamped_filename(self) -> str:
280
+ def _get_timestamped_filename(self, suffix: str = "") -> str:
283
281
  """Returns a timestamped filename.
284
282
 
283
+ Args:
284
+ suffix: optional suffix for the file name
285
+
285
286
  Returns:
286
287
  The timestamped filename.
287
288
  """
288
- return f"{time.time()}{LOGS_EXTENSION}"
289
+ return f"{time.time()}{suffix}{LOGS_EXTENSION}"
289
290
 
290
291
  def save_to_file(self, force: bool = False) -> None:
291
292
  """Method to save the buffer to the given URI.
@@ -302,12 +303,7 @@ class StepLogsStorage:
302
303
  try:
303
304
  if self.buffer:
304
305
  if self.artifact_store.config.IS_IMMUTABLE_FILESYSTEM:
305
- if not self.log_files_not_merged:
306
- self.next_merged_file_name = (
307
- self._get_timestamped_filename()
308
- )
309
306
  _logs_uri = self._get_timestamped_filename()
310
- self.log_files_not_merged.append(_logs_uri)
311
307
  with self.artifact_store.open(
312
308
  os.path.join(
313
309
  self.logs_uri,
@@ -346,42 +342,40 @@ class StepLogsStorage:
346
342
  and time.time() - self.last_merge_time > self.merge_files_interval
347
343
  ):
348
344
  try:
349
- self.merge_log_files(
350
- self.next_merged_file_name, self.log_files_not_merged
351
- )
345
+ self.merge_log_files()
352
346
  except (OSError, IOError) as e:
353
347
  logger.error(f"Error while trying to roll up logs: {e}")
354
- else:
355
- self.log_files_not_merged = []
356
348
  finally:
357
349
  self.last_merge_time = time.time()
358
350
 
359
- def merge_log_files(
360
- self,
361
- file_name: Optional[str] = None,
362
- files: Optional[List[str]] = None,
363
- ) -> None:
351
+ def merge_log_files(self, merge_all_files: bool = False) -> None:
364
352
  """Merges all log files into one in the given URI.
365
353
 
366
354
  Called on the logging context exit.
367
355
 
368
356
  Args:
369
- file_name: The name of the merged log file.
370
- files: The list of log files to merge.
357
+ merge_all_files: whether to merge all files or only raw files
371
358
  """
372
359
  if self.artifact_store.config.IS_IMMUTABLE_FILESYSTEM:
373
- files_ = files or self.artifact_store.listdir(self.logs_uri)
374
- file_name_ = file_name or self._get_timestamped_filename()
360
+ merged_file_suffix = "_merged"
361
+ files_ = self.artifact_store.listdir(self.logs_uri)
362
+ if not merge_all_files:
363
+ # already merged files will not be merged again
364
+ files_ = [f for f in files_ if merged_file_suffix not in f]
365
+ file_name_ = self._get_timestamped_filename(
366
+ suffix=merged_file_suffix
367
+ )
375
368
  if len(files_) > 1:
376
369
  files_.sort()
377
370
  logger.debug("Log files count: %s", len(files_))
378
371
 
379
- try:
380
- # dump all logs to a local file first
381
- with self.artifact_store.open(
382
- os.path.join(self.logs_uri, file_name_), "w"
383
- ) as merged_file:
384
- for file in files_:
372
+ missing_files = set()
373
+ # dump all logs to a local file first
374
+ with self.artifact_store.open(
375
+ os.path.join(self.logs_uri, file_name_), "w"
376
+ ) as merged_file:
377
+ for file in files_:
378
+ try:
385
379
  merged_file.write(
386
380
  str(
387
381
  _load_file_from_artifact_store(
@@ -391,11 +385,12 @@ class StepLogsStorage:
391
385
  )
392
386
  )
393
387
  )
394
- except Exception as e:
395
- logger.warning(f"Failed to merge log files. {e}")
396
- else:
397
- # clean up left over files
398
- for file in files_:
388
+ except DoesNotExistException:
389
+ missing_files.add(file)
390
+
391
+ # clean up left over files
392
+ for file in files_:
393
+ if file not in missing_files:
399
394
  self.artifact_store.remove(
400
395
  os.path.join(self.logs_uri, str(file))
401
396
  )
@@ -452,7 +447,6 @@ class StepLogsStorageContext:
452
447
  Restores the `write` method of both stderr and stdout.
453
448
  """
454
449
  self.storage.save_to_file(force=True)
455
- self.storage.merge_log_files()
456
450
 
457
451
  setattr(sys.stdout, "write", self.stdout_write)
458
452
  setattr(sys.stdout, "flush", self.stdout_flush)
@@ -462,6 +456,11 @@ class StepLogsStorageContext:
462
456
 
463
457
  redirected.set(False)
464
458
 
459
+ try:
460
+ self.storage.merge_log_files(merge_all_files=True)
461
+ except (OSError, IOError) as e:
462
+ logger.warning(f"Step logs roll-up failed: {e}")
463
+
465
464
  def _wrap_write(self, method: Callable[..., Any]) -> Callable[..., Any]:
466
465
  """Wrapper function that utilizes the storage object to store logs.
467
466
 
@@ -80,7 +80,7 @@ class BuiltInMaterializer(BaseMaterializer):
80
80
  The data read.
81
81
  """
82
82
  contents = yaml_utils.read_json(self.data_path)
83
- if type(contents) != data_type:
83
+ if type(contents) is not data_type:
84
84
  # TODO [ENG-142]: Raise error or try to coerce
85
85
  logger.debug(
86
86
  f"Contents {contents} was type {type(contents)} but expected "
@@ -94,7 +94,7 @@ class CloudpickleMaterializer(BaseMaterializer):
94
94
  """
95
95
  # Log a warning if this materializer was not explicitly specified for
96
96
  # the given data type.
97
- if type(self) == CloudpickleMaterializer:
97
+ if type(self) is CloudpickleMaterializer:
98
98
  logger.warning(
99
99
  f"No materializer is registered for type `{type(data)}`, so "
100
100
  "the default Pickle materializer was used. Pickle is not "
zenml/model/model.py CHANGED
@@ -518,7 +518,7 @@ class Model(BaseModel):
518
518
  and not suppress_class_validation_warnings
519
519
  ):
520
520
  logger.info(
521
- f"`version` `{version}` matches one of the possible "
521
+ f"Version `{version}` matches one of the possible "
522
522
  "`ModelStages` and will be fetched using stage."
523
523
  )
524
524
  if str(version).isnumeric() and not suppress_class_validation_warnings:
@@ -191,6 +191,17 @@ class ComponentResponseBody(WorkspaceScopedResponseBody):
191
191
  title="The flavor of the stack component.",
192
192
  max_length=STR_FIELD_MAX_LENGTH,
193
193
  )
194
+ integration: Optional[str] = Field(
195
+ default=None,
196
+ title="The name of the integration that the component's flavor "
197
+ "belongs to.",
198
+ max_length=STR_FIELD_MAX_LENGTH,
199
+ )
200
+ logo_url: Optional[str] = Field(
201
+ default=None,
202
+ title="Optionally, a url pointing to a png,"
203
+ "svg or jpg can be attached.",
204
+ )
194
205
 
195
206
 
196
207
  class ComponentResponseMetadata(WorkspaceScopedResponseMetadata):
@@ -285,6 +296,24 @@ class ComponentResponse(
285
296
  """
286
297
  return self.get_body().flavor
287
298
 
299
+ @property
300
+ def integration(self) -> Optional[str]:
301
+ """The `integration` property.
302
+
303
+ Returns:
304
+ the value of the property.
305
+ """
306
+ return self.get_body().integration
307
+
308
+ @property
309
+ def logo_url(self) -> Optional[str]:
310
+ """The `logo_url` property.
311
+
312
+ Returns:
313
+ the value of the property.
314
+ """
315
+ return self.get_body().logo_url
316
+
288
317
  @property
289
318
  def configuration(self) -> Dict[str, Any]:
290
319
  """The `configuration` property.
@@ -15,8 +15,6 @@
15
15
 
16
16
  from datetime import datetime
17
17
  from typing import (
18
- Any,
19
- Dict,
20
18
  Optional,
21
19
  )
22
20
  from uuid import UUID
@@ -57,10 +55,6 @@ class ServerSettingsUpdate(BaseZenModel):
57
55
  default=None,
58
56
  title="Whether to display notifications about ZenML updates in the dashboard.",
59
57
  )
60
- onboarding_state: Optional[Dict[str, Any]] = Field(
61
- default=None,
62
- title="The server's onboarding state.",
63
- )
64
58
 
65
59
 
66
60
  # ------------------ Response Model ------------------
@@ -96,11 +90,6 @@ class ServerSettingsResponseBody(BaseResponseBody):
96
90
  class ServerSettingsResponseMetadata(BaseResponseMetadata):
97
91
  """Response metadata for server settings."""
98
92
 
99
- onboarding_state: Dict[str, Any] = Field(
100
- default={},
101
- title="The server's onboarding state.",
102
- )
103
-
104
93
 
105
94
  class ServerSettingsResponseResources(BaseResponseResources):
106
95
  """Response resources for server settings."""
@@ -199,15 +188,6 @@ class ServerSettingsResponse(
199
188
  """
200
189
  return self.get_body().updated
201
190
 
202
- @property
203
- def onboarding_state(self) -> Dict[str, Any]:
204
- """The `onboarding_state` property.
205
-
206
- Returns:
207
- the value of the property.
208
- """
209
- return self.get_metadata().onboarding_state
210
-
211
191
 
212
192
  # ------------------ Filter Model ------------------
213
193
 
@@ -21,6 +21,7 @@ from pydantic import BaseModel, Field, model_validator
21
21
  from zenml.constants import STR_FIELD_MAX_LENGTH
22
22
  from zenml.enums import StackComponentType
23
23
  from zenml.models.v2.base.base import BaseRequest
24
+ from zenml.models.v2.core.component import ComponentResponse
24
25
 
25
26
 
26
27
  class ServiceConnectorInfo(BaseModel):
@@ -95,3 +96,34 @@ class FullStackRequest(BaseRequest):
95
96
  "the position in the list of service connectors."
96
97
  )
97
98
  return self
99
+
100
+
101
+ class ResourcesInfo(BaseModel):
102
+ """Information about the resources needed for CLI and UI."""
103
+
104
+ flavor: str
105
+ flavor_display_name: str
106
+ required_configuration: Dict[str, str] = {}
107
+ use_resource_value_as_fixed_config: bool = False
108
+
109
+ accessible_by_service_connector: List[str]
110
+ connected_through_service_connector: List[ComponentResponse]
111
+
112
+ @model_validator(mode="after")
113
+ def _validate_resource_info(self) -> "ResourcesInfo":
114
+ if (
115
+ self.use_resource_value_as_fixed_config
116
+ and len(self.required_configuration) > 1
117
+ ):
118
+ raise ValueError(
119
+ "Cannot use resource value as fixed config if more than one required configuration key is provided."
120
+ )
121
+ return self
122
+
123
+
124
+ class ServiceConnectorResourcesInfo(BaseModel):
125
+ """Information about the service connector resources needed for CLI and UI."""
126
+
127
+ connector_type: str
128
+
129
+ components_resources_info: Dict[StackComponentType, List[ResourcesInfo]]
@@ -55,6 +55,11 @@ class StackDeploymentInfo(BaseModel):
55
55
  description="The locations where the stack can be deployed, as a "
56
56
  "dictionary mapping location names to descriptions.",
57
57
  )
58
+ skypilot_default_regions: Dict[str, str] = Field(
59
+ title="The locations where the Skypilot clusters can be deployed by default.",
60
+ description="The locations where the Skypilot clusters can be deployed by default, as a "
61
+ "dictionary mapping location names to descriptions.",
62
+ )
58
63
 
59
64
 
60
65
  class StackDeploymentConfig(BaseModel):
@@ -250,7 +250,7 @@ def _validate_new_version_requests(
250
250
  if not is_cloud_model:
251
251
  logger.info(
252
252
  "Models can be viewed in the dashboard using ZenML Pro. Sign up "
253
- "for a free trial at https://www.zenml.io/cloud/"
253
+ "for a free trial at https://www.zenml.io/pro/"
254
254
  )
255
255
 
256
256
 
@@ -30,6 +30,9 @@ from zenml.orchestrators.base_orchestrator import (
30
30
  from zenml.orchestrators.containerized_orchestrator import (
31
31
  ContainerizedOrchestrator,
32
32
  )
33
+ from zenml.orchestrators.wheeled_orchestrator import (
34
+ WheeledOrchestrator,
35
+ )
33
36
  from zenml.orchestrators.local.local_orchestrator import (
34
37
  LocalOrchestrator,
35
38
  LocalOrchestratorFlavor,
@@ -44,6 +47,7 @@ __all__ = [
44
47
  "BaseOrchestratorConfig",
45
48
  "BaseOrchestratorFlavor",
46
49
  "ContainerizedOrchestrator",
50
+ "WheeledOrchestrator",
47
51
  "LocalOrchestrator",
48
52
  "LocalOrchestratorFlavor",
49
53
  "LocalDockerOrchestrator",
@@ -291,6 +291,7 @@ class StepLauncher:
291
291
  logger.error(
292
292
  f"Failed to run step `{self._step_name}` after {max_retries} retries. Exiting."
293
293
  )
294
+ logger.exception(e)
294
295
  publish_utils.publish_failed_step_run(
295
296
  step_run_response.id
296
297
  )