zenml-nightly 0.72.0.dev20250121__py3-none-any.whl → 0.73.0.dev20250123__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 (157) hide show
  1. zenml/VERSION +1 -1
  2. zenml/cli/service_connectors.py +8 -4
  3. zenml/cli/stack.py +2 -2
  4. zenml/config/pipeline_configurations.py +2 -2
  5. zenml/config/server_config.py +20 -0
  6. zenml/event_hub/base_event_hub.py +2 -2
  7. zenml/integrations/airflow/orchestrators/airflow_orchestrator.py +4 -2
  8. zenml/integrations/gcp/__init__.py +3 -0
  9. zenml/integrations/gcp/experiment_trackers/__init__.py +18 -0
  10. zenml/integrations/gcp/experiment_trackers/vertex_experiment_tracker.py +214 -0
  11. zenml/integrations/gcp/flavors/__init__.py +6 -0
  12. zenml/integrations/gcp/flavors/vertex_experiment_tracker_flavor.py +199 -0
  13. zenml/integrations/kubernetes/orchestrators/kube_utils.py +2 -2
  14. zenml/integrations/mlflow/experiment_trackers/mlflow_experiment_tracker.py +0 -1
  15. zenml/integrations/whylogs/data_validators/whylogs_data_validator.py +3 -1
  16. zenml/models/v2/core/api_key.py +2 -2
  17. zenml/orchestrators/publish_utils.py +4 -4
  18. zenml/orchestrators/step_launcher.py +3 -3
  19. zenml/orchestrators/step_run_utils.py +2 -2
  20. zenml/pipelines/run_utils.py +2 -2
  21. zenml/service_connectors/service_connector.py +2 -2
  22. zenml/stack/stack.py +3 -3
  23. zenml/stack_deployments/stack_deployment.py +5 -0
  24. zenml/utils/git_utils.py +1 -1
  25. zenml/utils/string_utils.py +2 -2
  26. zenml/zen_server/auth.py +13 -6
  27. zenml/zen_server/dashboard/assets/{404-Dfq64Boz.js → 404-c8OuXDAT.js} +1 -1
  28. zenml/zen_server/dashboard/assets/{@reactflow-BUNIMFeC.js → @reactflow-6JPoencd.js} +1 -1
  29. zenml/zen_server/dashboard/assets/{AlertDialogDropdownItem-B73Vs10T.js → AlertDialogDropdownItem-8yPFDxEI.js} +1 -1
  30. zenml/zen_server/dashboard/assets/{CodeSnippet-DIJRT2NT.js → CodeSnippet-Qh1ae_DJ.js} +1 -1
  31. zenml/zen_server/dashboard/assets/{CollapsibleCard-BzUHGZOU.js → CollapsibleCard-TiI4lId1.js} +1 -1
  32. zenml/zen_server/dashboard/assets/{Commands-BEGyld4c.js → Commands-BcR2Arie.js} +1 -1
  33. zenml/zen_server/dashboard/assets/{ComponentBadge-xyKiek1s.js → ComponentBadge-BqQNUZgb.js} +1 -1
  34. zenml/zen_server/dashboard/assets/{CopyButton-DhW-mapu.js → CopyButton-DCiXO3JC.js} +1 -1
  35. zenml/zen_server/dashboard/assets/{CsvVizualization-D8oazBiE.js → CsvVizualization-O9cVIaL8.js} +1 -1
  36. zenml/zen_server/dashboard/assets/{DeleteAlertDialog-WkSIIgfy.js → DeleteAlertDialog-DrPjHtXX.js} +1 -1
  37. zenml/zen_server/dashboard/assets/{DialogItem-Bgroeg29.js → DialogItem-BYG7d_M2.js} +1 -1
  38. zenml/zen_server/dashboard/assets/{Error-CY5tlu17.js → Error-C1zbWr19.js} +1 -1
  39. zenml/zen_server/dashboard/assets/{ExecutionStatus-G8mjIaeA.js → ExecutionStatus-Ct9srgHC.js} +1 -1
  40. zenml/zen_server/dashboard/assets/{Helpbox-Bb1ed--O.js → Helpbox-Bm_1Zx9f.js} +1 -1
  41. zenml/zen_server/dashboard/assets/{Infobox-Da6-76M2.js → Infobox-OQdkCLSP.js} +1 -1
  42. zenml/zen_server/dashboard/assets/{InlineAvatar-DqnZaBNq.js → InlineAvatar-CQNjKoEQ.js} +1 -1
  43. zenml/zen_server/dashboard/assets/{NestedCollapsible-aK5ojKoF.js → NestedCollapsible-DDgd2SGb.js} +1 -1
  44. zenml/zen_server/dashboard/assets/Partials-MD3e95Dk.js +1 -0
  45. zenml/zen_server/dashboard/assets/{ProBadge-B4tRUYve.js → ProBadge-D784iVNC.js} +1 -1
  46. zenml/zen_server/dashboard/assets/{ProCta-CZuP29Qz.js → ProCta-W2PEvNow.js} +1 -1
  47. zenml/zen_server/dashboard/assets/{ProviderIcon-Bd7GUQ1_.js → ProviderIcon-DfDUOeAy.js} +1 -1
  48. zenml/zen_server/dashboard/assets/{ProviderRadio-mstdqzsS.js → ProviderRadio-B81Elxrc.js} +1 -1
  49. zenml/zen_server/dashboard/assets/{RunSelector-CsruSB4i.js → RunSelector-DOXgdry5.js} +1 -1
  50. zenml/zen_server/dashboard/assets/{RunsBody-DxxtWVYz.js → RunsBody-Bnx2fxub.js} +1 -1
  51. zenml/zen_server/dashboard/assets/SearchField-Yjv-KRW4.js +1 -0
  52. zenml/zen_server/dashboard/assets/{SecretTooltip-CLzJIYW_.js → SecretTooltip-EKpMlG2f.js} +1 -1
  53. zenml/zen_server/dashboard/assets/{SetPassword-Yn50ooBC.js → SetPassword-CDLy57PZ.js} +1 -1
  54. zenml/zen_server/dashboard/assets/StackList-DKQaLDo4.js +1 -0
  55. zenml/zen_server/dashboard/assets/{Tabs-CNv-eTYM.js → Tabs-B5E-o_h6.js} +1 -1
  56. zenml/zen_server/dashboard/assets/{Tick-jEIevzVf.js → Tick-DSYBiuXU.js} +1 -1
  57. zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-C16GW-kX.js → UpdatePasswordSchemas-HBNOeyoP.js} +1 -1
  58. zenml/zen_server/dashboard/assets/{UsageReason-Bf2tzhv1.js → UsageReason-DXtPS5nE.js} +1 -1
  59. zenml/zen_server/dashboard/assets/{WizardFooter-D6i-AP1K.js → WizardFooter-_1VSMZ_c.js} +1 -1
  60. zenml/zen_server/dashboard/assets/{all-pipeline-runs-query-DUti43aF.js → all-pipeline-runs-query-D0qDLdKB.js} +1 -1
  61. zenml/zen_server/dashboard/assets/{create-stack-Ch2WPs9U.js → create-stack-7JzgAYAm.js} +1 -1
  62. zenml/zen_server/dashboard/assets/{delete-run-Byf9hTjA.js → delete-run-CUdtYFLl.js} +1 -1
  63. zenml/zen_server/dashboard/assets/{form-schemas-BZqKBPBF.js → form-schemas-B6PCV3Y4.js} +1 -1
  64. zenml/zen_server/dashboard/assets/index-B6U0OkEN.css +1 -0
  65. zenml/zen_server/dashboard/assets/{index-CyBKZcpO.js → index-CJ5IfeAl.js} +1 -1
  66. zenml/zen_server/dashboard/assets/{index-CtdYkjUi.js → index-Ceyzb1yI.js} +1 -1
  67. zenml/zen_server/dashboard/assets/{index-CE0aQlv8.js → index-CxO6541P.js} +3 -3
  68. zenml/zen_server/dashboard/assets/{index-v6gQjDEo.js → index-D4yoZ_gH.js} +1 -1
  69. zenml/zen_server/dashboard/assets/{login-mutation-DNDVp_2H.js → login-mutation-BaeJ7MAg.js} +1 -1
  70. zenml/zen_server/dashboard/assets/{not-found-Bmup4ctE.js → not-found-MGptrNBk.js} +1 -1
  71. zenml/zen_server/dashboard/assets/{page-DGlm1RVc.js → page-Aeu3v0MQ.js} +1 -1
  72. zenml/zen_server/dashboard/assets/{page-CltCNL0T.js → page-BCgEdmhP.js} +1 -1
  73. zenml/zen_server/dashboard/assets/{page-Hn8q9iJZ.js → page-BKwwfTNy.js} +1 -1
  74. zenml/zen_server/dashboard/assets/{page-BNxYrN0q.js → page-BUjw8Tp1.js} +1 -1
  75. zenml/zen_server/dashboard/assets/{page-BYJfqgLN.js → page-BXgXP-Qj.js} +1 -1
  76. zenml/zen_server/dashboard/assets/{page-DN4BVIOL.js → page-BXrtxEbw.js} +1 -1
  77. zenml/zen_server/dashboard/assets/{page-CHRn1fQm.js → page-BaUDR9Ri.js} +1 -1
  78. zenml/zen_server/dashboard/assets/{page-DlIi5ThM.js → page-BbljjC-k.js} +1 -1
  79. zenml/zen_server/dashboard/assets/{page-BrmJp1Wt.js → page-BhOXn-s9.js} +1 -1
  80. zenml/zen_server/dashboard/assets/{page-Cc8ZEuj4.js → page-C37IDa-Q.js} +1 -1
  81. zenml/zen_server/dashboard/assets/{page-Dif8CWyZ.js → page-C4JpDeUM.js} +1 -1
  82. zenml/zen_server/dashboard/assets/{page-BC27C_OI.js → page-CB2_GdBA.js} +1 -1
  83. zenml/zen_server/dashboard/assets/{page-B5Sr8pib.js → page-CBiT2Ox9.js} +1 -1
  84. zenml/zen_server/dashboard/assets/{page-IhckKFnD.js → page-CXPc-HN1.js} +1 -1
  85. zenml/zen_server/dashboard/assets/{page-Dth9X1Ih.js → page-CbwI6emp.js} +1 -1
  86. zenml/zen_server/dashboard/assets/{page-DweqqCkF.js → page-CeNL9JWi.js} +1 -1
  87. zenml/zen_server/dashboard/assets/{page-LyZ_l8vR.js → page-CkPwPmLZ.js} +1 -1
  88. zenml/zen_server/dashboard/assets/{page-C70wZtV2.js → page-CmJU3Gqo.js} +1 -1
  89. zenml/zen_server/dashboard/assets/{page-D9Oh05fl.js → page-CoFVtzhG.js} +1 -1
  90. zenml/zen_server/dashboard/assets/{page-PamGpk0j.js → page-D-KPzeQb.js} +1 -1
  91. zenml/zen_server/dashboard/assets/{page-DoW7YxTu.js → page-DKQ3wZgr.js} +1 -1
  92. zenml/zen_server/dashboard/assets/page-DWWhxCoF.js +1 -0
  93. zenml/zen_server/dashboard/assets/{page-CmlYj7Nl.js → page-DbW8MfQ4.js} +1 -1
  94. zenml/zen_server/dashboard/assets/{page-CWr96ZKN.js → page-Dv5lN2w7.js} +1 -1
  95. zenml/zen_server/dashboard/assets/{page-ANYGfEUL.js → page-Dvbq1BoF.js} +1 -1
  96. zenml/zen_server/dashboard/assets/{page-D6Ev5P8V.js → page-DyAuja95.js} +1 -1
  97. zenml/zen_server/dashboard/assets/{page-DyOJ_pq3.js → page-DzrdL2v1.js} +1 -1
  98. zenml/zen_server/dashboard/assets/{page-CXAbSyp9.js → page-I2B4Ocv8.js} +1 -1
  99. zenml/zen_server/dashboard/assets/page-OdjGauvw.js +2 -0
  100. zenml/zen_server/dashboard/assets/{page-CaeI9ptC.js → page-Ox-eC1ik.js} +1 -1
  101. zenml/zen_server/dashboard/assets/{page-B_0XkV48.js → page-khp8QJ6b.js} +1 -1
  102. zenml/zen_server/dashboard/assets/{page--XLMzHrn.js → page-yNh6PQKt.js} +1 -1
  103. zenml/zen_server/dashboard/assets/{persist-vP0-Xl4f.js → persist-DBTFy--v.js} +1 -1
  104. zenml/zen_server/dashboard/assets/{persist-DeXRG61d.js → persist-K7AY0ju4.js} +1 -1
  105. zenml/zen_server/dashboard/assets/{service-DH_oUqQj.js → service-BvOYLH5b.js} +1 -1
  106. zenml/zen_server/dashboard/assets/{sharedSchema-Bw1_Wa7l.js → sharedSchema-xJDsJNgJ.js} +1 -1
  107. zenml/zen_server/dashboard/assets/{stack-detail-query-B_0R_fd6.js → stack-detail-query-DMJoxwgv.js} +1 -1
  108. zenml/zen_server/dashboard/assets/{update-server-settings-mutation-D9qYhfaN.js → update-server-settings-mutation-ATZDNNZk.js} +1 -1
  109. zenml/zen_server/dashboard/assets/{url-Dh93fvh0.js → url-BWJXzuI4.js} +1 -1
  110. zenml/zen_server/dashboard/index.html +4 -4
  111. zenml/zen_server/deploy/helm/Chart.yaml +1 -1
  112. zenml/zen_server/deploy/helm/README.md +2 -2
  113. zenml/zen_server/deploy/helm/templates/server-db-job.yaml +5 -3
  114. zenml/zen_server/deploy/helm/values.yaml +4 -0
  115. zenml/zen_server/routers/devices_endpoints.py +4 -2
  116. zenml/zen_stores/migrations/versions/0.73.0_release.py +23 -0
  117. zenml/zen_stores/migrations/versions/25155145c545_separate_actions_and_triggers.py +2 -2
  118. zenml/zen_stores/migrations/versions/46506f72f0ed_add_server_settings.py +2 -2
  119. zenml/zen_stores/migrations/versions/5994f9ad0489_introduce_role_permissions.py +6 -6
  120. zenml/zen_stores/migrations/versions/7500f434b71c_remove_shared_columns.py +2 -2
  121. zenml/zen_stores/migrations/versions/a91762e6be36_artifact_version_table.py +3 -3
  122. zenml/zen_stores/schemas/action_schemas.py +2 -2
  123. zenml/zen_stores/schemas/api_key_schemas.py +4 -4
  124. zenml/zen_stores/schemas/artifact_schemas.py +3 -3
  125. zenml/zen_stores/schemas/base_schemas.py +7 -3
  126. zenml/zen_stores/schemas/code_repository_schemas.py +2 -2
  127. zenml/zen_stores/schemas/component_schemas.py +2 -2
  128. zenml/zen_stores/schemas/device_schemas.py +4 -4
  129. zenml/zen_stores/schemas/event_source_schemas.py +2 -2
  130. zenml/zen_stores/schemas/flavor_schemas.py +2 -2
  131. zenml/zen_stores/schemas/model_schemas.py +3 -3
  132. zenml/zen_stores/schemas/pipeline_run_schemas.py +3 -3
  133. zenml/zen_stores/schemas/pipeline_schemas.py +2 -2
  134. zenml/zen_stores/schemas/run_template_schemas.py +2 -2
  135. zenml/zen_stores/schemas/schedule_schema.py +2 -2
  136. zenml/zen_stores/schemas/secret_schemas.py +2 -2
  137. zenml/zen_stores/schemas/server_settings_schemas.py +9 -5
  138. zenml/zen_stores/schemas/service_connector_schemas.py +2 -2
  139. zenml/zen_stores/schemas/service_schemas.py +2 -2
  140. zenml/zen_stores/schemas/stack_schemas.py +2 -2
  141. zenml/zen_stores/schemas/step_run_schemas.py +2 -2
  142. zenml/zen_stores/schemas/tag_schemas.py +2 -2
  143. zenml/zen_stores/schemas/trigger_schemas.py +2 -2
  144. zenml/zen_stores/schemas/user_schemas.py +3 -3
  145. zenml/zen_stores/schemas/workspace_schemas.py +2 -2
  146. zenml/zen_stores/sql_zen_store.py +10 -1
  147. {zenml_nightly-0.72.0.dev20250121.dist-info → zenml_nightly-0.73.0.dev20250123.dist-info}/METADATA +2 -2
  148. {zenml_nightly-0.72.0.dev20250121.dist-info → zenml_nightly-0.73.0.dev20250123.dist-info}/RECORD +151 -147
  149. zenml/zen_server/dashboard/assets/Partials-CqZp5NMX.js +0 -1
  150. zenml/zen_server/dashboard/assets/SearchField-D6tPxyqw.js +0 -1
  151. zenml/zen_server/dashboard/assets/StackList-U537qoYd.js +0 -1
  152. zenml/zen_server/dashboard/assets/index-DXvT1_Um.css +0 -1
  153. zenml/zen_server/dashboard/assets/page-C2nU3Gxn.js +0 -1
  154. zenml/zen_server/dashboard/assets/page-PxOWfKgF.js +0 -2
  155. {zenml_nightly-0.72.0.dev20250121.dist-info → zenml_nightly-0.73.0.dev20250123.dist-info}/LICENSE +0 -0
  156. {zenml_nightly-0.72.0.dev20250121.dist-info → zenml_nightly-0.73.0.dev20250123.dist-info}/WHEEL +0 -0
  157. {zenml_nightly-0.72.0.dev20250121.dist-info → zenml_nightly-0.73.0.dev20250123.dist-info}/entry_points.txt +0 -0
