zenml-nightly 0.70.0.dev20241125__py3-none-any.whl → 0.71.0.dev20241220__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 (254) hide show
  1. README.md +4 -4
  2. RELEASE_NOTES.md +112 -0
  3. zenml/VERSION +1 -1
  4. zenml/artifact_stores/base_artifact_store.py +2 -2
  5. zenml/artifacts/artifact_config.py +15 -6
  6. zenml/artifacts/utils.py +59 -32
  7. zenml/cli/__init__.py +22 -4
  8. zenml/cli/base.py +5 -5
  9. zenml/cli/login.py +26 -0
  10. zenml/cli/pipeline.py +111 -62
  11. zenml/cli/server.py +20 -20
  12. zenml/cli/service_connectors.py +3 -3
  13. zenml/cli/stack.py +0 -3
  14. zenml/cli/stack_components.py +0 -1
  15. zenml/cli/utils.py +0 -5
  16. zenml/client.py +62 -20
  17. zenml/config/compiler.py +12 -3
  18. zenml/config/pipeline_configurations.py +20 -0
  19. zenml/config/pipeline_run_configuration.py +1 -0
  20. zenml/config/secret_reference_mixin.py +1 -1
  21. zenml/config/server_config.py +4 -0
  22. zenml/config/step_configurations.py +21 -0
  23. zenml/constants.py +10 -0
  24. zenml/enums.py +1 -0
  25. zenml/image_builders/base_image_builder.py +5 -2
  26. zenml/image_builders/build_context.py +7 -16
  27. zenml/image_builders/local_image_builder.py +13 -3
  28. zenml/integrations/__init__.py +1 -0
  29. zenml/integrations/aws/__init__.py +3 -0
  30. zenml/integrations/aws/flavors/__init__.py +6 -0
  31. zenml/integrations/aws/flavors/aws_image_builder_flavor.py +146 -0
  32. zenml/integrations/aws/image_builders/__init__.py +20 -0
  33. zenml/integrations/aws/image_builders/aws_image_builder.py +307 -0
  34. zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +14 -6
  35. zenml/integrations/constants.py +1 -0
  36. zenml/integrations/feast/__init__.py +1 -1
  37. zenml/integrations/feast/feature_stores/feast_feature_store.py +13 -9
  38. zenml/integrations/gcp/orchestrators/vertex_orchestrator.py +1 -1
  39. zenml/integrations/kaniko/image_builders/kaniko_image_builder.py +2 -1
  40. zenml/integrations/kubernetes/flavors/kubernetes_orchestrator_flavor.py +11 -0
  41. zenml/integrations/kubernetes/orchestrators/kube_utils.py +46 -2
  42. zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py +13 -2
  43. zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint.py +3 -1
  44. zenml/integrations/kubernetes/orchestrators/manifest_utils.py +3 -2
  45. zenml/integrations/kubernetes/step_operators/kubernetes_step_operator.py +3 -2
  46. zenml/integrations/lightning/flavors/lightning_orchestrator_flavor.py +11 -0
  47. zenml/integrations/modal/__init__.py +46 -0
  48. zenml/integrations/modal/flavors/__init__.py +26 -0
  49. zenml/integrations/modal/flavors/modal_step_operator_flavor.py +125 -0
  50. zenml/integrations/modal/step_operators/__init__.py +22 -0
  51. zenml/integrations/modal/step_operators/modal_step_operator.py +242 -0
  52. zenml/integrations/neptune/experiment_trackers/neptune_experiment_tracker.py +7 -5
  53. zenml/integrations/neptune/experiment_trackers/run_state.py +69 -53
  54. zenml/integrations/registry.py +2 -2
  55. zenml/integrations/skypilot/flavors/skypilot_orchestrator_base_vm_config.py +12 -0
  56. zenml/integrations/wandb/flavors/wandb_experiment_tracker_flavor.py +13 -5
  57. zenml/io/filesystem.py +2 -2
  58. zenml/io/local_filesystem.py +3 -3
  59. zenml/materializers/built_in_materializer.py +18 -1
  60. zenml/materializers/structured_string_materializer.py +8 -3
  61. zenml/model/model.py +23 -101
  62. zenml/model/utils.py +21 -17
  63. zenml/models/__init__.py +6 -0
  64. zenml/models/v2/base/filter.py +26 -30
  65. zenml/models/v2/base/scoped.py +258 -5
  66. zenml/models/v2/core/artifact_version.py +21 -29
  67. zenml/models/v2/core/code_repository.py +1 -12
  68. zenml/models/v2/core/component.py +5 -68
  69. zenml/models/v2/core/flavor.py +1 -11
  70. zenml/models/v2/core/model.py +1 -57
  71. zenml/models/v2/core/model_version.py +11 -36
  72. zenml/models/v2/core/model_version_artifact.py +11 -3
  73. zenml/models/v2/core/model_version_pipeline_run.py +14 -3
  74. zenml/models/v2/core/pipeline.py +47 -55
  75. zenml/models/v2/core/pipeline_build.py +67 -12
  76. zenml/models/v2/core/pipeline_deployment.py +0 -10
  77. zenml/models/v2/core/pipeline_run.py +110 -32
  78. zenml/models/v2/core/run_metadata.py +30 -9
  79. zenml/models/v2/core/run_template.py +21 -29
  80. zenml/models/v2/core/schedule.py +0 -10
  81. zenml/models/v2/core/secret.py +0 -14
  82. zenml/models/v2/core/service.py +9 -16
  83. zenml/models/v2/core/service_connector.py +0 -11
  84. zenml/models/v2/core/stack.py +21 -30
  85. zenml/models/v2/core/step_run.py +24 -18
  86. zenml/models/v2/core/trigger.py +19 -3
  87. zenml/models/v2/misc/run_metadata.py +38 -0
  88. zenml/orchestrators/base_orchestrator.py +13 -1
  89. zenml/orchestrators/input_utils.py +19 -6
  90. zenml/orchestrators/output_utils.py +5 -1
  91. zenml/orchestrators/publish_utils.py +12 -5
  92. zenml/orchestrators/step_launcher.py +16 -16
  93. zenml/orchestrators/step_run_utils.py +18 -197
  94. zenml/orchestrators/step_runner.py +40 -3
  95. zenml/orchestrators/utils.py +79 -50
  96. zenml/pipelines/build_utils.py +12 -0
  97. zenml/pipelines/pipeline_decorator.py +4 -0
  98. zenml/pipelines/pipeline_definition.py +26 -8
  99. zenml/pipelines/run_utils.py +9 -5
  100. zenml/service_connectors/service_connector_utils.py +3 -9
  101. zenml/stack/stack_component.py +1 -1
  102. zenml/stack_deployments/aws_stack_deployment.py +22 -0
  103. zenml/steps/base_step.py +11 -1
  104. zenml/steps/entrypoint_function_utils.py +7 -3
  105. zenml/steps/step_decorator.py +4 -0
  106. zenml/steps/utils.py +23 -7
  107. zenml/types.py +4 -0
  108. zenml/utils/archivable.py +65 -36
  109. zenml/utils/code_utils.py +8 -4
  110. zenml/utils/docker_utils.py +9 -0
  111. zenml/utils/metadata_utils.py +186 -153
  112. zenml/utils/string_utils.py +41 -16
  113. zenml/utils/visualization_utils.py +4 -1
  114. zenml/zen_server/auth.py +9 -10
  115. zenml/zen_server/cloud_utils.py +3 -1
  116. zenml/zen_server/dashboard/assets/{404-NVXKFp-x.js → 404-Cqu3EDCm.js} +1 -1
  117. zenml/zen_server/dashboard/assets/{@reactflow-CK0KJUen.js → @reactflow-D2Y7BWwz.js} +1 -1
  118. zenml/zen_server/dashboard/assets/{AlertDialogDropdownItem-DezXKmDf.js → AlertDialogDropdownItem-BHd71pVS.js} +1 -1
  119. zenml/zen_server/dashboard/assets/{CodeSnippet-JzR8CEtw.js → CodeSnippet-DIonwetW.js} +1 -1
  120. zenml/zen_server/dashboard/assets/{CollapsibleCard-DQW_ktMO.js → CollapsibleCard-CDnC97pB.js} +1 -1
  121. zenml/zen_server/dashboard/assets/{Commands-DL2kwkRd.js → Commands-BVEXKAOj.js} +1 -1
  122. zenml/zen_server/dashboard/assets/{ComponentBadge-D_g62Wv8.js → ComponentBadge-CrRvovox.js} +1 -1
  123. zenml/zen_server/dashboard/assets/{CopyButton-LNcWaa14.js → CopyButton-B6wGAhQv.js} +1 -1
  124. zenml/zen_server/dashboard/assets/{CsvVizualization-DknpE5ej.js → CsvVizualization-CjcT7LMm.js} +5 -5
  125. zenml/zen_server/dashboard/assets/DeleteAlertDialog-D2ELtM2W.js +1 -0
  126. zenml/zen_server/dashboard/assets/{DialogItem-Bxf8FuAT.js → DialogItem-DXIMhBgU.js} +1 -1
  127. zenml/zen_server/dashboard/assets/{Error-DYflYyps.js → Error-B8uUfTpL.js} +1 -1
  128. zenml/zen_server/dashboard/assets/{ExecutionStatus-C7zyIQKZ.js → ExecutionStatus-ibAdY-dG.js} +1 -1
  129. zenml/zen_server/dashboard/assets/{Helpbox-oYSGpLqd.js → Helpbox-BfAfhKHw.js} +1 -1
  130. zenml/zen_server/dashboard/assets/{Infobox-Cx4xGoXR.js → Infobox-M_SMOu96.js} +1 -1
  131. zenml/zen_server/dashboard/assets/{InlineAvatar-DiGOWNKF.js → InlineAvatar-DBA0a0-a.js} +1 -1
  132. zenml/zen_server/dashboard/assets/{NestedCollapsible-DYbgyKxK.js → NestedCollapsible-DpgmEFKw.js} +1 -1
  133. zenml/zen_server/dashboard/assets/{Partials-03iZf8-N.js → Partials-D_ldD9if.js} +1 -1
  134. zenml/zen_server/dashboard/assets/{ProBadge-D_EB8HNo.js → ProBadge-DQbfFotM.js} +1 -1
  135. zenml/zen_server/dashboard/assets/{ProCta-DqNS4v3x.js → ProCta-Bcpb4rcY.js} +1 -1
  136. zenml/zen_server/dashboard/assets/{ProviderIcon-Bki2aw8w.js → ProviderIcon-BZpgPigN.js} +1 -1
  137. zenml/zen_server/dashboard/assets/{ProviderRadio-8f43sPD4.js → ProviderRadio-DWPnMuQ1.js} +1 -1
  138. zenml/zen_server/dashboard/assets/RunSelector-DgRGaAc6.js +1 -0
  139. zenml/zen_server/dashboard/assets/{RunsBody-07YEO7qI.js → RunsBody-KecfSkjY.js} +1 -1
  140. zenml/zen_server/dashboard/assets/{SearchField-lp1KgU4e.js → SearchField-n-ILHnaP.js} +1 -1
  141. zenml/zen_server/dashboard/assets/{SecretTooltip-CgnbyeOx.js → SecretTooltip-B8MrX5yu.js} +1 -1
  142. zenml/zen_server/dashboard/assets/{SetPassword-CpP418A2.js → SetPassword-B_IVq_wg.js} +1 -1
  143. zenml/zen_server/dashboard/assets/StackList-TWPBYnkF.js +1 -0
  144. zenml/zen_server/dashboard/assets/{Tabs-BktHkCJJ.js → Tabs-Rg857zmd.js} +1 -1
  145. zenml/zen_server/dashboard/assets/{Tick-BlMoIlJT.js → Tick-COg4A-xo.js} +1 -1
  146. zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-Sc0A0pP-.js → UpdatePasswordSchemas-C6Aj3hm6.js} +1 -1
  147. zenml/zen_server/dashboard/assets/{UsageReason-YYduL4fj.js → UsageReason-BTLbx7w4.js} +1 -1
  148. zenml/zen_server/dashboard/assets/{WizardFooter-dgmizSJC.js → WizardFooter-BCAj69Vj.js} +1 -1
  149. zenml/zen_server/dashboard/assets/{all-pipeline-runs-query-D-c2G6lV.js → all-pipeline-runs-query-DMXkDrV2.js} +1 -1
  150. zenml/zen_server/dashboard/assets/code-snippets-CqONne41.js +13 -0
  151. zenml/zen_server/dashboard/assets/{create-stack-DM_JPgef.js → create-stack-HfdbhLs4.js} +1 -1
  152. zenml/zen_server/dashboard/assets/dates-3pMLCNrD.js +1 -0
  153. zenml/zen_server/dashboard/assets/delete-run-DZ4hIXff.js +1 -0
  154. zenml/zen_server/dashboard/assets/{form-schemas-K6FYKjwa.js → form-schemas-B0AVEd9b.js} +1 -1
  155. zenml/zen_server/dashboard/assets/{index-BAkC7FXi.js → index-DPqSWjug.js} +1 -1
  156. zenml/zen_server/dashboard/assets/{index-CEV4Cvaf.js → index-DScjfBRb.js} +1 -1
  157. zenml/zen_server/dashboard/assets/index-DXvT1_Um.css +1 -0
  158. zenml/zen_server/dashboard/assets/{index-CCOPpudF.js → index-FO-p0GU7.js} +5 -5
  159. zenml/zen_server/dashboard/assets/{index-B1mVPYxf.js → index-I3bKUGUj.js} +1 -1
  160. zenml/zen_server/dashboard/assets/key-icon-aH-QIa5R.js +1 -0
  161. zenml/zen_server/dashboard/assets/login-command-CkqxPtV3.js +1 -0
  162. zenml/zen_server/dashboard/assets/{login-mutation-hf-lK87O.js → login-mutation-BQeo4wTY.js} +1 -1
  163. zenml/zen_server/dashboard/assets/{not-found-BGirLjU-.js → not-found-gAJ5aDdR.js} +1 -1
  164. zenml/zen_server/dashboard/assets/page-9Y9-gig0.js +1 -0
  165. zenml/zen_server/dashboard/assets/{page-DjRJCGb3.js → page-AUwiQ14W.js} +1 -1
  166. zenml/zen_server/dashboard/assets/page-B6XU7yYT.js +2 -0
  167. zenml/zen_server/dashboard/assets/{page-C00YAkaB.js → page-BKZYc2Zv.js} +1 -1
  168. zenml/zen_server/dashboard/assets/{page-CdMWnQak.js → page-BU9FG4sR.js} +1 -1
  169. zenml/zen_server/dashboard/assets/{page-D7S3aCbF.js → page-B_Apk3xg.js} +1 -1
  170. zenml/zen_server/dashboard/assets/{page-Djikxq_S.js → page-BdowiCbr.js} +1 -1
  171. zenml/zen_server/dashboard/assets/page-Bg8OjTRe.js +1 -0
  172. zenml/zen_server/dashboard/assets/page-BxL4qD4_.js +1 -0
  173. zenml/zen_server/dashboard/assets/{page-DakHVWXF.js → page-CWxT5K5J.js} +1 -1
  174. zenml/zen_server/dashboard/assets/page-CXuQufSe.js +1 -0
  175. zenml/zen_server/dashboard/assets/{page-DLC-bNBP.js → page-CcQr8CPP.js} +1 -1
  176. zenml/zen_server/dashboard/assets/{page-CD-DcWoy.js → page-Ce4Hrjnr.js} +1 -1
  177. zenml/zen_server/dashboard/assets/page-CiYxgZP_.js +1 -0
  178. zenml/zen_server/dashboard/assets/page-Cldq1mpe.js +1 -0
  179. zenml/zen_server/dashboard/assets/{page-BDigxVpo.js → page-D4wdonLm.js} +1 -1
  180. zenml/zen_server/dashboard/assets/{page-D6uU2ax4.js → page-D8ObrbH8.js} +1 -1
  181. zenml/zen_server/dashboard/assets/{page-DXSTpqRD.js → page-DFuAUGt4.js} +1 -1
  182. zenml/zen_server/dashboard/assets/{page-CbpvrsDL.js → page-DGazBpuP.js} +1 -1
  183. zenml/zen_server/dashboard/assets/{page-COXXJj1k.js → page-DO1UcqPX.js} +1 -1
  184. zenml/zen_server/dashboard/assets/page-DRYXdL5o.js +1 -0
  185. zenml/zen_server/dashboard/assets/{page-Df-Fw0aq.js → page-DYEquBC2.js} +1 -1
  186. zenml/zen_server/dashboard/assets/page-Dk32IeZm.js +1 -0
  187. zenml/zen_server/dashboard/assets/{page-yYC9OI-E.js → page-I3nKFGie.js} +1 -1
  188. zenml/zen_server/dashboard/assets/{page-6m6yHHlE.js → page-M0w-n6vn.js} +1 -1
  189. zenml/zen_server/dashboard/assets/{page-Vcxara9U.js → page-R5dx3xGF.js} +1 -1
  190. zenml/zen_server/dashboard/assets/{page-BR68V0V1.js → page-bT5pOvcB.js} +1 -1
  191. zenml/zen_server/dashboard/assets/page-hUqK889I.js +6 -0
  192. zenml/zen_server/dashboard/assets/{page-CjGdWY13.js → page-h_Stveon.js} +1 -1
  193. zenml/zen_server/dashboard/assets/{page-D01JhjQB.js → page-r8XK5vR7.js} +1 -1
  194. zenml/zen_server/dashboard/assets/page-u_-ZXBKb.js +1 -0
  195. zenml/zen_server/dashboard/assets/page-zaMqB_ao.js +1 -0
  196. zenml/zen_server/dashboard/assets/{persist-GjC8PZoC.js → persist-AppN1B0J.js} +1 -1
  197. zenml/zen_server/dashboard/assets/{persist-Coz7ZWvz.js → persist-DAUi_3za.js} +1 -1
  198. zenml/zen_server/dashboard/assets/service-BqqeXLEe.js +2 -0
  199. zenml/zen_server/dashboard/assets/{sharedSchema-CQb14VSr.js → sharedSchema-uXN9FLLk.js} +1 -1
  200. zenml/zen_server/dashboard/assets/{stack-detail-query-OPEW-cDJ.js → stack-detail-query-XfZBiBP2.js} +1 -1
  201. zenml/zen_server/dashboard/assets/{update-server-settings-mutation-LwuQfHYn.js → update-server-settings-mutation-BWmgVJwA.js} +1 -1
  202. zenml/zen_server/dashboard/assets/{url-CkvKAnwF.js → url-BLwMbzES.js} +1 -1
  203. zenml/zen_server/dashboard/index.html +4 -4
  204. zenml/zen_server/deploy/helm/Chart.yaml +1 -1
  205. zenml/zen_server/deploy/helm/README.md +2 -2
  206. zenml/zen_server/rbac/endpoint_utils.py +6 -4
  207. zenml/zen_server/rbac/models.py +3 -2
  208. zenml/zen_server/rbac/rbac_sql_zen_store.py +173 -0
  209. zenml/zen_server/rbac/utils.py +4 -7
  210. zenml/zen_server/routers/auth_endpoints.py +22 -11
  211. zenml/zen_server/routers/steps_endpoints.py +7 -1
  212. zenml/zen_server/routers/users_endpoints.py +35 -37
  213. zenml/zen_server/routers/workspaces_endpoints.py +44 -55
  214. zenml/zen_server/template_execution/utils.py +4 -1
  215. zenml/zen_server/utils.py +4 -3
  216. zenml/zen_stores/base_zen_store.py +10 -2
  217. zenml/zen_stores/migrations/versions/0.71.0_release.py +23 -0
  218. zenml/zen_stores/migrations/versions/26351d482b9e_add_step_run_unique_constraint.py +37 -0
  219. zenml/zen_stores/migrations/versions/a1237ba94fd8_add_model_version_producer_run_unique_.py +68 -0
  220. zenml/zen_stores/migrations/versions/b73bc71f1106_remove_component_spec_path.py +36 -0
  221. zenml/zen_stores/migrations/versions/cc269488e5a9_separate_run_metadata.py +135 -0
  222. zenml/zen_stores/migrations/versions/ec6307720f92_simplify_model_version_links.py +7 -6
  223. zenml/zen_stores/rest_zen_store.py +76 -43
  224. zenml/zen_stores/schemas/__init__.py +5 -1
  225. zenml/zen_stores/schemas/artifact_schemas.py +12 -11
  226. zenml/zen_stores/schemas/component_schemas.py +0 -3
  227. zenml/zen_stores/schemas/model_schemas.py +55 -17
  228. zenml/zen_stores/schemas/pipeline_deployment_schemas.py +7 -7
  229. zenml/zen_stores/schemas/pipeline_run_schemas.py +52 -18
  230. zenml/zen_stores/schemas/pipeline_schemas.py +5 -0
  231. zenml/zen_stores/schemas/run_metadata_schemas.py +66 -31
  232. zenml/zen_stores/schemas/step_run_schemas.py +40 -13
  233. zenml/zen_stores/schemas/utils.py +47 -3
  234. zenml/zen_stores/sql_zen_store.py +462 -134
  235. {zenml_nightly-0.70.0.dev20241125.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/METADATA +5 -5
  236. {zenml_nightly-0.70.0.dev20241125.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/RECORD +239 -217
  237. zenml/utils/cloud_utils.py +0 -40
  238. zenml/zen_server/dashboard/assets/RunSelector-DkPiIiNr.js +0 -1
  239. zenml/zen_server/dashboard/assets/StackList-WvuKQusZ.js +0 -1
  240. zenml/zen_server/dashboard/assets/delete-run-CJdh1P_h.js +0 -1
  241. zenml/zen_server/dashboard/assets/index-DlGvJQPn.css +0 -1
  242. zenml/zen_server/dashboard/assets/page-0JE_-Ec1.js +0 -1
  243. zenml/zen_server/dashboard/assets/page-BRLpxOt0.js +0 -1
  244. zenml/zen_server/dashboard/assets/page-BU7huvKw.js +0 -6
  245. zenml/zen_server/dashboard/assets/page-BvqLv2Ky.js +0 -1
  246. zenml/zen_server/dashboard/assets/page-CwxrFarU.js +0 -1
  247. zenml/zen_server/dashboard/assets/page-DfbXf_8s.js +0 -1
  248. zenml/zen_server/dashboard/assets/page-Dnovpa0i.js +0 -3
  249. zenml/zen_server/dashboard/assets/page-Dot3LPmL.js +0 -1
  250. zenml/zen_server/dashboard/assets/page-Xynx4btY.js +0 -14
  251. zenml/zen_server/dashboard/assets/page-YpKAqVSa.js +0 -1
  252. {zenml_nightly-0.70.0.dev20241125.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/LICENSE +0 -0
  253. {zenml_nightly-0.70.0.dev20241125.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/WHEEL +0 -0
  254. {zenml_nightly-0.70.0.dev20241125.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/entry_points.txt +0 -0
@@ -26,6 +26,7 @@ from datetime import datetime, timezone
26
26
  from functools import lru_cache
27
27
  from pathlib import Path
28
28
  from typing import (
29
+ TYPE_CHECKING,
29
30
  Any,
30
31
  Callable,
31
32
  ClassVar,
@@ -54,7 +55,7 @@ from pydantic import (
54
55
  field_validator,
55
56
  model_validator,
56
57
  )
57
- from sqlalchemy import asc, case, desc, func
58
+ from sqlalchemy import func
58
59
  from sqlalchemy.engine import URL, Engine, make_url
59
60
  from sqlalchemy.exc import (
60
61
  ArgumentError,
@@ -70,6 +71,7 @@ from sqlmodel import (
70
71
  col,
71
72
  create_engine,
72
73
  delete,
74
+ desc,
73
75
  or_,
74
76
  select,
75
77
  )
@@ -99,7 +101,6 @@ from zenml.constants import (
99
101
  ENV_ZENML_SERVER,
100
102
  FINISHED_ONBOARDING_SURVEY_KEY,
101
103
  MAX_RETRIES_FOR_VERSIONED_ENTITY_CREATION,
102
- SORT_PIPELINES_BY_LATEST_RUN_KEY,
103
104
  SQL_STORE_BACKUP_DIRECTORY_NAME,
104
105
  TEXT_FIELD_MAX_LENGTH,
105
106
  handle_bool_env_var,
@@ -116,7 +117,6 @@ from zenml.enums import (
116
117
  OnboardingStep,
117
118
  SecretScope,
118
119
  SecretsStoreType,
119
- SorterOps,
120
120
  StackComponentType,
121
121
  StackDeploymentProvider,
122
122
  StepRunInputArtifactType,
@@ -219,6 +219,7 @@ from zenml.models import (
219
219
  PipelineRunUpdate,
220
220
  PipelineUpdate,
221
221
  RunMetadataRequest,
222
+ RunMetadataResource,
222
223
  RunTemplateFilter,
223
224
  RunTemplateRequest,
224
225
  RunTemplateResponse,
@@ -296,7 +297,11 @@ from zenml.utils.networking_utils import (
296
297
  replace_localhost_with_internal_hostname,
297
298
  )
298
299
  from zenml.utils.pydantic_utils import before_validator_handler
299
- from zenml.utils.string_utils import random_str, validate_name
300
+ from zenml.utils.string_utils import (
301
+ format_name_template,
302
+ random_str,
303
+ validate_name,
304
+ )
300
305
  from zenml.zen_stores import template_utils
301
306
  from zenml.zen_stores.base_zen_store import (
302
307
  BaseZenStore,
@@ -325,6 +330,7 @@ from zenml.zen_stores.schemas import (
325
330
  PipelineDeploymentSchema,
326
331
  PipelineRunSchema,
327
332
  PipelineSchema,
333
+ RunMetadataResourceSchema,
328
334
  RunMetadataSchema,
329
335
  RunTemplateSchema,
330
336
  ScheduleSchema,
@@ -354,6 +360,9 @@ from zenml.zen_stores.secrets_stores.sql_secrets_store import (
354
360
  SqlSecretsStoreConfiguration,
355
361
  )
356
362
 
363
+ if TYPE_CHECKING:
364
+ from zenml.metadata.metadata_types import MetadataType, MetadataTypeEnum
365
+
357
366
  AnyNamedSchema = TypeVar("AnyNamedSchema", bound=NamedSchema)
358
367
  AnySchema = TypeVar("AnySchema", bound=BaseSchema)
359
368
 
@@ -2726,7 +2735,9 @@ class SqlZenStore(BaseZenStore):
2726
2735
  # -------------------- Artifact Versions --------------------
2727
2736
 
2728
2737
  def _get_or_create_artifact_for_name(
2729
- self, name: str, has_custom_name: bool
2738
+ self,
2739
+ name: str,
2740
+ has_custom_name: bool,
2730
2741
  ) -> ArtifactSchema:
2731
2742
  """Get or create an artifact with a specific name.
2732
2743
 
@@ -2747,7 +2758,8 @@ class SqlZenStore(BaseZenStore):
2747
2758
  try:
2748
2759
  with session.begin_nested():
2749
2760
  artifact_request = ArtifactRequest(
2750
- name=name, has_custom_name=has_custom_name
2761
+ name=name,
2762
+ has_custom_name=has_custom_name,
2751
2763
  )
2752
2764
  artifact = ArtifactSchema.from_request(
2753
2765
  artifact_request
@@ -2915,17 +2927,41 @@ class SqlZenStore(BaseZenStore):
2915
2927
 
2916
2928
  # Save metadata of the artifact
2917
2929
  if artifact_version.metadata:
2930
+ values: Dict[str, "MetadataType"] = {}
2931
+ types: Dict[str, "MetadataTypeEnum"] = {}
2918
2932
  for key, value in artifact_version.metadata.items():
2919
- run_metadata_schema = RunMetadataSchema(
2920
- workspace_id=artifact_version.workspace,
2921
- user_id=artifact_version.user,
2922
- resource_id=artifact_version_id,
2923
- resource_type=MetadataResourceTypes.ARTIFACT_VERSION,
2924
- key=key,
2925
- value=json.dumps(value),
2926
- type=get_metadata_type(value),
2933
+ # Skip metadata that is too large to be stored in the DB.
2934
+ if len(json.dumps(value)) > TEXT_FIELD_MAX_LENGTH:
2935
+ logger.warning(
2936
+ f"Metadata value for key '{key}' is too large to be "
2937
+ "stored in the database. Skipping."
2938
+ )
2939
+ continue
2940
+ # Skip metadata that is not of a supported type.
2941
+ try:
2942
+ metadata_type = get_metadata_type(value)
2943
+ except ValueError as e:
2944
+ logger.warning(
2945
+ f"Metadata value for key '{key}' is not of a "
2946
+ f"supported type. Skipping. Full error: {e}"
2947
+ )
2948
+ continue
2949
+ values[key] = value
2950
+ types[key] = metadata_type
2951
+ self.create_run_metadata(
2952
+ RunMetadataRequest(
2953
+ workspace=artifact_version.workspace,
2954
+ user=artifact_version.user,
2955
+ resources=[
2956
+ RunMetadataResource(
2957
+ id=artifact_version_id,
2958
+ type=MetadataResourceTypes.ARTIFACT_VERSION,
2959
+ )
2960
+ ],
2961
+ values=values,
2962
+ types=types,
2927
2963
  )
2928
- session.add(run_metadata_schema)
2964
+ )
2929
2965
 
2930
2966
  session.commit()
2931
2967
  artifact_version_schema = session.exec(
@@ -4325,69 +4361,14 @@ class SqlZenStore(BaseZenStore):
4325
4361
  Returns:
4326
4362
  A list of all pipelines matching the filter criteria.
4327
4363
  """
4328
- query: Union[Select[Any], SelectOfScalar[Any]] = select(PipelineSchema)
4329
- _custom_conversion: Optional[Callable[[Any], PipelineResponse]] = None
4330
-
4331
- column, operand = pipeline_filter_model.sorting_params
4332
- if column == SORT_PIPELINES_BY_LATEST_RUN_KEY:
4333
- with Session(self.engine) as session:
4334
- max_date_subquery = (
4335
- # If no run exists for the pipeline yet, we use the pipeline
4336
- # creation date as a fallback, otherwise newly created
4337
- # pipeline would always be at the top/bottom
4338
- select(
4339
- PipelineSchema.id,
4340
- case(
4341
- (
4342
- func.max(PipelineRunSchema.created).is_(None),
4343
- PipelineSchema.created,
4344
- ),
4345
- else_=func.max(PipelineRunSchema.created),
4346
- ).label("run_or_created"),
4347
- )
4348
- .outerjoin(
4349
- PipelineRunSchema,
4350
- PipelineSchema.id == PipelineRunSchema.pipeline_id, # type: ignore[arg-type]
4351
- )
4352
- .group_by(col(PipelineSchema.id))
4353
- .subquery()
4354
- )
4355
-
4356
- if operand == SorterOps.DESCENDING:
4357
- sort_clause = desc
4358
- else:
4359
- sort_clause = asc
4360
-
4361
- query = (
4362
- # We need to include the subquery in the select here to
4363
- # make this query work with the distinct statement. This
4364
- # result will be removed in the custom conversion function
4365
- # applied later
4366
- select(PipelineSchema, max_date_subquery.c.run_or_created)
4367
- .where(PipelineSchema.id == max_date_subquery.c.id)
4368
- .order_by(sort_clause(max_date_subquery.c.run_or_created))
4369
- # We always add the `id` column as a tiebreaker to ensure a
4370
- # stable, repeatable order of items, otherwise subsequent
4371
- # pages might contain the same items.
4372
- .order_by(col(PipelineSchema.id))
4373
- )
4374
-
4375
- def _custom_conversion(row: Any) -> PipelineResponse:
4376
- return cast(
4377
- PipelineResponse,
4378
- row[0].to_model(
4379
- include_metadata=hydrate, include_resources=True
4380
- ),
4381
- )
4382
-
4383
4364
  with Session(self.engine) as session:
4365
+ query = select(PipelineSchema)
4384
4366
  return self.filter_and_paginate(
4385
4367
  session=session,
4386
4368
  query=query,
4387
4369
  table=PipelineSchema,
4388
4370
  filter_model=pipeline_filter_model,
4389
4371
  hydrate=hydrate,
4390
- custom_schema_to_model_conversion=_custom_conversion,
4391
4372
  )
4392
4373
 
4393
4374
  def count_pipelines(self, filter_model: Optional[PipelineFilter]) -> int:
@@ -5178,6 +5159,20 @@ class SqlZenStore(BaseZenStore):
5178
5159
  "already exists."
5179
5160
  )
5180
5161
 
5162
+ if model_version_id := self._get_or_create_model_version_for_run(
5163
+ new_run
5164
+ ):
5165
+ new_run.model_version_id = model_version_id
5166
+ session.add(new_run)
5167
+ session.commit()
5168
+
5169
+ self.create_model_version_pipeline_run_link(
5170
+ ModelVersionPipelineRunRequest(
5171
+ model_version=model_version_id, pipeline_run=new_run.id
5172
+ )
5173
+ )
5174
+ session.refresh(new_run)
5175
+
5181
5176
  return new_run.to_model(
5182
5177
  include_metadata=True, include_resources=True
5183
5178
  )
@@ -5329,6 +5324,19 @@ class SqlZenStore(BaseZenStore):
5329
5324
  "orchestrator run ID."
5330
5325
  )
5331
5326
 
5327
+ try:
5328
+ # We first try the most likely case that the run was already
5329
+ # created by a previous step in the same pipeline run.
5330
+ return (
5331
+ self._get_run_by_orchestrator_run_id(
5332
+ orchestrator_run_id=pipeline_run.orchestrator_run_id,
5333
+ deployment_id=pipeline_run.deployment,
5334
+ ),
5335
+ False,
5336
+ )
5337
+ except KeyError:
5338
+ pass
5339
+
5332
5340
  try:
5333
5341
  return (
5334
5342
  self._replace_placeholder_run(
@@ -5516,20 +5524,29 @@ class SqlZenStore(BaseZenStore):
5516
5524
  The created run metadata.
5517
5525
  """
5518
5526
  with Session(self.engine) as session:
5519
- for key, value in run_metadata.values.items():
5520
- type_ = run_metadata.types[key]
5521
- run_metadata_schema = RunMetadataSchema(
5522
- workspace_id=run_metadata.workspace,
5523
- user_id=run_metadata.user,
5524
- resource_id=run_metadata.resource_id,
5525
- resource_type=run_metadata.resource_type.value,
5526
- stack_component_id=run_metadata.stack_component_id,
5527
- key=key,
5528
- value=json.dumps(value),
5529
- type=type_,
5530
- )
5531
- session.add(run_metadata_schema)
5532
- session.commit()
5527
+ if run_metadata.resources:
5528
+ for key, value in run_metadata.values.items():
5529
+ type_ = run_metadata.types[key]
5530
+ run_metadata_schema = RunMetadataSchema(
5531
+ workspace_id=run_metadata.workspace,
5532
+ user_id=run_metadata.user,
5533
+ stack_component_id=run_metadata.stack_component_id,
5534
+ key=key,
5535
+ value=json.dumps(value),
5536
+ type=type_,
5537
+ publisher_step_id=run_metadata.publisher_step_id,
5538
+ )
5539
+ session.add(run_metadata_schema)
5540
+ session.commit()
5541
+
5542
+ for resource in run_metadata.resources:
5543
+ rm_resource_link = RunMetadataResourceSchema(
5544
+ resource_id=resource.id,
5545
+ resource_type=resource.type.value,
5546
+ run_metadata_id=run_metadata_schema.id,
5547
+ )
5548
+ session.add(rm_resource_link)
5549
+ session.commit()
5533
5550
  return None
5534
5551
 
5535
5552
  # ----------------------------- Schedules -----------------------------
@@ -8112,25 +8129,17 @@ class SqlZenStore(BaseZenStore):
8112
8129
  f"with ID '{step_run.pipeline_run_id}' found."
8113
8130
  )
8114
8131
 
8115
- # Check if the step name already exists in the pipeline run
8116
- existing_step_run = session.exec(
8117
- select(StepRunSchema)
8118
- .where(StepRunSchema.name == step_run.name)
8119
- .where(
8120
- StepRunSchema.pipeline_run_id == step_run.pipeline_run_id
8121
- )
8122
- ).first()
8123
- if existing_step_run is not None:
8132
+ step_schema = StepRunSchema.from_request(step_run)
8133
+ session.add(step_schema)
8134
+ try:
8135
+ session.commit()
8136
+ except IntegrityError:
8124
8137
  raise EntityExistsError(
8125
8138
  f"Unable to create step `{step_run.name}`: A step with "
8126
8139
  f"this name already exists in the pipeline run with ID "
8127
8140
  f"'{step_run.pipeline_run_id}'."
8128
8141
  )
8129
8142
 
8130
- # Create the step
8131
- step_schema = StepRunSchema.from_request(step_run)
8132
- session.add(step_schema)
8133
-
8134
8143
  # Add logs entry for the step if exists
8135
8144
  if step_run.logs is not None:
8136
8145
  log_entry = LogsSchema(
@@ -8140,6 +8149,46 @@ class SqlZenStore(BaseZenStore):
8140
8149
  )
8141
8150
  session.add(log_entry)
8142
8151
 
8152
+ # If cached, attach metadata of the original step
8153
+ if (
8154
+ step_run.status == ExecutionStatus.CACHED
8155
+ and step_run.original_step_run_id is not None
8156
+ ):
8157
+ original_metadata_links = session.exec(
8158
+ select(RunMetadataResourceSchema)
8159
+ .where(
8160
+ RunMetadataResourceSchema.run_metadata_id
8161
+ == RunMetadataSchema.id
8162
+ )
8163
+ .where(
8164
+ RunMetadataResourceSchema.resource_id
8165
+ == step_run.original_step_run_id
8166
+ )
8167
+ .where(
8168
+ RunMetadataResourceSchema.resource_type
8169
+ == MetadataResourceTypes.STEP_RUN
8170
+ )
8171
+ .where(
8172
+ RunMetadataSchema.publisher_step_id
8173
+ == step_run.original_step_run_id
8174
+ )
8175
+ ).all()
8176
+
8177
+ # Create new links in a batch
8178
+ new_links = [
8179
+ RunMetadataResourceSchema(
8180
+ resource_id=step_schema.id,
8181
+ resource_type=link.resource_type,
8182
+ run_metadata_id=link.run_metadata_id,
8183
+ )
8184
+ for link in original_metadata_links
8185
+ ]
8186
+ # Add all new links in a single operation
8187
+ session.add_all(new_links)
8188
+ # Commit the changes
8189
+ session.commit()
8190
+ session.refresh(step_schema)
8191
+
8143
8192
  # Save parent step IDs into the database.
8144
8193
  for parent_step_id in step_run.parent_step_ids:
8145
8194
  self._set_run_step_parent_step(
@@ -8169,12 +8218,12 @@ class SqlZenStore(BaseZenStore):
8169
8218
  )
8170
8219
 
8171
8220
  # Save output artifact IDs into the database.
8172
- for output_name, artifact_version_ids in step_run.outputs.items():
8221
+ for name, artifact_version_ids in step_run.outputs.items():
8173
8222
  for artifact_version_id in artifact_version_ids:
8174
8223
  self._set_run_step_output_artifact(
8175
8224
  step_run_id=step_schema.id,
8176
8225
  artifact_version_id=artifact_version_id,
8177
- name=output_name,
8226
+ name=name,
8178
8227
  session=session,
8179
8228
  )
8180
8229
 
@@ -8186,6 +8235,21 @@ class SqlZenStore(BaseZenStore):
8186
8235
  session.commit()
8187
8236
  session.refresh(step_schema)
8188
8237
 
8238
+ if model_version_id := self._get_or_create_model_version_for_run(
8239
+ step_schema
8240
+ ):
8241
+ step_schema.model_version_id = model_version_id
8242
+ session.add(step_schema)
8243
+ session.commit()
8244
+
8245
+ self.create_model_version_pipeline_run_link(
8246
+ ModelVersionPipelineRunRequest(
8247
+ model_version=model_version_id,
8248
+ pipeline_run=step_schema.pipeline_run_id,
8249
+ )
8250
+ )
8251
+ session.refresh(step_schema)
8252
+
8189
8253
  return step_schema.to_model(
8190
8254
  include_metadata=True, include_resources=True
8191
8255
  )
@@ -8278,13 +8342,14 @@ class SqlZenStore(BaseZenStore):
8278
8342
  session.add(existing_step_run)
8279
8343
 
8280
8344
  # Update the artifacts.
8281
- for name, artifact_version_id in step_run_update.outputs.items():
8282
- self._set_run_step_output_artifact(
8283
- step_run_id=step_run_id,
8284
- artifact_version_id=artifact_version_id,
8285
- name=name,
8286
- session=session,
8287
- )
8345
+ for name, artifact_version_ids in step_run_update.outputs.items():
8346
+ for artifact_version_id in artifact_version_ids:
8347
+ self._set_run_step_output_artifact(
8348
+ step_run_id=step_run_id,
8349
+ artifact_version_id=artifact_version_id,
8350
+ name=name,
8351
+ session=session,
8352
+ )
8288
8353
 
8289
8354
  # Update loaded artifacts.
8290
8355
  for (
@@ -8537,7 +8602,11 @@ class SqlZenStore(BaseZenStore):
8537
8602
 
8538
8603
  # Deployment always exists for pipeline runs of newer versions
8539
8604
  assert pipeline_run.deployment
8540
- num_steps = len(pipeline_run.deployment.to_model().step_configurations)
8605
+ num_steps = len(
8606
+ pipeline_run.deployment.to_model(
8607
+ include_metadata=True
8608
+ ).step_configurations
8609
+ )
8541
8610
  new_status = get_pipeline_run_status(
8542
8611
  step_statuses=[
8543
8612
  ExecutionStatus(step_run.status) for step_run in step_runs
@@ -10183,6 +10252,22 @@ class SqlZenStore(BaseZenStore):
10183
10252
 
10184
10253
  # ----------------------------- Model Versions -----------------------------
10185
10254
 
10255
+ def _get_or_create_model(
10256
+ self, model_request: ModelRequest
10257
+ ) -> Tuple[bool, ModelResponse]:
10258
+ """Get or create a model.
10259
+
10260
+ Args:
10261
+ model_request: The model request.
10262
+
10263
+ Returns:
10264
+ A boolean whether the model was created or not, and the model.
10265
+ """
10266
+ try:
10267
+ return True, self.create_model(model_request)
10268
+ except EntityExistsError:
10269
+ return False, self.get_model(model_request.name)
10270
+
10186
10271
  def _get_next_numeric_version_for_model(
10187
10272
  self, session: Session, model_id: UUID
10188
10273
  ) -> int:
@@ -10207,55 +10292,276 @@ class SqlZenStore(BaseZenStore):
10207
10292
  else:
10208
10293
  return int(current_max_version) + 1
10209
10294
 
10210
- def _model_version_exists(self, model_id: UUID, version: str) -> bool:
10295
+ def _model_version_exists(
10296
+ self,
10297
+ model_id: UUID,
10298
+ version: Optional[str] = None,
10299
+ producer_run_id: Optional[UUID] = None,
10300
+ ) -> bool:
10211
10301
  """Check if a model version with a certain version exists.
10212
10302
 
10213
10303
  Args:
10214
10304
  model_id: The model ID of the version.
10215
10305
  version: The version name.
10306
+ producer_run_id: The producer run ID. If given, checks if a numeric
10307
+ version for the producer run exists.
10308
+
10309
+ Returns:
10310
+ If a model version for the given arguments exists.
10311
+ """
10312
+ query = select(ModelVersionSchema.id).where(
10313
+ ModelVersionSchema.model_id == model_id
10314
+ )
10315
+
10316
+ if version:
10317
+ query = query.where(ModelVersionSchema.name == version)
10318
+
10319
+ if producer_run_id:
10320
+ query = query.where(
10321
+ ModelVersionSchema.producer_run_id_if_numeric
10322
+ == producer_run_id,
10323
+ )
10324
+
10325
+ with Session(self.engine) as session:
10326
+ return session.exec(query).first() is not None
10327
+
10328
+ def _get_model_version(
10329
+ self,
10330
+ model_id: UUID,
10331
+ version_name: Optional[str] = None,
10332
+ producer_run_id: Optional[UUID] = None,
10333
+ ) -> ModelVersionResponse:
10334
+ """Get a model version.
10335
+
10336
+ Args:
10337
+ model_id: The ID of the model.
10338
+ version_name: The name of the model version.
10339
+ producer_run_id: The ID of the producer pipeline run. If this is
10340
+ set, only numeric versions created as part of the pipeline run
10341
+ will be returned.
10342
+
10343
+ Raises:
10344
+ ValueError: If no version name or producer run ID was provided.
10345
+ KeyError: If no model version was found.
10216
10346
 
10217
10347
  Returns:
10218
- If a model version with the given version name exists.
10348
+ The model version.
10219
10349
  """
10350
+ query = select(ModelVersionSchema).where(
10351
+ ModelVersionSchema.model_id == model_id
10352
+ )
10353
+
10354
+ if version_name:
10355
+ if version_name.isnumeric():
10356
+ query = query.where(
10357
+ ModelVersionSchema.number == int(version_name)
10358
+ )
10359
+ error_text = (
10360
+ f"No version with number {version_name} found "
10361
+ f"for model {model_id}."
10362
+ )
10363
+ elif version_name in ModelStages.values():
10364
+ if version_name == ModelStages.LATEST:
10365
+ query = query.order_by(
10366
+ desc(col(ModelVersionSchema.number))
10367
+ ).limit(1)
10368
+ else:
10369
+ query = query.where(
10370
+ ModelVersionSchema.stage == version_name
10371
+ )
10372
+ error_text = (
10373
+ f"No {version_name} stage version found for "
10374
+ f"model {model_id}."
10375
+ )
10376
+ else:
10377
+ query = query.where(ModelVersionSchema.name == version_name)
10378
+ error_text = (
10379
+ f"No {version_name} version found for model {model_id}."
10380
+ )
10381
+
10382
+ elif producer_run_id:
10383
+ query = query.where(
10384
+ ModelVersionSchema.producer_run_id_if_numeric
10385
+ == producer_run_id,
10386
+ )
10387
+ error_text = (
10388
+ f"No numeric model version found for model {model_id} "
10389
+ f"and producer run {producer_run_id}."
10390
+ )
10391
+ else:
10392
+ raise ValueError(
10393
+ "Version name or producer run id need to be specified."
10394
+ )
10395
+
10220
10396
  with Session(self.engine) as session:
10221
- return (
10222
- session.exec(
10223
- select(ModelVersionSchema.id)
10224
- .where(ModelVersionSchema.model_id == model_id)
10225
- .where(ModelVersionSchema.name == version)
10226
- ).first()
10227
- is not None
10397
+ schema = session.exec(query).one_or_none()
10398
+
10399
+ if not schema:
10400
+ raise KeyError(error_text)
10401
+
10402
+ return schema.to_model(
10403
+ include_metadata=True, include_resources=True
10228
10404
  )
10229
10405
 
10230
- @track_decorator(AnalyticsEvent.CREATED_MODEL_VERSION)
10231
- def create_model_version(
10232
- self, model_version: ModelVersionRequest
10406
+ def _get_or_create_model_version(
10407
+ self,
10408
+ model_version_request: ModelVersionRequest,
10409
+ producer_run_id: Optional[UUID] = None,
10410
+ ) -> Tuple[bool, ModelVersionResponse]:
10411
+ """Get or create a model version.
10412
+
10413
+ Args:
10414
+ model_version_request: The model version request.
10415
+ producer_run_id: ID of the producer pipeline run.
10416
+
10417
+ Raises:
10418
+ EntityCreationError: If the model version creation failed.
10419
+
10420
+ Returns:
10421
+ A boolean whether the model version was created or not, and the
10422
+ model version.
10423
+ """
10424
+ try:
10425
+ model_version = self._create_model_version(
10426
+ model_version=model_version_request,
10427
+ producer_run_id=producer_run_id,
10428
+ )
10429
+ track(event=AnalyticsEvent.CREATED_MODEL_VERSION)
10430
+ return True, model_version
10431
+ except EntityCreationError:
10432
+ # Need to explicitly re-raise this here as otherwise the catching
10433
+ # of the RuntimeError would include this
10434
+ raise
10435
+ except RuntimeError:
10436
+ return False, self._get_model_version(
10437
+ model_id=model_version_request.model,
10438
+ producer_run_id=producer_run_id,
10439
+ )
10440
+ except EntityExistsError:
10441
+ return False, self._get_model_version(
10442
+ model_id=model_version_request.model,
10443
+ version_name=model_version_request.name,
10444
+ )
10445
+
10446
+ def _get_or_create_model_version_for_run(
10447
+ self, pipeline_or_step_run: Union[PipelineRunSchema, StepRunSchema]
10448
+ ) -> Optional[UUID]:
10449
+ """Get or create a model version for a pipeline or step run.
10450
+
10451
+ Args:
10452
+ pipeline_or_step_run: The pipeline or step run for which to create
10453
+ the model version.
10454
+
10455
+ Returns:
10456
+ The model version.
10457
+ """
10458
+ if isinstance(pipeline_or_step_run, PipelineRunSchema):
10459
+ producer_run_id = pipeline_or_step_run.id
10460
+ pipeline_run = pipeline_or_step_run.to_model(include_metadata=True)
10461
+ configured_model = pipeline_run.config.model
10462
+ substitutions = pipeline_run.config.substitutions
10463
+ else:
10464
+ producer_run_id = pipeline_or_step_run.pipeline_run_id
10465
+ step_run = pipeline_or_step_run.to_model(include_metadata=True)
10466
+ configured_model = step_run.config.model
10467
+ substitutions = step_run.config.substitutions
10468
+
10469
+ if not configured_model:
10470
+ return None
10471
+
10472
+ model_request = ModelRequest(
10473
+ name=format_name_template(
10474
+ configured_model.name, substitutions=substitutions
10475
+ ),
10476
+ license=configured_model.license,
10477
+ description=configured_model.description,
10478
+ audience=configured_model.audience,
10479
+ use_cases=configured_model.use_cases,
10480
+ limitations=configured_model.limitations,
10481
+ trade_offs=configured_model.trade_offs,
10482
+ ethics=configured_model.ethics,
10483
+ save_models_to_registry=configured_model.save_models_to_registry,
10484
+ user=pipeline_or_step_run.user_id,
10485
+ workspace=pipeline_or_step_run.workspace_id,
10486
+ )
10487
+
10488
+ _, model_response = self._get_or_create_model(
10489
+ model_request=model_request
10490
+ )
10491
+
10492
+ version_name = None
10493
+ if configured_model.version is not None:
10494
+ version_name = format_name_template(
10495
+ str(configured_model.version), substitutions=substitutions
10496
+ )
10497
+
10498
+ # If the model version was specified to be a numeric version or
10499
+ # stage we don't try to create it (which will fail because it is not
10500
+ # allowed) but try to fetch it immediately
10501
+ if (
10502
+ version_name.isnumeric()
10503
+ or version_name in ModelStages.values()
10504
+ ):
10505
+ return self._get_model_version(
10506
+ model_id=model_response.id, version_name=version_name
10507
+ ).id
10508
+
10509
+ model_version_request = ModelVersionRequest(
10510
+ model=model_response.id,
10511
+ name=version_name,
10512
+ description=configured_model.description,
10513
+ tags=configured_model.tags,
10514
+ user=pipeline_or_step_run.user_id,
10515
+ workspace=pipeline_or_step_run.workspace_id,
10516
+ )
10517
+
10518
+ _, model_version_response = self._get_or_create_model_version(
10519
+ model_version_request=model_version_request,
10520
+ producer_run_id=producer_run_id,
10521
+ )
10522
+ return model_version_response.id
10523
+
10524
+ def _create_model_version(
10525
+ self,
10526
+ model_version: ModelVersionRequest,
10527
+ producer_run_id: Optional[UUID] = None,
10233
10528
  ) -> ModelVersionResponse:
10234
10529
  """Creates a new model version.
10235
10530
 
10236
10531
  Args:
10237
10532
  model_version: the Model Version to be created.
10533
+ producer_run_id: ID of the pipeline run that produced this model
10534
+ version.
10238
10535
 
10239
10536
  Returns:
10240
10537
  The newly created model version.
10241
10538
 
10242
10539
  Raises:
10243
- ValueError: If `number` is not None during model version creation.
10540
+ ValueError: If the requested version name is invalid.
10244
10541
  EntityExistsError: If a model version with the given name already
10245
10542
  exists.
10246
10543
  EntityCreationError: If the model version creation failed.
10544
+ RuntimeError: If an auto-incremented model version already exists
10545
+ for the producer run.
10247
10546
  """
10248
- if model_version.number is not None:
10249
- raise ValueError(
10250
- "`number` field must be None during model version creation."
10251
- )
10547
+ has_custom_name = False
10548
+ if model_version.name:
10549
+ has_custom_name = True
10550
+ validate_name(model_version)
10252
10551
 
10253
- model = self.get_model(model_version.model)
10552
+ if model_version.name.isnumeric():
10553
+ raise ValueError(
10554
+ "Can't create model version with custom numeric model "
10555
+ "version name."
10556
+ )
10254
10557
 
10255
- has_custom_name = model_version.name is not None
10256
- if has_custom_name:
10257
- validate_name(model_version)
10558
+ if str(model_version.name).lower() in ModelStages.values():
10559
+ raise ValueError(
10560
+ "Can't create model version with a name that is used as a "
10561
+ f"model version stage ({ModelStages.values()})."
10562
+ )
10258
10563
 
10564
+ model = self.get_model(model_version.model)
10259
10565
  model_version_id = None
10260
10566
 
10261
10567
  remaining_tries = MAX_RETRIES_FOR_VERSIONED_ENTITY_CREATION
@@ -10263,17 +10569,19 @@ class SqlZenStore(BaseZenStore):
10263
10569
  remaining_tries -= 1
10264
10570
  try:
10265
10571
  with Session(self.engine) as session:
10266
- model_version.number = (
10572
+ model_version_number = (
10267
10573
  self._get_next_numeric_version_for_model(
10268
10574
  session=session,
10269
10575
  model_id=model.id,
10270
10576
  )
10271
10577
  )
10272
10578
  if not has_custom_name:
10273
- model_version.name = str(model_version.number)
10579
+ model_version.name = str(model_version_number)
10274
10580
 
10275
10581
  model_version_schema = ModelVersionSchema.from_request(
10276
- model_version
10582
+ model_version,
10583
+ model_version_number=model_version_number,
10584
+ producer_run_id=producer_run_id,
10277
10585
  )
10278
10586
  session.add(model_version_schema)
10279
10587
  session.commit()
@@ -10294,6 +10602,13 @@ class SqlZenStore(BaseZenStore):
10294
10602
  f"{model_version.name}): A model with the "
10295
10603
  "same name and version already exists."
10296
10604
  )
10605
+ elif producer_run_id and self._model_version_exists(
10606
+ model_id=model.id, producer_run_id=producer_run_id
10607
+ ):
10608
+ raise RuntimeError(
10609
+ "Auto-incremented model version already exists for "
10610
+ f"producer run {producer_run_id}."
10611
+ )
10297
10612
  elif remaining_tries == 0:
10298
10613
  raise EntityCreationError(
10299
10614
  f"Failed to create version for model "
@@ -10312,10 +10627,9 @@ class SqlZenStore(BaseZenStore):
10312
10627
  )
10313
10628
  logger.debug(
10314
10629
  "Failed to create model version %s "
10315
- "(version %s) due to an integrity error. "
10630
+ "due to an integrity error. "
10316
10631
  "Retrying in %f seconds.",
10317
10632
  model.name,
10318
- model_version.number,
10319
10633
  sleep_duration,
10320
10634
  )
10321
10635
  time.sleep(sleep_duration)
@@ -10330,6 +10644,20 @@ class SqlZenStore(BaseZenStore):
10330
10644
 
10331
10645
  return self.get_model_version(model_version_id)
10332
10646
 
10647
+ @track_decorator(AnalyticsEvent.CREATED_MODEL_VERSION)
10648
+ def create_model_version(
10649
+ self, model_version: ModelVersionRequest
10650
+ ) -> ModelVersionResponse:
10651
+ """Creates a new model version.
10652
+
10653
+ Args:
10654
+ model_version: the Model Version to be created.
10655
+
10656
+ Returns:
10657
+ The newly created model version.
10658
+ """
10659
+ return self._create_model_version(model_version=model_version)
10660
+
10333
10661
  def get_model_version(
10334
10662
  self, model_version_id: UUID, hydrate: bool = True
10335
10663
  ) -> ModelVersionResponse: