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
@@ -0,0 +1,135 @@
1
+ """Separate run metadata into resource link table with new UUIDs.
2
+
3
+ Revision ID: cc269488e5a9
4
+ Revises: b73bc71f1106
5
+ Create Date: 2024-11-12 09:46:46.587478
6
+ """
7
+
8
+ import uuid
9
+
10
+ import sqlalchemy as sa
11
+ import sqlmodel
12
+ from alembic import op
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision = "cc269488e5a9"
16
+ down_revision = "b73bc71f1106"
17
+ branch_labels = None
18
+ depends_on = None
19
+
20
+
21
+ def upgrade() -> None:
22
+ """Creates the 'run_metadata_resource' table and migrates data."""
23
+ op.create_table(
24
+ "run_metadata_resource",
25
+ sa.Column(
26
+ "id",
27
+ sqlmodel.sql.sqltypes.GUID(),
28
+ nullable=False,
29
+ primary_key=True,
30
+ ),
31
+ sa.Column("resource_id", sqlmodel.sql.sqltypes.GUID(), nullable=False),
32
+ sa.Column("resource_type", sa.String(length=255), nullable=False),
33
+ sa.Column(
34
+ "run_metadata_id",
35
+ sqlmodel.sql.sqltypes.GUID(),
36
+ sa.ForeignKey("run_metadata.id", ondelete="CASCADE"),
37
+ nullable=False,
38
+ ),
39
+ )
40
+
41
+ connection = op.get_bind()
42
+
43
+ run_metadata_data = connection.execute(
44
+ sa.text("""
45
+ SELECT id, resource_id, resource_type
46
+ FROM run_metadata
47
+ """)
48
+ ).fetchall()
49
+
50
+ # Prepare data with new UUIDs for bulk insert
51
+ resource_data = [
52
+ {
53
+ "id": str(uuid.uuid4()), # Generate a new UUID for each row
54
+ "resource_id": row.resource_id,
55
+ "resource_type": row.resource_type,
56
+ "run_metadata_id": row.id,
57
+ }
58
+ for row in run_metadata_data
59
+ ]
60
+
61
+ # Perform bulk insert into `run_metadata_resource`
62
+ if resource_data: # Only perform insert if there's data to migrate
63
+ op.bulk_insert(
64
+ sa.table(
65
+ "run_metadata_resource",
66
+ sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False),
67
+ sa.Column(
68
+ "resource_id", sqlmodel.sql.sqltypes.GUID(), nullable=False
69
+ ),
70
+ sa.Column(
71
+ "resource_type", sa.String(length=255), nullable=False
72
+ ),
73
+ sa.Column(
74
+ "run_metadata_id",
75
+ sqlmodel.sql.sqltypes.GUID(),
76
+ nullable=False,
77
+ ), # Changed to BIGINT
78
+ ),
79
+ resource_data,
80
+ )
81
+
82
+ op.drop_column("run_metadata", "resource_id")
83
+ op.drop_column("run_metadata", "resource_type")
84
+
85
+ op.add_column(
86
+ "run_metadata",
87
+ sa.Column(
88
+ "publisher_step_id", sqlmodel.sql.sqltypes.GUID(), nullable=True
89
+ ),
90
+ )
91
+
92
+
93
+ def downgrade() -> None:
94
+ """Reverts the 'run_metadata_resource' table and migrates data back."""
95
+ # Recreate the `resource_id` and `resource_type` columns in `run_metadata`
96
+ op.add_column(
97
+ "run_metadata",
98
+ sa.Column("resource_id", sqlmodel.sql.sqltypes.GUID(), nullable=True),
99
+ )
100
+ op.add_column(
101
+ "run_metadata",
102
+ sa.Column("resource_type", sa.String(length=255), nullable=True),
103
+ )
104
+
105
+ # Migrate data back from `run_metadata_resource` to `run_metadata`
106
+ connection = op.get_bind()
107
+
108
+ # Fetch data from `run_metadata_resource`
109
+ run_metadata_resource_data = connection.execute(
110
+ sa.text("""
111
+ SELECT resource_id, resource_type, run_metadata_id
112
+ FROM run_metadata_resource
113
+ """)
114
+ ).fetchall()
115
+
116
+ # Update `run_metadata` with the data from `run_metadata_resource`
117
+ for row in run_metadata_resource_data:
118
+ connection.execute(
119
+ sa.text("""
120
+ UPDATE run_metadata
121
+ SET resource_id = :resource_id, resource_type = :resource_type
122
+ WHERE id = :run_metadata_id
123
+ """),
124
+ {
125
+ "resource_id": row.resource_id,
126
+ "resource_type": row.resource_type,
127
+ "run_metadata_id": row.run_metadata_id,
128
+ },
129
+ )
130
+
131
+ # Drop the `run_metadata_resource` table
132
+ op.drop_table("run_metadata_resource")
133
+
134
+ # Drop the cached column
135
+ op.drop_column("run_metadata", "publisher_step_id")
@@ -59,12 +59,13 @@ def _migrate_artifact_type() -> None:
59
59
  {"id_": artifact_version_id, "type": "ServiceArtifact"}
