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.
- zenml/VERSION +1 -1
- zenml/__init__.py +2 -0
- zenml/analytics/context.py +7 -0
- zenml/artifacts/utils.py +0 -2
- zenml/cli/login.py +6 -0
- zenml/cli/model.py +7 -15
- zenml/cli/secret.py +47 -44
- zenml/cli/service_connectors.py +0 -1
- zenml/cli/stack.py +0 -1
- zenml/cli/tag.py +3 -5
- zenml/cli/utils.py +25 -23
- zenml/cli/workspace.py +79 -5
- zenml/client.py +618 -348
- zenml/config/global_config.py +16 -3
- zenml/config/pipeline_configurations.py +3 -2
- zenml/config/pipeline_run_configuration.py +2 -1
- zenml/config/secret_reference_mixin.py +1 -1
- zenml/constants.py +1 -3
- zenml/enums.py +0 -7
- zenml/event_hub/event_hub.py +3 -1
- zenml/exceptions.py +0 -24
- zenml/integrations/aws/orchestrators/sagemaker_orchestrator.py +5 -3
- zenml/integrations/bitbucket/plugins/event_sources/bitbucket_webhook_event_source.py +1 -4
- zenml/integrations/github/plugins/event_sources/github_webhook_event_source.py +1 -4
- zenml/integrations/mlflow/steps/mlflow_registry.py +1 -1
- zenml/integrations/seldon/model_deployers/seldon_model_deployer.py +1 -1
- zenml/integrations/wandb/flavors/wandb_experiment_tracker_flavor.py +3 -3
- zenml/model/model.py +8 -8
- zenml/models/__init__.py +18 -1
- zenml/models/v2/base/base.py +0 -5
- zenml/models/v2/base/filter.py +1 -1
- zenml/models/v2/base/scoped.py +104 -121
- zenml/models/v2/core/api_key.py +1 -1
- zenml/models/v2/core/artifact.py +31 -18
- zenml/models/v2/core/artifact_version.py +42 -25
- zenml/models/v2/core/component.py +22 -33
- zenml/models/v2/core/device.py +3 -2
- zenml/models/v2/core/event_source.py +2 -2
- zenml/models/v2/core/flavor.py +19 -47
- zenml/models/v2/core/logs.py +1 -2
- zenml/models/v2/core/model.py +7 -4
- zenml/models/v2/core/model_version.py +36 -27
- zenml/models/v2/core/pipeline.py +1 -1
- zenml/models/v2/core/pipeline_build.py +18 -0
- zenml/models/v2/core/pipeline_run.py +5 -13
- zenml/models/v2/core/run_template.py +1 -2
- zenml/models/v2/core/schedule.py +0 -9
- zenml/models/v2/core/secret.py +93 -127
- zenml/models/v2/core/server_settings.py +2 -2
- zenml/models/v2/core/service.py +43 -12
- zenml/models/v2/core/service_connector.py +14 -16
- zenml/models/v2/core/stack.py +24 -26
- zenml/models/v2/core/step_run.py +3 -15
- zenml/models/v2/core/tag.py +41 -15
- zenml/models/v2/core/user.py +19 -2
- zenml/models/v2/misc/statistics.py +45 -0
- zenml/models/v2/misc/tag.py +27 -0
- zenml/orchestrators/cache_utils.py +1 -1
- zenml/orchestrators/input_utils.py +1 -0
- zenml/orchestrators/step_launcher.py +0 -1
- zenml/orchestrators/step_run_utils.py +0 -2
- zenml/orchestrators/step_runner.py +10 -1
- zenml/pipelines/build_utils.py +4 -2
- zenml/pipelines/pipeline_decorator.py +3 -2
- zenml/pipelines/pipeline_definition.py +4 -5
- zenml/pipelines/run_utils.py +3 -3
- zenml/service_connectors/service_connector.py +0 -7
- zenml/service_connectors/service_connector_utils.py +0 -1
- zenml/stack/authentication_mixin.py +1 -1
- zenml/stack/flavor.py +3 -14
- zenml/stack/stack_component.py +1 -5
- zenml/steps/step_context.py +19 -0
- zenml/utils/string_utils.py +1 -1
- zenml/utils/tag_utils.py +642 -0
- zenml/zen_server/cloud_utils.py +21 -0
- zenml/zen_server/exceptions.py +0 -6
- zenml/zen_server/rbac/endpoint_utils.py +134 -46
- zenml/zen_server/rbac/models.py +65 -3
- zenml/zen_server/rbac/rbac_interface.py +9 -0
- zenml/zen_server/rbac/rbac_sql_zen_store.py +15 -7
- zenml/zen_server/rbac/utils.py +156 -29
- zenml/zen_server/rbac/zenml_cloud_rbac.py +43 -11
- zenml/zen_server/routers/actions_endpoints.py +3 -5
- zenml/zen_server/routers/artifact_endpoint.py +0 -5
- zenml/zen_server/routers/artifact_version_endpoints.py +15 -9
- zenml/zen_server/routers/auth_endpoints.py +22 -7
- zenml/zen_server/routers/code_repositories_endpoints.py +56 -3
- zenml/zen_server/routers/devices_endpoints.py +0 -4
- zenml/zen_server/routers/event_source_endpoints.py +0 -5
- zenml/zen_server/routers/flavors_endpoints.py +0 -5
- zenml/zen_server/routers/logs_endpoints.py +0 -1
- zenml/zen_server/routers/model_versions_endpoints.py +102 -23
- zenml/zen_server/routers/models_endpoints.py +51 -68
- zenml/zen_server/routers/pipeline_builds_endpoints.py +58 -4
- zenml/zen_server/routers/pipeline_deployments_endpoints.py +58 -4
- zenml/zen_server/routers/pipelines_endpoints.py +73 -4
- zenml/zen_server/routers/plugin_endpoints.py +0 -1
- zenml/zen_server/routers/run_metadata_endpoints.py +99 -0
- zenml/zen_server/routers/run_templates_endpoints.py +66 -3
- zenml/zen_server/routers/runs_endpoints.py +60 -8
- zenml/zen_server/routers/schedule_endpoints.py +69 -6
- zenml/zen_server/routers/secrets_endpoints.py +40 -4
- zenml/zen_server/routers/server_endpoints.py +53 -1
- zenml/zen_server/routers/service_accounts_endpoints.py +14 -15
- zenml/zen_server/routers/service_connectors_endpoints.py +96 -14
- zenml/zen_server/routers/service_endpoints.py +20 -7
- zenml/zen_server/routers/stack_components_endpoints.py +68 -7
- zenml/zen_server/routers/stacks_endpoints.py +98 -7
- zenml/zen_server/routers/steps_endpoints.py +17 -11
- zenml/zen_server/routers/tag_resource_endpoints.py +115 -0
- zenml/zen_server/routers/tags_endpoints.py +6 -17
- zenml/zen_server/routers/triggers_endpoints.py +5 -8
- zenml/zen_server/routers/users_endpoints.py +47 -12
- zenml/zen_server/routers/workspaces_endpoints.py +56 -1285
- zenml/zen_server/template_execution/utils.py +5 -4
- zenml/zen_server/utils.py +21 -0
- zenml/zen_server/zen_server_api.py +4 -0
- zenml/zen_stores/base_zen_store.py +29 -44
- zenml/zen_stores/migrations/versions/0392807467dc_add_build_duration.py +34 -0
- zenml/zen_stores/migrations/versions/1cb6477f72d6_move_artifact_save_type.py +20 -10
- zenml/zen_stores/migrations/versions/1f9d1cd00b90_add_unique_name_constraints.py +231 -0
- zenml/zen_stores/migrations/versions/288f4fb6e112_make_tags_user_scoped.py +74 -0
- zenml/zen_stores/migrations/versions/2e695a26fe7a_add_user_default_workspace.py +45 -0
- zenml/zen_stores/migrations/versions/3b1776345020_remove_workspace_from_globals.py +81 -0
- zenml/zen_stores/migrations/versions/41b28cae31ce_make_artifacts_workspace_scoped.py +136 -0
- zenml/zen_stores/migrations/versions/9e7bf0970266_adding_exclusive_attribute_to_tags.py +47 -0
- zenml/zen_stores/migrations/versions/b557b2871693_update_step_run_input_types.py +8 -4
- zenml/zen_stores/migrations/versions/cc269488e5a9_separate_run_metadata.py +12 -6
- zenml/zen_stores/migrations/versions/f1d723fd723b_add_secret_private_attr.py +61 -0
- zenml/zen_stores/migrations/versions/f76a368a25a5_add_stack_description.py +35 -0
- zenml/zen_stores/rest_zen_store.py +172 -171
- zenml/zen_stores/schemas/action_schemas.py +8 -1
- zenml/zen_stores/schemas/api_key_schemas.py +8 -1
- zenml/zen_stores/schemas/artifact_schemas.py +28 -1
- zenml/zen_stores/schemas/code_repository_schemas.py +8 -1
- zenml/zen_stores/schemas/component_schemas.py +9 -14
- zenml/zen_stores/schemas/event_source_schemas.py +8 -1
- zenml/zen_stores/schemas/flavor_schemas.py +14 -20
- zenml/zen_stores/schemas/model_schemas.py +3 -0
- zenml/zen_stores/schemas/pipeline_build_schemas.py +4 -0
- zenml/zen_stores/schemas/pipeline_deployment_schemas.py +3 -1
- zenml/zen_stores/schemas/pipeline_run_schemas.py +0 -3
- zenml/zen_stores/schemas/run_template_schemas.py +8 -4
- zenml/zen_stores/schemas/schedule_schema.py +9 -14
- zenml/zen_stores/schemas/secret_schemas.py +15 -25
- zenml/zen_stores/schemas/service_connector_schemas.py +8 -17
- zenml/zen_stores/schemas/service_schemas.py +0 -1
- zenml/zen_stores/schemas/stack_schemas.py +12 -15
- zenml/zen_stores/schemas/step_run_schemas.py +7 -8
- zenml/zen_stores/schemas/tag_schemas.py +30 -2
- zenml/zen_stores/schemas/trigger_schemas.py +8 -1
- zenml/zen_stores/schemas/user_schemas.py +24 -2
- zenml/zen_stores/schemas/utils.py +16 -0
- zenml/zen_stores/schemas/workspace_schemas.py +7 -25
- zenml/zen_stores/secrets_stores/service_connector_secrets_store.py +0 -3
- zenml/zen_stores/sql_zen_store.py +2905 -2280
- zenml/zen_stores/template_utils.py +1 -1
- zenml/zen_stores/zen_store_interface.py +82 -58
- {zenml_nightly-0.75.0.dev20250311.dist-info → zenml_nightly-0.75.0.dev20250313.dist-info}/METADATA +1 -1
- {zenml_nightly-0.75.0.dev20250311.dist-info → zenml_nightly-0.75.0.dev20250313.dist-info}/RECORD +163 -149
- {zenml_nightly-0.75.0.dev20250311.dist-info → zenml_nightly-0.75.0.dev20250313.dist-info}/LICENSE +0 -0
- {zenml_nightly-0.75.0.dev20250311.dist-info → zenml_nightly-0.75.0.dev20250313.dist-info}/WHEEL +0 -0
- {zenml_nightly-0.75.0.dev20250311.dist-info → zenml_nightly-0.75.0.dev20250313.dist-info}/entry_points.txt +0 -0
zenml/models/v2/base/scoped.py
CHANGED
@@ -27,8 +27,9 @@ from typing import (
|
|
27
27
|
)
|
28
28
|
from uuid import UUID
|
29
29
|
|
30
|
-
from pydantic import Field
|
30
|
+
from pydantic import Field, model_validator
|
31
31
|
|
32
|
+
from zenml.logger import get_logger
|
32
33
|
from zenml.models.v2.base.base import (
|
33
34
|
BaseDatedResponseBody,
|
34
35
|
BaseIdentifiedResponse,
|
@@ -47,6 +48,9 @@ if TYPE_CHECKING:
|
|
47
48
|
|
48
49
|
AnySchema = TypeVar("AnySchema", bound=BaseSchema)
|
49
50
|
|
51
|
+
logger = get_logger(__name__)
|
52
|
+
|
53
|
+
|
50
54
|
# ---------------------- Request Models ----------------------
|
51
55
|
|
52
56
|
|
@@ -56,7 +60,14 @@ class UserScopedRequest(BaseRequest):
|
|
56
60
|
Used as a base class for all domain models that are "owned" by a user.
|
57
61
|
"""
|
58
62
|
|
59
|
-
user: UUID = Field(
|
63
|
+
user: Optional[UUID] = Field(
|
64
|
+
default=None,
|
65
|
+
title="The id of the user that created this resource. Set "
|
66
|
+
"automatically by the server.",
|
67
|
+
# This field is set automatically by the server, so the client doesn't
|
68
|
+
# need to set it and it will not be serialized.
|
69
|
+
exclude=True,
|
70
|
+
)
|
60
71
|
|
61
72
|
def get_analytics_metadata(self) -> Dict[str, Any]:
|
62
73
|
"""Fetches the analytics metadata for user scoped models.
|
@@ -157,7 +168,6 @@ class UserScopedFilter(BaseFilter):
|
|
157
168
|
]
|
158
169
|
CLI_EXCLUDE_FIELDS: ClassVar[List[str]] = [
|
159
170
|
*BaseFilter.CLI_EXCLUDE_FIELDS,
|
160
|
-
"user_id",
|
161
171
|
"scope_user",
|
162
172
|
]
|
163
173
|
CUSTOM_SORTING_OPTIONS: ClassVar[List[str]] = [
|
@@ -169,14 +179,10 @@ class UserScopedFilter(BaseFilter):
|
|
169
179
|
default=None,
|
170
180
|
description="The user to scope this query to.",
|
171
181
|
)
|
172
|
-
user_id: Optional[Union[UUID, str]] = Field(
|
173
|
-
default=None,
|
174
|
-
description="UUID of the user that created the entity.",
|
175
|
-
union_mode="left_to_right",
|
176
|
-
)
|
177
182
|
user: Optional[Union[UUID, str]] = Field(
|
178
183
|
default=None,
|
179
184
|
description="Name/ID of the user that created the entity.",
|
185
|
+
union_mode="left_to_right",
|
180
186
|
)
|
181
187
|
|
182
188
|
def set_scope_user(self, user_id: UUID) -> None:
|
@@ -316,6 +322,18 @@ class WorkspaceScopedResponse(
|
|
316
322
|
Used as a base class for all domain models that are workspace-scoped.
|
317
323
|
"""
|
318
324
|
|
325
|
+
# Analytics
|
326
|
+
def get_analytics_metadata(self) -> Dict[str, Any]:
|
327
|
+
"""Fetches the analytics metadata for workspace scoped models.
|
328
|
+
|
329
|
+
Returns:
|
330
|
+
The analytics metadata.
|
331
|
+
"""
|
332
|
+
metadata = super().get_analytics_metadata()
|
333
|
+
if self.workspace is not None:
|
334
|
+
metadata["workspace_id"] = self.workspace.id
|
335
|
+
return metadata
|
336
|
+
|
319
337
|
# Body and metadata properties
|
320
338
|
@property
|
321
339
|
def workspace(self) -> "WorkspaceResponse":
|
@@ -327,75 +345,25 @@ class WorkspaceScopedResponse(
|
|
327
345
|
return self.get_metadata().workspace
|
328
346
|
|
329
347
|
|
348
|
+
# ---------------------- Filter Models ----------------------
|
349
|
+
|
350
|
+
|
330
351
|
class WorkspaceScopedFilter(UserScopedFilter):
|
331
352
|
"""Model to enable advanced scoping with workspace."""
|
332
353
|
|
333
354
|
FILTER_EXCLUDE_FIELDS: ClassVar[List[str]] = [
|
334
355
|
*UserScopedFilter.FILTER_EXCLUDE_FIELDS,
|
335
356
|
"workspace",
|
336
|
-
"scope_workspace",
|
337
|
-
]
|
338
|
-
CLI_EXCLUDE_FIELDS: ClassVar[List[str]] = [
|
339
|
-
*UserScopedFilter.CLI_EXCLUDE_FIELDS,
|
340
|
-
"workspace_id",
|
341
|
-
"workspace",
|
342
|
-
"scope_workspace",
|
343
|
-
]
|
344
|
-
CUSTOM_SORTING_OPTIONS: ClassVar[List[str]] = [
|
345
|
-
*UserScopedFilter.CUSTOM_SORTING_OPTIONS,
|
346
|
-
"workspace",
|
347
357
|
]
|
348
|
-
scope_workspace: Optional[UUID] = Field(
|
349
|
-
default=None,
|
350
|
-
description="The workspace to scope this query to.",
|
351
|
-
)
|
352
|
-
workspace_id: Optional[Union[UUID, str]] = Field(
|
353
|
-
default=None,
|
354
|
-
description="UUID of the workspace that this entity belongs to.",
|
355
|
-
union_mode="left_to_right",
|
356
|
-
)
|
357
358
|
workspace: Optional[Union[UUID, str]] = Field(
|
358
359
|
default=None,
|
359
|
-
description="Name/ID of the workspace
|
360
|
+
description="Name/ID of the workspace which the search is scoped to. "
|
361
|
+
"This field must always be set and is always applied in addition to "
|
362
|
+
"the other filters, regardless of the value of the "
|
363
|
+
"logical_operator field.",
|
364
|
+
union_mode="left_to_right",
|
360
365
|
)
|
361
366
|
|
362
|
-
def set_scope_workspace(self, workspace_id: UUID) -> None:
|
363
|
-
"""Set the workspace to scope this response.
|
364
|
-
|
365
|
-
Args:
|
366
|
-
workspace_id: The workspace to scope this response to.
|
367
|
-
"""
|
368
|
-
self.scope_workspace = workspace_id
|
369
|
-
|
370
|
-
def get_custom_filters(
|
371
|
-
self, table: Type["AnySchema"]
|
372
|
-
) -> List["ColumnElement[bool]"]:
|
373
|
-
"""Get custom filters.
|
374
|
-
|
375
|
-
Args:
|
376
|
-
table: The query table.
|
377
|
-
|
378
|
-
Returns:
|
379
|
-
A list of custom filters.
|
380
|
-
"""
|
381
|
-
custom_filters = super().get_custom_filters(table)
|
382
|
-
|
383
|
-
from sqlmodel import and_
|
384
|
-
|
385
|
-
from zenml.zen_stores.schemas import WorkspaceSchema
|
386
|
-
|
387
|
-
if self.workspace:
|
388
|
-
workspace_filter = and_(
|
389
|
-
getattr(table, "workspace_id") == WorkspaceSchema.id,
|
390
|
-
self.generate_name_or_id_query_conditions(
|
391
|
-
value=self.workspace,
|
392
|
-
table=WorkspaceSchema,
|
393
|
-
),
|
394
|
-
)
|
395
|
-
custom_filters.append(workspace_filter)
|
396
|
-
|
397
|
-
return custom_filters
|
398
|
-
|
399
367
|
def apply_filter(
|
400
368
|
self,
|
401
369
|
query: AnyQuery,
|
@@ -409,59 +377,35 @@ class WorkspaceScopedFilter(UserScopedFilter):
|
|
409
377
|
|
410
378
|
Returns:
|
411
379
|
The query with filter applied.
|
412
|
-
"""
|
413
|
-
from sqlmodel import or_
|
414
|
-
|
415
|
-
query = super().apply_filter(query=query, table=table)
|
416
|
-
|
417
|
-
if self.scope_workspace:
|
418
|
-
scope_filter = or_(
|
419
|
-
getattr(table, "workspace_id") == self.scope_workspace,
|
420
|
-
getattr(table, "workspace_id").is_(None),
|
421
|
-
)
|
422
|
-
query = query.where(scope_filter)
|
423
|
-
|
424
|
-
return query
|
425
|
-
|
426
|
-
def apply_sorting(
|
427
|
-
self,
|
428
|
-
query: AnyQuery,
|
429
|
-
table: Type["AnySchema"],
|
430
|
-
) -> AnyQuery:
|
431
|
-
"""Apply sorting to the query.
|
432
380
|
|
433
|
-
|
434
|
-
|
435
|
-
table: The query table.
|
436
|
-
|
437
|
-
Returns:
|
438
|
-
The query with sorting applied.
|
381
|
+
Raises:
|
382
|
+
ValueError: If the workspace scope is missing from the filter.
|
439
383
|
"""
|
440
|
-
|
441
|
-
|
442
|
-
from zenml.enums import SorterOps
|
443
|
-
from zenml.zen_stores.schemas import WorkspaceSchema
|
444
|
-
|
445
|
-
sort_by, operand = self.sorting_params
|
446
|
-
|
447
|
-
if sort_by == "workspace":
|
448
|
-
column = WorkspaceSchema.name
|
384
|
+
query = super().apply_filter(query=query, table=table)
|
449
385
|
|
450
|
-
|
451
|
-
|
452
|
-
|
386
|
+
# The workspace scope must always be set and must be a UUID. If the
|
387
|
+
# client sets this to a string, the server will try to resolve it to a
|
388
|
+
# workspace ID.
|
389
|
+
#
|
390
|
+
# If not set by the client, the server will fall back to using the
|
391
|
+
# user's default workspace or even the server's default workspace, if
|
392
|
+
# they are configured. If this also fails to yield a workspace, this
|
393
|
+
# method will raise a ValueError.
|
394
|
+
#
|
395
|
+
# See: SqlZenStore._set_filter_workspace_id
|
396
|
+
|
397
|
+
if not self.workspace:
|
398
|
+
raise ValueError("Workspace scope missing from the filter.")
|
399
|
+
|
400
|
+
if not isinstance(self.workspace, UUID):
|
401
|
+
raise ValueError(
|
402
|
+
f"Workspace scope must be a UUID, got {type(self.workspace)}."
|
453
403
|
)
|
454
404
|
|
455
|
-
|
405
|
+
scope_filter = getattr(table, "workspace_id") == self.workspace
|
406
|
+
query = query.where(scope_filter)
|
456
407
|
|
457
|
-
|
458
|
-
query = query.order_by(asc(column))
|
459
|
-
else:
|
460
|
-
query = query.order_by(desc(column))
|
461
|
-
|
462
|
-
return query
|
463
|
-
|
464
|
-
return super().apply_sorting(query=query, table=table)
|
408
|
+
return query
|
465
409
|
|
466
410
|
|
467
411
|
class TaggableFilter(BaseFilter):
|
@@ -470,16 +414,44 @@ class TaggableFilter(BaseFilter):
|
|
470
414
|
tag: Optional[str] = Field(
|
471
415
|
description="Tag to apply to the filter query.", default=None
|
472
416
|
)
|
417
|
+
tags: Optional[List[str]] = Field(
|
418
|
+
description="Tags to apply to the filter query.", default=None
|
419
|
+
)
|
473
420
|
|
421
|
+
CLI_EXCLUDE_FIELDS = [
|
422
|
+
*BaseFilter.CLI_EXCLUDE_FIELDS,
|
423
|
+
]
|
474
424
|
FILTER_EXCLUDE_FIELDS: ClassVar[List[str]] = [
|
475
425
|
*BaseFilter.FILTER_EXCLUDE_FIELDS,
|
476
426
|
"tag",
|
427
|
+
"tags",
|
477
428
|
]
|
478
429
|
CUSTOM_SORTING_OPTIONS: ClassVar[List[str]] = [
|
479
430
|
*BaseFilter.CUSTOM_SORTING_OPTIONS,
|
480
431
|
"tags",
|
481
432
|
]
|
482
433
|
|
434
|
+
@model_validator(mode="after")
|
435
|
+
def add_tag_to_tags(self) -> "TaggableFilter":
|
436
|
+
"""Deprecated the tag attribute in favor of the tags attribute.
|
437
|
+
|
438
|
+
Returns:
|
439
|
+
self
|
440
|
+
"""
|
441
|
+
if self.tag is not None:
|
442
|
+
logger.warning(
|
443
|
+
"The `tag` attribute is deprecated in favor of the `tags` attribute. "
|
444
|
+
"Please update your code to use the `tags` attribute instead."
|
445
|
+
)
|
446
|
+
if self.tags is not None:
|
447
|
+
self.tags.append(self.tag)
|
448
|
+
else:
|
449
|
+
self.tags = [self.tag]
|
450
|
+
|
451
|
+
self.tag = None
|
452
|
+
|
453
|
+
return self
|
454
|
+
|
483
455
|
def apply_filter(
|
484
456
|
self,
|
485
457
|
query: AnyQuery,
|
@@ -497,7 +469,8 @@ class TaggableFilter(BaseFilter):
|
|
497
469
|
from zenml.zen_stores.schemas import TagResourceSchema, TagSchema
|
498
470
|
|
499
471
|
query = super().apply_filter(query=query, table=table)
|
500
|
-
|
472
|
+
|
473
|
+
if self.tags is not None:
|
501
474
|
query = query.join(
|
502
475
|
TagResourceSchema,
|
503
476
|
TagResourceSchema.resource_id == getattr(table, "id"),
|
@@ -516,15 +489,25 @@ class TaggableFilter(BaseFilter):
|
|
516
489
|
Returns:
|
517
490
|
A list of custom filters.
|
518
491
|
"""
|
519
|
-
from zenml.zen_stores.schemas import TagSchema
|
520
|
-
|
521
492
|
custom_filters = super().get_custom_filters(table)
|
522
|
-
|
523
|
-
|
524
|
-
|
525
|
-
|
493
|
+
|
494
|
+
if self.tags is not None:
|
495
|
+
from sqlmodel import exists, select
|
496
|
+
|
497
|
+
from zenml.zen_stores.schemas import TagResourceSchema, TagSchema
|
498
|
+
|
499
|
+
for tag in self.tags:
|
500
|
+
condition = self.generate_custom_query_conditions_for_column(
|
501
|
+
value=tag, table=TagSchema, column="name"
|
526
502
|
)
|
527
|
-
|
503
|
+
exists_subquery = exists(
|
504
|
+
select(TagResourceSchema)
|
505
|
+
.join(TagSchema, TagSchema.id == TagResourceSchema.tag_id) # type: ignore[arg-type]
|
506
|
+
.where(
|
507
|
+
TagResourceSchema.resource_id == table.id, condition
|
508
|
+
)
|
509
|
+
)
|
510
|
+
custom_filters.append(exists_subquery)
|
528
511
|
|
529
512
|
return custom_filters
|
530
513
|
|
zenml/models/v2/core/api_key.py
CHANGED
zenml/models/v2/core/artifact.py
CHANGED
@@ -25,17 +25,19 @@ from typing import (
|
|
25
25
|
)
|
26
26
|
from uuid import UUID
|
27
27
|
|
28
|
-
from pydantic import
|
28
|
+
from pydantic import Field
|
29
29
|
|
30
30
|
from zenml.constants import SORT_BY_LATEST_VERSION_KEY, STR_FIELD_MAX_LENGTH
|
31
|
-
from zenml.models.v2.base.base import
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
31
|
+
from zenml.models.v2.base.base import BaseUpdate
|
32
|
+
from zenml.models.v2.base.scoped import (
|
33
|
+
TaggableFilter,
|
34
|
+
WorkspaceScopedFilter,
|
35
|
+
WorkspaceScopedRequest,
|
36
|
+
WorkspaceScopedResponse,
|
37
|
+
WorkspaceScopedResponseBody,
|
38
|
+
WorkspaceScopedResponseMetadata,
|
39
|
+
WorkspaceScopedResponseResources,
|
37
40
|
)
|
38
|
-
from zenml.models.v2.base.scoped import TaggableFilter
|
39
41
|
from zenml.models.v2.core.tag import TagResponse
|
40
42
|
|
41
43
|
if TYPE_CHECKING:
|
@@ -49,7 +51,7 @@ AnyQuery = TypeVar("AnyQuery", bound=Any)
|
|
49
51
|
# ------------------ Request Model ------------------
|
50
52
|
|
51
53
|
|
52
|
-
class ArtifactRequest(
|
54
|
+
class ArtifactRequest(WorkspaceScopedRequest):
|
53
55
|
"""Artifact request model."""
|
54
56
|
|
55
57
|
name: str = Field(
|
@@ -70,7 +72,7 @@ class ArtifactRequest(BaseRequest):
|
|
70
72
|
# ------------------ Update Model ------------------
|
71
73
|
|
72
74
|
|
73
|
-
class ArtifactUpdate(
|
75
|
+
class ArtifactUpdate(BaseUpdate):
|
74
76
|
"""Artifact update model."""
|
75
77
|
|
76
78
|
name: Optional[str] = None
|
@@ -82,7 +84,7 @@ class ArtifactUpdate(BaseModel):
|
|
82
84
|
# ------------------ Response Model ------------------
|
83
85
|
|
84
86
|
|
85
|
-
class ArtifactResponseBody(
|
87
|
+
class ArtifactResponseBody(WorkspaceScopedResponseBody):
|
86
88
|
"""Response body for artifacts."""
|
87
89
|
|
88
90
|
tags: List[TagResponse] = Field(
|
@@ -92,7 +94,7 @@ class ArtifactResponseBody(BaseDatedResponseBody):
|
|
92
94
|
latest_version_id: Optional[UUID] = None
|
93
95
|
|
94
96
|
|
95
|
-
class ArtifactResponseMetadata(
|
97
|
+
class ArtifactResponseMetadata(WorkspaceScopedResponseMetadata):
|
96
98
|
"""Response metadata for artifacts."""
|
97
99
|
|
98
100
|
has_custom_name: bool = Field(
|
@@ -101,12 +103,12 @@ class ArtifactResponseMetadata(BaseResponseMetadata):
|
|
101
103
|
)
|
102
104
|
|
103
105
|
|
104
|
-
class ArtifactResponseResources(
|
106
|
+
class ArtifactResponseResources(WorkspaceScopedResponseResources):
|
105
107
|
"""Class for all resource models associated with the Artifact Entity."""
|
106
108
|
|
107
109
|
|
108
110
|
class ArtifactResponse(
|
109
|
-
|
111
|
+
WorkspaceScopedResponse[
|
110
112
|
ArtifactResponseBody,
|
111
113
|
ArtifactResponseMetadata,
|
112
114
|
ArtifactResponseResources,
|
@@ -176,24 +178,35 @@ class ArtifactResponse(
|
|
176
178
|
"""
|
177
179
|
from zenml.client import Client
|
178
180
|
|
179
|
-
responses = Client().list_artifact_versions(
|
181
|
+
responses = Client().list_artifact_versions(artifact=self.name)
|
180
182
|
return {str(response.version): response for response in responses}
|
181
183
|
|
182
184
|
|
183
185
|
# ------------------ Filter Model ------------------
|
184
186
|
|
185
187
|
|
186
|
-
class ArtifactFilter(TaggableFilter):
|
188
|
+
class ArtifactFilter(WorkspaceScopedFilter, TaggableFilter):
|
187
189
|
"""Model to enable advanced filtering of artifacts."""
|
188
190
|
|
189
|
-
|
190
|
-
|
191
|
+
FILTER_EXCLUDE_FIELDS: ClassVar[List[str]] = [
|
192
|
+
*WorkspaceScopedFilter.FILTER_EXCLUDE_FIELDS,
|
193
|
+
*TaggableFilter.FILTER_EXCLUDE_FIELDS,
|
194
|
+
]
|
191
195
|
|
192
196
|
CUSTOM_SORTING_OPTIONS: ClassVar[List[str]] = [
|
197
|
+
*WorkspaceScopedFilter.CUSTOM_SORTING_OPTIONS,
|
193
198
|
*TaggableFilter.CUSTOM_SORTING_OPTIONS,
|
194
199
|
SORT_BY_LATEST_VERSION_KEY,
|
195
200
|
]
|
196
201
|
|
202
|
+
CLI_EXCLUDE_FIELDS: ClassVar[List[str]] = [
|
203
|
+
*WorkspaceScopedFilter.CLI_EXCLUDE_FIELDS,
|
204
|
+
*TaggableFilter.CLI_EXCLUDE_FIELDS,
|
205
|
+
]
|
206
|
+
|
207
|
+
name: Optional[str] = None
|
208
|
+
has_custom_name: Optional[bool] = None
|
209
|
+
|
197
210
|
def apply_sorting(
|
198
211
|
self,
|
199
212
|
query: AnyQuery,
|
@@ -27,7 +27,6 @@ from typing import (
|
|
27
27
|
from uuid import UUID
|
28
28
|
|
29
29
|
from pydantic import (
|
30
|
-
BaseModel,
|
31
30
|
ConfigDict,
|
32
31
|
Field,
|
33
32
|
field_validator,
|
@@ -36,10 +35,11 @@ from pydantic import (
|
|
36
35
|
|
37
36
|
from zenml.config.source import Source, SourceWithValidator
|
38
37
|
from zenml.constants import STR_FIELD_MAX_LENGTH, TEXT_FIELD_MAX_LENGTH
|
39
|
-
from zenml.enums import ArtifactSaveType, ArtifactType
|
38
|
+
from zenml.enums import ArtifactSaveType, ArtifactType
|
40
39
|
from zenml.logger import get_logger
|
41
40
|
from zenml.metadata.metadata_types import MetadataType
|
42
|
-
from zenml.models.v2.base.
|
41
|
+
from zenml.models.v2.base.base import BaseUpdate
|
42
|
+
from zenml.models.v2.base.filter import FilterGenerator
|
43
43
|
from zenml.models.v2.base.scoped import (
|
44
44
|
TaggableFilter,
|
45
45
|
WorkspaceScopedFilter,
|
@@ -165,7 +165,7 @@ class ArtifactVersionRequest(WorkspaceScopedRequest):
|
|
165
165
|
# ------------------ Update Model ------------------
|
166
166
|
|
167
167
|
|
168
|
-
class ArtifactVersionUpdate(
|
168
|
+
class ArtifactVersionUpdate(BaseUpdate):
|
169
169
|
"""Artifact version update model."""
|
170
170
|
|
171
171
|
name: Optional[str] = None
|
@@ -476,7 +476,8 @@ class ArtifactVersionFilter(WorkspaceScopedFilter, TaggableFilter):
|
|
476
476
|
FILTER_EXCLUDE_FIELDS: ClassVar[List[str]] = [
|
477
477
|
*WorkspaceScopedFilter.FILTER_EXCLUDE_FIELDS,
|
478
478
|
*TaggableFilter.FILTER_EXCLUDE_FIELDS,
|
479
|
-
"
|
479
|
+
"artifact_id",
|
480
|
+
"artifact",
|
480
481
|
"only_unused",
|
481
482
|
"has_custom_name",
|
482
483
|
"model",
|
@@ -484,23 +485,28 @@ class ArtifactVersionFilter(WorkspaceScopedFilter, TaggableFilter):
|
|
484
485
|
"model_version_id",
|
485
486
|
"run_metadata",
|
486
487
|
]
|
487
|
-
CUSTOM_SORTING_OPTIONS = [
|
488
|
+
CUSTOM_SORTING_OPTIONS: ClassVar[List[str]] = [
|
488
489
|
*WorkspaceScopedFilter.CUSTOM_SORTING_OPTIONS,
|
489
490
|
*TaggableFilter.CUSTOM_SORTING_OPTIONS,
|
490
491
|
]
|
491
|
-
CLI_EXCLUDE_FIELDS = [
|
492
|
+
CLI_EXCLUDE_FIELDS: ClassVar[List[str]] = [
|
492
493
|
*WorkspaceScopedFilter.CLI_EXCLUDE_FIELDS,
|
493
494
|
*TaggableFilter.CLI_EXCLUDE_FIELDS,
|
495
|
+
"artifact_id",
|
494
496
|
]
|
495
497
|
|
496
|
-
|
498
|
+
artifact: Optional[Union[UUID, str]] = Field(
|
497
499
|
default=None,
|
498
|
-
description="ID of the artifact
|
500
|
+
description="The name or ID of the artifact which the search is scoped "
|
501
|
+
"to. This field must always be set and is always applied in addition "
|
502
|
+
"to the other filters, regardless of the value of the "
|
503
|
+
"logical_operator field.",
|
499
504
|
union_mode="left_to_right",
|
500
505
|
)
|
501
|
-
|
506
|
+
artifact_id: Optional[Union[UUID, str]] = Field(
|
502
507
|
default=None,
|
503
|
-
description="
|
508
|
+
description="[Deprecated] Use 'artifact' instead. ID of the artifact to which this version belongs.",
|
509
|
+
union_mode="left_to_right",
|
504
510
|
)
|
505
511
|
version: Optional[str] = Field(
|
506
512
|
default=None,
|
@@ -545,10 +551,6 @@ class ArtifactVersionFilter(WorkspaceScopedFilter, TaggableFilter):
|
|
545
551
|
default=None,
|
546
552
|
description="Filter only artifacts with/without custom names.",
|
547
553
|
)
|
548
|
-
user: Optional[Union[UUID, str]] = Field(
|
549
|
-
default=None,
|
550
|
-
description="Name/ID of the user that created the artifact version.",
|
551
|
-
)
|
552
554
|
model: Optional[Union[UUID, str]] = Field(
|
553
555
|
default=None,
|
554
556
|
description="Name/ID of the model that is associated with this "
|
@@ -595,18 +597,15 @@ class ArtifactVersionFilter(WorkspaceScopedFilter, TaggableFilter):
|
|
595
597
|
StepRunSchema,
|
596
598
|
)
|
597
599
|
|
598
|
-
if self.
|
599
|
-
value,
|
600
|
-
|
601
|
-
operation=GenericFilterOps(filter_operator),
|
602
|
-
column="name",
|
603
|
-
value=value,
|
604
|
-
)
|
605
|
-
artifact_name_filter = and_(
|
600
|
+
if self.artifact:
|
601
|
+
value, operator = self._resolve_operator(self.artifact)
|
602
|
+
artifact_filter = and_(
|
606
603
|
ArtifactVersionSchema.artifact_id == ArtifactSchema.id,
|
607
|
-
|
604
|
+
self.generate_name_or_id_query_conditions(
|
605
|
+
value=self.artifact, table=ArtifactSchema
|
606
|
+
),
|
608
607
|
)
|
609
|
-
custom_filters.append(
|
608
|
+
custom_filters.append(artifact_filter)
|
610
609
|
|
611
610
|
if self.only_unused:
|
612
611
|
unused_filter = and_(
|
@@ -702,6 +701,24 @@ class ArtifactVersionFilter(WorkspaceScopedFilter, TaggableFilter):
|
|
702
701
|
|
703
702
|
return custom_filters
|
704
703
|
|
704
|
+
@model_validator(mode="after")
|
705
|
+
def _migrate_artifact_id(self) -> "ArtifactVersionFilter":
|
706
|
+
"""Migrate value from the deprecated artifact_id attribute.
|
707
|
+
|
708
|
+
Returns:
|
709
|
+
The filter with migrated value.
|
710
|
+
"""
|
711
|
+
# Handle deprecated artifact_id field
|
712
|
+
if self.artifact_id is not None:
|
713
|
+
logger.warning(
|
714
|
+
"The 'ArtifactVersionFilter.artifact_id' field is deprecated "
|
715
|
+
"and will be removed in a future version. Please use "
|
716
|
+
"'ArtifactVersionFilter.artifact' instead."
|
717
|
+
)
|
718
|
+
self.artifact = self.artifact or self.artifact_id
|
719
|
+
|
720
|
+
return self
|
721
|
+
|
705
722
|
|
706
723
|
# -------------------- Lazy Loader --------------------
|
707
724
|
|