zenml/VERSION CHANGED
@@ -1 +1 @@
1
- 0.72.0.dev20250121
1
+ 0.73.0.dev20250123
@@ -13,7 +13,7 @@
13
13
  # permissions and limitations under the License.
14
14
  """Service connector CLI commands."""
15
15
 
16
- from datetime import datetime
16
+ from datetime import datetime, timezone
17
17
  from typing import Any, Dict, List, Optional, Union, cast
18
18
  from uuid import UUID
19
19
 
@@ -291,7 +291,9 @@ def prompt_expires_at(
291
291
  while True:
292
292
  default_str = ""
293
293
  if default is not None:
294
- seconds = int((default - datetime.utcnow()).total_seconds())
294
+ seconds = int(
295
+ (default - datetime.now(timezone.utc)).total_seconds()
296
+ )
295
297
  default_str = (
296
298
  f" [{str(default)} i.e. in "
297
299
  f"{seconds_to_human_readable(seconds)}]"
@@ -307,14 +309,16 @@ def prompt_expires_at(
307
309
 
308
310
  assert expires_at is not None
309
311
  assert isinstance(expires_at, datetime)
310
- if expires_at < datetime.utcnow():
312
+ if expires_at < datetime.now(timezone.utc):
311
313
  cli_utils.warning(
312
314
  "The expiration time must be in the future. Please enter a "
313
315
  "later date and time."
314
316
  )
315
317
  continue
316
318
 
317
- seconds = int((expires_at - datetime.utcnow()).total_seconds())
319
+ seconds = int(
320
+ (expires_at - datetime.now(timezone.utc)).total_seconds()
321
+ )
318
322
 
319
323
  confirm = click.confirm(
320
324
  f"Credentials will be valid until {str(expires_at)} UTC (i.e. "
zenml/cli/stack.py CHANGED
@@ -17,7 +17,7 @@ import getpass
17
17
  import re
18
18
  import time
19
19
  import webbrowser
20
- from datetime import datetime
20
+ from datetime import datetime, timezone
21
21
  from typing import (
22
22
  TYPE_CHECKING,
23
23
  Any,
@@ -1575,7 +1575,7 @@ def deploy(
1575
1575
  ):
1576
1576
  raise click.Abort()
1577
1577
 
1578
- date_start = datetime.utcnow()
1578
+ date_start = datetime.now(timezone.utc)
1579
1579
 
1580
1580
  webbrowser.open(deployment_config.deployment_url)
1581
1581
  console.print(
@@ -13,7 +13,7 @@
13
13
  # permissions and limitations under the License.
14
14
  """Pipeline configuration classes."""
15
15
 
16
- from datetime import datetime
16
+ from datetime import datetime, timezone
17
17
  from typing import TYPE_CHECKING, Any, Dict, List, Optional
18
18
 
19
19
  from pydantic import SerializeAsAny, field_validator
@@ -61,7 +61,7 @@ class PipelineConfigurationUpdate(StrictBaseModel):
61
61
  The full substitutions dict including date and time.
62
62
  """
63
63
  if start_time is None:
64
- start_time = datetime.utcnow()
64
+ start_time = datetime.now(timezone.utc)
65
65
  ret = self.substitutions.copy()
66
66
  ret.setdefault("date", start_time.strftime("%Y_%m_%d"))
67
67
  ret.setdefault("time", start_time.strftime("%H_%M_%S_%f"))
@@ -432,6 +432,26 @@ class ServerConfiguration(BaseModel):
432
432
 
433
433
  return data
434
434
 
435
+ @field_validator("reportable_resources", mode="before")
436
+ @classmethod
437
+ def _convert_reportable_resources(cls, value: Any) -> Any:
438
+ """Convert reportable resources value if necessary.
439
+
440
+ This was previously set via an environment variable as a JSON-formatted
441
+ string. In case this still exists somewhere, this method converts the
442
+ JSON string to a list.
443
+
444
+ Args:
445
+ value: The reportable resources values.
446
+
447
+ Returns:
448
+ The potentially converted value.
449
+ """
450
+ if isinstance(value, str):
451
+ value = json.loads(value)
452
+
453
+ return value
454
+
435
455
  @property
436
456
  def deployment_id(self) -> UUID:
437
457
  """Get the ZenML server deployment ID.
@@ -14,7 +14,7 @@
14
14
  """Base class for event hub implementations."""
15
15
 
16
16
  from abc import ABC, abstractmethod
17
- from datetime import datetime, timedelta
17
+ from datetime import datetime, timedelta, timezone
18
18
  from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Tuple
19
19
 
20
20
  from zenml import EventSourceResponse
@@ -134,7 +134,7 @@ class BaseEventHub(ABC):
134
134
  )
135
135
  expires: Optional[datetime] = None
136
136
  if trigger.action.auth_window:
137
- expires = datetime.utcnow() + timedelta(
137
+ expires = datetime.now(timezone.utc) + timedelta(
138
138
  minutes=trigger.action.auth_window
139
139
  )
140
140
  encoded_token = token.encode(expires=expires)
@@ -408,7 +408,8 @@ class AirflowOrchestrator(ContainerizedOrchestrator):
408
408
  if schedule:
409
409
  if schedule.cron_expression:
410
410
  start_time = schedule.start_time or (
411
- datetime.datetime.utcnow() - datetime.timedelta(7)
411
+ datetime.datetime.now(datetime.timezone.utc)
412
+ - datetime.timedelta(7)
412
413
  )
413
414
  return {
414
415
  "schedule": schedule.cron_expression,
@@ -428,6 +429,7 @@ class AirflowOrchestrator(ContainerizedOrchestrator):
428
429
  "schedule": "@once",
429
430
  # set a start time in the past and disable catchup so airflow
430
431
  # runs the dag immediately
431
- "start_date": datetime.datetime.utcnow() - datetime.timedelta(7),
432
+ "start_date": datetime.datetime.now(datetime.timezone.utc)
433
+ - datetime.timedelta(7),
432
434
  "catchup": False,
433
435
  }
@@ -30,6 +30,7 @@ from zenml.stack import Flavor
30
30
 
31
31
  GCP_ARTIFACT_STORE_FLAVOR = "gcp"
32
32
  GCP_IMAGE_BUILDER_FLAVOR = "gcp"
33
+ GCP_VERTEX_EXPERIMENT_TRACKER_FLAVOR = "vertex"
33
34
  GCP_VERTEX_ORCHESTRATOR_FLAVOR = "vertex"
34
35
  GCP_VERTEX_STEP_OPERATOR_FLAVOR = "vertex"
35
36
 
@@ -70,6 +71,7 @@ class GcpIntegration(Integration):
70
71
  from zenml.integrations.gcp.flavors import (
71
72
  GCPArtifactStoreFlavor,
72
73
  GCPImageBuilderFlavor,
74
+ VertexExperimentTrackerFlavor,
73
75
  VertexOrchestratorFlavor,
74
76
  VertexStepOperatorFlavor,
75
77
  )
@@ -77,6 +79,7 @@ class GcpIntegration(Integration):
77
79
  return [
78
80
  GCPArtifactStoreFlavor,
79
81
  GCPImageBuilderFlavor,
82
+ VertexExperimentTrackerFlavor,
80
83
  VertexOrchestratorFlavor,
81
84
  VertexStepOperatorFlavor,
82
85
  ]
@@ -0,0 +1,18 @@
1
+ # Licensed under the Apache License, Version 2.0 (the "License");
2
+ # you may not use this file except in compliance with the License.
3
+ # You may obtain a copy of the License at:
4
+ #
5
+ # http://www.apache.org/licenses/LICENSE-2.0
6
+ #
7
+ # Unless required by applicable law or agreed to in writing, software
8
+ # distributed under the License is distributed on an "AS IS" BASIS,
9
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
10
+ # or implied. See the License for the specific language governing
11
+ # permissions and limitations under the License.
12
+ """Initialization for the VertexAI experiment tracker."""
13
+
14
+ from zenml.integrations.gcp.experiment_trackers.vertex_experiment_tracker import ( # noqa
15
+ VertexExperimentTracker,
16
+ )
17
+
18
+ __all__ = ["VertexExperimentTracker"]
@@ -0,0 +1,214 @@
1
+ # Copyright (c) ZenML GmbH 2022. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at:
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12
+ # or implied. See the License for the specific language governing
13
+ # permissions and limitations under the License.
14
+ """Implementation of the VertexAI experiment tracker for ZenML."""
15
+
16
+ import re
17
+ from typing import TYPE_CHECKING, Dict, Optional, Type, cast
18
+
19
+ from google.api_core import exceptions
20
+ from google.cloud import aiplatform
21
+ from google.cloud.aiplatform.compat.types import execution
22
+
23
+ from zenml.constants import METADATA_EXPERIMENT_TRACKER_URL
24
+ from zenml.experiment_trackers.base_experiment_tracker import (
25
+ BaseExperimentTracker,
26
+ )
27
+ from zenml.integrations.gcp.flavors.vertex_experiment_tracker_flavor import (
28
+ VertexExperimentTrackerConfig,
29
+ VertexExperimentTrackerSettings,
30
+ )
31
+ from zenml.integrations.gcp.google_credentials_mixin import (
32
+ GoogleCredentialsMixin,
33
+ )
34
+ from zenml.logger import get_logger
35
+ from zenml.metadata.metadata_types import Uri
36
+
37
+ if TYPE_CHECKING:
38
+ from zenml.config.step_run_info import StepRunInfo
39
+ from zenml.metadata.metadata_types import MetadataType
40
+
41
+ logger = get_logger(__name__)
42
+
43
+
44
+ class VertexExperimentTracker(BaseExperimentTracker, GoogleCredentialsMixin):
45
+ """Track experiments using VertexAI."""
46
+
47
+ @property
48
+ def config(self) -> VertexExperimentTrackerConfig:
49
+ """Returns the `VertexExperimentTrackerConfig` config.
50
+
51
+ Returns:
52
+ The configuration.
53
+ """
54
+ return cast(VertexExperimentTrackerConfig, self._config)
55
+
56
+ @property
57
+ def settings_class(self) -> Type[VertexExperimentTrackerSettings]:
58
+ """Returns the `BaseSettings` settings class.
59
+
60
+ Returns:
61
+ The settings class.
62
+ """
63
+ return VertexExperimentTrackerSettings
64
+
65
+ def prepare_step_run(self, info: "StepRunInfo") -> None:
66
+ """Configures a VertexAI run.
67
+
68
+ Args:
69
+ info: Info about the step that will be executed.
70
+ """
71
+ self._initialize_vertex(info=info)
72
+ self.experiment_name = self._get_experiment_name(info=info)
73
+ self.run_name = self._get_run_name(info=info)
74
+
75
+ def get_step_run_metadata(
76
+ self, info: "StepRunInfo"
77
+ ) -> Dict[str, "MetadataType"]:
78
+ """Get component- and step-specific metadata after a step ran.
79
+
80
+ Args:
81
+ info: Info about the step that was executed.
82
+
83
+ Returns:
84
+ A dictionary of metadata.
85
+ """
86
+ experiment_name = self._get_experiment_name(info=info)
87
+ run_name = self._get_run_name(info=info)
88
+ tensorboard_resource_name = self._get_tensorboard_resource_name(
89
+ experiment=experiment_name
90
+ )
91
+ dashboard_url = self._get_dashboard_url(experiment=experiment_name)
92
+ return {
93
+ METADATA_EXPERIMENT_TRACKER_URL: Uri(dashboard_url),
94
+ "tensorboard_resource_name": tensorboard_resource_name or "",
95
+ "vertex_run_name": run_name,
96
+ }
97
+
98
+ def _format_name(self, name: str) -> str:
99
+ return re.sub(r"[^a-z0-9-]", "-", name.strip().lower())[:128].rstrip(
100
+ "-"
101
+ )
102
+
103
+ def _get_experiment_name(self, info: "StepRunInfo") -> str:
104
+ """Gets the experiment name.
105
+
106
+ Args:
107
+ info: Info about the step.
108
+
109
+ Returns:
110
+ The experiment name.
111
+ """
112
+ settings = cast(
113
+ VertexExperimentTrackerSettings, self.get_settings(info)
114
+ )
115
+ name = settings.experiment or info.pipeline.name
116
+ return self._format_name(name)
117
+
118
+ def _get_run_name(self, info: "StepRunInfo") -> str:
119
+ """Gets the run name.
120
+
121
+ Args:
122
+ info: Info about the step that will be executed.
123
+
124
+ Returns:
125
+ The run name.
126
+ """
127
+ return self._format_name(info.run_name)
128
+
129
+ def _get_dashboard_url(self, experiment: str) -> str:
130
+ """Gets the run URL.
131
+
132
+ Args:
133
+ experiment: The name of the experiment.
134
+
135
+ Returns:
136
+ The run URL.
137
+ """
138
+ resource = aiplatform.Experiment(experiment_name=experiment)
139
+ return cast(str, resource.dashboard_url)
140
+
141
+ def _get_tensorboard_resource_name(self, experiment: str) -> Optional[str]:
142
+ resource = aiplatform.Experiment(
143
+ experiment_name=experiment
144
+ ).get_backing_tensorboard_resource()
145
+ resource_name = (
146
+ str(resource.resource_name) if resource is not None else None
147
+ )
148
+ return resource_name
149
+
150
+ def _initialize_vertex(self, info: "StepRunInfo") -> None:
151
+ """Initializes a VertexAI run.
152
+
153
+ Args:
154
+ info: Info about the step that will be executed.
155
+ """
156
+ settings = cast(
157
+ VertexExperimentTrackerSettings, self.get_settings(info)
158
+ )
159
+ experiment = self._get_experiment_name(info=info)
160
+ run_name = self._get_run_name(info=info)
161
+ credentials, project = self._get_authentication()
162
+ logger.info(
163
+ f"Initializing VertexAI with experiment name {experiment} "
164
+ f"and run name {run_name}."
165
+ )
166
+
167
+ aiplatform.init(
168
+ project=project,
169
+ location=self.config.location,
170
+ experiment=experiment,
171
+ experiment_tensorboard=settings.experiment_tensorboard,
172
+ staging_bucket=self.config.staging_bucket,
173
+ credentials=credentials,
174
+ encryption_spec_key_name=self.config.encryption_spec_key_name,
175
+ network=self.config.network,
176
+ api_endpoint=self.config.api_endpoint,
177
+ api_key=self.config.api_key,
178
+ api_transport=self.config.api_transport,
179
+ request_metadata=self.config.request_metadata,
180
+ )
181
+
182
+ try:
183
+ aiplatform.start_run(
184
+ run=run_name,
185
+ tensorboard=settings.experiment_tensorboard,
186
+ resume=True,
187
+ )
188
+ except exceptions.NotFound:
189
+ aiplatform.start_run(
190
+ run=run_name,
191
+ tensorboard=settings.experiment_tensorboard,
192
+ resume=False,
193
+ )
194
+
195
+ logger.info(
196
+ f"VertexAI experiment dashboard: {self._get_dashboard_url(experiment=experiment)}"
197
+ )
198
+ logger.info(
199
+ f"Tensorboard resource name: {self._get_tensorboard_resource_name(experiment=experiment)}"
200
+ )
201
+
202
+ def cleanup_step_run(self, info: "StepRunInfo", step_failed: bool) -> None:
203
+ """Stops the VertexAI run.
204
+
205
+ Args:
206
+ info: Info about the step that was executed.
207
+ step_failed: Whether the step failed or not.
208
+ """
209
+ state = (
210
+ execution.Execution.State.FAILED
211
+ if step_failed
212
+ else execution.Execution.State.COMPLETE
213
+ )
214
+ aiplatform.end_run(state=state)
@@ -21,6 +21,10 @@ from zenml.integrations.gcp.flavors.gcp_image_builder_flavor import (
21
21
  GCPImageBuilderConfig,
22
22
  GCPImageBuilderFlavor,
23
23
  )
24
+ from zenml.integrations.gcp.flavors.vertex_experiment_tracker_flavor import (
25
+ VertexExperimentTrackerConfig,
26
+ VertexExperimentTrackerFlavor,
27
+ )
24
28
  from zenml.integrations.gcp.flavors.vertex_orchestrator_flavor import (
25
29
  VertexOrchestratorConfig,
26
30
  VertexOrchestratorFlavor,
@@ -35,6 +39,8 @@ __all__ = [
35
39
  "GCPArtifactStoreConfig",
36
40
  "GCPImageBuilderFlavor",
37
41
  "GCPImageBuilderConfig",
42
+ "VertexExperimentTrackerFlavor",
43
+ "VertexExperimentTrackerConfig",
38
44
  "VertexOrchestratorFlavor",
39
45
  "VertexOrchestratorConfig",
40
46
  "VertexStepOperatorFlavor",
@@ -0,0 +1,199 @@
1
+ # Copyright (c) ZenML GmbH 2022. All Rights Reserved.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at:
6
+ #
7
+ # https://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12
+ # or implied. See the License for the specific language governing
13
+ # permissions and limitations under the License.
14
+ """Vertex experiment tracker flavor."""
15
+
16
+ import re
17
+ from typing import TYPE_CHECKING, Any, Dict, Optional, Type, Union
18
+
19
+ from pydantic import field_validator
20
+
21
+ from zenml.config.base_settings import BaseSettings
22
+ from zenml.experiment_trackers.base_experiment_tracker import (
23
+ BaseExperimentTrackerConfig,
24
+ BaseExperimentTrackerFlavor,
25
+ )
26
+ from zenml.integrations.gcp import (
27
+ GCP_RESOURCE_TYPE,
28
+ GCP_VERTEX_EXPERIMENT_TRACKER_FLAVOR,
29
+ )
30
+ from zenml.integrations.gcp.google_credentials_mixin import (
31
+ GoogleCredentialsConfigMixin,
32
+ )
33
+ from zenml.models import ServiceConnectorRequirements
34
+ from zenml.utils.secret_utils import SecretField
35
+
36
+ if TYPE_CHECKING:
37
+ from zenml.integrations.gcp.experiment_trackers import (
38
+ VertexExperimentTracker,
39
+ )
40
+
41
+
42
+ class VertexExperimentTrackerSettings(BaseSettings):
43
+ """Settings for the VertexAI experiment tracker.
44
+
45
+ Attributes:
46
+ experiment: The VertexAI experiment name.
47
+ experiment_tensorboard: The VertexAI experiment tensorboard.
48
+ """
49
+
50
+ experiment: Optional[str] = None
51
+ experiment_tensorboard: Optional[Union[str, bool]] = None
52
+
53
+ @field_validator("experiment", mode="before")
54
+ def _validate_experiment(cls, value: str) -> str:
55
+ """Validates the experiment name matches the the allowed format.
56
+
57
+ Args:
58
+ value: The experiment.
59
+
60
+ Raises:
61
+ ValueError: If the experiment name does not match the expected
62
+ format.
63
+
64
+ Returns:
65
+ The experiment.
66
+ """
67
+ if value and not re.match(r"^[a-z0-9][a-z0-9-]{0,127}$", value):
68
+ raise ValueError(
69
+ "Experiment name must match regex [a-z0-9][a-z0-9-]{0,127}"
70
+ )
71
+ return value
72
+
73
+
74
+ class VertexExperimentTrackerConfig(
75
+ BaseExperimentTrackerConfig,
76
+ GoogleCredentialsConfigMixin,
77
+ VertexExperimentTrackerSettings,
78
+ ):
79
+ """Config for the VertexAI experiment tracker.
80
+
81
+ Attributes:
82
+ location: Optional. The default location to use when making API calls. If not
83
+ set defaults to us-central1.
84
+ staging_bucket: Optional. The default staging bucket to use to stage artifacts
85
+ when making API calls. In the form gs://...
86
+ network:
87
+ Optional. The full name of the Compute Engine network to which jobs
88
+ and resources should be peered. E.g. "projects/12345/global/networks/myVPC".
89
+ Private services access must already be configured for the network.
90
+ If specified, all eligible jobs and resources created will be peered
91
+ with this VPC.
92
+ encryption_spec_key_name:
93
+ Optional. The Cloud KMS resource identifier of the customer
94
+ managed encryption key used to protect a resource. Has the
95
+ form:
96
+ ``projects/my-project/locations/my-region/keyRings/my-kr/cryptoKeys/my-key``.
97
+ The key needs to be in the same region as where the compute
98
+ resource is created.
99
+ api_endpoint (str):
100
+ Optional. The desired API endpoint,
101
+ e.g., us-central1-aiplatform.googleapis.com
102
+ api_key (str):
103
+ Optional. The API key to use for service calls.
104
+ NOTE: Not all services support API keys.
105
+ api_transport (str):
106
+ Optional. The transport method which is either 'grpc' or 'rest'.
107
+ NOTE: "rest" transport functionality is currently in a
108
+ beta state (preview).
109
+ request_metadata:
110
+ Optional. Additional gRPC metadata to send with every client request.
111
+ """
112
+
113
+ location: Optional[str] = None
114
+ staging_bucket: Optional[str] = None
115
+ network: Optional[str] = None
116
+ encryption_spec_key_name: Optional[str] = SecretField(default=None)
117
+ api_endpoint: Optional[str] = SecretField(default=None)
118
+ api_key: Optional[str] = SecretField(default=None)
119
+ api_transport: Optional[str] = None
120
+ request_metadata: Optional[Dict[str, Any]] = None
121
+
122
+
123
+ class VertexExperimentTrackerFlavor(BaseExperimentTrackerFlavor):
124
+ """Flavor for the VertexAI experiment tracker."""
125
+
126
+ @property
127
+ def name(self) -> str:
128
+ """Name of the flavor.
129
+
130
+ Returns:
131
+ The name of the flavor.
132
+ """
133
+ return GCP_VERTEX_EXPERIMENT_TRACKER_FLAVOR
134
+
135
+ @property
136
+ def docs_url(self) -> Optional[str]:
137
+ """A URL to point at docs explaining this flavor.
138
+
139
+ Returns:
140
+ A flavor docs url.
141
+ """
142
+ return self.generate_default_docs_url()
143
+
144
+ @property
145
+ def sdk_docs_url(self) -> Optional[str]:
146
+ """A URL to point at SDK docs explaining this flavor.
147
+
148
+ Returns:
149
+ A flavor SDK docs url.
150
+ """
151
+ return self.generate_default_sdk_docs_url()
152
+
153
+ @property
154
+ def logo_url(self) -> str:
155
+ """A URL to represent the flavor in the dashboard.
156
+
157
+ Returns:
158
+ The flavor logo.
159
+ """
160
+ return "https://public-flavor-logos.s3.eu-central-1.amazonaws.com/experiment_tracker/vertexai.png"
161
+
162
+ @property
163
+ def config_class(self) -> Type[VertexExperimentTrackerConfig]:
164
+ """Returns `VertexExperimentTrackerConfig` config class.
165
+
166
+ Returns:
167
+ The config class.
168
+ """
169
+ return VertexExperimentTrackerConfig
170
+
171
+ @property
172
+ def implementation_class(self) -> Type["VertexExperimentTracker"]:
173
+ """Implementation class for this flavor.
174
+
175
+ Returns:
176
+ The implementation class.
177
+ """
178
+ from zenml.integrations.gcp.experiment_trackers import (
179
+ VertexExperimentTracker,
180
+ )
181
+
182
+ return VertexExperimentTracker
183
+
184
+ @property
185
+ def service_connector_requirements(
186
+ self,
187
+ ) -> Optional[ServiceConnectorRequirements]:
188
+ """Service connector resource requirements for service connectors.
189
+
190
+ Specifies resource requirements that are used to filter the available
191
+ service connector types that are compatible with this flavor.
192
+
193
+ Returns:
194
+ Requirements for compatible service connectors, if a service
195
+ connector is required for this flavor.
196
+ """
197
+ return ServiceConnectorRequirements(
198
+ resource_type=GCP_RESOURCE_TYPE,
199
+ )
@@ -248,7 +248,7 @@ def wait_pod(
248
248
  Returns:
249
249
  The pod object which meets the exit condition.
250
250
  """
251
- start_time = datetime.datetime.utcnow()
251
+ start_time = datetime.datetime.now(datetime.timezone.utc)
252
252
 
253
253
  # Link to exponential back-off algorithm used here:
254
254
  # https://cloud.google.com/storage/docs/exponential-backoff
@@ -288,7 +288,7 @@ def wait_pod(
288
288
  return resp
289
289
 
290
290
  # Check if wait timed out.
291
- elapse_time = datetime.datetime.utcnow() - start_time
291
+ elapse_time = datetime.datetime.now(datetime.timezone.utc) - start_time
292
292
  if elapse_time.seconds >= timeout_sec and timeout_sec != 0:
293
293
  raise RuntimeError(
294
294
  f"Waiting for pod `{namespace}:{pod_name}` timed out after "
@@ -236,7 +236,6 @@ class MLFlowExperimentTracker(BaseExperimentTracker):
236
236
  """Disables MLflow autologging for all supported frameworks."""
237
237
  frameworks = [
238
238
  "tensorflow",
239
- "gluon",
240
239
  "xgboost",
241
240
  "lightgbm",
242
241
  "statsmodels",
@@ -97,7 +97,9 @@ class WhylogsDataValidator(BaseDataValidator, AuthenticationMixin):
97
97
  """
98
98
  results = why.log(pandas=dataset)
99
99
  profile = results.profile()
100
- dataset_timestamp = dataset_timestamp or datetime.datetime.utcnow()
100
+ dataset_timestamp = dataset_timestamp or datetime.datetime.now(
101
+ datetime.timezone.utc
102
+ )
101
103
  profile.set_dataset_timestamp(dataset_timestamp=dataset_timestamp)
102
104
  return profile.view()
103
105
 
@@ -13,7 +13,7 @@
13
13
  # permissions and limitations under the License.
14
14
  """Models representing API keys."""
15
15
 
16
- from datetime import datetime, timedelta
16
+ from datetime import datetime, timedelta, timezone
17
17
  from typing import TYPE_CHECKING, ClassVar, List, Optional, Type, Union
18
18
  from uuid import UUID
19
19
 
@@ -319,7 +319,7 @@ class APIKeyInternalResponse(APIKeyResponse):
319
319
  and self.retain_period_minutes > 0
320
320
  ):
321
321
  # check if the previous key is still valid
322
- if datetime.utcnow() - self.last_rotated < timedelta(
322
+ if datetime.now(timezone.utc) - self.last_rotated < timedelta(
323
323
  minutes=self.retain_period_minutes
324
324
  ):
325
325
  key_hash = self.previous_key