zenml-nightly 0.82.1.dev20250521__py3-none-any.whl → 0.82.1.dev20250524__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 (57) hide show
  1. zenml/VERSION +1 -1
  2. zenml/client.py +6 -2
  3. zenml/config/build_configuration.py +7 -0
  4. zenml/config/docker_settings.py +25 -0
  5. zenml/config/server_config.py +7 -0
  6. zenml/constants.py +1 -0
  7. zenml/enums.py +1 -0
  8. zenml/environment.py +12 -0
  9. zenml/integrations/gcp/__init__.py +1 -1
  10. zenml/integrations/gcp/service_connectors/gcp_service_connector.py +12 -11
  11. zenml/integrations/hyperai/orchestrators/hyperai_orchestrator.py +1 -1
  12. zenml/integrations/kubernetes/orchestrators/kube_utils.py +16 -12
  13. zenml/materializers/built_in_materializer.py +9 -3
  14. zenml/zen_server/cloud_utils.py +45 -21
  15. zenml/zen_server/routers/actions_endpoints.py +6 -6
  16. zenml/zen_server/routers/artifact_endpoint.py +6 -6
  17. zenml/zen_server/routers/artifact_version_endpoints.py +11 -11
  18. zenml/zen_server/routers/auth_endpoints.py +4 -3
  19. zenml/zen_server/routers/code_repositories_endpoints.py +6 -6
  20. zenml/zen_server/routers/devices_endpoints.py +6 -6
  21. zenml/zen_server/routers/event_source_endpoints.py +6 -6
  22. zenml/zen_server/routers/flavors_endpoints.py +7 -7
  23. zenml/zen_server/routers/logs_endpoints.py +2 -2
  24. zenml/zen_server/routers/model_versions_endpoints.py +13 -13
  25. zenml/zen_server/routers/models_endpoints.py +6 -6
  26. zenml/zen_server/routers/pipeline_builds_endpoints.py +5 -5
  27. zenml/zen_server/routers/pipeline_deployments_endpoints.py +6 -6
  28. zenml/zen_server/routers/pipelines_endpoints.py +7 -7
  29. zenml/zen_server/routers/plugin_endpoints.py +3 -3
  30. zenml/zen_server/routers/projects_endpoints.py +7 -7
  31. zenml/zen_server/routers/run_metadata_endpoints.py +2 -2
  32. zenml/zen_server/routers/run_templates_endpoints.py +7 -7
  33. zenml/zen_server/routers/runs_endpoints.py +11 -11
  34. zenml/zen_server/routers/schedule_endpoints.py +6 -6
  35. zenml/zen_server/routers/secrets_endpoints.py +8 -8
  36. zenml/zen_server/routers/server_endpoints.py +13 -9
  37. zenml/zen_server/routers/service_accounts_endpoints.py +12 -12
  38. zenml/zen_server/routers/service_connectors_endpoints.py +13 -13
  39. zenml/zen_server/routers/service_endpoints.py +6 -6
  40. zenml/zen_server/routers/stack_components_endpoints.py +18 -9
  41. zenml/zen_server/routers/stack_deployment_endpoints.py +4 -4
  42. zenml/zen_server/routers/stacks_endpoints.py +6 -6
  43. zenml/zen_server/routers/steps_endpoints.py +8 -8
  44. zenml/zen_server/routers/tag_resource_endpoints.py +5 -5
  45. zenml/zen_server/routers/tags_endpoints.py +6 -6
  46. zenml/zen_server/routers/triggers_endpoints.py +9 -9
  47. zenml/zen_server/routers/users_endpoints.py +12 -12
  48. zenml/zen_server/routers/webhook_endpoints.py +2 -2
  49. zenml/zen_server/utils.py +72 -33
  50. zenml/zen_server/zen_server_api.py +211 -55
  51. zenml/zen_stores/rest_zen_store.py +40 -11
  52. zenml/zen_stores/sql_zen_store.py +79 -2
  53. {zenml_nightly-0.82.1.dev20250521.dist-info → zenml_nightly-0.82.1.dev20250524.dist-info}/METADATA +1 -1
  54. {zenml_nightly-0.82.1.dev20250521.dist-info → zenml_nightly-0.82.1.dev20250524.dist-info}/RECORD +57 -57
  55. {zenml_nightly-0.82.1.dev20250521.dist-info → zenml_nightly-0.82.1.dev20250524.dist-info}/LICENSE +0 -0
  56. {zenml_nightly-0.82.1.dev20250521.dist-info → zenml_nightly-0.82.1.dev20250524.dist-info}/WHEEL +0 -0
  57. {zenml_nightly-0.82.1.dev20250521.dist-info → zenml_nightly-0.82.1.dev20250524.dist-info}/entry_points.txt +0 -0
