wandb 0.19.9__py3-none-win_amd64.whl → 0.19.10__py3-none-win_amd64.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.exe +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
wandb/__init__.py CHANGED
@@ -10,7 +10,7 @@ For reference documentation, see https://docs.wandb.com/ref/python.
10
10
  """
11
11
  from __future__ import annotations
12
12
 
13
- __version__ = "0.19.9"
13
+ __version__ = "0.19.10"
14
14
 
15
15
 
16
16
  from wandb.errors import Error
wandb/__init__.pyi CHANGED
@@ -106,7 +106,7 @@ if TYPE_CHECKING:
106
106
  import wandb
107
107
  from wandb.plot import CustomChart
108
108
 
109
- __version__: str = "0.19.9"
109
+ __version__: str = "0.19.10"
110
110
 
111
111
  run: Run | None
112
112
  config: wandb_config.Config
@@ -229,6 +229,7 @@ def init(
229
229
  "default",
230
230
  "return_previous",
231
231
  "finish_previous",
232
+ "create_new",
232
233
  ]
233
234
  ) = None,
234
235
  resume: bool | Literal["allow", "never", "must", "auto"] | None = None,
@@ -476,6 +477,7 @@ def login(
476
477
  force: Optional[bool] = None,
477
478
  timeout: Optional[int] = None,
478
479
  verify: bool = False,
480
+ referrer: Optional[str] = None,
479
481
  ) -> bool:
480
482
  """Set up W&B login credentials.
481
483
 
