zenml-nightly 0.75.0.dev20250317__py3-none-any.whl → 0.80.0.dev20250321__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 (259) hide show
  1. zenml/VERSION +1 -1
  2. zenml/__init__.py +3 -1
  3. zenml/artifacts/artifact_config.py +2 -2
  4. zenml/artifacts/utils.py +4 -8
  5. zenml/cli/__init__.py +6 -6
  6. zenml/cli/base.py +2 -2
  7. zenml/cli/login.py +2 -2
  8. zenml/cli/server.py +6 -4
  9. zenml/cli/utils.py +3 -3
  10. zenml/client.py +9 -9
  11. zenml/config/compiler.py +1 -1
  12. zenml/config/server_config.py +1 -1
  13. zenml/constants.py +2 -1
  14. zenml/enums.py +12 -0
  15. zenml/integrations/airflow/__init__.py +0 -2
  16. zenml/integrations/argilla/__init__.py +0 -1
  17. zenml/integrations/aws/__init__.py +0 -3
  18. zenml/integrations/azure/__init__.py +0 -2
  19. zenml/integrations/azure/service_connectors/azure_service_connector.py +1 -1
  20. zenml/integrations/bentoml/__init__.py +0 -2
  21. zenml/integrations/bentoml/services/bentoml_container_deployment.py +2 -2
  22. zenml/integrations/bentoml/services/bentoml_local_deployment.py +1 -1
  23. zenml/integrations/bentoml/steps/bentoml_deployer.py +1 -1
  24. zenml/integrations/bitbucket/__init__.py +0 -2
  25. zenml/integrations/comet/__init__.py +0 -2
  26. zenml/integrations/databricks/__init__.py +0 -2
  27. zenml/integrations/databricks/services/databricks_deployment.py +3 -1
  28. zenml/integrations/deepchecks/__init__.py +0 -2
  29. zenml/integrations/discord/__init__.py +0 -2
  30. zenml/integrations/evidently/__init__.py +0 -2
  31. zenml/integrations/facets/__init__.py +0 -2
  32. zenml/integrations/feast/__init__.py +0 -2
  33. zenml/integrations/gcp/__init__.py +0 -3
  34. zenml/integrations/gcp/orchestrators/vertex_orchestrator.py +5 -0
  35. zenml/integrations/github/__init__.py +0 -2
  36. zenml/integrations/github/code_repositories/github_code_repository.py +10 -3
  37. zenml/integrations/gitlab/__init__.py +0 -1
  38. zenml/integrations/gitlab/code_repositories/gitlab_code_repository.py +5 -2
  39. zenml/integrations/great_expectations/__init__.py +0 -3
  40. zenml/integrations/huggingface/__init__.py +0 -1
  41. zenml/integrations/huggingface/services/huggingface_deployment.py +3 -1
  42. zenml/integrations/hyperai/__init__.py +0 -2
  43. zenml/integrations/kaniko/__init__.py +0 -2
  44. zenml/integrations/kubeflow/__init__.py +0 -1
  45. zenml/integrations/kubeflow/orchestrators/kubeflow_orchestrator.py +6 -0
  46. zenml/integrations/kubernetes/__init__.py +0 -2
  47. zenml/integrations/kubernetes/flavors/kubernetes_orchestrator_flavor.py +4 -0
  48. zenml/integrations/kubernetes/orchestrators/kube_utils.py +89 -1
  49. zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py +82 -48
  50. zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint.py +44 -10
  51. zenml/integrations/kubernetes/orchestrators/manifest_utils.py +45 -1
  52. zenml/integrations/kubernetes/pod_settings.py +52 -0
  53. zenml/integrations/label_studio/__init__.py +0 -2
  54. zenml/integrations/langchain/__init__.py +0 -2
  55. zenml/integrations/lightgbm/__init__.py +0 -1
  56. zenml/integrations/lightning/__init__.py +0 -2
  57. zenml/integrations/mlflow/__init__.py +1 -3
  58. zenml/integrations/mlflow/services/mlflow_deployment.py +1 -1
  59. zenml/integrations/modal/__init__.py +0 -1
  60. zenml/integrations/neptune/__init__.py +0 -2
  61. zenml/integrations/neural_prophet/__init__.py +0 -2
  62. zenml/integrations/numpy/__init__.py +0 -2
  63. zenml/integrations/openai/__init__.py +0 -1
  64. zenml/integrations/pandas/__init__.py +0 -2
  65. zenml/integrations/pigeon/__init__.py +0 -1
  66. zenml/integrations/pillow/__init__.py +0 -3
  67. zenml/integrations/polars/__init__.py +0 -2
  68. zenml/integrations/prodigy/__init__.py +0 -1
  69. zenml/integrations/pycaret/__init__.py +0 -2
  70. zenml/integrations/pytorch/__init__.py +0 -1
  71. zenml/integrations/pytorch_lightning/__init__.py +0 -2
  72. zenml/integrations/s3/__init__.py +0 -2
  73. zenml/integrations/scipy/__init__.py +0 -2
  74. zenml/integrations/seldon/__init__.py +0 -2
  75. zenml/integrations/seldon/services/seldon_deployment.py +3 -2
  76. zenml/integrations/sklearn/__init__.py +1 -1
  77. zenml/integrations/skypilot/orchestrators/skypilot_base_vm_orchestrator.py +43 -18
  78. zenml/integrations/skypilot_aws/__init__.py +1 -3
  79. zenml/integrations/skypilot_azure/__init__.py +1 -2
  80. zenml/integrations/skypilot_gcp/__init__.py +1 -3
  81. zenml/integrations/skypilot_kubernetes/__init__.py +1 -3
  82. zenml/integrations/skypilot_lambda/__init__.py +1 -3
  83. zenml/integrations/slack/__init__.py +0 -1
  84. zenml/integrations/spark/__init__.py +0 -1
  85. zenml/integrations/tekton/__init__.py +0 -1
  86. zenml/integrations/tekton/orchestrators/tekton_orchestrator.py +5 -0
  87. zenml/integrations/tensorboard/__init__.py +0 -2
  88. zenml/integrations/tensorboard/services/tensorboard_service.py +1 -1
  89. zenml/integrations/tensorflow/__init__.py +0 -2
  90. zenml/integrations/vllm/__init__.py +0 -1
  91. zenml/integrations/vllm/services/vllm_deployment.py +1 -1
  92. zenml/integrations/wandb/__init__.py +0 -1
  93. zenml/integrations/whylogs/__init__.py +0 -1
  94. zenml/integrations/xgboost/__init__.py +0 -2
  95. zenml/login/credentials.py +1 -1
  96. zenml/login/pro/utils.py +1 -0
  97. zenml/materializers/__init__.py +1 -0
  98. zenml/model_deployers/base_model_deployer.py +1 -1
  99. zenml/models/__init__.py +4 -0
  100. zenml/models/v2/base/filter.py +162 -54
  101. zenml/models/v2/base/scoped.py +132 -0
  102. zenml/models/v2/core/artifact_version.py +12 -33
  103. zenml/models/v2/core/model_version.py +12 -50
  104. zenml/models/v2/core/pipeline_run.py +12 -32
  105. zenml/models/v2/core/service.py +2 -2
  106. zenml/models/v2/core/step_run.py +15 -32
  107. zenml/{services/service_type.py → models/v2/misc/service.py} +1 -1
  108. zenml/orchestrators/step_run_utils.py +1 -1
  109. zenml/orchestrators/utils.py +1 -1
  110. zenml/services/__init__.py +3 -5
  111. zenml/services/container/container_service.py +2 -1
  112. zenml/services/local/local_service.py +2 -1
  113. zenml/services/service.py +3 -2
  114. zenml/services/service_endpoint.py +2 -1
  115. zenml/services/service_monitor.py +1 -1
  116. zenml/services/service_status.py +1 -12
  117. zenml/steps/entrypoint_function_utils.py +1 -1
  118. zenml/utils/dashboard_utils.py +73 -8
  119. zenml/utils/server_utils.py +52 -0
  120. zenml/zen_server/dashboard/assets/{404-BbAvjc7Z.js → 404-2I8egBQu.js} +1 -1
  121. zenml/zen_server/dashboard/assets/@reactflow-BHoFKFSZ.js +17 -0
  122. zenml/zen_server/dashboard/assets/{AlertDialogDropdownItem-XL2NfFgP.js → AlertDialogDropdownItem-D7KZcPFw.js} +1 -1
  123. zenml/zen_server/dashboard/assets/CodeSnippet-DUkCnBpQ.js +9 -0
  124. zenml/zen_server/dashboard/assets/{CollapsibleCard-Djtd_ocf.js → CollapsibleCard-B5-5Plnd.js} +1 -1
  125. zenml/zen_server/dashboard/assets/{Commands-V-hH_IKQ.js → Commands-CbOMmarC.js} +1 -1
  126. zenml/zen_server/dashboard/assets/{ComponentBadge-CVN2FsiW.js → ComponentBadge-FrujKBC6.js} +1 -1
  127. zenml/zen_server/dashboard/assets/ComponentIcon-Dx5fBrDX.js +1 -0
  128. zenml/zen_server/dashboard/assets/{CsvVizualization-CWaQcWIN.js → CsvVizualization-B8E3p9we.js} +1 -1
  129. zenml/zen_server/dashboard/assets/{DeleteAlertDialog-CTLRrcFM.js → DeleteAlertDialog-BgTZbbAt.js} +1 -1
  130. zenml/zen_server/dashboard/assets/{DialogItem-ST291Hsl.js → DialogItem-CNWLiJcc.js} +1 -1
  131. zenml/zen_server/dashboard/assets/{Error-CIBjAdSc.js → Error-BkUP4Luv.js} +1 -1
  132. zenml/zen_server/dashboard/assets/ExecutionStatus-CD8Vj7sp.js +1 -0
  133. zenml/zen_server/dashboard/assets/{Helpbox-cwQNH06F.js → Helpbox-DIx6mDOH.js} +1 -1
  134. zenml/zen_server/dashboard/assets/{Infobox-DYKoAVhW.js → Infobox-BHEdNmME.js} +1 -1
  135. zenml/zen_server/dashboard/assets/{InlineAvatar-Bk4QLPTU.js → InlineAvatar-Bin9UPKJ.js} +1 -1
  136. zenml/zen_server/dashboard/assets/{NestedCollapsible-CE4OF670.js → NestedCollapsible-Da-k0Mff.js} +1 -1
  137. zenml/zen_server/dashboard/assets/{Partials-cL1-u_sT.js → Partials-TNaYjHsV.js} +1 -1
  138. zenml/zen_server/dashboard/assets/ProBadge-BfPp-B97.js +1 -0
  139. zenml/zen_server/dashboard/assets/{ProCta-DtUutIul.js → ProCta-7_FtpX3I.js} +1 -1
  140. zenml/zen_server/dashboard/assets/ProviderIcon-CxeziA5a.js +1 -0
  141. zenml/zen_server/dashboard/assets/{ProviderRadio-C4bltH6-.js → ProviderRadio-DPmZHff_.js} +1 -1
  142. zenml/zen_server/dashboard/assets/RunSelector-BVKB4Z8F.js +1 -0
  143. zenml/zen_server/dashboard/assets/{RunsBody-D2VoO-cR.js → RunsBody-Cj4sIqQB.js} +1 -1
  144. zenml/zen_server/dashboard/assets/{SearchField-DfNxVtjV.js → SearchField-DjAOZic5.js} +1 -1
  145. zenml/zen_server/dashboard/assets/SecretTooltip-mMAAP4dM.js +1 -0
  146. zenml/zen_server/dashboard/assets/{SetPassword-CWl2mwz8.js → SetPassword-B0o5kSJU.js} +1 -1
  147. zenml/zen_server/dashboard/assets/{StackList-C8KNd00o.js → StackList-5UB8LoEq.js} +1 -1
  148. zenml/zen_server/dashboard/assets/{Tabs-BEWDPvPV.js → Tabs-AuhCyzle.js} +1 -1
  149. zenml/zen_server/dashboard/assets/Tick-CHW0jc8Y.js +1 -0
  150. zenml/zen_server/dashboard/assets/{UpdatePasswordSchemas-DCuCj7NK.js → UpdatePasswordSchemas-Bauivjf-.js} +1 -1
  151. zenml/zen_server/dashboard/assets/{UsageReason-CwUrEwEz.js → UsageReason-Dr5ca5M4.js} +1 -1
  152. zenml/zen_server/dashboard/assets/{Wizard-CynnoHg4.js → Wizard-XEp9rGmf.js} +1 -1
  153. zenml/zen_server/dashboard/assets/{WizardFooter-B2bYs89C.js → WizardFooter-BtL1Gi1k.js} +1 -1
  154. zenml/zen_server/dashboard/assets/{all-pipeline-runs-query-B509kMlL.js → all-pipeline-runs-query-COvsm3bC.js} +1 -1
  155. zenml/zen_server/dashboard/assets/configuration-form-BJUCr0wl.js +1 -0
  156. zenml/zen_server/dashboard/assets/{create-stack-BjWXz5nx.js → create-stack-B2c98UlP.js} +1 -1
  157. zenml/zen_server/dashboard/assets/{delete-run-CzPWbsBy.js → delete-run-Do3XyF4W.js} +1 -1
  158. zenml/zen_server/dashboard/assets/flavor-select-D8CranSY.js +1 -0
  159. zenml/zen_server/dashboard/assets/{form-schemas-B6u3P_a4.js → form-schemas-Bm-dTV3L.js} +1 -1
  160. zenml/zen_server/dashboard/assets/{index-BCKg1Y5r.css → index-6mLFgFwe.css} +1 -1
  161. zenml/zen_server/dashboard/assets/{index-Bjeu4_0O.js → index-CzhJC6pc.js} +1 -1
  162. zenml/zen_server/dashboard/assets/{index-CaRx22lH.js → index-D-n6tspq.js} +1 -1
  163. zenml/zen_server/dashboard/assets/{index-DWoLoYDY.js → index-DPjvk73v.js} +8 -8
  164. zenml/zen_server/dashboard/assets/{index-Dba8yULY.js → index-eIIP-0dQ.js} +1 -1
  165. zenml/zen_server/dashboard/assets/login-mutation-D6uiKsKk.js +1 -0
  166. zenml/zen_server/dashboard/assets/{not-found-DGQ8rm7B.js → not-found-DFrksY0r.js} +1 -1
  167. zenml/zen_server/dashboard/assets/page-B-uHUFcm.js +1 -0
  168. zenml/zen_server/dashboard/assets/page-B0Llmzo_.js +1 -0
  169. zenml/zen_server/dashboard/assets/page-B150LbzG.js +1 -0
  170. zenml/zen_server/dashboard/assets/{page-CfeQbejg.js → page-B1Un9vAU.js} +1 -1
  171. zenml/zen_server/dashboard/assets/{page-CxrLV30P.js → page-B80TE04v.js} +1 -1
  172. zenml/zen_server/dashboard/assets/page-BIseZTJt.js +2 -0
  173. zenml/zen_server/dashboard/assets/{page-C5xq6rqE.js → page-BJ15SGwt.js} +1 -1
  174. zenml/zen_server/dashboard/assets/{page-B6msmF1h.js → page-BJrZsPSh.js} +1 -1
  175. zenml/zen_server/dashboard/assets/page-BMZaECzB.js +1 -0
  176. zenml/zen_server/dashboard/assets/{page-D1upvSPi.js → page-BTvnIFGR.js} +1 -1
  177. zenml/zen_server/dashboard/assets/{page-DLw1Apss.js → page-BXh1mF-D.js} +1 -1
  178. zenml/zen_server/dashboard/assets/page-BZUxCBoD.js +1 -0
  179. zenml/zen_server/dashboard/assets/page-BeFiRx31.js +1 -0
  180. zenml/zen_server/dashboard/assets/{page-C89bN6VV.js → page-BnUwQBeg.js} +1 -1
  181. zenml/zen_server/dashboard/assets/{page-k-UXKVnV.js → page-BqQ6y8Hb.js} +1 -1
  182. zenml/zen_server/dashboard/assets/page-BwAFqFCf.js +1 -0
  183. zenml/zen_server/dashboard/assets/page-BzlVs5tC.js +1 -0
  184. zenml/zen_server/dashboard/assets/{page-C3BbJ-5n.js → page-C11vPVkH.js} +1 -1
  185. zenml/zen_server/dashboard/assets/{page-AnG2ilmi.js → page-CAKBSE9f.js} +1 -1
  186. zenml/zen_server/dashboard/assets/page-CPe9nQSo.js +1 -0
  187. zenml/zen_server/dashboard/assets/page-D0Zt2-7X.js +1 -0
  188. zenml/zen_server/dashboard/assets/page-D2F0Rvak.js +1 -0
  189. zenml/zen_server/dashboard/assets/{page-2EzZ5aWS.js → page-D5GZlpKq.js} +1 -1
  190. zenml/zen_server/dashboard/assets/{page-CxoG4zme.js → page-DBNBYSwq.js} +1 -1
  191. zenml/zen_server/dashboard/assets/page-DDvwWgKP.js +6 -0
  192. zenml/zen_server/dashboard/assets/page-DF9q7ySu.js +1 -0
  193. zenml/zen_server/dashboard/assets/page-DOzFoJuo.js +1 -0
  194. zenml/zen_server/dashboard/assets/page-DaHH2ZEF.js +1 -0
  195. zenml/zen_server/dashboard/assets/{page-B9ELcPAy.js → page-Dd-0y3SU.js} +1 -1
  196. zenml/zen_server/dashboard/assets/{page-Dy6HYsJr.js → page-DhNnHHmX.js} +1 -1
  197. zenml/zen_server/dashboard/assets/{page-CZ_3LB0U.js → page-DkJfgcDi.js} +1 -1
  198. zenml/zen_server/dashboard/assets/page-EhqRFAZc.js +1 -0
  199. zenml/zen_server/dashboard/assets/{page-nHAZvd76.js → page-NIWnUdVg.js} +1 -1
  200. zenml/zen_server/dashboard/assets/{page-DazwBcbq.js → page-kYlFrH53.js} +1 -1
  201. zenml/zen_server/dashboard/assets/page-ygCPGHAV.js +1 -0
  202. zenml/zen_server/dashboard/assets/{persist-BglceT_t.js → persist-C5RlwSq6.js} +1 -1
  203. zenml/zen_server/dashboard/assets/{persist-CMkLV2Cs.js → persist-DHGuHP2H.js} +1 -1
  204. zenml/zen_server/dashboard/assets/{service-DNKY_ZYd.js → service-Do7yitqe.js} +1 -1
  205. zenml/zen_server/dashboard/assets/{sharedSchema-BOmQa793.js → sharedSchema-i_9Y4WcA.js} +1 -1
  206. zenml/zen_server/dashboard/assets/stack-detail-query-omCumL7U.js +1 -0
  207. zenml/zen_server/dashboard/assets/update-server-settings-mutation-B4eE33z-.js +1 -0
  208. zenml/zen_server/dashboard/index.html +4 -4
  209. zenml/zen_server/deploy/daemon/daemon_zen_server.py +1 -1
  210. zenml/zen_server/deploy/deployment.py +1 -2
  211. zenml/zen_server/deploy/docker/docker_zen_server.py +1 -1
  212. zenml/zen_server/rbac/endpoint_utils.py +1 -2
  213. zenml/zen_server/routers/projects_endpoints.py +14 -3
  214. zenml/zen_server/utils.py +2 -86
  215. zenml/zen_stores/migrations/versions/0.80.0_release.py +23 -0
  216. zenml/zen_stores/schemas/artifact_visualization_schemas.py +1 -1
  217. zenml/zen_stores/schemas/model_schemas.py +1 -1
  218. zenml/zen_stores/schemas/service_schemas.py +1 -1
  219. zenml/zen_stores/schemas/step_run_schemas.py +1 -1
  220. zenml/zen_stores/schemas/trigger_schemas.py +1 -1
  221. zenml/zen_stores/sql_zen_store.py +5 -0
  222. {zenml_nightly-0.75.0.dev20250317.dist-info → zenml_nightly-0.80.0.dev20250321.dist-info}/METADATA +7 -8
  223. {zenml_nightly-0.75.0.dev20250317.dist-info → zenml_nightly-0.80.0.dev20250321.dist-info}/RECORD +226 -225
  224. zenml/zen_server/dashboard/assets/@reactflow-DMaYqp8l.js +0 -17
  225. zenml/zen_server/dashboard/assets/CodeSnippet-D8ptwPjg.js +0 -9
  226. zenml/zen_server/dashboard/assets/ComponentIcon-gpMJ2Y2e.js +0 -1
  227. zenml/zen_server/dashboard/assets/ExecutionStatus-DHiK3Am-.js +0 -1
  228. zenml/zen_server/dashboard/assets/ProBadge-ypma7R8i.js +0 -1
  229. zenml/zen_server/dashboard/assets/ProviderIcon-DKN3Gdcg.js +0 -1
  230. zenml/zen_server/dashboard/assets/RunSelector-CYmRHGdm.js +0 -1
  231. zenml/zen_server/dashboard/assets/SecretTooltip-CHPWF0bu.js +0 -1
  232. zenml/zen_server/dashboard/assets/Tick-DgU4udUn.js +0 -1
  233. zenml/zen_server/dashboard/assets/configuration-form-BEwWCxqY.js +0 -1
  234. zenml/zen_server/dashboard/assets/flavor-select-C1pyy8gq.js +0 -1
  235. zenml/zen_server/dashboard/assets/login-mutation-7WFxPe10.js +0 -1
  236. zenml/zen_server/dashboard/assets/page-BKN4SYXY.js +0 -1
  237. zenml/zen_server/dashboard/assets/page-BNrOW_3T.js +0 -2
  238. zenml/zen_server/dashboard/assets/page-BX6ZrAVH.js +0 -1
  239. zenml/zen_server/dashboard/assets/page-BnOdORy3.js +0 -1
  240. zenml/zen_server/dashboard/assets/page-BtkfcEI7.js +0 -1
  241. zenml/zen_server/dashboard/assets/page-Bz_grLBY.js +0 -1
  242. zenml/zen_server/dashboard/assets/page-CCEwuGU4.js +0 -1
  243. zenml/zen_server/dashboard/assets/page-COAGXWJu.js +0 -1
  244. zenml/zen_server/dashboard/assets/page-CaibMa0l.js +0 -1
  245. zenml/zen_server/dashboard/assets/page-CskoTYOC.js +0 -1
  246. zenml/zen_server/dashboard/assets/page-Cyoe7AtN.js +0 -1
  247. zenml/zen_server/dashboard/assets/page-D03wm5f1.js +0 -1
  248. zenml/zen_server/dashboard/assets/page-D8UimvyP.js +0 -1
  249. zenml/zen_server/dashboard/assets/page-DEnmFyzi.js +0 -1
  250. zenml/zen_server/dashboard/assets/page-TiOZeeo0.js +0 -1
  251. zenml/zen_server/dashboard/assets/page-cveasWUr.js +0 -6
  252. zenml/zen_server/dashboard/assets/page-iTvxfhgZ.js +0 -1
  253. zenml/zen_server/dashboard/assets/page-niRD8Hqz.js +0 -1
  254. zenml/zen_server/dashboard/assets/stack-detail-query-CI_YMUx6.js +0 -1
  255. zenml/zen_server/dashboard/assets/transform-DKsRLKTv.js +0 -1
  256. zenml/zen_server/dashboard/assets/update-server-settings-mutation-CNYCc-FU.js +0 -1
  257. {zenml_nightly-0.75.0.dev20250317.dist-info → zenml_nightly-0.80.0.dev20250321.dist-info}/LICENSE +0 -0
  258. {zenml_nightly-0.75.0.dev20250317.dist-info → zenml_nightly-0.80.0.dev20250321.dist-info}/WHEEL +0 -0
  259. {zenml_nightly-0.75.0.dev20250317.dist-info → zenml_nightly-0.80.0.dev20250321.dist-info}/entry_points.txt +0 -0
