wandb 0.19.9__py3-none-macosx_11_0_arm64.whl → 0.19.10__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.
Files changed (128) hide show
  1. wandb/__init__.py +1 -1
  2. wandb/__init__.pyi +4 -1
  3. wandb/_pydantic/__init__.py +14 -7
  4. wandb/_pydantic/base.py +44 -9
  5. wandb/_pydantic/utils.py +66 -0
  6. wandb/_pydantic/v1_compat.py +78 -56
  7. wandb/apis/public/__init__.py +2 -2
  8. wandb/apis/public/api.py +114 -2
  9. wandb/apis/public/artifacts.py +365 -673
  10. wandb/apis/public/automations.py +69 -0
  11. wandb/apis/public/integrations.py +168 -0
  12. wandb/apis/public/projects.py +29 -0
  13. wandb/apis/public/utils.py +107 -1
  14. wandb/automations/__init__.py +81 -0
  15. wandb/automations/_filters/__init__.py +40 -0
  16. wandb/automations/_filters/expressions.py +179 -0
  17. wandb/automations/_filters/operators.py +267 -0
  18. wandb/automations/_filters/run_metrics.py +183 -0
  19. wandb/automations/_generated/__init__.py +184 -0
  20. wandb/automations/_generated/create_filter_trigger.py +21 -0
  21. wandb/automations/_generated/create_generic_webhook_integration.py +43 -0
  22. wandb/automations/_generated/delete_trigger.py +19 -0
  23. wandb/automations/_generated/enums.py +33 -0
  24. wandb/automations/_generated/fragments.py +343 -0
  25. wandb/automations/_generated/generic_webhook_integrations_by_entity.py +22 -0
  26. wandb/automations/_generated/get_triggers.py +24 -0
  27. wandb/automations/_generated/get_triggers_by_entity.py +24 -0
  28. wandb/automations/_generated/input_types.py +104 -0
  29. wandb/automations/_generated/integrations_by_entity.py +22 -0
  30. wandb/automations/_generated/operations.py +710 -0
  31. wandb/automations/_generated/slack_integrations_by_entity.py +22 -0
  32. wandb/automations/_generated/update_filter_trigger.py +21 -0
  33. wandb/automations/_utils.py +123 -0
  34. wandb/automations/_validators.py +73 -0
  35. wandb/automations/actions.py +205 -0
  36. wandb/automations/automations.py +109 -0
  37. wandb/automations/events.py +235 -0
  38. wandb/automations/integrations.py +26 -0
  39. wandb/automations/scopes.py +76 -0
  40. wandb/beta/workflows.py +9 -10
  41. wandb/bin/gpu_stats +0 -0
  42. wandb/bin/wandb-core +0 -0
  43. wandb/cli/cli.py +3 -3
  44. wandb/integration/keras/keras.py +2 -1
  45. wandb/integration/langchain/wandb_tracer.py +2 -1
  46. wandb/jupyter.py +137 -118
  47. wandb/old/summary.py +0 -2
  48. wandb/proto/v3/wandb_internal_pb2.py +293 -292
  49. wandb/proto/v3/wandb_settings_pb2.py +2 -2
  50. wandb/proto/v3/wandb_telemetry_pb2.py +10 -10
  51. wandb/proto/v4/wandb_internal_pb2.py +292 -292
  52. wandb/proto/v4/wandb_settings_pb2.py +2 -2
  53. wandb/proto/v4/wandb_telemetry_pb2.py +10 -10
  54. wandb/proto/v5/wandb_internal_pb2.py +292 -292
  55. wandb/proto/v5/wandb_settings_pb2.py +2 -2
  56. wandb/proto/v5/wandb_telemetry_pb2.py +10 -10
  57. wandb/proto/v6/wandb_base_pb2.py +41 -0
  58. wandb/proto/v6/wandb_internal_pb2.py +393 -0
  59. wandb/proto/v6/wandb_server_pb2.py +78 -0
  60. wandb/proto/v6/wandb_settings_pb2.py +58 -0
  61. wandb/proto/v6/wandb_telemetry_pb2.py +52 -0
  62. wandb/proto/wandb_base_pb2.py +2 -0
  63. wandb/proto/wandb_deprecated.py +8 -0
  64. wandb/proto/wandb_internal_pb2.py +3 -1
  65. wandb/proto/wandb_server_pb2.py +2 -0
  66. wandb/proto/wandb_settings_pb2.py +2 -0
  67. wandb/proto/wandb_telemetry_pb2.py +2 -0
  68. wandb/sdk/artifacts/_generated/__init__.py +248 -0
  69. wandb/sdk/artifacts/_generated/artifact_collection_membership_files.py +43 -0
  70. wandb/sdk/artifacts/_generated/artifact_version_files.py +36 -0
  71. wandb/sdk/artifacts/_generated/create_artifact_collection_tag_assignments.py +36 -0
  72. wandb/sdk/artifacts/_generated/delete_artifact_collection_tag_assignments.py +25 -0
  73. wandb/sdk/artifacts/_generated/delete_artifact_portfolio.py +35 -0
  74. wandb/sdk/artifacts/_generated/delete_artifact_sequence.py +35 -0
  75. wandb/sdk/artifacts/_generated/enums.py +17 -0
  76. wandb/sdk/artifacts/_generated/fragments.py +186 -0
  77. wandb/sdk/artifacts/_generated/input_types.py +16 -0
  78. wandb/sdk/artifacts/_generated/move_artifact_collection.py +35 -0
  79. wandb/sdk/artifacts/_generated/operations.py +510 -0
  80. wandb/sdk/artifacts/_generated/project_artifact_collection.py +101 -0
  81. wandb/sdk/artifacts/_generated/project_artifact_collections.py +33 -0
  82. wandb/sdk/artifacts/_generated/project_artifact_type.py +24 -0
  83. wandb/sdk/artifacts/_generated/project_artifact_types.py +24 -0
  84. wandb/sdk/artifacts/_generated/project_artifacts.py +42 -0
  85. wandb/sdk/artifacts/_generated/run_input_artifacts.py +51 -0
  86. wandb/sdk/artifacts/_generated/run_output_artifacts.py +51 -0
  87. wandb/sdk/artifacts/_generated/update_artifact_portfolio.py +35 -0
  88. wandb/sdk/artifacts/_generated/update_artifact_sequence.py +35 -0
  89. wandb/sdk/artifacts/_graphql_fragments.py +56 -79
  90. wandb/sdk/artifacts/artifact.py +40 -13
  91. wandb/sdk/artifacts/artifact_manifest_entry.py +2 -1
  92. wandb/sdk/artifacts/storage_handlers/azure_handler.py +1 -0
  93. wandb/sdk/data_types/base_types/media.py +2 -3
  94. wandb/sdk/data_types/base_types/wb_value.py +34 -11
  95. wandb/sdk/data_types/html.py +36 -9
  96. wandb/sdk/data_types/image.py +12 -12
  97. wandb/sdk/data_types/table.py +5 -0
  98. wandb/sdk/data_types/trace_tree.py +2 -0
  99. wandb/sdk/data_types/utils.py +1 -1
  100. wandb/sdk/data_types/video.py +14 -26
  101. wandb/sdk/interface/interface.py +2 -0
  102. wandb/sdk/internal/profiler.py +6 -5
  103. wandb/sdk/internal/run.py +13 -6
  104. wandb/sdk/lib/apikey.py +25 -4
  105. wandb/sdk/lib/asyncio_compat.py +1 -1
  106. wandb/sdk/lib/deprecate.py +13 -22
  107. wandb/sdk/lib/disabled.py +2 -1
  108. wandb/sdk/lib/printer.py +37 -8
  109. wandb/sdk/lib/printer_asyncio.py +46 -0
  110. wandb/sdk/lib/redirect.py +10 -5
  111. wandb/sdk/service/server_sock.py +19 -14
  112. wandb/sdk/service/service.py +9 -7
  113. wandb/sdk/service/streams.py +5 -0
  114. wandb/sdk/verify/verify.py +6 -3
  115. wandb/sdk/wandb_init.py +185 -65
  116. wandb/sdk/wandb_login.py +13 -4
  117. wandb/sdk/wandb_run.py +382 -286
  118. wandb/sdk/wandb_settings.py +21 -3
  119. wandb/sdk/wandb_setup.py +49 -0
  120. wandb/util.py +29 -29
  121. {wandb-0.19.9.dist-info → wandb-0.19.10.dist-info}/METADATA +5 -5
  122. {wandb-0.19.9.dist-info → wandb-0.19.10.dist-info}/RECORD +125 -72
  123. wandb/_globals.py +0 -19
  124. wandb/sdk/internal/_generated/base.py +0 -226
  125. wandb/sdk/internal/_generated/typing_compat.py +0 -14
  126. {wandb-0.19.9.dist-info → wandb-0.19.10.dist-info}/WHEEL +0 -0
  127. {wandb-0.19.9.dist-info → wandb-0.19.10.dist-info}/entry_points.txt +0 -0
  128. {wandb-0.19.9.dist-info → wandb-0.19.10.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,21 @@
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 pydantic import Field
9
+
10
+ from wandb._pydantic import GQLBase
11
+
12
+ from .fragments import UpdateFilterTriggerResult
13
+
14
+
15
+ class UpdateFilterTrigger(GQLBase):
16
+ update_filter_trigger: Optional[UpdateFilterTriggerResult] = Field(
17
+ alias="updateFilterTrigger"
18
+ )
19
+
20
+
21
+ UpdateFilterTrigger.model_rebuild()
@@ -0,0 +1,123 @@
1
+ # ruff: noqa: UP007 # Avoid using `X | Y` for union fields, as this can cause issues with pydantic < 2.6
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import TYPE_CHECKING, Optional, Protocol, TypedDict
6
+
7
+ from wandb._pydantic import to_json
8
+ from wandb.automations.events import InputEvent
9
+ from wandb.automations.scopes import InputScope
10
+
11
+ from ._generated import (
12
+ CreateFilterTriggerInput,
13
+ TriggeredActionConfig,
14
+ UpdateFilterTriggerInput,
15
+ )
16
+ from .actions import (
17
+ ActionType,
18
+ DoNothing,
19
+ DoNotification,
20
+ DoWebhook,
21
+ InputAction,
22
+ SavedAction,
23
+ )
24
+
25
+ if TYPE_CHECKING:
26
+ from typing_extensions import Unpack
27
+
28
+ from .automations import Automation, NewAutomation
29
+
30
+
31
+ class HasId(Protocol):
32
+ id: str
33
+
34
+
35
+ def extract_id(obj: HasId | str) -> str:
36
+ return obj.id if hasattr(obj, "id") else obj
37
+
38
+
39
+ # NOTE: `QueueJobActionInput` for defining a Launch job is deprecated,
40
+ # so we deliberately don't currently expose it in the API for *creating* automations.
41
+ _ACTION_CONFIG_KEYS: dict[ActionType, str] = {
42
+ ActionType.NOTIFICATION: "notification_action_input",
43
+ ActionType.GENERIC_WEBHOOK: "generic_webhook_action_input",
44
+ ActionType.NO_OP: "no_op_action_input",
45
+ }
46
+
47
+
48
+ class InputActionConfig(TriggeredActionConfig):
49
+ """A `TriggeredActionConfig` that prepares the action config for saving an automation."""
50
+
51
+ notification_action_input: Optional[DoNotification] = None
52
+ generic_webhook_action_input: Optional[DoWebhook] = None
53
+ no_op_action_input: Optional[DoNothing] = None
54
+
55
+
56
+ def prepare_action_input(obj: InputAction | SavedAction) -> InputActionConfig:
57
+ """Return a `TriggeredActionConfig` as required in the input schema of CreateFilterTriggerInput."""
58
+ key = _ACTION_CONFIG_KEYS[obj.action_type]
59
+ # Delegate the validators for each ActionInput type to handle custom logic
60
+ # for converting from saved action types to an input action type.
61
+ return InputActionConfig.model_validate({key: obj})
62
+
63
+
64
+ class AutomationParams(TypedDict, total=False):
65
+ """Keyword arguments that can be passed to create or update an automation."""
66
+
67
+ name: str
68
+ description: str
69
+ enabled: bool
70
+
71
+ scope: InputScope
72
+ event: InputEvent
73
+ action: InputAction
74
+
75
+
76
+ def prepare_create_input(
77
+ obj: NewAutomation, **updates: Unpack[AutomationParams]
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
+ )
98
+
99
+
100
+ def prepare_update_input(
101
+ obj: Automation, **updates: Unpack[AutomationParams]
102
+ ) -> UpdateFilterTriggerInput:
103
+ """Prepares the payload to update an automation in a GraphQL request."""
104
+ from .events import SavedEventFilter
105
+
106
+ updated = obj.model_copy(update=updates)
107
+
108
+ return UpdateFilterTriggerInput(
109
+ id=updated.id,
110
+ name=updated.name,
111
+ description=updated.description,
112
+ enabled=updated.enabled,
113
+ scope_type=updated.scope.scope_type,
114
+ scope_id=updated.scope.id,
115
+ triggering_event_type=updated.event.event_type,
116
+ event_filter=to_json(
117
+ updated.event.filter.filter
118
+ if isinstance(updated.event.filter, SavedEventFilter)
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(),
123
+ )
@@ -0,0 +1,73 @@
1
+ from __future__ import annotations
2
+
3
+ from functools import singledispatch
4
+ from itertools import chain
5
+ from typing import Any, TypeVar
6
+
7
+ from ._filters import And, FilterExpr, In, Nor, Not, NotIn, Op, Or
8
+
9
+ T = TypeVar("T")
10
+
11
+
12
+ def validate_scope(v: Any) -> Any:
13
+ """Convert a familiar wandb `Project` or `ArtifactCollection` object to an automation scope."""
14
+ from wandb.apis.public import ArtifactCollection, Project
15
+
16
+ from .scopes import ProjectScope, _ArtifactPortfolioScope, _ArtifactSequenceScope
17
+
18
+ if isinstance(v, Project):
19
+ return ProjectScope(id=v.id, name=v.name)
20
+ if isinstance(v, ArtifactCollection):
21
+ cls = _ArtifactSequenceScope if v.is_sequence() else _ArtifactPortfolioScope
22
+ return cls(id=v.id, name=v.name)
23
+ return v
24
+
25
+
26
+ @singledispatch
27
+ def simplify_op(op: Op | FilterExpr) -> Op | FilterExpr:
28
+ """Simplify a MongoDB filter by removing and unnesting redundant operators."""
29
+ return op
30
+
31
+
32
+ @simplify_op.register
33
+ def _(op: And) -> Op:
34
+ # {"$and": []} -> {"$and": []}
35
+ if not (args := op.and_):
36
+ return op
37
+
38
+ # {"$and": [op]} -> op
39
+ if len(args) == 1:
40
+ return simplify_op(args[0])
41
+
42
+ # {"$and": [op, {"$and": [op2, ...]}]} -> {"$and": [op, op2, ...]}
43
+ flattened = chain.from_iterable(x.and_ if isinstance(x, And) else [x] for x in args)
44
+ return And(and_=map(simplify_op, flattened))
45
+
46
+
47
+ @simplify_op.register
48
+ def _(op: Or) -> Op:
49
+ # {"$or": []} -> {"$or": []}
50
+ if not (args := op.or_):
51
+ return op
52
+
53
+ # {"$or": [op]} -> op
54
+ if len(args) == 1:
55
+ return simplify_op(args[0])
56
+
57
+ # {"$or": [op, {"$or": [op2, ...]}]} -> {"$or": [op, op2, ...]}
58
+ flattened = chain.from_iterable(x.or_ if isinstance(x, Or) else [x] for x in args)
59
+ return Or(or_=map(simplify_op, flattened))
60
+
61
+
62
+ @simplify_op.register
63
+ def _(op: Not) -> Op:
64
+ inner = op.not_
65
+
66
+ # {"$not": {"$not": op}} -> op
67
+ # {"$not": {"$or": [op, ...]}} -> {"$nor": [op, ...]}
68
+ # {"$not": {"$nor": [op, ...]}} -> {"$or": [op, ...]}
69
+ # {"$not": {"$in": [op, ...]}} -> {"$nin": [op, ...]}
70
+ # {"$not": {"$nin": [op, ...]}} -> {"$in": [op, ...]}
71
+ if isinstance(inner, (Not, Or, Nor, In, NotIn)):
72
+ return simplify_op(~inner)
73
+ return Not(not_=simplify_op(inner))
@@ -0,0 +1,205 @@
1
+ """Actions that are triggered by W&B Automations."""
2
+
3
+ # ruff: noqa: UP007 # Avoid using `X | Y` for union fields, as this can cause issues with pydantic < 2.6
4
+
5
+ from __future__ import annotations
6
+
7
+ from typing import Any, Dict, List, Literal, Optional, Union
8
+
9
+ from pydantic import Field
10
+ from typing_extensions import Self, TypeAlias, get_args
11
+
12
+ from wandb._pydantic import (
13
+ SerializedToJson,
14
+ field_validator,
15
+ model_validator,
16
+ pydantic_isinstance,
17
+ )
18
+
19
+ from ._generated import (
20
+ AlertSeverity,
21
+ GenericWebhookActionFields,
22
+ GenericWebhookActionInput,
23
+ NoOpActionFields,
24
+ NoOpTriggeredActionInput,
25
+ NotificationActionFields,
26
+ NotificationActionInput,
27
+ QueueJobActionFields,
28
+ TriggeredActionType,
29
+ )
30
+ from .integrations import SlackIntegration, WebhookIntegration
31
+
32
+ # Note: Pydantic doesn't like `list['JsonValue']` or `dict[str, 'JsonValue']`,
33
+ # which causes a RecursionError.
34
+ JsonValue: TypeAlias = Union[
35
+ List[Any],
36
+ Dict[str, Any],
37
+ # NOTE: For now, we're not expecting any doubly-serialized strings, as this makes validation logic easier, but revisit and revise if needed.
38
+ # str,
39
+ bool,
40
+ int,
41
+ float,
42
+ None,
43
+ ]
44
+
45
+ # NOTE: Name shortened for readability and defined publicly for easier access
46
+ ActionType = TriggeredActionType
47
+ """The type of action triggered by an automation."""
48
+
49
+
50
+ # ------------------------------------------------------------------------------
51
+ # Saved types: for parsing response data from saved automations
52
+
53
+
54
+ # NOTE: `QueueJobActionInput` for defining a Launch job is deprecated,
55
+ # so while we allow parsing it from previously saved Automations, we deliberately
56
+ # don't currently expose it in the API for creating automations.
57
+ class SavedLaunchJobAction(QueueJobActionFields):
58
+ action_type: Literal[ActionType.QUEUE_JOB] = ActionType.QUEUE_JOB
59
+
60
+
61
+ class SavedNotificationAction(NotificationActionFields):
62
+ action_type: Literal[ActionType.NOTIFICATION] = ActionType.NOTIFICATION
63
+
64
+
65
+ class SavedWebhookAction(GenericWebhookActionFields):
66
+ action_type: Literal[ActionType.GENERIC_WEBHOOK] = ActionType.GENERIC_WEBHOOK
67
+
68
+ # We override the type of the `requestPayload` field since the original GraphQL
69
+ # schema (and generated class) effectively defines it as a string, when we know
70
+ # and need to anticipate the expected structure of the JSON-serialized data.
71
+ request_payload: Optional[SerializedToJson[JsonValue]] = Field( # type: ignore[assignment]
72
+ default=None, alias="requestPayload"
73
+ )
74
+
75
+
76
+ class SavedNoOpAction(NoOpActionFields):
77
+ action_type: Literal[ActionType.NO_OP] = ActionType.NO_OP
78
+
79
+
80
+ # for type annotations
81
+ SavedAction = Union[
82
+ SavedLaunchJobAction,
83
+ SavedNotificationAction,
84
+ SavedWebhookAction,
85
+ SavedNoOpAction,
86
+ ]
87
+ # for runtime type checks
88
+ SavedActionTypes: tuple[type, ...] = get_args(SavedAction)
89
+
90
+
91
+ # ------------------------------------------------------------------------------
92
+ # Input types: for creating or updating automations
93
+ class DoNotification(NotificationActionInput):
94
+ """Schema for defining a triggered notification action."""
95
+
96
+ action_type: Literal[ActionType.NOTIFICATION] = ActionType.NOTIFICATION
97
+
98
+ # Note: Validation aliases match arg names from `wandb.alert()` to allow
99
+ # continuity with previous API.
100
+ title: str = Field(default="", validation_alias="title")
101
+ message: str = Field(default="", validation_alias="text")
102
+ severity: AlertSeverity = Field(
103
+ default=AlertSeverity.INFO, validation_alias="level"
104
+ )
105
+
106
+ @field_validator("severity", mode="before")
107
+ @classmethod
108
+ def _validate_severity(cls, v: Any) -> Any:
109
+ # Be helpful by accepting case-insensitive strings
110
+ return v.upper() if isinstance(v, str) else v
111
+
112
+ @model_validator(mode="before")
113
+ @classmethod
114
+ def _from_saved(cls, v: Any) -> Any:
115
+ """Convert an action on a saved automation to a new/input action."""
116
+ if pydantic_isinstance(v, SavedNotificationAction):
117
+ return cls(
118
+ integration_id=v.integration.id,
119
+ title=v.title,
120
+ message=v.message,
121
+ severity=v.severity,
122
+ )
123
+ return v
124
+
125
+ @classmethod
126
+ def from_integration(
127
+ cls,
128
+ integration: SlackIntegration,
129
+ *,
130
+ title: str = "",
131
+ text: str = "",
132
+ level: AlertSeverity = AlertSeverity.INFO,
133
+ ) -> Self:
134
+ """Define a notification action that sends to the given (Slack) integration."""
135
+ integration = SlackIntegration.model_validate(integration)
136
+ return cls(
137
+ integration_id=integration.id,
138
+ title=title,
139
+ message=text,
140
+ severity=level,
141
+ )
142
+
143
+
144
+ class DoWebhook(GenericWebhookActionInput):
145
+ """Schema for defining a triggered webhook action."""
146
+
147
+ action_type: Literal[ActionType.GENERIC_WEBHOOK] = ActionType.GENERIC_WEBHOOK
148
+
149
+ # overrides the generated field type to parse/serialize JSON strings
150
+ request_payload: Optional[SerializedToJson[JsonValue]] = Field( # type: ignore[assignment]
151
+ default=None, alias="requestPayload"
152
+ )
153
+
154
+ @model_validator(mode="before")
155
+ @classmethod
156
+ def _from_saved(cls, v: Any) -> Any:
157
+ """Convert an action on a saved automation to a new/input action."""
158
+ if pydantic_isinstance(v, SavedWebhookAction):
159
+ return cls(
160
+ integration_id=v.integration.id,
161
+ request_payload=v.request_payload,
162
+ )
163
+ return v
164
+
165
+ @classmethod
166
+ def from_integration(
167
+ cls,
168
+ integration: WebhookIntegration,
169
+ *,
170
+ request_payload: Optional[SerializedToJson[JsonValue]] = None,
171
+ ) -> Self:
172
+ """Define a webhook action that sends to the given (webhook) integration."""
173
+ integration = WebhookIntegration.model_validate(integration)
174
+ return cls(integration_id=integration.id, request_payload=request_payload)
175
+
176
+
177
+ class DoNothing(NoOpTriggeredActionInput):
178
+ """Schema for defining a triggered no-op action."""
179
+
180
+ action_type: Literal[ActionType.NO_OP] = ActionType.NO_OP
181
+
182
+ no_op: bool = True # prevent exclusion on `.model_dump(exclude_none=True)`
183
+
184
+ @field_validator("no_op", mode="before")
185
+ @classmethod
186
+ def _ensure_nonnull(cls, v: Any) -> Any:
187
+ # Ensuring the value isn't None complies with validation and
188
+ # prevents the field (and action data) from getting excluded on
189
+ # `.model_dump(exclude_none=True)`
190
+ return True if (v is None) else v
191
+
192
+
193
+ DoNotification.model_rebuild()
194
+ DoWebhook.model_rebuild()
195
+ DoNothing.model_rebuild()
196
+
197
+
198
+ # for type annotations
199
+ InputAction = Union[
200
+ DoNotification,
201
+ DoWebhook,
202
+ DoNothing,
203
+ ]
204
+ # for runtime type checks
205
+ InputActionTypes: tuple[type, ...] = get_args(InputAction)
@@ -0,0 +1,109 @@
1
+ # ruff: noqa: UP007 # Avoid using `X | Y` for union fields, as this can cause issues with pydantic < 2.6
2
+
3
+ from __future__ import annotations
4
+
5
+ from datetime import datetime
6
+ from typing import TYPE_CHECKING, Optional
7
+
8
+ from pydantic import Field
9
+
10
+ from wandb._pydantic import Base, GQLId
11
+
12
+ from ._generated import TriggerFields, UserFields
13
+ from .actions import InputAction, SavedAction
14
+ from .events import InputEvent, SavedEvent
15
+ from .scopes import InputScope, SavedScope
16
+
17
+ if TYPE_CHECKING:
18
+ pass
19
+
20
+
21
+ # ------------------------------------------------------------------------------
22
+ # Saved types: for parsing response data from saved automations
23
+ class Automation(TriggerFields):
24
+ """A local instance of a saved W&B automation."""
25
+
26
+ id: GQLId
27
+
28
+ created_by: UserFields = Field(repr=False, frozen=True, alias="createdBy")
29
+ created_at: datetime = Field(repr=False, frozen=True, alias="createdAt")
30
+ updated_at: Optional[datetime] = Field(repr=False, frozen=True, alias="updatedAt")
31
+
32
+ name: str
33
+ description: Optional[str]
34
+
35
+ scope: SavedScope = Field(discriminator="typename__")
36
+ event: SavedEvent
37
+ action: SavedAction = Field(discriminator="typename__", alias="triggeredAction")
38
+
39
+ enabled: bool
40
+
41
+ # def save(
42
+ # self, api: Api | None = None, **updates: Unpack[AutomationParams]
43
+ # ) -> Automation:
44
+ # """Save this existing automation to the server, applying any local changes.
45
+
46
+ # Args:
47
+ # api: The API instance to use. If not provided, the default API instance is used.
48
+ # updates:
49
+ # Any final updates to apply to the automation before
50
+ # saving it. These override previously-set values, if any.
51
+
52
+ # Returns:
53
+ # The updated automation.
54
+ # """
55
+ # from wandb import Api
56
+
57
+ # return (api or Api()).update_automation(self, **updates)
58
+
59
+ # def delete(self, api: Api | None = None) -> DeleteTriggerResult:
60
+ # """Delete this automation from the server.
61
+
62
+ # Args:
63
+ # api: The API instance to use. If not provided, the default API instance is used.
64
+ # """
65
+ # from wandb import Api
66
+
67
+ # return (api or Api()).delete_automation(self)
68
+
69
+
70
+ class NewAutomation(Base):
71
+ """An automation which can hold any of the fields of a NewAutomation, but may not be complete yet."""
72
+
73
+ name: Optional[str] = None
74
+ description: Optional[str] = None
75
+ enabled: bool = True
76
+
77
+ scope: Optional[InputScope] = Field(discriminator="typename__", default=None)
78
+ event: Optional[InputEvent] = Field(discriminator="event_type", default=None)
79
+ action: Optional[InputAction] = Field(discriminator="action_type", default=None)
80
+
81
+ # def save(
82
+ # self, api: Api | None = None, **updates: Unpack[AutomationParams]
83
+ # ) -> Automation:
84
+ # """Create this automation by saving it to the server.
85
+
86
+ # Args:
87
+ # api: The API instance to use. If not provided, the default API instance is used.
88
+ # updates:
89
+ # Any final updates to apply to the automation before
90
+ # saving it. These override previously-set values, if any.
91
+
92
+ # Returns:
93
+ # The created automation.
94
+ # """
95
+ # from wandb import Api
96
+
97
+ # return (api or Api()).create_automation(self, **updates)
98
+
99
+
100
+ class PreparedAutomation(NewAutomation):
101
+ """A fully defined automation, ready to be sent to the server to create or update it."""
102
+
103
+ name: str
104
+ description: Optional[str] = None
105
+ enabled: bool = True
106
+
107
+ scope: InputScope
108
+ event: InputEvent
109
+ action: InputAction