zenml-nightly 0.63.0.dev20240801__py3-none-any.whl → 0.64.0.dev20240809__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 (198) hide show
  1. README.md +2 -2
  2. RELEASE_NOTES.md +79 -0
  3. zenml/VERSION +1 -1
  4. zenml/__init__.py +0 -4
  5. zenml/analytics/enums.py +0 -6
  6. zenml/cli/__init__.py +0 -61
  7. zenml/cli/base.py +1 -1
  8. zenml/cli/web_login.py +8 -0
  9. zenml/client.py +0 -4
  10. zenml/config/build_configuration.py +43 -17
  11. zenml/config/docker_settings.py +80 -57
  12. zenml/config/source.py +58 -0
  13. zenml/constants.py +9 -2
  14. zenml/entrypoints/base_entrypoint_configuration.py +53 -8
  15. zenml/enums.py +1 -1
  16. zenml/environment.py +25 -9
  17. zenml/image_builders/base_image_builder.py +1 -1
  18. zenml/image_builders/build_context.py +25 -72
  19. zenml/integrations/azure/__init__.py +4 -0
  20. zenml/integrations/azure/flavors/__init__.py +11 -0
  21. zenml/integrations/azure/flavors/azureml_orchestrator_flavor.py +263 -0
  22. zenml/{_hub → integrations/azure/orchestrators}/__init__.py +7 -2
  23. zenml/integrations/azure/orchestrators/azureml_orchestrator.py +544 -0
  24. zenml/integrations/azure/orchestrators/azureml_orchestrator_entrypoint_config.py +86 -0
  25. zenml/integrations/azure/step_operators/azureml_step_operator.py +3 -0
  26. zenml/integrations/databricks/flavors/databricks_orchestrator_flavor.py +9 -0
  27. zenml/integrations/gcp/orchestrators/vertex_orchestrator.py +7 -2
  28. zenml/integrations/gcp/service_connectors/gcp_service_connector.py +123 -6
  29. zenml/integrations/kaniko/image_builders/kaniko_image_builder.py +1 -1
  30. zenml/integrations/mlflow/__init__.py +1 -1
  31. zenml/integrations/mlflow/experiment_trackers/mlflow_experiment_tracker.py +3 -1
  32. zenml/integrations/mlflow/flavors/mlflow_experiment_tracker_flavor.py +3 -0
  33. zenml/logger.py +13 -0
  34. zenml/models/__init__.py +0 -12
  35. zenml/models/v2/core/pipeline_deployment.py +21 -29
  36. zenml/models/v2/core/pipeline_run.py +13 -0
  37. zenml/models/v2/core/server_settings.py +12 -0
  38. zenml/models/v2/core/user.py +0 -21
  39. zenml/models/v2/misc/server_models.py +7 -1
  40. zenml/models/v2/misc/user_auth.py +0 -7
  41. zenml/new/pipelines/build_utils.py +193 -38
  42. zenml/new/pipelines/code_archive.py +157 -0
  43. zenml/new/pipelines/pipeline.py +29 -2
  44. zenml/new/pipelines/run_utils.py +67 -1
  45. zenml/service_connectors/service_connector_utils.py +14 -0
  46. zenml/stack_deployments/aws_stack_deployment.py +26 -3
  47. zenml/stack_deployments/azure_stack_deployment.py +11 -6
  48. zenml/stack_deployments/gcp_stack_deployment.py +24 -2
  49. zenml/stack_deployments/stack_deployment.py +17 -2
  50. zenml/steps/base_step.py +3 -0
  51. zenml/utils/archivable.py +149 -0
  52. zenml/utils/code_utils.py +244 -0
  53. zenml/utils/notebook_utils.py +122 -0
  54. zenml/utils/pipeline_docker_image_builder.py +3 -96
  55. zenml/utils/source_utils.py +109 -1
  56. zenml/zen_server/dashboard/assets/{404-CI13wQp4.js → 404-CRAA_Lew.js} +1 -1
  57. zenml/zen_server/dashboard/assets/@radix-BXWm7HOa.js +85 -0
  58. zenml/zen_server/dashboard/assets/{@react-router-CO-OsFwI.js → @react-router-l3lMcXA2.js} +1 -1
  59. zenml/zen_server/dashboard/assets/{@reactflow-DIYUhKYX.js → @reactflow-CeVxyqYT.js} +2 -2
  60. zenml/zen_server/dashboard/assets/{@tanstack-k96lU_C-.js → @tanstack-FmcYZMuX.js} +4 -4
  61. zenml/zen_server/dashboard/assets/AlertDialogDropdownItem-ErO9aOgK.js +1 -0
  62. zenml/zen_server/dashboard/assets/{AwarenessChannel-BNg5uWgI.js → AwarenessChannel-CLXo5rKM.js} +1 -1
  63. zenml/zen_server/dashboard/assets/{CodeSnippet-Cyp7f4dM.js → CodeSnippet-D0VLxT2A.js} +1 -1
  64. zenml/zen_server/dashboard/assets/{CollapsibleCard-Cu_A9W57.js → CollapsibleCard-BaUPiVg0.js} +1 -1
  65. zenml/zen_server/dashboard/assets/{Commands-DmQwTXjj.js → Commands-JrcZK-3j.js} +1 -1
  66. zenml/zen_server/dashboard/assets/CopyButton-Dbo52T1K.js +2 -0
  67. zenml/zen_server/dashboard/assets/{CsvVizualization-BvqItd-O.js → CsvVizualization-D3kAypDj.js} +3 -3
  68. zenml/zen_server/dashboard/assets/DisplayDate-DizbSeT-.js +1 -0
  69. zenml/zen_server/dashboard/assets/EditSecretDialog-Bd7mFLS4.js +1 -0
  70. zenml/zen_server/dashboard/assets/{EmptyState-BMLnFVlB.js → EmptyState-BHblM39I.js} +1 -1
  71. zenml/zen_server/dashboard/assets/{Error-DbXCTGua.js → Error-C6LeJSER.js} +1 -1
  72. zenml/zen_server/dashboard/assets/{ExecutionStatus-9zM7eaLh.js → ExecutionStatus-jH4OrWBq.js} +1 -1
  73. zenml/zen_server/dashboard/assets/{Helpbox-BIiNc-uH.js → Helpbox-aAB2XP-z.js} +1 -1
  74. zenml/zen_server/dashboard/assets/{Infobox-iv1Nu1A0.js → Infobox-BQ0aty32.js} +1 -1
  75. zenml/zen_server/dashboard/assets/{InlineAvatar-BvBtO2Dp.js → InlineAvatar-DpTLgM3Q.js} +1 -1
  76. zenml/zen_server/dashboard/assets/Lock-CNyJvf2r.js +1 -0
  77. zenml/zen_server/dashboard/assets/{MarkdownVisualization-xp3hhULl.js → MarkdownVisualization-Bajxn0HY.js} +1 -1
  78. zenml/zen_server/dashboard/assets/NumberBox-BmKE0qnO.js +1 -0
  79. zenml/zen_server/dashboard/assets/{PasswordChecker-DUveqlva.js → PasswordChecker-yGGoJSB-.js} +1 -1
  80. zenml/zen_server/dashboard/assets/{ProviderRadio-pSAvrGRS.js → ProviderRadio-BBqkIuTd.js} +1 -1
  81. zenml/zen_server/dashboard/assets/RadioItem-xLhXoiFV.js +1 -0
  82. zenml/zen_server/dashboard/assets/SearchField-C9R0mdaX.js +1 -0
  83. zenml/zen_server/dashboard/assets/{SetPassword-BOxpgh6N.js → SetPassword-52sNxNiO.js} +1 -1
  84. zenml/zen_server/dashboard/assets/{SuccessStep-CTSKN2lp.js → SuccessStep-DlkItqYG.js} +1 -1
  85. zenml/zen_server/dashboard/assets/{Tick-Bnr2TpW6.js → Tick-uxv80Q6a.js} +1 -1
  86. zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-BeCeaRW5.js → UpdatePasswordSchemas-oN4G3sKz.js} +1 -1
  87. zenml/zen_server/dashboard/assets/{aws-BgKTfTfx.js → aws-0_3UsPif.js} +1 -1
  88. zenml/zen_server/dashboard/assets/{check-circle-i56092KI.js → check-circle-1_I207rW.js} +1 -1
  89. zenml/zen_server/dashboard/assets/{chevron-down-D_ZlKMqH.js → chevron-down-BpaF8JqM.js} +1 -1
  90. zenml/zen_server/dashboard/assets/{chevron-right-double-CZBOf6JM.js → chevron-right-double-Dk8e2L99.js} +1 -1
  91. zenml/zen_server/dashboard/assets/{cloud-only-qelmY92E.js → cloud-only-BkUuI0lZ.js} +1 -1
  92. zenml/zen_server/dashboard/assets/components-Br2ezRib.js +1 -0
  93. zenml/zen_server/dashboard/assets/{copy-BXNk6BjL.js → copy-f3XGPPxt.js} +1 -1
  94. zenml/zen_server/dashboard/assets/{database-1xWSgZfO.js → database-cXYNX9tt.js} +1 -1
  95. zenml/zen_server/dashboard/assets/{docker-CQMVm_4d.js → docker-8uj__HHK.js} +1 -1
  96. zenml/zen_server/dashboard/assets/{dots-horizontal-BObFzD5l.js → dots-horizontal-sKQlWEni.js} +1 -1
  97. zenml/zen_server/dashboard/assets/edit-C0MVvPD2.js +1 -0
  98. zenml/zen_server/dashboard/assets/{file-text-CqD_iu6l.js → file-text-B9JibxTs.js} +1 -1
  99. zenml/zen_server/dashboard/assets/{help-bu_DgLKI.js → help-FuHlZwn0.js} +1 -1
  100. zenml/zen_server/dashboard/assets/index-Bd1xgUQG.js +1 -0
  101. zenml/zen_server/dashboard/assets/index-DaGknux4.css +1 -0
  102. zenml/zen_server/dashboard/assets/{index-KsTz2dHG.js → index-DhIZtpxB.js} +5 -5
  103. zenml/zen_server/dashboard/assets/{index.esm-CbHNSeVw.js → index.esm-DT4uyn2i.js} +1 -1
  104. zenml/zen_server/dashboard/assets/layout-D6oiSbfd.js +1 -0
  105. zenml/zen_server/dashboard/assets/{login-mutation-DRpbESS7.js → login-mutation-13A_JSVA.js} +1 -1
  106. zenml/zen_server/dashboard/assets/{logs-D8k8BVFf.js → logs-CgeE2vZP.js} +1 -1
  107. zenml/zen_server/dashboard/assets/{not-found-Dfx9hfkf.js → not-found-B0Mmb90p.js} +1 -1
  108. zenml/zen_server/dashboard/assets/{package-ClbU3KUi.js → package-DdkziX79.js} +1 -1
  109. zenml/zen_server/dashboard/assets/page-7-v2OBm-.js +1 -0
  110. zenml/zen_server/dashboard/assets/{page-f3jBVI5Z.js → page-B3ozwdD1.js} +1 -1
  111. zenml/zen_server/dashboard/assets/{page-DYBNGxJt.js → page-BGwA9B1M.js} +1 -1
  112. zenml/zen_server/dashboard/assets/{page-C176KxyB.js → page-BkjAUyTA.js} +1 -1
  113. zenml/zen_server/dashboard/assets/page-BnacgBiy.js +1 -0
  114. zenml/zen_server/dashboard/assets/{page-CzucfYPo.js → page-BxF_KMQ3.js} +2 -2
  115. zenml/zen_server/dashboard/assets/page-C4POHC0K.js +1 -0
  116. zenml/zen_server/dashboard/assets/page-C9kudd44.js +9 -0
  117. zenml/zen_server/dashboard/assets/page-CA1j3GpJ.js +1 -0
  118. zenml/zen_server/dashboard/assets/page-CCY6yfmu.js +1 -0
  119. zenml/zen_server/dashboard/assets/page-CgTe7Bme.js +1 -0
  120. zenml/zen_server/dashboard/assets/{page-DtpwnNXq.js → page-Cgn-6v2Y.js} +1 -1
  121. zenml/zen_server/dashboard/assets/page-CxQmQqDw.js +1 -0
  122. zenml/zen_server/dashboard/assets/page-D2Goey3H.js +1 -0
  123. zenml/zen_server/dashboard/assets/page-DLpOnf7u.js +1 -0
  124. zenml/zen_server/dashboard/assets/{page-DVPxY5fT.js → page-DSTQnBk-.js} +1 -1
  125. zenml/zen_server/dashboard/assets/{page-BoFtUD9H.js → page-DTysUGOy.js} +1 -1
  126. zenml/zen_server/dashboard/assets/{page-p2hLJdS2.js → page-D_EXUFJb.js} +1 -1
  127. zenml/zen_server/dashboard/assets/page-Db15QzsM.js +1 -0
  128. zenml/zen_server/dashboard/assets/{page-Btu39x7k.js → page-DugsjcQ_.js} +1 -1
  129. zenml/zen_server/dashboard/assets/{page-CZe9GEBF.js → page-OFKSPyN7.js} +1 -1
  130. zenml/zen_server/dashboard/assets/{page-CDgZmwxP.js → page-RnG-qhv9.js} +1 -1
  131. zenml/zen_server/dashboard/assets/{page-Cjn97HMv.js → page-T2BtjwPl.js} +1 -1
  132. zenml/zen_server/dashboard/assets/page-TXe1Eo3Z.js +1 -0
  133. zenml/zen_server/dashboard/assets/{page-BxiWdeyg.js → page-YiF_fNbe.js} +1 -1
  134. zenml/zen_server/dashboard/assets/{page-399pVZHU.js → page-hQaiQXfg.js} +1 -1
  135. zenml/zen_server/dashboard/assets/persist-3-5nOJ6m.js +1 -0
  136. zenml/zen_server/dashboard/assets/{play-circle-CNtZKDnW.js → play-circle-XSkLR12B.js} +1 -1
  137. zenml/zen_server/dashboard/assets/{plus-DOeLmm7C.js → plus-FB9-lEq_.js} +1 -1
  138. zenml/zen_server/dashboard/assets/refresh-COb6KYDi.js +1 -0
  139. zenml/zen_server/dashboard/assets/sharedSchema-BoYx_B_L.js +14 -0
  140. zenml/zen_server/dashboard/assets/{stack-detail-query-Ck7j7BP_.js → stack-detail-query-B-US_-wa.js} +1 -1
  141. zenml/zen_server/dashboard/assets/{terminal-By9cErXc.js → terminal-grtjrIEJ.js} +1 -1
  142. zenml/zen_server/dashboard/assets/trash-Cd5CSFqA.js +1 -0
  143. zenml/zen_server/dashboard/assets/{update-server-settings-mutation-f3ZT7psb.js → update-server-settings-mutation-B8GB_ubU.js} +1 -1
  144. zenml/zen_server/dashboard/assets/{url-rGEp5Umh.js → url-hcMJkz8p.js} +1 -1
  145. zenml/zen_server/dashboard/assets/{zod-BtSyGx4C.js → zod-CnykDKJj.js} +1 -1
  146. zenml/zen_server/dashboard/index.html +7 -7
  147. zenml/zen_server/dashboard_legacy/asset-manifest.json +4 -4
  148. zenml/zen_server/dashboard_legacy/index.html +1 -1
  149. zenml/zen_server/dashboard_legacy/{precache-manifest.2fa6e528a6e7447caaf35dadfe7514bb.js → precache-manifest.9c473c96a43298343a7ce1256183123b.js} +4 -4
  150. zenml/zen_server/dashboard_legacy/service-worker.js +1 -1
  151. zenml/zen_server/dashboard_legacy/static/js/{main.4aab7e98.chunk.js → main.463c90b9.chunk.js} +2 -2
  152. zenml/zen_server/dashboard_legacy/static/js/{main.4aab7e98.chunk.js.map → main.463c90b9.chunk.js.map} +1 -1
  153. zenml/zen_server/deploy/helm/Chart.yaml +1 -1
  154. zenml/zen_server/deploy/helm/README.md +2 -2
  155. zenml/zen_server/routers/stack_deployment_endpoints.py +6 -0
  156. zenml/zen_server/routers/users_endpoints.py +0 -7
  157. zenml/zen_server/utils.py +75 -0
  158. zenml/zen_server/zen_server_api.py +52 -1
  159. zenml/zen_stores/base_zen_store.py +7 -1
  160. zenml/zen_stores/migrations/versions/0.64.0_release.py +23 -0
  161. zenml/zen_stores/migrations/versions/026d4577b6a0_add_code_path.py +39 -0
  162. zenml/zen_stores/migrations/versions/3dcc5d20e82f_add_last_user_activity.py +51 -0
  163. zenml/zen_stores/migrations/versions/909550c7c4da_remove_user_hub_token.py +36 -0
  164. zenml/zen_stores/rest_zen_store.py +5 -3
  165. zenml/zen_stores/schemas/pipeline_deployment_schemas.py +3 -0
  166. zenml/zen_stores/schemas/pipeline_run_schemas.py +3 -0
  167. zenml/zen_stores/schemas/server_settings_schemas.py +2 -0
  168. zenml/zen_stores/schemas/user_schemas.py +0 -2
  169. zenml/zen_stores/sql_zen_store.py +25 -1
  170. {zenml_nightly-0.63.0.dev20240801.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/METADATA +3 -3
  171. {zenml_nightly-0.63.0.dev20240801.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/RECORD +174 -157
  172. zenml/_hub/client.py +0 -289
  173. zenml/_hub/constants.py +0 -21
  174. zenml/_hub/utils.py +0 -79
  175. zenml/cli/hub.py +0 -1116
  176. zenml/models/v2/misc/hub_plugin_models.py +0 -79
  177. zenml/zen_server/dashboard/assets/@radix-CFOkMR_E.js +0 -85
  178. zenml/zen_server/dashboard/assets/CopyButton-B3sWVJ4Z.js +0 -2
  179. zenml/zen_server/dashboard/assets/DisplayDate-DYgIjlDF.js +0 -1
  180. zenml/zen_server/dashboard/assets/SearchField-CXoBknpt.js +0 -1
  181. zenml/zen_server/dashboard/assets/components-DWe4cTjS.js +0 -1
  182. zenml/zen_server/dashboard/assets/index-vfjX_fJV.css +0 -1
  183. zenml/zen_server/dashboard/assets/page-C6tXXjnK.js +0 -1
  184. zenml/zen_server/dashboard/assets/page-CP9obrnG.js +0 -1
  185. zenml/zen_server/dashboard/assets/page-CaTOsNNw.js +0 -1
  186. zenml/zen_server/dashboard/assets/page-CmXmB_5i.js +0 -1
  187. zenml/zen_server/dashboard/assets/page-CvGAOfad.js +0 -1
  188. zenml/zen_server/dashboard/assets/page-D0bbc-qr.js +0 -5
  189. zenml/zen_server/dashboard/assets/page-DLEtD2ex.js +0 -1
  190. zenml/zen_server/dashboard/assets/page-DupV0aBd.js +0 -1
  191. zenml/zen_server/dashboard/assets/page-EweAR81y.js +0 -1
  192. zenml/zen_server/dashboard/assets/page-w-YaL77M.js +0 -9
  193. zenml/zen_server/dashboard/assets/persist-BReKApOc.js +0 -14
  194. zenml/zen_server/dashboard/assets/secrets-video-OBJ6irhH.svg +0 -21
  195. zenml/zen_server/dashboard/assets/stacks-video-7gfxpAq4.svg +0 -21
  196. {zenml_nightly-0.63.0.dev20240801.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/LICENSE +0 -0
  197. {zenml_nightly-0.63.0.dev20240801.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/WHEEL +0 -0
  198. {zenml_nightly-0.63.0.dev20240801.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/entry_points.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  apiVersion: v2
2
2
  name: zenml
3
- version: "0.63.0"
3
+ version: "0.64.0"
4
4
  description: Open source MLOps framework for portable production ready ML pipelines
5
5
  keywords:
6
6
  - mlops
@@ -20,8 +20,8 @@ ZenML is an open-source MLOps framework designed to help you create robust, main
20
20
  To install the ZenML chart directly from Amazon ECR, use the following command:
21
21
 
22
22
  ```bash
23
- # example command for version 0.63.0
24
- helm install my-zenml oci://public.ecr.aws/zenml/zenml --version 0.63.0
23
+ # example command for version 0.64.0
24
+ helm install my-zenml oci://public.ecr.aws/zenml/zenml --version 0.64.0
25
25
  ```
26
26
 
27
27
  Note: Ensure you have OCI support enabled in your Helm client and that you are authenticated with Amazon ECR.
@@ -78,6 +78,7 @@ def get_stack_deployment_config(
78
78
  provider: StackDeploymentProvider,
79
79
  stack_name: str,
80
80
  location: Optional[str] = None,
81
+ terraform: bool = False,
81
82
  auth_context: AuthContext = Security(authorize),
82
83
  ) -> StackDeploymentConfig:
83
84
  """Return the URL to deploy the ZenML stack to the specified cloud provider.
@@ -87,6 +88,7 @@ def get_stack_deployment_config(
87
88
  provider: The stack deployment provider.
88
89
  stack_name: The name of the stack.
89
90
  location: The location where the stack should be deployed.
91
+ terraform: Whether the stack should be deployed using Terraform.
90
92
  auth_context: The authentication context.
91
93
 
92
94
  Returns:
@@ -118,6 +120,7 @@ def get_stack_deployment_config(
118
120
  api_token = token.encode(expires=expires)
119
121
 
120
122
  return stack_deployment_class(
123
+ terraform=terraform,
121
124
  stack_name=stack_name,
122
125
  location=location,
123
126
  zenml_server_url=str(url),
@@ -134,6 +137,7 @@ def get_deployed_stack(
134
137
  stack_name: str,
135
138
  location: Optional[str] = None,
136
139
  date_start: Optional[datetime.datetime] = None,
140
+ terraform: bool = False,
137
141
  _: AuthContext = Security(authorize),
138
142
  ) -> Optional[DeployedStack]:
139
143
  """Return a matching ZenML stack that was deployed and registered.
@@ -143,6 +147,7 @@ def get_deployed_stack(
143
147
  stack_name: The name of the stack.
144
148
  location: The location where the stack should be deployed.
145
149
  date_start: The date when the deployment started.
150
+ terraform: Whether the stack was deployed using Terraform.
146
151
 
147
152
  Returns:
148
153
  The ZenML stack that was deployed and registered or None if the stack
@@ -150,6 +155,7 @@ def get_deployed_stack(
150
155
  """
151
156
  stack_deployment_class = get_stack_deployment_class(provider)
152
157
  return stack_deployment_class(
158
+ terraform=terraform,
153
159
  stack_name=stack_name,
154
160
  location=location,
155
161
  # These fields are not needed for this operation
@@ -286,7 +286,6 @@ if server_config().auth_scheme != AuthScheme.EXTERNAL:
286
286
  # - active
287
287
  # - password
288
288
  # - email_opted_in + email
289
- # - hub_token
290
289
  #
291
290
  safe_user_update = user_update.create_copy(
292
291
  exclude={
@@ -298,7 +297,6 @@ if server_config().auth_scheme != AuthScheme.EXTERNAL:
298
297
  "old_password",
299
298
  "email_opted_in",
300
299
  "email",
301
- "hub_token",
302
300
  },
303
301
  )
304
302
 
@@ -387,7 +385,6 @@ if server_config().auth_scheme != AuthScheme.EXTERNAL:
387
385
  if (
388
386
  user_update.email_opted_in is not None
389
387
  or user_update.email is not None
390
- or user_update.hub_token is not None
391
388
  ):
392
389
  if user.id != auth_context.user.id:
393
390
  raise IllegalOperationError(
@@ -399,8 +396,6 @@ if server_config().auth_scheme != AuthScheme.EXTERNAL:
399
396
  if safe_user_update.email_opted_in is not None:
400
397
  safe_user_update.email_opted_in = user_update.email_opted_in
401
398
  safe_user_update.email = user_update.email
402
- if safe_user_update.hub_token is not None:
403
- safe_user_update.hub_token = user_update.hub_token
404
399
 
405
400
  updated_user = zen_store().update_user(
406
401
  user_id=user.id,
@@ -444,7 +439,6 @@ if server_config().auth_scheme != AuthScheme.EXTERNAL:
444
439
  # - is_admin
445
440
  # - active
446
441
  # - old_password
447
- # - hub_token
448
442
  #
449
443
  safe_user_update = user_update.create_copy(
450
444
  exclude={
@@ -453,7 +447,6 @@ if server_config().auth_scheme != AuthScheme.EXTERNAL:
453
447
  "is_admin",
454
448
  "active",
455
449
  "old_password",
456
- "hub_token",
457
450
  },
458
451
  )
459
452
 
zenml/zen_server/utils.py CHANGED
@@ -17,8 +17,10 @@ import inspect
17
17
  import os
18
18
  from functools import wraps
19
19
  from typing import (
20
+ TYPE_CHECKING,
20
21
  Any,
21
22
  Callable,
23
+ List,
22
24
  Optional,
23
25
  Tuple,
24
26
  Type,
@@ -33,7 +35,10 @@ from pydantic import BaseModel, ValidationError
33
35
  from zenml.config.global_config import GlobalConfiguration
34
36
  from zenml.config.server_config import ServerConfiguration
35
37
  from zenml.constants import (
38
+ API,
36
39
  ENV_ZENML_SERVER,
40
+ INFO,
41
+ VERSION_1,
37
42
  )
38
43
  from zenml.enums import ServerProviderType
39
44
  from zenml.exceptions import IllegalOperationError, OAuthError
@@ -53,6 +58,9 @@ from zenml.zen_server.template_execution.workload_manager_interface import (
53
58
  )
54
59
  from zenml.zen_stores.sql_zen_store import SqlZenStore
55
60
 
61
+ if TYPE_CHECKING:
62
+ from fastapi import Request
63
+
56
64
  logger = get_logger(__name__)
57
65
 
58
66
  _zen_store: Optional["SqlZenStore"] = None
@@ -570,3 +578,70 @@ def verify_admin_status_if_no_rbac(
570
578
  "without RBAC enabled.",
571
579
  )
572
580
  return
581
+
582
+
583
+ def is_user_request(request: "Request") -> bool:
584
+ """Determine if the incoming request is a user request.
585
+
586
+ This function checks various aspects of the request to determine
587
+ if it's a user-initiated request or a system request.
588
+
589
+ Args:
590
+ request: The incoming FastAPI request object.
591
+
592
+ Returns:
593
+ True if it's a user request, False otherwise.
594
+ """
595
+ # Define system paths that should be excluded
596
+ system_paths: List[str] = [
597
+ "/health",
598
+ "/metrics",
599
+ "/system",
600
+ "/docs",
601
+ "/redoc",
602
+ "/openapi.json",
603
+ ]
604
+
605
+ user_prefix = f"{API}{VERSION_1}"
606
+ excluded_user_apis = [INFO]
607
+ # Check if this is not an excluded endpoint
608
+ if request.url.path in [
609
+ user_prefix + suffix for suffix in excluded_user_apis
610
+ ]:
611
+ return False
612
+
613
+ # Check if this is other user request
614
+ if request.url.path.startswith(user_prefix):
615
+ return True
616
+
617
+ # Exclude system paths
618
+ if any(request.url.path.startswith(path) for path in system_paths):
619
+ return False
620
+
621
+ # Exclude requests with specific headers
622
+ if request.headers.get("X-System-Request") == "true":
623
+ return False
624
+
625
+ # Exclude requests from certain user agents (e.g., monitoring tools)
626
+ user_agent = request.headers.get("User-Agent", "").lower()
627
+ system_agents = ["prometheus", "datadog", "newrelic", "pingdom"]
628
+ if any(agent in user_agent for agent in system_agents):
629
+ return False
630
+
631
+ # Check for internal IP addresses
632
+ client_host = request.client.host if request.client else None
633
+ if client_host and (
634
+ client_host.startswith("10.") or client_host.startswith("192.168.")
635
+ ):
636
+ return False
637
+
638
+ # Exclude OPTIONS requests (often used for CORS preflight)
639
+ if request.method == "OPTIONS":
640
+ return False
641
+
642
+ # Exclude specific query parameters that might indicate system requests
643
+ if request.query_params.get("system_check"):
644
+ return False
645
+
646
+ # If none of the above conditions are met, consider it a user request
647
+ return True
@@ -22,6 +22,7 @@ To run this file locally, execute:
22
22
 
23
23
  import os
24
24
  from asyncio.log import logger
25
+ from datetime import datetime, timedelta, timezone
25
26
  from genericpath import isfile
26
27
  from typing import Any, List
27
28
 
@@ -36,7 +37,11 @@ from starlette.responses import FileResponse
36
37
 
37
38
  import zenml
38
39
  from zenml.analytics import source_context
39
- from zenml.constants import API, HEALTH
40
+ from zenml.constants import (
41
+ API,
42
+ DEFAULT_ZENML_SERVER_REPORT_USER_ACTIVITY_TO_DB_SECONDS,
43
+ HEALTH,
44
+ )
40
45
  from zenml.enums import AuthScheme, SourceContextTypes
41
46
  from zenml.zen_server.exceptions import error_detail
42
47
  from zenml.zen_server.routers import (
@@ -80,8 +85,10 @@ from zenml.zen_server.utils import (
80
85
  initialize_secure_headers,
81
86
  initialize_workload_manager,
82
87
  initialize_zen_store,
88
+ is_user_request,
83
89
  secure_headers,
84
90
  server_config,
91
+ zen_store,
85
92
  )
86
93
 
87
94
  if server_config().use_legacy_dashboard:
@@ -109,6 +116,12 @@ app = FastAPI(
109
116
  default_response_class=ORJSONResponse,
110
117
  )
111
118
 
119
+ # Initialize last_user_activity
120
+ last_user_activity: datetime = datetime.now(timezone.utc)
121
+ last_user_activity_reported: datetime = datetime.now(timezone.utc) + timedelta(
122
+ seconds=-DEFAULT_ZENML_SERVER_REPORT_USER_ACTIVITY_TO_DB_SECONDS
123
+ )
124
+
112
125
 
113
126
  # Customize the default request validation handler that comes with FastAPI
114
127
  # to return a JSON response that matches the ZenML API spec.
@@ -159,6 +172,44 @@ async def set_secure_headers(request: Request, call_next: Any) -> Any:
159
172
  return response
160
173
 
161
174
 
175
+ @app.middleware("http")
176
+ async def track_last_user_activity(request: Request, call_next: Any) -> Any:
177
+ """A middleware to track last user activity.
178
+
179
+ This middleware checks if the incoming request is a user request and
180
+ updates the last activity timestamp if it is.
181
+
182
+ Args:
183
+ request: The incoming request object.
184
+ call_next: A function that will receive the request as a parameter and
185
+ pass it to the corresponding path operation.
186
+
187
+ Returns:
188
+ The response to the request.
189
+ """
190
+ global last_user_activity
191
+ global last_user_activity_reported
192
+
193
+ try:
194
+ if is_user_request(request):
195
+ last_user_activity = datetime.now(timezone.utc)
196
+ except Exception as e:
197
+ logger.debug(
198
+ f"An unexpected error occurred while checking user activity: {e}"
199
+ )
200
+ if (
201
+ (
202
+ datetime.now(timezone.utc) - last_user_activity_reported
203
+ ).total_seconds()
204
+ > DEFAULT_ZENML_SERVER_REPORT_USER_ACTIVITY_TO_DB_SECONDS
205
+ ):
206
+ last_user_activity_reported = datetime.now(timezone.utc)
207
+ zen_store()._update_last_user_activity_timestamp(
208
+ last_user_activity=last_user_activity
209
+ )
210
+ return await call_next(request)
211
+
212
+
162
213
  @app.middleware("http")
163
214
  async def infer_source_context(request: Request, call_next: Any) -> Any:
164
215
  """A middleware to track the source of an event.
@@ -39,6 +39,7 @@ from zenml.constants import (
39
39
  DEFAULT_WORKSPACE_NAME,
40
40
  ENV_ZENML_DEFAULT_WORKSPACE_NAME,
41
41
  IS_DEBUG_ENV,
42
+ ZENML_PRO_CONNECTION_ISSUES_SUSPENDED_PAUSED_TENANT_HINT,
42
43
  )
43
44
  from zenml.enums import (
44
45
  SecretsStoreType,
@@ -171,9 +172,14 @@ class BaseZenStore(
171
172
  )
172
173
 
173
174
  except Exception as e:
175
+ zenml_pro_extra = ""
176
+ if ".zenml.io" in self.url:
177
+ zenml_pro_extra = (
178
+ ZENML_PRO_CONNECTION_ISSUES_SUSPENDED_PAUSED_TENANT_HINT
179
+ )
174
180
  raise RuntimeError(
175
181
  f"Error initializing {self.type.value} store with URL "
176
- f"'{self.url}': {str(e)}"
182
+ f"'{self.url}': {str(e)}" + zenml_pro_extra
177
183
  ) from e
178
184
 
179
185
  if not skip_default_registrations:
@@ -0,0 +1,23 @@
1
+ """Release [0.64.0].
2
+
3
+ Revision ID: 0.64.0
4
+ Revises: 3dcc5d20e82f
5
+ Create Date: 2024-08-08 12:25:12.058636
6
+
7
+ """
8
+
9
+ # revision identifiers, used by Alembic.
10
+ revision = "0.64.0"
11
+ down_revision = "3dcc5d20e82f"
12
+ branch_labels = None
13
+ depends_on = None
14
+
15
+
16
+ def upgrade() -> None:
17
+ """Upgrade database schema and/or data, creating a new revision."""
18
+ pass
19
+
20
+
21
+ def downgrade() -> None:
22
+ """Downgrade database schema and/or data back to the previous revision."""
23
+ pass
@@ -0,0 +1,39 @@
1
+ """Add code path [026d4577b6a0].
2
+
3
+ Revision ID: 026d4577b6a0
4
+ Revises: 909550c7c4da
5
+ Create Date: 2024-07-30 16:53:32.777594
6
+
7
+ """
8
+
9
+ import sqlalchemy as sa
10
+ import sqlmodel
11
+ from alembic import op
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision = "026d4577b6a0"
15
+ down_revision = "909550c7c4da"
16
+ branch_labels = None
17
+ depends_on = None
18
+
19
+
20
+ def upgrade() -> None:
21
+ """Upgrade database schema and/or data, creating a new revision."""
22
+ # ### commands auto generated by Alembic - please adjust! ###
23
+ with op.batch_alter_table("pipeline_deployment", schema=None) as batch_op:
24
+ batch_op.add_column(
25
+ sa.Column(
26
+ "code_path", sqlmodel.sql.sqltypes.AutoString(), nullable=True
27
+ )
28
+ )
29
+
30
+ # ### end Alembic commands ###
31
+
32
+
33
+ def downgrade() -> None:
34
+ """Downgrade database schema and/or data back to the previous revision."""
35
+ # ### commands auto generated by Alembic - please adjust! ###
36
+ with op.batch_alter_table("pipeline_deployment", schema=None) as batch_op:
37
+ batch_op.drop_column("code_path")
38
+
39
+ # ### end Alembic commands ###
@@ -0,0 +1,51 @@
1
+ """add last_user_activity [3dcc5d20e82f].
2
+
3
+ Revision ID: 3dcc5d20e82f
4
+ Revises: 909550c7c4da
5
+ Create Date: 2024-08-07 14:49:07.623500
6
+
7
+ """
8
+
9
+ from datetime import datetime, timezone
10
+
11
+ import sqlalchemy as sa
12
+ import sqlmodel
13
+ from alembic import op
14
+
15
+ # revision identifiers, used by Alembic.
16
+ revision = "3dcc5d20e82f"
17
+ down_revision = "026d4577b6a0"
18
+ branch_labels = None
19
+ depends_on = None
20
+
21
+
22
+ def upgrade() -> None:
23
+ """Upgrade database schema and/or data, creating a new revision."""
24
+ bind = op.get_bind()
25
+ session = sqlmodel.Session(bind=bind)
26
+
27
+ with op.batch_alter_table("server_settings", schema=None) as batch_op:
28
+ batch_op.add_column(
29
+ sa.Column("last_user_activity", sa.DateTime(), nullable=True)
30
+ )
31
+
32
+ session.execute(
33
+ sa.text(
34
+ """
35
+ UPDATE server_settings
36
+ SET last_user_activity = :last_user_activity
37
+ """
38
+ ),
39
+ params=(dict(last_user_activity=datetime.now(timezone.utc))),
40
+ )
41
+
42
+ with op.batch_alter_table("server_settings", schema=None) as batch_op:
43
+ batch_op.alter_column(
44
+ "last_user_activity", existing_type=sa.DateTime(), nullable=False
45
+ )
46
+
47
+
48
+ def downgrade() -> None:
49
+ """Downgrade database schema and/or data back to the previous revision."""
50
+ with op.batch_alter_table("server_settings", schema=None) as batch_op:
51
+ batch_op.drop_column("last_user_activity")
@@ -0,0 +1,36 @@
1
+ """Remove user hub token [909550c7c4da].
2
+
3
+ Revision ID: 909550c7c4da
4
+ Revises: 0.63.0
5
+ Create Date: 2024-08-05 16:02:48.990897
6
+
7
+ """
8
+
9
+ import sqlalchemy as sa
10
+ from alembic import op
11
+
12
+ # revision identifiers, used by Alembic.
13
+ revision = "909550c7c4da"
14
+ down_revision = "0.63.0"
15
+ branch_labels = None
16
+ depends_on = None
17
+
18
+
19
+ def upgrade() -> None:
20
+ """Upgrade database schema and/or data, creating a new revision."""
21
+ # ### commands auto generated by Alembic - please adjust! ###
22
+ with op.batch_alter_table("user", schema=None) as batch_op:
23
+ batch_op.drop_column("hub_token")
24
+
25
+ # ### end Alembic commands ###
26
+
27
+
28
+ def downgrade() -> None:
29
+ """Downgrade database schema and/or data back to the previous revision."""
30
+ # ### commands auto generated by Alembic - please adjust! ###
31
+ with op.batch_alter_table("user", schema=None) as batch_op:
32
+ batch_op.add_column(
33
+ sa.Column("hub_token", sa.VARCHAR(), nullable=True)
34
+ )
35
+
36
+ # ### end Alembic commands ###
@@ -4295,7 +4295,7 @@ class RestZenStore(BaseZenStore):
4295
4295
  return self._request(
4296
4296
  "POST",
4297
4297
  self.url + API + VERSION_1 + path,
4298
- data=body.model_dump_json(),
4298
+ json=body.model_dump(mode="json"),
4299
4299
  params=params,
4300
4300
  timeout=timeout,
4301
4301
  **kwargs,
@@ -4322,11 +4322,13 @@ class RestZenStore(BaseZenStore):
4322
4322
  The response body.
4323
4323
  """
4324
4324
  logger.debug(f"Sending PUT request to {path}...")
4325
- data = body.model_dump_json(exclude_unset=True) if body else None
4325
+ json = (
4326
+ body.model_dump(mode="json", exclude_unset=True) if body else None
4327
+ )
4326
4328
  return self._request(
4327
4329
  "PUT",
4328
4330
  self.url + API + VERSION_1 + path,
4329
- data=data,
4331
+ json=json,
4330
4332
  params=params,
4331
4333
  timeout=timeout,
4332
4334
  **kwargs,
@@ -84,6 +84,7 @@ class PipelineDeploymentSchema(BaseSchema, table=True):
84
84
  nullable=True,
85
85
  )
86
86
  )
87
+ code_path: Optional[str] = Field(nullable=True)
87
88
 
88
89
  # Foreign keys
89
90
  user_id: Optional[UUID] = build_foreign_key_field(
@@ -207,6 +208,7 @@ class PipelineDeploymentSchema(BaseSchema, table=True):
207
208
  )
208
209
  if request.pipeline_spec
209
210
  else None,
211
+ code_path=request.code_path,
210
212
  )
211
213
 
212
214
  def to_model(
@@ -261,6 +263,7 @@ class PipelineDeploymentSchema(BaseSchema, table=True):
261
263
  )
262
264
  if self.pipeline_spec
263
265
  else None,
266
+ code_path=self.code_path,
264
267
  template_id=self.template_id,
265
268
  )
266
269
  return PipelineDeploymentResponse(
@@ -322,6 +322,9 @@ class PipelineRunSchema(NamedSchema, table=True):
322
322
  client_environment=client_environment,
323
323
  orchestrator_environment=orchestrator_environment,
324
324
  orchestrator_run_id=self.orchestrator_run_id,
325
+ code_path=self.deployment.code_path
326
+ if self.deployment
327
+ else None,
325
328
  template_id=self.deployment.template_id
326
329
  if self.deployment
327
330
  else None,
@@ -42,6 +42,7 @@ class ServerSettingsSchema(SQLModel, table=True):
42
42
  display_announcements: Optional[bool] = Field(nullable=True)
43
43
  display_updates: Optional[bool] = Field(nullable=True)
44
44
  onboarding_state: Optional[str] = Field(nullable=True)
45
+ last_user_activity: datetime = Field(default_factory=datetime.utcnow)
45
46
  updated: datetime = Field(default_factory=datetime.utcnow)
46
47
 
47
48
  def update(
@@ -111,6 +112,7 @@ class ServerSettingsSchema(SQLModel, table=True):
111
112
  display_updates=self.display_updates,
112
113
  active=self.active,
113
114
  updated=self.updated,
115
+ last_user_activity=self.last_user_activity,
114
116
  )
115
117
 
116
118
  metadata = None
@@ -77,7 +77,6 @@ class UserSchema(NamedSchema, table=True):
77
77
  active: bool
78
78
  password: Optional[str] = Field(nullable=True)
79
79
  activation_token: Optional[str] = Field(nullable=True)
80
- hub_token: Optional[str] = Field(nullable=True)
81
80
  email_opted_in: Optional[bool] = Field(nullable=True)
82
81
  external_user_id: Optional[UUID] = Field(nullable=True)
83
82
  is_admin: bool = Field(default=False)
@@ -281,7 +280,6 @@ class UserSchema(NamedSchema, table=True):
281
280
  if include_metadata:
282
281
  metadata = UserResponseMetadata(
283
282
  email=self.email if include_private else None,
284
- hub_token=self.hub_token if include_private else None,
285
283
  external_user_id=self.external_user_id,
286
284
  user_metadata=json.loads(self.user_metadata)
287
285
  if self.user_metadata
@@ -20,7 +20,7 @@ import math
20
20
  import os
21
21
  import re
22
22
  import sys
23
- from datetime import datetime
23
+ from datetime import datetime, timezone
24
24
  from functools import lru_cache
25
25
  from pathlib import Path
26
26
  from typing import (
@@ -1587,6 +1587,7 @@ class SqlZenStore(BaseZenStore):
1587
1587
  # the one fetched from the global configuration
1588
1588
  model.id = settings.server_id
1589
1589
  model.active = settings.active
1590
+ model.last_user_activity = settings.last_user_activity
1590
1591
  if not handle_bool_env_var(ENV_ZENML_LOCAL_SERVER):
1591
1592
  model.analytics_enabled = settings.enable_analytics
1592
1593
  return model
@@ -1689,6 +1690,29 @@ class SqlZenStore(BaseZenStore):
1689
1690
 
1690
1691
  return settings.to_model(include_metadata=True)
1691
1692
 
1693
+ def _update_last_user_activity_timestamp(
1694
+ self, last_user_activity: datetime
1695
+ ) -> None:
1696
+ """Update the last user activity timestamp.
1697
+
1698
+ Args:
1699
+ last_user_activity: The timestamp of latest user activity
1700
+ traced by server instance.
1701
+ """
1702
+ with Session(self.engine) as session:
1703
+ settings = self._get_server_settings(session=session)
1704
+
1705
+ if last_user_activity < settings.last_user_activity.replace(
1706
+ tzinfo=timezone.utc
1707
+ ):
1708
+ return
1709
+
1710
+ settings.last_user_activity = last_user_activity
1711
+ # `updated` kept intentionally unchanged here
1712
+ session.add(settings)
1713
+ session.commit()
1714
+ session.refresh(settings)
1715
+
1692
1716
  def get_onboarding_state(self) -> List[str]:
1693
1717
  """Get the server onboarding state.
1694
1718
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zenml-nightly
3
- Version: 0.63.0.dev20240801
3
+ Version: 0.64.0.dev20240809
4
4
  Summary: ZenML: Write production-ready ML code.
5
5
  Home-page: https://zenml.io
6
6
  License: Apache-2.0
@@ -140,7 +140,7 @@ Description-Content-Type: text/markdown
140
140
 
141
141
  <div align="center">
142
142
  <img referrerpolicy="no-referrer-when-downgrade" src="https://static.scarf.sh/a.png?x-pxid=0fcbab94-8fbe-4a38-93e8-c2348450a42e" />
143
- <h1 align="center">Create an internal MLOps platform for your entire machine learning team.
143
+ <h1 align="center">Connecting data science teams seamlessly to cloud infrastructure.
144
144
  </h1>
145
145
  </div>
146
146
 
@@ -467,7 +467,7 @@ the Apache License Version 2.0.
467
467
  <a href="https://github.com/zenml-io/zenml-projects">Projects Showcase</a>
468
468
  <br />
469
469
  <br />
470
- 🎉 Version 0.63.0 is out. Check out the release notes
470
+ 🎉 Version 0.64.0 is out. Check out the release notes
471
471
  <a href="https://github.com/zenml-io/zenml/releases">here</a>.
472
472
  <br />
473
473
  🖥️ Download our VS Code Extension <a href="https://marketplace.visualstudio.com/items?itemName=ZenML.zenml-vscode">here</a>.