wandb 0.19.9__py3-none-win32.whl → 0.19.11__py3-none-win32.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 +6 -3
- wandb/_pydantic/__init__.py +14 -8
- wandb/_pydantic/base.py +51 -36
- wandb/_pydantic/utils.py +73 -0
- wandb/_pydantic/v1_compat.py +79 -57
- wandb/apis/public/__init__.py +2 -2
- wandb/apis/public/api.py +684 -4
- wandb/apis/public/artifacts.py +377 -677
- wandb/apis/public/automations.py +69 -0
- wandb/apis/public/integrations.py +180 -0
- wandb/apis/public/projects.py +29 -0
- 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/apis/public/utils.py +107 -1
- wandb/automations/__init__.py +73 -0
- wandb/automations/_filters/__init__.py +40 -0
- wandb/automations/_filters/expressions.py +181 -0
- wandb/automations/_filters/operators.py +258 -0
- wandb/automations/_filters/run_metrics.py +332 -0
- wandb/automations/_generated/__init__.py +177 -0
- wandb/automations/_generated/create_automation.py +17 -0
- wandb/automations/_generated/create_generic_webhook_integration.py +43 -0
- wandb/automations/_generated/delete_automation.py +17 -0
- wandb/automations/_generated/enums.py +33 -0
- wandb/automations/_generated/fragments.py +358 -0
- wandb/automations/_generated/generic_webhook_integrations_by_entity.py +22 -0
- wandb/automations/_generated/get_automations.py +24 -0
- wandb/automations/_generated/get_automations_by_entity.py +26 -0
- wandb/automations/_generated/input_types.py +104 -0
- wandb/automations/_generated/integrations_by_entity.py +22 -0
- wandb/automations/_generated/operations.py +647 -0
- wandb/automations/_generated/slack_integrations_by_entity.py +22 -0
- wandb/automations/_generated/update_automation.py +17 -0
- wandb/automations/_utils.py +237 -0
- wandb/automations/_validators.py +165 -0
- wandb/automations/actions.py +220 -0
- wandb/automations/automations.py +87 -0
- wandb/automations/events.py +287 -0
- wandb/automations/integrations.py +45 -0
- wandb/automations/scopes.py +78 -0
- wandb/beta/workflows.py +9 -10
- wandb/bin/gpu_stats.exe +0 -0
- wandb/bin/wandb-core +0 -0
- wandb/cli/cli.py +3 -3
- wandb/env.py +11 -0
- wandb/integration/keras/keras.py +2 -1
- wandb/integration/langchain/wandb_tracer.py +2 -1
- wandb/jupyter.py +137 -118
- wandb/old/settings.py +4 -1
- wandb/old/summary.py +0 -2
- wandb/proto/v3/wandb_internal_pb2.py +297 -292
- wandb/proto/v3/wandb_settings_pb2.py +2 -2
- wandb/proto/v3/wandb_telemetry_pb2.py +10 -10
- wandb/proto/v4/wandb_internal_pb2.py +292 -292
- wandb/proto/v4/wandb_settings_pb2.py +2 -2
- wandb/proto/v4/wandb_telemetry_pb2.py +10 -10
- wandb/proto/v5/wandb_internal_pb2.py +292 -292
- wandb/proto/v5/wandb_settings_pb2.py +2 -2
- wandb/proto/v5/wandb_telemetry_pb2.py +10 -10
- wandb/proto/v6/wandb_base_pb2.py +41 -0
- wandb/proto/v6/wandb_internal_pb2.py +393 -0
- wandb/proto/v6/wandb_server_pb2.py +78 -0
- wandb/proto/v6/wandb_settings_pb2.py +58 -0
- wandb/proto/v6/wandb_telemetry_pb2.py +52 -0
- wandb/proto/wandb_base_pb2.py +2 -0
- wandb/proto/wandb_deprecated.py +8 -0
- wandb/proto/wandb_internal_pb2.py +3 -1
- wandb/proto/wandb_server_pb2.py +2 -0
- wandb/proto/wandb_settings_pb2.py +2 -0
- wandb/proto/wandb_telemetry_pb2.py +2 -0
- wandb/sdk/artifacts/_generated/__init__.py +289 -0
- wandb/sdk/artifacts/_generated/add_aliases.py +21 -0
- wandb/sdk/artifacts/_generated/artifact_collection_membership_files.py +43 -0
- wandb/sdk/artifacts/_generated/artifact_version_files.py +36 -0
- wandb/sdk/artifacts/_generated/create_artifact_collection_tag_assignments.py +36 -0
- wandb/sdk/artifacts/_generated/delete_aliases.py +21 -0
- wandb/sdk/artifacts/_generated/delete_artifact_collection_tag_assignments.py +25 -0
- wandb/sdk/artifacts/_generated/delete_artifact_portfolio.py +35 -0
- wandb/sdk/artifacts/_generated/delete_artifact_sequence.py +35 -0
- wandb/sdk/artifacts/_generated/enums.py +17 -0
- wandb/sdk/artifacts/_generated/fetch_linked_artifacts.py +67 -0
- wandb/sdk/artifacts/_generated/fragments.py +221 -0
- wandb/sdk/artifacts/_generated/input_types.py +28 -0
- wandb/sdk/artifacts/_generated/move_artifact_collection.py +35 -0
- wandb/sdk/artifacts/_generated/operations.py +611 -0
- wandb/sdk/artifacts/_generated/project_artifact_collection.py +101 -0
- wandb/sdk/artifacts/_generated/project_artifact_collections.py +33 -0
- wandb/sdk/artifacts/_generated/project_artifact_type.py +24 -0
- wandb/sdk/artifacts/_generated/project_artifact_types.py +24 -0
- wandb/sdk/artifacts/_generated/project_artifacts.py +42 -0
- wandb/sdk/artifacts/_generated/run_input_artifacts.py +51 -0
- wandb/sdk/artifacts/_generated/run_output_artifacts.py +51 -0
- wandb/sdk/artifacts/_generated/update_artifact.py +26 -0
- wandb/sdk/artifacts/_generated/update_artifact_portfolio.py +35 -0
- wandb/sdk/artifacts/_generated/update_artifact_sequence.py +35 -0
- wandb/sdk/artifacts/_graphql_fragments.py +57 -79
- wandb/sdk/artifacts/_validators.py +120 -1
- wandb/sdk/artifacts/artifact.py +419 -215
- wandb/sdk/artifacts/artifact_file_cache.py +4 -6
- wandb/sdk/artifacts/artifact_manifest_entry.py +13 -3
- wandb/sdk/artifacts/storage_handlers/azure_handler.py +1 -0
- wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +182 -1
- wandb/sdk/artifacts/storage_policy.py +3 -0
- wandb/sdk/data_types/base_types/media.py +2 -3
- wandb/sdk/data_types/base_types/wb_value.py +34 -11
- wandb/sdk/data_types/html.py +36 -9
- wandb/sdk/data_types/image.py +12 -12
- wandb/sdk/data_types/table.py +5 -0
- wandb/sdk/data_types/trace_tree.py +2 -0
- wandb/sdk/data_types/utils.py +1 -1
- wandb/sdk/data_types/video.py +59 -57
- wandb/sdk/interface/interface.py +4 -3
- wandb/sdk/internal/internal_api.py +21 -31
- wandb/sdk/internal/profiler.py +6 -5
- wandb/sdk/internal/run.py +13 -6
- wandb/sdk/internal/sender.py +5 -2
- wandb/sdk/launch/sweeps/utils.py +8 -0
- wandb/sdk/lib/apikey.py +25 -4
- wandb/sdk/lib/asyncio_compat.py +1 -1
- wandb/sdk/lib/deprecate.py +13 -22
- wandb/sdk/lib/disabled.py +2 -1
- wandb/sdk/lib/printer.py +37 -8
- wandb/sdk/lib/printer_asyncio.py +46 -0
- wandb/sdk/lib/redirect.py +10 -5
- 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/server_sock.py +19 -14
- wandb/sdk/service/service.py +18 -8
- wandb/sdk/service/streams.py +5 -0
- wandb/sdk/verify/verify.py +6 -3
- wandb/sdk/wandb_init.py +217 -70
- wandb/sdk/wandb_login.py +13 -4
- wandb/sdk/wandb_run.py +419 -295
- wandb/sdk/wandb_settings.py +27 -10
- wandb/sdk/wandb_setup.py +61 -0
- wandb/util.py +33 -29
- {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/METADATA +5 -5
- {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/RECORD +153 -83
- wandb/_globals.py +0 -19
- wandb/sdk/internal/_generated/base.py +0 -226
- wandb/sdk/internal/_generated/typing_compat.py +0 -14
- {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/WHEEL +0 -0
- {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/entry_points.txt +0 -0
- {wandb-0.19.9.dist-info → wandb-0.19.11.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,87 @@
|
|
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 Optional
|
7
|
+
|
8
|
+
from pydantic import Field
|
9
|
+
from typing_extensions import Annotated
|
10
|
+
|
11
|
+
from wandb._pydantic import GQLBase, GQLId
|
12
|
+
|
13
|
+
from ._generated import TriggerFields
|
14
|
+
from .actions import InputAction, SavedAction
|
15
|
+
from .events import InputEvent, SavedEvent
|
16
|
+
from .scopes import AutomationScope
|
17
|
+
|
18
|
+
|
19
|
+
# ------------------------------------------------------------------------------
|
20
|
+
# Saved types: for parsing response data from saved automations
|
21
|
+
class Automation(TriggerFields):
|
22
|
+
"""A local instance of a saved W&B automation."""
|
23
|
+
|
24
|
+
id: GQLId
|
25
|
+
|
26
|
+
created_at: Annotated[datetime, Field(repr=False, frozen=True, alias="createdAt")]
|
27
|
+
"""The date and time when this automation was created."""
|
28
|
+
|
29
|
+
updated_at: Annotated[
|
30
|
+
Optional[datetime], Field(repr=False, frozen=True, alias="updatedAt")
|
31
|
+
] = None
|
32
|
+
"""The date and time when this automation was last updated, if applicable."""
|
33
|
+
|
34
|
+
name: str
|
35
|
+
"""The name of this automation."""
|
36
|
+
|
37
|
+
description: Optional[str]
|
38
|
+
"""An optional description of this automation."""
|
39
|
+
|
40
|
+
enabled: bool
|
41
|
+
"""Whether this automation is enabled. Only enabled automations will trigger."""
|
42
|
+
|
43
|
+
event: SavedEvent
|
44
|
+
"""The event that will trigger this automation."""
|
45
|
+
|
46
|
+
scope: AutomationScope
|
47
|
+
"""The scope in which the triggering event must occur."""
|
48
|
+
|
49
|
+
action: SavedAction
|
50
|
+
"""The action that will execute when this automation is triggered."""
|
51
|
+
|
52
|
+
|
53
|
+
class NewAutomation(GQLBase, extra="forbid", validate_default=False):
|
54
|
+
"""A new automation to be created."""
|
55
|
+
|
56
|
+
name: Optional[str] = None
|
57
|
+
"""The name of this automation."""
|
58
|
+
|
59
|
+
description: Optional[str] = None
|
60
|
+
"""An optional description of this automation."""
|
61
|
+
|
62
|
+
enabled: Optional[bool] = None
|
63
|
+
"""Whether this automation is enabled. Only enabled automations will trigger."""
|
64
|
+
|
65
|
+
event: Optional[InputEvent] = None
|
66
|
+
"""The event that will trigger this automation."""
|
67
|
+
|
68
|
+
# Ensure that the event and its scope are always consistent, if the event is set.
|
69
|
+
@property
|
70
|
+
def scope(self) -> Optional[AutomationScope]:
|
71
|
+
"""The scope in which the triggering event must occur."""
|
72
|
+
return self.event.scope if self.event else None
|
73
|
+
|
74
|
+
@scope.setter
|
75
|
+
def scope(self, value: AutomationScope) -> None:
|
76
|
+
if self.event is None:
|
77
|
+
raise ValueError("Cannot set `scope` for an automation with no `event`")
|
78
|
+
self.event.scope = value
|
79
|
+
|
80
|
+
action: Optional[InputAction] = None
|
81
|
+
"""The action that will execute when this automation is triggered."""
|
82
|
+
|
83
|
+
|
84
|
+
__all__ = [
|
85
|
+
"Automation",
|
86
|
+
"NewAutomation",
|
87
|
+
]
|
@@ -0,0 +1,287 @@
|
|
1
|
+
"""Events that trigger 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 TYPE_CHECKING, Any, Literal, Optional, Union
|
8
|
+
|
9
|
+
from pydantic import Field
|
10
|
+
from typing_extensions import Annotated, Self, get_args
|
11
|
+
|
12
|
+
from wandb._pydantic import (
|
13
|
+
GQLBase,
|
14
|
+
SerializedToJson,
|
15
|
+
ensure_json,
|
16
|
+
field_validator,
|
17
|
+
model_validator,
|
18
|
+
pydantic_isinstance,
|
19
|
+
)
|
20
|
+
|
21
|
+
from ._filters import And, MongoLikeFilter, Or
|
22
|
+
from ._filters.expressions import FilterableField
|
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
|
28
|
+
|
29
|
+
if TYPE_CHECKING:
|
30
|
+
from .automations import NewAutomation
|
31
|
+
|
32
|
+
|
33
|
+
# NOTE: Re-defined publicly with a more readable name for easier access
|
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
|
+
|
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"
|
51
|
+
|
52
|
+
|
53
|
+
# ------------------------------------------------------------------------------
|
54
|
+
# Saved types: for parsing response data from saved automations
|
55
|
+
|
56
|
+
|
57
|
+
# Note: In GQL responses containing saved automation data, the filter is wrapped in an extra `filter` key.
|
58
|
+
class _WrappedSavedEventFilter(GQLBase): # from: TriggeringFilterEvent
|
59
|
+
filter: SerializedToJson[MongoLikeFilter] = And()
|
60
|
+
|
61
|
+
|
62
|
+
class _WrappedMetricFilter(GQLBase): # from: RunMetricFilter
|
63
|
+
threshold_filter: Optional[MetricThresholdFilter] = None
|
64
|
+
change_filter: Optional[MetricChangeFilter] = None
|
65
|
+
|
66
|
+
@model_validator(mode="before")
|
67
|
+
@classmethod
|
68
|
+
def _wrap_metric_filter(cls, v: Any) -> Any:
|
69
|
+
if pydantic_isinstance(v, MetricThresholdFilter):
|
70
|
+
return cls(threshold_filter=v)
|
71
|
+
if pydantic_isinstance(v, MetricChangeFilter):
|
72
|
+
return cls(change_filter=v)
|
73
|
+
return v
|
74
|
+
|
75
|
+
@model_validator(mode="after")
|
76
|
+
def _ensure_exactly_one_set(self) -> Self:
|
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
|
+
|
87
|
+
return self
|
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")]
|
101
|
+
|
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.
|
108
|
+
|
109
|
+
For new automations, use the `metric` field (`run_metric_filter` JSON alias) instead.
|
110
|
+
"""
|
111
|
+
|
112
|
+
@model_validator(mode="before")
|
113
|
+
@classmethod
|
114
|
+
def _wrap_metric_filter(cls, v: Any) -> Any:
|
115
|
+
if pydantic_isinstance(v, (MetricThresholdFilter, MetricChangeFilter)):
|
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)
|
121
|
+
return v
|
122
|
+
|
123
|
+
@field_validator("run", mode="after")
|
124
|
+
def _wrap_run_filter(cls, v: MongoLikeFilter) -> Any:
|
125
|
+
v_new = simplify_op(v)
|
126
|
+
return v_new if pydantic_isinstance(v_new, And) else And(and_=[v_new])
|
127
|
+
|
128
|
+
|
129
|
+
class SavedEvent(FilterEventFields): # from: FilterEventTriggeringCondition
|
130
|
+
"""A triggering event from a saved automation."""
|
131
|
+
|
132
|
+
event_type: Annotated[EventType, Field(frozen=True)] # type: ignore[assignment]
|
133
|
+
|
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."""
|
138
|
+
|
139
|
+
|
140
|
+
# ------------------------------------------------------------------------------
|
141
|
+
# Input types: for creating or updating automations
|
142
|
+
|
143
|
+
|
144
|
+
# Note: The GQL input for "eventFilter" does NOT wrap the filter in an extra `filter` key, unlike the
|
145
|
+
# eventFilter returned in responses for saved automations.
|
146
|
+
class _BaseEventInput(GQLBase):
|
147
|
+
event_type: EventType
|
148
|
+
|
149
|
+
scope: AutomationScope
|
150
|
+
"""The scope of the event."""
|
151
|
+
|
152
|
+
filter: SerializedToJson[Any]
|
153
|
+
|
154
|
+
def then(self, action: InputAction) -> NewAutomation:
|
155
|
+
"""Define a new Automation in which this event triggers the given action."""
|
156
|
+
from .automations import NewAutomation
|
157
|
+
|
158
|
+
if isinstance(action, (InputActionTypes, SavedActionTypes)):
|
159
|
+
return NewAutomation(event=self, action=action)
|
160
|
+
|
161
|
+
raise TypeError(f"Expected a valid action, got: {type(action).__qualname__!r}")
|
162
|
+
|
163
|
+
def __rshift__(self, other: InputAction) -> NewAutomation:
|
164
|
+
"""Implements `event >> action` to define an Automation with this event and action."""
|
165
|
+
return self.then(other)
|
166
|
+
|
167
|
+
|
168
|
+
# ------------------------------------------------------------------------------
|
169
|
+
# Events that trigger on specific mutations in the backend
|
170
|
+
class _BaseMutationEventInput(_BaseEventInput):
|
171
|
+
filter: SerializedToJson[MongoLikeFilter] = And()
|
172
|
+
"""Additional condition(s), if any, that must be met for this event to trigger an automation."""
|
173
|
+
|
174
|
+
@field_validator("filter", mode="after")
|
175
|
+
def _wrap_filter(cls, v: Any) -> Any:
|
176
|
+
"""Ensure the given filter is wrapped like: `{"$or": [{"$and": [<original_filter>]}]}`.
|
177
|
+
|
178
|
+
This is awkward but necessary, because the frontend expects this format.
|
179
|
+
"""
|
180
|
+
v_new = simplify_op(v)
|
181
|
+
v_new = v_new if pydantic_isinstance(v_new, And) else And(and_=[v_new])
|
182
|
+
return Or(or_=[v_new])
|
183
|
+
|
184
|
+
|
185
|
+
class OnLinkArtifact(_BaseMutationEventInput):
|
186
|
+
"""A new artifact is linked to a collection."""
|
187
|
+
|
188
|
+
event_type: Literal[EventType.LINK_ARTIFACT] = EventType.LINK_ARTIFACT
|
189
|
+
|
190
|
+
|
191
|
+
class OnAddArtifactAlias(_BaseMutationEventInput):
|
192
|
+
"""A new alias is assigned to an artifact."""
|
193
|
+
|
194
|
+
event_type: Literal[EventType.ADD_ARTIFACT_ALIAS] = EventType.ADD_ARTIFACT_ALIAS
|
195
|
+
|
196
|
+
|
197
|
+
class OnCreateArtifact(_BaseMutationEventInput):
|
198
|
+
"""A new artifact is created."""
|
199
|
+
|
200
|
+
event_type: Literal[EventType.CREATE_ARTIFACT] = EventType.CREATE_ARTIFACT
|
201
|
+
|
202
|
+
scope: ArtifactCollectionScope
|
203
|
+
"""The scope of the event: only artifact collections are valid scopes for this event."""
|
204
|
+
|
205
|
+
|
206
|
+
# ------------------------------------------------------------------------------
|
207
|
+
# Events that trigger on run conditions
|
208
|
+
class _BaseRunEventInput(_BaseEventInput):
|
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
|
+
|
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
|
236
|
+
|
237
|
+
|
238
|
+
# for type annotations
|
239
|
+
InputEvent = Annotated[
|
240
|
+
Union[
|
241
|
+
OnLinkArtifact,
|
242
|
+
OnAddArtifactAlias,
|
243
|
+
OnCreateArtifact,
|
244
|
+
OnRunMetric,
|
245
|
+
],
|
246
|
+
Field(discriminator="event_type"),
|
247
|
+
]
|
248
|
+
# for runtime type checks
|
249
|
+
InputEventTypes: tuple[type, ...] = get_args(InputEvent.__origin__) # type: ignore[attr-defined]
|
250
|
+
|
251
|
+
|
252
|
+
# ----------------------------------------------------------------------------
|
253
|
+
|
254
|
+
|
255
|
+
class RunEvent:
|
256
|
+
name = FilterableField(server_name="display_name")
|
257
|
+
# `Run.name` is actually filtered on `Run.display_name` in the backend.
|
258
|
+
# We can't reasonably expect users to know this a priori, so
|
259
|
+
# automatically fix it here.
|
260
|
+
|
261
|
+
@staticmethod
|
262
|
+
def metric(name: str) -> MetricVal:
|
263
|
+
"""Define a metric filter condition."""
|
264
|
+
return MetricVal(name=name)
|
265
|
+
|
266
|
+
|
267
|
+
class ArtifactEvent:
|
268
|
+
alias = FilterableField()
|
269
|
+
|
270
|
+
|
271
|
+
MetricThresholdFilter.model_rebuild()
|
272
|
+
RunMetricFilter.model_rebuild()
|
273
|
+
_WrappedSavedEventFilter.model_rebuild()
|
274
|
+
|
275
|
+
OnLinkArtifact.model_rebuild()
|
276
|
+
OnAddArtifactAlias.model_rebuild()
|
277
|
+
OnCreateArtifact.model_rebuild()
|
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
|
+
]
|
@@ -0,0 +1,45 @@
|
|
1
|
+
from typing import Union
|
2
|
+
|
3
|
+
from pydantic import Field
|
4
|
+
from typing_extensions import Annotated
|
5
|
+
|
6
|
+
from wandb._pydantic import GQLBase
|
7
|
+
from wandb.automations._generated import (
|
8
|
+
GenericWebhookIntegrationFields,
|
9
|
+
SlackIntegrationFields,
|
10
|
+
)
|
11
|
+
|
12
|
+
|
13
|
+
class SlackIntegration(SlackIntegrationFields):
|
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."""
|
19
|
+
|
20
|
+
|
21
|
+
class WebhookIntegration(GenericWebhookIntegrationFields):
|
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."""
|
27
|
+
|
28
|
+
|
29
|
+
Integration = Annotated[
|
30
|
+
Union[SlackIntegration, WebhookIntegration],
|
31
|
+
Field(discriminator="typename__"),
|
32
|
+
]
|
33
|
+
|
34
|
+
|
35
|
+
# For parsing integration instances from paginated responses
|
36
|
+
class _IntegrationEdge(GQLBase):
|
37
|
+
cursor: str
|
38
|
+
node: Integration
|
39
|
+
|
40
|
+
|
41
|
+
__all__ = [
|
42
|
+
"Integration",
|
43
|
+
"SlackIntegration",
|
44
|
+
"WebhookIntegration",
|
45
|
+
]
|
@@ -0,0 +1,78 @@
|
|
1
|
+
"""Scopes in which a W&B Automation can be triggered."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from typing import Literal, Union
|
6
|
+
|
7
|
+
from pydantic import BeforeValidator, Field
|
8
|
+
from typing_extensions import Annotated, TypeAlias, get_args
|
9
|
+
|
10
|
+
from wandb._pydantic import GQLBase
|
11
|
+
from wandb.automations._generated import (
|
12
|
+
ArtifactPortfolioScopeFields,
|
13
|
+
ArtifactSequenceScopeFields,
|
14
|
+
ProjectScopeFields,
|
15
|
+
)
|
16
|
+
|
17
|
+
from ._validators import LenientStrEnum, to_scope
|
18
|
+
|
19
|
+
|
20
|
+
# NOTE: Re-defined publicly with a more readable name for easier access
|
21
|
+
class ScopeType(LenientStrEnum):
|
22
|
+
"""The kind of scope that triggers an automation."""
|
23
|
+
|
24
|
+
PROJECT = "PROJECT"
|
25
|
+
ARTIFACT_COLLECTION = "ARTIFACT_COLLECTION"
|
26
|
+
|
27
|
+
|
28
|
+
class _BaseScope(GQLBase):
|
29
|
+
scope_type: Annotated[ScopeType, Field(frozen=True)]
|
30
|
+
|
31
|
+
|
32
|
+
class _ArtifactSequenceScope(_BaseScope, ArtifactSequenceScopeFields):
|
33
|
+
"""An automation scope defined by a specific `ArtifactSequence`."""
|
34
|
+
|
35
|
+
scope_type: Literal[ScopeType.ARTIFACT_COLLECTION] = ScopeType.ARTIFACT_COLLECTION
|
36
|
+
|
37
|
+
|
38
|
+
class _ArtifactPortfolioScope(_BaseScope, ArtifactPortfolioScopeFields):
|
39
|
+
"""An automation scope defined by a specific `ArtifactPortfolio` (e.g. a registry collection)."""
|
40
|
+
|
41
|
+
scope_type: Literal[ScopeType.ARTIFACT_COLLECTION] = ScopeType.ARTIFACT_COLLECTION
|
42
|
+
|
43
|
+
|
44
|
+
# for type annotations
|
45
|
+
ArtifactCollectionScope = Annotated[
|
46
|
+
Union[_ArtifactSequenceScope, _ArtifactPortfolioScope],
|
47
|
+
BeforeValidator(to_scope),
|
48
|
+
Field(discriminator="typename__"),
|
49
|
+
]
|
50
|
+
"""An automation scope defined by a specific `ArtifactCollection`."""
|
51
|
+
|
52
|
+
# for runtime type checks
|
53
|
+
ArtifactCollectionScopeTypes: tuple[type, ...] = get_args(
|
54
|
+
ArtifactCollectionScope.__origin__ # type: ignore[attr-defined]
|
55
|
+
)
|
56
|
+
|
57
|
+
|
58
|
+
class ProjectScope(_BaseScope, ProjectScopeFields):
|
59
|
+
"""An automation scope defined by a specific `Project`."""
|
60
|
+
|
61
|
+
scope_type: Literal[ScopeType.PROJECT] = ScopeType.PROJECT
|
62
|
+
|
63
|
+
|
64
|
+
# for type annotations
|
65
|
+
AutomationScope: TypeAlias = Annotated[
|
66
|
+
Union[_ArtifactSequenceScope, _ArtifactPortfolioScope, ProjectScope],
|
67
|
+
BeforeValidator(to_scope),
|
68
|
+
Field(discriminator="typename__"),
|
69
|
+
]
|
70
|
+
# for runtime type checks
|
71
|
+
AutomationScopeTypes: tuple[type, ...] = get_args(AutomationScope.__origin__) # type: ignore[attr-defined]
|
72
|
+
|
73
|
+
|
74
|
+
__all__ = [
|
75
|
+
"ScopeType",
|
76
|
+
"ArtifactCollectionScope",
|
77
|
+
"ProjectScope",
|
78
|
+
]
|
wandb/beta/workflows.py
CHANGED
@@ -5,6 +5,7 @@ from typing import Any, Dict, List, Optional, Union
|
|
5
5
|
import wandb
|
6
6
|
import wandb.data_types as data_types
|
7
7
|
from wandb.data_types import _SavedModel
|
8
|
+
from wandb.sdk import wandb_setup
|
8
9
|
from wandb.sdk.artifacts.artifact import Artifact
|
9
10
|
from wandb.sdk.artifacts.artifact_manifest_entry import ArtifactManifestEntry
|
10
11
|
|
@@ -85,12 +86,13 @@ def _log_artifact_version(
|
|
85
86
|
Artifact
|
86
87
|
|
87
88
|
"""
|
88
|
-
|
89
|
+
run = wandb_setup._setup(start_service=False).most_recent_active_run
|
90
|
+
if not run:
|
89
91
|
run = wandb.init(
|
90
|
-
project=project,
|
92
|
+
project=project,
|
93
|
+
job_type=job_type,
|
94
|
+
settings=wandb.Settings(silent=True),
|
91
95
|
)
|
92
|
-
else:
|
93
|
-
run = wandb.run
|
94
96
|
|
95
97
|
if not scope_project:
|
96
98
|
name = f"{name}-{run.id}"
|
@@ -124,7 +126,7 @@ def log_model(
|
|
124
126
|
|
125
127
|
Supported frameworks include PyTorch, Keras, Tensorflow, Scikit-learn, etc. Under
|
126
128
|
the hood, we create a model artifact, bind it to the run that produced this model,
|
127
|
-
associate it with the latest metrics logged with `
|
129
|
+
associate it with the latest metrics logged with `run.log(...)` and more.
|
128
130
|
|
129
131
|
Args:
|
130
132
|
model_obj: any model object created with the following ML frameworks: PyTorch,
|
@@ -215,8 +217,7 @@ def use_model(aliased_path: str, unsafe: bool = False) -> "_SavedModel":
|
|
215
217
|
)
|
216
218
|
|
217
219
|
# Returns a _SavedModel instance
|
218
|
-
if
|
219
|
-
run = wandb.run
|
220
|
+
if run := wandb_setup._setup(start_service=False).most_recent_active_run:
|
220
221
|
artifact = run.use_artifact(aliased_path)
|
221
222
|
sm = artifact.get("index")
|
222
223
|
|
@@ -261,9 +262,7 @@ def link_model(
|
|
261
262
|
"""
|
262
263
|
aliases = wandb.util._resolve_aliases(aliases)
|
263
264
|
|
264
|
-
if
|
265
|
-
run = wandb.run
|
266
|
-
|
265
|
+
if run := wandb_setup._setup(start_service=False).most_recent_active_run:
|
267
266
|
# _artifact_source, if it exists, points to a Public Artifact.
|
268
267
|
# Its existence means that _SavedModel was deserialized from a logged artifact, most likely from `use_model`.
|
269
268
|
if model._artifact_source:
|
wandb/bin/gpu_stats.exe
CHANGED
Binary file
|
wandb/bin/wandb-core
CHANGED
Binary file
|
wandb/cli/cli.py
CHANGED
@@ -83,14 +83,13 @@ def cli_unsupported(argument):
|
|
83
83
|
|
84
84
|
class ClickWandbException(ClickException):
|
85
85
|
def format_message(self):
|
86
|
-
# log_file = util.get_log_file_path()
|
87
|
-
log_file = ""
|
88
86
|
orig_type = f"{self.orig_type.__module__}.{self.orig_type.__name__}"
|
89
87
|
if issubclass(self.orig_type, Error):
|
90
88
|
return click.style(str(self.message), fg="red")
|
91
89
|
else:
|
92
90
|
return (
|
93
|
-
f"An Exception was raised, see {
|
91
|
+
f"An Exception was raised, see {_wandb_log_path} for full"
|
92
|
+
" traceback.\n"
|
94
93
|
f"{orig_type}: {self.message}"
|
95
94
|
)
|
96
95
|
|
@@ -257,6 +256,7 @@ def login(key, host, cloud, relogin, anonymously, verify, no_offline=False):
|
|
257
256
|
key=key,
|
258
257
|
relogin=relogin,
|
259
258
|
verify=verify,
|
259
|
+
referrer="models",
|
260
260
|
)
|
261
261
|
|
262
262
|
|
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/integration/keras/keras.py
CHANGED
@@ -12,8 +12,9 @@ import tensorflow as tf
|
|
12
12
|
import tensorflow.keras.backend as K # noqa: N812
|
13
13
|
|
14
14
|
import wandb
|
15
|
+
from wandb.proto.wandb_deprecated import Deprecated
|
15
16
|
from wandb.sdk.integration_utils.data_logging import ValidationDataLogger
|
16
|
-
from wandb.sdk.lib.deprecate import
|
17
|
+
from wandb.sdk.lib.deprecate import deprecate
|
17
18
|
from wandb.util import add_import_hook
|
18
19
|
|
19
20
|
|
@@ -18,6 +18,7 @@ will be raised when importing this module.
|
|
18
18
|
from packaging import version
|
19
19
|
|
20
20
|
import wandb.util
|
21
|
+
from wandb.proto.wandb_deprecated import Deprecated
|
21
22
|
from wandb.sdk.lib import deprecate
|
22
23
|
|
23
24
|
langchain = wandb.util.get_module(
|
@@ -40,7 +41,7 @@ class WandbTracer(WandbTracer):
|
|
40
41
|
def __init__(self, *args, **kwargs):
|
41
42
|
super().__init__(*args, **kwargs)
|
42
43
|
deprecate.deprecate(
|
43
|
-
field_name=
|
44
|
+
field_name=Deprecated.langchain_tracer,
|
44
45
|
warning_message="This feature is deprecated and has been moved to `langchain`. Enable tracing by setting "
|
45
46
|
"LANGCHAIN_WANDB_TRACING=true in your environment. See the documentation at "
|
46
47
|
"https://python.langchain.com/docs/ecosystem/integrations/agent_with_wandb_tracing for guidance. "
|