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
@@ -16,10 +16,13 @@
16
16
  from datetime import datetime
17
17
  from typing import (
18
18
  TYPE_CHECKING,
19
+ Any,
19
20
  ClassVar,
20
21
  Dict,
21
22
  List,
22
23
  Optional,
24
+ Type,
25
+ TypeVar,
23
26
  Union,
24
27
  cast,
25
28
  )
@@ -55,6 +58,11 @@ if TYPE_CHECKING:
55
58
  from zenml.models.v2.core.schedule import ScheduleResponse
56
59
  from zenml.models.v2.core.stack import StackResponse
57
60
  from zenml.models.v2.core.step_run import StepRunResponse
61
+ from zenml.zen_stores.schemas.base_schemas import BaseSchema
62
+
63
+ AnySchema = TypeVar("AnySchema", bound=BaseSchema)
64
+
65
+ AnyQuery = TypeVar("AnyQuery", bound=Any)
58
66
 
59
67
 
60
68
  # ------------------ Request Model ------------------
@@ -237,6 +245,10 @@ class PipelineRunResponseMetadata(WorkspaceScopedResponseMetadata):
237
245
  default=False,
238
246
  description="Whether a template can be created from this run.",
239
247
  )
248
+ step_substitutions: Dict[str, Dict[str, str]] = Field(
249
+ title="Substitutions used in the step runs of this pipeline run.",
250
+ default_factory=dict,
251
+ )
240
252
 
241
253
 
242
254
  class PipelineRunResponseResources(WorkspaceScopedResponseResources):
