zenml-nightly 0.75.0.dev20250311__py3-none-any.whl → 0.75.0.dev20250313__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 (163) hide show
  1. zenml/VERSION +1 -1
  2. zenml/__init__.py +2 -0
  3. zenml/analytics/context.py +7 -0
  4. zenml/artifacts/utils.py +0 -2
  5. zenml/cli/login.py +6 -0
  6. zenml/cli/model.py +7 -15
  7. zenml/cli/secret.py +47 -44
  8. zenml/cli/service_connectors.py +0 -1
  9. zenml/cli/stack.py +0 -1
  10. zenml/cli/tag.py +3 -5
  11. zenml/cli/utils.py +25 -23
  12. zenml/cli/workspace.py +79 -5
  13. zenml/client.py +618 -348
  14. zenml/config/global_config.py +16 -3
  15. zenml/config/pipeline_configurations.py +3 -2
  16. zenml/config/pipeline_run_configuration.py +2 -1
  17. zenml/config/secret_reference_mixin.py +1 -1
  18. zenml/constants.py +1 -3
  19. zenml/enums.py +0 -7
  20. zenml/event_hub/event_hub.py +3 -1
  21. zenml/exceptions.py +0 -24
  22. zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +5 -3
  23. zenml/integrations/bitbucket/plugins/event_sources/bitbucket_webhook_event_source.py +1 -4
  24. zenml/integrations/github/plugins/event_sources/github_webhook_event_source.py +1 -4
  25. zenml/integrations/mlflow/steps/mlflow_registry.py +1 -1
  26. zenml/integrations/seldon/model_deployers/seldon_model_deployer.py +1 -1
  27. zenml/integrations/wandb/flavors/wandb_experiment_tracker_flavor.py +3 -3
  28. zenml/model/model.py +8 -8
  29. zenml/models/__init__.py +18 -1
  30. zenml/models/v2/base/base.py +0 -5
  31. zenml/models/v2/base/filter.py +1 -1
  32. zenml/models/v2/base/scoped.py +104 -121
  33. zenml/models/v2/core/api_key.py +1 -1
  34. zenml/models/v2/core/artifact.py +31 -18
  35. zenml/models/v2/core/artifact_version.py +42 -25
  36. zenml/models/v2/core/component.py +22 -33
  37. zenml/models/v2/core/device.py +3 -2
  38. zenml/models/v2/core/event_source.py +2 -2
  39. zenml/models/v2/core/flavor.py +19 -47
  40. zenml/models/v2/core/logs.py +1 -2
  41. zenml/models/v2/core/model.py +7 -4
  42. zenml/models/v2/core/model_version.py +36 -27
  43. zenml/models/v2/core/pipeline.py +1 -1
  44. zenml/models/v2/core/pipeline_build.py +18 -0
  45. zenml/models/v2/core/pipeline_run.py +5 -13
  46. zenml/models/v2/core/run_template.py +1 -2
  47. zenml/models/v2/core/schedule.py +0 -9
  48. zenml/models/v2/core/secret.py +93 -127
  49. zenml/models/v2/core/server_settings.py +2 -2
  50. zenml/models/v2/core/service.py +43 -12
  51. zenml/models/v2/core/service_connector.py +14 -16
  52. zenml/models/v2/core/stack.py +24 -26
  53. zenml/models/v2/core/step_run.py +3 -15
  54. zenml/models/v2/core/tag.py +41 -15
  55. zenml/models/v2/core/user.py +19 -2
  56. zenml/models/v2/misc/statistics.py +45 -0
  57. zenml/models/v2/misc/tag.py +27 -0
  58. zenml/orchestrators/cache_utils.py +1 -1
  59. zenml/orchestrators/input_utils.py +1 -0
  60. zenml/orchestrators/step_launcher.py +0 -1
  61. zenml/orchestrators/step_run_utils.py +0 -2
  62. zenml/orchestrators/step_runner.py +10 -1
  63. zenml/pipelines/build_utils.py +4 -2
  64. zenml/pipelines/pipeline_decorator.py +3 -2
  65. zenml/pipelines/pipeline_definition.py +4 -5
  66. zenml/pipelines/run_utils.py +3 -3
  67. zenml/service_connectors/service_connector.py +0 -7
  68. zenml/service_connectors/service_connector_utils.py +0 -1
  69. zenml/stack/authentication_mixin.py +1 -1
  70. zenml/stack/flavor.py +3 -14
  71. zenml/stack/stack_component.py +1 -5
  72. zenml/steps/step_context.py +19 -0
  73. zenml/utils/string_utils.py +1 -1
  74. zenml/utils/tag_utils.py +642 -0
  75. zenml/zen_server/cloud_utils.py +21 -0
  76. zenml/zen_server/exceptions.py +0 -6
  77. zenml/zen_server/rbac/endpoint_utils.py +134 -46
  78. zenml/zen_server/rbac/models.py +65 -3
  79. zenml/zen_server/rbac/rbac_interface.py +9 -0
  80. zenml/zen_server/rbac/rbac_sql_zen_store.py +15 -7
  81. zenml/zen_server/rbac/utils.py +156 -29
  82. zenml/zen_server/rbac/zenml_cloud_rbac.py +43 -11
  83. zenml/zen_server/routers/actions_endpoints.py +3 -5
  84. zenml/zen_server/routers/artifact_endpoint.py +0 -5
  85. zenml/zen_server/routers/artifact_version_endpoints.py +15 -9
  86. zenml/zen_server/routers/auth_endpoints.py +22 -7
  87. zenml/zen_server/routers/code_repositories_endpoints.py +56 -3
  88. zenml/zen_server/routers/devices_endpoints.py +0 -4
  89. zenml/zen_server/routers/event_source_endpoints.py +0 -5
  90. zenml/zen_server/routers/flavors_endpoints.py +0 -5
  91. zenml/zen_server/routers/logs_endpoints.py +0 -1
  92. zenml/zen_server/routers/model_versions_endpoints.py +102 -23
  93. zenml/zen_server/routers/models_endpoints.py +51 -68
  94. zenml/zen_server/routers/pipeline_builds_endpoints.py +58 -4
  95. zenml/zen_server/routers/pipeline_deployments_endpoints.py +58 -4
  96. zenml/zen_server/routers/pipelines_endpoints.py +73 -4
  97. zenml/zen_server/routers/plugin_endpoints.py +0 -1
  98. zenml/zen_server/routers/run_metadata_endpoints.py +99 -0
  99. zenml/zen_server/routers/run_templates_endpoints.py +66 -3
  100. zenml/zen_server/routers/runs_endpoints.py +60 -8
  101. zenml/zen_server/routers/schedule_endpoints.py +69 -6
  102. zenml/zen_server/routers/secrets_endpoints.py +40 -4
  103. zenml/zen_server/routers/server_endpoints.py +53 -1
  104. zenml/zen_server/routers/service_accounts_endpoints.py +14 -15
  105. zenml/zen_server/routers/service_connectors_endpoints.py +96 -14
  106. zenml/zen_server/routers/service_endpoints.py +20 -7
  107. zenml/zen_server/routers/stack_components_endpoints.py +68 -7
  108. zenml/zen_server/routers/stacks_endpoints.py +98 -7
  109. zenml/zen_server/routers/steps_endpoints.py +17 -11
  110. zenml/zen_server/routers/tag_resource_endpoints.py +115 -0
  111. zenml/zen_server/routers/tags_endpoints.py +6 -17
  112. zenml/zen_server/routers/triggers_endpoints.py +5 -8
  113. zenml/zen_server/routers/users_endpoints.py +47 -12
  114. zenml/zen_server/routers/workspaces_endpoints.py +56 -1285
  115. zenml/zen_server/template_execution/utils.py +5 -4
  116. zenml/zen_server/utils.py +21 -0
  117. zenml/zen_server/zen_server_api.py +4 -0
  118. zenml/zen_stores/base_zen_store.py +29 -44
  119. zenml/zen_stores/migrations/versions/0392807467dc_add_build_duration.py +34 -0
  120. zenml/zen_stores/migrations/versions/1cb6477f72d6_move_artifact_save_type.py +20 -10
  121. zenml/zen_stores/migrations/versions/1f9d1cd00b90_add_unique_name_constraints.py +231 -0
  122. zenml/zen_stores/migrations/versions/288f4fb6e112_make_tags_user_scoped.py +74 -0
  123. zenml/zen_stores/migrations/versions/2e695a26fe7a_add_user_default_workspace.py +45 -0
  124. zenml/zen_stores/migrations/versions/3b1776345020_remove_workspace_from_globals.py +81 -0
  125. zenml/zen_stores/migrations/versions/41b28cae31ce_make_artifacts_workspace_scoped.py +136 -0
  126. zenml/zen_stores/migrations/versions/9e7bf0970266_adding_exclusive_attribute_to_tags.py +47 -0
  127. zenml/zen_stores/migrations/versions/b557b2871693_update_step_run_input_types.py +8 -4
  128. zenml/zen_stores/migrations/versions/cc269488e5a9_separate_run_metadata.py +12 -6
  129. zenml/zen_stores/migrations/versions/f1d723fd723b_add_secret_private_attr.py +61 -0
  130. zenml/zen_stores/migrations/versions/f76a368a25a5_add_stack_description.py +35 -0
  131. zenml/zen_stores/rest_zen_store.py +172 -171
  132. zenml/zen_stores/schemas/action_schemas.py +8 -1
  133. zenml/zen_stores/schemas/api_key_schemas.py +8 -1
  134. zenml/zen_stores/schemas/artifact_schemas.py +28 -1
  135. zenml/zen_stores/schemas/code_repository_schemas.py +8 -1
  136. zenml/zen_stores/schemas/component_schemas.py +9 -14
  137. zenml/zen_stores/schemas/event_source_schemas.py +8 -1
  138. zenml/zen_stores/schemas/flavor_schemas.py +14 -20
  139. zenml/zen_stores/schemas/model_schemas.py +3 -0
  140. zenml/zen_stores/schemas/pipeline_build_schemas.py +4 -0
  141. zenml/zen_stores/schemas/pipeline_deployment_schemas.py +3 -1
  142. zenml/zen_stores/schemas/pipeline_run_schemas.py +0 -3
  143. zenml/zen_stores/schemas/run_template_schemas.py +8 -4
  144. zenml/zen_stores/schemas/schedule_schema.py +9 -14
  145. zenml/zen_stores/schemas/secret_schemas.py +15 -25
  146. zenml/zen_stores/schemas/service_connector_schemas.py +8 -17
  147. zenml/zen_stores/schemas/service_schemas.py +0 -1
  148. zenml/zen_stores/schemas/stack_schemas.py +12 -15
  149. zenml/zen_stores/schemas/step_run_schemas.py +7 -8
  150. zenml/zen_stores/schemas/tag_schemas.py +30 -2
  151. zenml/zen_stores/schemas/trigger_schemas.py +8 -1
  152. zenml/zen_stores/schemas/user_schemas.py +24 -2
  153. zenml/zen_stores/schemas/utils.py +16 -0
  154. zenml/zen_stores/schemas/workspace_schemas.py +7 -25
  155. zenml/zen_stores/secrets_stores/service_connector_secrets_store.py +0 -3
  156. zenml/zen_stores/sql_zen_store.py +2905 -2280
  157. zenml/zen_stores/template_utils.py +1 -1
  158. zenml/zen_stores/zen_store_interface.py +82 -58
  159. {zenml_nightly-0.75.0.dev20250311.dist-info → zenml_nightly-0.75.0.dev20250313.dist-info}/METADATA +1 -1
  160. {zenml_nightly-0.75.0.dev20250311.dist-info → zenml_nightly-0.75.0.dev20250313.dist-info}/RECORD +163 -149
  161. {zenml_nightly-0.75.0.dev20250311.dist-info → zenml_nightly-0.75.0.dev20250313.dist-info}/LICENSE +0 -0
  162. {zenml_nightly-0.75.0.dev20250311.dist-info → zenml_nightly-0.75.0.dev20250313.dist-info}/WHEEL +0 -0
  163. {zenml_nightly-0.75.0.dev20250311.dist-info → zenml_nightly-0.75.0.dev20250313.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,
@@ -102,6 +102,7 @@ from zenml.constants import (
102
102
  STACK_DEPLOYMENT,
103
103
  STACKS,
104
104
  STEPS,
105
+ TAG_RESOURCES,
105
106
  TAGS,
106
107
  TRIGGER_EXECUTIONS,
107
108
  TRIGGERS,
@@ -244,6 +245,8 @@ from zenml.models import (
244
245
  StepRunUpdate,
245
246
  TagFilter,
246
247
  TagRequest,
248
+ TagResourceRequest,
249
+ TagResourceResponse,
247
250
  TagResponse,
248
251
  TagUpdate,
249
252
  TriggerExecutionFilter,
@@ -259,7 +262,6 @@ from zenml.models import (
259
262
  WorkspaceFilter,
260
263
  WorkspaceRequest,
261
264
  WorkspaceResponse,
262
- WorkspaceScopedRequest,
263
265
  WorkspaceUpdate,
264
266
  )
265
267
  from zenml.service_connectors.service_connector_registry import (
@@ -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
+ workspace_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
+ workspace_name_or_id: The workspace 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
+ "workspace_name_or_id": workspace_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
@@ -2140,7 +2123,7 @@ class RestZenStore(BaseZenStore):
2140
2123
  Returns:
2141
2124
  The newly created secret.
2142
2125
  """
2143
- return self._create_workspace_scoped_resource(
2126
+ return self._create_resource(
2144
2127
  resource=secret,
2145
2128
  route=SECRETS,
2146
2129
  response_model=SecretResponse,
@@ -2399,7 +2382,7 @@ class RestZenStore(BaseZenStore):
2399
2382
  Returns:
2400
2383
  The newly created service connector.
2401
2384
  """
2402
- connector_model = self._create_workspace_scoped_resource(
2385
+ connector_model = self._create_resource(
2403
2386
  resource=service_connector,
2404
2387
  route=SERVICE_CONNECTORS,
2405
2388
  response_model=ServiceConnectorResponse,
@@ -2661,33 +2644,21 @@ class RestZenStore(BaseZenStore):
2661
2644
 
2662
2645
  def list_service_connector_resources(
2663
2646
  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,
2647
+ filter_model: ServiceConnectorFilter,
2668
2648
  ) -> List[ServiceConnectorResourcesModel]:
2669
2649
  """List resources that can be accessed by service connectors.
2670
2650
 
2671
2651
  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.
2652
+ filter_model: The filter model to use when fetching service
2653
+ connectors.
2676
2654
 
2677
2655
  Returns:
2678
2656
  The matching list of resources that available service
2679
2657
  connectors have access to.
2680
2658
  """
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
2659
  response_body = self.get(
2689
- f"{WORKSPACES}/{workspace_name_or_id}{SERVICE_CONNECTORS}{SERVICE_CONNECTOR_RESOURCES}",
2690
- params=params,
2660
+ SERVICE_CONNECTOR_RESOURCES,
2661
+ params=filter_model.model_dump(exclude_none=True),
2691
2662
  timeout=max(
2692
2663
  self.config.http_timeout,
2693
2664
  SERVICE_CONNECTOR_VERIFY_REQUEST_TIMEOUT,
@@ -2724,12 +2695,12 @@ class RestZenStore(BaseZenStore):
2724
2695
 
2725
2696
  try:
2726
2697
  local_resources = connector_instance.verify(
2727
- resource_type=resource_type,
2728
- resource_id=resource_id,
2698
+ resource_type=filter_model.resource_type,
2699
+ resource_id=filter_model.resource_id,
2729
2700
  )
2730
2701
  except (ValueError, AuthorizationException) as e:
2731
2702
  logger.error(
2732
- f"Failed to fetch {resource_type or 'available'} "
2703
+ f"Failed to fetch {filter_model.resource_type or 'available'} "
2733
2704
  f"resources from service connector {connector.name}/"
2734
2705
  f"{connector.id}: {e}"
2735
2706
  )
@@ -2858,12 +2829,10 @@ class RestZenStore(BaseZenStore):
2858
2829
  Returns:
2859
2830
  The registered stack.
2860
2831
  """
2861
- assert stack.workspace is not None
2862
-
2863
2832
  return self._create_resource(
2864
2833
  resource=stack,
2865
2834
  response_model=StackResponse,
2866
- route=f"{WORKSPACES}/{str(stack.workspace)}{STACKS}",
2835
+ route=STACKS,
2867
2836
  )
2868
2837
 
2869
2838
  def get_stack(self, stack_id: UUID, hydrate: bool = True) -> StackResponse:
@@ -3473,19 +3442,19 @@ class RestZenStore(BaseZenStore):
3473
3442
  Returns:
3474
3443
  The newly created model.
3475
3444
  """
3476
- return self._create_workspace_scoped_resource(
3445
+ return self._create_resource(
3477
3446
  resource=model,
3478
3447
  response_model=ModelResponse,
3479
3448
  route=MODELS,
3480
3449
  )
3481
3450
 
3482
- def delete_model(self, model_name_or_id: Union[str, UUID]) -> None:
3451
+ def delete_model(self, model_id: UUID) -> None:
3483
3452
  """Deletes a model.
3484
3453
 
3485
3454
  Args:
3486
- model_name_or_id: name or id of the model to be deleted.
3455
+ model_id: id of the model to be deleted.
3487
3456
  """
3488
- self._delete_resource(resource_id=model_name_or_id, route=MODELS)
3457
+ self._delete_resource(resource_id=model_id, route=MODELS)
3489
3458
 
3490
3459
  def update_model(
3491
3460
  self,
@@ -3508,13 +3477,11 @@ class RestZenStore(BaseZenStore):
3508
3477
  response_model=ModelResponse,
3509
3478
  )
3510
3479
 
3511
- def get_model(
3512
- self, model_name_or_id: Union[str, UUID], hydrate: bool = True
3513
- ) -> ModelResponse:
3480
+ def get_model(self, model_id: UUID, hydrate: bool = True) -> ModelResponse:
3514
3481
  """Get an existing model.
3515
3482
 
3516
3483
  Args:
3517
- model_name_or_id: name or id of the model to be retrieved.
3484
+ model_id: id of the model to be retrieved.
3518
3485
  hydrate: Flag deciding whether to hydrate the output model(s)
3519
3486
  by including metadata fields in the response.
3520
3487
 
@@ -3522,7 +3489,7 @@ class RestZenStore(BaseZenStore):
3522
3489
  The model of interest.
3523
3490
  """
3524
3491
  return self._get_resource(
3525
- resource_id=model_name_or_id,
3492
+ resource_id=model_id,
3526
3493
  route=MODELS,
3527
3494
  response_model=ModelResponse,
3528
3495
  params={"hydrate": hydrate},
@@ -3564,10 +3531,10 @@ class RestZenStore(BaseZenStore):
3564
3531
  Returns:
3565
3532
  The newly created model version.
3566
3533
  """
3567
- return self._create_workspace_scoped_resource(
3534
+ return self._create_resource(
3568
3535
  resource=model_version,
3569
3536
  response_model=ModelVersionResponse,
3570
- route=f"{MODELS}/{model_version.model}{MODEL_VERSIONS}",
3537
+ route=MODEL_VERSIONS,
3571
3538
  )
3572
3539
 
3573
3540
  def delete_model_version(
@@ -3581,7 +3548,7 @@ class RestZenStore(BaseZenStore):
3581
3548
  """
3582
3549
  self._delete_resource(
3583
3550
  resource_id=model_version_id,
3584
- route=f"{MODEL_VERSIONS}",
3551
+ route=MODEL_VERSIONS,
3585
3552
  )
3586
3553
 
3587
3554
  def get_model_version(
@@ -3608,14 +3575,11 @@ class RestZenStore(BaseZenStore):
3608
3575
  def list_model_versions(
3609
3576
  self,
3610
3577
  model_version_filter_model: ModelVersionFilter,
3611
- model_name_or_id: Optional[Union[str, UUID]] = None,
3612
3578
  hydrate: bool = False,
3613
3579
  ) -> Page[ModelVersionResponse]:
3614
3580
  """Get all model versions by filter.
3615
3581
 
3616
3582
  Args:
3617
- model_name_or_id: name or id of the model containing the model
3618
- versions.
3619
3583
  model_version_filter_model: All filter parameters including
3620
3584
  pagination params.
3621
3585
  hydrate: Flag deciding whether to hydrate the output model(s)
@@ -3624,20 +3588,12 @@ class RestZenStore(BaseZenStore):
3624
3588
  Returns:
3625
3589
  A page of all model versions.
3626
3590
  """
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
- )
3591
+ return self._list_paginated_resources(
3592
+ route=MODEL_VERSIONS,
3593
+ response_model=ModelVersionResponse,
3594
+ filter_model=model_version_filter_model,
3595
+ params={"hydrate": hydrate},
3596
+ )
3641
3597
 
3642
3598
  def update_model_version(
3643
3599
  self,
@@ -3944,10 +3900,15 @@ class RestZenStore(BaseZenStore):
3944
3900
  Args:
3945
3901
  tag_name_or_id: name or id of the tag to delete.
3946
3902
  """
3947
- self._delete_resource(resource_id=tag_name_or_id, route=TAGS)
3903
+ self._delete_resource(
3904
+ resource_id=tag_name_or_id,
3905
+ route=TAGS,
3906
+ )
3948
3907
 
3949
3908
  def get_tag(
3950
- self, tag_name_or_id: Union[str, UUID], hydrate: bool = True
3909
+ self,
3910
+ tag_name_or_id: Union[str, UUID],
3911
+ hydrate: bool = True,
3951
3912
  ) -> TagResponse:
3952
3913
  """Get an existing tag.
3953
3914
 
@@ -3959,11 +3920,13 @@ class RestZenStore(BaseZenStore):
3959
3920
  Returns:
3960
3921
  The tag of interest.
3961
3922
  """
3923
+ params: Dict[str, Any] = {"hydrate": hydrate}
3924
+
3962
3925
  return self._get_resource(
3963
3926
  resource_id=tag_name_or_id,
3964
3927
  route=TAGS,
3965
3928
  response_model=TagResponse,
3966
- params={"hydrate": hydrate},
3929
+ params=params,
3967
3930
  )
3968
3931
 
3969
3932
  def list_tags(
@@ -4010,6 +3973,67 @@ class RestZenStore(BaseZenStore):
4010
3973
  response_model=TagResponse,
4011
3974
  )
4012
3975
 
3976
+ # ---------------------------- Tag Resource ----------------------------
3977
+
3978
+ def create_tag_resource(
3979
+ self,
3980
+ tag_resource: TagResourceRequest,
3981
+ ) -> TagResourceResponse:
3982
+ """Create a new tag resource.
3983
+
3984
+ Args:
3985
+ tag_resource: The tag resource to be created.
3986
+
3987
+ Returns:
3988
+ The newly created tag resource.
3989
+ """
3990
+ return self._create_resource(
3991
+ resource=tag_resource,
3992
+ response_model=TagResourceResponse,
3993
+ route=TAG_RESOURCES,
3994
+ )
3995
+
3996
+ def batch_create_tag_resource(
3997
+ self, tag_resources: List[TagResourceRequest]
3998
+ ) -> List[TagResourceResponse]:
3999
+ """Create a batch of tag resource relationships.
4000
+
4001
+ Args:
4002
+ tag_resources: The tag resource relationships to be created.
4003
+
4004
+ Returns:
4005
+ The newly created tag resource relationships.
4006
+ """
4007
+ return self._batch_create_resources(
4008
+ resources=tag_resources,
4009
+ response_model=TagResourceResponse,
4010
+ route=TAG_RESOURCES,
4011
+ )
4012
+
4013
+ def delete_tag_resource(
4014
+ self,
4015
+ tag_resource: TagResourceRequest,
4016
+ ) -> None:
4017
+ """Delete a tag resource.
4018
+
4019
+ Args:
4020
+ tag_resource: The tag resource relationship to delete.
4021
+ """
4022
+ self.delete(path=TAG_RESOURCES, body=tag_resource)
4023
+
4024
+ def batch_delete_tag_resource(
4025
+ self, tag_resources: List[TagResourceRequest]
4026
+ ) -> None:
4027
+ """Delete a batch of tag resources.
4028
+
4029
+ Args:
4030
+ tag_resources: The tag resource relationships to be deleted.
4031
+ """
4032
+ self._batch_delete_resources(
4033
+ resources=tag_resources,
4034
+ route=TAG_RESOURCES,
4035
+ )
4036
+
4013
4037
  # =======================
4014
4038
  # Internal helper methods
4015
4039
  # =======================
@@ -4458,6 +4482,7 @@ class RestZenStore(BaseZenStore):
4458
4482
  def delete(
4459
4483
  self,
4460
4484
  path: str,
4485
+ body: Optional[BaseModel] = None,
4461
4486
  params: Optional[Dict[str, Any]] = None,
4462
4487
  timeout: Optional[int] = None,
4463
4488
  **kwargs: Any,
@@ -4466,6 +4491,7 @@ class RestZenStore(BaseZenStore):
4466
4491
 
4467
4492
  Args:
4468
4493
  path: The path to the endpoint.
4494
+ body: The body to send.
4469
4495
  params: The query parameters to pass to the endpoint.
4470
4496
  timeout: The request timeout in seconds.
4471
4497
  kwargs: Additional keyword arguments to pass to the request.
@@ -4477,6 +4503,7 @@ class RestZenStore(BaseZenStore):
4477
4503
  return self._request(
4478
4504
  "DELETE",
4479
4505
  self.url + API + VERSION_1 + path,
4506
+ json=body.model_dump(mode="json") if body else None,
4480
4507
  params=params,
4481
4508
  timeout=timeout,
4482
4509
  **kwargs,
@@ -4602,32 +4629,6 @@ class RestZenStore(BaseZenStore):
4602
4629
  for model_data in response
4603
4630
  ]
4604
4631
 
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
4632
  def _get_or_create_resource(
4632
4633
  self,
4633
4634
  resource: AnyRequest,
@@ -4654,57 +4655,30 @@ class RestZenStore(BaseZenStore):
4654
4655
  a boolean indicating whether the resource was created or not.
4655
4656
  """
4656
4657
  response_body = self.post(
4657
- f"{route}{GET_OR_CREATE}",
4658
+ route,
4658
4659
  body=resource,
4659
4660
  params=params,
4660
4661
  )
4661
4662
  if not isinstance(response_body, list):
4662
4663
  raise ValueError(
4663
- f"Expected a list response from the {route}{GET_OR_CREATE} "
4664
+ f"Expected a list response from the {route} "
4664
4665
  f"endpoint but got {type(response_body)} instead."
4665
4666
  )
4666
4667
  if len(response_body) != 2:
4667
4668
  raise ValueError(
4668
4669
  f"Expected a list response with 2 elements from the "
4669
- f"{route}{GET_OR_CREATE} endpoint but got {len(response_body)} "
4670
+ f"{route} endpoint but got {len(response_body)} "
4670
4671
  f"elements instead."
4671
4672
  )
4672
4673
  model_json, was_created = response_body
4673
4674
  if not isinstance(was_created, bool):
4674
4675
  raise ValueError(
4675
4676
  f"Expected a boolean as the second element of the list "
4676
- f"response from the {route}{GET_OR_CREATE} endpoint but got "
4677
+ f"response from the {route} endpoint but got "
4677
4678
  f"{type(was_created)} instead."
4678
4679
  )
4679
4680
  return response_model.model_validate(model_json), was_created
4680
4681
 
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
4682
  def _get_resource(
4709
4683
  self,
4710
4684
  resource_id: Union[str, int, UUID],
@@ -4820,12 +4794,39 @@ class RestZenStore(BaseZenStore):
4820
4794
  return response_model.model_validate(response_body)
4821
4795
 
4822
4796
  def _delete_resource(
4823
- self, resource_id: Union[str, UUID], route: str
4797
+ self,
4798
+ resource_id: Union[str, UUID],
4799
+ route: str,
4800
+ params: Optional[Dict[str, Any]] = None,
4824
4801
  ) -> None:
4825
4802
  """Delete a resource.
4826
4803
 
4827
4804
  Args:
4828
4805
  resource_id: The ID of the resource to delete.
4829
4806
  route: The resource REST API route to use.
4807
+ params: Optional query parameters to pass to the endpoint.
4808
+ """
4809
+ self.delete(f"{route}/{str(resource_id)}", params=params)
4810
+
4811
+ def _batch_delete_resources(
4812
+ self,
4813
+ resources: Sequence[BaseModel],
4814
+ route: str,
4815
+ params: Optional[Dict[str, Any]] = None,
4816
+ ) -> None:
4817
+ """Delete a batch of resources.
4818
+
4819
+ Args:
4820
+ resources: The resources to delete.
4821
+ route: The resource REST route to use.
4822
+ params: Optional query parameters to pass to the endpoint.
4830
4823
  """
4831
- self.delete(f"{route}/{str(resource_id)}")
4824
+ json_data = [
4825
+ resource.model_dump(mode="json") for resource in resources
4826
+ ]
4827
+ self._request(
4828
+ "DELETE",
4829
+ self.url + API + VERSION_1 + route + BATCH,
4830
+ json=json_data,
4831
+ params=params,
4832
+ )