60
60
  )
61
61
 
62
- connection.execute(
63
- sa.update(artifact_version_table).where(
64
- artifact_version_table.c.id == sa.bindparam("id_")
65
- ),
66
- updates,
67
- )
62
+ if updates:
63
+ connection.execute(
64
+ sa.update(artifact_version_table).where(
65
+ artifact_version_table.c.id == sa.bindparam("id_")
66
+ ),
67
+ updates,
68
+ )
68
69
 
69
70
 
70
71
  def upgrade() -> None:
@@ -3873,13 +3873,17 @@ class RestZenStore(BaseZenStore):
3873
3873
 
3874
3874
  def get_api_token(
3875
3875
  self,
3876
+ token_type: APITokenType = APITokenType.WORKLOAD,
3877
+ expires_in: Optional[int] = None,
3876
3878
  schedule_id: Optional[UUID] = None,
3877
3879
  pipeline_run_id: Optional[UUID] = None,
3878
3880
  step_run_id: Optional[UUID] = None,
3879
3881
  ) -> str:
3880
- """Get an API token for a workload.
3882
+ """Get an API token.
3881
3883
 
3882
3884
  Args:
3885
+ token_type: The type of the token to get.
3886
+ expires_in: The time in seconds until the token expires.
3883
3887
  schedule_id: The ID of the schedule to get a token for.
3884
3888
  pipeline_run_id: The ID of the pipeline run to get a token for.
3885
3889
  step_run_id: The ID of the step run to get a token for.
@@ -3891,9 +3895,10 @@ class RestZenStore(BaseZenStore):
3891
3895
  ValueError: if the server response is not valid.
