wandb 0.19.10__py3-none-musllinux_1_2_aarch64.whl → 0.19.11__py3-none-musllinux_1_2_aarch64.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 (90) hide show
  1. wandb/__init__.py +1 -1
  2. wandb/__init__.pyi +3 -3
  3. wandb/_pydantic/__init__.py +2 -3
  4. wandb/_pydantic/base.py +11 -31
  5. wandb/_pydantic/utils.py +8 -1
  6. wandb/_pydantic/v1_compat.py +3 -3
  7. wandb/apis/public/api.py +590 -22
  8. wandb/apis/public/artifacts.py +13 -5
  9. wandb/apis/public/automations.py +1 -1
  10. wandb/apis/public/integrations.py +22 -10
  11. wandb/apis/public/registries/__init__.py +0 -0
  12. wandb/apis/public/registries/_freezable_list.py +179 -0
  13. wandb/apis/public/{registries.py → registries/registries_search.py} +22 -129
  14. wandb/apis/public/registries/registry.py +357 -0
  15. wandb/apis/public/registries/utils.py +140 -0
  16. wandb/apis/public/runs.py +58 -56
  17. wandb/automations/__init__.py +16 -24
  18. wandb/automations/_filters/expressions.py +12 -10
  19. wandb/automations/_filters/operators.py +10 -19
  20. wandb/automations/_filters/run_metrics.py +231 -82
  21. wandb/automations/_generated/__init__.py +27 -34
  22. wandb/automations/_generated/create_automation.py +17 -0
  23. wandb/automations/_generated/delete_automation.py +17 -0
  24. wandb/automations/_generated/fragments.py +40 -25
  25. wandb/automations/_generated/{get_triggers.py → get_automations.py} +5 -5
  26. wandb/automations/_generated/{get_triggers_by_entity.py → get_automations_by_entity.py} +7 -5
  27. wandb/automations/_generated/operations.py +35 -98
  28. wandb/automations/_generated/update_automation.py +17 -0
  29. wandb/automations/_utils.py +178 -64
  30. wandb/automations/_validators.py +94 -2
  31. wandb/automations/actions.py +113 -98
  32. wandb/automations/automations.py +47 -69
  33. wandb/automations/events.py +139 -87
  34. wandb/automations/integrations.py +23 -4
  35. wandb/automations/scopes.py +22 -20
  36. wandb/bin/gpu_stats +0 -0
  37. wandb/bin/wandb-core +0 -0
  38. wandb/env.py +11 -0
  39. wandb/old/settings.py +4 -1
  40. wandb/proto/v3/wandb_internal_pb2.py +240 -236
  41. wandb/proto/v3/wandb_telemetry_pb2.py +10 -10
  42. wandb/proto/v4/wandb_internal_pb2.py +236 -236
  43. wandb/proto/v4/wandb_telemetry_pb2.py +10 -10
  44. wandb/proto/v5/wandb_internal_pb2.py +236 -236
  45. wandb/proto/v5/wandb_telemetry_pb2.py +10 -10
  46. wandb/proto/v6/wandb_internal_pb2.py +236 -236
  47. wandb/proto/v6/wandb_telemetry_pb2.py +10 -10
  48. wandb/sdk/artifacts/_generated/__init__.py +42 -1
  49. wandb/sdk/artifacts/_generated/add_aliases.py +21 -0
  50. wandb/sdk/artifacts/_generated/delete_aliases.py +21 -0
  51. wandb/sdk/artifacts/_generated/fetch_linked_artifacts.py +67 -0
  52. wandb/sdk/artifacts/_generated/fragments.py +35 -0
  53. wandb/sdk/artifacts/_generated/input_types.py +12 -0
  54. wandb/sdk/artifacts/_generated/operations.py +101 -0
  55. wandb/sdk/artifacts/_generated/update_artifact.py +26 -0
  56. wandb/sdk/artifacts/_graphql_fragments.py +1 -0
  57. wandb/sdk/artifacts/_validators.py +120 -1
  58. wandb/sdk/artifacts/artifact.py +380 -203
  59. wandb/sdk/artifacts/artifact_file_cache.py +4 -6
  60. wandb/sdk/artifacts/artifact_manifest_entry.py +11 -2
  61. wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +182 -1
  62. wandb/sdk/artifacts/storage_policy.py +3 -0
  63. wandb/sdk/data_types/video.py +46 -32
  64. wandb/sdk/interface/interface.py +2 -3
  65. wandb/sdk/internal/internal_api.py +21 -31
  66. wandb/sdk/internal/sender.py +5 -2
  67. wandb/sdk/launch/sweeps/utils.py +8 -0
  68. wandb/sdk/projects/_generated/__init__.py +47 -0
  69. wandb/sdk/projects/_generated/delete_project.py +22 -0
  70. wandb/sdk/projects/_generated/enums.py +4 -0
  71. wandb/sdk/projects/_generated/fetch_registry.py +22 -0
  72. wandb/sdk/projects/_generated/fragments.py +41 -0
  73. wandb/sdk/projects/_generated/input_types.py +13 -0
  74. wandb/sdk/projects/_generated/operations.py +88 -0
  75. wandb/sdk/projects/_generated/rename_project.py +27 -0
  76. wandb/sdk/projects/_generated/upsert_registry_project.py +27 -0
  77. wandb/sdk/service/service.py +9 -1
  78. wandb/sdk/wandb_init.py +32 -5
  79. wandb/sdk/wandb_run.py +37 -9
  80. wandb/sdk/wandb_settings.py +6 -7
  81. wandb/sdk/wandb_setup.py +12 -0
  82. wandb/util.py +7 -3
  83. {wandb-0.19.10.dist-info → wandb-0.19.11.dist-info}/METADATA +1 -1
  84. {wandb-0.19.10.dist-info → wandb-0.19.11.dist-info}/RECORD +87 -70
  85. wandb/automations/_generated/create_filter_trigger.py +0 -21
  86. wandb/automations/_generated/delete_trigger.py +0 -19
  87. wandb/automations/_generated/update_filter_trigger.py +0 -21
  88. {wandb-0.19.10.dist-info → wandb-0.19.11.dist-info}/WHEEL +0 -0
  89. {wandb-0.19.10.dist-info → wandb-0.19.11.dist-info}/entry_points.txt +0 -0
  90. {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()
@@ -2,30 +2,63 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import TYPE_CHECKING, Optional, Protocol, TypedDict
5
+ from typing import Any, Collection, Final, Optional, Protocol, TypedDict
6
6
 
7
- from wandb._pydantic import to_json
8
- from wandb.automations.events import InputEvent
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
- if TYPE_CHECKING:
26
- from typing_extensions import Unpack
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
- from .automations import Automation, NewAutomation
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
- # 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] = {
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
- notification_action_input: Optional[DoNotification] = None
52
- generic_webhook_action_input: Optional[DoWebhook] = None
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 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})
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 AutomationParams(TypedDict, total=False):
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
- 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
- )
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
- def prepare_update_input(
101
- obj: Automation, **updates: Unpack[AutomationParams]
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
- from .events import SavedEventFilter
105
-
106
- updated = obj.model_copy(update=updates)
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=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(),
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
  )
@@ -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
- def validate_scope(v: Any) -> Any:
13
- """Convert a familiar wandb `Project` or `ArtifactCollection` object to an automation scope."""
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."""