@@ -546,6 +558,15 @@ class PipelineRunResponse(
546
558
  """
547
559
  return self.get_metadata().is_templatable
548
560
 
561
+ @property
562
+ def step_substitutions(self) -> Dict[str, Dict[str, str]]:
563
+ """The `step_substitutions` property.
564
+
565
+ Returns:
566
+ the value of the property.
567
+ """
568
+ return self.get_metadata().step_substitutions
569
+
549
570
  @property
550
571
  def model_version(self) -> Optional[ModelVersionResponse]:
551
572
  """The `model_version` property.
@@ -571,6 +592,15 @@ class PipelineRunResponse(
571
592
  class PipelineRunFilter(WorkspaceScopedTaggableFilter):
572
593
  """Model to enable advanced filtering of all Workspaces."""
573
594
 
595
+ CUSTOM_SORTING_OPTIONS: ClassVar[List[str]] = [
596
+ *WorkspaceScopedTaggableFilter.CUSTOM_SORTING_OPTIONS,
597
+ "tag",
598
+ "stack",
599
+ "pipeline",
600
+ "model",
601
+ "model_version",
602
+ ]
603
+
574
604
  FILTER_EXCLUDE_FIELDS: ClassVar[List[str]] = [
575
605
  *WorkspaceScopedTaggableFilter.FILTER_EXCLUDE_FIELDS,
576
606
  "unlisted",
@@ -579,7 +609,6 @@ class PipelineRunFilter(WorkspaceScopedTaggableFilter):
579
609
  "schedule_id",
580
610
  "stack_id",
581
611
  "template_id",
582
- "user",
583
612
  "pipeline",
584
613
  "stack",
585
614
  "code_repository",
@@ -602,16 +631,6 @@ class PipelineRunFilter(WorkspaceScopedTaggableFilter):
602
631
  description="Pipeline associated with the Pipeline Run",
603
632
  union_mode="left_to_right",
604
633
  )
605
- workspace_id: Optional[Union[UUID, str]] = Field(
606
- default=None,
607
- description="Workspace of the Pipeline Run",
608
- union_mode="left_to_right",
609
- )
610
- user_id: Optional[Union[UUID, str]] = Field(
611
- default=None,
612
- description="User that created the Pipeline Run",
613
- union_mode="left_to_right",
614
- )
615
634
  stack_id: Optional[Union[UUID, str]] = Field(
616
635
  default=None,
617
636
  description="Stack used for the Pipeline Run",
@@ -662,16 +681,12 @@ class PipelineRunFilter(WorkspaceScopedTaggableFilter):
662
681
  union_mode="left_to_right",
663
682
  )
664
683
  unlisted: Optional[bool] = None
665
- user: Optional[Union[UUID, str]] = Field(
666
- default=None,
667
- description="Name/ID of the user that created the run.",
668
- )
669
684
  run_metadata: Optional[Dict[str, str]] = Field(
670
685
  default=None,
671
686
  description="The run_metadata to filter the pipeline runs by.",
672
687
  )
673
688
  # TODO: Remove once frontend is ready for it. This is replaced by the more
674
- # generic `pipeline` filter below.
689
+ # generic `pipeline` filter below.
675
690
  pipeline_name: Optional[str] = Field(
676
691
  default=None,
677
692
  description="Name of the pipeline associated with the run",
@@ -703,13 +718,17 @@ class PipelineRunFilter(WorkspaceScopedTaggableFilter):
703
718
 
704
719
  def get_custom_filters(
705
720
  self,
721
+ table: Type["AnySchema"],
706
722
  ) -> List["ColumnElement[bool]"]:
707
723
  """Get custom filters.
708
724
 
725
+ Args:
726
+ table: The query table.
727
+
709
728
  Returns:
710
729
  A list of custom filters.
711
730
  """
712
- custom_filters = super().get_custom_filters()
731
+ custom_filters = super().get_custom_filters(table)
713
732
 
714
733
  from sqlmodel import and_, col, or_
715
734
 
@@ -722,12 +741,12 @@ class PipelineRunFilter(WorkspaceScopedTaggableFilter):
722
741
  PipelineDeploymentSchema,
723
742
  PipelineRunSchema,
724
743
  PipelineSchema,
744
+ RunMetadataResourceSchema,
725
745
  RunMetadataSchema,
726
746
  ScheduleSchema,
727
747
  StackComponentSchema,
728
748
  StackCompositionSchema,
729
749
  StackSchema,
730
- UserSchema,
731
750
  )
732
751
 
733
752
  if self.unlisted is not None:
@@ -778,17 +797,6 @@ class PipelineRunFilter(WorkspaceScopedTaggableFilter):
778
797
  )
779
798
  custom_filters.append(run_template_filter)
780
799
 
781
- if self.user:
782
- user_filter = and_(
783
- PipelineRunSchema.user_id == UserSchema.id,
784
- self.generate_name_or_id_query_conditions(
785
- value=self.user,
786
- table=UserSchema,
787
- additional_columns=["full_name"],
788
- ),
789
- )
790
- custom_filters.append(user_filter)
791
-
792
800
  if self.pipeline:
793
801
  pipeline_filter = and_(
794
802
  PipelineRunSchema.pipeline_id == PipelineSchema.id,
@@ -897,10 +905,12 @@ class PipelineRunFilter(WorkspaceScopedTaggableFilter):
897
905
 
898
906
  for key, value in self.run_metadata.items():
899
907
  additional_filter = and_(
900
- RunMetadataSchema.resource_id == PipelineRunSchema.id,
901
- RunMetadataSchema.resource_type
908
+ RunMetadataResourceSchema.resource_id
909
+ == PipelineRunSchema.id,
910
+ RunMetadataResourceSchema.resource_type
902
911
  == MetadataResourceTypes.PIPELINE_RUN,
903
- RunMetadataSchema.key == key,
912
+ RunMetadataResourceSchema.run_metadata_id
913
+ == RunMetadataSchema.id,
904
914
  self.generate_custom_query_conditions_for_column(
905
915
  value=value,
906
916
  table=RunMetadataSchema,
@@ -910,3 +920,71 @@ class PipelineRunFilter(WorkspaceScopedTaggableFilter):
910
920
  custom_filters.append(additional_filter)
911
921
 
912
922
  return custom_filters
923
+
924
+ def apply_sorting(
925
+ self,
926
+ query: AnyQuery,
927
+ table: Type["AnySchema"],
928
+ ) -> AnyQuery:
929
+ """Apply sorting to the query.
930
+
931
+ Args:
932
+ query: The query to which to apply the sorting.
933
+ table: The query table.
934
+
935
+ Returns:
936
+ The query with sorting applied.
937
+ """
938
+ from sqlmodel import asc, desc
939
+
940
+ from zenml.enums import SorterOps
941
+ from zenml.zen_stores.schemas import (
942
+ ModelSchema,
943
+ ModelVersionSchema,
944
+ PipelineDeploymentSchema,
945
+ PipelineRunSchema,
946
+ PipelineSchema,
947
+ StackSchema,
948
+ )
949
+
950
+ sort_by, operand = self.sorting_params
951
+
952
+ if sort_by == "pipeline":
953
+ query = query.join(
954
+ PipelineSchema,
955
+ PipelineRunSchema.pipeline_id == PipelineSchema.id,
956
+ )
957
+ column = PipelineSchema.name
958
+ elif sort_by == "stack":
959
+ query = query.join(
960
+ PipelineDeploymentSchema,
961
+ PipelineRunSchema.deployment_id == PipelineDeploymentSchema.id,
962
+ ).join(
963
+ StackSchema,
964
+ PipelineDeploymentSchema.stack_id == StackSchema.id,
965
+ )
966
+ column = StackSchema.name
967
+ elif sort_by == "model":
968
+ query = query.join(
969
+ ModelVersionSchema,
970
+ PipelineRunSchema.model_version_id == ModelVersionSchema.id,
971
+ ).join(
972
+ ModelSchema,
973
+ ModelVersionSchema.model_id == ModelSchema.id,
974
+ )
975
+ column = ModelSchema.name
976
+ elif sort_by == "model_version":
977
+ query = query.join(
978
+ ModelVersionSchema,
979
+ PipelineRunSchema.model_version_id == ModelVersionSchema.id,
980
+ )
981
+ column = ModelVersionSchema.name
982
+ else:
983
+ return super().apply_sorting(query=query, table=table)
984
+
985
+ if operand == SorterOps.ASCENDING:
986
+ query = query.order_by(asc(column))
987
+ else:
988
+ query = query.order_by(desc(column))
989
+
990
+ return query
@@ -13,16 +13,16 @@
13
13
  # permissions and limitations under the License.
14
14
  """Models representing run metadata."""
15
15
 
16
- from typing import Dict, Optional
16
+ from typing import Dict, List, Optional
17
17
  from uuid import UUID
18
18
 
19
- from pydantic import Field
19
+ from pydantic import Field, model_validator
20
20
 
21
- from zenml.enums import MetadataResourceTypes
22
21
  from zenml.metadata.metadata_types import MetadataType, MetadataTypeEnum
23
22
  from zenml.models.v2.base.scoped import (
24
23
  WorkspaceScopedRequest,
25
24
  )
25
+ from zenml.models.v2.misc.run_metadata import RunMetadataResource
26
26
 
27
27
  # ------------------ Request Model ------------------
28
28
 
@@ -30,14 +30,12 @@ from zenml.models.v2.base.scoped import (
30
30
  class RunMetadataRequest(WorkspaceScopedRequest):
31
31
  """Request model for run metadata."""
32
32
 
33
- resource_id: UUID = Field(
34
- title="The ID of the resource that this metadata belongs to.",
35
- )
36
- resource_type: MetadataResourceTypes = Field(
37
- title="The type of the resource that this metadata belongs to.",
33
+ resources: List[RunMetadataResource] = Field(
34
+ title="The list of resources that this metadata belongs to."
38
35
  )
39
36
  stack_component_id: Optional[UUID] = Field(
40
- title="The ID of the stack component that this metadata belongs to."
37
+ title="The ID of the stack component that this metadata belongs to.",
38
+ default=None,
41
39
  )
42
40
  values: Dict[str, "MetadataType"] = Field(
43
41
  title="The metadata to be created.",
@@ -45,3 +43,26 @@ class RunMetadataRequest(WorkspaceScopedRequest):
45
43
  types: Dict[str, "MetadataTypeEnum"] = Field(
46
44
  title="The types of the metadata to be created.",
47
45
  )
46
+ publisher_step_id: Optional[UUID] = Field(
47
+ title="The ID of the step execution that published this metadata.",
48
+ default=None,
49
+ )
50
+
51
+ @model_validator(mode="after")
52
+ def validate_values_keys(self) -> "RunMetadataRequest":
53
+ """Validates if the keys in the metadata are properly defined.
54
+
55
+ Returns:
56
+ self
57
+
58
+ Raises:
59
+ ValueError: if one of the key in the metadata contains `:`
60
+ """
61
+ invalid_keys = [key for key in self.values.keys() if ":" in key]
62
+ if invalid_keys:
63
+ raise ValueError(
64
+ "You can not use colons (`:`) in the key names when you "
65
+ "are creating metadata for your ZenML objects. Please change "
66
+ f"the following keys: {invalid_keys}"
67
+ )
68
+ return self
@@ -13,7 +13,17 @@
13
13
  # permissions and limitations under the License.
14
14
  """Models representing pipeline templates."""
15
15
 
16
- from typing import TYPE_CHECKING, Any, ClassVar, Dict, List, Optional, Union
16
+ from typing import (
17
+ TYPE_CHECKING,
18
+ Any,
19
+ ClassVar,
20
+ Dict,
21
+ List,
22
+ Optional,
23
+ Type,
24
+ TypeVar,
25
+ Union,
26
+ )
17
27
  from uuid import UUID
18
28
 
19
29
  from pydantic import Field
@@ -45,6 +55,11 @@ from zenml.models.v2.core.tag import TagResponse
45
55
  if TYPE_CHECKING:
46
56
  from sqlalchemy.sql.elements import ColumnElement
47
57
 
58
+ from zenml.zen_stores.schemas.base_schemas import BaseSchema
59
+
60
+ AnySchema = TypeVar("AnySchema", bound=BaseSchema)
61
+
62
+
48
63
  # ------------------ Request Model ------------------
49
64
 
50
65
 
@@ -310,16 +325,6 @@ class RunTemplateFilter(WorkspaceScopedTaggableFilter):
310
325
  default=None,
311
326
  description="Name of the run template.",
312
327
  )
313
- workspace_id: Optional[Union[UUID, str]] = Field(
314
- default=None,
315
- description="Workspace associated with the template.",
316
- union_mode="left_to_right",
317
- )
318
- user_id: Optional[Union[UUID, str]] = Field(
319
- default=None,
320
- description="User that created the template.",
321
- union_mode="left_to_right",
322
- )
323
328
  pipeline_id: Optional[Union[UUID, str]] = Field(
324
329
  default=None,
325
330
  description="Pipeline associated with the template.",
@@ -340,10 +345,6 @@ class RunTemplateFilter(WorkspaceScopedTaggableFilter):
340
345
  description="Code repository associated with the template.",
341
346
  union_mode="left_to_right",
342
347
  )
343
- user: Optional[Union[UUID, str]] = Field(
344
- default=None,
345
- description="Name/ID of the user that created the template.",
346
- )
347
348
  pipeline: Optional[Union[UUID, str]] = Field(
348
349
  default=None,
349
350
  description="Name/ID of the pipeline associated with the template.",
@@ -354,14 +355,17 @@ class RunTemplateFilter(WorkspaceScopedTaggableFilter):
354
355
  )
355
356
 
356
357
  def get_custom_filters(
357
- self,
358
+ self, table: Type["AnySchema"]
358
359
  ) -> List["ColumnElement[bool]"]:
359
360
  """Get custom filters.
360
361
 
362
+ Args:
363
+ table: The query table.
364
+
361
365
  Returns:
362
366
  A list of custom filters.
363
367
  """
364
- custom_filters = super().get_custom_filters()
368
+ custom_filters = super().get_custom_filters(table)
365
369
 
366
370
  from sqlmodel import and_
367
371
 
@@ -371,7 +375,6 @@ class RunTemplateFilter(WorkspaceScopedTaggableFilter):
371
375
  PipelineSchema,
372
376
  RunTemplateSchema,
373
377
  StackSchema,
374
- UserSchema,
375
378
  )
376
379
 
377
380
  if self.code_repository_id:
@@ -409,17 +412,6 @@ class RunTemplateFilter(WorkspaceScopedTaggableFilter):
409
412
  )
410
413
  custom_filters.append(pipeline_filter)
411
414
 
412
- if self.user:
413
- user_filter = and_(
414
- RunTemplateSchema.user_id == UserSchema.id,
415
- self.generate_name_or_id_query_conditions(
416
- value=self.user,
417
- table=UserSchema,
418
- additional_columns=["full_name"],
419
- ),
420
- )
421
- custom_filters.append(user_filter)
422
-
423
415
  if self.pipeline:
424
416
  pipeline_filter = and_(
425
417
  RunTemplateSchema.source_deployment_id
@@ -279,16 +279,6 @@ class ScheduleResponse(
279
279
  class ScheduleFilter(WorkspaceScopedFilter):
280
280
  """Model to enable advanced filtering of all Users."""
281
281
 
282
- workspace_id: Optional[Union[UUID, str]] = Field(
283
- default=None,
284
- description="Workspace scope of the schedule.",
285
- union_mode="left_to_right",
286
- )
287
- user_id: Optional[Union[UUID, str]] = Field(
288
- default=None,
289
- description="User that created the schedule",
290
- union_mode="left_to_right",
291
- )
292
282
  pipeline_id: Optional[Union[UUID, str]] = Field(
293
283
  default=None,
294
284
  description="Pipeline that the schedule is attached to.",
@@ -15,7 +15,6 @@
15
15
 
16
16
  from datetime import datetime
17
17
  from typing import Any, ClassVar, Dict, List, Optional, Union
18
- from uuid import UUID
19
18
 
20
19
  from pydantic import Field, SecretStr
21
20
 
@@ -253,25 +252,12 @@ class SecretFilter(WorkspaceScopedFilter):
253
252
  default=None,
254
253
  description="Name of the secret",
255
254
  )
256
-
257
255
  scope: Optional[Union[SecretScope, str]] = Field(
258
256
  default=None,
259
257
  description="Scope in which to filter secrets",
260
258
  union_mode="left_to_right",
261
259
  )
262
260
 
263
- workspace_id: Optional[Union[UUID, str]] = Field(
264
- default=None,
265
- description="Workspace of the Secret",
266
- union_mode="left_to_right",
267
- )
268
-
269
- user_id: Optional[Union[UUID, str]] = Field(
270
- default=None,
271
- description="User that created the Secret",
272
- union_mode="left_to_right",
273
- )
274
-
275
261
  @staticmethod
276
262
  def _get_filtering_value(value: Optional[Any]) -> str:
277
263
  """Convert the value to a string that can be used for lexicographical filtering and sorting.
@@ -15,19 +15,20 @@
15
15
 
16
16
  from datetime import datetime
17
17
  from typing import (
18
+ TYPE_CHECKING,
18
19
  Any,
19
20
  ClassVar,
20
21
  Dict,
21
22
  List,
22
23
  Optional,
23
24
  Type,
25
+ TypeVar,
24
26
  Union,
25
27
  )
26
28
  from uuid import UUID
27
29
 
28
30
  from pydantic import BaseModel, ConfigDict, Field
29
31
  from sqlalchemy.sql.elements import ColumnElement
30
- from sqlmodel import SQLModel
31
32
 
32
33
  from zenml.constants import STR_FIELD_MAX_LENGTH
33
34
  from zenml.models.v2.base.scoped import (
@@ -37,11 +38,15 @@ from zenml.models.v2.base.scoped import (
37
38
  WorkspaceScopedResponseBody,
38
39
  WorkspaceScopedResponseMetadata,
39
40
  WorkspaceScopedResponseResources,
40
- WorkspaceScopedTaggableFilter,
41
41
  )
42
42
  from zenml.services.service_status import ServiceState
43
43
  from zenml.services.service_type import ServiceType
44
44
 
45
+ if TYPE_CHECKING:
46
+ from zenml.zen_stores.schemas import BaseSchema
47
+
48
+ AnySchema = TypeVar("AnySchema", bound=BaseSchema)
49
+
45
50
  # ------------------ Request Model ------------------
46
51
 
47
52
 
@@ -376,16 +381,6 @@ class ServiceFilter(WorkspaceScopedFilter):
376
381
  description="Name of the service. Use this to filter services by "
377
382
  "their name.",
378
383
  )
379
- workspace_id: Optional[Union[UUID, str]] = Field(
380
- default=None,
381
- description="Workspace of the service",
382
- union_mode="left_to_right",
383
- )
384
- user_id: Optional[Union[UUID, str]] = Field(
385
- default=None,
386
- description="User of the service",
387
- union_mode="left_to_right",
388
- )
389
384
  type: Optional[str] = Field(
390
385
  default=None,
391
386
  description="Type of the service. Filter services by their type.",
@@ -457,9 +452,7 @@ class ServiceFilter(WorkspaceScopedFilter):
457
452
  "config",
458
453
  ]
459
454
  CLI_EXCLUDE_FIELDS: ClassVar[List[str]] = [
460
- *WorkspaceScopedTaggableFilter.CLI_EXCLUDE_FIELDS,
461
- "workspace_id",
462
- "user_id",
455
+ *WorkspaceScopedFilter.CLI_EXCLUDE_FIELDS,
463
456
  "flavor",
464
457
  "type",
465
458
  "pipeline_step_name",
@@ -468,7 +461,7 @@ class ServiceFilter(WorkspaceScopedFilter):
468
461
  ]
469
462
 
470
463
  def generate_filter(
471
- self, table: Type["SQLModel"]
464
+ self, table: Type["AnySchema"]
472
465
  ) -> Union["ColumnElement[bool]"]:
473
466
  """Generate the filter for the query.
474
467
 
@@ -801,7 +801,6 @@ class ServiceConnectorFilter(WorkspaceScopedFilter):
801
801
  default=None,
802
802
  description="The type to scope this query to.",
803
803
  )
804
-
805
804
  name: Optional[str] = Field(
806
805
  default=None,
807
806
  description="The name to filter by",
@@ -810,16 +809,6 @@ class ServiceConnectorFilter(WorkspaceScopedFilter):
810
809
  default=None,
811
810
  description="The type of service connector to filter by",
812
811
  )
813
- workspace_id: Optional[Union[UUID, str]] = Field(
814
- default=None,
815
- description="Workspace to filter by",
816
- union_mode="left_to_right",
817
- )
818
- user_id: Optional[Union[UUID, str]] = Field(
819
- default=None,
820
- description="User to filter by",
821
- union_mode="left_to_right",
822
- )
823
812
  auth_method: Optional[str] = Field(
824
813
  default=None,
825
814
  title="Filter by the authentication method configured for the "
@@ -14,7 +14,17 @@
14
14
  """Models representing stacks."""
15
15
 
16
16
  import json
17
- from typing import TYPE_CHECKING, Any, ClassVar, Dict, List, Optional, Union
17
+ from typing import (
18
+ TYPE_CHECKING,
19
+ Any,
20
+ ClassVar,
21
+ Dict,
22
+ List,
23
+ Optional,
24
+ Type,
25
+ TypeVar,
26
+ Union,
27
+ )
18
28
  from uuid import UUID
19
29
 
20
30
  from pydantic import Field, model_validator
@@ -39,6 +49,9 @@ if TYPE_CHECKING:
39
49
  from sqlalchemy.sql.elements import ColumnElement
40
50
 
41
51
  from zenml.models.v2.core.component import ComponentResponse
52
+ from zenml.zen_stores.schemas import BaseSchema
53
+
54
+ AnySchema = TypeVar("AnySchema", bound=BaseSchema)
42
55
 
43
56
 
44
57
  # ------------------ Request Model ------------------
@@ -323,7 +336,6 @@ class StackFilter(WorkspaceScopedFilter):
323
336
  FILTER_EXCLUDE_FIELDS: ClassVar[List[str]] = [
324
337
  *WorkspaceScopedFilter.FILTER_EXCLUDE_FIELDS,
325
338
  "component_id",
326
- "user",
327
339
  "component",
328
340
  ]
329
341
 
@@ -334,42 +346,32 @@ class StackFilter(WorkspaceScopedFilter):
334
346
  description: Optional[str] = Field(
335
347
  default=None, description="Description of the stack"
336
348
  )
337
- workspace_id: Optional[Union[UUID, str]] = Field(
338
- default=None,
339
- description="Workspace of the stack",
340
- union_mode="left_to_right",
341
- )
342
- user_id: Optional[Union[UUID, str]] = Field(
343
- default=None,
344
- description="User of the stack",
345
- union_mode="left_to_right",
346
- )
347
349
  component_id: Optional[Union[UUID, str]] = Field(
348
350
  default=None,
349
351
  description="Component in the stack",
350
352
  union_mode="left_to_right",
351
353
  )
352
- user: Optional[Union[UUID, str]] = Field(
353
- default=None,
354
- description="Name/ID of the user that created the stack.",
355
- )
356
354
  component: Optional[Union[UUID, str]] = Field(
357
355
  default=None, description="Name/ID of a component in the stack."
358
356
  )
359
357
 
360
- def get_custom_filters(self) -> List["ColumnElement[bool]"]:
358
+ def get_custom_filters(
359
+ self, table: Type["AnySchema"]
360
+ ) -> List["ColumnElement[bool]"]:
361
361
  """Get custom filters.
362
362
 
363
+ Args:
364
+ table: The query table.
365
+
363
366
  Returns:
364
367
  A list of custom filters.
365
368
  """
366
- custom_filters = super().get_custom_filters()
369
+ custom_filters = super().get_custom_filters(table)
367
370
 
368
371
  from zenml.zen_stores.schemas import (
369
372
  StackComponentSchema,
370
373
  StackCompositionSchema,
371
374
  StackSchema,
372
- UserSchema,
373
375
  )
374
376
 
375
377
  if self.component_id:
@@ -379,17 +381,6 @@ class StackFilter(WorkspaceScopedFilter):
379
381
  )
380
382
  custom_filters.append(component_id_filter)
381
383
 
382
- if self.user:
383
- user_filter = and_(
384
- StackSchema.user_id == UserSchema.id,
385
- self.generate_name_or_id_query_conditions(
386
- value=self.user,
387
- table=UserSchema,
388
- additional_columns=["full_name"],
389
- ),
390
- )
391
- custom_filters.append(user_filter)
392
-
393
384
  if self.component:
394
385
  component_filter = and_(
395
386
  StackCompositionSchema.stack_id == StackSchema.id,