3892
3896
  """
3893
3897
  params: Dict[str, Any] = {
3894
- # Python clients may only request workload tokens.
3895
- "token_type": APITokenType.WORKLOAD.value,
3898
+ "token_type": token_type.value,
3896
3899
  }
3900
+ if expires_in:
3901
+ params["expires_in"] = expires_in
3897
3902
  if schedule_id:
3898
3903
  params["schedule_id"] = schedule_id
3899
3904
  if pipeline_run_id:
@@ -4062,7 +4067,7 @@ class RestZenStore(BaseZenStore):
4062
4067
  "you should use a service account API key to authenticate to "
4063
4068
  "the server instead of temporary CLI login credentials. For "
4064
4069
  "more information, see "
4065
- "https://docs.zenml.io/how-to/connecting-to-zenml/connect-with-a-service-account"
4070
+ "https://docs.zenml.io/how-to/project-setup-and-management/connecting-to-zenml/connect-with-a-service-account"
4066
4071
  )
4067
4072
 
4068
4073
  if api_key is not None:
@@ -4344,46 +4349,74 @@ class RestZenStore(BaseZenStore):
4344
4349
  {source_context.name: source_context.get().value}
4345
4350
  )
4346
4351
 
4347
- try:
4348
- return self._handle_response(
4349
- self.session.request(
4350
- method,
4351
- url,
4352
- params=params,
4353
- verify=self.config.verify_ssl,
4354
- timeout=timeout or self.config.http_timeout,
4355
- **kwargs,
4356
- )
4357
- )
4358
- except CredentialsNotValid:
4359
- # NOTE: CredentialsNotValid is raised only when the server
4360
- # explicitly indicates that the credentials are not valid and they
4361
- # can be thrown away.
4362
-
4363
- # We authenticate or re-authenticate here and then try the request
4364
- # again, this time with a valid API token in the header.
4365
- self.authenticate(
4366
- # If the last request was authenticated with an API token,
4367
- # we force a re-authentication to get a fresh token.
4368
- force=self._api_token is not None
4369
- )
4370
-
4371
- try:
4372
- return self._handle_response(
4373
- self.session.request(
4374
- method,
4375
- url,
4376
- params=params,
4377
- verify=self.config.verify_ssl,
4378
- timeout=self.config.http_timeout,
4379
- **kwargs,
4352
+ # If the server replies with a credentials validation (401 Unauthorized)
4353
+ # error, we (re-)authenticate and retry the request here in the
4354
+ # following cases:
4355
+ #
4356
+ # 1. initial authentication: the last request was not authenticated
4357
+ # with an API token.
4358
+ # 2. re-authentication: the last request was authenticated with an API
4359
+ # token that was rejected by the server. This is to cover the case
4360
+ # of expired tokens that can be refreshed by the client automatically
4361
+ # without user intervention from other sources (e.g. API keys).
4362
+ #
4363
+ # NOTE: it can happen that the same request is retried here for up to
4364
+ # two times: once after initial authentication and once after
4365
+ # re-authentication.
4366
+ re_authenticated = False
4367
+ while True:
4368
+ try:
4369
+ return self._handle_response(
4370
+ self.session.request(
4371
+ method,
4372
+ url,
4373
+ params=params,
4374
+ verify=self.config.verify_ssl,
4375
+ timeout=timeout or self.config.http_timeout,
4376
+ **kwargs,
4377
+ )
4380
4378
  )
4381
- )
4382
- except CredentialsNotValid as e:
4383
- raise CredentialsNotValid(
4384
- "The current credentials are no longer valid. Please log in "
4385
- "again using 'zenml login'."
4386
- ) from e
4379
+ except CredentialsNotValid as e:
4380
+ # NOTE: CredentialsNotValid is raised only when the server
4381
+ # explicitly indicates that the credentials are not valid and
4382
+ # they can be thrown away or when the request is not
4383
+ # authenticated at all.
4384
+
4385
+ if self._api_token is None:
4386
+ # The last request was not authenticated with an API
4387
+ # token at all. We authenticate here and then try the
4388
+ # request again, this time with a valid API token in the
4389
+ # header.
4390
+ logger.debug(
4391
+ f"The last request was not authenticated: {e}\n"
4392
+ "Re-authenticating and retrying..."
4393
+ )
4394
+ self.authenticate()
4395
+ elif not re_authenticated:
4396
+ # The last request was authenticated with an API token
4397
+ # that was rejected by the server. We attempt a
4398
+ # re-authentication here and then retry the request.
4399
+ logger.debug(
4400
+ "The last request was authenticated with an API token "
4401
+ f"that was rejected by the server: {e}\n"
4402
+ "Re-authenticating and retrying..."
4403
+ )
4404
+ re_authenticated = True
4405
+ self.authenticate(
4406
+ # Ignore the current token and force a re-authentication
4407
+ force=True
4408
+ )
4409
+ else:
4410
+ # The last request was made after re-authenticating but
4411
+ # still failed. Bailing out.
4412
+ logger.debug(
4413
+ f"The last request failed after re-authenticating: {e}\n"
4414
+ "Bailing out..."
4415
+ )
4416
+ raise CredentialsNotValid(
4417
+ "The current credentials are no longer valid. Please "
4418
+ "log in again using 'zenml login'."
4419
+ ) from e
4387
4420
 
4388
4421
  def get(
4389
4422
  self,
@@ -39,7 +39,10 @@ from zenml.zen_stores.schemas.pipeline_deployment_schemas import (
39
39
  from zenml.zen_stores.schemas.pipeline_run_schemas import PipelineRunSchema
40
40
  from zenml.zen_stores.schemas.pipeline_schemas import PipelineSchema
41
41
  from zenml.zen_stores.schemas.workspace_schemas import WorkspaceSchema
42
- from zenml.zen_stores.schemas.run_metadata_schemas import RunMetadataSchema
42
+ from zenml.zen_stores.schemas.run_metadata_schemas import (
43
+ RunMetadataResourceSchema,
44
+ RunMetadataSchema,
45
+ )
43
46
  from zenml.zen_stores.schemas.schedule_schema import ScheduleSchema
44
47
  from zenml.zen_stores.schemas.secret_schemas import SecretSchema
45
48
  from zenml.zen_stores.schemas.service_schemas import ServiceSchema
@@ -90,6 +93,7 @@ __all__ = [
90
93
  "PipelineDeploymentSchema",
91
94
  "PipelineRunSchema",
92
95
  "PipelineSchema",
96
+ "RunMetadataResourceSchema",
93
97
  "RunMetadataSchema",
94
98
  "ScheduleSchema",
95
99
  "SecretSchema",
@@ -13,7 +13,6 @@
13
13
  # permissions and limitations under the License.
14
14
  """SQLModel implementation of artifact table."""
15
15
 
16
- import json
17
16
  from datetime import datetime
18
17
  from typing import TYPE_CHECKING, Any, List, Optional
19
18
  from uuid import UUID
@@ -50,6 +49,7 @@ from zenml.zen_stores.schemas.step_run_schemas import (
50
49
  StepRunOutputArtifactSchema,
51
50
  )
52
51
  from zenml.zen_stores.schemas.user_schemas import UserSchema
52
+ from zenml.zen_stores.schemas.utils import RunMetadataInterface
53
53
  from zenml.zen_stores.schemas.workspace_schemas import WorkspaceSchema
54
54
 
55
55
  if TYPE_CHECKING:
@@ -59,7 +59,9 @@ if TYPE_CHECKING:
59
59
  from zenml.zen_stores.schemas.model_schemas import (
60
60
  ModelVersionArtifactSchema,
61
61
  )
62
- from zenml.zen_stores.schemas.run_metadata_schemas import RunMetadataSchema
62
+ from zenml.zen_stores.schemas.run_metadata_schemas import (
63
+ RunMetadataResourceSchema,
64
+ )
63
65
  from zenml.zen_stores.schemas.tag_schemas import TagResourceSchema
64
66
 
65
67
 
@@ -171,7 +173,7 @@ class ArtifactSchema(NamedSchema, table=True):
171
173
  return self
172
174
 
173
175
 
174
- class ArtifactVersionSchema(BaseSchema, table=True):
176
+ class ArtifactVersionSchema(BaseSchema, RunMetadataInterface, table=True):
175
177
  """SQL Model for artifact versions."""
176
178
 
177
179
  __tablename__ = "artifact_version"
@@ -242,12 +244,12 @@ class ArtifactVersionSchema(BaseSchema, table=True):
242
244
  workspace: "WorkspaceSchema" = Relationship(
243
245
  back_populates="artifact_versions"
244
246
  )
245
- run_metadata: List["RunMetadataSchema"] = Relationship(
246
- back_populates="artifact_version",
247
+ run_metadata_resources: List["RunMetadataResourceSchema"] = Relationship(
248
+ back_populates="artifact_versions",
247
249
  sa_relationship_kwargs=dict(
248
- primaryjoin=f"and_(RunMetadataSchema.resource_type=='{MetadataResourceTypes.ARTIFACT_VERSION.value}', foreign(RunMetadataSchema.resource_id)==ArtifactVersionSchema.id)",
250
+ primaryjoin=f"and_(RunMetadataResourceSchema.resource_type=='{MetadataResourceTypes.ARTIFACT_VERSION.value}', foreign(RunMetadataResourceSchema.resource_id)==ArtifactVersionSchema.id)",
249
251
  cascade="delete",
250
- overlaps="run_metadata",
252
+ overlaps="run_metadata_resources",
251
253
  ),
252
254
  )
253
255
  output_of_step_runs: List["StepRunOutputArtifactSchema"] = Relationship(
@@ -352,8 +354,9 @@ class ArtifactVersionSchema(BaseSchema, table=True):
352
354
  producer_step_run_id = step_run.original_step_run_id
353
355
 
354
356
  # Create the body of the model
357
+ artifact = self.artifact.to_model()
355
358
  body = ArtifactVersionResponseBody(
356
- artifact=self.artifact.to_model(),
359
+ artifact=artifact,
357
360
  version=self.version or str(self.version_number),
358
361
  user=self.user.to_model() if self.user else None,
359
362
  uri=self.uri,
@@ -375,9 +378,7 @@ class ArtifactVersionSchema(BaseSchema, table=True):
375
378
  workspace=self.workspace.to_model(),
376
379
  producer_step_run_id=producer_step_run_id,
377
380
  visualizations=[v.to_model() for v in self.visualizations],
378
- run_metadata={
379
- m.key: json.loads(m.value) for m in self.run_metadata
380
- },
381
+ run_metadata=self.fetch_metadata(),
381
382
  )
382
383
 
383
384
  resources = None
@@ -56,7 +56,6 @@ class StackComponentSchema(NamedSchema, table=True):
56
56
  flavor: str
57
57
  configuration: bytes
58
58
  labels: Optional[bytes]
59
- component_spec_path: Optional[str]
60
59
 
61
60
  workspace_id: UUID = build_foreign_key_field(
62
61
  source=__tablename__,
@@ -135,7 +134,6 @@ class StackComponentSchema(NamedSchema, table=True):
135
134
  name=request.name,
136
135
  workspace_id=request.workspace,
137
136
  user_id=request.user,
138
- component_spec_path=request.component_spec_path,
139
137
  type=request.type,
140
138
  flavor=request.flavor,
141
139
  configuration=base64.b64encode(
@@ -218,7 +216,6 @@ class StackComponentSchema(NamedSchema, table=True):
218
216
  labels=json.loads(base64.b64decode(self.labels).decode())
219
217
  if self.labels
220
218
  else None,
221
- component_spec_path=self.component_spec_path,
222
219
  connector_resource_id=self.connector_resource_id,
223
220
  connector=self.connector.to_model()
224
221
  if self.connector
@@ -13,13 +13,18 @@
13
13
  # permissions and limitations under the License.
14
14
  """SQLModel implementation of model tables."""
15
15
 
16
- import json
17
16
  from datetime import datetime
18
17
  from typing import TYPE_CHECKING, Any, Dict, List, Optional, cast
19
- from uuid import UUID
18
+ from uuid import UUID, uuid4
20
19
 
21
20
  from pydantic import ConfigDict
22
- from sqlalchemy import BOOLEAN, INTEGER, TEXT, Column, UniqueConstraint
21
+ from sqlalchemy import (
22
+ BOOLEAN,
23
+ INTEGER,
24
+ TEXT,
25
+ Column,
26
+ UniqueConstraint,
27
+ )
23
28
  from sqlmodel import Field, Relationship
24
29
 
25
30
  from zenml.enums import (
@@ -51,11 +56,16 @@ from zenml.zen_stores.schemas.artifact_schemas import ArtifactVersionSchema
51
56
  from zenml.zen_stores.schemas.base_schemas import BaseSchema, NamedSchema
52
57
  from zenml.zen_stores.schemas.constants import MODEL_VERSION_TABLENAME
53
58
  from zenml.zen_stores.schemas.pipeline_run_schemas import PipelineRunSchema
54
- from zenml.zen_stores.schemas.run_metadata_schemas import RunMetadataSchema
59
+ from zenml.zen_stores.schemas.run_metadata_schemas import (
60
+ RunMetadataResourceSchema,
61
+ )
55
62
  from zenml.zen_stores.schemas.schema_utils import build_foreign_key_field
56
63
  from zenml.zen_stores.schemas.tag_schemas import TagResourceSchema
57
64
  from zenml.zen_stores.schemas.user_schemas import UserSchema
58
- from zenml.zen_stores.schemas.utils import get_page_from_list
65
+ from zenml.zen_stores.schemas.utils import (
66
+ RunMetadataInterface,
67
+ get_page_from_list,
68
+ )
59
69
  from zenml.zen_stores.schemas.workspace_schemas import WorkspaceSchema
60
70
 
61
71
  if TYPE_CHECKING:
@@ -219,16 +229,18 @@ class ModelSchema(NamedSchema, table=True):
219
229
  return self
220
230
 
221
231
 
222
- class ModelVersionSchema(NamedSchema, table=True):
232
+ class ModelVersionSchema(NamedSchema, RunMetadataInterface, table=True):
223
233
  """SQL Model for model version."""
224
234
 
225
235
  __tablename__ = MODEL_VERSION_TABLENAME
226
236
  __table_args__ = (
227
- # We need two unique constraints here:
237
+ # We need three unique constraints here:
228
238
  # - The first to ensure that each model version for a
229
239
  # model has a unique version number
230
240
  # - The second one to ensure that explicit names given by
231
241
  # users are unique
242
+ # - The third one to ensure that a pipeline run only produces a single
243
+ # auto-incremented version per model
232
244
  UniqueConstraint(
233
245
  "number",
234
246
  "model_id",
@@ -239,6 +251,11 @@ class ModelVersionSchema(NamedSchema, table=True):
239
251
  "model_id",
240
252
  name="unique_version_for_model_id",
241
253
  ),
254
+ UniqueConstraint(
255
+ "model_id",
256
+ "producer_run_id_if_numeric",
257
+ name="unique_numeric_version_for_pipeline_run",
258
+ ),
242
259
  )
243
260
 
244
261
  workspace_id: UUID = build_foreign_key_field(
@@ -299,21 +316,32 @@ class ModelVersionSchema(NamedSchema, table=True):
299
316
  description: str = Field(sa_column=Column(TEXT, nullable=True))
300
317
  stage: str = Field(sa_column=Column(TEXT, nullable=True))
301
318
 
302
- run_metadata: List["RunMetadataSchema"] = Relationship(
303
- back_populates="model_version",
319
+ run_metadata_resources: List["RunMetadataResourceSchema"] = Relationship(
320
+ back_populates="model_versions",
304
321
  sa_relationship_kwargs=dict(
305
- primaryjoin=f"and_(RunMetadataSchema.resource_type=='{MetadataResourceTypes.MODEL_VERSION.value}', foreign(RunMetadataSchema.resource_id)==ModelVersionSchema.id)",
322
+ primaryjoin=f"and_(RunMetadataResourceSchema.resource_type=='{MetadataResourceTypes.MODEL_VERSION.value}', foreign(RunMetadataResourceSchema.resource_id)==ModelVersionSchema.id)",
306
323
  cascade="delete",
307
- overlaps="run_metadata",
324
+ overlaps="run_metadata_resources",
308
325
  ),
309
326
  )
310
327
  pipeline_runs: List["PipelineRunSchema"] = Relationship(
311
- back_populates="model_version"
328
+ back_populates="model_version",
312
329
  )
313
330
  step_runs: List["StepRunSchema"] = Relationship(
314
331
  back_populates="model_version"
315
332
  )
316
333
 
334
+ # We want to make sure each pipeline run only creates a single numeric
335
+ # version for each model. To solve this, we need to add a unique constraint.
336
+ # If a value of a unique constraint is NULL it is ignored and the
337
+ # remaining values in the unique constraint have to be unique. In
338
+ # our case however, we only want the unique constraint applied in
339
+ # case there is a producer run and only for numeric versions. To solve this,
340
+ # we fall back to the model version ID (which is the primary key and
341
+ # therefore unique) in case there is no producer run or the version is not
342
+ # numeric.
343
+ producer_run_id_if_numeric: UUID
344
+
317
345
  # TODO: In Pydantic v2, the `model_` is a protected namespaces for all
318
346
  # fields defined under base models. If not handled, this raises a warning.
319
347
  # It is possible to suppress this warning message with the following
@@ -324,24 +352,36 @@ class ModelVersionSchema(NamedSchema, table=True):
324
352
 
325
353
  @classmethod
326
354
  def from_request(
327
- cls, model_version_request: ModelVersionRequest
355
+ cls,
356
+ model_version_request: ModelVersionRequest,
357
+ model_version_number: int,
358
+ producer_run_id: Optional[UUID] = None,
328
359
  ) -> "ModelVersionSchema":
329
360
  """Convert an `ModelVersionRequest` to an `ModelVersionSchema`.
