wandb 0.19.10__py3-none-macosx_11_0_arm64.whl → 0.19.11__py3-none-macosx_11_0_arm64.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.
- wandb/__init__.py +1 -1
- wandb/__init__.pyi +3 -3
- wandb/_pydantic/__init__.py +2 -3
- wandb/_pydantic/base.py +11 -31
- wandb/_pydantic/utils.py +8 -1
- wandb/_pydantic/v1_compat.py +3 -3
- wandb/apis/public/api.py +590 -22
- wandb/apis/public/artifacts.py +13 -5
- wandb/apis/public/automations.py +1 -1
- wandb/apis/public/integrations.py +22 -10
- wandb/apis/public/registries/__init__.py +0 -0
- wandb/apis/public/registries/_freezable_list.py +179 -0
- wandb/apis/public/{registries.py → registries/registries_search.py} +22 -129
- wandb/apis/public/registries/registry.py +357 -0
- wandb/apis/public/registries/utils.py +140 -0
- wandb/apis/public/runs.py +58 -56
- wandb/automations/__init__.py +16 -24
- wandb/automations/_filters/expressions.py +12 -10
- wandb/automations/_filters/operators.py +10 -19
- wandb/automations/_filters/run_metrics.py +231 -82
- wandb/automations/_generated/__init__.py +27 -34
- wandb/automations/_generated/create_automation.py +17 -0
- wandb/automations/_generated/delete_automation.py +17 -0
- wandb/automations/_generated/fragments.py +40 -25
- wandb/automations/_generated/{get_triggers.py → get_automations.py} +5 -5
- wandb/automations/_generated/{get_triggers_by_entity.py → get_automations_by_entity.py} +7 -5
- wandb/automations/_generated/operations.py +35 -98
- wandb/automations/_generated/update_automation.py +17 -0
- wandb/automations/_utils.py +178 -64
- wandb/automations/_validators.py +94 -2
- wandb/automations/actions.py +113 -98
- wandb/automations/automations.py +47 -69
- wandb/automations/events.py +139 -87
- wandb/automations/integrations.py +23 -4
- wandb/automations/scopes.py +22 -20
- wandb/bin/gpu_stats +0 -0
- wandb/bin/wandb-core +0 -0
- wandb/env.py +11 -0
- wandb/old/settings.py +4 -1
- wandb/proto/v3/wandb_internal_pb2.py +240 -236
- wandb/proto/v3/wandb_telemetry_pb2.py +10 -10
- wandb/proto/v4/wandb_internal_pb2.py +236 -236
- wandb/proto/v4/wandb_telemetry_pb2.py +10 -10
- wandb/proto/v5/wandb_internal_pb2.py +236 -236
- wandb/proto/v5/wandb_telemetry_pb2.py +10 -10
- wandb/proto/v6/wandb_internal_pb2.py +236 -236
- wandb/proto/v6/wandb_telemetry_pb2.py +10 -10
- wandb/sdk/artifacts/_generated/__init__.py +42 -1
- wandb/sdk/artifacts/_generated/add_aliases.py +21 -0
- wandb/sdk/artifacts/_generated/delete_aliases.py +21 -0
- wandb/sdk/artifacts/_generated/fetch_linked_artifacts.py +67 -0
- wandb/sdk/artifacts/_generated/fragments.py +35 -0
- wandb/sdk/artifacts/_generated/input_types.py +12 -0
- wandb/sdk/artifacts/_generated/operations.py +101 -0
- wandb/sdk/artifacts/_generated/update_artifact.py +26 -0
- wandb/sdk/artifacts/_graphql_fragments.py +1 -0
- wandb/sdk/artifacts/_validators.py +120 -1
- wandb/sdk/artifacts/artifact.py +380 -203
- wandb/sdk/artifacts/artifact_file_cache.py +4 -6
- wandb/sdk/artifacts/artifact_manifest_entry.py +11 -2
- wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +182 -1
- wandb/sdk/artifacts/storage_policy.py +3 -0
- wandb/sdk/data_types/video.py +46 -32
- wandb/sdk/interface/interface.py +2 -3
- wandb/sdk/internal/internal_api.py +21 -31
- wandb/sdk/internal/sender.py +5 -2
- wandb/sdk/launch/sweeps/utils.py +8 -0
- wandb/sdk/projects/_generated/__init__.py +47 -0
- wandb/sdk/projects/_generated/delete_project.py +22 -0
- wandb/sdk/projects/_generated/enums.py +4 -0
- wandb/sdk/projects/_generated/fetch_registry.py +22 -0
- wandb/sdk/projects/_generated/fragments.py +41 -0
- wandb/sdk/projects/_generated/input_types.py +13 -0
- wandb/sdk/projects/_generated/operations.py +88 -0
- wandb/sdk/projects/_generated/rename_project.py +27 -0
- wandb/sdk/projects/_generated/upsert_registry_project.py +27 -0
- wandb/sdk/service/service.py +9 -1
- wandb/sdk/wandb_init.py +32 -5
- wandb/sdk/wandb_run.py +37 -9
- wandb/sdk/wandb_settings.py +6 -7
- wandb/sdk/wandb_setup.py +12 -0
- wandb/util.py +7 -3
- {wandb-0.19.10.dist-info → wandb-0.19.11.dist-info}/METADATA +1 -1
- {wandb-0.19.10.dist-info → wandb-0.19.11.dist-info}/RECORD +87 -70
- wandb/automations/_generated/create_filter_trigger.py +0 -21
- wandb/automations/_generated/delete_trigger.py +0 -19
- wandb/automations/_generated/update_filter_trigger.py +0 -21
- {wandb-0.19.10.dist-info → wandb-0.19.11.dist-info}/WHEEL +0 -0
- {wandb-0.19.10.dist-info → wandb-0.19.11.dist-info}/entry_points.txt +0 -0
- {wandb-0.19.10.dist-info → wandb-0.19.11.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
# Generated by ariadne-codegen
|
2
|
+
# Source: tools/graphql_codegen/automations/
|
3
|
+
|
4
|
+
from __future__ import annotations
|
5
|
+
|
6
|
+
from typing import Optional
|
7
|
+
|
8
|
+
from wandb._pydantic import GQLBase
|
9
|
+
|
10
|
+
from .fragments import UpdateAutomationResult
|
11
|
+
|
12
|
+
|
13
|
+
class UpdateAutomation(GQLBase):
|
14
|
+
result: Optional[UpdateAutomationResult]
|
15
|
+
|
16
|
+
|
17
|
+
UpdateAutomation.model_rebuild()
|
wandb/automations/_utils.py
CHANGED
@@ -2,30 +2,63 @@
|
|
2
2
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
|
-
from typing import
|
5
|
+
from typing import Any, Collection, Final, Optional, Protocol, TypedDict
|
6
6
|
|
7
|
-
from
|
8
|
-
from
|
9
|
-
from wandb.automations.scopes import InputScope
|
7
|
+
from pydantic import Field
|
8
|
+
from typing_extensions import Annotated, Self, Unpack
|
10
9
|
|
10
|
+
from wandb._pydantic import GQLBase, GQLId, computed_field, model_validator, to_json
|
11
|
+
|
12
|
+
from ._filters import MongoLikeFilter
|
11
13
|
from ._generated import (
|
12
14
|
CreateFilterTriggerInput,
|
15
|
+
QueueJobActionInput,
|
13
16
|
TriggeredActionConfig,
|
14
17
|
UpdateFilterTriggerInput,
|
15
18
|
)
|
19
|
+
from ._validators import to_input_action
|
16
20
|
from .actions import (
|
17
21
|
ActionType,
|
18
22
|
DoNothing,
|
19
|
-
DoNotification,
|
20
|
-
DoWebhook,
|
21
23
|
InputAction,
|
22
24
|
SavedAction,
|
25
|
+
SendNotification,
|
26
|
+
SendWebhook,
|
27
|
+
)
|
28
|
+
from .automations import Automation, NewAutomation
|
29
|
+
from .events import EventType, InputEvent, RunMetricFilter, _WrappedSavedEventFilter
|
30
|
+
from .scopes import AutomationScope, ScopeType
|
31
|
+
|
32
|
+
EXCLUDED_INPUT_EVENTS: Final[Collection[EventType]] = frozenset(
|
33
|
+
{
|
34
|
+
EventType.UPDATE_ARTIFACT_ALIAS,
|
35
|
+
}
|
23
36
|
)
|
37
|
+
"""Event types that should not be assigned when creating/updating automations."""
|
24
38
|
|
25
|
-
|
26
|
-
|
39
|
+
EXCLUDED_INPUT_ACTIONS: Final[Collection[ActionType]] = frozenset(
|
40
|
+
{
|
41
|
+
ActionType.QUEUE_JOB,
|
42
|
+
}
|
43
|
+
)
|
44
|
+
"""Action types that should not be assigned when creating/updating automations."""
|
45
|
+
|
46
|
+
ALWAYS_SUPPORTED_EVENTS: Final[Collection[EventType]] = frozenset(
|
47
|
+
{
|
48
|
+
EventType.CREATE_ARTIFACT,
|
49
|
+
EventType.LINK_ARTIFACT,
|
50
|
+
EventType.ADD_ARTIFACT_ALIAS,
|
51
|
+
}
|
52
|
+
)
|
53
|
+
"""Event types that we can safely assume all contemporary server versions support."""
|
27
54
|
|
28
|
-
|
55
|
+
ALWAYS_SUPPORTED_ACTIONS: Final[Collection[ActionType]] = frozenset(
|
56
|
+
{
|
57
|
+
ActionType.NOTIFICATION,
|
58
|
+
ActionType.GENERIC_WEBHOOK,
|
59
|
+
}
|
60
|
+
)
|
61
|
+
"""Action types that we can safely assume all contemporary server versions support."""
|
29
62
|
|
30
63
|
|
31
64
|
class HasId(Protocol):
|
@@ -36,88 +69,169 @@ def extract_id(obj: HasId | str) -> str:
|
|
36
69
|
return obj.id if hasattr(obj, "id") else obj
|
37
70
|
|
38
71
|
|
39
|
-
#
|
40
|
-
|
41
|
-
_ACTION_CONFIG_KEYS: dict[ActionType, str] = {
|
72
|
+
# ---------------------------------------------------------------------------
|
73
|
+
ACTION_CONFIG_KEYS: dict[ActionType, str] = {
|
42
74
|
ActionType.NOTIFICATION: "notification_action_input",
|
43
75
|
ActionType.GENERIC_WEBHOOK: "generic_webhook_action_input",
|
44
76
|
ActionType.NO_OP: "no_op_action_input",
|
77
|
+
ActionType.QUEUE_JOB: "queue_job_action_input",
|
45
78
|
}
|
46
79
|
|
47
80
|
|
48
81
|
class InputActionConfig(TriggeredActionConfig):
|
49
82
|
"""A `TriggeredActionConfig` that prepares the action config for saving an automation."""
|
50
83
|
|
51
|
-
|
52
|
-
|
84
|
+
# NOTE: `QueueJobActionInput` for defining a Launch job is deprecated,
|
85
|
+
# so while it's allowed here to update EXISTING mutations, we don't
|
86
|
+
# currently expose it through the public API to create NEW automations.
|
87
|
+
queue_job_action_input: Optional[QueueJobActionInput] = None
|
88
|
+
|
89
|
+
notification_action_input: Optional[SendNotification] = None
|
90
|
+
generic_webhook_action_input: Optional[SendWebhook] = None
|
53
91
|
no_op_action_input: Optional[DoNothing] = None
|
54
92
|
|
55
93
|
|
56
|
-
def
|
57
|
-
"""
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
94
|
+
def prepare_action_config_input(obj: SavedAction | InputAction) -> dict[str, Any]:
|
95
|
+
"""Prepare the `TriggeredActionConfig` input, nesting the action input inside the appropriate key.
|
96
|
+
|
97
|
+
This is necessary to conform to the schemas for:
|
98
|
+
- CreateFilterTriggerInput
|
99
|
+
- UpdateFilterTriggerInput
|
100
|
+
"""
|
101
|
+
# Delegate to inner validators to convert SavedAction -> InputAction types, if needed.
|
102
|
+
obj = to_input_action(obj)
|
103
|
+
return InputActionConfig(**{ACTION_CONFIG_KEYS[obj.action_type]: obj}).model_dump()
|
104
|
+
|
105
|
+
|
106
|
+
def prepare_event_filter_input(
|
107
|
+
obj: _WrappedSavedEventFilter | MongoLikeFilter | RunMetricFilter,
|
108
|
+
) -> str:
|
109
|
+
"""Prepare the `EventFilter` input, unnesting the filter if needed and serializing to JSON.
|
110
|
+
|
111
|
+
This is necessary to conform to the schemas for:
|
112
|
+
- CreateFilterTriggerInput
|
113
|
+
- UpdateFilterTriggerInput
|
114
|
+
"""
|
115
|
+
# Input event filters are nested one level deeper than saved event filters.
|
116
|
+
# Note that this is NOT the case for run/run metric filters.
|
117
|
+
#
|
118
|
+
# Yes, this is confusing. It's also necessary to conform to under-the-hood
|
119
|
+
# schemas and logic in the backend.
|
120
|
+
filter_to_serialize = (
|
121
|
+
obj.filter if isinstance(obj, _WrappedSavedEventFilter) else obj
|
122
|
+
)
|
123
|
+
return to_json(filter_to_serialize)
|
62
124
|
|
63
125
|
|
64
|
-
class
|
126
|
+
class WriteAutomationsKwargs(TypedDict, total=False):
|
65
127
|
"""Keyword arguments that can be passed to create or update an automation."""
|
66
128
|
|
67
129
|
name: str
|
68
130
|
description: str
|
69
131
|
enabled: bool
|
70
|
-
|
71
|
-
scope: InputScope
|
132
|
+
scope: AutomationScope
|
72
133
|
event: InputEvent
|
73
134
|
action: InputAction
|
74
135
|
|
75
136
|
|
76
|
-
|
77
|
-
|
78
|
-
) -> CreateFilterTriggerInput:
|
79
|
-
"""Prepares the payload to create an automation in a GraphQL request."""
|
80
|
-
from .automations import PreparedAutomation
|
81
|
-
|
82
|
-
# Apply any updates to the properties of the automation
|
83
|
-
updated = obj.model_copy(update=updates)
|
84
|
-
prepared = PreparedAutomation.model_validate(updated)
|
85
|
-
|
86
|
-
# Prepare the input as required for the GraphQL request
|
87
|
-
return CreateFilterTriggerInput(
|
88
|
-
name=prepared.name,
|
89
|
-
description=prepared.description,
|
90
|
-
enabled=prepared.enabled,
|
91
|
-
scope_type=prepared.scope.scope_type,
|
92
|
-
scope_id=prepared.scope.id,
|
93
|
-
triggering_event_type=prepared.event.event_type,
|
94
|
-
event_filter=to_json(prepared.event.filter),
|
95
|
-
triggered_action_type=prepared.action.action_type,
|
96
|
-
triggered_action_config=prepare_action_input(prepared.action).model_dump(),
|
97
|
-
)
|
137
|
+
class ValidatedCreateInput(GQLBase, extra="forbid", frozen=True):
|
138
|
+
"""Validated automation parameters, prepared for creating a new automation.
|
98
139
|
|
140
|
+
Note: Users should never need to instantiate this class directly.
|
141
|
+
"""
|
99
142
|
|
100
|
-
|
101
|
-
|
143
|
+
name: str
|
144
|
+
description: Optional[str] = None
|
145
|
+
enabled: bool = True
|
146
|
+
|
147
|
+
# ------------------------------------------------------------------------------
|
148
|
+
# Set on instantiation, but used to derive other fields and deliberately
|
149
|
+
# EXCLUDED from the final GraphQL request vars
|
150
|
+
event: Annotated[InputEvent, Field(exclude=True)]
|
151
|
+
action: Annotated[InputAction, Field(exclude=True)]
|
152
|
+
|
153
|
+
# ------------------------------------------------------------------------------
|
154
|
+
# Derived fields to match the input schemas
|
155
|
+
@computed_field
|
156
|
+
def scope_type(self) -> ScopeType:
|
157
|
+
return self.event.scope.scope_type
|
158
|
+
|
159
|
+
@computed_field
|
160
|
+
def scope_id(self) -> GQLId:
|
161
|
+
return self.event.scope.id
|
162
|
+
|
163
|
+
@computed_field
|
164
|
+
def triggering_event_type(self) -> EventType:
|
165
|
+
return self.event.event_type
|
166
|
+
|
167
|
+
@computed_field
|
168
|
+
def event_filter(self) -> str:
|
169
|
+
return prepare_event_filter_input(self.event.filter)
|
170
|
+
|
171
|
+
@computed_field
|
172
|
+
def triggered_action_type(self) -> ActionType:
|
173
|
+
return self.action.action_type
|
174
|
+
|
175
|
+
@computed_field
|
176
|
+
def triggered_action_config(self) -> dict[str, Any]:
|
177
|
+
return prepare_action_config_input(self.action)
|
178
|
+
|
179
|
+
# ------------------------------------------------------------------------------
|
180
|
+
# Custom validation
|
181
|
+
@model_validator(mode="after")
|
182
|
+
def _forbid_legacy_event_types(self) -> Self:
|
183
|
+
if (type_ := self.event.event_type) in EXCLUDED_INPUT_EVENTS:
|
184
|
+
raise ValueError(f"{type_!r} events cannot be assigned to automations.")
|
185
|
+
return self
|
186
|
+
|
187
|
+
@model_validator(mode="after")
|
188
|
+
def _forbid_legacy_action_types(self) -> Self:
|
189
|
+
if (type_ := self.action.action_type) in EXCLUDED_INPUT_ACTIONS:
|
190
|
+
raise ValueError(f"{type_!r} actions cannot be assigned to automations.")
|
191
|
+
return self
|
192
|
+
|
193
|
+
|
194
|
+
def prepare_to_create(
|
195
|
+
obj: NewAutomation | None = None,
|
196
|
+
/,
|
197
|
+
**kwargs: Unpack[WriteAutomationsKwargs],
|
198
|
+
) -> CreateFilterTriggerInput:
|
199
|
+
"""Prepares the payload to create an automation in a GraphQL request."""
|
200
|
+
# Validate all input variables, and prepare as expected by the GraphQL request.
|
201
|
+
# - if an object is provided, override its fields with any keyword args
|
202
|
+
# - otherwise, instantiate from the keyword args
|
203
|
+
|
204
|
+
# NOTE: `exclude_none=True` drops fields that are still `None`.
|
205
|
+
#
|
206
|
+
# This assumes that `None` is good enough for now as a sentinel
|
207
|
+
# "unset" value. If this proves insufficient, revisit in the future,
|
208
|
+
# as it should be reasonably easy to implement a custom sentinel
|
209
|
+
# type later on.
|
210
|
+
obj_dict = {**obj.model_dump(exclude_none=True), **kwargs} if obj else kwargs
|
211
|
+
validated = ValidatedCreateInput(**obj_dict)
|
212
|
+
return CreateFilterTriggerInput.model_validate(validated)
|
213
|
+
|
214
|
+
|
215
|
+
def prepare_to_update(
|
216
|
+
obj: Automation | None = None,
|
217
|
+
/,
|
218
|
+
**kwargs: Unpack[WriteAutomationsKwargs],
|
102
219
|
) -> UpdateFilterTriggerInput:
|
103
220
|
"""Prepares the payload to update an automation in a GraphQL request."""
|
104
|
-
|
105
|
-
|
106
|
-
|
221
|
+
# Validate all values:
|
222
|
+
# - if an object is provided, override its fields with any keyword args
|
223
|
+
# - otherwise, instantiate from the keyword args
|
224
|
+
v_obj = Automation(**{**dict(obj or {}), **kwargs})
|
107
225
|
|
108
226
|
return UpdateFilterTriggerInput(
|
109
|
-
id=
|
110
|
-
name=
|
111
|
-
description=
|
112
|
-
enabled=
|
113
|
-
scope_type=
|
114
|
-
scope_id=
|
115
|
-
triggering_event_type=
|
116
|
-
event_filter=
|
117
|
-
|
118
|
-
|
119
|
-
else updated.event.filter
|
120
|
-
),
|
121
|
-
triggered_action_type=updated.action.action_type,
|
122
|
-
triggered_action_config=prepare_action_input(updated.action).model_dump(),
|
227
|
+
id=v_obj.id,
|
228
|
+
name=v_obj.name,
|
229
|
+
description=v_obj.description,
|
230
|
+
enabled=v_obj.enabled,
|
231
|
+
scope_type=v_obj.scope.scope_type,
|
232
|
+
scope_id=v_obj.scope.id,
|
233
|
+
triggering_event_type=v_obj.event.event_type,
|
234
|
+
event_filter=prepare_event_filter_input(v_obj.event.filter),
|
235
|
+
triggered_action_type=v_obj.action.action_type,
|
236
|
+
triggered_action_config=prepare_action_config_input(v_obj.action),
|
123
237
|
)
|
wandb/automations/_validators.py
CHANGED
@@ -1,16 +1,53 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
+
from enum import Enum
|
3
4
|
from functools import singledispatch
|
4
5
|
from itertools import chain
|
5
6
|
from typing import Any, TypeVar
|
6
7
|
|
8
|
+
from pydantic_core import PydanticUseDefault
|
9
|
+
|
7
10
|
from ._filters import And, FilterExpr, In, Nor, Not, NotIn, Op, Or
|
8
11
|
|
9
12
|
T = TypeVar("T")
|
10
13
|
|
11
14
|
|
12
|
-
|
13
|
-
"""
|
15
|
+
class LenientStrEnum(str, Enum):
|
16
|
+
"""A string enum allowing for case-insensitive lookups by value.
|
17
|
+
|
18
|
+
May include other internal customizations if needed.
|
19
|
+
|
20
|
+
Note: This is a bespoke, internal implementation and NOT intended as a
|
21
|
+
backport of `enum.StrEnum` from Python 3.11+.
|
22
|
+
"""
|
23
|
+
|
24
|
+
def __repr__(self) -> str:
|
25
|
+
return self.name
|
26
|
+
|
27
|
+
@classmethod
|
28
|
+
def _missing_(cls, value: object) -> Any:
|
29
|
+
# Accept case-insensitive enum values
|
30
|
+
if isinstance(value, str):
|
31
|
+
v = value.lower()
|
32
|
+
return next((e for e in cls if e.value.lower() == v), None)
|
33
|
+
return None
|
34
|
+
|
35
|
+
|
36
|
+
def default_if_none(v: Any) -> Any:
|
37
|
+
"""A before-validator validator that coerces `None` to the default field value instead."""
|
38
|
+
# https://docs.pydantic.dev/2.11/api/pydantic_core/#pydantic_core.PydanticUseDefault
|
39
|
+
if v is None:
|
40
|
+
raise PydanticUseDefault
|
41
|
+
return v
|
42
|
+
|
43
|
+
|
44
|
+
def upper_if_str(v: Any) -> Any:
|
45
|
+
return v.strip().upper() if isinstance(v, str) else v
|
46
|
+
|
47
|
+
|
48
|
+
# ----------------------------------------------------------------------------
|
49
|
+
def to_scope(v: Any) -> Any:
|
50
|
+
"""Convert eligible objects, including pre-existing `wandb` types, to an automation scope."""
|
14
51
|
from wandb.apis.public import ArtifactCollection, Project
|
15
52
|
|
16
53
|
from .scopes import ProjectScope, _ArtifactPortfolioScope, _ArtifactSequenceScope
|
@@ -23,6 +60,61 @@ def validate_scope(v: Any) -> Any:
|
|
23
60
|
return v
|
24
61
|
|
25
62
|
|
63
|
+
def to_saved_action(v: Any) -> Any:
|
64
|
+
"""If necessary (and possible), convert the object to a saved action."""
|
65
|
+
from .actions import (
|
66
|
+
DoNothing,
|
67
|
+
SavedNoOpAction,
|
68
|
+
SavedNotificationAction,
|
69
|
+
SavedWebhookAction,
|
70
|
+
SendNotification,
|
71
|
+
SendWebhook,
|
72
|
+
)
|
73
|
+
|
74
|
+
if isinstance(v, SendNotification):
|
75
|
+
return SavedNotificationAction(
|
76
|
+
integration={"id": v.integration_id},
|
77
|
+
**v.model_dump(exclude={"integration_id"}),
|
78
|
+
)
|
79
|
+
if isinstance(v, SendWebhook):
|
80
|
+
return SavedWebhookAction(
|
81
|
+
integration={"id": v.integration_id},
|
82
|
+
**v.model_dump(exclude={"integration_id"}),
|
83
|
+
)
|
84
|
+
if isinstance(v, DoNothing):
|
85
|
+
return SavedNoOpAction.model_validate(v)
|
86
|
+
|
87
|
+
return v
|
88
|
+
|
89
|
+
|
90
|
+
def to_input_action(v: Any) -> Any:
|
91
|
+
"""If necessary (and possible), convert the object to an input action."""
|
92
|
+
from .actions import (
|
93
|
+
DoNothing,
|
94
|
+
SavedNoOpAction,
|
95
|
+
SavedNotificationAction,
|
96
|
+
SavedWebhookAction,
|
97
|
+
SendNotification,
|
98
|
+
SendWebhook,
|
99
|
+
)
|
100
|
+
|
101
|
+
if isinstance(v, SavedNotificationAction):
|
102
|
+
return SendNotification(
|
103
|
+
integration_id=v.integration.id,
|
104
|
+
**v.model_dump(exclude={"integration"}),
|
105
|
+
)
|
106
|
+
if isinstance(v, SavedWebhookAction):
|
107
|
+
return SendWebhook(
|
108
|
+
integration_id=v.integration.id,
|
109
|
+
**v.model_dump(exclude={"integration"}),
|
110
|
+
)
|
111
|
+
if isinstance(v, SavedNoOpAction):
|
112
|
+
return DoNothing.model_validate(v)
|
113
|
+
|
114
|
+
return v
|
115
|
+
|
116
|
+
|
117
|
+
# ----------------------------------------------------------------------------
|
26
118
|
@singledispatch
|
27
119
|
def simplify_op(op: Op | FilterExpr) -> Op | FilterExpr:
|
28
120
|
"""Simplify a MongoDB filter by removing and unnesting redundant operators."""
|