zenml-nightly 0.70.0.dev20241201__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 (193) hide show
  1. README.md +4 -4
  2. RELEASE_NOTES.md +112 -0
  3. zenml/VERSION +1 -1
  4. zenml/artifacts/artifact_config.py +8 -5
  5. zenml/artifacts/utils.py +3 -1
  6. zenml/cli/__init__.py +4 -4
  7. zenml/cli/base.py +1 -1
  8. zenml/cli/pipeline.py +48 -79
  9. zenml/cli/server.py +19 -19
  10. zenml/client.py +54 -2
  11. zenml/config/secret_reference_mixin.py +1 -1
  12. zenml/config/server_config.py +4 -0
  13. zenml/constants.py +10 -0
  14. zenml/image_builders/base_image_builder.py +5 -2
  15. zenml/image_builders/build_context.py +7 -16
  16. zenml/integrations/aws/__init__.py +3 -0
  17. zenml/integrations/aws/flavors/__init__.py +6 -0
  18. zenml/integrations/aws/flavors/aws_image_builder_flavor.py +146 -0
  19. zenml/integrations/aws/image_builders/__init__.py +20 -0
  20. zenml/integrations/aws/image_builders/aws_image_builder.py +307 -0
  21. zenml/integrations/gcp/orchestrators/vertex_orchestrator.py +1 -1
  22. zenml/integrations/kaniko/image_builders/kaniko_image_builder.py +2 -1
  23. zenml/integrations/kubernetes/flavors/kubernetes_orchestrator_flavor.py +11 -0
  24. zenml/integrations/kubernetes/step_operators/kubernetes_step_operator.py +0 -1
  25. zenml/integrations/lightning/flavors/lightning_orchestrator_flavor.py +11 -0
  26. zenml/integrations/neptune/experiment_trackers/neptune_experiment_tracker.py +7 -5
  27. zenml/integrations/neptune/experiment_trackers/run_state.py +69 -53
  28. zenml/integrations/registry.py +2 -2
  29. zenml/integrations/skypilot/flavors/skypilot_orchestrator_base_vm_config.py +12 -0
  30. zenml/integrations/wandb/flavors/wandb_experiment_tracker_flavor.py +13 -5
  31. zenml/materializers/built_in_materializer.py +1 -1
  32. zenml/model/model.py +12 -16
  33. zenml/model/utils.py +3 -1
  34. zenml/models/v2/base/filter.py +26 -30
  35. zenml/models/v2/base/scoped.py +258 -5
  36. zenml/models/v2/core/artifact_version.py +15 -26
  37. zenml/models/v2/core/code_repository.py +1 -12
  38. zenml/models/v2/core/component.py +5 -46
  39. zenml/models/v2/core/flavor.py +1 -11
  40. zenml/models/v2/core/model.py +1 -57
  41. zenml/models/v2/core/model_version.py +5 -33
  42. zenml/models/v2/core/model_version_artifact.py +11 -3
  43. zenml/models/v2/core/model_version_pipeline_run.py +14 -3
  44. zenml/models/v2/core/pipeline.py +47 -55
  45. zenml/models/v2/core/pipeline_build.py +67 -12
  46. zenml/models/v2/core/pipeline_deployment.py +0 -10
  47. zenml/models/v2/core/pipeline_run.py +91 -29
  48. zenml/models/v2/core/run_template.py +21 -29
  49. zenml/models/v2/core/schedule.py +0 -10
  50. zenml/models/v2/core/secret.py +0 -14
  51. zenml/models/v2/core/service.py +9 -16
  52. zenml/models/v2/core/service_connector.py +0 -11
  53. zenml/models/v2/core/stack.py +21 -30
  54. zenml/models/v2/core/step_run.py +18 -14
  55. zenml/models/v2/core/trigger.py +19 -3
  56. zenml/orchestrators/base_orchestrator.py +13 -1
  57. zenml/orchestrators/output_utils.py +5 -1
  58. zenml/orchestrators/step_launcher.py +9 -13
  59. zenml/orchestrators/step_run_utils.py +8 -204
  60. zenml/orchestrators/utils.py +55 -27
  61. zenml/pipelines/build_utils.py +12 -0
  62. zenml/service_connectors/service_connector_utils.py +3 -9
  63. zenml/stack/stack_component.py +1 -1
  64. zenml/stack_deployments/aws_stack_deployment.py +22 -0
  65. zenml/utils/archivable.py +65 -36
  66. zenml/utils/code_utils.py +8 -4
  67. zenml/utils/docker_utils.py +9 -0
  68. zenml/zen_server/auth.py +9 -10
  69. zenml/zen_server/dashboard/assets/{404-NVXKFp-x.js → 404-Cqu3EDCm.js} +1 -1
  70. zenml/zen_server/dashboard/assets/{@reactflow-CK0KJUen.js → @reactflow-D2Y7BWwz.js} +1 -1
  71. zenml/zen_server/dashboard/assets/{AlertDialogDropdownItem-DezXKmDf.js → AlertDialogDropdownItem-BHd71pVS.js} +1 -1
  72. zenml/zen_server/dashboard/assets/{CodeSnippet-JzR8CEtw.js → CodeSnippet-DIonwetW.js} +1 -1
  73. zenml/zen_server/dashboard/assets/{CollapsibleCard-DQW_ktMO.js → CollapsibleCard-CDnC97pB.js} +1 -1
  74. zenml/zen_server/dashboard/assets/{Commands-DL2kwkRd.js → Commands-BVEXKAOj.js} +1 -1
  75. zenml/zen_server/dashboard/assets/{ComponentBadge-D_g62Wv8.js → ComponentBadge-CrRvovox.js} +1 -1
  76. zenml/zen_server/dashboard/assets/{CopyButton-LNcWaa14.js → CopyButton-B6wGAhQv.js} +1 -1
  77. zenml/zen_server/dashboard/assets/{CsvVizualization-DknpE5ej.js → CsvVizualization-CjcT7LMm.js} +5 -5
  78. zenml/zen_server/dashboard/assets/DeleteAlertDialog-D2ELtM2W.js +1 -0
  79. zenml/zen_server/dashboard/assets/{DialogItem-Bxf8FuAT.js → DialogItem-DXIMhBgU.js} +1 -1
  80. zenml/zen_server/dashboard/assets/{Error-DYflYyps.js → Error-B8uUfTpL.js} +1 -1
  81. zenml/zen_server/dashboard/assets/{ExecutionStatus-C7zyIQKZ.js → ExecutionStatus-ibAdY-dG.js} +1 -1
  82. zenml/zen_server/dashboard/assets/{Helpbox-oYSGpLqd.js → Helpbox-BfAfhKHw.js} +1 -1
  83. zenml/zen_server/dashboard/assets/{Infobox-Cx4xGoXR.js → Infobox-M_SMOu96.js} +1 -1
  84. zenml/zen_server/dashboard/assets/{InlineAvatar-DiGOWNKF.js → InlineAvatar-DBA0a0-a.js} +1 -1
  85. zenml/zen_server/dashboard/assets/{NestedCollapsible-DYbgyKxK.js → NestedCollapsible-DpgmEFKw.js} +1 -1
  86. zenml/zen_server/dashboard/assets/{Partials-03iZf8-N.js → Partials-D_ldD9if.js} +1 -1
  87. zenml/zen_server/dashboard/assets/{ProBadge-D_EB8HNo.js → ProBadge-DQbfFotM.js} +1 -1
  88. zenml/zen_server/dashboard/assets/{ProCta-DqNS4v3x.js → ProCta-Bcpb4rcY.js} +1 -1
  89. zenml/zen_server/dashboard/assets/{ProviderIcon-Bki2aw8w.js → ProviderIcon-BZpgPigN.js} +1 -1
  90. zenml/zen_server/dashboard/assets/{ProviderRadio-8f43sPD4.js → ProviderRadio-DWPnMuQ1.js} +1 -1
  91. zenml/zen_server/dashboard/assets/RunSelector-DgRGaAc6.js +1 -0
  92. zenml/zen_server/dashboard/assets/{RunsBody-07YEO7qI.js → RunsBody-KecfSkjY.js} +1 -1
  93. zenml/zen_server/dashboard/assets/{SearchField-lp1KgU4e.js → SearchField-n-ILHnaP.js} +1 -1
  94. zenml/zen_server/dashboard/assets/{SecretTooltip-CgnbyeOx.js → SecretTooltip-B8MrX5yu.js} +1 -1
  95. zenml/zen_server/dashboard/assets/{SetPassword-CpP418A2.js → SetPassword-B_IVq_wg.js} +1 -1
  96. zenml/zen_server/dashboard/assets/StackList-TWPBYnkF.js +1 -0
  97. zenml/zen_server/dashboard/assets/{Tabs-BktHkCJJ.js → Tabs-Rg857zmd.js} +1 -1
  98. zenml/zen_server/dashboard/assets/{Tick-BlMoIlJT.js → Tick-COg4A-xo.js} +1 -1
  99. zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-Sc0A0pP-.js → UpdatePasswordSchemas-C6Aj3hm6.js} +1 -1
  100. zenml/zen_server/dashboard/assets/{UsageReason-YYduL4fj.js → UsageReason-BTLbx7w4.js} +1 -1
  101. zenml/zen_server/dashboard/assets/{WizardFooter-dgmizSJC.js → WizardFooter-BCAj69Vj.js} +1 -1
  102. zenml/zen_server/dashboard/assets/{all-pipeline-runs-query-D-c2G6lV.js → all-pipeline-runs-query-DMXkDrV2.js} +1 -1
  103. zenml/zen_server/dashboard/assets/code-snippets-CqONne41.js +13 -0
  104. zenml/zen_server/dashboard/assets/{create-stack-DM_JPgef.js → create-stack-HfdbhLs4.js} +1 -1
  105. zenml/zen_server/dashboard/assets/dates-3pMLCNrD.js +1 -0
  106. zenml/zen_server/dashboard/assets/delete-run-DZ4hIXff.js +1 -0
  107. zenml/zen_server/dashboard/assets/{form-schemas-K6FYKjwa.js → form-schemas-B0AVEd9b.js} +1 -1
  108. zenml/zen_server/dashboard/assets/{index-BAkC7FXi.js → index-DPqSWjug.js} +1 -1
  109. zenml/zen_server/dashboard/assets/{index-CEV4Cvaf.js → index-DScjfBRb.js} +1 -1
  110. zenml/zen_server/dashboard/assets/index-DXvT1_Um.css +1 -0
  111. zenml/zen_server/dashboard/assets/{index-CCOPpudF.js → index-FO-p0GU7.js} +5 -5
  112. zenml/zen_server/dashboard/assets/{index-B1mVPYxf.js → index-I3bKUGUj.js} +1 -1
  113. zenml/zen_server/dashboard/assets/key-icon-aH-QIa5R.js +1 -0
  114. zenml/zen_server/dashboard/assets/login-command-CkqxPtV3.js +1 -0
  115. zenml/zen_server/dashboard/assets/{login-mutation-hf-lK87O.js → login-mutation-BQeo4wTY.js} +1 -1
  116. zenml/zen_server/dashboard/assets/{not-found-BGirLjU-.js → not-found-gAJ5aDdR.js} +1 -1
  117. zenml/zen_server/dashboard/assets/page-9Y9-gig0.js +1 -0
  118. zenml/zen_server/dashboard/assets/{page-DjRJCGb3.js → page-AUwiQ14W.js} +1 -1
  119. zenml/zen_server/dashboard/assets/page-B6XU7yYT.js +2 -0
  120. zenml/zen_server/dashboard/assets/{page-C00YAkaB.js → page-BKZYc2Zv.js} +1 -1
  121. zenml/zen_server/dashboard/assets/{page-CdMWnQak.js → page-BU9FG4sR.js} +1 -1
  122. zenml/zen_server/dashboard/assets/{page-D7S3aCbF.js → page-B_Apk3xg.js} +1 -1
  123. zenml/zen_server/dashboard/assets/{page-Djikxq_S.js → page-BdowiCbr.js} +1 -1
  124. zenml/zen_server/dashboard/assets/page-Bg8OjTRe.js +1 -0
  125. zenml/zen_server/dashboard/assets/page-BxL4qD4_.js +1 -0
  126. zenml/zen_server/dashboard/assets/{page-DakHVWXF.js → page-CWxT5K5J.js} +1 -1
  127. zenml/zen_server/dashboard/assets/page-CXuQufSe.js +1 -0
  128. zenml/zen_server/dashboard/assets/{page-DLC-bNBP.js → page-CcQr8CPP.js} +1 -1
  129. zenml/zen_server/dashboard/assets/{page-CD-DcWoy.js → page-Ce4Hrjnr.js} +1 -1
  130. zenml/zen_server/dashboard/assets/page-CiYxgZP_.js +1 -0
  131. zenml/zen_server/dashboard/assets/page-Cldq1mpe.js +1 -0
  132. zenml/zen_server/dashboard/assets/{page-BDigxVpo.js → page-D4wdonLm.js} +1 -1
  133. zenml/zen_server/dashboard/assets/{page-D6uU2ax4.js → page-D8ObrbH8.js} +1 -1
  134. zenml/zen_server/dashboard/assets/{page-DXSTpqRD.js → page-DFuAUGt4.js} +1 -1
  135. zenml/zen_server/dashboard/assets/{page-CbpvrsDL.js → page-DGazBpuP.js} +1 -1
  136. zenml/zen_server/dashboard/assets/{page-COXXJj1k.js → page-DO1UcqPX.js} +1 -1
  137. zenml/zen_server/dashboard/assets/page-DRYXdL5o.js +1 -0
  138. zenml/zen_server/dashboard/assets/{page-Df-Fw0aq.js → page-DYEquBC2.js} +1 -1
  139. zenml/zen_server/dashboard/assets/page-Dk32IeZm.js +1 -0
  140. zenml/zen_server/dashboard/assets/{page-yYC9OI-E.js → page-I3nKFGie.js} +1 -1
  141. zenml/zen_server/dashboard/assets/{page-6m6yHHlE.js → page-M0w-n6vn.js} +1 -1
  142. zenml/zen_server/dashboard/assets/{page-Vcxara9U.js → page-R5dx3xGF.js} +1 -1
  143. zenml/zen_server/dashboard/assets/{page-BR68V0V1.js → page-bT5pOvcB.js} +1 -1
  144. zenml/zen_server/dashboard/assets/page-hUqK889I.js +6 -0
  145. zenml/zen_server/dashboard/assets/{page-CjGdWY13.js → page-h_Stveon.js} +1 -1
  146. zenml/zen_server/dashboard/assets/{page-D01JhjQB.js → page-r8XK5vR7.js} +1 -1
  147. zenml/zen_server/dashboard/assets/page-u_-ZXBKb.js +1 -0
  148. zenml/zen_server/dashboard/assets/page-zaMqB_ao.js +1 -0
  149. zenml/zen_server/dashboard/assets/{persist-GjC8PZoC.js → persist-AppN1B0J.js} +1 -1
  150. zenml/zen_server/dashboard/assets/{persist-Coz7ZWvz.js → persist-DAUi_3za.js} +1 -1
  151. zenml/zen_server/dashboard/assets/service-BqqeXLEe.js +2 -0
  152. zenml/zen_server/dashboard/assets/{sharedSchema-CQb14VSr.js → sharedSchema-uXN9FLLk.js} +1 -1
  153. zenml/zen_server/dashboard/assets/{stack-detail-query-OPEW-cDJ.js → stack-detail-query-XfZBiBP2.js} +1 -1
  154. zenml/zen_server/dashboard/assets/{update-server-settings-mutation-LwuQfHYn.js → update-server-settings-mutation-BWmgVJwA.js} +1 -1
  155. zenml/zen_server/dashboard/assets/{url-CkvKAnwF.js → url-BLwMbzES.js} +1 -1
  156. zenml/zen_server/dashboard/index.html +4 -4
  157. zenml/zen_server/deploy/helm/Chart.yaml +1 -1
  158. zenml/zen_server/deploy/helm/README.md +2 -2
  159. zenml/zen_server/rbac/rbac_sql_zen_store.py +173 -0
  160. zenml/zen_server/routers/auth_endpoints.py +22 -11
  161. zenml/zen_server/routers/steps_endpoints.py +7 -1
  162. zenml/zen_server/template_execution/utils.py +3 -1
  163. zenml/zen_server/utils.py +4 -3
  164. zenml/zen_stores/base_zen_store.py +10 -2
  165. zenml/zen_stores/migrations/versions/0.71.0_release.py +23 -0
  166. zenml/zen_stores/migrations/versions/26351d482b9e_add_step_run_unique_constraint.py +37 -0
  167. zenml/zen_stores/migrations/versions/a1237ba94fd8_add_model_version_producer_run_unique_.py +68 -0
  168. zenml/zen_stores/rest_zen_store.py +76 -43
  169. zenml/zen_stores/schemas/model_schemas.py +42 -6
  170. zenml/zen_stores/schemas/pipeline_deployment_schemas.py +7 -7
  171. zenml/zen_stores/schemas/pipeline_run_schemas.py +12 -6
  172. zenml/zen_stores/schemas/pipeline_schemas.py +5 -0
  173. zenml/zen_stores/schemas/step_run_schemas.py +8 -1
  174. zenml/zen_stores/sql_zen_store.py +332 -100
  175. {zenml_nightly-0.70.0.dev20241201.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/METADATA +5 -5
  176. {zenml_nightly-0.70.0.dev20241201.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/RECORD +179 -164
  177. zenml/zen_server/dashboard/assets/RunSelector-DkPiIiNr.js +0 -1
  178. zenml/zen_server/dashboard/assets/StackList-WvuKQusZ.js +0 -1
  179. zenml/zen_server/dashboard/assets/delete-run-CJdh1P_h.js +0 -1
  180. zenml/zen_server/dashboard/assets/index-DlGvJQPn.css +0 -1
  181. zenml/zen_server/dashboard/assets/page-0JE_-Ec1.js +0 -1
  182. zenml/zen_server/dashboard/assets/page-BRLpxOt0.js +0 -1
  183. zenml/zen_server/dashboard/assets/page-BU7huvKw.js +0 -6
  184. zenml/zen_server/dashboard/assets/page-BvqLv2Ky.js +0 -1
  185. zenml/zen_server/dashboard/assets/page-CwxrFarU.js +0 -1
  186. zenml/zen_server/dashboard/assets/page-DfbXf_8s.js +0 -1
  187. zenml/zen_server/dashboard/assets/page-Dnovpa0i.js +0 -3
  188. zenml/zen_server/dashboard/assets/page-Dot3LPmL.js +0 -1
  189. zenml/zen_server/dashboard/assets/page-Xynx4btY.js +0 -14
  190. zenml/zen_server/dashboard/assets/page-YpKAqVSa.js +0 -1
  191. {zenml_nightly-0.70.0.dev20241201.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/LICENSE +0 -0
  192. {zenml_nightly-0.70.0.dev20241201.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/WHEEL +0 -0
  193. {zenml_nightly-0.70.0.dev20241201.dist-info → zenml_nightly-0.71.0.dev20241220.dist-info}/entry_points.txt +0 -0
@@ -55,7 +55,7 @@ from pydantic import (
55
55
  field_validator,
56
56
  model_validator,
57
57
  )
58
- from sqlalchemy import asc, case, desc, func
58
+ from sqlalchemy import func
59
59
  from sqlalchemy.engine import URL, Engine, make_url
60
60
  from sqlalchemy.exc import (
61
61
  ArgumentError,
@@ -71,6 +71,7 @@ from sqlmodel import (
71
71
  col,
72
72
  create_engine,
73
73
  delete,
74
+ desc,
74
75
  or_,
75
76
  select,
76
77
  )
@@ -100,7 +101,6 @@ from zenml.constants import (
100
101
  ENV_ZENML_SERVER,
101
102
  FINISHED_ONBOARDING_SURVEY_KEY,
102
103
  MAX_RETRIES_FOR_VERSIONED_ENTITY_CREATION,
103
- SORT_PIPELINES_BY_LATEST_RUN_KEY,
104
104
  SQL_STORE_BACKUP_DIRECTORY_NAME,
105
105
  TEXT_FIELD_MAX_LENGTH,
106
106
  handle_bool_env_var,
@@ -117,7 +117,6 @@ from zenml.enums import (
117
117
  OnboardingStep,
118
118
  SecretScope,
119
119
  SecretsStoreType,
120
- SorterOps,
121
120
  StackComponentType,
122
121
  StackDeploymentProvider,
123
122
  StepRunInputArtifactType,
@@ -298,7 +297,11 @@ from zenml.utils.networking_utils import (
298
297
  replace_localhost_with_internal_hostname,
299
298
  )
300
299
  from zenml.utils.pydantic_utils import before_validator_handler
301
- 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
+ )
302
305
  from zenml.zen_stores import template_utils
303
306
  from zenml.zen_stores.base_zen_store import (
304
307
  BaseZenStore,
@@ -4358,69 +4361,14 @@ class SqlZenStore(BaseZenStore):
4358
4361
  Returns:
4359
4362
  A list of all pipelines matching the filter criteria.
4360
4363
  """
4361
- query: Union[Select[Any], SelectOfScalar[Any]] = select(PipelineSchema)
4362
- _custom_conversion: Optional[Callable[[Any], PipelineResponse]] = None
4363
-
4364
- column, operand = pipeline_filter_model.sorting_params
4365
- if column == SORT_PIPELINES_BY_LATEST_RUN_KEY:
4366
- with Session(self.engine) as session:
4367
- max_date_subquery = (
4368
- # If no run exists for the pipeline yet, we use the pipeline
4369
- # creation date as a fallback, otherwise newly created
4370
- # pipeline would always be at the top/bottom
4371
- select(
4372
- PipelineSchema.id,
4373
- case(
4374
- (
4375
- func.max(PipelineRunSchema.created).is_(None),
4376
- PipelineSchema.created,
4377
- ),
4378
- else_=func.max(PipelineRunSchema.created),
4379
- ).label("run_or_created"),
4380
- )
4381
- .outerjoin(
4382
- PipelineRunSchema,
4383
- PipelineSchema.id == PipelineRunSchema.pipeline_id, # type: ignore[arg-type]
4384
- )
4385
- .group_by(col(PipelineSchema.id))
4386
- .subquery()
4387
- )
4388
-
4389
- if operand == SorterOps.DESCENDING:
4390
- sort_clause = desc
4391
- else:
4392
- sort_clause = asc
4393
-
4394
- query = (
4395
- # We need to include the subquery in the select here to
4396
- # make this query work with the distinct statement. This
4397
- # result will be removed in the custom conversion function
4398
- # applied later
4399
- select(PipelineSchema, max_date_subquery.c.run_or_created)
4400
- .where(PipelineSchema.id == max_date_subquery.c.id)
4401
- .order_by(sort_clause(max_date_subquery.c.run_or_created))
4402
- # We always add the `id` column as a tiebreaker to ensure a
4403
- # stable, repeatable order of items, otherwise subsequent
4404
- # pages might contain the same items.
4405
- .order_by(col(PipelineSchema.id))
4406
- )
4407
-
4408
- def _custom_conversion(row: Any) -> PipelineResponse:
4409
- return cast(
4410
- PipelineResponse,
4411
- row[0].to_model(
4412
- include_metadata=hydrate, include_resources=True
4413
- ),
4414
- )
4415
-
4416
4364
  with Session(self.engine) as session:
4365
+ query = select(PipelineSchema)
4417
4366
  return self.filter_and_paginate(
4418
4367
  session=session,
4419
4368
  query=query,
4420
4369
  table=PipelineSchema,
4421
4370
  filter_model=pipeline_filter_model,
4422
4371
  hydrate=hydrate,
4423
- custom_schema_to_model_conversion=_custom_conversion,
4424
4372
  )
4425
4373
 
4426
4374
  def count_pipelines(self, filter_model: Optional[PipelineFilter]) -> int:
@@ -5211,6 +5159,20 @@ class SqlZenStore(BaseZenStore):
5211
5159
  "already exists."
5212
5160
  )
5213
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
+
5214
5176
  return new_run.to_model(
5215
5177
  include_metadata=True, include_resources=True
5216
5178
  )
@@ -8167,25 +8129,17 @@ class SqlZenStore(BaseZenStore):
8167
8129
  f"with ID '{step_run.pipeline_run_id}' found."
8168
8130
  )
8169
8131
 
8170
- # Check if the step name already exists in the pipeline run
8171
- existing_step_run = session.exec(
8172
- select(StepRunSchema)
8173
- .where(StepRunSchema.name == step_run.name)
8174
- .where(
8175
- StepRunSchema.pipeline_run_id == step_run.pipeline_run_id
8176
- )
8177
- ).first()
8178
- 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:
8179
8137
  raise EntityExistsError(
8180
8138
  f"Unable to create step `{step_run.name}`: A step with "
8181
8139
  f"this name already exists in the pipeline run with ID "
8182
8140
  f"'{step_run.pipeline_run_id}'."
8183
8141
  )
8184
8142
 
8185
- # Create the step
8186
- step_schema = StepRunSchema.from_request(step_run)
8187
- session.add(step_schema)
8188
-
8189
8143
  # Add logs entry for the step if exists
8190
8144
  if step_run.logs is not None:
8191
8145
  log_entry = LogsSchema(
@@ -8281,6 +8235,21 @@ class SqlZenStore(BaseZenStore):
8281
8235
  session.commit()
8282
8236
  session.refresh(step_schema)
8283
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
+
8284
8253
  return step_schema.to_model(
8285
8254
  include_metadata=True, include_resources=True
8286
8255
  )
@@ -8633,7 +8602,11 @@ class SqlZenStore(BaseZenStore):
8633
8602
 
8634
8603
  # Deployment always exists for pipeline runs of newer versions
8635
8604
  assert pipeline_run.deployment
8636
- 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
+ )
8637
8610
  new_status = get_pipeline_run_status(
8638
8611
  step_statuses=[
8639
8612
  ExecutionStatus(step_run.status) for step_run in step_runs
@@ -10279,6 +10252,22 @@ class SqlZenStore(BaseZenStore):
10279
10252
 
10280
10253
  # ----------------------------- Model Versions -----------------------------
10281
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
+
10282
10271
  def _get_next_numeric_version_for_model(
10283
10272
  self, session: Session, model_id: UUID
10284
10273
  ) -> int:
@@ -10303,55 +10292,276 @@ class SqlZenStore(BaseZenStore):
10303
10292
  else:
10304
10293
  return int(current_max_version) + 1
10305
10294
 
10306
- 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:
10307
10301
  """Check if a model version with a certain version exists.
10308
10302
 
10309
10303
  Args:
10310
10304
  model_id: The model ID of the version.
10311
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.
10312
10308
 
10313
10309
  Returns:
10314
- If a model version with the given version name exists.
10310
+ If a model version for the given arguments exists.
10315
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
+
10316
10325
  with Session(self.engine) as session:
10317
- return (
10318
- session.exec(
10319
- select(ModelVersionSchema.id)
10320
- .where(ModelVersionSchema.model_id == model_id)
10321
- .where(ModelVersionSchema.name == version)
10322
- ).first()
10323
- is not None
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.
10346
+
10347
+ Returns:
10348
+ The model version.
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."
10324
10394
  )
10325
10395
 
10326
- @track_decorator(AnalyticsEvent.CREATED_MODEL_VERSION)
10327
- def create_model_version(
10328
- self, model_version: ModelVersionRequest
10396
+ with Session(self.engine) as session:
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
10404
+ )
10405
+
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,
10329
10528
  ) -> ModelVersionResponse:
10330
10529
  """Creates a new model version.
10331
10530
 
10332
10531
  Args:
10333
10532
  model_version: the Model Version to be created.
10533
+ producer_run_id: ID of the pipeline run that produced this model
10534
+ version.
10334
10535
 
10335
10536
  Returns:
10336
10537
  The newly created model version.
10337
10538
 
10338
10539
  Raises:
10339
- ValueError: If `number` is not None during model version creation.
10540
+ ValueError: If the requested version name is invalid.
10340
10541
  EntityExistsError: If a model version with the given name already
10341
10542
  exists.
10342
10543
  EntityCreationError: If the model version creation failed.
10544
+ RuntimeError: If an auto-incremented model version already exists
10545
+ for the producer run.
10343
10546
  """
10344
- if model_version.number is not None:
10345
- raise ValueError(
10346
- "`number` field must be None during model version creation."
10347
- )
10547
+ has_custom_name = False
10548
+ if model_version.name:
10549
+ has_custom_name = True
10550
+ validate_name(model_version)
10348
10551
 
10349
- 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
+ )
10350
10557
 
10351
- has_custom_name = model_version.name is not None
10352
- if has_custom_name:
10353
- 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
+ )
10354
10563
 
10564
+ model = self.get_model(model_version.model)
10355
10565
  model_version_id = None
10356
10566
 
10357
10567
  remaining_tries = MAX_RETRIES_FOR_VERSIONED_ENTITY_CREATION
@@ -10359,17 +10569,19 @@ class SqlZenStore(BaseZenStore):
10359
10569
  remaining_tries -= 1
10360
10570
  try:
10361
10571
  with Session(self.engine) as session:
10362
- model_version.number = (
10572
+ model_version_number = (
10363
10573
  self._get_next_numeric_version_for_model(
10364
10574
  session=session,
10365
10575
  model_id=model.id,
10366
10576
  )
10367
10577
  )
10368
10578
  if not has_custom_name:
10369
- model_version.name = str(model_version.number)
10579
+ model_version.name = str(model_version_number)
10370
10580
 
10371
10581
  model_version_schema = ModelVersionSchema.from_request(
10372
- model_version
10582
+ model_version,
10583
+ model_version_number=model_version_number,
10584
+ producer_run_id=producer_run_id,
10373
10585
  )
10374
10586
  session.add(model_version_schema)
10375
10587
  session.commit()
@@ -10390,6 +10602,13 @@ class SqlZenStore(BaseZenStore):
10390
10602
  f"{model_version.name}): A model with the "
10391
10603
  "same name and version already exists."
10392
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
+ )
10393
10612
  elif remaining_tries == 0:
10394
10613
  raise EntityCreationError(
10395
10614
  f"Failed to create version for model "
@@ -10408,10 +10627,9 @@ class SqlZenStore(BaseZenStore):
10408
10627
  )
10409
10628
  logger.debug(
10410
10629
  "Failed to create model version %s "
10411
- "(version %s) due to an integrity error. "
10630
+ "due to an integrity error. "
10412
10631
  "Retrying in %f seconds.",
10413
10632
  model.name,
10414
- model_version.number,
10415
10633
  sleep_duration,
10416
10634
  )
10417
10635
  time.sleep(sleep_duration)
@@ -10426,6 +10644,20 @@ class SqlZenStore(BaseZenStore):
10426
10644
 
10427
10645
  return self.get_model_version(model_version_id)
10428
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
+
10429
10661
  def get_model_version(
10430
10662
  self, model_version_id: UUID, hydrate: bool = True
10431
10663
  ) -> ModelVersionResponse:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: zenml-nightly
3
- Version: 0.70.0.dev20241201
3
+ Version: 0.71.0.dev20241220
4
4
  Summary: ZenML: Write production-ready ML code.
5
5
  Home-page: https://zenml.io
6
6
  License: Apache-2.0
@@ -288,7 +288,7 @@ Or, through our CLI command:
288
288
  zenml stack deploy --provider aws
289
289
  ```
290
290
 
291
- Alternatively, if the necessary pieces of infrastructure is already deployed, you can register a cloud stack seamlessly through the stack wizard:
291
+ Alternatively, if the necessary pieces of infrastructure are already deployed, you can register a cloud stack seamlessly through the stack wizard:
292
292
 
293
293
  ```bash
294
294
  zenml stack register <STACK_NAME> --provider aws
@@ -336,9 +336,9 @@ def trainer(training_df: pd.DataFrame) -> Annotated["model", torch.nn.Module]:
336
336
 
337
337
  ![Exploring ZenML Models](/docs/book/.gitbook/assets/readme_mcp.gif)
338
338
 
339
- ### Purpose built for machine learning with integration to you favorite tools
339
+ ### Purpose built for machine learning with integrations to your favorite tools
340
340
 
341
- While ZenML brings a lot of value of the box, it also integrates into your existing tooling and infrastructure without you having to be locked in.
341
+ While ZenML brings a lot of value out of the box, it also integrates into your existing tooling and infrastructure without you having to be locked in.
342
342
 
343
343
  ```python
344
344
  from bentoml._internal.bento import bento
@@ -474,7 +474,7 @@ the Apache License Version 2.0.
474
474
  <a href="https://github.com/zenml-io/zenml-projects">Projects Showcase</a>
475
475
  <br />
476
476
  <br />
477
- 🎉 Version 0.70.0 is out. Check out the release notes
477
+ 🎉 Version 0.71.0 is out. Check out the release notes
478
478
  <a href="https://github.com/zenml-io/zenml/releases">here</a>.
479
479
  <br />
480
480
  🖥️ Download our VS Code Extension <a href="https://marketplace.visualstudio.com/items?itemName=ZenML.zenml-vscode">here</a>.