zenml-nightly 0.75.0.dev20250312__py3-none-any.whl → 0.75.0.dev20250314__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 (191) hide show
  1. zenml/VERSION +1 -1
  2. zenml/__init__.py +2 -0
  3. zenml/analytics/context.py +7 -0
  4. zenml/analytics/enums.py +2 -2
  5. zenml/artifacts/utils.py +2 -4
  6. zenml/cli/__init__.py +8 -9
  7. zenml/cli/base.py +2 -2
  8. zenml/cli/code_repository.py +1 -1
  9. zenml/cli/login.py +6 -0
  10. zenml/cli/model.py +7 -15
  11. zenml/cli/pipeline.py +3 -3
  12. zenml/cli/project.py +172 -0
  13. zenml/cli/secret.py +47 -44
  14. zenml/cli/service_accounts.py +0 -1
  15. zenml/cli/service_connectors.py +15 -17
  16. zenml/cli/stack.py +0 -3
  17. zenml/cli/stack_components.py +2 -2
  18. zenml/cli/tag.py +3 -5
  19. zenml/cli/utils.py +25 -23
  20. zenml/client.py +749 -475
  21. zenml/config/global_config.py +48 -37
  22. zenml/config/pipeline_configurations.py +3 -2
  23. zenml/config/pipeline_run_configuration.py +2 -1
  24. zenml/config/secret_reference_mixin.py +1 -1
  25. zenml/constants.py +6 -6
  26. zenml/enums.py +0 -7
  27. zenml/event_hub/event_hub.py +3 -1
  28. zenml/exceptions.py +0 -24
  29. zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +5 -3
  30. zenml/integrations/bitbucket/plugins/event_sources/bitbucket_webhook_event_source.py +1 -4
  31. zenml/integrations/gcp/service_connectors/gcp_service_connector.py +7 -6
  32. zenml/integrations/github/plugins/event_sources/github_webhook_event_source.py +1 -4
  33. zenml/integrations/mlflow/steps/mlflow_registry.py +3 -3
  34. zenml/integrations/seldon/model_deployers/seldon_model_deployer.py +1 -1
  35. zenml/integrations/wandb/__init__.py +1 -1
  36. zenml/integrations/wandb/experiment_trackers/wandb_experiment_tracker.py +29 -9
  37. zenml/integrations/wandb/flavors/wandb_experiment_tracker_flavor.py +5 -3
  38. zenml/model/model.py +10 -10
  39. zenml/model_registries/base_model_registry.py +1 -1
  40. zenml/models/__init__.py +45 -28
  41. zenml/models/v2/base/base.py +0 -5
  42. zenml/models/v2/base/filter.py +2 -2
  43. zenml/models/v2/base/scoped.py +135 -156
  44. zenml/models/v2/core/action.py +12 -12
  45. zenml/models/v2/core/api_key.py +1 -1
  46. zenml/models/v2/core/artifact.py +31 -18
  47. zenml/models/v2/core/artifact_version.py +57 -40
  48. zenml/models/v2/core/code_repository.py +12 -12
  49. zenml/models/v2/core/component.py +22 -33
  50. zenml/models/v2/core/device.py +3 -2
  51. zenml/models/v2/core/event_source.py +14 -14
  52. zenml/models/v2/core/flavor.py +19 -47
  53. zenml/models/v2/core/logs.py +1 -2
  54. zenml/models/v2/core/model.py +23 -20
  55. zenml/models/v2/core/model_version.py +51 -42
  56. zenml/models/v2/core/pipeline.py +16 -16
  57. zenml/models/v2/core/pipeline_build.py +14 -14
  58. zenml/models/v2/core/pipeline_deployment.py +12 -14
  59. zenml/models/v2/core/pipeline_run.py +21 -29
  60. zenml/models/v2/core/project.py +203 -0
  61. zenml/models/v2/core/run_metadata.py +2 -2
  62. zenml/models/v2/core/run_template.py +16 -17
  63. zenml/models/v2/core/schedule.py +12 -21
  64. zenml/models/v2/core/secret.py +94 -128
  65. zenml/models/v2/core/server_settings.py +2 -2
  66. zenml/models/v2/core/service.py +57 -26
  67. zenml/models/v2/core/service_connector.py +14 -16
  68. zenml/models/v2/core/stack.py +24 -26
  69. zenml/models/v2/core/step_run.py +16 -28
  70. zenml/models/v2/core/tag.py +41 -15
  71. zenml/models/v2/core/trigger.py +13 -13
  72. zenml/models/v2/core/trigger_execution.py +2 -2
  73. zenml/models/v2/core/user.py +2 -2
  74. zenml/models/v2/misc/statistics.py +45 -0
  75. zenml/models/v2/misc/tag.py +27 -0
  76. zenml/orchestrators/cache_utils.py +7 -7
  77. zenml/orchestrators/input_utils.py +1 -0
  78. zenml/orchestrators/step_launcher.py +1 -2
  79. zenml/orchestrators/step_run_utils.py +2 -4
  80. zenml/orchestrators/step_runner.py +10 -1
  81. zenml/orchestrators/utils.py +4 -4
  82. zenml/pipelines/build_utils.py +2 -4
  83. zenml/pipelines/pipeline_decorator.py +3 -2
  84. zenml/pipelines/pipeline_definition.py +8 -9
  85. zenml/pipelines/run_utils.py +4 -4
  86. zenml/service_connectors/service_connector.py +0 -10
  87. zenml/service_connectors/service_connector_utils.py +0 -2
  88. zenml/stack/authentication_mixin.py +1 -1
  89. zenml/stack/flavor.py +3 -14
  90. zenml/stack/stack.py +0 -1
  91. zenml/stack/stack_component.py +1 -5
  92. zenml/steps/base_step.py +10 -2
  93. zenml/steps/step_context.py +19 -0
  94. zenml/utils/string_utils.py +1 -1
  95. zenml/utils/tag_utils.py +642 -0
  96. zenml/zen_server/cloud_utils.py +21 -0
  97. zenml/zen_server/exceptions.py +0 -6
  98. zenml/zen_server/rbac/endpoint_utils.py +134 -46
  99. zenml/zen_server/rbac/models.py +65 -3
  100. zenml/zen_server/rbac/rbac_interface.py +9 -0
  101. zenml/zen_server/rbac/rbac_sql_zen_store.py +15 -7
  102. zenml/zen_server/rbac/utils.py +155 -30
  103. zenml/zen_server/rbac/zenml_cloud_rbac.py +39 -11
  104. zenml/zen_server/routers/actions_endpoints.py +3 -5
  105. zenml/zen_server/routers/artifact_endpoint.py +0 -5
  106. zenml/zen_server/routers/artifact_version_endpoints.py +15 -9
  107. zenml/zen_server/routers/auth_endpoints.py +22 -7
  108. zenml/zen_server/routers/code_repositories_endpoints.py +54 -3
  109. zenml/zen_server/routers/devices_endpoints.py +0 -4
  110. zenml/zen_server/routers/event_source_endpoints.py +0 -5
  111. zenml/zen_server/routers/flavors_endpoints.py +0 -5
  112. zenml/zen_server/routers/logs_endpoints.py +0 -1
  113. zenml/zen_server/routers/model_versions_endpoints.py +100 -23
  114. zenml/zen_server/routers/models_endpoints.py +50 -69
  115. zenml/zen_server/routers/pipeline_builds_endpoints.py +55 -3
  116. zenml/zen_server/routers/pipeline_deployments_endpoints.py +56 -4
  117. zenml/zen_server/routers/pipelines_endpoints.py +70 -3
  118. zenml/zen_server/routers/plugin_endpoints.py +0 -1
  119. zenml/zen_server/routers/projects_endpoints.py +283 -0
  120. zenml/zen_server/routers/run_metadata_endpoints.py +97 -0
  121. zenml/zen_server/routers/run_templates_endpoints.py +64 -3
  122. zenml/zen_server/routers/runs_endpoints.py +58 -8
  123. zenml/zen_server/routers/schedule_endpoints.py +67 -6
  124. zenml/zen_server/routers/secrets_endpoints.py +38 -4
  125. zenml/zen_server/routers/server_endpoints.py +53 -1
  126. zenml/zen_server/routers/service_accounts_endpoints.py +14 -15
  127. zenml/zen_server/routers/service_connectors_endpoints.py +94 -14
  128. zenml/zen_server/routers/service_endpoints.py +18 -7
  129. zenml/zen_server/routers/stack_components_endpoints.py +66 -7
  130. zenml/zen_server/routers/stacks_endpoints.py +95 -6
  131. zenml/zen_server/routers/steps_endpoints.py +17 -11
  132. zenml/zen_server/routers/tag_resource_endpoints.py +115 -0
  133. zenml/zen_server/routers/tags_endpoints.py +6 -17
  134. zenml/zen_server/routers/triggers_endpoints.py +5 -8
  135. zenml/zen_server/routers/users_endpoints.py +9 -12
  136. zenml/zen_server/template_execution/utils.py +8 -7
  137. zenml/zen_server/utils.py +21 -0
  138. zenml/zen_server/zen_server_api.py +7 -2
  139. zenml/zen_stores/base_zen_store.py +50 -69
  140. zenml/zen_stores/migrations/versions/12eff0206201_rename_workspace_to_project.py +768 -0
  141. zenml/zen_stores/migrations/versions/1cb6477f72d6_move_artifact_save_type.py +20 -10
  142. zenml/zen_stores/migrations/versions/1f9d1cd00b90_add_unique_name_constraints.py +231 -0
  143. zenml/zen_stores/migrations/versions/288f4fb6e112_make_tags_user_scoped.py +74 -0
  144. zenml/zen_stores/migrations/versions/2e695a26fe7a_add_user_default_workspace.py +45 -0
  145. zenml/zen_stores/migrations/versions/3b1776345020_remove_workspace_from_globals.py +81 -0
  146. zenml/zen_stores/migrations/versions/41b28cae31ce_make_artifacts_workspace_scoped.py +136 -0
  147. zenml/zen_stores/migrations/versions/9e7bf0970266_adding_exclusive_attribute_to_tags.py +47 -0
  148. zenml/zen_stores/migrations/versions/b557b2871693_update_step_run_input_types.py +8 -4
  149. zenml/zen_stores/migrations/versions/cbc6acd71f92_add_workspace_display_name.py +58 -0
  150. zenml/zen_stores/migrations/versions/cc269488e5a9_separate_run_metadata.py +12 -6
  151. zenml/zen_stores/migrations/versions/f1d723fd723b_add_secret_private_attr.py +61 -0
  152. zenml/zen_stores/migrations/versions/f76a368a25a5_add_stack_description.py +35 -0
  153. zenml/zen_stores/rest_zen_store.py +223 -230
  154. zenml/zen_stores/schemas/__init__.py +2 -2
  155. zenml/zen_stores/schemas/action_schemas.py +15 -8
  156. zenml/zen_stores/schemas/api_key_schemas.py +8 -1
  157. zenml/zen_stores/schemas/artifact_schemas.py +35 -10
  158. zenml/zen_stores/schemas/code_repository_schemas.py +22 -17
  159. zenml/zen_stores/schemas/component_schemas.py +9 -14
  160. zenml/zen_stores/schemas/event_source_schemas.py +15 -8
  161. zenml/zen_stores/schemas/flavor_schemas.py +14 -20
  162. zenml/zen_stores/schemas/model_schemas.py +18 -17
  163. zenml/zen_stores/schemas/pipeline_build_schemas.py +7 -7
  164. zenml/zen_stores/schemas/pipeline_deployment_schemas.py +10 -8
  165. zenml/zen_stores/schemas/pipeline_run_schemas.py +9 -12
  166. zenml/zen_stores/schemas/pipeline_schemas.py +9 -9
  167. zenml/zen_stores/schemas/{workspace_schemas.py → project_schemas.py} +53 -65
  168. zenml/zen_stores/schemas/run_metadata_schemas.py +5 -5
  169. zenml/zen_stores/schemas/run_template_schemas.py +17 -13
  170. zenml/zen_stores/schemas/schedule_schema.py +16 -21
  171. zenml/zen_stores/schemas/secret_schemas.py +15 -25
  172. zenml/zen_stores/schemas/service_connector_schemas.py +8 -17
  173. zenml/zen_stores/schemas/service_schemas.py +7 -8
  174. zenml/zen_stores/schemas/stack_schemas.py +12 -15
  175. zenml/zen_stores/schemas/step_run_schemas.py +14 -15
  176. zenml/zen_stores/schemas/tag_schemas.py +30 -2
  177. zenml/zen_stores/schemas/trigger_schemas.py +15 -8
  178. zenml/zen_stores/schemas/user_schemas.py +12 -2
  179. zenml/zen_stores/schemas/utils.py +16 -0
  180. zenml/zen_stores/secrets_stores/service_connector_secrets_store.py +0 -3
  181. zenml/zen_stores/sql_zen_store.py +2984 -2369
  182. zenml/zen_stores/template_utils.py +1 -1
  183. zenml/zen_stores/zen_store_interface.py +136 -126
  184. {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250314.dist-info}/METADATA +1 -1
  185. {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250314.dist-info}/RECORD +188 -173
  186. zenml/cli/workspace.py +0 -86
  187. zenml/models/v2/core/workspace.py +0 -131
  188. zenml/zen_server/routers/workspaces_endpoints.py +0 -1469
  189. {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250314.dist-info}/LICENSE +0 -0
  190. {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250314.dist-info}/WHEEL +0 -0
  191. {zenml_nightly-0.75.0.dev20250312.dist-info → zenml_nightly-0.75.0.dev20250314.dist-info}/entry_points.txt +0 -0
