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
@@ -7,11 +7,12 @@ from __future__ import annotations
7
7
  from typing import TYPE_CHECKING, Any, Literal, Optional, Union
8
8
 
9
9
  from pydantic import Field
10
- from typing_extensions import Self, TypeAlias, get_args
10
+ from typing_extensions import Annotated, Self, get_args
11
11
 
12
12
  from wandb._pydantic import (
13
13
  GQLBase,
14
14
  SerializedToJson,
15
+ ensure_json,
15
16
  field_validator,
16
17
  model_validator,
17
18
  pydantic_isinstance,
@@ -19,26 +20,34 @@ from wandb._pydantic import (
19
20
 
20
21
  from ._filters import And, MongoLikeFilter, Or
21
22
  from ._filters.expressions import FilterableField
22
- from ._filters.run_metrics import (
23
- Agg,
24
- MetricChangeFilter,
25
- MetricOperand,
26
- MetricThresholdFilter,
27
- )
28
- from ._generated import EventTriggeringConditionType, FilterEventFields
29
- from ._validators import simplify_op
30
- from .actions import InputAction, InputActionTypes
31
- from .scopes import ArtifactCollectionScope, InputScope, ProjectScope
23
+ from ._filters.run_metrics import MetricChangeFilter, MetricThresholdFilter, MetricVal
24
+ from ._generated import FilterEventFields
25
+ from ._validators import LenientStrEnum, simplify_op
26
+ from .actions import InputAction, InputActionTypes, SavedActionTypes
27
+ from .scopes import ArtifactCollectionScope, AutomationScope, ProjectScope
32
28
 
33
29
  if TYPE_CHECKING:
34
30
  from .automations import NewAutomation
35
31
 
36
32
 
37
33
  # NOTE: Re-defined publicly with a more readable name for easier access
38
- EventType = EventTriggeringConditionType
39
- """The type of event that triggers an automation."""
34
+ class EventType(LenientStrEnum):
35
+ """The type of event that triggers an automation."""
36
+
37
+ # ---------------------------------------------------------------------------
38
+ # Events triggered by GraphQL mutations
39
+ UPDATE_ARTIFACT_ALIAS = "UPDATE_ARTIFACT_ALIAS" # NOTE: Avoid in new automations
40
40
 
41
- Agg = Agg
41
+ CREATE_ARTIFACT = "CREATE_ARTIFACT"
42
+ ADD_ARTIFACT_ALIAS = "ADD_ARTIFACT_ALIAS"
43
+ LINK_ARTIFACT = "LINK_MODEL"
44
+ # Note: "LINK_MODEL" is the (legacy) value expected by the backend, but we
45
+ # name it "LINK_ARTIFACT" here in the public API for clarity and consistency.
46
+
47
+ # ---------------------------------------------------------------------------
48
+ # Events triggered by Run conditions
49
+ RUN_METRIC_THRESHOLD = "RUN_METRIC"
50
+ RUN_METRIC_CHANGE = "RUN_METRIC_CHANGE"
42
51
 
43
52
 
44
53
  # ------------------------------------------------------------------------------
@@ -46,11 +55,11 @@ Agg = Agg
46
55
 
47
56
 
48
57
  # Note: In GQL responses containing saved automation data, the filter is wrapped in an extra `filter` key.
49
- class SavedEventFilter(GQLBase):
50
- filter: SerializedToJson[MongoLikeFilter] = Field(default_factory=And)
58
+ class _WrappedSavedEventFilter(GQLBase): # from: TriggeringFilterEvent
59
+ filter: SerializedToJson[MongoLikeFilter] = And()
51
60
 
52
61
 
53
- class _InnerRunMetricFilter(GQLBase): # from `RunMetricFilter`
62
+ class _WrappedMetricFilter(GQLBase): # from: RunMetricFilter
54
63
  threshold_filter: Optional[MetricThresholdFilter] = None
55
64
  change_filter: Optional[MetricChangeFilter] = None
56
65
 
@@ -65,58 +74,67 @@ class _InnerRunMetricFilter(GQLBase): # from `RunMetricFilter`
65
74
 
66
75
  @model_validator(mode="after")
67
76
  def _ensure_exactly_one_set(self) -> Self:
68
- set_field_names = [name for name, val in self if (val is not None)]
69
- if not set_field_names:
70
- raise ValueError("Must specify a run metric filter")
71
- if len(set_field_names) > 1:
72
- names = ", ".join(map(repr, set_field_names))
73
- raise ValueError(f"Must specify a single run metric filter, got: {names}")
77
+ set_fields = [name for name, val in self if (val is not None)]
78
+
79
+ if not set_fields:
80
+ all_names = ", ".join(map(repr, type(self).model_fields))
81
+ raise ValueError(f"Expected one of: {all_names}")
82
+
83
+ if len(set_fields) > 1:
84
+ set_names = ", ".join(map(repr, set_fields))
85
+ raise ValueError(f"Expected exactly one metric filter, got: {set_names}")
86
+
74
87
  return self
75
88
 
89
+ @property
90
+ def event_type(self) -> EventType:
91
+ if self.threshold_filter is not None:
92
+ return EventType.RUN_METRIC_THRESHOLD
93
+ if self.change_filter is not None:
94
+ return EventType.RUN_METRIC_CHANGE
95
+ raise RuntimeError("Expected one of: `threshold_filter` or `change_filter`")
96
+
97
+
98
+ class RunMetricFilter(GQLBase): # from: TriggeringRunMetricEvent
99
+ run: Annotated[SerializedToJson[MongoLikeFilter], Field(alias="run_filter")] = And()
100
+ metric: Annotated[_WrappedMetricFilter, Field(alias="run_metric_filter")]
76
101
 
77
- class RunMetricFilter(GQLBase): # from `RunMetricEvent`
78
- run_filter: SerializedToJson[MongoLikeFilter] = Field(default_factory=And)
79
- run_metric_filter: _InnerRunMetricFilter
102
+ # ------------------------------------------------------------------------------
103
+ legacy_metric_filter: Annotated[
104
+ Optional[SerializedToJson[MetricThresholdFilter]],
105
+ Field(alias="metric_filter", deprecated=True),
106
+ ] = None
107
+ """Deprecated legacy field that was previously used to define run metric threshold events.
80
108
 
81
- #: Legacy field to define triggers on run metrics from absolute thresholds. Use `run_metric_filter` instead.
82
- metric_filter: Optional[SerializedToJson[MetricThresholdFilter]] = Field(
83
- default=None,
84
- deprecated="The `metric_filter` field is deprecated: use `run_metric_filter` instead.",
85
- )
109
+ For new automations, use the `metric` field (`run_metric_filter` JSON alias) instead.
110
+ """
86
111
 
87
112
  @model_validator(mode="before")
88
113
  @classmethod
89
114
  def _wrap_metric_filter(cls, v: Any) -> Any:
90
115
  if pydantic_isinstance(v, (MetricThresholdFilter, MetricChangeFilter)):
91
- # If we're only given an (unwrapped) metric filter, automatically wrap it
92
- # in the appropriate nested structure, and use the default run filter.
93
-
94
- # Delegate to the inner validator to further wrap the filter as appropriate.
95
- return cls(run_metric_filter=_InnerRunMetricFilter.model_validate(v))
116
+ # If only an (unnested) metric filter is given, nest it under the
117
+ # `metric` field, delegating to inner validator(s) for further
118
+ # wrapping/nesting, if needed.
119
+ # This is necessary to conform to the expected backend schema.
120
+ return cls(metric=v)
96
121
  return v
97
122
 
98
- @field_validator("run_filter", mode="after")
99
- @classmethod
123
+ @field_validator("run", mode="after")
100
124
  def _wrap_run_filter(cls, v: MongoLikeFilter) -> Any:
101
125
  v_new = simplify_op(v)
102
- return (
103
- And.model_validate(v_new)
104
- if pydantic_isinstance(v_new, And)
105
- else And(and_=[v_new])
106
- )
126
+ return v_new if pydantic_isinstance(v_new, And) else And(and_=[v_new])
107
127
 
108
128
 
109
- # type alias defined for naming consistency/clarity
110
- SavedRunMetricFilter: TypeAlias = RunMetricFilter
129
+ class SavedEvent(FilterEventFields): # from: FilterEventTriggeringCondition
130
+ """A triggering event from a saved automation."""
111
131
 
132
+ event_type: Annotated[EventType, Field(frozen=True)] # type: ignore[assignment]
112
133
 
113
- class SavedEvent(FilterEventFields): # from `FilterEventTriggeringCondition`
114
- """A more introspection-friendly representation of a triggering event from a saved automation."""
115
-
116
- # We override the type of the `filter` field since the original GraphQL
117
- # schema (and generated class) defines it as a JSONString (str), but we
118
- # have more specific expectations for the structure of the JSON data.
119
- filter: SerializedToJson[Union[SavedEventFilter, SavedRunMetricFilter]]
134
+ # We override the type of the `filter` field in order to enforce the expected
135
+ # structure for the JSON data when validating and serializing.
136
+ filter: SerializedToJson[Union[_WrappedSavedEventFilter, RunMetricFilter]]
137
+ """The condition(s) under which this event triggers an automation."""
120
138
 
121
139
 
122
140
  # ------------------------------------------------------------------------------
@@ -127,98 +145,123 @@ class SavedEvent(FilterEventFields): # from `FilterEventTriggeringCondition`
127
145
  # eventFilter returned in responses for saved automations.
128
146
  class _BaseEventInput(GQLBase):
129
147
  event_type: EventType
130
- scope: InputScope
148
+
149
+ scope: AutomationScope
150
+ """The scope of the event."""
151
+
131
152
  filter: SerializedToJson[Any]
132
153
 
133
- def add_action(self, action: InputAction) -> NewAutomation:
134
- """Define an executed action to be triggered by this event."""
154
+ def then(self, action: InputAction) -> NewAutomation:
155
+ """Define a new Automation in which this event triggers the given action."""
135
156
  from .automations import NewAutomation
136
157
 
137
- if isinstance(action, InputActionTypes):
138
- return NewAutomation(scope=self.scope, event=self, action=action)
158
+ if isinstance(action, (InputActionTypes, SavedActionTypes)):
159
+ return NewAutomation(event=self, action=action)
139
160
 
140
161
  raise TypeError(f"Expected a valid action, got: {type(action).__qualname__!r}")
141
162
 
142
163
  def __rshift__(self, other: InputAction) -> NewAutomation:
143
- """Supports `event >> action` as syntactic sugar to combine an event and action."""
144
- return self.add_action(other)
164
+ """Implements `event >> action` to define an Automation with this event and action."""
165
+ return self.then(other)
145
166
 
146
167
 
168
+ # ------------------------------------------------------------------------------
169
+ # Events that trigger on specific mutations in the backend
147
170
  class _BaseMutationEventInput(_BaseEventInput):
148
- event_type: EventType
149
- scope: InputScope
150
- filter: SerializedToJson[MongoLikeFilter] = Field(default_factory=And)
171
+ filter: SerializedToJson[MongoLikeFilter] = And()
172
+ """Additional condition(s), if any, that must be met for this event to trigger an automation."""
151
173
 
152
174
  @field_validator("filter", mode="after")
153
- @classmethod
154
175
  def _wrap_filter(cls, v: Any) -> Any:
155
176
  """Ensure the given filter is wrapped like: `{"$or": [{"$and": [<original_filter>]}]}`.
156
177
 
157
178
  This is awkward but necessary, because the frontend expects this format.
158
179
  """
159
180
  v_new = simplify_op(v)
160
- v_new = (
161
- And.model_validate(v_new)
162
- if pydantic_isinstance(v_new, And)
163
- else And(and_=[v_new])
164
- )
165
- v_new = Or(or_=[v_new])
166
- return v_new
181
+ v_new = v_new if pydantic_isinstance(v_new, And) else And(and_=[v_new])
182
+ return Or(or_=[v_new])
167
183
 
168
184
 
169
185
  class OnLinkArtifact(_BaseMutationEventInput):
170
186
  """A new artifact is linked to a collection."""
171
187
 
172
- event_type: Literal[EventType.LINK_MODEL] = EventType.LINK_MODEL
173
- scope: InputScope
188
+ event_type: Literal[EventType.LINK_ARTIFACT] = EventType.LINK_ARTIFACT
174
189
 
175
190
 
176
191
  class OnAddArtifactAlias(_BaseMutationEventInput):
177
192
  """A new alias is assigned to an artifact."""
178
193
 
179
194
  event_type: Literal[EventType.ADD_ARTIFACT_ALIAS] = EventType.ADD_ARTIFACT_ALIAS
180
- scope: InputScope
181
195
 
182
196
 
183
197
  class OnCreateArtifact(_BaseMutationEventInput):
184
198
  """A new artifact is created."""
185
199
 
186
200
  event_type: Literal[EventType.CREATE_ARTIFACT] = EventType.CREATE_ARTIFACT
187
- scope: ArtifactCollectionScope
188
201
 
202
+ scope: ArtifactCollectionScope
203
+ """The scope of the event: only artifact collections are valid scopes for this event."""
189
204
 
190
- class OnRunMetric(_BaseEventInput):
191
- """A run metric satisfies a user-defined absolute threshold."""
192
205
 
193
- event_type: Literal[EventType.RUN_METRIC] = EventType.RUN_METRIC
206
+ # ------------------------------------------------------------------------------
207
+ # Events that trigger on run conditions
208
+ class _BaseRunEventInput(_BaseEventInput):
194
209
  scope: ProjectScope
210
+ """The scope of the event: only projects are valid scopes for this event."""
211
+
212
+
213
+ class OnRunMetric(_BaseRunEventInput):
214
+ """A run metric satisfies a user-defined condition."""
215
+
216
+ event_type: Literal[EventType.RUN_METRIC_THRESHOLD, EventType.RUN_METRIC_CHANGE]
217
+
195
218
  filter: SerializedToJson[RunMetricFilter]
219
+ """Run and/or metric condition(s) that must be satisfied for this event to trigger an automation."""
220
+
221
+ @model_validator(mode="before")
222
+ @classmethod
223
+ def _infer_event_type(cls, data: Any) -> Any:
224
+ """Infer the event type at validation time from the inner filter.
225
+
226
+ This allows this class to accommodate both "threshold" and "change" metric
227
+ filter types, which are can only be determined after parsing and validating
228
+ the inner JSON data.
229
+ """
230
+ if isinstance(data, dict) and (raw_filter := data.get("filter")):
231
+ # At this point, `raw_filter` may or may not be JSON-serialized
232
+ parsed_filter = RunMetricFilter.model_validate_json(ensure_json(raw_filter))
233
+ return {**data, "event_type": parsed_filter.metric.event_type}
234
+
235
+ return data
196
236
 
197
237
 
198
238
  # for type annotations
199
- InputEvent = Union[
200
- OnLinkArtifact,
201
- OnAddArtifactAlias,
202
- OnCreateArtifact,
203
- OnRunMetric,
239
+ InputEvent = Annotated[
240
+ Union[
241
+ OnLinkArtifact,
242
+ OnAddArtifactAlias,
243
+ OnCreateArtifact,
244
+ OnRunMetric,
245
+ ],
246
+ Field(discriminator="event_type"),
204
247
  ]
205
248
  # for runtime type checks
206
- InputEventTypes: tuple[type, ...] = get_args(InputEvent)
249
+ InputEventTypes: tuple[type, ...] = get_args(InputEvent.__origin__) # type: ignore[attr-defined]
207
250
 
208
251
 
209
252
  # ----------------------------------------------------------------------------
210
253
 
211
254
 
212
255
  class RunEvent:
213
- name = FilterableField("display_name")
256
+ name = FilterableField(server_name="display_name")
214
257
  # `Run.name` is actually filtered on `Run.display_name` in the backend.
215
258
  # We can't reasonably expect users to know this a priori, so
216
259
  # automatically fix it here.
217
260
 
218
261
  @staticmethod
219
- def metric(name: str) -> MetricOperand:
262
+ def metric(name: str) -> MetricVal:
220
263
  """Define a metric filter condition."""
221
- return MetricOperand(name=name)
264
+ return MetricVal(name=name)
222
265
 
223
266
 
224
267
  class ArtifactEvent:
@@ -227,9 +270,18 @@ class ArtifactEvent:
227
270
 
228
271
  MetricThresholdFilter.model_rebuild()
229
272
  RunMetricFilter.model_rebuild()
230
- SavedEventFilter.model_rebuild()
273
+ _WrappedSavedEventFilter.model_rebuild()
231
274
 
232
275
  OnLinkArtifact.model_rebuild()
233
276
  OnAddArtifactAlias.model_rebuild()
234
277
  OnCreateArtifact.model_rebuild()
235
278
  OnRunMetric.model_rebuild()
279
+
280
+ __all__ = [
281
+ "EventType",
282
+ *(cls.__name__ for cls in InputEventTypes),
283
+ "RunEvent",
284
+ "ArtifactEvent",
285
+ "MetricThresholdFilter",
286
+ "MetricChangeFilter",
287
+ ]
@@ -1,6 +1,7 @@
1
1
  from typing import Union
2
2
 
3
3
  from pydantic import Field
4
+ from typing_extensions import Annotated
4
5
 
5
6
  from wandb._pydantic import GQLBase
6
7
  from wandb.automations._generated import (
@@ -10,17 +11,35 @@ from wandb.automations._generated import (
10
11
 
11
12
 
12
13
  class SlackIntegration(SlackIntegrationFields):
13
- pass
14
+ team_name: str
15
+ """The name of the Slack workspace (not the W&B team) that this integration is associated with."""
16
+
17
+ channel_name: str
18
+ """The name of the Slack channel that this integration will post messages to."""
14
19
 
15
20
 
16
21
  class WebhookIntegration(GenericWebhookIntegrationFields):
17
- pass
22
+ name: str
23
+ """The name of this webhook integration."""
24
+
25
+ url_endpoint: str
26
+ """The URL that this webhook will POST events to."""
18
27
 
19
28
 
20
- Integration = Union[SlackIntegration, WebhookIntegration]
29
+ Integration = Annotated[
30
+ Union[SlackIntegration, WebhookIntegration],
31
+ Field(discriminator="typename__"),
32
+ ]
21
33
 
22
34
 
23
35
  # For parsing integration instances from paginated responses
24
36
  class _IntegrationEdge(GQLBase):
25
37
  cursor: str
26
- node: Integration = Field(discriminator="typename__")
38
+ node: Integration
39
+
40
+
41
+ __all__ = [
42
+ "Integration",
43
+ "SlackIntegration",
44
+ "WebhookIntegration",
45
+ ]
@@ -14,26 +14,29 @@ from wandb.automations._generated import (
14
14
  ProjectScopeFields,
15
15
  )
16
16
 
17
- from ._generated import TriggerScopeType
18
- from ._validators import validate_scope
17
+ from ._validators import LenientStrEnum, to_scope
18
+
19
19
 
20
20
  # NOTE: Re-defined publicly with a more readable name for easier access
21
- ScopeType = TriggerScopeType
22
- """The type of scope that triggers an automation."""
21
+ class ScopeType(LenientStrEnum):
22
+ """The kind of scope that triggers an automation."""
23
+
24
+ PROJECT = "PROJECT"
25
+ ARTIFACT_COLLECTION = "ARTIFACT_COLLECTION"
23
26
 
24
27
 
25
28
  class _BaseScope(GQLBase):
26
- scope_type: ScopeType
29
+ scope_type: Annotated[ScopeType, Field(frozen=True)]
27
30
 
28
31
 
29
32
  class _ArtifactSequenceScope(_BaseScope, ArtifactSequenceScopeFields):
30
- """The ID and name of a "sequence"-type ArtifactCollection scope of an automation."""
33
+ """An automation scope defined by a specific `ArtifactSequence`."""
31
34
 
32
35
  scope_type: Literal[ScopeType.ARTIFACT_COLLECTION] = ScopeType.ARTIFACT_COLLECTION
33
36
 
34
37
 
35
38
  class _ArtifactPortfolioScope(_BaseScope, ArtifactPortfolioScopeFields):
36
- """The ID and name of a "portfolio"-type ArtifactCollection scope of an automation."""
39
+ """An automation scope defined by a specific `ArtifactPortfolio` (e.g. a registry collection)."""
37
40
 
38
41
  scope_type: Literal[ScopeType.ARTIFACT_COLLECTION] = ScopeType.ARTIFACT_COLLECTION
39
42
 
@@ -41,20 +44,19 @@ class _ArtifactPortfolioScope(_BaseScope, ArtifactPortfolioScopeFields):
41
44
  # for type annotations
42
45
  ArtifactCollectionScope = Annotated[
43
46
  Union[_ArtifactSequenceScope, _ArtifactPortfolioScope],
44
- BeforeValidator(validate_scope),
47
+ BeforeValidator(to_scope),
45
48
  Field(discriminator="typename__"),
46
49
  ]
47
- """The ID and name of the ArtifactCollection scope of an automation."""
50
+ """An automation scope defined by a specific `ArtifactCollection`."""
48
51
 
49
52
  # for runtime type checks
50
- ArtifactCollectionScopeTypes: tuple[type, ...] = (
51
- _ArtifactSequenceScope,
52
- _ArtifactPortfolioScope,
53
+ ArtifactCollectionScopeTypes: tuple[type, ...] = get_args(
54
+ ArtifactCollectionScope.__origin__ # type: ignore[attr-defined]
53
55
  )
54
56
 
55
57
 
56
58
  class ProjectScope(_BaseScope, ProjectScopeFields):
57
- """The ID and name of the Project scope of an automation."""
59
+ """An automation scope defined by a specific `Project`."""
58
60
 
59
61
  scope_type: Literal[ScopeType.PROJECT] = ScopeType.PROJECT
60
62
 
@@ -62,15 +64,15 @@ class ProjectScope(_BaseScope, ProjectScopeFields):
62
64
  # for type annotations
63
65
  AutomationScope: TypeAlias = Annotated[
64
66
  Union[_ArtifactSequenceScope, _ArtifactPortfolioScope, ProjectScope],
65
- BeforeValidator(validate_scope),
67
+ BeforeValidator(to_scope),
66
68
  Field(discriminator="typename__"),
67
69
  ]
68
70
  # for runtime type checks
69
- AutomationScopeTypes: tuple[type, ...] = get_args(AutomationScope)
71
+ AutomationScopeTypes: tuple[type, ...] = get_args(AutomationScope.__origin__) # type: ignore[attr-defined]
70
72
 
71
- # Aliases for naming clarity/consistency
72
- SavedScope: TypeAlias = AutomationScope
73
- InputScope: TypeAlias = AutomationScope
74
73
 
75
- SavedScopeTypes: tuple[type, ...] = get_args(SavedScope)
76
- InputScopeTypes: tuple[type, ...] = get_args(InputScope)
74
+ __all__ = [
75
+ "ScopeType",
76
+ "ArtifactCollectionScope",
77
+ "ProjectScope",
78
+ ]
wandb/bin/gpu_stats CHANGED
Binary file
wandb/bin/wandb-core CHANGED
Binary file
wandb/env.py CHANGED
@@ -88,6 +88,7 @@ LAUNCH_QUEUE_NAME = "WANDB_LAUNCH_QUEUE_NAME"
88
88
  LAUNCH_QUEUE_ENTITY = "WANDB_LAUNCH_QUEUE_ENTITY"
89
89
  LAUNCH_TRACE_ID = "WANDB_LAUNCH_TRACE_ID"
90
90
  _REQUIRE_LEGACY_SERVICE = "WANDB_X_REQUIRE_LEGACY_SERVICE"
91
+ ENABLE_DCGM_PROFILING = "WANDB_ENABLE_DCGM_PROFILING"
91
92
 
92
93
  # For testing, to be removed in future version
93
94
  USE_V1_ARTIFACTS = "_WANDB_USE_V1_ARTIFACTS"
@@ -177,6 +178,16 @@ def ssl_disabled() -> bool:
177
178
  return _env_as_bool(DISABLE_SSL, default="False")
178
179
 
179
180
 
181
+ def dcgm_profiling_enabled() -> bool:
182
+ """Checks whether collecting profiling metrics for Nvidia GPUs using DCGM is requested.
183
+
184
+ Note: Enabling this feature can lead to increased resource usage
185
+ compared to standard monitoring.
186
+ Requires the `nvidia-dcgm` service to be running on the machine.
187
+ """
188
+ return _env_as_bool(ENABLE_DCGM_PROFILING, default="False")
189
+
190
+
180
191
  def get_error_reporting(
181
192
  default: bool | str = True,
182
193
  env: MutableMapping | None = None,
wandb/old/settings.py CHANGED
@@ -146,6 +146,10 @@ class Settings:
146
146
  try:
147
147
  home_config_dir = os.path.join(os.path.expanduser("~"), ".config", "wandb")
148
148
 
149
+ if os.getenv(env.CONFIG_DIR):
150
+ try_create_dir(os.getenv(env.CONFIG_DIR))
151
+ return os.path.join(os.getenv(env.CONFIG_DIR), "settings")
152
+
149
153
  if not try_create_dir(home_config_dir):
150
154
  temp_config_dir = os.path.join(
151
155
  tempfile.gettempdir(), ".config", "wandb"
@@ -162,7 +166,6 @@ class Settings:
162
166
  else:
163
167
  config_dir = home_config_dir
164
168
 
165
- config_dir = os.environ.get(env.CONFIG_DIR, config_dir)
166
169
  return os.path.join(config_dir, "settings")
167
170
  except Exception:
168
171
  return None