zenml/VERSION CHANGED
@@ -1 +1 @@
1
- 0.82.1.dev20250521
1
+ 0.82.1.dev20250524
zenml/client.py CHANGED
@@ -2024,7 +2024,9 @@ class Client(metaclass=ClientMetaClass):
2024
2024
  name=name,
2025
2025
  type=component_type,
2026
2026
  flavor=flavor,
2027
- configuration=configuration,
2027
+ configuration=validated_config.model_dump(
2028
+ mode="json", exclude_unset=True
2029
+ ),
2028
2030
  labels=labels,
2029
2031
  )
2030
2032
 
@@ -2112,7 +2114,9 @@ class Client(metaclass=ClientMetaClass):
2112
2114
  assert validated_config is not None
2113
2115
  warn_if_config_server_mismatch(validated_config)
2114
2116
 
2115
- update_model.configuration = existing_configuration
2117
+ update_model.configuration = validated_config.model_dump(
2118
+ mode="json", exclude_unset=True
2119
+ )
2116
2120
 
2117
2121
  if labels is not None:
2118
2122
  existing_labels = component.labels or {}
@@ -108,6 +108,13 @@ class BuildConfiguration(BaseModel):
108
108
  )
109
109
  if digest:
110
110
  hash_.update(digest.encode())
111
+ elif self.settings.skip_build:
112
+ # If the user has specified to skip the build, the image being
113
+ # used for the run is whatever they set for `parent_image`.
114
+ # In this case, the bevavior depends on the orchestrators image
115
+ # pull policy, and whether there is any caching involved. This
116
+ # is out of our control here, so we don't log any warning.
117
+ pass
111
118
  else:
112
119
  logger.warning(
113
120
  "Unable to fetch parent image digest for image `%s`. "
@@ -386,6 +386,31 @@ class DockerSettings(BaseSettings):
386
386
 
387
387
  return self
388
388
 
389
+ @model_validator(mode="before")
390
+ @classmethod
391
+ @before_validator_handler
392
+ def _warn_about_future_default_installer(
393
+ cls, data: Dict[str, Any]
394
+ ) -> Dict[str, Any]:
395
+ """Warns about the future change of default package installer from pip to uv.
396
+
397
+ Args:
398
+ data: The model data.
399
+
400
+ Returns:
401
+ The validated settings values.
402
+ """
403
+ if "python_package_installer" not in data:
404
+ logger.warning(
405
+ "In a future release, the default Python package installer "
406
+ "used by ZenML to build container images for your "
407
+ "containerized pipelines will change from 'pip' to 'uv'. "
408
+ "To maintain current behavior, you can explicitly set "
409
+ "`python_package_installer=PythonPackageInstaller.PIP` "
410
+ "in your DockerSettings."
411
+ )
412
+ return data
413
+
389
414
  model_config = ConfigDict(
390
415
  # public attributes are immutable
391
416
  frozen=True,
@@ -45,6 +45,7 @@ from zenml.constants import (
45
45
  DEFAULT_ZENML_SERVER_MAX_REQUEST_BODY_SIZE_IN_BYTES,
46
46
  DEFAULT_ZENML_SERVER_NAME,
47
47
  DEFAULT_ZENML_SERVER_PIPELINE_RUN_AUTH_WINDOW,
48
+ DEFAULT_ZENML_SERVER_REQUEST_TIMEOUT,
48
49
  DEFAULT_ZENML_SERVER_SECURE_HEADERS_CACHE,
49
50
  DEFAULT_ZENML_SERVER_SECURE_HEADERS_CONTENT,
50
51
  DEFAULT_ZENML_SERVER_SECURE_HEADERS_CSP,
@@ -252,6 +253,11 @@ class ServerConfiguration(BaseModel):
252
253
  used.
253
254
  file_download_size_limit: The maximum size of the file download in
254
255
  bytes. If not specified, the default value of 2GB will be used.
256
+ thread_pool_size: The size of the thread pool for handling requests. If
257
+ not specified, the default value of 40 will be used.
258
+ server_request_timeout: The timeout for server requests in seconds. If
259
+ not specified, the default value of 20 seconds will be used. This
260
+ value should be lower than the client's request timeout.
255
261
  """
256
262
 
257
263
  deployment_type: ServerDeploymentType = ServerDeploymentType.OTHER
@@ -348,6 +354,7 @@ class ServerConfiguration(BaseModel):
348
354
  auto_activate: bool = False
349
355
 
350
356
  thread_pool_size: int = DEFAULT_ZENML_SERVER_THREAD_POOL_SIZE
357
+ server_request_timeout: int = DEFAULT_ZENML_SERVER_REQUEST_TIMEOUT
351
358
 
352
359
  max_request_body_size_in_bytes: int = (
353
360
  DEFAULT_ZENML_SERVER_MAX_REQUEST_BODY_SIZE_IN_BYTES
zenml/constants.py CHANGED
@@ -282,6 +282,7 @@ DEFAULT_ZENML_SERVER_MAX_DEVICE_AUTH_ATTEMPTS = 3
282
282
  DEFAULT_ZENML_SERVER_DEVICE_AUTH_TIMEOUT = 60 * 5 # 5 minutes
283
283
  DEFAULT_ZENML_SERVER_DEVICE_AUTH_POLLING = 5 # seconds
284
284
  DEFAULT_HTTP_TIMEOUT = 30
285
+ DEFAULT_ZENML_SERVER_REQUEST_TIMEOUT = 20 # seconds
285
286
  SERVICE_CONNECTOR_VERIFY_REQUEST_TIMEOUT = 120 # seconds
286
287
  ZENML_API_KEY_PREFIX = "ZENKEY_"
287
288
  DEFAULT_ZENML_SERVER_PIPELINE_RUN_AUTH_WINDOW = 60 * 48 # 48 hours
zenml/enums.py CHANGED
@@ -314,6 +314,7 @@ class EnvironmentType(StrEnum):
314
314
  LIGHTNING_AI_STUDIO = "lightning_ai_studio"
315
315
  GITHUB_CODESPACES = "github_codespaces"
316
316
  VSCODE_REMOTE_CONTAINER = "vscode_remote_container"
317
+ ZENML_CODESPACE = "zenml_codespace"
317
318
 
318
319
 
319
320
  class ModelStages(StrEnum):
zenml/environment.py CHANGED
@@ -79,6 +79,8 @@ def get_environment() -> str:
79
79
  return EnvironmentType.GENERIC_CI
80
80
  elif Environment.in_github_codespaces():
81
81
  return EnvironmentType.GITHUB_CODESPACES
82
+ elif Environment.in_zenml_codespace():
83
+ return EnvironmentType.ZENML_CODESPACE
82
84
  elif Environment.in_vscode_remote_container():
83
85
  return EnvironmentType.VSCODE_REMOTE_CONTAINER
84
86
  elif Environment.in_lightning_ai_studio():
@@ -254,6 +256,16 @@ class Environment(metaclass=SingletonMetaClass):
254
256
  or "GITHUB_CODESPACES_PORT_FORWARDING_DOMAIN" in os.environ
255
257
  )
256
258
 
259
+ @staticmethod
260
+ def in_zenml_codespace() -> bool:
261
+ """If the current Python process is running in ZenML Codespaces.
262
+
263
+ Returns:
264
+ `True` if the current Python process is running in ZenML Codespaces,
265
+ `False` otherwise.
266
+ """
267
+ return os.environ.get("ZENML_ENVIRONMENT") == "codespace"
268
+
257
269
  @staticmethod
258
270
  def in_vscode_remote_container() -> bool:
259
271
  """If the current Python process is running in a VS Code Remote Container.
@@ -45,7 +45,7 @@ class GcpIntegration(Integration):
45
45
  NAME = GCP
46
46
  REQUIREMENTS = [
47
47
  "kfp>=2.6.0",
48
- "gcsfs",
48
+ "gcsfs!=2025.5.0,!=2025.5.0.post1",
49
49
  "google-cloud-secret-manager",
50
50
  "google-cloud-container>=2.21.0",
51
51
  "google-cloud-artifact-registry>=1.11.3",
@@ -78,7 +78,7 @@ from zenml.service_connectors.service_connector import (
78
78
  from zenml.utils.enum_utils import StrEnum
79
79
  from zenml.utils.pydantic_utils import before_validator_handler
80
80
  from zenml.utils.secret_utils import PlainSerializedSecretStr
81
- from zenml.utils.time_utils import utc_now
81
+ from zenml.utils.time_utils import to_utc_timezone, utc_now
82
82
 
83
83
  logger = get_logger(__name__)
84
84
 
@@ -1200,14 +1200,13 @@ class GCPServiceConnector(ServiceConnector):
1200
1200
  elif auth_method == GCPAuthenticationMethods.OAUTH2_TOKEN:
1201
1201
  assert isinstance(cfg, GCPOAuth2TokenConfig)
1202
1202
 
1203
- expires_at = self.expires_at
1204
- if expires_at:
1205
- # Remove the UTC timezone
1206
- expires_at = expires_at.replace(tzinfo=None)
1207
-
1208
1203
  credentials = gcp_credentials.Credentials(
1209
1204
  token=cfg.token.get_secret_value(),
1210
- expiry=expires_at,
1205
+ # Currently GCP expects the expiry to be a timezone-naive
1206
+ # UTC datetime.
1207
+ expiry=to_utc_timezone(self.expires_at).replace(tzinfo=None)
1208
+ if self.expires_at
1209
+ else None,
1211
1210
  scopes=scopes,
1212
1211
  )
1213
1212
 
@@ -1303,10 +1302,12 @@ class GCPServiceConnector(ServiceConnector):
1303
1302
  )
1304
1303
 
1305
1304
  if credentials.expiry:
1306
- # Add the UTC timezone to the expiration time
1307
- expires_at = credentials.expiry.replace(
1308
- tzinfo=datetime.timezone.utc
1309
- )
1305
+ expires_at = credentials.expiry
1306
+
1307
+ if expires_at:
1308
+ # Add the UTC timezone to the expiration time, if it's not already
1309
+ # set
1310
+ expires_at = to_utc_timezone(expires_at)
1310
1311
 
1311
1312
  return credentials, expires_at
1312
1313
 
@@ -425,7 +425,7 @@ class HyperAIOrchestrator(ContainerizedOrchestrator):
425
425
  with tempfile.NamedTemporaryFile(mode="w", delete=True) as f:
426
426
  # Define bash line and command line
427
427
  bash_line = "#!/bin/bash\n"
428
- command_line = f'cd {directory_name} && echo {ENV_ZENML_HYPERAI_RUN_ID}="{deployment_id}_$(date +\%s)" > .env && docker compose up -d'
428
+ command_line = rf'cd {directory_name} && echo {ENV_ZENML_HYPERAI_RUN_ID}="{deployment_id}_$(date +\%s)" > .env && docker compose up -d'
429
429
 
430
430
  # Write script to temporary file
431
431
  with f.file as f_:
@@ -270,18 +270,22 @@ def wait_pod(
270
270
  # Stream logs to `zenml.logger.info()`.
271
271
  # TODO: can we do this without parsing all logs every time?
272
272
  if stream_logs and pod_is_not_pending(resp):
273
- response = core_api.read_namespaced_pod_log(
274
- name=pod_name,
275
- namespace=namespace,
276
- _preload_content=False,
277
- )
278
- raw_data = response.data
279
- decoded_log = raw_data.decode("utf-8", errors="replace")
280
- logs = decoded_log.splitlines()
281
- if len(logs) > logged_lines:
282
- for line in logs[logged_lines:]:
283
- logger.info(line)
284
- logged_lines = len(logs)
273
+ try:
274
+ response = core_api.read_namespaced_pod_log(
275
+ name=pod_name,
276
+ namespace=namespace,
277
+ _preload_content=False,
278
+ )
279
+ except ApiException as e:
280
+ logger.error(f"Error reading pod logs: {e}. Retrying...")
281
+ else:
282
+ raw_data = response.data
283
+ decoded_log = raw_data.decode("utf-8", errors="replace")
284
+ logs = decoded_log.splitlines()
285
+ if len(logs) > logged_lines:
286
+ for line in logs[logged_lines:]:
287
+ logger.info(line)
288
+ logged_lines = len(logs)
285
289
 
286
290
  # Raise an error if the pod failed.
287
291
  if pod_failed(resp):
@@ -352,7 +352,9 @@ class BuiltInContainerMaterializer(BaseMaterializer):
352
352
  ):
353
353
  type_ = find_type_by_str(type_str)
354
354
  materializer_class = materializer_registry[type_]
355
- materializer = materializer_class(uri=path_)
355
+ materializer = materializer_class(
356
+ uri=path_, artifact_store=self.artifact_store
357
+ )
356
358
  element = materializer.load(type_)
357
359
  outputs.append(element)
358
360
 
@@ -364,7 +366,9 @@ class BuiltInContainerMaterializer(BaseMaterializer):
364
366
  materializer_class = source_utils.load(
365
367
  entry["materializer"]
366
368
  )
367
- materializer = materializer_class(uri=path_)
369
+ materializer = materializer_class(
370
+ uri=path_, artifact_store=self.artifact_store
371
+ )
368
372
  element = materializer.load(type_)
369
373
  outputs.append(element)
370
374
 
@@ -427,7 +431,9 @@ class BuiltInContainerMaterializer(BaseMaterializer):
427
431
  self.artifact_store.mkdir(element_path)
428
432
  type_ = type(element)
429
433
  materializer_class = materializer_registry[type_]
430
- materializer = materializer_class(uri=element_path)
434
+ materializer = materializer_class(
435
+ uri=element_path, artifact_store=self.artifact_store
436
+ )
431
437
  materializers.append(materializer)
432
438
  metadata.append(
433
439
  {
@@ -1,5 +1,8 @@
1
1
  """Utils concerning anything concerning the cloud control plane backend."""
2
2
 
3
+ import logging
4
+ import threading
5
+ import time
3
6
  from datetime import datetime, timedelta
4
7
  from threading import RLock
5
8
  from typing import Any, Dict, Optional
@@ -12,9 +15,12 @@ from zenml.exceptions import (
12
15
  IllegalOperationError,
13
16
  SubscriptionUpgradeRequiredError,
14
17
  )
18
+ from zenml.logger import get_logger
15
19
  from zenml.utils.time_utils import utc_now
16
20
  from zenml.zen_server.utils import get_zenml_headers, server_config
17
21
 
22
+ logger = get_logger(__name__)
23
+
18
24
  _cloud_connection: Optional["ZenMLCloudConnection"] = None
19
25
 
20
26
 
@@ -56,16 +62,16 @@ class ZenMLCloudConnection:
56
62
  """
57
63
  url = self._config.api_url + endpoint
58
64
 
59
- response = self.session.request(
60
- method=method,
61
- url=url,
62
- params=params,
63
- json=data,
64
- timeout=self._config.http_timeout,
65
- )
66
- if response.status_code == 401:
67
- # Refresh the auth token and try again
68
- self._reset_login()
65
+ if logger.isEnabledFor(logging.DEBUG):
66
+ # Get the request ID from the current thread object
67
+ request_id = threading.current_thread().name
68
+ logger.debug(
69
+ f"[{request_id}] RBAC STATS - {method} {endpoint} started"
70
+ )
71
+ start_time = time.time()
72
+
73
+ status_code: Optional[int] = None
74
+ try:
69
75
  response = self.session.request(
70
76
  method=method,
71
77
  url=url,
@@ -73,18 +79,36 @@ class ZenMLCloudConnection:
73
79
  json=data,
74
80
  timeout=self._config.http_timeout,
75
81
  )
82
+ if response.status_code == 401:
83
+ # Refresh the auth token and try again
84
+ self._reset_login()
85
+ response = self.session.request(
86
+ method=method,
87
+ url=url,
88
+ params=params,
89
+ json=data,
90
+ timeout=self._config.http_timeout,
91
+ )
76
92
 
77
- try:
78
- response.raise_for_status()
79
- except requests.HTTPError as e:
80
- if response.status_code == 402:
81
- raise SubscriptionUpgradeRequiredError(response.json())
82
- elif response.status_code == 403:
83
- raise IllegalOperationError(response.json())
84
- else:
85
- raise RuntimeError(
86
- f"Failed while trying to contact the central zenml pro "
87
- f"service: {e}"
93
+ status_code = response.status_code
94
+ try:
95
+ response.raise_for_status()
96
+ except requests.HTTPError as e:
97
+ if response.status_code == 402:
98
+ raise SubscriptionUpgradeRequiredError(response.json())
99
+ elif response.status_code == 403:
100
+ raise IllegalOperationError(response.json())
101
+ else:
102
+ raise RuntimeError(
103
+ f"Failed while trying to contact the central zenml pro "
104
+ f"service: {e}"
105
+ )
106
+ finally:
107
+ if logger.isEnabledFor(logging.DEBUG):
108
+ duration = (time.time() - start_time) * 1000
109
+ logger.debug(
110
+ f"[{request_id}] RBAC STATS - {status_code} {method} "
111
+ f"{endpoint} completed in {duration:.2f}ms"
88
112
  )
89
113
 
90
114
  return response
@@ -44,7 +44,7 @@ from zenml.zen_server.rbac.utils import (
44
44
  verify_permission_for_model,
45
45
  )
46
46
  from zenml.zen_server.utils import (
47
- handle_exceptions,
47
+ async_fastapi_endpoint_wrapper,
48
48
  make_dependable,
49
49
  plugin_flavor_registry,
50
50
  zen_store,
@@ -61,7 +61,7 @@ router = APIRouter(
61
61
  "",
62
62
  responses={401: error_response, 404: error_response, 422: error_response},
63
63
  )
64
- @handle_exceptions
64
+ @async_fastapi_endpoint_wrapper
65
65
  def list_actions(
66
66
  action_filter_model: ActionFilter = Depends(make_dependable(ActionFilter)),
67
67
  hydrate: bool = False,
@@ -132,7 +132,7 @@ def list_actions(
132
132
  "/{action_id}",
133
133
  responses={401: error_response, 404: error_response, 422: error_response},
134
134
  )
135
- @handle_exceptions
135
+ @async_fastapi_endpoint_wrapper
136
136
  def get_action(
137
137
  action_id: UUID,
138
138
  hydrate: bool = True,
@@ -179,7 +179,7 @@ def get_action(
179
179
  "",
180
180
  responses={401: error_response, 409: error_response, 422: error_response},
181
181
  )
182
- @handle_exceptions
182
+ @async_fastapi_endpoint_wrapper
183
183
  def create_action(
184
184
  action: ActionRequest,
185
185
  _: AuthContext = Security(authorize),
@@ -225,7 +225,7 @@ def create_action(
225
225
  "/{action_id}",
226
226
  responses={401: error_response, 404: error_response, 422: error_response},
227
227
  )
228
- @handle_exceptions
228
+ @async_fastapi_endpoint_wrapper
229
229
  def update_action(
230
230
  action_id: UUID,
231
231
  action_update: ActionUpdate,
@@ -280,7 +280,7 @@ def update_action(
280
280
  "/{action_id}",
281
281
  responses={401: error_response, 404: error_response, 422: error_response},
282
282
  )
283
- @handle_exceptions
283
+ @async_fastapi_endpoint_wrapper
284
284
  def delete_action(
285
285
  action_id: UUID,
286
286
  force: bool = False,
@@ -36,7 +36,7 @@ from zenml.zen_server.rbac.endpoint_utils import (
36
36
  )
37
37
  from zenml.zen_server.rbac.models import ResourceType
38
38
  from zenml.zen_server.utils import (
39
- handle_exceptions,
39
+ async_fastapi_endpoint_wrapper,
40
40
  make_dependable,
41
41
  zen_store,
42
42
  )
@@ -52,7 +52,7 @@ artifact_router = APIRouter(
52
52
  "",
53
53
  responses={401: error_response, 404: error_response, 422: error_response},
54
54
  )
55
- @handle_exceptions
55
+ @async_fastapi_endpoint_wrapper
56
56
  def list_artifacts(
57
57
  artifact_filter_model: ArtifactFilter = Depends(
58
58
  make_dependable(ArtifactFilter)
@@ -83,7 +83,7 @@ def list_artifacts(
83
83
  "",
84
84
  responses={401: error_response, 409: error_response, 422: error_response},
85
85
  )
86
- @handle_exceptions
86
+ @async_fastapi_endpoint_wrapper
87
87
  def create_artifact(
88
88
  artifact: ArtifactRequest,
89
89
  _: AuthContext = Security(authorize),
@@ -106,7 +106,7 @@ def create_artifact(
106
106
  "/{artifact_id}",
107
107
  responses={401: error_response, 404: error_response, 422: error_response},
108
108
  )
109
- @handle_exceptions
109
+ @async_fastapi_endpoint_wrapper
110
110
  def get_artifact(
111
111
  artifact_id: UUID,
112
112
  hydrate: bool = True,
@@ -133,7 +133,7 @@ def get_artifact(
133
133
  "/{artifact_id}",
134
134
  responses={401: error_response, 404: error_response, 422: error_response},
135
135
  )
136
- @handle_exceptions
136
+ @async_fastapi_endpoint_wrapper
137
137
  def update_artifact(
138
138
  artifact_id: UUID,
139
139
  artifact_update: ArtifactUpdate,
@@ -160,7 +160,7 @@ def update_artifact(
160
160
  "/{artifact_id}",
161
161
  responses={401: error_response, 404: error_response, 422: error_response},
162
162
  )
163
- @handle_exceptions
163
+ @async_fastapi_endpoint_wrapper
164
164
  def delete_artifact(
165
165
  artifact_id: UUID,
166
166
  _: AuthContext = Security(authorize),
@@ -66,7 +66,7 @@ from zenml.zen_server.rbac.utils import (
66
66
  get_allowed_resource_ids,
67
67
  )
68
68
  from zenml.zen_server.utils import (
69
- handle_exceptions,
69
+ async_fastapi_endpoint_wrapper,
70
70
  make_dependable,
71
71
  set_filter_project_scope,
72
72
  zen_store,
@@ -83,7 +83,7 @@ artifact_version_router = APIRouter(
83
83
  "",
84
84
  responses={401: error_response, 404: error_response, 422: error_response},
85
85
  )
86
- @handle_exceptions
86
+ @async_fastapi_endpoint_wrapper
87
87
  def list_artifact_versions(
88
88
  artifact_version_filter_model: ArtifactVersionFilter = Depends(
89
89
  make_dependable(ArtifactVersionFilter)
@@ -127,7 +127,7 @@ def list_artifact_versions(
127
127
  "",
128
128
  responses={401: error_response, 409: error_response, 422: error_response},
129
129
  )
130
- @handle_exceptions
130
+ @async_fastapi_endpoint_wrapper
131
131
  def create_artifact_version(
132
132
  artifact_version: ArtifactVersionRequest,
133
133
  _: AuthContext = Security(authorize),
@@ -150,7 +150,7 @@ def create_artifact_version(
150
150
  BATCH,
151
151
  responses={401: error_response, 409: error_response, 422: error_response},
152
152
  )
153
- @handle_exceptions
153
+ @async_fastapi_endpoint_wrapper
154
154
  def batch_create_artifact_version(
155
155
  artifact_versions: List[ArtifactVersionRequest],
156
156
  _: AuthContext = Security(authorize),
@@ -173,7 +173,7 @@ def batch_create_artifact_version(
173
173
  "/{artifact_version_id}",
174
174
  responses={401: error_response, 404: error_response, 422: error_response},
175
175
  )
176
- @handle_exceptions
176
+ @async_fastapi_endpoint_wrapper
177
177
  def get_artifact_version(
178
178
  artifact_version_id: UUID,
179
179
  hydrate: bool = True,
@@ -200,7 +200,7 @@ def get_artifact_version(
200
200
  "/{artifact_version_id}",
201
201
  responses={401: error_response, 404: error_response, 422: error_response},
202
202
  )
203
- @handle_exceptions
203
+ @async_fastapi_endpoint_wrapper
204
204
  def update_artifact_version(
205
205
  artifact_version_id: UUID,
206
206
  artifact_version_update: ArtifactVersionUpdate,
@@ -227,7 +227,7 @@ def update_artifact_version(
227
227
  "/{artifact_version_id}",
228
228
  responses={401: error_response, 404: error_response, 422: error_response},
229
229
  )
230
- @handle_exceptions
230
+ @async_fastapi_endpoint_wrapper
231
231
  def delete_artifact_version(
232
232
  artifact_version_id: UUID,
233
233
  _: AuthContext = Security(authorize),
@@ -248,7 +248,7 @@ def delete_artifact_version(
248
248
  "",
249
249
  responses={401: error_response, 404: error_response, 422: error_response},
250
250
  )
251
- @handle_exceptions
251
+ @async_fastapi_endpoint_wrapper
252
252
  def prune_artifact_versions(
253
253
  project_name_or_id: Union[str, UUID],
254
254
  only_versions: bool = True,
@@ -275,7 +275,7 @@ def prune_artifact_versions(
275
275
  "/{artifact_version_id}" + VISUALIZE,
276
276
  responses={401: error_response, 404: error_response, 422: error_response},
277
277
  )
278
- @handle_exceptions
278
+ @async_fastapi_endpoint_wrapper
279
279
  def get_artifact_visualization(
280
280
  artifact_version_id: UUID,
281
281
  index: int = 0,
@@ -303,7 +303,7 @@ def get_artifact_visualization(
303
303
  "/{artifact_version_id}" + DOWNLOAD_TOKEN,
304
304
  responses={401: error_response, 404: error_response, 422: error_response},
305
305
  )
306
- @handle_exceptions
306
+ @async_fastapi_endpoint_wrapper
307
307
  def get_artifact_download_token(
308
308
  artifact_version_id: UUID,
309
309
  _: AuthContext = Security(authorize),
@@ -334,7 +334,7 @@ def get_artifact_download_token(
334
334
  "/{artifact_version_id}" + DATA,
335
335
  responses={401: error_response, 404: error_response, 422: error_response},
336
336
  )
337
- @handle_exceptions
337
+ @async_fastapi_endpoint_wrapper
338
338
  def download_artifact_data(
339
339
  artifact_version_id: UUID, token: str
340
340
  ) -> FileResponse:
@@ -71,8 +71,8 @@ from zenml.zen_server.rbac.utils import (
71
71
  verify_permission_for_model,
72
72
  )
73
73
  from zenml.zen_server.utils import (
74
+ async_fastapi_endpoint_wrapper,
74
75
  get_ip_location,
75
- handle_exceptions,
76
76
  server_config,
77
77
  zen_store,
78
78
  )
@@ -222,11 +222,11 @@ class OAuthLoginRequestForm:
222
222
  LOGIN,
223
223
  response_model=Union[OAuthTokenResponse, OAuthRedirectResponse],
224
224
  )
225
+ @async_fastapi_endpoint_wrapper
225
226
  @rate_limit_requests(
226
227
  day_limit=server_config().login_rate_limit_day,
227
228
  minute_limit=server_config().login_rate_limit_minute,
228
229
  )
229
- @handle_exceptions
230
230
  def token(
231
231
  request: Request,
232
232
  response: Response,
@@ -341,6 +341,7 @@ def logout(
341
341
  DEVICE_AUTHORIZATION,
342
342
  response_model=OAuthDeviceAuthorizationResponse,
343
343
  )
344
+ @async_fastapi_endpoint_wrapper
344
345
  def device_authorization(
345
346
  request: Request,
346
347
  client_id: UUID = Form(...),
@@ -471,7 +472,7 @@ def device_authorization(
471
472
  API_TOKEN,
472
473
  response_model=str,
473
474
  )
474
- @handle_exceptions
475
+ @async_fastapi_endpoint_wrapper
475
476
  def api_token(
476
477
  token_type: APITokenType = APITokenType.GENERIC,
477
478
  expires_in: Optional[int] = None,