@@ -23,6 +23,7 @@ from typing import (
23
23
  Dict,
24
24
  List,
25
25
  Optional,
26
+ Sequence,
26
27
  Tuple,
27
28
  Type,
28
29
  TypeVar,
@@ -69,7 +70,6 @@ from zenml.constants import (
69
70
  ENV_ZENML_DISABLE_CLIENT_SERVER_MISMATCH_WARNING,
70
71
  EVENT_SOURCES,
71
72
  FLAVORS,
72
- GET_OR_CREATE,
73
73
  INFO,
74
74
  LOGIN,
75
75
  LOGS,
@@ -80,6 +80,7 @@ from zenml.constants import (
80
80
  PIPELINE_BUILDS,
81
81
  PIPELINE_DEPLOYMENTS,
82
82
  PIPELINES,
83
+ PROJECTS,
83
84
  RUN_METADATA,
84
85
  RUN_TEMPLATES,
85
86
  RUNS,
@@ -102,12 +103,12 @@ from zenml.constants import (
102
103
  STACK_DEPLOYMENT,
103
104
  STACKS,
104
105
  STEPS,
106
+ TAG_RESOURCES,
105
107
  TAGS,
106
108
  TRIGGER_EXECUTIONS,
107
109
  TRIGGERS,
108
110
  USERS,
109
111
  VERSION_1,
110
- WORKSPACES,
111
112
  )
112
113
  from zenml.enums import (
113
114
  APITokenType,
@@ -202,6 +203,10 @@ from zenml.models import (
202
203
  PipelineRunResponse,
203
204
  PipelineRunUpdate,
204
205
  PipelineUpdate,
206
+ ProjectFilter,
207
+ ProjectRequest,
208
+ ProjectResponse,
209
+ ProjectUpdate,
205
210
  RunMetadataRequest,
206
211
  RunTemplateFilter,
207
212
  RunTemplateRequest,
@@ -244,6 +249,8 @@ from zenml.models import (
244
249
  StepRunUpdate,
245
250
  TagFilter,
246
251
  TagRequest,
252
+ TagResourceRequest,
253
+ TagResourceResponse,
247
254
  TagResponse,
248
255
  TagUpdate,
249
256
  TriggerExecutionFilter,
@@ -256,11 +263,6 @@ from zenml.models import (
256
263
  UserRequest,
257
264
  UserResponse,
258
265
  UserUpdate,
259
- WorkspaceFilter,
260
- WorkspaceRequest,
261
- WorkspaceResponse,
262
- WorkspaceScopedRequest,
263
- WorkspaceUpdate,
264
266
  )
265
267
  from zenml.service_connectors.service_connector_registry import (
266
268
  service_connector_registry,
@@ -280,10 +282,6 @@ Json = Union[Dict[str, Any], List[Any], str, int, float, bool, None]
280
282
 
281
283
  AnyRequest = TypeVar("AnyRequest", bound=BaseRequest)
282
284
  AnyResponse = TypeVar("AnyResponse", bound=BaseIdentifiedResponse) # type: ignore[type-arg]
283
- AnyWorkspaceScopedRequest = TypeVar(
284
- "AnyWorkspaceScopedRequest",
285
- bound=WorkspaceScopedRequest,
286
- )
287
285
 
288
286
 
289
287
  class RestZenStoreConfiguration(StoreConfiguration):
@@ -1088,15 +1086,22 @@ class RestZenStore(BaseZenStore):
1088
1086
 
1089
1087
  def prune_artifact_versions(
1090
1088
  self,
1089
+ project_name_or_id: Union[str, UUID],
1091
1090
  only_versions: bool = True,
1092
1091
  ) -> None:
1093
1092
  """Prunes unused artifact versions and their artifacts.
1094
1093
 
1095
1094
  Args:
1095
+ project_name_or_id: The project name or ID to prune artifact
1096
+ versions for.
1096
1097
  only_versions: Only delete artifact versions, keeping artifacts
1097
1098
  """
1098
1099
  self.delete(
1099
- path=ARTIFACT_VERSIONS, params={"only_versions": only_versions}
1100
+ path=ARTIFACT_VERSIONS,
1101
+ params={
1102
+ "only_versions": only_versions,
1103
+ "project_name_or_id": project_name_or_id,
1104
+ },
1100
1105
  )
1101
1106
 
1102
1107
  # ------------------------ Artifact Visualizations ------------------------
@@ -1157,7 +1162,7 @@ class RestZenStore(BaseZenStore):
1157
1162
  Returns:
1158
1163
  The newly created code repository.
1159
1164
  """
1160
- return self._create_workspace_scoped_resource(
1165
+ return self._create_resource(
1161
1166
  resource=code_repository,
1162
1167
  response_model=CodeRepositoryResponse,
1163
1168
  route=CODE_REPOSITORIES,
@@ -1249,7 +1254,7 @@ class RestZenStore(BaseZenStore):
1249
1254
  Returns:
1250
1255
  The created stack component.
1251
1256
  """
1252
- return self._create_workspace_scoped_resource(
1257
+ return self._create_resource(
1253
1258
  resource=component,
1254
1259
  route=STACK_COMPONENTS,
1255
1260
  response_model=ComponentResponse,
@@ -1443,7 +1448,7 @@ class RestZenStore(BaseZenStore):
1443
1448
  # ----------------------------- Pipelines -----------------------------
1444
1449
 
1445
1450
  def create_pipeline(self, pipeline: PipelineRequest) -> PipelineResponse:
1446
- """Creates a new pipeline in a workspace.
1451
+ """Creates a new pipeline.
1447
1452
 
1448
1453
  Args:
1449
1454
  pipeline: The pipeline to create.
@@ -1451,7 +1456,7 @@ class RestZenStore(BaseZenStore):
1451
1456
  Returns:
1452
1457
  The newly created pipeline.
1453
1458
  """
1454
- return self._create_workspace_scoped_resource(
1459
+ return self._create_resource(
1455
1460
  resource=pipeline,
1456
1461
  route=PIPELINES,
1457
1462
  response_model=PipelineResponse,
@@ -1536,7 +1541,7 @@ class RestZenStore(BaseZenStore):
1536
1541
  self,
1537
1542
  build: PipelineBuildRequest,
1538
1543
  ) -> PipelineBuildResponse:
1539
- """Creates a new build in a workspace.
1544
+ """Creates a new build.
1540
1545
 
1541
1546
  Args:
1542
1547
  build: The build to create.
@@ -1544,7 +1549,7 @@ class RestZenStore(BaseZenStore):
1544
1549
  Returns:
1545
1550
  The newly created build.
1546
1551
  """
1547
- return self._create_workspace_scoped_resource(
1552
+ return self._create_resource(
1548
1553
  resource=build,
1549
1554
  route=PIPELINE_BUILDS,
1550
1555
  response_model=PipelineBuildResponse,
@@ -1610,7 +1615,7 @@ class RestZenStore(BaseZenStore):
1610
1615
  self,
1611
1616
  deployment: PipelineDeploymentRequest,
1612
1617
  ) -> PipelineDeploymentResponse:
1613
- """Creates a new deployment in a workspace.
1618
+ """Creates a new deployment.
1614
1619
 
1615
1620
  Args:
1616
1621
  deployment: The deployment to create.
@@ -1618,7 +1623,7 @@ class RestZenStore(BaseZenStore):
1618
1623
  Returns:
1619
1624
  The newly created deployment.
1620
1625
  """
1621
- return self._create_workspace_scoped_resource(
1626
+ return self._create_resource(
1622
1627
  resource=deployment,
1623
1628
  route=PIPELINE_DEPLOYMENTS,
1624
1629
  response_model=PipelineDeploymentResponse,
@@ -1692,7 +1697,7 @@ class RestZenStore(BaseZenStore):
1692
1697
  Returns:
1693
1698
  The newly created template.
1694
1699
  """
1695
- return self._create_workspace_scoped_resource(
1700
+ return self._create_resource(
1696
1701
  resource=template,
1697
1702
  route=RUN_TEMPLATES,
1698
1703
  response_model=RunTemplateResponse,
@@ -1902,30 +1907,34 @@ class RestZenStore(BaseZenStore):
1902
1907
 
1903
1908
  # ----------------------------- Pipeline runs -----------------------------
1904
1909
 
1905
- def create_run(
1910
+ def get_or_create_run(
1906
1911
  self, pipeline_run: PipelineRunRequest
1907
- ) -> PipelineRunResponse:
1908
- """Creates a pipeline run.
1912
+ ) -> Tuple[PipelineRunResponse, bool]:
1913
+ """Gets or creates a pipeline run.
1914
+
1915
+ If a run with the same ID or name already exists, it is returned.
1916
+ Otherwise, a new run is created.
1909
1917
 
1910
1918
  Args:
1911
- pipeline_run: The pipeline run to create.
1919
+ pipeline_run: The pipeline run to get or create.
1912
1920
 
1913
1921
  Returns:
1914
- The created pipeline run.
1922
+ The pipeline run, and a boolean indicating whether the run was
1923
+ created or not.
1915
1924
  """
1916
- return self._create_workspace_scoped_resource(
1925
+ return self._get_or_create_resource(
1917
1926
  resource=pipeline_run,
1918
- response_model=PipelineRunResponse,
1919
1927
  route=RUNS,
1928
+ response_model=PipelineRunResponse,
1920
1929
  )
1921
1930
 
1922
1931
  def get_run(
1923
- self, run_name_or_id: Union[UUID, str], hydrate: bool = True
1932
+ self, run_id: UUID, hydrate: bool = True
1924
1933
  ) -> PipelineRunResponse:
1925
1934
  """Gets a pipeline run.
1926
1935
 
1927
1936
  Args:
1928
- run_name_or_id: The name or ID of the pipeline run to get.
1937
+ run_id: The ID of the pipeline run to get.
1929
1938
  hydrate: Flag deciding whether to hydrate the output model(s)
1930
1939
  by including metadata fields in the response.
1931
1940
 
@@ -1933,7 +1942,7 @@ class RestZenStore(BaseZenStore):
1933
1942
  The pipeline run.
1934
1943
  """
1935
1944
  return self._get_resource(
1936
- resource_id=run_name_or_id,
1945
+ resource_id=run_id,
1937
1946
  route=RUNS,
1938
1947
  response_model=PipelineRunResponse,
1939
1948
  params={"hydrate": hydrate},
@@ -1993,27 +2002,6 @@ class RestZenStore(BaseZenStore):
1993
2002
  route=RUNS,
1994
2003
  )
1995
2004
 
1996
- def get_or_create_run(
1997
- self, pipeline_run: PipelineRunRequest
1998
- ) -> Tuple[PipelineRunResponse, bool]:
1999
- """Gets or creates a pipeline run.
2000
-
2001
- If a run with the same ID or name already exists, it is returned.
2002
- Otherwise, a new run is created.
2003
-
2004
- Args:
2005
- pipeline_run: The pipeline run to get or create.
2006
-
2007
- Returns:
2008
- The pipeline run, and a boolean indicating whether the run was
2009
- created or not.
2010
- """
2011
- return self._get_or_create_workspace_scoped_resource(
2012
- resource=pipeline_run,
2013
- route=RUNS,
2014
- response_model=PipelineRunResponse,
2015
- )
2016
-
2017
2005
  # ----------------------------- Run Metadata -----------------------------
2018
2006
 
2019
2007
  def create_run_metadata(self, run_metadata: RunMetadataRequest) -> None:
@@ -2021,13 +2009,8 @@ class RestZenStore(BaseZenStore):
2021
2009
 
2022
2010
  Args:
2023
2011
  run_metadata: The run metadata to create.
2024
-
2025
- Returns:
2026
- The created run metadata.
2027
2012
  """
2028
- route = f"{WORKSPACES}/{str(run_metadata.workspace)}{RUN_METADATA}"
2029
- self.post(f"{route}", body=run_metadata)
2030
- return None
2013
+ self.post(RUN_METADATA, body=run_metadata)
2031
2014
 
2032
2015
  # ----------------------------- Schedules -----------------------------
2033
2016
 
@@ -2040,7 +2023,7 @@ class RestZenStore(BaseZenStore):
2040
2023
  Returns:
2041
2024
  The newly created schedule.
2042
2025
  """
2043
- return self._create_workspace_scoped_resource(
2026
+ return self._create_resource(
2044
2027
  resource=schedule,
2045
2028
  route=SCHEDULES,
2046
2029
  response_model=ScheduleResponse,
@@ -2071,7 +2054,7 @@ class RestZenStore(BaseZenStore):
2071
2054
  schedule_filter_model: ScheduleFilter,
2072
2055
  hydrate: bool = False,
2073
2056
  ) -> Page[ScheduleResponse]:
2074
- """List all schedules in the workspace.
2057
+ """List all schedules.
2075
2058
 
2076
2059
  Args:
2077
2060
  schedule_filter_model: All filter parameters including pagination
@@ -2129,10 +2112,8 @@ class RestZenStore(BaseZenStore):
2129
2112
  The new secret is also validated against the scoping rules enforced in
2130
2113
  the secrets store:
2131
2114
 
2132
- - only one workspace-scoped secret with the given name can exist
2133
- in the target workspace.
2134
- - only one user-scoped secret with the given name can exist in the
2135
- target workspace for the target user.
2115
+ - only one private secret with the given name can exist.
2116
+ - only one public secret with the given name can exist.
2136
2117
 
2137
2118
  Args:
2138
2119
  secret: The secret to create.
@@ -2140,7 +2121,7 @@ class RestZenStore(BaseZenStore):
2140
2121
  Returns:
2141
2122
  The newly created secret.
2142
2123
  """
2143
- return self._create_workspace_scoped_resource(
2124
+ return self._create_resource(
2144
2125
  resource=secret,
2145
2126
  route=SECRETS,
2146
2127
  response_model=SecretResponse,
@@ -2207,10 +2188,8 @@ class RestZenStore(BaseZenStore):
2207
2188
  If the update includes a change of name or scope, the scoping rules
2208
2189
  enforced in the secrets store are used to validate the update:
2209
2190
 
2210
- - only one workspace-scoped secret with the given name can exist
2211
- in the target workspace.
2212
- - only one user-scoped secret with the given name can exist in the
2213
- target workspace for the target user.
2191
+ - only one private secret with the given name can exist.
2192
+ - only one public secret with the given name can exist.
2214
2193
 
2215
2194
  Args:
2216
2195
  secret_id: The ID of the secret to be updated.
@@ -2399,7 +2378,7 @@ class RestZenStore(BaseZenStore):
2399
2378
  Returns:
2400
2379
  The newly created service connector.
2401
2380
  """
2402
- connector_model = self._create_workspace_scoped_resource(
2381
+ connector_model = self._create_resource(
2403
2382
  resource=service_connector,
2404
2383
  route=SERVICE_CONNECTORS,
2405
2384
  response_model=ServiceConnectorResponse,
@@ -2661,33 +2640,21 @@ class RestZenStore(BaseZenStore):
2661
2640
 
2662
2641
  def list_service_connector_resources(
2663
2642
  self,
2664
- workspace_name_or_id: Union[str, UUID],
2665
- connector_type: Optional[str] = None,
2666
- resource_type: Optional[str] = None,
2667
- resource_id: Optional[str] = None,
2643
+ filter_model: ServiceConnectorFilter,
2668
2644
  ) -> List[ServiceConnectorResourcesModel]:
2669
2645
  """List resources that can be accessed by service connectors.
2670
2646
 
2671
2647
  Args:
2672
- workspace_name_or_id: The name or ID of the workspace to scope to.
2673
- connector_type: The type of service connector to scope to.
2674
- resource_type: The type of resource to scope to.
2675
- resource_id: The ID of the resource to scope to.
2648
+ filter_model: The filter model to use when fetching service
2649
+ connectors.
2676
2650
 
2677
2651
  Returns:
2678
2652
  The matching list of resources that available service
2679
2653
  connectors have access to.
2680
2654
  """
2681
- params = {}
2682
- if connector_type:
2683
- params["connector_type"] = connector_type
2684
- if resource_type:
2685
- params["resource_type"] = resource_type
2686
- if resource_id:
2687
- params["resource_id"] = resource_id
2688
2655
  response_body = self.get(
2689
- f"{WORKSPACES}/{workspace_name_or_id}{SERVICE_CONNECTORS}{SERVICE_CONNECTOR_RESOURCES}",
2690
- params=params,
2656
+ SERVICE_CONNECTOR_RESOURCES,
2657
+ params=filter_model.model_dump(exclude_none=True),
2691
2658
  timeout=max(
2692
2659
  self.config.http_timeout,
2693
2660
  SERVICE_CONNECTOR_VERIFY_REQUEST_TIMEOUT,
@@ -2724,12 +2691,12 @@ class RestZenStore(BaseZenStore):
2724
2691
 
2725
2692
  try:
2726
2693
  local_resources = connector_instance.verify(
2727
- resource_type=resource_type,
2728
- resource_id=resource_id,
2694
+ resource_type=filter_model.resource_type,
2695
+ resource_id=filter_model.resource_id,
2729
2696
  )
2730
2697
  except (ValueError, AuthorizationException) as e:
2731
2698
  logger.error(
2732
- f"Failed to fetch {resource_type or 'available'} "
2699
+ f"Failed to fetch {filter_model.resource_type or 'available'} "
2733
2700
  f"resources from service connector {connector.name}/"
2734
2701
  f"{connector.id}: {e}"
2735
2702
  )
@@ -2858,12 +2825,10 @@ class RestZenStore(BaseZenStore):
2858
2825
  Returns:
2859
2826
  The registered stack.
2860
2827
  """
2861
- assert stack.workspace is not None
2862
-
2863
2828
  return self._create_resource(
2864
2829
  resource=stack,
2865
2830
  response_model=StackResponse,
2866
- route=f"{WORKSPACES}/{str(stack.workspace)}{STACKS}",
2831
+ route=STACKS,
2867
2832
  )
2868
2833
 
2869
2834
  def get_stack(self, stack_id: UUID, hydrate: bool = True) -> StackResponse:
@@ -3370,96 +3335,94 @@ class RestZenStore(BaseZenStore):
3370
3335
  route=USERS,
3371
3336
  )
3372
3337
 
3373
- # ----------------------------- Workspaces -----------------------------
3338
+ # ----------------------------- Projects -----------------------------
3374
3339
 
3375
- def create_workspace(
3376
- self, workspace: WorkspaceRequest
3377
- ) -> WorkspaceResponse:
3378
- """Creates a new workspace.
3340
+ def create_project(self, project: ProjectRequest) -> ProjectResponse:
3341
+ """Creates a new project.
3379
3342
 
3380
3343
  Args:
3381
- workspace: The workspace to create.
3344
+ project: The project to create.
3382
3345
 
3383
3346
  Returns:
3384
- The newly created workspace.
3347
+ The newly created project.
3385
3348
  """
3386
3349
  return self._create_resource(
3387
- resource=workspace,
3388
- route=WORKSPACES,
3389
- response_model=WorkspaceResponse,
3350
+ resource=project,
3351
+ route=PROJECTS,
3352
+ response_model=ProjectResponse,
3390
3353
  )
3391
3354
 
3392
- def get_workspace(
3393
- self, workspace_name_or_id: Union[UUID, str], hydrate: bool = True
3394
- ) -> WorkspaceResponse:
3395
- """Get an existing workspace by name or ID.
3355
+ def get_project(
3356
+ self, project_name_or_id: Union[UUID, str], hydrate: bool = True
3357
+ ) -> ProjectResponse:
3358
+ """Get an existing project by name or ID.
3396
3359
 
3397
3360
  Args:
3398
- workspace_name_or_id: Name or ID of the workspace to get.
3361
+ project_name_or_id: Name or ID of the project to get.
3399
3362
  hydrate: Flag deciding whether to hydrate the output model(s)
3400
3363
  by including metadata fields in the response.
3401
3364
 
3402
3365
  Returns:
3403
- The requested workspace.
3366
+ The requested project.
3404
3367
  """
3405
3368
  return self._get_resource(
3406
- resource_id=workspace_name_or_id,
3407
- route=WORKSPACES,
3408
- response_model=WorkspaceResponse,
3369
+ resource_id=project_name_or_id,
3370
+ route=PROJECTS,
3371
+ response_model=ProjectResponse,
3409
3372
  params={"hydrate": hydrate},
3410
3373
  )
3411
3374
 
3412
- def list_workspaces(
3375
+ def list_projects(
3413
3376
  self,
3414
- workspace_filter_model: WorkspaceFilter,
3377
+ project_filter_model: ProjectFilter,
3415
3378
  hydrate: bool = False,
3416
- ) -> Page[WorkspaceResponse]:
3417
- """List all workspace matching the given filter criteria.
3379
+ ) -> Page[ProjectResponse]:
3380
+ """List all projects matching the given filter criteria.
3418
3381
 
3419
3382
  Args:
3420
- workspace_filter_model: All filter parameters including pagination
3383
+ project_filter_model: All filter parameters including pagination
3421
3384
  params.
3422
3385
  hydrate: Flag deciding whether to hydrate the output model(s)
3423
3386
  by including metadata fields in the response.
3424
3387
 
3425
3388
  Returns:
3426
- A list of all workspace matching the filter criteria.
3389
+ A list of all projects matching the filter criteria.
3427
3390
  """
3428
3391
  return self._list_paginated_resources(
3429
- route=WORKSPACES,
3430
- response_model=WorkspaceResponse,
3431
- filter_model=workspace_filter_model,
3392
+ route=PROJECTS,
3393
+ response_model=ProjectResponse,
3394
+ filter_model=project_filter_model,
3432
3395
  params={"hydrate": hydrate},
3433
3396
  )
3434
3397
 
3435
- def update_workspace(
3436
- self, workspace_id: UUID, workspace_update: WorkspaceUpdate
3437
- ) -> WorkspaceResponse:
3438
- """Update an existing workspace.
3398
+ def update_project(
3399
+ self, project_id: UUID, project_update: ProjectUpdate
3400
+ ) -> ProjectResponse:
3401
+ """Update an existing project.
3439
3402
 
3440
3403
  Args:
3441
- workspace_id: The ID of the workspace to be updated.
3442
- workspace_update: The update to be applied to the workspace.
3404
+ project_id: The ID of the project to be updated.
3405
+ project_update: The update to be applied to the project.
3443
3406
 
3444
3407
  Returns:
3445
- The updated workspace.
3408
+ The updated project.
3446
3409
  """
3447
3410
  return self._update_resource(
3448
- resource_id=workspace_id,
3449
- resource_update=workspace_update,
3450
- route=WORKSPACES,
3451
- response_model=WorkspaceResponse,
3411
+ resource_id=project_id,
3412
+ resource_update=project_update,
3413
+ route=PROJECTS,
3414
+ response_model=ProjectResponse,
3452
3415
  )
3453
3416
 
3454
- def delete_workspace(self, workspace_name_or_id: Union[str, UUID]) -> None:
3455
- """Deletes a workspace.
3417
+ def delete_project(self, project_name_or_id: Union[str, UUID]) -> None:
3418
+ """Deletes a project.
3456
3419
 
3457
3420
  Args:
3458
- workspace_name_or_id: Name or ID of the workspace to delete.
3421
+ project_name_or_id: Name or ID of the project to delete.
3459
3422
  """
3460
3423
  self._delete_resource(
3461
- resource_id=workspace_name_or_id,
3462
- route=WORKSPACES,
3424
+ resource_id=project_name_or_id,
3425
+ route=PROJECTS,
3463
3426
  )
3464
3427
 
3465
3428
  # --------------------------- Model ---------------------------
@@ -3473,19 +3436,19 @@ class RestZenStore(BaseZenStore):
3473
3436
  Returns:
3474
3437
  The newly created model.
3475
3438
  """
3476
- return self._create_workspace_scoped_resource(
3439
+ return self._create_resource(
3477
3440
  resource=model,
3478
3441
  response_model=ModelResponse,
3479
3442
  route=MODELS,
3480
3443
  )
3481
3444
 
3482
- def delete_model(self, model_name_or_id: Union[str, UUID]) -> None:
3445
+ def delete_model(self, model_id: UUID) -> None:
3483
3446
  """Deletes a model.
3484
3447
 
3485
3448
  Args:
3486
- model_name_or_id: name or id of the model to be deleted.
3449
+ model_id: id of the model to be deleted.
3487
3450
  """
3488
- self._delete_resource(resource_id=model_name_or_id, route=MODELS)
3451
+ self._delete_resource(resource_id=model_id, route=MODELS)
3489
3452
 
3490
3453
  def update_model(
3491
3454
  self,
@@ -3508,13 +3471,11 @@ class RestZenStore(BaseZenStore):
3508
3471
  response_model=ModelResponse,
3509
3472
  )
3510
3473
 
3511
- def get_model(
3512
- self, model_name_or_id: Union[str, UUID], hydrate: bool = True
3513
- ) -> ModelResponse:
3474
+ def get_model(self, model_id: UUID, hydrate: bool = True) -> ModelResponse:
3514
3475
  """Get an existing model.
3515
3476
 
3516
3477
  Args:
3517
- model_name_or_id: name or id of the model to be retrieved.
3478
+ model_id: id of the model to be retrieved.
3518
3479
  hydrate: Flag deciding whether to hydrate the output model(s)
3519
3480
  by including metadata fields in the response.
3520
3481
 
@@ -3522,7 +3483,7 @@ class RestZenStore(BaseZenStore):
3522
3483
  The model of interest.
3523
3484
  """
3524
3485
  return self._get_resource(
3525
- resource_id=model_name_or_id,
3486
+ resource_id=model_id,
3526
3487
  route=MODELS,
3527
3488
  response_model=ModelResponse,
3528
3489
  params={"hydrate": hydrate},
@@ -3564,10 +3525,10 @@ class RestZenStore(BaseZenStore):
3564
3525
  Returns:
3565
3526
  The newly created model version.
3566
3527
  """
3567
- return self._create_workspace_scoped_resource(
3528
+ return self._create_resource(
3568
3529
  resource=model_version,
3569
3530
  response_model=ModelVersionResponse,
3570
- route=f"{MODELS}/{model_version.model}{MODEL_VERSIONS}",
3531
+ route=MODEL_VERSIONS,
3571
3532
  )
3572
3533
 
3573
3534
  def delete_model_version(
@@ -3581,7 +3542,7 @@ class RestZenStore(BaseZenStore):
3581
3542
  """
3582
3543
  self._delete_resource(
3583
3544
  resource_id=model_version_id,
3584
- route=f"{MODEL_VERSIONS}",
3545
+ route=MODEL_VERSIONS,
3585
3546
  )
3586
3547
 
3587
3548
  def get_model_version(
@@ -3608,14 +3569,11 @@ class RestZenStore(BaseZenStore):
3608
3569
  def list_model_versions(
3609
3570
  self,
3610
3571
  model_version_filter_model: ModelVersionFilter,
3611
- model_name_or_id: Optional[Union[str, UUID]] = None,
3612
3572
  hydrate: bool = False,
3613
3573
  ) -> Page[ModelVersionResponse]:
3614
3574
  """Get all model versions by filter.
3615
3575
 
3616
3576
  Args:
3617
- model_name_or_id: name or id of the model containing the model
3618
- versions.
3619
3577
  model_version_filter_model: All filter parameters including
3620
3578
  pagination params.
3621
3579
  hydrate: Flag deciding whether to hydrate the output model(s)
@@ -3624,20 +3582,12 @@ class RestZenStore(BaseZenStore):
3624
3582
  Returns:
3625
3583
  A page of all model versions.
3626
3584
  """
3627
- if model_name_or_id:
3628
- return self._list_paginated_resources(
3629
- route=f"{MODELS}/{model_name_or_id}{MODEL_VERSIONS}",
3630
- response_model=ModelVersionResponse,
3631
- filter_model=model_version_filter_model,
3632
- params={"hydrate": hydrate},
3633
- )
3634
- else:
3635
- return self._list_paginated_resources(
3636
- route=MODEL_VERSIONS,
3637
- response_model=ModelVersionResponse,
3638
- filter_model=model_version_filter_model,
3639
- params={"hydrate": hydrate},
3640
- )
3585
+ return self._list_paginated_resources(
3586
+ route=MODEL_VERSIONS,
3587
+ response_model=ModelVersionResponse,
3588
+ filter_model=model_version_filter_model,
3589
+ params={"hydrate": hydrate},
3590
+ )
3641
3591
 
3642
3592
  def update_model_version(
3643
3593
  self,
@@ -3944,10 +3894,15 @@ class RestZenStore(BaseZenStore):
3944
3894
  Args:
3945
3895
  tag_name_or_id: name or id of the tag to delete.
3946
3896
  """
3947
- self._delete_resource(resource_id=tag_name_or_id, route=TAGS)
3897
+ self._delete_resource(
3898
+ resource_id=tag_name_or_id,
3899
+ route=TAGS,
3900
+ )
3948
3901
 
3949
3902
  def get_tag(
3950
- self, tag_name_or_id: Union[str, UUID], hydrate: bool = True
3903
+ self,
3904
+ tag_name_or_id: Union[str, UUID],
3905
+ hydrate: bool = True,
3951
3906
  ) -> TagResponse:
3952
3907
  """Get an existing tag.
3953
3908
 
@@ -3959,11 +3914,13 @@ class RestZenStore(BaseZenStore):
3959
3914
  Returns:
3960
3915
  The tag of interest.
3961
3916
  """
3917
+ params: Dict[str, Any] = {"hydrate": hydrate}
3918
+
3962
3919
  return self._get_resource(
3963
3920
  resource_id=tag_name_or_id,
3964
3921
  route=TAGS,
3965
3922
  response_model=TagResponse,
3966
- params={"hydrate": hydrate},
3923
+ params=params,
3967
3924
  )
3968
3925
 
3969
3926
  def list_tags(
@@ -4010,6 +3967,67 @@ class RestZenStore(BaseZenStore):
4010
3967
  response_model=TagResponse,
4011
3968
  )
4012
3969
 
3970
+ # ---------------------------- Tag Resource ----------------------------
3971
+
3972
+ def create_tag_resource(
3973
+ self,
3974
+ tag_resource: TagResourceRequest,
3975
+ ) -> TagResourceResponse:
3976
+ """Create a new tag resource.
3977
+
3978
+ Args:
3979
+ tag_resource: The tag resource to be created.
3980
+
3981
+ Returns:
3982
+ The newly created tag resource.
3983
+ """
3984
+ return self._create_resource(
3985
+ resource=tag_resource,
3986
+ response_model=TagResourceResponse,
3987
+ route=TAG_RESOURCES,
3988
+ )
3989
+
3990
+ def batch_create_tag_resource(
3991
+ self, tag_resources: List[TagResourceRequest]
3992
+ ) -> List[TagResourceResponse]:
3993
+ """Create a batch of tag resource relationships.
3994
+
3995
+ Args:
3996
+ tag_resources: The tag resource relationships to be created.
3997
+
3998
+ Returns:
3999
+ The newly created tag resource relationships.
4000
+ """
4001
+ return self._batch_create_resources(
4002
+ resources=tag_resources,
4003
+ response_model=TagResourceResponse,
4004
+ route=TAG_RESOURCES,
4005
+ )
4006
+
4007
+ def delete_tag_resource(
4008
+ self,
4009
+ tag_resource: TagResourceRequest,
4010
+ ) -> None:
4011
+ """Delete a tag resource.
4012
+
4013
+ Args:
4014
+ tag_resource: The tag resource relationship to delete.
4015
+ """
4016
+ self.delete(path=TAG_RESOURCES, body=tag_resource)
4017
+
4018
+ def batch_delete_tag_resource(
4019
+ self, tag_resources: List[TagResourceRequest]
4020
+ ) -> None:
4021
+ """Delete a batch of tag resources.
4022
+
4023
+ Args:
4024
+ tag_resources: The tag resource relationships to be deleted.
4025
+ """
4026
+ self._batch_delete_resources(
4027
+ resources=tag_resources,
4028
+ route=TAG_RESOURCES,
4029
+ )
4030
+
4013
4031
  # =======================
4014
4032
  # Internal helper methods
4015
4033
  # =======================
@@ -4185,7 +4203,6 @@ class RestZenStore(BaseZenStore):
4185
4203
  # Retries are triggered for idempotent HTTP methods (GET, HEAD, PUT,
4186
4204
  # OPTIONS and DELETE) on specific HTTP status codes:
4187
4205
  #
4188
- # 500: Internal Server Error.
4189
4206
  # 502: Bad Gateway.
4190
4207
  # 503: Service Unavailable.
4191
4208
  # 504: Gateway Timeout.
@@ -4212,7 +4229,6 @@ class RestZenStore(BaseZenStore):
4212
4229
  status_forcelist=[
4213
4230
  408, # Request Timeout
4214
4231
  429, # Too Many Requests
4215
- 500, # Internal Server Error
4216
4232
  502, # Bad Gateway
4217
4233
  503, # Service Unavailable
4218
4234
  504, # Gateway Timeout
@@ -4458,6 +4474,7 @@ class RestZenStore(BaseZenStore):
4458
4474
  def delete(
4459
4475
  self,
4460
4476
  path: str,
4477
+ body: Optional[BaseModel] = None,
4461
4478
  params: Optional[Dict[str, Any]] = None,
4462
4479
  timeout: Optional[int] = None,
4463
4480
  **kwargs: Any,
@@ -4466,6 +4483,7 @@ class RestZenStore(BaseZenStore):
4466
4483
 
4467
4484
  Args:
4468
4485
  path: The path to the endpoint.
4486
+ body: The body to send.
4469
4487
  params: The query parameters to pass to the endpoint.
4470
4488
  timeout: The request timeout in seconds.
4471
4489
  kwargs: Additional keyword arguments to pass to the request.
@@ -4477,6 +4495,7 @@ class RestZenStore(BaseZenStore):
4477
4495
  return self._request(
4478
4496
  "DELETE",
4479
4497
  self.url + API + VERSION_1 + path,
4498
+ json=body.model_dump(mode="json") if body else None,
4480
4499
  params=params,
4481
4500
  timeout=timeout,
4482
4501
  **kwargs,
@@ -4602,32 +4621,6 @@ class RestZenStore(BaseZenStore):
4602
4621
  for model_data in response
4603
4622
  ]
4604
4623
 
4605
- def _create_workspace_scoped_resource(
4606
- self,
4607
- resource: AnyWorkspaceScopedRequest,
4608
- response_model: Type[AnyResponse],
4609
- route: str,
4610
- params: Optional[Dict[str, Any]] = None,
4611
- ) -> AnyResponse:
4612
- """Create a new workspace scoped resource.
4613
-
4614
- Args:
4615
- resource: The resource to create.
4616
- route: The resource REST API route to use.
4617
- response_model: Optional model to use to deserialize the response
4618
- body. If not provided, the resource class itself will be used.
4619
- params: Optional query parameters to pass to the endpoint.
4620
-
4621
- Returns:
4622
- The created resource.
4623
- """
4624
- return self._create_resource(
4625
- resource=resource,
4626
- response_model=response_model,
4627
- route=f"{WORKSPACES}/{str(resource.workspace)}{route}",
4628
- params=params,
4629
- )
4630
-
4631
4624
  def _get_or_create_resource(
4632
4625
  self,
4633
4626
  resource: AnyRequest,
@@ -4654,57 +4647,30 @@ class RestZenStore(BaseZenStore):
4654
4647
  a boolean indicating whether the resource was created or not.
4655
4648
  """
4656
4649
  response_body = self.post(
4657
- f"{route}{GET_OR_CREATE}",
4650
+ route,
4658
4651
  body=resource,
4659
4652
  params=params,
4660
4653
  )
4661
4654
  if not isinstance(response_body, list):
4662
4655
  raise ValueError(
4663
- f"Expected a list response from the {route}{GET_OR_CREATE} "
4656
+ f"Expected a list response from the {route} "
4664
4657
  f"endpoint but got {type(response_body)} instead."
4665
4658
  )
4666
4659
  if len(response_body) != 2:
4667
4660
  raise ValueError(
4668
4661
  f"Expected a list response with 2 elements from the "
4669
- f"{route}{GET_OR_CREATE} endpoint but got {len(response_body)} "
4662
+ f"{route} endpoint but got {len(response_body)} "
4670
4663
  f"elements instead."
4671
4664
  )
4672
4665
  model_json, was_created = response_body
4673
4666
  if not isinstance(was_created, bool):
4674
4667
  raise ValueError(
4675
4668
  f"Expected a boolean as the second element of the list "
4676
- f"response from the {route}{GET_OR_CREATE} endpoint but got "
4669
+ f"response from the {route} endpoint but got "
4677
4670
  f"{type(was_created)} instead."
4678
4671
  )
4679
4672
  return response_model.model_validate(model_json), was_created
4680
4673
 
4681
- def _get_or_create_workspace_scoped_resource(
4682
- self,
4683
- resource: AnyWorkspaceScopedRequest,
4684
- response_model: Type[AnyResponse],
4685
- route: str,
4686
- params: Optional[Dict[str, Any]] = None,
4687
- ) -> Tuple[AnyResponse, bool]:
4688
- """Get or create a workspace scoped resource.
4689
-
4690
- Args:
4691
- resource: The resource to get or create.
4692
- route: The resource REST API route to use.
4693
- response_model: Optional model to use to deserialize the response
4694
- body. If not provided, the resource class itself will be used.
4695
- params: Optional query parameters to pass to the endpoint.
4696
-
4697
- Returns:
4698
- The created resource, and a boolean indicating whether the resource
4699
- was created or not.
4700
- """
4701
- return self._get_or_create_resource(
4702
- resource=resource,
4703
- response_model=response_model,
4704
- route=f"{WORKSPACES}/{str(resource.workspace)}{route}",
4705
- params=params,
4706
- )
4707
-
4708
4674
  def _get_resource(
4709
4675
  self,
4710
4676
  resource_id: Union[str, int, UUID],
@@ -4820,12 +4786,39 @@ class RestZenStore(BaseZenStore):
4820
4786
  return response_model.model_validate(response_body)
4821
4787
 
4822
4788
  def _delete_resource(
4823
- self, resource_id: Union[str, UUID], route: str
4789
+ self,
4790
+ resource_id: Union[str, UUID],
4791
+ route: str,
4792
+ params: Optional[Dict[str, Any]] = None,
4824
4793
  ) -> None:
4825
4794
  """Delete a resource.
4826
4795
 
4827
4796
  Args:
4828
4797
  resource_id: The ID of the resource to delete.
4829
4798
  route: The resource REST API route to use.
4799
+ params: Optional query parameters to pass to the endpoint.
4800
+ """
4801
+ self.delete(f"{route}/{str(resource_id)}", params=params)
4802
+
4803
+ def _batch_delete_resources(
4804
+ self,
4805
+ resources: Sequence[BaseModel],
4806
+ route: str,
4807
+ params: Optional[Dict[str, Any]] = None,
4808
+ ) -> None:
4809
+ """Delete a batch of resources.
4810
+
4811
+ Args:
4812
+ resources: The resources to delete.
4813
+ route: The resource REST route to use.
4814
+ params: Optional query parameters to pass to the endpoint.
4830
4815
  """
4831
- self.delete(f"{route}/{str(resource_id)}")
4816
+ json_data = [
4817
+ resource.model_dump(mode="json") for resource in resources
4818
+ ]
4819
+ self._request(
4820
+ "DELETE",
4821
+ self.url + API + VERSION_1 + route + BATCH,
4822
+ json=json_data,
4823
+ params=params,
4824
+ )