zenml-nightly 0.82.1.dev20250522__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.
- zenml/VERSION +1 -1
- zenml/client.py +6 -2
- zenml/config/docker_settings.py +25 -0
- zenml/config/server_config.py +7 -0
- zenml/constants.py +1 -0
- zenml/enums.py +1 -0
- zenml/environment.py +12 -0
- zenml/integrations/gcp/__init__.py +1 -1
- zenml/integrations/gcp/service_connectors/gcp_service_connector.py +12 -11
- zenml/integrations/hyperai/orchestrators/hyperai_orchestrator.py +1 -1
- zenml/integrations/kubernetes/orchestrators/kube_utils.py +16 -12
- zenml/materializers/built_in_materializer.py +9 -3
- zenml/zen_server/cloud_utils.py +45 -21
- zenml/zen_server/routers/stack_components_endpoints.py +11 -2
- zenml/zen_server/utils.py +44 -35
- zenml/zen_server/zen_server_api.py +211 -55
- zenml/zen_stores/rest_zen_store.py +40 -11
- zenml/zen_stores/sql_zen_store.py +79 -2
- {zenml_nightly-0.82.1.dev20250522.dist-info → zenml_nightly-0.82.1.dev20250524.dist-info}/METADATA +1 -1
- {zenml_nightly-0.82.1.dev20250522.dist-info → zenml_nightly-0.82.1.dev20250524.dist-info}/RECORD +23 -23
- {zenml_nightly-0.82.1.dev20250522.dist-info → zenml_nightly-0.82.1.dev20250524.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.82.1.dev20250522.dist-info → zenml_nightly-0.82.1.dev20250524.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.82.1.dev20250522.dist-info → zenml_nightly-0.82.1.dev20250524.dist-info}/entry_points.txt +0 -0
zenml/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.82.1.
|
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=
|
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 =
|
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 {}
|
zenml/config/docker_settings.py
CHANGED
@@ -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,
|
zenml/config/server_config.py
CHANGED
@@ -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.
|
@@ -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
|
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
|
-
|
1307
|
-
|
1308
|
-
|
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 =
|
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
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
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(
|
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(
|
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(
|
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
|
{
|
zenml/zen_server/cloud_utils.py
CHANGED
@@ -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
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
@@ -93,7 +93,7 @@ def create_stack_component(
|
|
93
93
|
|
94
94
|
from zenml.stack.utils import validate_stack_component_config
|
95
95
|
|
96
|
-
validate_stack_component_config(
|
96
|
+
validated_config = validate_stack_component_config(
|
97
97
|
configuration_dict=component.configuration,
|
98
98
|
flavor=component.flavor,
|
99
99
|
component_type=component.type,
|
@@ -102,6 +102,11 @@ def create_stack_component(
|
|
102
102
|
validate_custom_flavors=False,
|
103
103
|
)
|
104
104
|
|
105
|
+
if validated_config:
|
106
|
+
component.configuration = validated_config.model_dump(
|
107
|
+
mode="json", exclude_unset=True
|
108
|
+
)
|
109
|
+
|
105
110
|
return verify_permissions_and_create_entity(
|
106
111
|
request_model=component,
|
107
112
|
create_method=zen_store().create_stack_component,
|
@@ -199,7 +204,7 @@ def update_stack_component(
|
|
199
204
|
from zenml.stack.utils import validate_stack_component_config
|
200
205
|
|
201
206
|
existing_component = zen_store().get_stack_component(component_id)
|
202
|
-
validate_stack_component_config(
|
207
|
+
validated_config = validate_stack_component_config(
|
203
208
|
configuration_dict=component_update.configuration,
|
204
209
|
flavor=existing_component.flavor_name,
|
205
210
|
component_type=existing_component.type,
|
@@ -207,6 +212,10 @@ def update_stack_component(
|
|
207
212
|
# We allow custom flavors to fail import on the server side.
|
208
213
|
validate_custom_flavors=False,
|
209
214
|
)
|
215
|
+
if validated_config:
|
216
|
+
component_update.configuration = validated_config.model_dump(
|
217
|
+
mode="json", exclude_unset=True
|
218
|
+
)
|
210
219
|
|
211
220
|
if component_update.connector:
|
212
221
|
service_connector = zen_store().get_service_connector(
|
zenml/zen_server/utils.py
CHANGED
@@ -15,6 +15,7 @@
|
|
15
15
|
|
16
16
|
import inspect
|
17
17
|
import os
|
18
|
+
import threading
|
18
19
|
from functools import wraps
|
19
20
|
from typing import (
|
20
21
|
TYPE_CHECKING,
|
@@ -322,40 +323,6 @@ def async_fastapi_endpoint_wrapper(
|
|
322
323
|
Decorated function.
|
323
324
|
"""
|
324
325
|
|
325
|
-
@wraps(func)
|
326
|
-
def decorated(*args: P.args, **kwargs: P.kwargs) -> Any:
|
327
|
-
# These imports can't happen at module level as this module is also
|
328
|
-
# used by the CLI when installed without the `server` extra
|
329
|
-
from fastapi import HTTPException
|
330
|
-
from fastapi.responses import JSONResponse
|
331
|
-
|
332
|
-
from zenml.zen_server.auth import AuthContext, set_auth_context
|
333
|
-
|
334
|
-
for arg in args:
|
335
|
-
if isinstance(arg, AuthContext):
|
336
|
-
set_auth_context(arg)
|
337
|
-
break
|
338
|
-
else:
|
339
|
-
for _, arg in kwargs.items():
|
340
|
-
if isinstance(arg, AuthContext):
|
341
|
-
set_auth_context(arg)
|
342
|
-
break
|
343
|
-
|
344
|
-
try:
|
345
|
-
return func(*args, **kwargs)
|
346
|
-
except OAuthError as error:
|
347
|
-
# The OAuthError is special because it needs to have a JSON response
|
348
|
-
return JSONResponse(
|
349
|
-
status_code=error.status_code,
|
350
|
-
content=error.to_dict(),
|
351
|
-
)
|
352
|
-
except HTTPException:
|
353
|
-
raise
|
354
|
-
except Exception as error:
|
355
|
-
logger.exception("API error")
|
356
|
-
http_exception = http_exception_from_error(error)
|
357
|
-
raise http_exception
|
358
|
-
|
359
326
|
# When having a sync FastAPI endpoint, it runs the endpoint function in
|
360
327
|
# a worker threadpool. If all threads are busy, it will queue the task.
|
361
328
|
# The problem is that after the endpoint code returns, FastAPI will queue
|
@@ -369,10 +336,52 @@ def async_fastapi_endpoint_wrapper(
|
|
369
336
|
# a worker thread to become available.
|
370
337
|
# See: `fastapi.routing.serialize_response(...)` and
|
371
338
|
# https://github.com/fastapi/fastapi/pull/888 for more information.
|
372
|
-
@wraps(
|
339
|
+
@wraps(func)
|
373
340
|
async def async_decorated(*args: P.args, **kwargs: P.kwargs) -> Any:
|
374
341
|
from starlette.concurrency import run_in_threadpool
|
375
342
|
|
343
|
+
from zenml.zen_server.zen_server_api import request_ids
|
344
|
+
|
345
|
+
request_id = request_ids.get()
|
346
|
+
|
347
|
+
@wraps(func)
|
348
|
+
def decorated(*args: P.args, **kwargs: P.kwargs) -> Any:
|
349
|
+
# These imports can't happen at module level as this module is also
|
350
|
+
# used by the CLI when installed without the `server` extra
|
351
|
+
from fastapi import HTTPException
|
352
|
+
from fastapi.responses import JSONResponse
|
353
|
+
|
354
|
+
from zenml.zen_server.auth import AuthContext, set_auth_context
|
355
|
+
|
356
|
+
if request_id:
|
357
|
+
# Change the name of the current thread to the request ID
|
358
|
+
threading.current_thread().name = request_id
|
359
|
+
|
360
|
+
for arg in args:
|
361
|
+
if isinstance(arg, AuthContext):
|
362
|
+
set_auth_context(arg)
|
363
|
+
break
|
364
|
+
else:
|
365
|
+
for _, arg in kwargs.items():
|
366
|
+
if isinstance(arg, AuthContext):
|
367
|
+
set_auth_context(arg)
|
368
|
+
break
|
369
|
+
|
370
|
+
try:
|
371
|
+
return func(*args, **kwargs)
|
372
|
+
except OAuthError as error:
|
373
|
+
# The OAuthError is special because it needs to have a JSON response
|
374
|
+
return JSONResponse(
|
375
|
+
status_code=error.status_code,
|
376
|
+
content=error.to_dict(),
|
377
|
+
)
|
378
|
+
except HTTPException:
|
379
|
+
raise
|
380
|
+
except Exception as error:
|
381
|
+
logger.exception("API error")
|
382
|
+
http_exception = http_exception_from_error(error)
|
383
|
+
raise http_exception
|
384
|
+
|
376
385
|
return await run_in_threadpool(decorated, *args, **kwargs)
|
377
386
|
|
378
387
|
return async_decorated
|
@@ -20,11 +20,17 @@ To run this file locally, execute:
|
|
20
20
|
```
|
21
21
|
"""
|
22
22
|
|
23
|
+
import logging
|
23
24
|
import os
|
25
|
+
import threading
|
26
|
+
import time
|
27
|
+
from asyncio import Lock, Semaphore, TimeoutError, wait_for
|
24
28
|
from asyncio.log import logger
|
29
|
+
from contextvars import ContextVar
|
25
30
|
from datetime import datetime, timedelta
|
26
31
|
from genericpath import isfile
|
27
|
-
from typing import Any, List, Set
|
32
|
+
from typing import Any, List, Optional, Set
|
33
|
+
from uuid import uuid4
|
28
34
|
|
29
35
|
from anyio import to_thread
|
30
36
|
from fastapi import FastAPI, HTTPException, Request
|
@@ -113,6 +119,10 @@ from zenml.zen_server.utils import (
|
|
113
119
|
|
114
120
|
DASHBOARD_DIRECTORY = "dashboard"
|
115
121
|
|
122
|
+
request_ids: ContextVar[Optional[str]] = ContextVar(
|
123
|
+
"request_ids", default=None
|
124
|
+
)
|
125
|
+
|
116
126
|
|
117
127
|
def relative_path(rel: str) -> str:
|
118
128
|
"""Get the absolute path of a path relative to the ZenML server module.
|
@@ -138,6 +148,7 @@ last_user_activity: datetime = utc_now()
|
|
138
148
|
last_user_activity_reported: datetime = last_user_activity + timedelta(
|
139
149
|
seconds=-DEFAULT_ZENML_SERVER_REPORT_USER_ACTIVITY_TO_DB_SECONDS
|
140
150
|
)
|
151
|
+
last_user_activity_lock = Lock()
|
141
152
|
|
142
153
|
|
143
154
|
# Customize the default request validation handler that comes with FastAPI
|
@@ -247,51 +258,6 @@ class RestrictFileUploadsMiddleware(BaseHTTPMiddleware):
|
|
247
258
|
|
248
259
|
ALLOWED_FOR_FILE_UPLOAD: Set[str] = set()
|
249
260
|
|
250
|
-
app.add_middleware(
|
251
|
-
CORSMiddleware,
|
252
|
-
allow_origins=server_config().cors_allow_origins,
|
253
|
-
allow_credentials=True,
|
254
|
-
allow_methods=["*"],
|
255
|
-
allow_headers=["*"],
|
256
|
-
)
|
257
|
-
|
258
|
-
app.add_middleware(
|
259
|
-
RequestBodyLimit, max_bytes=server_config().max_request_body_size_in_bytes
|
260
|
-
)
|
261
|
-
app.add_middleware(
|
262
|
-
RestrictFileUploadsMiddleware, allowed_paths=ALLOWED_FOR_FILE_UPLOAD
|
263
|
-
)
|
264
|
-
|
265
|
-
|
266
|
-
@app.middleware("http")
|
267
|
-
async def set_secure_headers(request: Request, call_next: Any) -> Any:
|
268
|
-
"""Middleware to set secure headers.
|
269
|
-
|
270
|
-
Args:
|
271
|
-
request: The incoming request.
|
272
|
-
call_next: The next function to be called.
|
273
|
-
|
274
|
-
Returns:
|
275
|
-
The response with secure headers set.
|
276
|
-
"""
|
277
|
-
try:
|
278
|
-
response = await call_next(request)
|
279
|
-
except Exception:
|
280
|
-
logger.exception("An error occurred while processing the request")
|
281
|
-
response = JSONResponse(
|
282
|
-
status_code=500,
|
283
|
-
content={"detail": "An unexpected error occurred."},
|
284
|
-
)
|
285
|
-
|
286
|
-
# If the request is for the openAPI docs, don't set secure headers
|
287
|
-
if request.url.path.startswith("/docs") or request.url.path.startswith(
|
288
|
-
"/redoc"
|
289
|
-
):
|
290
|
-
return response
|
291
|
-
|
292
|
-
secure_headers().framework.fastapi(response)
|
293
|
-
return response
|
294
|
-
|
295
261
|
|
296
262
|
@app.middleware("http")
|
297
263
|
async def track_last_user_activity(request: Request, call_next: Any) -> Any:
|
@@ -310,24 +276,30 @@ async def track_last_user_activity(request: Request, call_next: Any) -> Any:
|
|
310
276
|
"""
|
311
277
|
global last_user_activity
|
312
278
|
global last_user_activity_reported
|
279
|
+
global last_user_activity_lock
|
313
280
|
|
314
281
|
now = utc_now()
|
315
282
|
|
316
283
|
try:
|
317
284
|
if is_user_request(request):
|
318
|
-
|
285
|
+
report_user_activity = False
|
286
|
+
async with last_user_activity_lock:
|
287
|
+
last_user_activity = now
|
288
|
+
if (
|
289
|
+
(now - last_user_activity_reported).total_seconds()
|
290
|
+
> DEFAULT_ZENML_SERVER_REPORT_USER_ACTIVITY_TO_DB_SECONDS
|
291
|
+
):
|
292
|
+
last_user_activity_reported = now
|
293
|
+
report_user_activity = True
|
294
|
+
|
295
|
+
if report_user_activity:
|
296
|
+
zen_store()._update_last_user_activity_timestamp(
|
297
|
+
last_user_activity=last_user_activity
|
298
|
+
)
|
319
299
|
except Exception as e:
|
320
300
|
logger.debug(
|
321
301
|
f"An unexpected error occurred while checking user activity: {e}"
|
322
302
|
)
|
323
|
-
if (
|
324
|
-
(now - last_user_activity_reported).total_seconds()
|
325
|
-
> DEFAULT_ZENML_SERVER_REPORT_USER_ACTIVITY_TO_DB_SECONDS
|
326
|
-
):
|
327
|
-
last_user_activity_reported = now
|
328
|
-
zen_store()._update_last_user_activity_timestamp(
|
329
|
-
last_user_activity=last_user_activity
|
330
|
-
)
|
331
303
|
|
332
304
|
try:
|
333
305
|
return await call_next(request)
|
@@ -378,6 +350,190 @@ async def infer_source_context(request: Request, call_next: Any) -> Any:
|
|
378
350
|
)
|
379
351
|
|
380
352
|
|
353
|
+
request_semaphore = Semaphore(server_config().thread_pool_size)
|
354
|
+
|
355
|
+
|
356
|
+
@app.middleware("http")
|
357
|
+
async def prevent_read_timeout(request: Request, call_next: Any) -> Any:
|
358
|
+
"""Prevent read timeout client errors.
|
359
|
+
|
360
|
+
Args:
|
361
|
+
request: The incoming request.
|
362
|
+
call_next: The next function to be called.
|
363
|
+
|
364
|
+
Returns:
|
365
|
+
The response to the request.
|
366
|
+
"""
|
367
|
+
# Only process the REST API requests because these are the ones that
|
368
|
+
# take the most time to complete.
|
369
|
+
if not request.url.path.startswith(API):
|
370
|
+
return await call_next(request)
|
371
|
+
|
372
|
+
server_request_timeout = server_config().server_request_timeout
|
373
|
+
|
374
|
+
active_threads = threading.active_count()
|
375
|
+
request_id = request_ids.get()
|
376
|
+
|
377
|
+
client_ip = request.client.host if request.client else "unknown"
|
378
|
+
method = request.method
|
379
|
+
url_path = request.url.path
|
380
|
+
|
381
|
+
logger.debug(
|
382
|
+
f"[{request_id}] API STATS - {method} {url_path} from {client_ip} "
|
383
|
+
f"QUEUED [ "
|
384
|
+
f"threads: {active_threads} "
|
385
|
+
f"]"
|
386
|
+
)
|
387
|
+
|
388
|
+
start_time = time.time()
|
389
|
+
|
390
|
+
try:
|
391
|
+
# Here we wait until a worker thread is available to process the
|
392
|
+
# request with a timeout value that is set to be lower than the
|
393
|
+
# what the client is willing to wait for (i.e. lower than the
|
394
|
+
# client's HTTP request timeout). The rationale is that we want to
|
395
|
+
# respond to the client before it times out and decides to retry the
|
396
|
+
# request (which would overwhelm the server).
|
397
|
+
await wait_for(
|
398
|
+
request_semaphore.acquire(),
|
399
|
+
timeout=server_request_timeout,
|
400
|
+
)
|
401
|
+
except TimeoutError:
|
402
|
+
end_time = time.time()
|
403
|
+
duration = (end_time - start_time) * 1000
|
404
|
+
active_threads = threading.active_count()
|
405
|
+
|
406
|
+
logger.debug(
|
407
|
+
f"[{request_id}] API STATS - {method} {url_path} from {client_ip} "
|
408
|
+
f"THROTTLED after {duration:.2f}ms [ "
|
409
|
+
f"threads: {active_threads} "
|
410
|
+
f"]"
|
411
|
+
)
|
412
|
+
|
413
|
+
# We return a 429 error, basically telling the client to slow down.
|
414
|
+
# For the client, the 429 error is more meaningful than a ReadTimeout
|
415
|
+
# error, because it also tells the client two additional things:
|
416
|
+
#
|
417
|
+
# 1. The server is alive.
|
418
|
+
# 2. The server hasn't processed the request, so even if the request
|
419
|
+
# is not idempotent, it's safe to retry it.
|
420
|
+
return JSONResponse(
|
421
|
+
{"error": "Server too busy. Please try again later."},
|
422
|
+
status_code=429,
|
423
|
+
)
|
424
|
+
|
425
|
+
duration = (time.time() - start_time) * 1000
|
426
|
+
active_threads = threading.active_count()
|
427
|
+
|
428
|
+
logger.debug(
|
429
|
+
f"[{request_id}] API STATS - {method} {url_path} from {client_ip} "
|
430
|
+
f"ACCEPTED after {duration:.2f}ms [ "
|
431
|
+
f"threads: {active_threads} "
|
432
|
+
f"]"
|
433
|
+
)
|
434
|
+
|
435
|
+
try:
|
436
|
+
return await call_next(request)
|
437
|
+
finally:
|
438
|
+
request_semaphore.release()
|
439
|
+
|
440
|
+
|
441
|
+
@app.middleware("http")
|
442
|
+
async def log_requests(request: Request, call_next: Any) -> Any:
|
443
|
+
"""Log requests to the ZenML server.
|
444
|
+
|
445
|
+
Args:
|
446
|
+
request: The incoming request object.
|
447
|
+
call_next: A function that will receive the request as a parameter and
|
448
|
+
pass it to the corresponding path operation.
|
449
|
+
|
450
|
+
Returns:
|
451
|
+
The response to the request.
|
452
|
+
"""
|
453
|
+
if not logger.isEnabledFor(logging.DEBUG):
|
454
|
+
return await call_next(request)
|
455
|
+
|
456
|
+
# Get active threads count
|
457
|
+
active_threads = threading.active_count()
|
458
|
+
|
459
|
+
request_id = request.headers.get("X-Request-ID", str(uuid4())[:8])
|
460
|
+
# Detect if the request comes from Python, Web UI or something else
|
461
|
+
if source := request.headers.get("User-Agent"):
|
462
|
+
source = source.split("/")[0]
|
463
|
+
request_id = f"{request_id}/{source}"
|
464
|
+
|
465
|
+
request_ids.set(request_id)
|
466
|
+
client_ip = request.client.host if request.client else "unknown"
|
467
|
+
method = request.method
|
468
|
+
url_path = request.url.path
|
469
|
+
|
470
|
+
logger.debug(
|
471
|
+
f"[{request_id}] API STATS - {method} {url_path} from {client_ip} "
|
472
|
+
f"RECEIVED [ "
|
473
|
+
f"threads: {active_threads} "
|
474
|
+
f"]"
|
475
|
+
)
|
476
|
+
|
477
|
+
start_time = time.time()
|
478
|
+
response = await call_next(request)
|
479
|
+
duration = (time.time() - start_time) * 1000
|
480
|
+
status_code = response.status_code
|
481
|
+
|
482
|
+
logger.debug(
|
483
|
+
f"[{request_id}] API STATS - {status_code} {method} {url_path} from "
|
484
|
+
f"{client_ip} took {duration:.2f}ms [ "
|
485
|
+
f"threads: {active_threads} "
|
486
|
+
f"]"
|
487
|
+
)
|
488
|
+
return response
|
489
|
+
|
490
|
+
|
491
|
+
app.add_middleware(
|
492
|
+
CORSMiddleware,
|
493
|
+
allow_origins=server_config().cors_allow_origins,
|
494
|
+
allow_credentials=True,
|
495
|
+
allow_methods=["*"],
|
496
|
+
allow_headers=["*"],
|
497
|
+
)
|
498
|
+
|
499
|
+
app.add_middleware(
|
500
|
+
RequestBodyLimit, max_bytes=server_config().max_request_body_size_in_bytes
|
501
|
+
)
|
502
|
+
app.add_middleware(
|
503
|
+
RestrictFileUploadsMiddleware, allowed_paths=ALLOWED_FOR_FILE_UPLOAD
|
504
|
+
)
|
505
|
+
|
506
|
+
|
507
|
+
@app.middleware("http")
|
508
|
+
async def set_secure_headers(request: Request, call_next: Any) -> Any:
|
509
|
+
"""Middleware to set secure headers.
|
510
|
+
|
511
|
+
Args:
|
512
|
+
request: The incoming request.
|
513
|
+
call_next: The next function to be called.
|
514
|
+
|
515
|
+
Returns:
|
516
|
+
The response with secure headers set.
|
517
|
+
"""
|
518
|
+
try:
|
519
|
+
response = await call_next(request)
|
520
|
+
except Exception:
|
521
|
+
logger.exception("An error occurred while processing the request")
|
522
|
+
response = JSONResponse(
|
523
|
+
status_code=500,
|
524
|
+
content={"detail": "An unexpected error occurred."},
|
525
|
+
)
|
526
|
+
|
527
|
+
# If the request is for the openAPI docs, don't set secure headers
|
528
|
+
if request.url.path.startswith("/docs") or request.url.path.startswith(
|
529
|
+
"/redoc"
|
530
|
+
):
|
531
|
+
return response
|
532
|
+
|
533
|
+
secure_headers().framework.fastapi(response)
|
534
|
+
return response
|
535
|
+
|
536
|
+
|
381
537
|
@app.on_event("startup")
|
382
538
|
def initialize() -> None:
|
383
539
|
"""Initialize the ZenML server."""
|
@@ -15,6 +15,7 @@
|
|
15
15
|
|
16
16
|
import os
|
17
17
|
import re
|
18
|
+
import time
|
18
19
|
from datetime import datetime
|
19
20
|
from pathlib import Path
|
20
21
|
from typing import (
|
@@ -30,7 +31,7 @@ from typing import (
|
|
30
31
|
Union,
|
31
32
|
)
|
32
33
|
from urllib.parse import urlparse
|
33
|
-
from uuid import UUID
|
34
|
+
from uuid import UUID, uuid4
|
34
35
|
|
35
36
|
import requests
|
36
37
|
import urllib3
|
@@ -4179,6 +4180,20 @@ class RestZenStore(BaseZenStore):
|
|
4179
4180
|
Returns:
|
4180
4181
|
A requests session.
|
4181
4182
|
"""
|
4183
|
+
|
4184
|
+
class AugmentedRetry(Retry):
|
4185
|
+
"""Augmented retry class that also retries on 429 status codes for POST requests."""
|
4186
|
+
|
4187
|
+
def is_retry(
|
4188
|
+
self,
|
4189
|
+
method: str,
|
4190
|
+
status_code: int,
|
4191
|
+
has_retry_after: bool = False,
|
4192
|
+
) -> bool:
|
4193
|
+
if status_code == 429:
|
4194
|
+
return True
|
4195
|
+
return super().is_retry(method, status_code, has_retry_after)
|
4196
|
+
|
4182
4197
|
if self._session is None:
|
4183
4198
|
# We only need to initialize the session once over the lifetime
|
4184
4199
|
# of the client. We can swap the token out when it expires.
|
@@ -4208,7 +4223,7 @@ class RestZenStore(BaseZenStore):
|
|
4208
4223
|
# the timeout period.
|
4209
4224
|
# Connection Refused: If the server refuses the connection.
|
4210
4225
|
#
|
4211
|
-
retries =
|
4226
|
+
retries = AugmentedRetry(
|
4212
4227
|
connect=5,
|
4213
4228
|
read=8,
|
4214
4229
|
redirect=3,
|
@@ -4222,7 +4237,7 @@ class RestZenStore(BaseZenStore):
|
|
4222
4237
|
504, # Gateway Timeout
|
4223
4238
|
],
|
4224
4239
|
other=3,
|
4225
|
-
backoff_factor=
|
4240
|
+
backoff_factor=1,
|
4226
4241
|
)
|
4227
4242
|
self._session.mount("https://", HTTPAdapter(max_retries=retries))
|
4228
4243
|
self._session.mount("http://", HTTPAdapter(max_retries=retries))
|
@@ -4360,6 +4375,14 @@ class RestZenStore(BaseZenStore):
|
|
4360
4375
|
self.session.headers.update(
|
4361
4376
|
{source_context.name: source_context.get().value}
|
4362
4377
|
)
|
4378
|
+
# Add a request ID to the request headers
|
4379
|
+
request_id = str(uuid4())[:8]
|
4380
|
+
self.session.headers.update({"X-Request-ID": request_id})
|
4381
|
+
path = url.removeprefix(self.url)
|
4382
|
+
start_time = time.time()
|
4383
|
+
logger.debug(
|
4384
|
+
f"Sending {method} request to {path} with request ID {request_id}..."
|
4385
|
+
)
|
4363
4386
|
|
4364
4387
|
# If the server replies with a credentials validation (401 Unauthorized)
|
4365
4388
|
# error, we (re-)authenticate and retry the request here in the
|
@@ -4401,7 +4424,8 @@ class RestZenStore(BaseZenStore):
|
|
4401
4424
|
# request again, this time with a valid API token in the
|
4402
4425
|
# header.
|
4403
4426
|
logger.debug(
|
4404
|
-
f"The last request was not
|
4427
|
+
f"The last request with ID {request_id} was not "
|
4428
|
+
f"authenticated: {e}\n"
|
4405
4429
|
"Re-authenticating and retrying..."
|
4406
4430
|
)
|
4407
4431
|
self.authenticate()
|
@@ -4428,8 +4452,9 @@ class RestZenStore(BaseZenStore):
|
|
4428
4452
|
# that was rejected by the server. We attempt a
|
4429
4453
|
# re-authentication here and then retry the request.
|
4430
4454
|
logger.debug(
|
4431
|
-
"The last request
|
4432
|
-
|
4455
|
+
f"The last request with ID {request_id} was authenticated "
|
4456
|
+
"with an API token that was rejected by the server: "
|
4457
|
+
f"{e}\n"
|
4433
4458
|
"Re-authenticating and retrying..."
|
4434
4459
|
)
|
4435
4460
|
re_authenticated = True
|
@@ -4441,13 +4466,21 @@ class RestZenStore(BaseZenStore):
|
|
4441
4466
|
# The last request was made after re-authenticating but
|
4442
4467
|
# still failed. Bailing out.
|
4443
4468
|
logger.debug(
|
4444
|
-
f"The last request failed after
|
4469
|
+
f"The last request with ID {request_id} failed after "
|
4470
|
+
"re-authenticating: {e}\n"
|
4445
4471
|
"Bailing out..."
|
4446
4472
|
)
|
4447
4473
|
raise CredentialsNotValid(
|
4448
4474
|
"The current credentials are no longer valid. Please "
|
4449
4475
|
"log in again using 'zenml login'."
|
4450
4476
|
) from e
|
4477
|
+
finally:
|
4478
|
+
end_time = time.time()
|
4479
|
+
duration = (end_time - start_time) * 1000
|
4480
|
+
logger.debug(
|
4481
|
+
f"Request to {path} with request ID {request_id} took "
|
4482
|
+
f"{duration:.2f}ms."
|
4483
|
+
)
|
4451
4484
|
|
4452
4485
|
def get(
|
4453
4486
|
self,
|
@@ -4467,7 +4500,6 @@ class RestZenStore(BaseZenStore):
|
|
4467
4500
|
Returns:
|
4468
4501
|
The response body.
|
4469
4502
|
"""
|
4470
|
-
logger.debug(f"Sending GET request to {path}...")
|
4471
4503
|
return self._request(
|
4472
4504
|
"GET",
|
4473
4505
|
self.url + API + VERSION_1 + path,
|
@@ -4496,7 +4528,6 @@ class RestZenStore(BaseZenStore):
|
|
4496
4528
|
Returns:
|
4497
4529
|
The response body.
|
4498
4530
|
"""
|
4499
|
-
logger.debug(f"Sending DELETE request to {path}...")
|
4500
4531
|
return self._request(
|
4501
4532
|
"DELETE",
|
4502
4533
|
self.url + API + VERSION_1 + path,
|
@@ -4526,7 +4557,6 @@ class RestZenStore(BaseZenStore):
|
|
4526
4557
|
Returns:
|
4527
4558
|
The response body.
|
4528
4559
|
"""
|
4529
|
-
logger.debug(f"Sending POST request to {path}...")
|
4530
4560
|
return self._request(
|
4531
4561
|
"POST",
|
4532
4562
|
self.url + API + VERSION_1 + path,
|
@@ -4556,7 +4586,6 @@ class RestZenStore(BaseZenStore):
|
|
4556
4586
|
Returns:
|
4557
4587
|
The response body.
|
4558
4588
|
"""
|
4559
|
-
logger.debug(f"Sending PUT request to {path}...")
|
4560
4589
|
json = (
|
4561
4590
|
body.model_dump(mode="json", exclude_unset=True) if body else None
|
4562
4591
|
)
|
@@ -14,6 +14,7 @@
|
|
14
14
|
"""SQL Zen Store implementation."""
|
15
15
|
|
16
16
|
import base64
|
17
|
+
import inspect
|
17
18
|
import json
|
18
19
|
import logging
|
19
20
|
import math
|
@@ -21,6 +22,7 @@ import os
|
|
21
22
|
import random
|
22
23
|
import re
|
23
24
|
import sys
|
25
|
+
import threading
|
24
26
|
import time
|
25
27
|
from collections import defaultdict
|
26
28
|
from datetime import datetime
|
@@ -58,7 +60,7 @@ from pydantic import (
|
|
58
60
|
field_validator,
|
59
61
|
model_validator,
|
60
62
|
)
|
61
|
-
from sqlalchemy import func
|
63
|
+
from sqlalchemy import QueuePool, func
|
62
64
|
from sqlalchemy.engine import URL, Engine, make_url
|
63
65
|
from sqlalchemy.exc import (
|
64
66
|
ArgumentError,
|
@@ -66,6 +68,7 @@ from sqlalchemy.exc import (
|
|
66
68
|
)
|
67
69
|
from sqlalchemy.orm import Mapped, noload
|
68
70
|
from sqlalchemy.util import immutabledict
|
71
|
+
from sqlmodel import Session as SqlModelSession
|
69
72
|
|
70
73
|
# Important to note: The select function of SQLModel works slightly differently
|
71
74
|
# from the select function of sqlalchemy. If you input only one entity on the
|
@@ -74,7 +77,6 @@ from sqlalchemy.util import immutabledict
|
|
74
77
|
# the tuple. While this is convenient in most cases, in unique cases like using
|
75
78
|
# the "add_columns" functionality, one might encounter unexpected results.
|
76
79
|
from sqlmodel import (
|
77
|
-
Session,
|
78
80
|
SQLModel,
|
79
81
|
and_,
|
80
82
|
col,
|
@@ -412,6 +414,81 @@ def exponential_backoff_with_jitter(
|
|
412
414
|
return random.uniform(0, exponential_backoff)
|
413
415
|
|
414
416
|
|
417
|
+
class Session(SqlModelSession):
|
418
|
+
"""Session subclass that automatically tracks duration and calling context."""
|
419
|
+
|
420
|
+
def __enter__(self) -> "Session":
|
421
|
+
"""Enter the context manager.
|
422
|
+
|
423
|
+
Returns:
|
424
|
+
The SqlModel session.
|
425
|
+
"""
|
426
|
+
if logger.isEnabledFor(logging.DEBUG):
|
427
|
+
# Get the request ID from the current thread object
|
428
|
+
self.request_id = threading.current_thread().name
|
429
|
+
|
430
|
+
# Get SQLAlchemy connection pool info
|
431
|
+
assert isinstance(self.bind, Engine)
|
432
|
+
assert isinstance(self.bind.pool, QueuePool)
|
433
|
+
checked_out_connections = self.bind.pool.checkedout()
|
434
|
+
available_connections = self.bind.pool.checkedin()
|
435
|
+
overflow = self.bind.pool.overflow()
|
436
|
+
|
437
|
+
# Look up the stack to find the SQLZenStore method
|
438
|
+
for frame in inspect.stack():
|
439
|
+
if "self" in frame.frame.f_locals:
|
440
|
+
instance = frame.frame.f_locals["self"]
|
441
|
+
if isinstance(instance, SqlZenStore):
|
442
|
+
self.caller_method = (
|
443
|
+
f"{instance.__class__.__name__}.{frame.function}"
|
444
|
+
)
|
445
|
+
break
|
446
|
+
else:
|
447
|
+
self.caller_method = "unknown"
|
448
|
+
|
449
|
+
logger.debug(
|
450
|
+
f"[{self.request_id}] SQL STATS - "
|
451
|
+
f"'{self.caller_method}' started [ conn(active): "
|
452
|
+
f"{checked_out_connections} conn(idle): "
|
453
|
+
f"{available_connections} conn(overflow): {overflow} ]"
|
454
|
+
)
|
455
|
+
|
456
|
+
self.start_time = time.time()
|
457
|
+
|
458
|
+
return super().__enter__()
|
459
|
+
|
460
|
+
def __exit__(
|
461
|
+
self,
|
462
|
+
exc_type: Optional[Any],
|
463
|
+
exc_val: Optional[Any],
|
464
|
+
exc_tb: Optional[Any],
|
465
|
+
) -> None:
|
466
|
+
"""Exit the context manager.
|
467
|
+
|
468
|
+
Args:
|
469
|
+
exc_type: The exception type.
|
470
|
+
exc_val: The exception value.
|
471
|
+
exc_tb: The exception traceback.
|
472
|
+
"""
|
473
|
+
if logger.isEnabledFor(logging.DEBUG):
|
474
|
+
duration = (time.time() - self.start_time) * 1000
|
475
|
+
|
476
|
+
# Get SQLAlchemy connection pool info
|
477
|
+
assert isinstance(self.bind, Engine)
|
478
|
+
assert isinstance(self.bind.pool, QueuePool)
|
479
|
+
checked_out_connections = self.bind.pool.checkedout()
|
480
|
+
available_connections = self.bind.pool.checkedin()
|
481
|
+
overflow = self.bind.pool.overflow()
|
482
|
+
logger.debug(
|
483
|
+
f"[{self.request_id}] SQL STATS - "
|
484
|
+
f"'{self.caller_method}' completed in "
|
485
|
+
f"{duration:.2f}ms [ conn(active): "
|
486
|
+
f"{checked_out_connections} conn(idle): "
|
487
|
+
f"{available_connections} conn(overflow): {overflow} ]"
|
488
|
+
)
|
489
|
+
super().__exit__(exc_type, exc_val, exc_tb)
|
490
|
+
|
491
|
+
|
415
492
|
class SQLDatabaseDriver(StrEnum):
|
416
493
|
"""SQL database drivers supported by the SQL ZenML store."""
|
417
494
|
|
{zenml_nightly-0.82.1.dev20250522.dist-info → zenml_nightly-0.82.1.dev20250524.dist-info}/RECORD
RENAMED
@@ -1,5 +1,5 @@
|
|
1
1
|
zenml/README.md,sha256=827dekbOWAs1BpW7VF1a4d7EbwPbjwccX-2zdXBENZo,1777
|
2
|
-
zenml/VERSION,sha256=
|
2
|
+
zenml/VERSION,sha256=MexsT1YdyW8E4qM4hiGrG8oqIdUf48PHwk4-TGyiebg,19
|
3
3
|
zenml/__init__.py,sha256=CKEyepFK-7akXYiMrNVh92Nb01Cjs23w4_YyI6sgdc8,2242
|
4
4
|
zenml/actions/__init__.py,sha256=mrt6wPo73iKRxK754_NqsGyJ3buW7RnVeIGXr1xEw8Y,681
|
5
5
|
zenml/actions/base_action.py,sha256=UcaHev6BTuLDwuswnyaPjdA8AgUqB5xPZ-lRtuvf2FU,25553
|
@@ -55,7 +55,7 @@ zenml/cli/text_utils.py,sha256=bY1GIjoULt1cW2FyrPlMoAXNS2R7cSOjDFEZQqrpVQ8,3553
|
|
55
55
|
zenml/cli/user_management.py,sha256=sNnhaUxH-cHecbZBR1L0mEU0TnLNZHzI6ZBCUSQa7OY,13078
|
56
56
|
zenml/cli/utils.py,sha256=vMAb9f6GDfNVGmZWOz9UOyPRpKI3KfnYpRl_w9YUBNE,86501
|
57
57
|
zenml/cli/version.py,sha256=nm1iSU_1V6-MUwpMKeXcwFhLYGUMLswvQL67cEuCpxA,3635
|
58
|
-
zenml/client.py,sha256=
|
58
|
+
zenml/client.py,sha256=aCj2HEE6M724js6t4QJQq1S9R6C-_hxQe4UMUN0gjbw,293202
|
59
59
|
zenml/client_lazy_loader.py,sha256=MOBgS1ITYqGvPUnWQ6edn9s8Hr_72YfWbwEIfHKUr9g,7104
|
60
60
|
zenml/code_repositories/__init__.py,sha256=W5bDfzAG8OXIKZSV1L-VHuzMcSCYa9qzTdPb3jqfyYw,920
|
61
61
|
zenml/code_repositories/base_code_repository.py,sha256=Id6VjbUu8N3ZpNvBGhOgbahtoMiCAtYXed3G7YQ_iAc,5225
|
@@ -67,7 +67,7 @@ zenml/config/base_settings.py,sha256=itoLqc1cOwEYhgSGdZmSKSaBevQkvYH7NQh7PUamazc
|
|
67
67
|
zenml/config/build_configuration.py,sha256=jGGNwP0Cb7a80JXArNxDgxzxl9ytSZRtv-WW7_psLbM,6870
|
68
68
|
zenml/config/compiler.py,sha256=bK3LCDkrFc9SapJYH-vuQZ_o8scHNs-FdC5DblIUU4U,23024
|
69
69
|
zenml/config/constants.py,sha256=QvSgMwXWxtspcJ45CrFDP1ZY3w6gS3bIhXLOtIDAbZA,713
|
70
|
-
zenml/config/docker_settings.py,sha256=
|
70
|
+
zenml/config/docker_settings.py,sha256=WhoocDGn9cegEd4mKb_oo7pZ87sXZ0NvqkOVnWK8CY4,17985
|
71
71
|
zenml/config/global_config.py,sha256=ZD3WodfcWMBJZOl1FNn3ztzwilGDDv9EKdHClLqSO8s,29562
|
72
72
|
zenml/config/pipeline_configurations.py,sha256=7trCbElpqGGgawii2FrdLW8fKaAWCR8jACkNqdG_vcQ,3983
|
73
73
|
zenml/config/pipeline_run_configuration.py,sha256=Y9C5mVUH-w4gc1w1PCQFpjEmfBBpSMvb8riA_sL78hY,2311
|
@@ -77,7 +77,7 @@ zenml/config/retry_config.py,sha256=4UH1xqw0G6fSEbXSNKfmiFEkwadxQef9BGMe3JBm6NI,
|
|
77
77
|
zenml/config/schedule.py,sha256=-83j99U9OyiG7E322XWA7QvuLSwQzF21whwpeiF0b30,5348
|
78
78
|
zenml/config/secret_reference_mixin.py,sha256=YvY68MTd1gE23IVprf0BLkNn62hoxcvb5nqGgc8jMkU,5871
|
79
79
|
zenml/config/secrets_store_config.py,sha256=y05zqyQhr_DGrs3IfBGa_FRoZ043hSYRT5wzrx-zHTU,2818
|
80
|
-
zenml/config/server_config.py,sha256=
|
80
|
+
zenml/config/server_config.py,sha256=DYYQ10HddkvlCWobBpwpg8ggO6imMZCnfFxzqlkQg_U,32336
|
81
81
|
zenml/config/settings_resolver.py,sha256=PR9BRm_x1dy7nVKa9UqpeFdck8IEATSW6aWT8FKd-DI,4278
|
82
82
|
zenml/config/source.py,sha256=RzUw8lin8QztUjz-AdoCzVM5Om_cSSPuroaPx-qAO4w,8226
|
83
83
|
zenml/config/step_configurations.py,sha256=vjGtCoHneUJeaMkTiuzl7TTPBO94MefxNlFIAL1l5rU,13038
|
@@ -85,7 +85,7 @@ zenml/config/step_run_info.py,sha256=KiVRSTtKmZ1GbvseDTap2imr7XwMHD3jSFVpyLNEK1I
|
|
85
85
|
zenml/config/store_config.py,sha256=Cla5p5dTB6nNlo8_OZDs9hod5hspi64vxwtZj882XgU,3559
|
86
86
|
zenml/config/strict_base_model.py,sha256=t_ULrtJF2eW7TgyYBRobl1fscwwIZXATYky8ER97ev4,860
|
87
87
|
zenml/console.py,sha256=hj_KerPQKwnyKACj0ehSqUQX0mGVCJBKE1QvCt6ik3A,1160
|
88
|
-
zenml/constants.py,sha256=
|
88
|
+
zenml/constants.py,sha256=O8b67hvsObx42r2REHA_X4XqDwqm_Hcsq7hv6q96HDs,16650
|
89
89
|
zenml/container_registries/__init__.py,sha256=ZSPbBIOnzhg88kQSpYgKe_POLuru14m629665-kAVAA,2200
|
90
90
|
zenml/container_registries/azure_container_registry.py,sha256=t1sfDa94Vzbyqtb1iPFNutJ2EXV5_p9CUNITasoiQ70,2667
|
91
91
|
zenml/container_registries/base_container_registry.py,sha256=-9RIkD6oXNPaU59R3PB_PtyCqsFoLPLSn5xYZmEmzbc,8915
|
@@ -100,8 +100,8 @@ zenml/entrypoints/base_entrypoint_configuration.py,sha256=t7sU2MEEPVomX9dZCpPhk1
|
|
100
100
|
zenml/entrypoints/entrypoint.py,sha256=XNgXBCMKoidmP0_AYgMpqo-neG8Y8jG0rj43ofTDZ9E,2033
|
101
101
|
zenml/entrypoints/pipeline_entrypoint_configuration.py,sha256=To-vTP29qAE36ndJDF1fRw9wL2Nk2bsBuO-ayAwvSmo,1646
|
102
102
|
zenml/entrypoints/step_entrypoint_configuration.py,sha256=fJuTvJnGuhKc60CH1VMQL5EHomGXkYZulv6pVgd9M6w,7316
|
103
|
-
zenml/enums.py,sha256=
|
104
|
-
zenml/environment.py,sha256=
|
103
|
+
zenml/enums.py,sha256=xXp6VkaY03NlGKEBs9Bf7oVfUrtESECmhkQYAaKRS5g,11239
|
104
|
+
zenml/environment.py,sha256=_iS9o4rTxRCjLocSw_LEe9pZ9jfyMhXBY47LIUF3VCI,12255
|
105
105
|
zenml/event_hub/__init__.py,sha256=-fD9mPOslf4J-_XFBPp5gYlPz7-6ZaAKHa5jxf_nyAo,757
|
106
106
|
zenml/event_hub/base_event_hub.py,sha256=PqKrnvOye0UUS3s09zGgjI5dtj0IwzEBDbavA_PgfZQ,6579
|
107
107
|
zenml/event_hub/event_hub.py,sha256=e1eCRB1qAabOFIx2OJCAoNqN1QQIW0CSTukiWuGMOi8,6502
|
@@ -253,7 +253,7 @@ zenml/integrations/feast/feature_stores/__init__.py,sha256=Wi3NBBBPJg6CjgtxmBjoU
|
|
253
253
|
zenml/integrations/feast/feature_stores/feast_feature_store.py,sha256=jV6WznuKT3y1aikI3OEwoI8r_l8bEu5waX0LKePPuU8,5880
|
254
254
|
zenml/integrations/feast/flavors/__init__.py,sha256=gbCZ4tKgLZSI4-gzOCR2xihiPNmpe-lMUxwvMrhYL-w,858
|
255
255
|
zenml/integrations/feast/flavors/feast_feature_store_flavor.py,sha256=E0k2iwgNti8lOVr9W8n2nTVQQf2wSeJWeaQD1vwAIbU,3060
|
256
|
-
zenml/integrations/gcp/__init__.py,sha256=
|
256
|
+
zenml/integrations/gcp/__init__.py,sha256=9o7TXcQlNebImis821VXcOVFxKau86mbQ7H8d8e6mIw,2984
|
257
257
|
zenml/integrations/gcp/artifact_stores/__init__.py,sha256=zYQkZBI4-COGX-E0NS7G-hLT88wbQBnYY6Oh1gruSRs,798
|
258
258
|
zenml/integrations/gcp/artifact_stores/gcp_artifact_store.py,sha256=XfSIJ4HtsZvaUrUtzXvUp7QHr3WbgVDNsz7_q1h-DCo,10988
|
259
259
|
zenml/integrations/gcp/constants.py,sha256=ZBQS_ZEjerUrJq-hH3UusgZAvB45FLgxNv11TSt3qhw,1334
|
@@ -271,7 +271,7 @@ zenml/integrations/gcp/image_builders/gcp_image_builder.py,sha256=5T6BXsHxLhvp1B
|
|
271
271
|
zenml/integrations/gcp/orchestrators/__init__.py,sha256=6xLFJKZKQk73fHPF-XdpbQO87zjQNGTsNHjJjLfG_Kg,805
|
272
272
|
zenml/integrations/gcp/orchestrators/vertex_orchestrator.py,sha256=qoMCr36buZUz0y4CyTFQde3RDslkaGLAG0FjXc0XEPU,42100
|
273
273
|
zenml/integrations/gcp/service_connectors/__init__.py,sha256=fdydawaor8KAtMYvRZieiTuA1i5QATxXXgI-yV1lsn8,788
|
274
|
-
zenml/integrations/gcp/service_connectors/gcp_service_connector.py,sha256=
|
274
|
+
zenml/integrations/gcp/service_connectors/gcp_service_connector.py,sha256=9u-vEHbmSyN5IGwYI8v39TcFZg5ObgkxlbwSPz-e5zE,95018
|
275
275
|
zenml/integrations/gcp/step_operators/__init__.py,sha256=iPkob2LtPIQ-OHszhbNz_ojhoovL6SprmTx37It4EJ8,808
|
276
276
|
zenml/integrations/gcp/step_operators/vertex_step_operator.py,sha256=X8CCniyAo7NHiy3Mv_YSKQ4Hw3UYMXob6B3uWKsCJ-0,13567
|
277
277
|
zenml/integrations/gcp/vertex_custom_job_parameters.py,sha256=B5RLkw7KDOi4ZfWHFnC6TGLphXMzToMjROxszCEAS9c,3676
|
@@ -317,7 +317,7 @@ zenml/integrations/hyperai/__init__.py,sha256=6ed5rgRgiRr2Ksi3DDFUaNQwPkCfE4BRSn
|
|
317
317
|
zenml/integrations/hyperai/flavors/__init__.py,sha256=PUGBPmQ7y3H7QU2zAj7Ri0rrUBbOWnM_L59AIVWUYwU,800
|
318
318
|
zenml/integrations/hyperai/flavors/hyperai_orchestrator_flavor.py,sha256=erjbYJ5E3qkifyGd_ArtbY1JdfyOEz4vgl9QrZK0R9Y,5578
|
319
319
|
zenml/integrations/hyperai/orchestrators/__init__.py,sha256=kSYpMZPEWwNu2vxoOC6PeyQ9RLzsPAgTHxL35K36MiE,784
|
320
|
-
zenml/integrations/hyperai/orchestrators/hyperai_orchestrator.py,sha256=
|
320
|
+
zenml/integrations/hyperai/orchestrators/hyperai_orchestrator.py,sha256=6Z4GAXfkC2Ia4czQrgCy85xuocC9TYXTLmaNP8xxv_s,20287
|
321
321
|
zenml/integrations/hyperai/service_connectors/__init__.py,sha256=oHuCNC09z5C7Wlb3vV1d4zJjftttHg364eoEBVRDOdo,803
|
322
322
|
zenml/integrations/hyperai/service_connectors/hyperai_service_connector.py,sha256=7Ql5cVoSY3SE4j7uzeVi5TWuoKFDvsHFTn72k9_wPUY,13400
|
323
323
|
zenml/integrations/integration.py,sha256=ljUK2GHy-LNqsdloOvf8mIfUQsqoi0yBJYocMyS-DX8,6879
|
@@ -337,7 +337,7 @@ zenml/integrations/kubernetes/flavors/__init__.py,sha256=a5gU45qCj3FkLwl_uVjlIkL
|
|
337
337
|
zenml/integrations/kubernetes/flavors/kubernetes_orchestrator_flavor.py,sha256=JH_Kxfh1eoiSlz-OpixJ6itEsDiGPvbkA5_PffMTa54,10215
|
338
338
|
zenml/integrations/kubernetes/flavors/kubernetes_step_operator_flavor.py,sha256=xFO7cSusji-mgbRrt4mU29gdyC9iEjEHKtomdFLp9mM,6265
|
339
339
|
zenml/integrations/kubernetes/orchestrators/__init__.py,sha256=TJID3OTieZBox36WpQpzD0jdVRA_aZVcs_bNtfXS8ik,811
|
340
|
-
zenml/integrations/kubernetes/orchestrators/kube_utils.py,sha256=
|
340
|
+
zenml/integrations/kubernetes/orchestrators/kube_utils.py,sha256=N66GH5ac22Xm_A3nr162kbFBhMeypSFaQjOQRHlGXIQ,18942
|
341
341
|
zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator.py,sha256=qubV13fSGE_vo8wSzIfcp66McclLw-zDtmB4yCCaS08,25974
|
342
342
|
zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint.py,sha256=Nd1YZsP6FZ4LTpoeFIM3YlFUugHX7F9axb7U9ZgGNdQ,12692
|
343
343
|
zenml/integrations/kubernetes/orchestrators/kubernetes_orchestrator_entrypoint_configuration.py,sha256=KjHfQK9VQEQkkkM2i9w51AzqolgIU01M5dgb2YGamvY,2754
|
@@ -596,7 +596,7 @@ zenml/login/server_info.py,sha256=-_sK2L-curHdzUv1JDOwF6GoEeAXT5vFZN0J-5Ug4wU,16
|
|
596
596
|
zenml/login/web_login.py,sha256=Kq_fA9UQEravB2DtAkMmNvDttk8xppnxV617tCYUl6U,9186
|
597
597
|
zenml/materializers/__init__.py,sha256=C3lZaTmIFxwIPwCKF8oLQUkLaX2o7Dbj9hvYVFrSzt8,1758
|
598
598
|
zenml/materializers/base_materializer.py,sha256=M4hwkw7PB0LskCE92r-S35011l7DlFemit-EuUCW3Nc,14002
|
599
|
-
zenml/materializers/built_in_materializer.py,sha256=
|
599
|
+
zenml/materializers/built_in_materializer.py,sha256=HgcCHg3GpuQ4t-jecYcdg0kldUfLeUJvIpm8-wcS11I,17189
|
600
600
|
zenml/materializers/cloudpickle_materializer.py,sha256=x8a6jEMTky6N2YVHiwrnGWSfVJUpiy-4kQsD2Aqj_E0,4837
|
601
601
|
zenml/materializers/materializer_registry.py,sha256=ic-aWhJ2Ex9F_rml2dDVAxhRfW3nd71QMxzfTPP6BIM,4002
|
602
602
|
zenml/materializers/numpy_materializer.py,sha256=OLcHF9Z0tAqQ_U8TraA0vGmZjHoT7eT_XevncIutt0M,1715
|
@@ -805,7 +805,7 @@ zenml/utils/yaml_utils.py,sha256=RvEr-N84wwG1Aq8rfdPAP7lev_SEaSQlqcL9fB_vHk8,584
|
|
805
805
|
zenml/zen_server/__init__.py,sha256=WyltI9TzFW2mEHZVOs6alLWMCQrrZaFALtrQXs83STA,1355
|
806
806
|
zenml/zen_server/auth.py,sha256=sJ_UAG58Qa0sUx8P1QkWmwp5zfAafFnm8WBHAOnPi4w,40070
|
807
807
|
zenml/zen_server/cache.py,sha256=Tc4TSugmsU1bhThxlYfE8rv0KmltIX1CcVHgzrJ0Eus,6633
|
808
|
-
zenml/zen_server/cloud_utils.py,sha256=
|
808
|
+
zenml/zen_server/cloud_utils.py,sha256=vKsjWadgPctZDtfeG9qJC-5gKOir_tZsglYMBkC8QqU,11874
|
809
809
|
zenml/zen_server/csrf.py,sha256=Jsbi_IKriWCOuquSYTOEWqSXiGORJATIhR5Wrkm4XzQ,2684
|
810
810
|
zenml/zen_server/dashboard/assets/404-_AtuLtaX.js,sha256=Y2jAo7K2feru-k7QcMxjnfUNkDrO7xv341YyKKpjZiU,1033
|
811
811
|
zenml/zen_server/dashboard/assets/@radix-C7hRs6Kx.js,sha256=cNA9UX8LGrKUQubGrls3E3Wq1fCrlK51W14yh22FE_U,296700
|
@@ -1052,7 +1052,7 @@ zenml/zen_server/routers/server_endpoints.py,sha256=Aci-7TB5VZylmasEYc2tVpG7Npgt
|
|
1052
1052
|
zenml/zen_server/routers/service_accounts_endpoints.py,sha256=q-vFzzYYorqaOPUnESQU06h2I-BaZvIyPxCAhQ4gLPw,12086
|
1053
1053
|
zenml/zen_server/routers/service_connectors_endpoints.py,sha256=CkwhnNI49_64lqG7aWHtyE41iqKlY-lumiOrWU7aDU0,17570
|
1054
1054
|
zenml/zen_server/routers/service_endpoints.py,sha256=37CY-22h4hTscEkBByKJqdHcOWsP2lTGAiY2WgBPB0w,5566
|
1055
|
-
zenml/zen_server/routers/stack_components_endpoints.py,sha256
|
1055
|
+
zenml/zen_server/routers/stack_components_endpoints.py,sha256=MBQ3iz29WN1n9T_BLpd2BtWxWoLIBP6QT7BfaAkGEMY,8580
|
1056
1056
|
zenml/zen_server/routers/stack_deployment_endpoints.py,sha256=r7N4UaDAwj9ACYfdyUofRHpNPRrzWl-vmGoSErN9oYI,5381
|
1057
1057
|
zenml/zen_server/routers/stacks_endpoints.py,sha256=ci-WZ774Q2T3-XN6cZARJ95wmRUW2-W1mh7gKlbNE54,7527
|
1058
1058
|
zenml/zen_server/routers/steps_endpoints.py,sha256=KZpVsEsycLMmiEGnwT2EkmQvaTIIyL4QOUJBXMu7NSo,8239
|
@@ -1066,8 +1066,8 @@ zenml/zen_server/template_execution/__init__.py,sha256=79knXLKfegsvVSVSWecpqrepq
|
|
1066
1066
|
zenml/zen_server/template_execution/runner_entrypoint_configuration.py,sha256=Y8aYJhqqs8Kv8I1q-dM1WemS5VBIfyoaaYH_YkzC7iY,1541
|
1067
1067
|
zenml/zen_server/template_execution/utils.py,sha256=CiN7d8jCUTV5oKuoNF9Z_eDkitZrhL6V_Tz4CjU7em8,19410
|
1068
1068
|
zenml/zen_server/template_execution/workload_manager_interface.py,sha256=CL9c7z8ajuZE01DaHmdCDCZmsroDcTarvN-nE8jv6qQ,2590
|
1069
|
-
zenml/zen_server/utils.py,sha256=
|
1070
|
-
zenml/zen_server/zen_server_api.py,sha256=
|
1069
|
+
zenml/zen_server/utils.py,sha256=roJuND2KMVPue7fztVOKn4Tvoj1bvQ54TFpXgIa78Y0,20474
|
1070
|
+
zenml/zen_server/zen_server_api.py,sha256=Bhdn2AwGINEpKJiTL3QpC0EK8pvgGNK1IZNJr6E6ijE,23229
|
1071
1071
|
zenml/zen_stores/__init__.py,sha256=6LTgH6XwDeDxKqVJ1JTfGhmS8II1NLopPloINGmdyI0,691
|
1072
1072
|
zenml/zen_stores/base_zen_store.py,sha256=AplsW2NR-G9_CU54XvNTQJo4W0KJ5TJV22cjKW4n2BY,16124
|
1073
1073
|
zenml/zen_stores/migrations/README.md,sha256=x04jsb6EOP6PBEGMQlDELiqKEham2O-iztAs9AylMFc,4898
|
@@ -1273,7 +1273,7 @@ zenml/zen_stores/migrations/versions/f49904a80aa7_increase_length_of_artifact_ta
|
|
1273
1273
|
zenml/zen_stores/migrations/versions/f76a368a25a5_add_stack_description.py,sha256=u8fRomaasFeGhxvM2zU-Ab-AEpVsWm5zRcixxKFXdRw,904
|
1274
1274
|
zenml/zen_stores/migrations/versions/fbd7f18ced1e_increase_step_run_field_lengths.py,sha256=kn-ng5EHe_mmLfffIFbz7T59z-to3oMx8III_4wOsz4,1956
|
1275
1275
|
zenml/zen_stores/migrations/versions/ff538a321a92_migrate_onboarding_state.py,sha256=gsUFLJQ32_o9U35JCVqkqJVVk-zfq3yel25hXhzVFm4,3829
|
1276
|
-
zenml/zen_stores/rest_zen_store.py,sha256=
|
1276
|
+
zenml/zen_stores/rest_zen_store.py,sha256=ljz1iIc8szDh1dlX1129Q817gcDBuzFzQnw6hkSMYCg,159628
|
1277
1277
|
zenml/zen_stores/schemas/__init__.py,sha256=4EXqExiVyxdnGxhQ_Hz79mOdRuMD0LsGlw0PaP2Ef6o,4333
|
1278
1278
|
zenml/zen_stores/schemas/action_schemas.py,sha256=sv2J2TP12MeyGPQR2JsOPIivbPQ5OImg64exYS7CZBM,6496
|
1279
1279
|
zenml/zen_stores/schemas/api_key_schemas.py,sha256=0pK7b9HlJuQL3DuKT4eGjFb87tyd4x-E2VyxJLpRv3o,7459
|
@@ -1316,11 +1316,11 @@ zenml/zen_stores/secrets_stores/hashicorp_secrets_store.py,sha256=5err1a-TrV3SR5
|
|
1316
1316
|
zenml/zen_stores/secrets_stores/secrets_store_interface.py,sha256=Q2Jbnt2Pp7NGlR-u1YBfRZV2g8su2Fd0ArBMdksAE-Q,2819
|
1317
1317
|
zenml/zen_stores/secrets_stores/service_connector_secrets_store.py,sha256=S87ne23D08PAwtfRVlVnBn8R0ilTpEh6r8blauNV5WQ,6941
|
1318
1318
|
zenml/zen_stores/secrets_stores/sql_secrets_store.py,sha256=LPFW757WCJLP1S8vrvjsrl2Tf1yo281xUTjSBsos4qk,8788
|
1319
|
-
zenml/zen_stores/sql_zen_store.py,sha256=
|
1319
|
+
zenml/zen_stores/sql_zen_store.py,sha256=jsx_1_MJZE4dnNXxj8bAMX8pT5JfoJliADSBF9n6kJc,444809
|
1320
1320
|
zenml/zen_stores/template_utils.py,sha256=GbJ7LgGVYHSCKPEA8RNTxPoVTWqpC77F_lGzjJ4O1Fw,9220
|
1321
1321
|
zenml/zen_stores/zen_store_interface.py,sha256=fF_uL_FplnvGvM5o3jOQ8i1zHXhuhKLL2n4nvIKSR7E,92090
|
1322
|
-
zenml_nightly-0.82.1.
|
1323
|
-
zenml_nightly-0.82.1.
|
1324
|
-
zenml_nightly-0.82.1.
|
1325
|
-
zenml_nightly-0.82.1.
|
1326
|
-
zenml_nightly-0.82.1.
|
1322
|
+
zenml_nightly-0.82.1.dev20250524.dist-info/LICENSE,sha256=wbnfEnXnafPbqwANHkV6LUsPKOtdpsd-SNw37rogLtc,11359
|
1323
|
+
zenml_nightly-0.82.1.dev20250524.dist-info/METADATA,sha256=OFFcRSYChQvW4vCBOO5FJDfx8UkZbhmq9DKwtmBN2s0,24317
|
1324
|
+
zenml_nightly-0.82.1.dev20250524.dist-info/WHEEL,sha256=b4K_helf-jlQoXBBETfwnf4B04YC67LOev0jo4fX5m8,88
|
1325
|
+
zenml_nightly-0.82.1.dev20250524.dist-info/entry_points.txt,sha256=QK3ETQE0YswAM2mWypNMOv8TLtr7EjnqAFq1br_jEFE,43
|
1326
|
+
zenml_nightly-0.82.1.dev20250524.dist-info/RECORD,,
|
{zenml_nightly-0.82.1.dev20250522.dist-info → zenml_nightly-0.82.1.dev20250524.dist-info}/LICENSE
RENAMED
File without changes
|
{zenml_nightly-0.82.1.dev20250522.dist-info → zenml_nightly-0.82.1.dev20250524.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|