@@ -495,6 +497,7 @@ def login(
495
497
  force: (bool, optional) If true, will force a relogin.
496
498
  timeout: (int, optional) Number of seconds to wait for user input.
497
499
  verify: (bool) Verify the credentials with the W&B server.
500
+ referrer: (string, optional) The referrer to use in the URL login request.
498
501
 
499
502
  Returns:
500
503
  bool: if key is configured
@@ -1,16 +1,20 @@
1
1
  """Internal utilities for working with pydantic."""
2
2
 
3
- from .base import Base, GQLBase, GQLId, SerializedToJson, Typename
4
- from .v1_compat import (
5
- IS_PYDANTIC_V2,
6
- AliasChoices,
7
- computed_field,
8
- field_validator,
9
- model_validator,
3
+ from .base import (
4
+ Base,
5
+ CompatBaseModel,
6
+ GQLBase,
7
+ GQLId,
8
+ SerializedToJson,
9
+ Typename,
10
+ ensure_json,
10
11
  )
12
+ from .utils import IS_PYDANTIC_V2, pydantic_isinstance, to_json
13
+ from .v1_compat import AliasChoices, computed_field, field_validator, model_validator
11
14
 
12
15
  __all__ = [
13
16
  "IS_PYDANTIC_V2",
17
+ "CompatBaseModel",
14
18
  "Base",
15
19
  "GQLBase",
16
20
  "Typename",
@@ -20,4 +24,7 @@ __all__ = [
20
24
  "computed_field",
21
25
  "field_validator",
22
26
  "model_validator",
27
+ "pydantic_isinstance",
28
+ "to_json",
29
+ "ensure_json",
23
30
  ]
wandb/_pydantic/base.py CHANGED
@@ -4,9 +4,10 @@ from __future__ import annotations
4
4
 
5
5
  from typing import TYPE_CHECKING, Any, Callable, Literal, TypeVar
6
6
 
7
- from pydantic import BaseModel, ConfigDict, Field, Json
7
+ from pydantic import BaseModel, ConfigDict, Field, Json, StrictStr
8
8
  from typing_extensions import Annotated, TypedDict, Unpack, override
9
9
 
10
+ from .utils import IS_PYDANTIC_V2, to_json
10
11
  from .v1_compat import PydanticCompatMixin
11
12
 
12
13
  if TYPE_CHECKING:
@@ -36,9 +37,14 @@ MODEL_DUMP_DEFAULTS = ModelDumpKwargs(
36
37
  )
37
38
 
38
39
 
40
+ # v1-compatible base class for pydantic types.
41
+ class CompatBaseModel(PydanticCompatMixin, BaseModel):
42
+ __doc__ = None # Prevent subclasses from inheriting the BaseModel docstring
43
+
44
+
39
45
  # Base class for all generated classes/types.
40
46
  # Omitted from docstring to avoid inclusion in generated docs.
41
- class Base(PydanticCompatMixin, BaseModel):
47
+ class Base(CompatBaseModel):
42
48
  model_config = ConfigDict(
43
49
  populate_by_name=True,
44
50
  validate_assignment=True,
@@ -83,17 +89,48 @@ class GQLBase(Base):
83
89
  # Reusable annotations for field types
84
90
  T = TypeVar("T")
85
91
 
86
- GQLId = Annotated[
87
- str,
88
- Field(repr=False, strict=True, frozen=True),
89
- ]
92
+ if IS_PYDANTIC_V2:
93
+ GQLId = Annotated[
94
+ StrictStr,
95
+ Field(repr=False, frozen=True),
96
+ ]
97
+ else:
98
+ # FIXME: Find a way to fix this for pydantic v1, which doesn't like when
99
+ # `Field(...)` used in the field assignment AND `Annotated[...]`.
100
+ # This is a problem for codegen, which can currently outputs e.g.
101
+ #
102
+ # class MyModel(GQLBase):
103
+ # my_id: GQLId = Field(alias="myID")
104
+ #
105
+ GQLId = StrictStr # type: ignore[misc]
90
106
 
91
107
  Typename = Annotated[
92
108
  T,
93
- Field(repr=False, alias="__typename", frozen=True),
109
+ Field(repr=False, frozen=True, alias="__typename"),
94
110
  ]
95
111
 
96
112
 
113
+ def ensure_json(v: Any) -> Any:
114
+ """In case the incoming value isn't serialized JSON, reserialize it.
115
+
116
+ This lets us use `Json[...]` fields with values that are already deserialized.
117
+ """
118
+ # NOTE: Assumes that the deserialized type is not itself a string.
119
+ # Revisit this if we need to support deserialized types that are str/bytes.
120
+ return v if isinstance(v, (str, bytes)) else to_json(v)
121
+
122
+
123
+ if IS_PYDANTIC_V2 or TYPE_CHECKING:
124
+ from pydantic import BeforeValidator
125
+
126
+ SerializedToJson = Annotated[
127
+ Json[T],
128
+ # Allow lenient instantiation/validation: incoming data may already be deserialized.
129
+ BeforeValidator(ensure_json),
130
+ ]
131
+ else:
132
+ SerializedToJson = Json[T] # type: ignore[misc]
133
+
97
134
  # FIXME: Restore, modify, or replace this later after ensuring pydantic v1 compatibility.
98
135
  # def validate_maybe_json(v: Any, handler: ValidatorFunctionWrapHandler) -> Any:
99
136
  # """Wraps default Json[...] field validator to allow instantiation with an already-decoded value."""
@@ -109,5 +146,3 @@ Typename = Annotated[
109
146
  # # Allow lenient instantiation/validation: incoming data may already be deserialized.
110
147
  # WrapValidator(validate_maybe_json),
111
148
  # ]
112
-
113
- SerializedToJson = Json[T]
@@ -0,0 +1,66 @@
1
+ """Internal utilities for working with Pydantic types and data."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import sys
7
+ from contextlib import suppress
8
+ from typing import Any, Type
9
+
10
+ import pydantic
11
+ from pydantic import BaseModel, ValidationError
12
+ from typing_extensions import TypeAlias
13
+
14
+ PYTHON_VERSION = sys.version_info
15
+
16
+ pydantic_major, *_ = pydantic.VERSION.split(".")
17
+ IS_PYDANTIC_V2: bool = int(pydantic_major) >= 2
18
+
19
+
20
+ BaseModelType: TypeAlias = Type[BaseModel]
21
+
22
+
23
+ if IS_PYDANTIC_V2:
24
+ import pydantic_core # pydantic_core is only installed by pydantic v2
25
+
26
+ def to_json(v: Any) -> str:
27
+ """Serialize a Python object to a JSON string."""
28
+ return pydantic_core.to_json(v, by_alias=True, round_trip=True).decode("utf-8")
29
+
30
+ def pydantic_isinstance(
31
+ v: Any, classinfo: BaseModelType | tuple[BaseModelType, ...]
32
+ ) -> bool:
33
+ """Return True if the object could be parsed into the given Pydantic type.
34
+
35
+ This is like a more lenient version of `isinstance()` for use with Pydantic.
36
+ In Pydantic v2, should be fast since the underlying implementation is in Rust,
37
+ and it may be preferable over `try:...except ValidationError:...`.
38
+
39
+ See: https://docs.pydantic.dev/latest/api/pydantic_core/#pydantic_core.SchemaValidator.isinstance_python
40
+ """
41
+ if isinstance(classinfo, tuple):
42
+ return any(
43
+ cls.__pydantic_validator__.isinstance_python(v) for cls in classinfo
44
+ )
45
+ cls = classinfo
46
+ return cls.__pydantic_validator__.isinstance_python(v)
47
+
48
+ else:
49
+ # Pydantic v1 fallback implementations.
50
+ # These may be noticeably slower, but their primary goal is to ensure
51
+ # compatibility with Pydantic v1 so long as we need to support it.
52
+
53
+ from pydantic.json import pydantic_encoder # Only valid in pydantic v1
54
+
55
+ def to_json(v: Any) -> str:
56
+ return json.dumps(v, default=pydantic_encoder)
57
+
58
+ def pydantic_isinstance(
59
+ v: Any, classinfo: BaseModelType | tuple[BaseModelType, ...]
60
+ ) -> bool:
61
+ classes = classinfo if isinstance(classinfo, tuple) else (classinfo,)
62
+ for cls in classes:
63
+ with suppress(ValidationError):
64
+ cls.model_validate(v)
65
+ return True
66
+ return False
@@ -2,26 +2,29 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- import sys
6
- from importlib.metadata import version
7
- from typing import (
8
- TYPE_CHECKING,
9
- Any,
10
- Callable,
11
- ClassVar,
12
- Literal,
13
- Mapping,
14
- TypeVar,
15
- overload,
16
- )
5
+ import json
6
+ from functools import lru_cache
7
+ from inspect import signature
8
+ from operator import attrgetter
9
+ from typing import TYPE_CHECKING, Any, Callable, ClassVar, Literal, overload
17
10
 
18
11
  import pydantic
19
- from typing_extensions import ParamSpec
12
+
13
+ from .utils import IS_PYDANTIC_V2, to_json
20
14
 
21
15
  if TYPE_CHECKING:
22
16
  from typing import Protocol
23
17
 
24
18
  class V1Model(Protocol):
19
+ # ------------------------------------------------------------------------------
20
+ # NOTE: These aren't part of the original v1 BaseModel spec, but were added as
21
+ # internal helpers and are (re-)declared here to satisfy mypy checks.
22
+ @classmethod
23
+ def _dump_json_vals(cls, values: dict, by_alias: bool) -> dict: ...
24
+
25
+ # ------------------------------------------------------------------------------
26
+ # These methods are part of the original v1 BaseModel spec.
27
+
25
28
  __config__: ClassVar[type]
26
29
  __fields__: ClassVar[dict[str, Any]]
27
30
  __fields_set__: set[str]
@@ -34,20 +37,10 @@ if TYPE_CHECKING:
34
37
  def parse_obj(cls, *args: Any, **kwargs: Any) -> V1Model: ...
35
38
  @classmethod
36
39
  def parse_raw(cls, *args: Any, **kwargs: Any) -> V1Model: ...
37
- def dict(self, *args: Any, **kwargs: Any) -> dict[str, Any]: ...
38
- def json(self, *args: Any, **kwargs: Any) -> str: ...
39
- def copy(self, *args: Any, **kwargs: Any) -> V1Model: ...
40
-
41
40
 
42
- PYTHON_VERSION = sys.version_info
43
-
44
- pydantic_major_version, *_ = version(pydantic.__name__).split(".")
45
- IS_PYDANTIC_V2: bool = int(pydantic_major_version) >= 2
46
-
47
-
48
- ModelT = TypeVar("ModelT")
49
- RT = TypeVar("RT")
50
- P = ParamSpec("P")
41
+ def dict(self, **kwargs: Any) -> dict[str, Any]: ...
42
+ def json(self, **kwargs: Any) -> str: ...
43
+ def copy(self, **kwargs: Any) -> V1Model: ...
51
44
 
52
45
 
53
46
  # Maps {v2 -> v1} model config keys that were renamed in v2.
@@ -66,11 +59,17 @@ _V1_CONFIG_KEYS = {
66
59
  }
67
60
 
68
61
 
69
- def _convert_v2_config(v2_config: dict[str, Any]) -> dict[str, Any]:
70
- """Return a copy of the v2 ConfigDict with renamed v1 keys."""
62
+ def convert_v2_config(v2_config: dict[str, Any]) -> dict[str, Any]:
63
+ """Internal helper: Return a copy of the v2 ConfigDict with renamed v1 keys."""
71
64
  return {_V1_CONFIG_KEYS.get(k, k): v for k, v in v2_config.items()}
72
65
 
73
66
 
67
+ @lru_cache(maxsize=None) # Reduce repeat introspection via `signature()`
68
+ def allowed_arg_names(func: Callable) -> set[str]:
69
+ """Internal helper: Return the names of args accepted by the given function."""
70
+ return set(signature(func).parameters)
71
+
72
+
74
73
  # Pydantic BaseModels are defined with a custom metaclass, but its namespace
75
74
  # has changed between pydantic versions.
76
75
  #
@@ -87,28 +86,22 @@ class V1MixinMetaclass(PydanticModelMetaclass):
87
86
  namespace: dict[str, Any],
88
87
  **kwargs: Any,
89
88
  ):
90
- # Converts a `model_config` dict in a V2 class definition, e.g.:
91
- #
92
- # class MyModel(BaseModel):
89
+ # In the class definition, convert the model config, if any:
90
+ # # BEFORE
91
+ # class MyModel(BaseModel): # v2 model with `ConfigDict`
93
92
  # model_config = ConfigDict(populate_by_name=True)
94
93
  #
95
- # ...to a `Config` class in a V1 class definition, e.g.:
96
- #
97
- # class MyModel(BaseModel):
94
+ # # AFTER
95
+ # class MyModel(BaseModel): # v1 model with inner `Config` class
98
96
  # class Config:
99
97
  # allow_population_by_field_name = True
100
- #
101
98
  if config_dict := namespace.pop("model_config", None):
102
- namespace["Config"] = type("Config", (), _convert_v2_config(config_dict))
99
+ namespace["Config"] = type("Config", (), convert_v2_config(config_dict))
103
100
  return super().__new__(cls, name, bases, namespace, **kwargs)
104
101
 
105
- # note: workarounds to patch "class properties" aren't consistent between python
106
- # versions, so this will have to do until changes are needed.
107
- if not ((3, 9) <= PYTHON_VERSION < (3, 13)):
108
-
109
- @property
110
- def model_fields(self) -> dict[str, Any]:
111
- return self.__fields__
102
+ @property
103
+ def model_fields(self) -> dict[str, Any]:
104
+ return self.__fields__
112
105
 
113
106
 
114
107
  # Mixin to ensure compatibility of Pydantic models if Pydantic v1 is detected.
@@ -118,6 +111,23 @@ class V1MixinMetaclass(PydanticModelMetaclass):
118
111
  # Whenever possible, users should strongly prefer upgrading to Pydantic v2 to
119
112
  # ensure full compatibility.
120
113
  class V1Mixin(metaclass=V1MixinMetaclass):
114
+ # Internal compat helpers
115
+ @classmethod
116
+ def _dump_json_vals(cls, values: dict[str, Any], by_alias: bool) -> dict[str, Any]:
117
+ """Reserialize values from `Json`-typed fields after dumping the model to dict."""
118
+ # Get the expected keys (after `.model_dump()`) for `Json`-typed fields.
119
+ # Note: In v1, `Json` fields have `ModelField.parse_json == True`
120
+ json_fields = (f for f in cls.__fields__.values() if f.parse_json)
121
+ get_key = attrgetter("alias" if by_alias else "name")
122
+ json_field_keys = set(map(get_key, json_fields))
123
+
124
+ return {
125
+ # Only serialize `Json` fields with non-null values.
126
+ k: to_json(v) if ((v is not None) and (k in json_field_keys)) else v
127
+ for k, v in values.items()
128
+ }
129
+
130
+ # ------------------------------------------------------------------------------
121
131
  @classmethod
122
132
  def __try_update_forward_refs__(cls: type[V1Model], **localns: Any) -> None:
123
133
  if hasattr(sup := super(), "__try_update_forward_refs__"):
@@ -139,23 +149,35 @@ class V1Mixin(metaclass=V1MixinMetaclass):
139
149
  def model_validate_json(cls, *args: Any, **kwargs: Any) -> V1Model:
140
150
  return cls.parse_raw(*args, **kwargs)
141
151
 
142
- def model_dump(self: V1Model, *args: Any, **kwargs: Any) -> dict[str, Any]:
143
- return self.dict(*args, **kwargs)
152
+ def model_dump(self: V1Model, **kwargs: Any) -> dict[str, Any]:
153
+ # Pass only kwargs that are allowed in the V1 method.
154
+ allowed_keys = allowed_arg_names(self.dict) & kwargs.keys()
155
+ dict_ = self.dict(**{k: kwargs[k] for k in allowed_keys})
156
+
157
+ # Ugly hack: Try to serialize `Json` fields correctly when `round_trip=True` in pydantic v1
158
+ if kwargs.get("round_trip", False):
159
+ by_alias: bool = kwargs.get("by_alias", False)
160
+ return self._dump_json_vals(dict_, by_alias=by_alias)
161
+
162
+ return dict_
144
163
 
145
- def model_dump_json(self: V1Model, *args: Any, **kwargs: Any) -> str:
146
- return self.json(*args, **kwargs)
164
+ def model_dump_json(self: V1Model, **kwargs: Any) -> str:
165
+ # Pass only kwargs that are allowed in the V1 method.
166
+ allowed_keys = allowed_arg_names(self.json) & kwargs.keys()
167
+ json_ = self.json(**{k: kwargs[k] for k in allowed_keys})
147
168
 
148
- def model_copy(self: V1Model, *args: Any, **kwargs: Any) -> V1Model:
149
- return self.copy(*args, **kwargs)
169
+ # Ugly hack: Try to serialize `Json` fields correctly when `round_trip=True` in pydantic v1
170
+ if kwargs.get("round_trip", False):
171
+ by_alias: bool = kwargs.get("by_alias", False)
172
+ dict_ = json.loads(json_)
173
+ return json.dumps(self._dump_json_vals(dict_, by_alias=by_alias))
150
174
 
151
- # workarounds to patch "class properties" aren't consistent between python
152
- # versions, so this will have to do until changes are needed.
153
- if (3, 9) <= PYTHON_VERSION < (3, 13):
175
+ return json_
154
176
 
155
- @classmethod # type: ignore[misc]
156
- @property
157
- def model_fields(cls: type[V1Model]) -> Mapping[str, Any]:
158
- return cls.__fields__
177
+ def model_copy(self: V1Model, **kwargs: Any) -> V1Model:
178
+ # Pass only kwargs that are allowed in the V1 method.
179
+ allowed_keys = allowed_arg_names(self.copy) & kwargs.keys()
180
+ return self.copy(**{k: kwargs[k] for k in allowed_keys})
159
181
 
160
182
  @property
161
183
  def model_fields_set(self: V1Model) -> set[str]:
@@ -1,7 +1,5 @@
1
1
  from wandb.apis.public.api import Api, RetryingClient, requests
2
2
  from wandb.apis.public.artifacts import (
3
- ARTIFACT_FILES_FRAGMENT,
4
- ARTIFACTS_TYPES_FRAGMENT,
5
3
  ArtifactCollection,
6
4
  ArtifactCollections,
7
5
  ArtifactFiles,
@@ -10,8 +8,10 @@ from wandb.apis.public.artifacts import (
10
8
  ArtifactTypes,
11
9
  RunArtifacts,
12
10
  )
11
+ from wandb.apis.public.automations import Automations
13
12
  from wandb.apis.public.files import FILE_FRAGMENT, File, Files
14
13
  from wandb.apis.public.history import HistoryScan, SampledHistoryScan
14
+ from wandb.apis.public.integrations import SlackIntegrations, WebhookIntegrations
15
15
  from wandb.apis.public.jobs import (
16
16
  Job,
17
17
  QueuedRun,
wandb/apis/public/api.py CHANGED
@@ -15,7 +15,7 @@ import json
15
15
  import logging
16
16
  import os
17
17
  import urllib
18
- from typing import Any, Dict, List, Optional
18
+ from typing import TYPE_CHECKING, Any, Dict, Iterator, List, Optional
19
19
 
20
20
  import requests
21
21
  from wandb_gql import Client, gql
@@ -32,15 +32,19 @@ from wandb.apis.public.utils import (
32
32
  fetch_org_from_settings_or_entity,
33
33
  parse_org_from_registry_path,
34
34
  )
35
+ from wandb.proto.wandb_deprecated import Deprecated
35
36
  from wandb.proto.wandb_internal_pb2 import ServerFeature
36
37
  from wandb.sdk.artifacts._validators import is_artifact_registry_project
37
38
  from wandb.sdk.internal.internal_api import Api as InternalApi
38
39
  from wandb.sdk.internal.thread_local_settings import _thread_local_api_settings
39
40
  from wandb.sdk.launch.utils import LAUNCH_DEFAULT_PROJECT
40
41
  from wandb.sdk.lib import retry, runid
41
- from wandb.sdk.lib.deprecate import Deprecated, deprecate
42
+ from wandb.sdk.lib.deprecate import deprecate
42
43
  from wandb.sdk.lib.gql_request import GraphQLSession
43
44
 
45
+ if TYPE_CHECKING:
46
+ from wandb.automations import Integration, SlackIntegration, WebhookIntegration
47
+
44
48
  logger = logging.getLogger(__name__)
45
49
 
46
50
 
@@ -1517,3 +1521,111 @@ class Api:
1517
1521
  self.settings, self.default_entity
1518
1522
  )
1519
1523
  return Registries(self.client, organization, filter)
1524
+
1525
+ def integrations(
1526
+ self,
1527
+ entity: Optional[str] = None,
1528
+ *,
1529
+ per_page: int = 50,
1530
+ ) -> Iterator["Integration"]:
1531
+ """Return an iterator of all integrations for an entity.
1532
+
1533
+ Args:
1534
+ entity (str, optional): The entity (e.g. team name) for which to
1535
+ fetch integrations. If not provided, the user's default entity
1536
+ will be used.
1537
+ per_page (int, optional): Number of integrations to fetch per page.
1538
+ Defaults to 50.
1539
+
1540
+ Yields:
1541
+ Iterator[SlackIntegration | WebhookIntegration]: An iterator of any supported integrations.
1542
+ """
1543
+ from wandb.apis.public.integrations import Integrations
1544
+
1545
+ entity = entity or self.default_entity
1546
+ params = {"entityName": entity, "includeWebhook": True, "includeSlack": True}
1547
+ return Integrations(client=self.client, variables=params, per_page=per_page)
1548
+
1549
+ def webhook_integrations(
1550
+ self, entity: Optional[str] = None, *, per_page: int = 50
1551
+ ) -> Iterator["WebhookIntegration"]:
1552
+ """Return an iterator of webhook integrations for an entity.
1553
+
1554
+ Args:
1555
+ entity (str, optional): The entity (e.g. team name) for which to
1556
+ fetch integrations. If not provided, the user's default entity
1557
+ will be used.
1558
+ per_page (int, optional): Number of integrations to fetch per page.
1559
+ Defaults to 50.
1560
+
1561
+ Yields:
1562
+ Iterator[WebhookIntegration]: An iterator of webhook integrations.
1563
+
1564
+ Examples:
1565
+ Get all registered webhook integrations for the team "my-team":
1566
+ ```python
1567
+ import wandb
1568
+
1569
+ api = wandb.Api()
1570
+ webhook_integrations = api.webhook_integrations(entity="my-team")
1571
+ ```
1572
+
1573
+ Find only webhook integrations that post requests to "https://my-fake-url.com":
1574
+ ```python
1575
+ webhook_integrations = api.webhook_integrations(entity="my-team")
1576
+ my_webhooks = [
1577
+ ig
1578
+ for ig in webhook_integrations
1579
+ if ig.url_endpoint.startswith("https://my-fake-url.com")
1580
+ ]
1581
+ ```
1582
+ """
1583
+ from wandb.apis.public.integrations import WebhookIntegrations
1584
+
1585
+ entity = entity or self.default_entity
1586
+ params = {"entityName": entity, "includeWebhook": True}
1587
+ return WebhookIntegrations(
1588
+ client=self.client, variables=params, per_page=per_page
1589
+ )
1590
+
1591
+ def slack_integrations(
1592
+ self, entity: Optional[str] = None, *, per_page: int = 50
1593
+ ) -> Iterator["SlackIntegration"]:
1594
+ """Return an iterator of Slack integrations for an entity.
1595
+
1596
+ Args:
1597
+ entity (str, optional): The entity (e.g. team name) for which to
1598
+ fetch integrations. If not provided, the user's default entity
1599
+ will be used.
1600
+ per_page (int, optional): Number of integrations to fetch per page.
1601
+ Defaults to 50.
1602
+
1603
+ Yields:
1604
+ Iterator[SlackIntegration]: An iterator of Slack integrations.
1605
+
1606
+ Examples:
1607
+ Get all registered Slack integrations for the team "my-team":
1608
+ ```python
1609
+ import wandb
1610
+
1611
+ api = wandb.Api()
1612
+ slack_integrations = api.slack_integrations(entity="my-team")
1613
+ ```
1614
+
1615
+ Find only Slack integrations that post to channel names starting with "team-alerts-":
1616
+ ```python
1617
+ slack_integrations = api.slack_integrations(entity="my-team")
1618
+ team_alert_integrations = [
1619
+ ig
1620
+ for ig in slack_integrations
1621
+ if ig.channel_name.startswith("team-alerts-")
1622
+ ]
1623
+ ```
1624
+ """
1625
+ from wandb.apis.public.integrations import SlackIntegrations
1626
+
1627
+ entity = entity or self.default_entity
1628
+ params = {"entityName": entity, "includeSlack": True}
1629
+ return SlackIntegrations(
1630
+ client=self.client, variables=params, per_page=per_page
1631
+ )