330
361
 
331
362
  Args:
332
363
  model_version_request: The request model version to convert.
364
+ model_version_number: The model version number.
365
+ producer_run_id: The ID of the producer run.
333
366
 
334
367
  Returns:
335
368
  The converted schema.
336
369
  """
370
+ id_ = uuid4()
371
+ is_numeric = str(model_version_number) == model_version_request.name
372
+
337
373
  return cls(
374
+ id=id_,
338
375
  workspace_id=model_version_request.workspace,
339
376
  user_id=model_version_request.user,
340
377
  model_id=model_version_request.model,
341
378
  name=model_version_request.name,
342
- number=model_version_request.number,
379
+ number=model_version_number,
343
380
  description=model_version_request.description,
344
381
  stage=model_version_request.stage,
382
+ producer_run_id_if_numeric=producer_run_id
383
+ if (producer_run_id and is_numeric)
384
+ else id_,
345
385
  )
346
386
 
347
387
  def to_model(
@@ -402,9 +442,7 @@ class ModelVersionSchema(NamedSchema, table=True):
402
442
  metadata = ModelVersionResponseMetadata(
403
443
  workspace=self.workspace.to_model(),
404
444
  description=self.description,
405
- run_metadata={
406
- rm.key: json.loads(rm.value) for rm in self.run_metadata
407
- },
445
+ run_metadata=self.fetch_metadata(),
408
446
  )
409
447
 
410
448
  resources = None
@@ -228,13 +228,6 @@ class PipelineDeploymentSchema(BaseSchema, table=True):
228
228
  Returns:
229
229
  The created `PipelineDeploymentResponse`.
230
230
  """
231
- pipeline_configuration = PipelineConfiguration.model_validate_json(
232
- self.pipeline_configuration
233
- )
234
- step_configurations = json.loads(self.step_configurations)
235
- for s, c in step_configurations.items():
236
- step_configurations[s] = Step.model_validate(c)
237
-
238
231
  body = PipelineDeploymentResponseBody(
239
232
  user=self.user.to_model() if self.user else None,
240
233
  created=self.created,
@@ -242,6 +235,13 @@ class PipelineDeploymentSchema(BaseSchema, table=True):
242
235
  )
243
236
  metadata = None
244
237
  if include_metadata:
238
+ pipeline_configuration = PipelineConfiguration.model_validate_json(
239
+ self.pipeline_configuration
240
+ )
241
+ step_configurations = json.loads(self.step_configurations)
242
+ for s, c in step_configurations.items():
243
+ step_configurations[s] = Step.model_validate(c)
244
+
245
245
  metadata = PipelineDeploymentResponseMetadata(
246
246
  workspace=self.workspace.to_model(),
247
247
  run_name_template=self.run_name_template,