@@ -37,7 +37,7 @@ from pydantic import (
37
37
  field_validator,
38
38
  model_validator,
39
39
  )
40
- from sqlalchemy import Float, and_, asc, cast, desc
40
+ from sqlalchemy import Float, and_, asc, cast, desc, or_
41
41
  from sqlmodel import SQLModel
42
42
 
43
43
  from zenml.constants import (
@@ -171,8 +171,6 @@ class BoolFilter(Filter):
171
171
  class StrFilter(Filter):
172
172
  """Filter for all string fields."""
173
173
 
174
- json_encode_value: bool = False
175
-
176
174
  ALLOWED_OPS: ClassVar[List[str]] = [
177
175
  GenericFilterOps.EQUALS,
178
176
  GenericFilterOps.NOT_EQUALS,
@@ -201,6 +199,23 @@ class StrFilter(Filter):
201
199
  raise ValueError(ONEOF_ERROR)
202
200
  return self
203
201
 
202
+ def _check_if_column_is_json_encoded(self, column: Any) -> bool:
203
+ """Check if the column is json encoded.
204
+
205
+ Args:
206
+ column: The column of an SQLModel table on which to filter.
207
+
208
+ Returns:
209
+ True if the column is json encoded, False otherwise.
210
+ """
211
+ from zenml.zen_stores.schemas import RunMetadataSchema
212
+
213
+ JSON_ENCODED_COLUMNS = [RunMetadataSchema.value]
214
+
215
+ if column in JSON_ENCODED_COLUMNS:
216
+ return True
217
+ return False
218
+
204
219
  def generate_query_conditions_from_column(self, column: Any) -> Any:
205
220
  """Generate query conditions for a string column.
206
221
 
@@ -209,70 +224,168 @@ class StrFilter(Filter):
209
224
 
210
225
  Returns:
211
226
  A list of query conditions.
212
-
213
- Raises:
214
- ValueError: the comparison of the column to a numeric value fails.
215
227
  """
228
+ # Handle numeric comparisons (GT, LT, GTE, LTE)
216
229
  if self.operation in {
217
230
  GenericFilterOps.GT,
218
231
  GenericFilterOps.LT,
219
232
  GenericFilterOps.GTE,
220
233
  GenericFilterOps.LTE,
221
234
  }:
222
- try:
223
- numeric_column = cast(column, Float)
224
-
225
- assert self.value is not None
226
-
227
- if self.operation == GenericFilterOps.GT:
228
- return and_(
229
- numeric_column, numeric_column > float(self.value)
230
- )
231
- if self.operation == GenericFilterOps.LT:
232
- return and_(
233
- numeric_column, numeric_column < float(self.value)
234
- )
235
- if self.operation == GenericFilterOps.GTE:
236
- return and_(
237
- numeric_column, numeric_column >= float(self.value)
238
- )
239
- if self.operation == GenericFilterOps.LTE:
240
- return and_(
241
- numeric_column, numeric_column <= float(self.value)
242
- )
243
- except Exception as e:
244
- raise ValueError(
245
- f"Failed to compare the column '{column}' to the "
246
- f"value '{self.value}' (must be numeric): {e}"
247
- )
235
+ return self._handle_numeric_comparison(column)
236
+
237
+ # Handle operations that need special treatment for JSON-encoded columns
238
+ is_json_encoded = self._check_if_column_is_json_encoded(column)
248
239
 
240
+ # Handle list operations
249
241
  if self.operation == GenericFilterOps.ONEOF:
250
242
  assert isinstance(self.value, list)
251
- # Convert the list of values to a list of json strings
252
- json_list = (
253
- [json.dumps(v) for v in self.value]
254
- if self.json_encode_value
255
- else self.value
256
- )
257
- return column.in_(json_list)
243
+ return self._handle_oneof(column, is_json_encoded)
258
244
 
259
- # Don't convert the value to a json string if the operation is contains
260
- # because the quotes around strings will mess with the comparison
245
+ # Handle pattern matching operations
261
246
  if self.operation == GenericFilterOps.CONTAINS:
262
247
  return column.like(f"%{self.value}%")
263
248
 
264
- json_value = (
265
- json.dumps(self.value) if self.json_encode_value else self.value
266
- )
267
-
268
249
  if self.operation == GenericFilterOps.STARTSWITH:
269
- return column.startswith(f"{json_value}")
250
+ return self._handle_startswith(column, is_json_encoded)
251
+
270
252
  if self.operation == GenericFilterOps.ENDSWITH:
271
- return column.endswith(f"{json_value}")
253
+ return self._handle_endswith(column, is_json_encoded)
254
+
272
255
  if self.operation == GenericFilterOps.NOT_EQUALS:
273
- return column != json_value
256
+ return self._handle_not_equals(column, is_json_encoded)
274
257
 
275
- return column == json_value
258
+ # Default case (EQUALS)
259
+ return self._handle_equals(column, is_json_encoded)
260
+
261
+ def _handle_numeric_comparison(self, column: Any) -> Any:
262
+ """Handle numeric comparison operations.
263
+
264
+ Args:
265
+ column: The column to compare.
266
+
267
+ Returns:
268
+ The query condition.
269
+
270
+ Raises:
271
+ ValueError: If the comparison fails.
272
+ """
273
+ try:
274
+ numeric_column = cast(column, Float)
275
+ assert self.value is not None
276
+
277
+ operations = {
278
+ GenericFilterOps.GT: lambda col, val: and_(
279
+ col, col > float(val)
280
+ ),
281
+ GenericFilterOps.LT: lambda col, val: and_(
282
+ col, col < float(val)
283
+ ),
284
+ GenericFilterOps.GTE: lambda col, val: and_(
285
+ col, col >= float(val)
286
+ ),
287
+ GenericFilterOps.LTE: lambda col, val: and_(
288
+ col, col <= float(val)
289
+ ),
290
+ }
291
+
292
+ return operations[self.operation](numeric_column, self.value) # type: ignore[no-untyped-call]
293
+ except Exception as e:
294
+ raise ValueError(
295
+ f"Failed to compare the column '{column}' to the "
296
+ f"value '{self.value}' (must be numeric): {e}"
297
+ )
298
+
299
+ def _handle_oneof(self, column: Any, is_json_encoded: bool) -> Any:
300
+ """Handle the ONEOF operation.
301
+
302
+ Args:
303
+ column: The column to check.
304
+ is_json_encoded: Whether the column is JSON encoded.
305
+
306
+ Returns:
307
+ The query condition.
308
+ """
309
+ from sqlalchemy import or_
310
+
311
+ conditions = []
312
+
313
+ assert isinstance(self.value, list)
314
+
315
+ for value in self.value:
316
+ if is_json_encoded:
317
+ # For JSON encoded columns, add conditions for both raw and JSON-quoted values
318
+ conditions.append(column == value)
319
+ conditions.append(column == f'"{value}"')
320
+ else:
321
+ conditions.append(column == value)
322
+
323
+ return or_(*conditions)
324
+
325
+ def _handle_startswith(self, column: Any, is_json_encoded: bool) -> Any:
326
+ """Handle the STARTSWITH operation.
327
+
328
+ Args:
329
+ column: The column to check.
330
+ is_json_encoded: Whether the column is JSON encoded.
331
+
332
+ Returns:
333
+ The query condition.
334
+ """
335
+ if is_json_encoded:
336
+ return or_(
337
+ column.startswith(self.value),
338
+ column.startswith(f'"{self.value}'),
339
+ )
340
+ else:
341
+ return column.startswith(self.value)
342
+
343
+ def _handle_endswith(self, column: Any, is_json_encoded: bool) -> Any:
344
+ """Handle the ENDSWITH operation.
345
+
346
+ Args:
347
+ column: The column to check.
348
+ is_json_encoded: Whether the column is JSON encoded.
349
+
350
+ Returns:
351
+ The query condition.
352
+ """
353
+ if is_json_encoded:
354
+ return or_(
355
+ column.endswith(self.value), column.endswith(f'{self.value}"')
356
+ )
357
+ else:
358
+ return column.endswith(self.value)
359
+
360
+ def _handle_not_equals(self, column: Any, is_json_encoded: bool) -> Any:
361
+ """Handle the NOT_EQUALS operation.
362
+
363
+ Args:
364
+ column: The column to check.
365
+ is_json_encoded: Whether the column is JSON encoded.
366
+
367
+ Returns:
368
+ The query condition.
369
+ """
370
+ if is_json_encoded:
371
+ return and_(column != self.value, column != f'"{self.value}"')
372
+ else:
373
+ return column != self.value
374
+
375
+ def _handle_equals(self, column: Any, is_json_encoded: bool) -> Any:
376
+ """Handle the EQUALS operation (default).
377
+
378
+ Args:
379
+ column: The column to check.
380
+ is_json_encoded: Whether the column is JSON encoded.
381
+
382
+ Returns:
383
+ The query condition.
384
+ """
385
+ if is_json_encoded:
386
+ return or_(column == self.value, column == f'"{self.value}"')
387
+ else:
388
+ return column == self.value
276
389
 
277
390
 
278
391
  class UUIDFilter(StrFilter):
@@ -751,7 +864,6 @@ class BaseFilter(BaseModel):
751
864
  value: Any,
752
865
  table: Type[SQLModel],
753
866
  column: str,
754
- json_encode_value: bool = False,
755
867
  ) -> "ColumnElement[bool]":
756
868
  """Generate custom filter conditions for a column of a table.
757
869
 
@@ -759,7 +871,6 @@ class BaseFilter(BaseModel):
759
871
  value: The filter value.
760
872
  table: The table which contains the column.
761
873
  column: The column name.
762
- json_encode_value: Whether to json encode the value.
763
874
 
764
875
  Returns:
765
876
  The query conditions.
@@ -768,9 +879,6 @@ class BaseFilter(BaseModel):
768
879
  filter_ = FilterGenerator(table).define_filter(
769
880
  column=column, value=value, operator=operator
770
881
  )
771
- if isinstance(filter_, StrFilter):
772
- filter_.json_encode_value = json_encode_value
773
-
774
882
  return filter_.generate_query_conditions(table=table)
775
883
 
776
884
  @property
@@ -426,6 +426,10 @@ class TaggableFilter(BaseFilter):
426
426
  *BaseFilter.CUSTOM_SORTING_OPTIONS,
427
427
  "tags",
428
428
  ]
429
+ API_MULTI_INPUT_PARAMS: ClassVar[List[str]] = [
430
+ *BaseFilter.API_MULTI_INPUT_PARAMS,
431
+ "tags",
432
+ ]
429
433
 
430
434
  @model_validator(mode="after")
431
435
  def add_tag_to_tags(self) -> "TaggableFilter":
@@ -583,3 +587,131 @@ class TaggableFilter(BaseFilter):
583
587
  return query
584
588
 
585
589
  return super().apply_sorting(query=query, table=table)
590
+
591
+
592
+ class RunMetadataFilterMixin(BaseFilter):
593
+ """Model to enable filtering and sorting by run metadata."""
594
+
595
+ run_metadata: Optional[List[str]] = Field(
596
+ default=None,
597
+ description="The run_metadata to filter the pipeline runs by.",
598
+ )
599
+ FILTER_EXCLUDE_FIELDS: ClassVar[List[str]] = [
600
+ *BaseFilter.FILTER_EXCLUDE_FIELDS,
601
+ "run_metadata",
602
+ ]
603
+ API_MULTI_INPUT_PARAMS: ClassVar[List[str]] = [
604
+ *BaseFilter.API_MULTI_INPUT_PARAMS,
605
+ "run_metadata",
606
+ ]
607
+
608
+ @model_validator(mode="after")
609
+ def validate_run_metadata_format(self) -> "RunMetadataFilterMixin":
610
+ """Validates that run_metadata entries are in the correct format.
611
+
612
+ Each run_metadata entry must be in one of the following formats:
613
+ 1. "key:value" - Direct equality comparison (key equals value)
614
+ 2. "key:filterop:value" - Where filterop is one of the GenericFilterOps:
615
+ - equals: Exact match
616
+ - notequals: Not equal to
617
+ - contains: String contains value
618
+ - startswith: String starts with value
619
+ - endswith: String ends with value
620
+ - oneof: Value is one of the specified options
621
+ - gte: Greater than or equal to
622
+ - gt: Greater than
623
+ - lte: Less than or equal to
624
+ - lt: Less than
625
+ - in: Value is in a list
626
+
627
+ Examples:
628
+ - "status:completed" - Find entries where status equals "completed"
629
+ - "name:contains:test" - Find entries where name contains "test"
630
+ - "duration:gt:10" - Find entries where duration is greater than 10
631
+
632
+ Returns:
633
+ self
634
+
635
+ Raises:
636
+ ValueError: If any entry in run_metadata does not contain a colon.
637
+ """
638
+ if self.run_metadata:
639
+ for entry in self.run_metadata:
640
+ if ":" not in entry:
641
+ raise ValueError(
642
+ f"Invalid run_metadata entry format: '{entry}'. "
643
+ "Entry must be in format 'key:value' for direct "
644
+ "equality comparison or 'key:filterop:value' where "
645
+ "filterop is one of: equals, notequals, "
646
+ f"contains, startswith, endswith, oneof, gte, gt, "
647
+ f"lte, lt, in."
648
+ )
649
+ return self
650
+
651
+ def get_custom_filters(
652
+ self, table: Type["AnySchema"]
653
+ ) -> List["ColumnElement[bool]"]:
654
+ """Get custom run metadata filters.
655
+
656
+ Args:
657
+ table: The query table.
658
+
659
+ Returns:
660
+ A list of custom filters.
661
+ """
662
+ custom_filters = super().get_custom_filters(table)
663
+
664
+ if self.run_metadata is not None:
665
+ from sqlmodel import exists, select
666
+
667
+ from zenml.enums import MetadataResourceTypes
668
+ from zenml.zen_stores.schemas import (
669
+ ArtifactVersionSchema,
670
+ ModelVersionSchema,
671
+ PipelineRunSchema,
672
+ RunMetadataResourceSchema,
673
+ RunMetadataSchema,
674
+ ScheduleSchema,
675
+ StepRunSchema,
676
+ )
677
+
678
+ resource_type_mapping = {
679
+ ArtifactVersionSchema: MetadataResourceTypes.ARTIFACT_VERSION,
680
+ ModelVersionSchema: MetadataResourceTypes.MODEL_VERSION,
681
+ PipelineRunSchema: MetadataResourceTypes.PIPELINE_RUN,
682
+ StepRunSchema: MetadataResourceTypes.STEP_RUN,
683
+ ScheduleSchema: MetadataResourceTypes.SCHEDULE,
684
+ }
685
+
686
+ # Create an EXISTS subquery for each run_metadata filter
687
+ for entry in self.run_metadata:
688
+ # Split at the first colon to get the key
689
+ key, value = entry.split(":", 1)
690
+
691
+ # Create an exists subquery
692
+ exists_subquery = exists(
693
+ select(RunMetadataResourceSchema.id)
694
+ .join(
695
+ RunMetadataSchema,
696
+ RunMetadataSchema.id # type: ignore[arg-type]
697
+ == RunMetadataResourceSchema.run_metadata_id,
698
+ )
699
+ .where(
700
+ RunMetadataResourceSchema.resource_id == table.id,
701
+ RunMetadataResourceSchema.resource_type
702
+ == resource_type_mapping[table].value,
703
+ self.generate_custom_query_conditions_for_column(
704
+ value=key,
705
+ table=RunMetadataSchema,
706
+ column="key",
707
+ ),
708
+ self.generate_custom_query_conditions_for_column(
709
+ value=value,
710
+ table=RunMetadataSchema,
711
+ column="value",
712
+ ),
713
+ )
714
+ )
715
+ custom_filters.append(exists_subquery)
716
+
717
+ return custom_filters
@@ -47,6 +47,7 @@ from zenml.models.v2.base.scoped import (
47
47
  ProjectScopedResponseBody,
48
48
  ProjectScopedResponseMetadata,
49
49
  ProjectScopedResponseResources,
50
+ RunMetadataFilterMixin,
50
51
  TaggableFilter,
51
52
  )
52
53
  from zenml.models.v2.core.artifact import ArtifactResponse
@@ -470,12 +471,15 @@ class ArtifactVersionResponse(
470
471
  # ------------------ Filter Model ------------------
471
472
 
472
473
 
473
- class ArtifactVersionFilter(ProjectScopedFilter, TaggableFilter):
474
+ class ArtifactVersionFilter(
475
+ ProjectScopedFilter, TaggableFilter, RunMetadataFilterMixin
476
+ ):
474
477
  """Model to enable advanced filtering of artifact versions."""
475
478
 
476
479
  FILTER_EXCLUDE_FIELDS: ClassVar[List[str]] = [
477
480
  *ProjectScopedFilter.FILTER_EXCLUDE_FIELDS,
478
481
  *TaggableFilter.FILTER_EXCLUDE_FIELDS,
482
+ *RunMetadataFilterMixin.FILTER_EXCLUDE_FIELDS,
479
483
  "artifact_id",
480
484
  "artifact",
481
485
  "only_unused",
@@ -483,17 +487,23 @@ class ArtifactVersionFilter(ProjectScopedFilter, TaggableFilter):
483
487
  "model",
484
488
  "pipeline_run",
485
489
  "model_version_id",
486
- "run_metadata",
487
490
  ]
488
491
  CUSTOM_SORTING_OPTIONS: ClassVar[List[str]] = [
489
492
  *ProjectScopedFilter.CUSTOM_SORTING_OPTIONS,
490
493
  *TaggableFilter.CUSTOM_SORTING_OPTIONS,
494
+ *RunMetadataFilterMixin.CUSTOM_SORTING_OPTIONS,
491
495
  ]
492
496
  CLI_EXCLUDE_FIELDS: ClassVar[List[str]] = [
493
497
  *ProjectScopedFilter.CLI_EXCLUDE_FIELDS,
494
498
  *TaggableFilter.CLI_EXCLUDE_FIELDS,
499
+ *RunMetadataFilterMixin.CLI_EXCLUDE_FIELDS,
495
500
  "artifact_id",
496
501
  ]
502
+ API_MULTI_INPUT_PARAMS: ClassVar[List[str]] = [
503
+ *ProjectScopedFilter.API_MULTI_INPUT_PARAMS,
504
+ *TaggableFilter.API_MULTI_INPUT_PARAMS,
505
+ *RunMetadataFilterMixin.API_MULTI_INPUT_PARAMS,
506
+ ]
497
507
 
498
508
  artifact: Optional[Union[UUID, str]] = Field(
499
509
  default=None,
@@ -561,10 +571,6 @@ class ArtifactVersionFilter(ProjectScopedFilter, TaggableFilter):
561
571
  description="Name/ID of a pipeline run that is associated with this "
562
572
  "artifact version.",
563
573
  )
564
- run_metadata: Optional[Dict[str, Any]] = Field(
565
- default=None,
566
- description="The run_metadata to filter the artifact versions by.",
567
- )
568
574
 
569
575
  model_config = ConfigDict(protected_namespaces=())
570
576
 
@@ -590,8 +596,6 @@ class ArtifactVersionFilter(ProjectScopedFilter, TaggableFilter):
590
596
  ModelVersionArtifactSchema,
591
597
  ModelVersionSchema,
592
598
  PipelineRunSchema,
593
- RunMetadataResourceSchema,
594
- RunMetadataSchema,
595
599
  StepRunInputArtifactSchema,
596
600
  StepRunOutputArtifactSchema,
597
601
  StepRunSchema,
@@ -674,31 +678,6 @@ class ArtifactVersionFilter(ProjectScopedFilter, TaggableFilter):
674
678
  )
675
679
  custom_filters.append(pipeline_run_filter)
676
680
 
677
- if self.run_metadata is not None:
678
- from zenml.enums import MetadataResourceTypes
679
-
680
- for key, value in self.run_metadata.items():
681
- additional_filter = and_(
682
- RunMetadataResourceSchema.resource_id
683
- == ArtifactVersionSchema.id,
684
- RunMetadataResourceSchema.resource_type
685
- == MetadataResourceTypes.ARTIFACT_VERSION.value,
686
- RunMetadataResourceSchema.run_metadata_id
687
- == RunMetadataSchema.id,
688
- self.generate_custom_query_conditions_for_column(
689
- value=key,
690
- table=RunMetadataSchema,
691
- column="key",
692
- ),
693
- self.generate_custom_query_conditions_for_column(
694
- value=value,
695
- table=RunMetadataSchema,
696
- column="value",
697
- json_encode_value=True,
698
- ),
699
- )
700
- custom_filters.append(additional_filter)
701
-
702
681
  return custom_filters
703
682
 
704
683
  @model_validator(mode="after")
@@ -40,14 +40,13 @@ from zenml.models.v2.base.scoped import (
40
40
  ProjectScopedResponseBody,
41
41
  ProjectScopedResponseMetadata,
42
42
  ProjectScopedResponseResources,
43
+ RunMetadataFilterMixin,
43
44
  TaggableFilter,
44
45
  )
45
46
  from zenml.models.v2.core.service import ServiceResponse
46
47
  from zenml.models.v2.core.tag import TagResponse
47
48
 
48
49
  if TYPE_CHECKING:
49
- from sqlalchemy.sql.elements import ColumnElement
50
-
51
50
  from zenml.model.model import Model
52
51
  from zenml.models.v2.core.artifact_version import ArtifactVersionResponse
53
52
  from zenml.models.v2.core.model import ModelResponse
@@ -566,24 +565,33 @@ class ModelVersionResponse(
566
565
  # ------------------ Filter Model ------------------
567
566
 
568
567
 
569
- class ModelVersionFilter(ProjectScopedFilter, TaggableFilter):
568
+ class ModelVersionFilter(
569
+ ProjectScopedFilter, TaggableFilter, RunMetadataFilterMixin
570
+ ):
570
571
  """Filter model for model versions."""
571
572
 
572
573
  FILTER_EXCLUDE_FIELDS: ClassVar[List[str]] = [
573
574
  *ProjectScopedFilter.FILTER_EXCLUDE_FIELDS,
574
575
  *TaggableFilter.FILTER_EXCLUDE_FIELDS,
576
+ *RunMetadataFilterMixin.FILTER_EXCLUDE_FIELDS,
575
577
  "model",
576
- "run_metadata",
577
578
  ]
578
579
  CUSTOM_SORTING_OPTIONS: ClassVar[List[str]] = [
579
580
  *ProjectScopedFilter.CUSTOM_SORTING_OPTIONS,
580
581
  *TaggableFilter.CUSTOM_SORTING_OPTIONS,
582
+ *RunMetadataFilterMixin.CUSTOM_SORTING_OPTIONS,
581
583
  ]
582
584
  CLI_EXCLUDE_FIELDS: ClassVar[List[str]] = [
583
585
  *ProjectScopedFilter.CLI_EXCLUDE_FIELDS,
584
586
  *TaggableFilter.CLI_EXCLUDE_FIELDS,
587
+ *RunMetadataFilterMixin.CLI_EXCLUDE_FIELDS,
585
588
  "model",
586
589
  ]
590
+ API_MULTI_INPUT_PARAMS: ClassVar[List[str]] = [
591
+ *ProjectScopedFilter.API_MULTI_INPUT_PARAMS,
592
+ *TaggableFilter.API_MULTI_INPUT_PARAMS,
593
+ *RunMetadataFilterMixin.API_MULTI_INPUT_PARAMS,
594
+ ]
587
595
 
588
596
  name: Optional[str] = Field(
589
597
  default=None,
@@ -598,10 +606,6 @@ class ModelVersionFilter(ProjectScopedFilter, TaggableFilter):
598
606
  default=None,
599
607
  union_mode="left_to_right",
600
608
  )
601
- run_metadata: Optional[Dict[str, str]] = Field(
602
- default=None,
603
- description="The run_metadata to filter the model versions by.",
604
- )
605
609
  model: Optional[Union[str, UUID]] = Field(
606
610
  default=None,
607
611
  description="The name or ID of the model which the search is scoped "
@@ -611,48 +615,6 @@ class ModelVersionFilter(ProjectScopedFilter, TaggableFilter):
611
615
  union_mode="left_to_right",
612
616
  )
613
617
 
614
- def get_custom_filters(
615
- self, table: Type["AnySchema"]
616
- ) -> List["ColumnElement[bool]"]:
617
- """Get custom filters.
618
-
619
- Args:
620
- table: The query table.
621
-
622
- Returns:
623
- A list of custom filters.
624
- """
625
- custom_filters = super().get_custom_filters(table)
626
-
627
- from sqlmodel import and_
628
-
629
- from zenml.zen_stores.schemas import (
630
- ModelVersionSchema,
631
- RunMetadataResourceSchema,
632
- RunMetadataSchema,
633
- )
634
-
635
- if self.run_metadata is not None:
636
- from zenml.enums import MetadataResourceTypes
637
-
638
- for key, value in self.run_metadata.items():
639
- additional_filter = and_(
640
- RunMetadataResourceSchema.resource_id
641
- == ModelVersionSchema.id,
642
- RunMetadataResourceSchema.resource_type
643
- == MetadataResourceTypes.MODEL_VERSION,
644
- RunMetadataResourceSchema.run_metadata_id
645
- == RunMetadataSchema.id,
646
- self.generate_custom_query_conditions_for_column(
647
- value=value,
648
- table=RunMetadataSchema,
649
- column="value",
650
- ),
651
- )
652
- custom_filters.append(additional_filter)
653
-
654
- return custom_filters
655
-
656
618
  def apply_filter(
657
619
  self,
658
620
  query: AnyQuery,