zenml-nightly 0.62.0.dev20240729__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 (240) hide show
  1. README.md +2 -2
  2. RELEASE_NOTES.md +120 -0
  3. zenml/VERSION +1 -1
  4. zenml/__init__.py +0 -4
  5. zenml/actions/pipeline_run/pipeline_run_action.py +19 -17
  6. zenml/analytics/enums.py +4 -6
  7. zenml/cli/__init__.py +28 -76
  8. zenml/cli/base.py +2 -2
  9. zenml/cli/pipeline.py +54 -61
  10. zenml/cli/stack.py +6 -8
  11. zenml/cli/web_login.py +8 -0
  12. zenml/client.py +232 -103
  13. zenml/config/build_configuration.py +43 -17
  14. zenml/config/compiler.py +14 -22
  15. zenml/config/docker_settings.py +80 -57
  16. zenml/config/pipeline_run_configuration.py +3 -0
  17. zenml/config/server_config.py +3 -0
  18. zenml/config/source.py +60 -1
  19. zenml/constants.py +11 -2
  20. zenml/entrypoints/base_entrypoint_configuration.py +53 -8
  21. zenml/enums.py +4 -1
  22. zenml/environment.py +25 -9
  23. zenml/image_builders/base_image_builder.py +1 -1
  24. zenml/image_builders/build_context.py +25 -72
  25. zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +13 -4
  26. zenml/integrations/azure/__init__.py +4 -0
  27. zenml/integrations/azure/flavors/__init__.py +11 -0
  28. zenml/integrations/azure/flavors/azureml_orchestrator_flavor.py +263 -0
  29. zenml/{_hub → integrations/azure/orchestrators}/__init__.py +7 -2
  30. zenml/integrations/azure/orchestrators/azureml_orchestrator.py +544 -0
  31. zenml/integrations/azure/orchestrators/azureml_orchestrator_entrypoint_config.py +86 -0
  32. zenml/integrations/azure/step_operators/azureml_step_operator.py +3 -0
  33. zenml/integrations/databricks/flavors/databricks_orchestrator_flavor.py +20 -2
  34. zenml/integrations/databricks/orchestrators/databricks_orchestrator.py +19 -13
  35. zenml/integrations/gcp/orchestrators/vertex_orchestrator.py +7 -2
  36. zenml/integrations/gcp/service_connectors/gcp_service_connector.py +123 -6
  37. zenml/integrations/kaniko/image_builders/kaniko_image_builder.py +1 -1
  38. zenml/integrations/mlflow/__init__.py +1 -1
  39. zenml/integrations/mlflow/experiment_trackers/mlflow_experiment_tracker.py +3 -1
  40. zenml/integrations/mlflow/flavors/mlflow_experiment_tracker_flavor.py +3 -0
  41. zenml/logger.py +13 -0
  42. zenml/models/__init__.py +26 -22
  43. zenml/models/v2/base/filter.py +32 -0
  44. zenml/models/v2/core/pipeline.py +73 -89
  45. zenml/models/v2/core/pipeline_build.py +15 -11
  46. zenml/models/v2/core/pipeline_deployment.py +72 -24
  47. zenml/models/v2/core/pipeline_run.py +65 -1
  48. zenml/models/v2/core/run_template.py +393 -0
  49. zenml/models/v2/core/server_settings.py +12 -0
  50. zenml/models/v2/core/user.py +0 -21
  51. zenml/models/v2/misc/server_models.py +7 -1
  52. zenml/models/v2/misc/stack_deployment.py +5 -0
  53. zenml/models/v2/misc/user_auth.py +0 -7
  54. zenml/new/pipelines/build_utils.py +220 -89
  55. zenml/new/pipelines/code_archive.py +157 -0
  56. zenml/new/pipelines/pipeline.py +46 -78
  57. zenml/new/pipelines/run_utils.py +79 -1
  58. zenml/post_execution/pipeline.py +1 -4
  59. zenml/service_connectors/service_connector_utils.py +18 -2
  60. zenml/stack_deployments/aws_stack_deployment.py +32 -8
  61. zenml/stack_deployments/azure_stack_deployment.py +122 -10
  62. zenml/stack_deployments/gcp_stack_deployment.py +36 -7
  63. zenml/stack_deployments/stack_deployment.py +23 -7
  64. zenml/steps/base_step.py +3 -0
  65. zenml/steps/utils.py +0 -4
  66. zenml/utils/archivable.py +149 -0
  67. zenml/utils/code_utils.py +244 -0
  68. zenml/utils/notebook_utils.py +122 -0
  69. zenml/utils/package_utils.py +39 -0
  70. zenml/utils/pipeline_docker_image_builder.py +3 -96
  71. zenml/utils/source_utils.py +109 -1
  72. zenml/zen_server/dashboard/assets/{404-B_YdvmwS.js → 404-CRAA_Lew.js} +1 -1
  73. zenml/zen_server/dashboard/assets/@radix-BXWm7HOa.js +85 -0
  74. zenml/zen_server/dashboard/assets/{@react-router-CO-OsFwI.js → @react-router-l3lMcXA2.js} +1 -1
  75. zenml/zen_server/dashboard/assets/{@reactflow-l_1hUr1S.js → @reactflow-CeVxyqYT.js} +2 -2
  76. zenml/zen_server/dashboard/assets/{@tanstack-DYiOyJUL.js → @tanstack-FmcYZMuX.js} +4 -4
  77. zenml/zen_server/dashboard/assets/AlertDialogDropdownItem-ErO9aOgK.js +1 -0
  78. zenml/zen_server/dashboard/assets/{AwarenessChannel-CFg5iX4Z.js → AwarenessChannel-CLXo5rKM.js} +1 -1
  79. zenml/zen_server/dashboard/assets/{CodeSnippet-Dvkx_82E.js → CodeSnippet-D0VLxT2A.js} +2 -2
  80. zenml/zen_server/dashboard/assets/CollapsibleCard-BaUPiVg0.js +1 -0
  81. zenml/zen_server/dashboard/assets/{Commands-DoN1xrEq.js → Commands-JrcZK-3j.js} +1 -1
  82. zenml/zen_server/dashboard/assets/CopyButton-Dbo52T1K.js +2 -0
  83. zenml/zen_server/dashboard/assets/{CsvVizualization-Ck-nZ43m.js → CsvVizualization-D3kAypDj.js} +3 -3
  84. zenml/zen_server/dashboard/assets/DisplayDate-DizbSeT-.js +1 -0
  85. zenml/zen_server/dashboard/assets/EditSecretDialog-Bd7mFLS4.js +1 -0
  86. zenml/zen_server/dashboard/assets/{EmptyState-BMLnFVlB.js → EmptyState-BHblM39I.js} +1 -1
  87. zenml/zen_server/dashboard/assets/{Error-kLtljEOM.js → Error-C6LeJSER.js} +1 -1
  88. zenml/zen_server/dashboard/assets/{ExecutionStatus-DguLLgTK.js → ExecutionStatus-jH4OrWBq.js} +1 -1
  89. zenml/zen_server/dashboard/assets/{Helpbox-BXUMP21n.js → Helpbox-aAB2XP-z.js} +1 -1
  90. zenml/zen_server/dashboard/assets/{Infobox-DSt0O-dm.js → Infobox-BQ0aty32.js} +1 -1
  91. zenml/zen_server/dashboard/assets/{InlineAvatar-xsrsIGE-.js → InlineAvatar-DpTLgM3Q.js} +1 -1
  92. zenml/zen_server/dashboard/assets/Lock-CNyJvf2r.js +1 -0
  93. zenml/zen_server/dashboard/assets/{MarkdownVisualization-xp3hhULl.js → MarkdownVisualization-Bajxn0HY.js} +1 -1
  94. zenml/zen_server/dashboard/assets/NumberBox-BmKE0qnO.js +1 -0
  95. zenml/zen_server/dashboard/assets/{PasswordChecker-DUveqlva.js → PasswordChecker-yGGoJSB-.js} +1 -1
  96. zenml/zen_server/dashboard/assets/ProviderRadio-BBqkIuTd.js +1 -0
  97. zenml/zen_server/dashboard/assets/RadioItem-xLhXoiFV.js +1 -0
  98. zenml/zen_server/dashboard/assets/SearchField-C9R0mdaX.js +1 -0
  99. zenml/zen_server/dashboard/assets/{SetPassword-BXGTWiwj.js → SetPassword-52sNxNiO.js} +1 -1
  100. zenml/zen_server/dashboard/assets/{SuccessStep-DZC60t0x.js → SuccessStep-DlkItqYG.js} +1 -1
  101. zenml/zen_server/dashboard/assets/Tick-uxv80Q6a.js +1 -0
  102. zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-DGvwFWO1.js → UpdatePasswordSchemas-oN4G3sKz.js} +1 -1
  103. zenml/zen_server/dashboard/assets/{aws-BgKTfTfx.js → aws-0_3UsPif.js} +1 -1
  104. zenml/zen_server/dashboard/assets/{check-circle-i56092KI.js → check-circle-1_I207rW.js} +1 -1
  105. zenml/zen_server/dashboard/assets/chevron-down-BpaF8JqM.js +1 -0
  106. zenml/zen_server/dashboard/assets/{chevron-right-double-CZBOf6JM.js → chevron-right-double-Dk8e2L99.js} +1 -1
  107. zenml/zen_server/dashboard/assets/{cloud-only-C_yFCAkP.js → cloud-only-BkUuI0lZ.js} +1 -1
  108. zenml/zen_server/dashboard/assets/components-Br2ezRib.js +1 -0
  109. zenml/zen_server/dashboard/assets/{copy-BXNk6BjL.js → copy-f3XGPPxt.js} +1 -1
  110. zenml/zen_server/dashboard/assets/{database-1xWSgZfO.js → database-cXYNX9tt.js} +1 -1
  111. zenml/zen_server/dashboard/assets/{docker-CQMVm_4d.js → docker-8uj__HHK.js} +1 -1
  112. zenml/zen_server/dashboard/assets/dots-horizontal-sKQlWEni.js +1 -0
  113. zenml/zen_server/dashboard/assets/edit-C0MVvPD2.js +1 -0
  114. zenml/zen_server/dashboard/assets/{file-text-CqD_iu6l.js → file-text-B9JibxTs.js} +1 -1
  115. zenml/zen_server/dashboard/assets/{help-bu_DgLKI.js → help-FuHlZwn0.js} +1 -1
  116. zenml/zen_server/dashboard/assets/{index-rK_Wuy2W.js → index-Bd1xgUQG.js} +1 -1
  117. zenml/zen_server/dashboard/assets/index-DaGknux4.css +1 -0
  118. zenml/zen_server/dashboard/assets/{index-BczVOqUf.js → index-DhIZtpxB.js} +5 -5
  119. zenml/zen_server/dashboard/assets/index.esm-DT4uyn2i.js +1 -0
  120. zenml/zen_server/dashboard/assets/layout-D6oiSbfd.js +1 -0
  121. zenml/zen_server/dashboard/assets/{login-mutation-CrHrndTI.js → login-mutation-13A_JSVA.js} +1 -1
  122. zenml/zen_server/dashboard/assets/{logs-D8k8BVFf.js → logs-CgeE2vZP.js} +1 -1
  123. zenml/zen_server/dashboard/assets/{not-found-DYa4pC-C.js → not-found-B0Mmb90p.js} +1 -1
  124. zenml/zen_server/dashboard/assets/package-DdkziX79.js +1 -0
  125. zenml/zen_server/dashboard/assets/page-7-v2OBm-.js +1 -0
  126. zenml/zen_server/dashboard/assets/{page-MFQyIJd3.js → page-B3ozwdD1.js} +1 -1
  127. zenml/zen_server/dashboard/assets/{page-BkuQDIf-.js → page-BGwA9B1M.js} +1 -1
  128. zenml/zen_server/dashboard/assets/{page-1iL8aMqs.js → page-BkjAUyTA.js} +1 -1
  129. zenml/zen_server/dashboard/assets/page-BnacgBiy.js +1 -0
  130. zenml/zen_server/dashboard/assets/page-BxF_KMQ3.js +2 -0
  131. zenml/zen_server/dashboard/assets/page-C4POHC0K.js +1 -0
  132. zenml/zen_server/dashboard/assets/page-C9kudd44.js +9 -0
  133. zenml/zen_server/dashboard/assets/page-CA1j3GpJ.js +1 -0
  134. zenml/zen_server/dashboard/assets/page-CCY6yfmu.js +1 -0
  135. zenml/zen_server/dashboard/assets/page-CgTe7Bme.js +1 -0
  136. zenml/zen_server/dashboard/assets/{page-8a4UMKXZ.js → page-Cgn-6v2Y.js} +1 -1
  137. zenml/zen_server/dashboard/assets/page-CxQmQqDw.js +1 -0
  138. zenml/zen_server/dashboard/assets/page-D2Goey3H.js +1 -0
  139. zenml/zen_server/dashboard/assets/page-DLpOnf7u.js +1 -0
  140. zenml/zen_server/dashboard/assets/{page-BhgCDInH.js → page-DSTQnBk-.js} +1 -1
  141. zenml/zen_server/dashboard/assets/{page-1h_sD1jz.js → page-DTysUGOy.js} +1 -1
  142. zenml/zen_server/dashboard/assets/{page-2grKx_MY.js → page-D_EXUFJb.js} +1 -1
  143. zenml/zen_server/dashboard/assets/page-Db15QzsM.js +1 -0
  144. zenml/zen_server/dashboard/assets/{page-BDns21Iz.js → page-DugsjcQ_.js} +1 -1
  145. zenml/zen_server/dashboard/assets/{page-C6-UGEbH.js → page-OFKSPyN7.js} +1 -1
  146. zenml/zen_server/dashboard/assets/{page-BkeAAYwp.js → page-RnG-qhv9.js} +1 -1
  147. zenml/zen_server/dashboard/assets/{page-CCNRIt_f.js → page-T2BtjwPl.js} +1 -1
  148. zenml/zen_server/dashboard/assets/page-TXe1Eo3Z.js +1 -0
  149. zenml/zen_server/dashboard/assets/{page-BnaevhnB.js → page-YiF_fNbe.js} +1 -1
  150. zenml/zen_server/dashboard/assets/{page-uA5prJGY.js → page-hQaiQXfg.js} +1 -1
  151. zenml/zen_server/dashboard/assets/persist-3-5nOJ6m.js +1 -0
  152. zenml/zen_server/dashboard/assets/{play-circle-CNtZKDnW.js → play-circle-XSkLR12B.js} +1 -1
  153. zenml/zen_server/dashboard/assets/plus-FB9-lEq_.js +1 -0
  154. zenml/zen_server/dashboard/assets/refresh-COb6KYDi.js +1 -0
  155. zenml/zen_server/dashboard/assets/sharedSchema-BoYx_B_L.js +14 -0
  156. zenml/zen_server/dashboard/assets/{stack-detail-query-Cficsl6d.js → stack-detail-query-B-US_-wa.js} +1 -1
  157. zenml/zen_server/dashboard/assets/{terminal-By9cErXc.js → terminal-grtjrIEJ.js} +1 -1
  158. zenml/zen_server/dashboard/assets/trash-Cd5CSFqA.js +1 -0
  159. zenml/zen_server/dashboard/assets/{update-server-settings-mutation-7d8xi1tS.js → update-server-settings-mutation-B8GB_ubU.js} +1 -1
  160. zenml/zen_server/dashboard/assets/{url-D7mAQGUM.js → url-hcMJkz8p.js} +1 -1
  161. zenml/zen_server/dashboard/assets/{zod-BhoGpZ63.js → zod-CnykDKJj.js} +1 -1
  162. zenml/zen_server/dashboard/index.html +7 -7
  163. zenml/zen_server/dashboard_legacy/asset-manifest.json +4 -4
  164. zenml/zen_server/dashboard_legacy/index.html +1 -1
  165. zenml/zen_server/dashboard_legacy/{precache-manifest.12246c7548e71e2c4438e496360de80c.js → precache-manifest.9c473c96a43298343a7ce1256183123b.js} +4 -4
  166. zenml/zen_server/dashboard_legacy/service-worker.js +1 -1
  167. zenml/zen_server/dashboard_legacy/static/js/{main.3b27024b.chunk.js → main.463c90b9.chunk.js} +2 -2
  168. zenml/zen_server/dashboard_legacy/static/js/{main.3b27024b.chunk.js.map → main.463c90b9.chunk.js.map} +1 -1
  169. zenml/zen_server/deploy/helm/Chart.yaml +1 -1
  170. zenml/zen_server/deploy/helm/README.md +2 -2
  171. zenml/zen_server/rbac/models.py +1 -0
  172. zenml/zen_server/rbac/utils.py +4 -0
  173. zenml/zen_server/routers/pipeline_builds_endpoints.py +2 -66
  174. zenml/zen_server/routers/pipeline_deployments_endpoints.py +2 -53
  175. zenml/zen_server/routers/pipelines_endpoints.py +1 -74
  176. zenml/zen_server/routers/run_templates_endpoints.py +212 -0
  177. zenml/zen_server/routers/stack_deployment_endpoints.py +6 -0
  178. zenml/zen_server/routers/users_endpoints.py +0 -7
  179. zenml/zen_server/routers/workspaces_endpoints.py +79 -0
  180. zenml/zen_server/{pipeline_deployment → template_execution}/runner_entrypoint_configuration.py +1 -8
  181. zenml/zen_server/{pipeline_deployment → template_execution}/utils.py +214 -92
  182. zenml/zen_server/utils.py +77 -2
  183. zenml/zen_server/zen_server_api.py +54 -2
  184. zenml/zen_stores/base_zen_store.py +7 -1
  185. zenml/zen_stores/migrations/versions/0.63.0_release.py +23 -0
  186. zenml/zen_stores/migrations/versions/0.64.0_release.py +23 -0
  187. zenml/zen_stores/migrations/versions/026d4577b6a0_add_code_path.py +39 -0
  188. zenml/zen_stores/migrations/versions/3dcc5d20e82f_add_last_user_activity.py +51 -0
  189. zenml/zen_stores/migrations/versions/7d1919bb1ef0_add_run_templates.py +100 -0
  190. zenml/zen_stores/migrations/versions/909550c7c4da_remove_user_hub_token.py +36 -0
  191. zenml/zen_stores/migrations/versions/b59aa68fdb1f_simplify_pipelines.py +139 -0
  192. zenml/zen_stores/rest_zen_store.py +112 -39
  193. zenml/zen_stores/schemas/__init__.py +2 -0
  194. zenml/zen_stores/schemas/pipeline_build_schemas.py +3 -3
  195. zenml/zen_stores/schemas/pipeline_deployment_schemas.py +32 -2
  196. zenml/zen_stores/schemas/pipeline_run_schemas.py +29 -3
  197. zenml/zen_stores/schemas/pipeline_schemas.py +29 -30
  198. zenml/zen_stores/schemas/run_template_schemas.py +264 -0
  199. zenml/zen_stores/schemas/server_settings_schemas.py +2 -0
  200. zenml/zen_stores/schemas/step_run_schemas.py +11 -4
  201. zenml/zen_stores/schemas/user_schemas.py +0 -2
  202. zenml/zen_stores/sql_zen_store.py +389 -151
  203. zenml/zen_stores/template_utils.py +261 -0
  204. zenml/zen_stores/zen_store_interface.py +93 -20
  205. {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/METADATA +3 -3
  206. {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/RECORD +211 -184
  207. zenml/_hub/client.py +0 -289
  208. zenml/_hub/constants.py +0 -21
  209. zenml/_hub/utils.py +0 -79
  210. zenml/cli/hub.py +0 -1116
  211. zenml/models/v2/core/pipeline_namespace.py +0 -113
  212. zenml/models/v2/misc/hub_plugin_models.py +0 -79
  213. zenml/new/pipelines/deserialization_utils.py +0 -292
  214. zenml/zen_server/dashboard/assets/@radix-CFOkMR_E.js +0 -85
  215. zenml/zen_server/dashboard/assets/CollapsibleCard-opiuBHHc.js +0 -1
  216. zenml/zen_server/dashboard/assets/CopyButton-Cr7xYEPb.js +0 -2
  217. zenml/zen_server/dashboard/assets/DisplayDate-DYgIjlDF.js +0 -1
  218. zenml/zen_server/dashboard/assets/Pagination-C6X-mifw.js +0 -1
  219. zenml/zen_server/dashboard/assets/index-EpMIKgrI.css +0 -1
  220. zenml/zen_server/dashboard/assets/index.esm-Corw4lXQ.js +0 -1
  221. zenml/zen_server/dashboard/assets/package-B3fWP-Dh.js +0 -1
  222. zenml/zen_server/dashboard/assets/page-5NCOHOsy.js +0 -1
  223. zenml/zen_server/dashboard/assets/page-B6h3iaHJ.js +0 -1
  224. zenml/zen_server/dashboard/assets/page-Bi-wtWiO.js +0 -5
  225. zenml/zen_server/dashboard/assets/page-Bq0YxkLV.js +0 -1
  226. zenml/zen_server/dashboard/assets/page-Bs2F4eoD.js +0 -2
  227. zenml/zen_server/dashboard/assets/page-CHNxpz3n.js +0 -1
  228. zenml/zen_server/dashboard/assets/page-DgorQFqi.js +0 -1
  229. zenml/zen_server/dashboard/assets/page-K8ebxVIs.js +0 -1
  230. zenml/zen_server/dashboard/assets/page-TgCF0P_U.js +0 -1
  231. zenml/zen_server/dashboard/assets/page-ZnCEe-eK.js +0 -9
  232. zenml/zen_server/dashboard/assets/persist-D7HJNBWx.js +0 -1
  233. zenml/zen_server/dashboard/assets/plus-C8WOyCzt.js +0 -1
  234. zenml/zen_server/dashboard/assets/secrets-video-OBJ6irhH.svg +0 -21
  235. zenml/zen_server/dashboard/assets/stacks-video-7gfxpAq4.svg +0 -21
  236. /zenml/zen_server/{pipeline_deployment → template_execution}/__init__.py +0 -0
  237. /zenml/zen_server/{pipeline_deployment → template_execution}/workload_manager_interface.py +0 -0
  238. {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/LICENSE +0 -0
  239. {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/WHEEL +0 -0
  240. {zenml_nightly-0.62.0.dev20240729.dist-info → zenml_nightly-0.64.0.dev20240809.dist-info}/entry_points.txt +0 -0
@@ -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 (
@@ -52,7 +52,7 @@ from pydantic import (
52
52
  field_validator,
53
53
  model_validator,
54
54
  )
55
- from sqlalchemy import asc, desc, func
55
+ from sqlalchemy import asc, case, desc, func
56
56
  from sqlalchemy.engine import URL, Engine, make_url
57
57
  from sqlalchemy.exc import (
58
58
  ArgumentError,
@@ -96,6 +96,7 @@ from zenml.constants import (
96
96
  ENV_ZENML_LOCAL_SERVER,
97
97
  ENV_ZENML_SERVER,
98
98
  FINISHED_ONBOARDING_SURVEY_KEY,
99
+ SORT_PIPELINES_BY_LATEST_RUN_KEY,
99
100
  SQL_STORE_BACKUP_DIRECTORY_NAME,
100
101
  TEXT_FIELD_MAX_LENGTH,
101
102
  handle_bool_env_var,
@@ -206,9 +207,6 @@ from zenml.models import (
206
207
  PipelineDeploymentRequest,
207
208
  PipelineDeploymentResponse,
208
209
  PipelineFilter,
209
- PipelineNamespaceFilter,
210
- PipelineNamespaceResponse,
211
- PipelineNamespaceResponseBody,
212
210
  PipelineRequest,
213
211
  PipelineResponse,
214
212
  PipelineRunFilter,
@@ -219,6 +217,10 @@ from zenml.models import (
219
217
  RunMetadataFilter,
220
218
  RunMetadataRequest,
221
219
  RunMetadataResponse,
220
+ RunTemplateFilter,
221
+ RunTemplateRequest,
222
+ RunTemplateResponse,
223
+ RunTemplateUpdate,
222
224
  ScheduleFilter,
223
225
  ScheduleRequest,
224
226
  ScheduleResponse,
@@ -294,6 +296,7 @@ from zenml.utils.networking_utils import (
294
296
  )
295
297
  from zenml.utils.pydantic_utils import before_validator_handler
296
298
  from zenml.utils.string_utils import random_str, validate_name
299
+ from zenml.zen_stores import template_utils
297
300
  from zenml.zen_stores.base_zen_store import (
298
301
  BaseZenStore,
299
302
  )
@@ -322,6 +325,7 @@ from zenml.zen_stores.schemas import (
322
325
  PipelineRunSchema,
323
326
  PipelineSchema,
324
327
  RunMetadataSchema,
328
+ RunTemplateSchema,
325
329
  ScheduleSchema,
326
330
  SecretSchema,
327
331
  ServerSettingsSchema,
@@ -991,16 +995,7 @@ class SqlZenStore(BaseZenStore):
991
995
  total = 0
992
996
 
993
997
  # Sorting
994
- column, operand = filter_model.sorting_params
995
- if operand == SorterOps.DESCENDING:
996
- sort_clause = desc(getattr(table, column)) # type: ignore[var-annotated]
997
- else:
998
- sort_clause = asc(getattr(table, column))
999
-
1000
- # We always add the `id` column as a tiebreaker to ensure a stable,
1001
- # repeatable order of items, otherwise subsequent pages might contain
1002
- # the same items.
1003
- query = query.order_by(sort_clause, asc(table.id)) # type: ignore[arg-type]
998
+ query = filter_model.apply_sorting(query=query, table=table)
1004
999
 
1005
1000
  # Get the total amount of pages in the database for a given query
1006
1001
  if total == 0:
@@ -1040,7 +1035,9 @@ class SqlZenStore(BaseZenStore):
1040
1035
  # Otherwise, try to use the `to_model` method of the schema.
1041
1036
  to_model = getattr(schema, "to_model", None)
1042
1037
  if callable(to_model):
1043
- items.append(to_model(include_metadata=hydrate))
1038
+ items.append(
1039
+ to_model(include_metadata=hydrate, include_resources=True)
1040
+ )
1044
1041
  continue
1045
1042
  # If neither of the above work, raise an error.
1046
1043
  raise RuntimeError(
@@ -1590,6 +1587,7 @@ class SqlZenStore(BaseZenStore):
1590
1587
  # the one fetched from the global configuration
1591
1588
  model.id = settings.server_id
1592
1589
  model.active = settings.active
1590
+ model.last_user_activity = settings.last_user_activity
1593
1591
  if not handle_bool_env_var(ENV_ZENML_LOCAL_SERVER):
1594
1592
  model.analytics_enabled = settings.enable_analytics
1595
1593
  return model
@@ -1692,6 +1690,29 @@ class SqlZenStore(BaseZenStore):
1692
1690
 
1693
1691
  return settings.to_model(include_metadata=True)
1694
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
+
1695
1716
  def get_onboarding_state(self) -> List[str]:
1696
1717
  """Get the server onboarding state.
1697
1718
 
@@ -1916,7 +1937,7 @@ class SqlZenStore(BaseZenStore):
1916
1937
  action = self._get_action(action_id=action_id, session=session)
1917
1938
 
1918
1939
  return action.to_model(
1919
- include_metadata=hydrate, include_resources=hydrate
1940
+ include_metadata=hydrate, include_resources=True
1920
1941
  )
1921
1942
 
1922
1943
  def list_actions(
@@ -2427,7 +2448,7 @@ class SqlZenStore(BaseZenStore):
2427
2448
  "service with this ID found."
2428
2449
  )
2429
2450
  return service.to_model(
2430
- include_metadata=hydrate, include_resources=hydrate
2451
+ include_metadata=hydrate, include_resources=True
2431
2452
  )
2432
2453
 
2433
2454
  def list_services(
@@ -2766,7 +2787,7 @@ class SqlZenStore(BaseZenStore):
2766
2787
  f"found."
2767
2788
  )
2768
2789
  return artifact_version.to_model(
2769
- include_metadata=hydrate, include_resources=hydrate
2790
+ include_metadata=hydrate, include_resources=True
2770
2791
  )
2771
2792
 
2772
2793
  def list_artifact_versions(
@@ -3984,23 +4005,32 @@ class SqlZenStore(BaseZenStore):
3984
4005
  existing_pipeline = session.exec(
3985
4006
  select(PipelineSchema)
3986
4007
  .where(PipelineSchema.name == pipeline.name)
3987
- .where(PipelineSchema.version_hash == pipeline.version_hash)
3988
4008
  .where(PipelineSchema.workspace_id == pipeline.workspace)
3989
4009
  ).first()
3990
4010
  if existing_pipeline is not None:
3991
4011
  raise EntityExistsError(
3992
4012
  f"Unable to create pipeline in workspace "
3993
- f"'{pipeline.workspace}': A pipeline with this name and "
3994
- f"version already exists."
4013
+ f"'{pipeline.workspace}': A pipeline with this name "
4014
+ "already exists."
3995
4015
  )
3996
4016
 
3997
4017
  # Create the pipeline
3998
4018
  new_pipeline = PipelineSchema.from_request(pipeline)
4019
+
4020
+ if pipeline.tags:
4021
+ self._attach_tags_to_resource(
4022
+ tag_names=pipeline.tags,
4023
+ resource_id=new_pipeline.id,
4024
+ resource_type=TaggableResourceTypes.PIPELINE,
4025
+ )
4026
+
3999
4027
  session.add(new_pipeline)
4000
4028
  session.commit()
4001
4029
  session.refresh(new_pipeline)
4002
4030
 
4003
- return new_pipeline.to_model(include_metadata=True)
4031
+ return new_pipeline.to_model(
4032
+ include_metadata=True, include_resources=True
4033
+ )
4004
4034
 
4005
4035
  def get_pipeline(
4006
4036
  self, pipeline_id: UUID, hydrate: bool = True
@@ -4029,80 +4059,8 @@ class SqlZenStore(BaseZenStore):
4029
4059
  "No pipeline with this ID found."
4030
4060
  )
4031
4061
 
4032
- return pipeline.to_model(include_metadata=hydrate)
4033
-
4034
- def list_pipeline_namespaces(
4035
- self,
4036
- filter_model: PipelineNamespaceFilter,
4037
- hydrate: bool = False,
4038
- ) -> Page[PipelineNamespaceResponse]:
4039
- """List all pipeline namespaces matching the given filter criteria.
4040
-
4041
- Args:
4042
- filter_model: All filter parameters including pagination
4043
- params.
4044
- hydrate: Flag deciding whether to hydrate the output model(s)
4045
- by including metadata fields in the response.
4046
-
4047
- Returns:
4048
- A list of all pipeline namespaces matching the filter criteria.
4049
- """
4050
-
4051
- def _custom_conversion(
4052
- row: Tuple[str, UUID, str],
4053
- ) -> PipelineNamespaceResponse:
4054
- name, latest_run_id, latest_run_status = row
4055
-
4056
- body = PipelineNamespaceResponseBody(
4057
- latest_run_id=latest_run_id,
4058
- latest_run_status=latest_run_status,
4059
- )
4060
-
4061
- return PipelineNamespaceResponse(name=name, body=body)
4062
-
4063
- def _custom_fetch(
4064
- session: Session,
4065
- query: Union[Select[Any], SelectOfScalar[Any]],
4066
- filter: BaseFilter,
4067
- ) -> Sequence[Any]:
4068
- return session.exec(query).all()
4069
-
4070
- with Session(self.engine) as session:
4071
- max_date_subquery = (
4072
- select(
4073
- PipelineSchema.name,
4074
- func.max(PipelineRunSchema.created).label("max_created"),
4075
- )
4076
- .outerjoin(
4077
- PipelineRunSchema,
4078
- PipelineSchema.id == PipelineRunSchema.pipeline_id, # type: ignore[arg-type]
4079
- )
4080
- .group_by(PipelineSchema.name)
4081
- .subquery()
4082
- )
4083
-
4084
- query = (
4085
- select(
4086
- max_date_subquery.c.name,
4087
- PipelineRunSchema.id,
4088
- PipelineRunSchema.status,
4089
- )
4090
- .outerjoin(
4091
- PipelineRunSchema,
4092
- PipelineRunSchema.created # type: ignore[arg-type]
4093
- == max_date_subquery.c.max_created,
4094
- )
4095
- .order_by(desc(PipelineRunSchema.updated)) # type: ignore[arg-type]
4096
- )
4097
-
4098
- return self.filter_and_paginate(
4099
- session=session,
4100
- query=query,
4101
- table=PipelineSchema,
4102
- filter_model=filter_model,
4103
- hydrate=hydrate,
4104
- custom_fetch=_custom_fetch,
4105
- custom_schema_to_model_conversion=_custom_conversion,
4062
+ return pipeline.to_model(
4063
+ include_metadata=hydrate, include_resources=True
4106
4064
  )
4107
4065
 
4108
4066
  def list_pipelines(
@@ -4121,8 +4079,48 @@ class SqlZenStore(BaseZenStore):
4121
4079
  Returns:
4122
4080
  A list of all pipelines matching the filter criteria.
4123
4081
  """
4082
+ query = select(PipelineSchema)
4083
+
4084
+ column, operand = pipeline_filter_model.sorting_params
4085
+ if column == SORT_PIPELINES_BY_LATEST_RUN_KEY:
4086
+ with Session(self.engine) as session:
4087
+ max_date_subquery = (
4088
+ # If no run exists for the pipeline yet, we use the pipeline
4089
+ # creation date as a fallback, otherwise newly created
4090
+ # pipeline would always be at the top/bottom
4091
+ select(
4092
+ PipelineSchema.id,
4093
+ case(
4094
+ (
4095
+ func.max(PipelineRunSchema.created).is_(None),
4096
+ PipelineSchema.created,
4097
+ ),
4098
+ else_=func.max(PipelineRunSchema.created),
4099
+ ).label("run_or_created"),
4100
+ )
4101
+ .outerjoin(
4102
+ PipelineRunSchema,
4103
+ PipelineSchema.id == PipelineRunSchema.pipeline_id, # type: ignore[arg-type]
4104
+ )
4105
+ .group_by(col(PipelineSchema.id))
4106
+ .subquery()
4107
+ )
4108
+
4109
+ if operand == SorterOps.DESCENDING:
4110
+ sort_clause = desc
4111
+ else:
4112
+ sort_clause = asc
4113
+
4114
+ query = (
4115
+ query.where(PipelineSchema.id == max_date_subquery.c.id)
4116
+ .order_by(sort_clause(max_date_subquery.c.run_or_created))
4117
+ # We always add the `id` column as a tiebreaker to ensure a
4118
+ # stable, repeatable order of items, otherwise subsequent
4119
+ # pages might contain the same items.
4120
+ .order_by(col(PipelineSchema.id))
4121
+ )
4122
+
4124
4123
  with Session(self.engine) as session:
4125
- query = select(PipelineSchema)
4126
4124
  return self.filter_and_paginate(
4127
4125
  session=session,
4128
4126
  query=query,
@@ -4172,13 +4170,29 @@ class SqlZenStore(BaseZenStore):
4172
4170
  f"No pipeline with this ID found."
4173
4171
  )
4174
4172
 
4175
- # Update the pipeline
4176
- existing_pipeline.update(pipeline_update)
4173
+ if pipeline_update.add_tags:
4174
+ self._attach_tags_to_resource(
4175
+ tag_names=pipeline_update.add_tags,
4176
+ resource_id=existing_pipeline.id,
4177
+ resource_type=TaggableResourceTypes.PIPELINE,
4178
+ )
4179
+ pipeline_update.add_tags = None
4180
+ if pipeline_update.remove_tags:
4181
+ self._detach_tags_from_resource(
4182
+ tag_names=pipeline_update.remove_tags,
4183
+ resource_id=existing_pipeline.id,
4184
+ resource_type=TaggableResourceTypes.PIPELINE,
4185
+ )
4186
+ pipeline_update.remove_tags = None
4177
4187
 
4188
+ existing_pipeline.update(pipeline_update)
4178
4189
  session.add(existing_pipeline)
4179
4190
  session.commit()
4191
+ session.refresh(existing_pipeline)
4180
4192
 
4181
- return existing_pipeline.to_model(include_metadata=True)
4193
+ return existing_pipeline.to_model(
4194
+ include_metadata=True, include_resources=True
4195
+ )
4182
4196
 
4183
4197
  def delete_pipeline(self, pipeline_id: UUID) -> None:
4184
4198
  """Deletes a pipeline.
@@ -4308,24 +4322,6 @@ class SqlZenStore(BaseZenStore):
4308
4322
  session.delete(build)
4309
4323
  session.commit()
4310
4324
 
4311
- def run_build(
4312
- self,
4313
- build_id: UUID,
4314
- run_configuration: Optional[PipelineRunConfiguration] = None,
4315
- ) -> NoReturn:
4316
- """Run a pipeline from a build.
4317
-
4318
- Args:
4319
- build_id: The ID of the build to run.
4320
- run_configuration: Configuration for the run.
4321
-
4322
- Raises:
4323
- NotImplementedError: Always.
4324
- """
4325
- raise NotImplementedError(
4326
- "Running a build is not possible with a local store."
4327
- )
4328
-
4329
4325
  # -------------------------- Pipeline Deployments --------------------------
4330
4326
 
4331
4327
  def create_deployment(
@@ -4436,37 +4432,235 @@ class SqlZenStore(BaseZenStore):
4436
4432
  )
4437
4433
 
4438
4434
  session.delete(deployment)
4435
+ session.commit()
4439
4436
 
4440
- # Look for all pipeline builds that reference this deployment
4441
- # and remove the reference
4442
- pipeline_builds = session.exec(
4443
- select(PipelineBuildSchema).where(
4444
- PipelineBuildSchema.template_deployment_id == deployment_id
4437
+ # -------------------- Run templates --------------------
4438
+
4439
+ @track_decorator(AnalyticsEvent.CREATED_RUN_TEMPLATE)
4440
+ def create_run_template(
4441
+ self,
4442
+ template: RunTemplateRequest,
4443
+ ) -> RunTemplateResponse:
4444
+ """Create a new run template.
4445
+
4446
+ Args:
4447
+ template: The template to create.
4448
+
4449
+ Returns:
4450
+ The newly created template.
4451
+
4452
+ Raises:
4453
+ EntityExistsError: If a template with the same name already exists.
4454
+ ValueError: If the source deployment does not exist or does not
4455
+ have an associated build.
4456
+ """
4457
+ with Session(self.engine) as session:
4458
+ existing_template = session.exec(
4459
+ select(RunTemplateSchema)
4460
+ .where(RunTemplateSchema.name == template.name)
4461
+ .where(RunTemplateSchema.workspace_id == template.workspace)
4462
+ ).first()
4463
+ if existing_template is not None:
4464
+ raise EntityExistsError(
4465
+ f"Unable to create run template in workspace "
4466
+ f"'{existing_template.workspace.name}': A run template "
4467
+ "with this name already exists."
4445
4468
  )
4446
- ).all()
4447
4469
 
4448
- for pipeline_build in pipeline_builds:
4449
- pipeline_build.template_deployment_id = None
4450
- session.add(pipeline_build)
4470
+ deployment = session.exec(
4471
+ select(PipelineDeploymentSchema).where(
4472
+ PipelineDeploymentSchema.id
4473
+ == template.source_deployment_id
4474
+ )
4475
+ ).first()
4476
+ if not deployment:
4477
+ raise ValueError(
4478
+ f"Source deployment {template.source_deployment_id} not "
4479
+ "found."
4480
+ )
4481
+
4482
+ template_utils.validate_deployment_is_templatable(deployment)
4483
+
4484
+ template_schema = RunTemplateSchema.from_request(request=template)
4451
4485
 
4486
+ if template.tags:
4487
+ self._attach_tags_to_resource(
4488
+ tag_names=template.tags,
4489
+ resource_id=template_schema.id,
4490
+ resource_type=TaggableResourceTypes.RUN_TEMPLATE,
4491
+ )
4492
+
4493
+ session.add(template_schema)
4452
4494
  session.commit()
4495
+ session.refresh(template_schema)
4496
+
4497
+ return template_schema.to_model(
4498
+ include_metadata=True, include_resources=True
4499
+ )
4500
+
4501
+ def get_run_template(
4502
+ self, template_id: UUID, hydrate: bool = True
4503
+ ) -> RunTemplateResponse:
4504
+ """Get a run template with a given ID.
4505
+
4506
+ Args:
4507
+ template_id: ID of the template.
4508
+ hydrate: Flag deciding whether to hydrate the output model(s)
4509
+ by including metadata fields in the response.
4510
+
4511
+ Returns:
4512
+ The template.
4513
+
4514
+ Raises:
4515
+ KeyError: If the template does not exist.
4516
+ """
4517
+ with Session(self.engine) as session:
4518
+ template = session.exec(
4519
+ select(RunTemplateSchema).where(
4520
+ RunTemplateSchema.id == template_id
4521
+ )
4522
+ ).first()
4523
+ if template is None:
4524
+ raise KeyError(
4525
+ f"Unable to get run template with ID {template_id}: "
4526
+ f"No run template with this ID found."
4527
+ )
4528
+
4529
+ return template.to_model(
4530
+ include_metadata=hydrate, include_resources=True
4531
+ )
4532
+
4533
+ def list_run_templates(
4534
+ self,
4535
+ template_filter_model: RunTemplateFilter,
4536
+ hydrate: bool = False,
4537
+ ) -> Page[RunTemplateResponse]:
4538
+ """List all run templates matching the given filter criteria.
4539
+
4540
+ Args:
4541
+ template_filter_model: All filter parameters including pagination
4542
+ params.
4543
+ hydrate: Flag deciding whether to hydrate the output model(s)
4544
+ by including metadata fields in the response.
4453
4545
 
4454
- def run_deployment(
4546
+ Returns:
4547
+ A list of all templates matching the filter criteria.
4548
+ """
4549
+ with Session(self.engine) as session:
4550
+ query = select(RunTemplateSchema)
4551
+ return self.filter_and_paginate(
4552
+ session=session,
4553
+ query=query,
4554
+ table=RunTemplateSchema,
4555
+ filter_model=template_filter_model,
4556
+ hydrate=hydrate,
4557
+ )
4558
+
4559
+ def update_run_template(
4455
4560
  self,
4456
- deployment_id: UUID,
4561
+ template_id: UUID,
4562
+ template_update: RunTemplateUpdate,
4563
+ ) -> RunTemplateResponse:
4564
+ """Updates a run template.
4565
+
4566
+ Args:
4567
+ template_id: The ID of the template to update.
4568
+ template_update: The update to apply.
4569
+
4570
+ Returns:
4571
+ The updated template.
4572
+
4573
+ Raises:
4574
+ KeyError: If the template does not exist.
4575
+ """
4576
+ with Session(self.engine) as session:
4577
+ template = session.exec(
4578
+ select(RunTemplateSchema).where(
4579
+ RunTemplateSchema.id == template_id
4580
+ )
4581
+ ).first()
4582
+ if template is None:
4583
+ raise KeyError(
4584
+ f"Unable to update run template with ID {template_id}: "
4585
+ f"No run template with this ID found."
4586
+ )
4587
+
4588
+ if template_update.add_tags:
4589
+ self._attach_tags_to_resource(
4590
+ tag_names=template_update.add_tags,
4591
+ resource_id=template.id,
4592
+ resource_type=TaggableResourceTypes.RUN_TEMPLATE,
4593
+ )
4594
+ template_update.add_tags = None
4595
+
4596
+ if template_update.remove_tags:
4597
+ self._detach_tags_from_resource(
4598
+ tag_names=template_update.remove_tags,
4599
+ resource_id=template.id,
4600
+ resource_type=TaggableResourceTypes.RUN_TEMPLATE,
4601
+ )
4602
+ template_update.remove_tags = None
4603
+
4604
+ template.update(template_update)
4605
+ session.add(template)
4606
+ session.commit()
4607
+ session.refresh(template)
4608
+
4609
+ return template.to_model(
4610
+ include_metadata=True, include_resources=True
4611
+ )
4612
+
4613
+ def delete_run_template(self, template_id: UUID) -> None:
4614
+ """Delete a run template.
4615
+
4616
+ Args:
4617
+ template_id: The ID of the template to delete.
4618
+
4619
+ Raises:
4620
+ KeyError: If the template does not exist.
4621
+ """
4622
+ with Session(self.engine) as session:
4623
+ template = session.exec(
4624
+ select(RunTemplateSchema).where(
4625
+ RunTemplateSchema.id == template_id
4626
+ )
4627
+ ).first()
4628
+ if template is None:
4629
+ raise KeyError(
4630
+ f"Unable to delete run template with ID {template_id}: "
4631
+ f"No run template with this ID found."
4632
+ )
4633
+
4634
+ session.delete(template)
4635
+ # We set the reference of all deployments to this template to null
4636
+ # manually as we can't have a foreign key there to avoid a cycle
4637
+ deployments = session.exec(
4638
+ select(PipelineDeploymentSchema).where(
4639
+ PipelineDeploymentSchema.template_id == template_id
4640
+ )
4641
+ ).all()
4642
+ for deployment in deployments:
4643
+ deployment.template_id = None
4644
+ session.add(deployment)
4645
+
4646
+ session.commit()
4647
+
4648
+ def run_template(
4649
+ self,
4650
+ template_id: UUID,
4457
4651
  run_configuration: Optional[PipelineRunConfiguration] = None,
4458
4652
  ) -> NoReturn:
4459
- """Run a pipeline from a deployment.
4653
+ """Run a template.
4460
4654
 
4461
4655
  Args:
4462
- deployment_id: The ID of the deployment to run.
4656
+ template_id: The ID of the template to run.
4463
4657
  run_configuration: Configuration for the run.
4464
4658
 
4465
4659
  Raises:
4466
4660
  NotImplementedError: Always.
4467
4661
  """
4468
4662
  raise NotImplementedError(
4469
- "Running a deployment is not possible with a local store."
4663
+ "Running a template is not possible with a local store."
4470
4664
  )
4471
4665
 
4472
4666
  # -------------------- Event Sources --------------------
@@ -4563,7 +4757,7 @@ class SqlZenStore(BaseZenStore):
4563
4757
  with Session(self.engine) as session:
4564
4758
  return self._get_event_source(
4565
4759
  event_source_id=event_source_id, session=session
4566
- ).to_model(include_metadata=hydrate, include_resources=hydrate)
4760
+ ).to_model(include_metadata=hydrate, include_resources=True)
4567
4761
 
4568
4762
  def list_event_sources(
4569
4763
  self,
@@ -4681,10 +4875,20 @@ class SqlZenStore(BaseZenStore):
4681
4875
 
4682
4876
  # Create the pipeline run
4683
4877
  new_run = PipelineRunSchema.from_request(pipeline_run)
4878
+
4879
+ if pipeline_run.tags:
4880
+ self._attach_tags_to_resource(
4881
+ tag_names=pipeline_run.tags,
4882
+ resource_id=new_run.id,
4883
+ resource_type=TaggableResourceTypes.PIPELINE_RUN,
4884
+ )
4885
+
4684
4886
  session.add(new_run)
4685
4887
  session.commit()
4686
4888
 
4687
- return new_run.to_model(include_metadata=True)
4889
+ return new_run.to_model(
4890
+ include_metadata=True, include_resources=True
4891
+ )
4688
4892
 
4689
4893
  def get_run(
4690
4894
  self, run_name_or_id: Union[str, UUID], hydrate: bool = True
@@ -4702,7 +4906,7 @@ class SqlZenStore(BaseZenStore):
4702
4906
  with Session(self.engine) as session:
4703
4907
  return self._get_run_schema(
4704
4908
  run_name_or_id, session=session
4705
- ).to_model(include_metadata=hydrate, include_resources=hydrate)
4909
+ ).to_model(include_metadata=hydrate, include_resources=True)
4706
4910
 
4707
4911
  def _replace_placeholder_run(
4708
4912
  self,
@@ -4751,10 +4955,20 @@ class SqlZenStore(BaseZenStore):
4751
4955
  if pre_replacement_hook:
4752
4956
  pre_replacement_hook()
4753
4957
  run_schema.update_placeholder(pipeline_run)
4958
+
4959
+ if pipeline_run.tags:
4960
+ self._attach_tags_to_resource(
4961
+ tag_names=pipeline_run.tags,
4962
+ resource_id=run_schema.id,
4963
+ resource_type=TaggableResourceTypes.PIPELINE_RUN,
4964
+ )
4965
+
4754
4966
  session.add(run_schema)
4755
4967
  session.commit()
4756
4968
 
4757
- return run_schema.to_model(include_metadata=True)
4969
+ return run_schema.to_model(
4970
+ include_metadata=True, include_resources=True
4971
+ )
4758
4972
 
4759
4973
  def _get_run_by_orchestrator_run_id(
4760
4974
  self, orchestrator_run_id: str, deployment_id: UUID
@@ -4788,7 +5002,9 @@ class SqlZenStore(BaseZenStore):
4788
5002
  f"{orchestrator_run_id} and deployment ID {deployment_id}."
4789
5003
  )
4790
5004
 
4791
- return run_schema.to_model(include_metadata=True)
5005
+ return run_schema.to_model(
5006
+ include_metadata=True, include_resources=True
5007
+ )
4792
5008
 
4793
5009
  def get_or_create_run(
4794
5010
  self,
@@ -4949,13 +5165,29 @@ class SqlZenStore(BaseZenStore):
4949
5165
  f"No pipeline run with this ID found."
4950
5166
  )
4951
5167
 
4952
- # Update the pipeline run
5168
+ if run_update.add_tags:
5169
+ self._attach_tags_to_resource(
5170
+ tag_names=run_update.add_tags,
5171
+ resource_id=existing_run.id,
5172
+ resource_type=TaggableResourceTypes.PIPELINE_RUN,
5173
+ )
5174
+ run_update.add_tags = None
5175
+ if run_update.remove_tags:
5176
+ self._detach_tags_from_resource(
5177
+ tag_names=run_update.remove_tags,
5178
+ resource_id=existing_run.id,
5179
+ resource_type=TaggableResourceTypes.PIPELINE_RUN,
5180
+ )
5181
+ run_update.remove_tags = None
5182
+
4953
5183
  existing_run.update(run_update=run_update)
4954
5184
  session.add(existing_run)
4955
5185
  session.commit()
4956
5186
 
4957
5187
  session.refresh(existing_run)
4958
- return existing_run.to_model(include_metadata=True)
5188
+ return existing_run.to_model(
5189
+ include_metadata=True, include_resources=True
5190
+ )
4959
5191
 
4960
5192
  def delete_run(self, run_id: UUID) -> None:
4961
5193
  """Deletes a pipeline run.
@@ -7086,12 +7318,16 @@ class SqlZenStore(BaseZenStore):
7086
7318
  )
7087
7319
  is not False
7088
7320
  ):
7321
+ connector_config = (
7322
+ existing_service_connector.configuration
7323
+ )
7324
+ connector_config["generate_temporary_tokens"] = (
7325
+ False
7326
+ )
7089
7327
  self.update_service_connector(
7090
7328
  existing_service_connector.id,
7091
7329
  ServiceConnectorUpdate(
7092
- configuration=existing_service_connector.configuration.update(
7093
- {"generate_temporary_tokens": False}
7094
- )
7330
+ configuration=connector_config
7095
7331
  ),
7096
7332
  )
7097
7333
  service_connectors.append(
@@ -7100,17 +7336,18 @@ class SqlZenStore(BaseZenStore):
7100
7336
  # Create a new service connector
7101
7337
  else:
7102
7338
  connector_name = full_stack.name
7339
+ connector_config = connector_id_or_info.configuration
7340
+ connector_config[
7341
+ "generate_temporary_tokens"
7342
+ ] = not need_to_generate_permanent_tokens
7343
+
7103
7344
  while True:
7104
7345
  try:
7105
7346
  service_connector_request = ServiceConnectorRequest(
7106
7347
  name=connector_name,
7107
7348
  connector_type=connector_id_or_info.type,
7108
7349
  auth_method=connector_id_or_info.auth_method,
7109
- configuration=connector_id_or_info.configuration.update(
7110
- {
7111
- "generate_temporary_tokens": not need_to_generate_permanent_tokens
7112
- }
7113
- ),
7350
+ configuration=connector_config,
7114
7351
  user=full_stack.user,
7115
7352
  workspace=full_stack.workspace,
7116
7353
  labels={
@@ -7739,7 +7976,7 @@ class SqlZenStore(BaseZenStore):
7739
7976
  "run with this ID found."
7740
7977
  )
7741
7978
  return step_run.to_model(
7742
- include_metadata=hydrate, include_resources=hydrate
7979
+ include_metadata=hydrate, include_resources=True
7743
7980
  )
7744
7981
 
7745
7982
  def list_run_steps(
@@ -8087,6 +8324,7 @@ class SqlZenStore(BaseZenStore):
8087
8324
  ) as analytics_handler:
8088
8325
  analytics_handler.metadata = {
8089
8326
  "pipeline_run_id": pipeline_run_id,
8327
+ "template_id": pipeline_run.deployment.template_id,
8090
8328
  "status": new_status,
8091
8329
  "num_steps": num_steps,
8092
8330
  "start_time": start_time_str,
@@ -8183,7 +8421,7 @@ class SqlZenStore(BaseZenStore):
8183
8421
  if trigger is None:
8184
8422
  raise KeyError(f"Trigger with ID {trigger_id} not found.")
8185
8423
  return trigger.to_model(
8186
- include_metadata=hydrate, include_resources=hydrate
8424
+ include_metadata=hydrate, include_resources=True
8187
8425
  )
8188
8426
 
8189
8427
  def list_triggers(
@@ -9772,7 +10010,7 @@ class SqlZenStore(BaseZenStore):
9772
10010
  f"ID found."
9773
10011
  )
9774
10012
  return model_version.to_model(
9775
- include_metadata=hydrate, include_resources=hydrate
10013
+ include_metadata=hydrate, include_resources=True
9776
10014
  )
9777
10015
 
9778
10016
  def list_model_versions(