wandb 0.19.8__py3-none-any.whl → 0.19.10__py3-none-any.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 (153) hide show
  1. wandb/__init__.py +5 -1
  2. wandb/__init__.pyi +15 -8
  3. wandb/_pydantic/__init__.py +30 -0
  4. wandb/_pydantic/base.py +148 -0
  5. wandb/_pydantic/utils.py +66 -0
  6. wandb/_pydantic/v1_compat.py +284 -0
  7. wandb/apis/paginator.py +82 -38
  8. wandb/apis/public/__init__.py +2 -2
  9. wandb/apis/public/api.py +111 -53
  10. wandb/apis/public/artifacts.py +387 -639
  11. wandb/apis/public/automations.py +69 -0
  12. wandb/apis/public/files.py +2 -2
  13. wandb/apis/public/integrations.py +168 -0
  14. wandb/apis/public/projects.py +32 -2
  15. wandb/apis/public/reports.py +2 -2
  16. wandb/apis/public/runs.py +19 -11
  17. wandb/apis/public/utils.py +107 -1
  18. wandb/automations/__init__.py +81 -0
  19. wandb/automations/_filters/__init__.py +40 -0
  20. wandb/automations/_filters/expressions.py +179 -0
  21. wandb/automations/_filters/operators.py +267 -0
  22. wandb/automations/_filters/run_metrics.py +183 -0
  23. wandb/automations/_generated/__init__.py +184 -0
  24. wandb/automations/_generated/create_filter_trigger.py +21 -0
  25. wandb/automations/_generated/create_generic_webhook_integration.py +43 -0
  26. wandb/automations/_generated/delete_trigger.py +19 -0
  27. wandb/automations/_generated/enums.py +33 -0
  28. wandb/automations/_generated/fragments.py +343 -0
  29. wandb/automations/_generated/generic_webhook_integrations_by_entity.py +22 -0
  30. wandb/automations/_generated/get_triggers.py +24 -0
  31. wandb/automations/_generated/get_triggers_by_entity.py +24 -0
  32. wandb/automations/_generated/input_types.py +104 -0
  33. wandb/automations/_generated/integrations_by_entity.py +22 -0
  34. wandb/automations/_generated/operations.py +710 -0
  35. wandb/automations/_generated/slack_integrations_by_entity.py +22 -0
  36. wandb/automations/_generated/update_filter_trigger.py +21 -0
  37. wandb/automations/_utils.py +123 -0
  38. wandb/automations/_validators.py +73 -0
  39. wandb/automations/actions.py +205 -0
  40. wandb/automations/automations.py +109 -0
  41. wandb/automations/events.py +235 -0
  42. wandb/automations/integrations.py +26 -0
  43. wandb/automations/scopes.py +76 -0
  44. wandb/beta/workflows.py +9 -10
  45. wandb/bin/gpu_stats +0 -0
  46. wandb/cli/cli.py +3 -3
  47. wandb/integration/keras/keras.py +2 -1
  48. wandb/integration/langchain/wandb_tracer.py +2 -1
  49. wandb/integration/metaflow/metaflow.py +19 -17
  50. wandb/integration/sacred/__init__.py +1 -1
  51. wandb/jupyter.py +155 -133
  52. wandb/old/summary.py +0 -2
  53. wandb/proto/v3/wandb_internal_pb2.py +297 -292
  54. wandb/proto/v3/wandb_settings_pb2.py +2 -2
  55. wandb/proto/v3/wandb_telemetry_pb2.py +10 -10
  56. wandb/proto/v4/wandb_internal_pb2.py +292 -292
  57. wandb/proto/v4/wandb_settings_pb2.py +2 -2
  58. wandb/proto/v4/wandb_telemetry_pb2.py +10 -10
  59. wandb/proto/v5/wandb_internal_pb2.py +292 -292
  60. wandb/proto/v5/wandb_settings_pb2.py +2 -2
  61. wandb/proto/v5/wandb_telemetry_pb2.py +10 -10
  62. wandb/proto/v6/wandb_base_pb2.py +41 -0
  63. wandb/proto/v6/wandb_internal_pb2.py +393 -0
  64. wandb/proto/v6/wandb_server_pb2.py +78 -0
  65. wandb/proto/v6/wandb_settings_pb2.py +58 -0
  66. wandb/proto/v6/wandb_telemetry_pb2.py +52 -0
  67. wandb/proto/wandb_base_pb2.py +2 -0
  68. wandb/proto/wandb_deprecated.py +10 -0
  69. wandb/proto/wandb_internal_pb2.py +3 -1
  70. wandb/proto/wandb_server_pb2.py +2 -0
  71. wandb/proto/wandb_settings_pb2.py +2 -0
  72. wandb/proto/wandb_telemetry_pb2.py +2 -0
  73. wandb/sdk/artifacts/_generated/__init__.py +248 -0
  74. wandb/sdk/artifacts/_generated/artifact_collection_membership_files.py +43 -0
  75. wandb/sdk/artifacts/_generated/artifact_version_files.py +36 -0
  76. wandb/sdk/artifacts/_generated/create_artifact_collection_tag_assignments.py +36 -0
  77. wandb/sdk/artifacts/_generated/delete_artifact_collection_tag_assignments.py +25 -0
  78. wandb/sdk/artifacts/_generated/delete_artifact_portfolio.py +35 -0
  79. wandb/sdk/artifacts/_generated/delete_artifact_sequence.py +35 -0
  80. wandb/sdk/artifacts/_generated/enums.py +17 -0
  81. wandb/sdk/artifacts/_generated/fragments.py +186 -0
  82. wandb/sdk/artifacts/_generated/input_types.py +16 -0
  83. wandb/sdk/artifacts/_generated/move_artifact_collection.py +35 -0
  84. wandb/sdk/artifacts/_generated/operations.py +510 -0
  85. wandb/sdk/artifacts/_generated/project_artifact_collection.py +101 -0
  86. wandb/sdk/artifacts/_generated/project_artifact_collections.py +33 -0
  87. wandb/sdk/artifacts/_generated/project_artifact_type.py +24 -0
  88. wandb/sdk/artifacts/_generated/project_artifact_types.py +24 -0
  89. wandb/sdk/artifacts/_generated/project_artifacts.py +42 -0
  90. wandb/sdk/artifacts/_generated/run_input_artifacts.py +51 -0
  91. wandb/sdk/artifacts/_generated/run_output_artifacts.py +51 -0
  92. wandb/sdk/artifacts/_generated/update_artifact_portfolio.py +35 -0
  93. wandb/sdk/artifacts/_generated/update_artifact_sequence.py +35 -0
  94. wandb/sdk/artifacts/_graphql_fragments.py +56 -81
  95. wandb/sdk/artifacts/_validators.py +1 -0
  96. wandb/sdk/artifacts/artifact.py +110 -49
  97. wandb/sdk/artifacts/artifact_manifest_entry.py +2 -1
  98. wandb/sdk/artifacts/artifact_saver.py +16 -2
  99. wandb/sdk/artifacts/storage_handlers/azure_handler.py +1 -0
  100. wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +23 -2
  101. wandb/sdk/data_types/audio.py +1 -3
  102. wandb/sdk/data_types/base_types/media.py +13 -7
  103. wandb/sdk/data_types/base_types/wb_value.py +34 -11
  104. wandb/sdk/data_types/html.py +36 -9
  105. wandb/sdk/data_types/image.py +56 -37
  106. wandb/sdk/data_types/molecule.py +1 -5
  107. wandb/sdk/data_types/object_3d.py +2 -1
  108. wandb/sdk/data_types/saved_model.py +7 -9
  109. wandb/sdk/data_types/table.py +5 -0
  110. wandb/sdk/data_types/trace_tree.py +2 -0
  111. wandb/sdk/data_types/utils.py +1 -1
  112. wandb/sdk/data_types/video.py +15 -30
  113. wandb/sdk/interface/interface.py +2 -0
  114. wandb/{apis/public → sdk/internal}/_generated/__init__.py +0 -6
  115. wandb/{apis/public → sdk/internal}/_generated/server_features_query.py +3 -3
  116. wandb/sdk/internal/internal_api.py +138 -47
  117. wandb/sdk/internal/profiler.py +6 -5
  118. wandb/sdk/internal/run.py +13 -6
  119. wandb/sdk/internal/sender.py +2 -0
  120. wandb/sdk/internal/sender_config.py +8 -11
  121. wandb/sdk/internal/settings_static.py +24 -2
  122. wandb/sdk/lib/apikey.py +40 -20
  123. wandb/sdk/lib/asyncio_compat.py +1 -1
  124. wandb/sdk/lib/deprecate.py +13 -22
  125. wandb/sdk/lib/disabled.py +2 -1
  126. wandb/sdk/lib/printer.py +37 -8
  127. wandb/sdk/lib/printer_asyncio.py +46 -0
  128. wandb/sdk/lib/redirect.py +10 -5
  129. wandb/sdk/lib/run_moment.py +4 -6
  130. wandb/sdk/lib/wb_logging.py +161 -0
  131. wandb/sdk/service/server_sock.py +19 -14
  132. wandb/sdk/service/service.py +9 -7
  133. wandb/sdk/service/streams.py +5 -0
  134. wandb/sdk/verify/verify.py +6 -3
  135. wandb/sdk/wandb_config.py +44 -43
  136. wandb/sdk/wandb_init.py +323 -141
  137. wandb/sdk/wandb_login.py +13 -4
  138. wandb/sdk/wandb_metadata.py +107 -91
  139. wandb/sdk/wandb_run.py +529 -325
  140. wandb/sdk/wandb_settings.py +422 -202
  141. wandb/sdk/wandb_setup.py +52 -1
  142. wandb/util.py +29 -29
  143. {wandb-0.19.8.dist-info → wandb-0.19.10.dist-info}/METADATA +7 -7
  144. {wandb-0.19.8.dist-info → wandb-0.19.10.dist-info}/RECORD +150 -93
  145. wandb/_globals.py +0 -19
  146. wandb/apis/public/_generated/base.py +0 -128
  147. wandb/apis/public/_generated/typing_compat.py +0 -14
  148. /wandb/{apis/public → sdk/internal}/_generated/enums.py +0 -0
  149. /wandb/{apis/public → sdk/internal}/_generated/input_types.py +0 -0
  150. /wandb/{apis/public → sdk/internal}/_generated/operations.py +0 -0
  151. {wandb-0.19.8.dist-info → wandb-0.19.10.dist-info}/WHEEL +0 -0
  152. {wandb-0.19.8.dist-info → wandb-0.19.10.dist-info}/entry_points.txt +0 -0
  153. {wandb-0.19.8.dist-info → wandb-0.19.10.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,42 @@
1
+ # Generated by ariadne-codegen
2
+ # Source: tools/graphql_codegen/artifacts/
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import Literal, Optional
7
+
8
+ from pydantic import Field
9
+
10
+ from wandb._pydantic import GQLBase, Typename
11
+
12
+ from .fragments import ArtifactsFragment
13
+
14
+
15
+ class ProjectArtifacts(GQLBase):
16
+ project: Optional[ProjectArtifactsProject]
17
+
18
+
19
+ class ProjectArtifactsProject(GQLBase):
20
+ artifact_type: Optional[ProjectArtifactsProjectArtifactType] = Field(
21
+ alias="artifactType"
22
+ )
23
+
24
+
25
+ class ProjectArtifactsProjectArtifactType(GQLBase):
26
+ artifact_collection: Optional[
27
+ ProjectArtifactsProjectArtifactTypeArtifactCollection
28
+ ] = Field(alias="artifactCollection")
29
+
30
+
31
+ class ProjectArtifactsProjectArtifactTypeArtifactCollection(GQLBase):
32
+ typename__: Typename[
33
+ Literal["ArtifactCollection", "ArtifactPortfolio", "ArtifactSequence"]
34
+ ]
35
+ name: str
36
+ artifacts: Optional[ArtifactsFragment]
37
+
38
+
39
+ ProjectArtifacts.model_rebuild()
40
+ ProjectArtifactsProject.model_rebuild()
41
+ ProjectArtifactsProjectArtifactType.model_rebuild()
42
+ ProjectArtifactsProjectArtifactTypeArtifactCollection.model_rebuild()
@@ -0,0 +1,51 @@
1
+ # Generated by ariadne-codegen
2
+ # Source: tools/graphql_codegen/artifacts/
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import List, Optional
7
+
8
+ from pydantic import Field
9
+
10
+ from wandb._pydantic import GQLBase
11
+
12
+ from .fragments import ArtifactFragment
13
+
14
+
15
+ class RunInputArtifacts(GQLBase):
16
+ project: Optional[RunInputArtifactsProject]
17
+
18
+
19
+ class RunInputArtifactsProject(GQLBase):
20
+ run: Optional[RunInputArtifactsProjectRun]
21
+
22
+
23
+ class RunInputArtifactsProjectRun(GQLBase):
24
+ input_artifacts: Optional[RunInputArtifactsProjectRunInputArtifacts] = Field(
25
+ alias="inputArtifacts"
26
+ )
27
+
28
+
29
+ class RunInputArtifactsProjectRunInputArtifacts(GQLBase):
30
+ total_count: int = Field(alias="totalCount")
31
+ edges: List[RunInputArtifactsProjectRunInputArtifactsEdges]
32
+ page_info: RunInputArtifactsProjectRunInputArtifactsPageInfo = Field(
33
+ alias="pageInfo"
34
+ )
35
+
36
+
37
+ class RunInputArtifactsProjectRunInputArtifactsEdges(GQLBase):
38
+ node: Optional[ArtifactFragment]
39
+ cursor: str
40
+
41
+
42
+ class RunInputArtifactsProjectRunInputArtifactsPageInfo(GQLBase):
43
+ end_cursor: Optional[str] = Field(alias="endCursor")
44
+ has_next_page: bool = Field(alias="hasNextPage")
45
+
46
+
47
+ RunInputArtifacts.model_rebuild()
48
+ RunInputArtifactsProject.model_rebuild()
49
+ RunInputArtifactsProjectRun.model_rebuild()
50
+ RunInputArtifactsProjectRunInputArtifacts.model_rebuild()
51
+ RunInputArtifactsProjectRunInputArtifactsEdges.model_rebuild()
@@ -0,0 +1,51 @@
1
+ # Generated by ariadne-codegen
2
+ # Source: tools/graphql_codegen/artifacts/
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import List, Optional
7
+
8
+ from pydantic import Field
9
+
10
+ from wandb._pydantic import GQLBase
11
+
12
+ from .fragments import ArtifactFragment
13
+
14
+
15
+ class RunOutputArtifacts(GQLBase):
16
+ project: Optional[RunOutputArtifactsProject]
17
+
18
+
19
+ class RunOutputArtifactsProject(GQLBase):
20
+ run: Optional[RunOutputArtifactsProjectRun]
21
+
22
+
23
+ class RunOutputArtifactsProjectRun(GQLBase):
24
+ output_artifacts: Optional[RunOutputArtifactsProjectRunOutputArtifacts] = Field(
25
+ alias="outputArtifacts"
26
+ )
27
+
28
+
29
+ class RunOutputArtifactsProjectRunOutputArtifacts(GQLBase):
30
+ total_count: int = Field(alias="totalCount")
31
+ edges: List[RunOutputArtifactsProjectRunOutputArtifactsEdges]
32
+ page_info: RunOutputArtifactsProjectRunOutputArtifactsPageInfo = Field(
33
+ alias="pageInfo"
34
+ )
35
+
36
+
37
+ class RunOutputArtifactsProjectRunOutputArtifactsEdges(GQLBase):
38
+ node: Optional[ArtifactFragment]
39
+ cursor: str
40
+
41
+
42
+ class RunOutputArtifactsProjectRunOutputArtifactsPageInfo(GQLBase):
43
+ end_cursor: Optional[str] = Field(alias="endCursor")
44
+ has_next_page: bool = Field(alias="hasNextPage")
45
+
46
+
47
+ RunOutputArtifacts.model_rebuild()
48
+ RunOutputArtifactsProject.model_rebuild()
49
+ RunOutputArtifactsProjectRun.model_rebuild()
50
+ RunOutputArtifactsProjectRunOutputArtifacts.model_rebuild()
51
+ RunOutputArtifactsProjectRunOutputArtifactsEdges.model_rebuild()
@@ -0,0 +1,35 @@
1
+ # Generated by ariadne-codegen
2
+ # Source: tools/graphql_codegen/artifacts/
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import Literal, Optional
7
+
8
+ from pydantic import Field
9
+
10
+ from wandb._pydantic import GQLBase, GQLId, Typename
11
+
12
+
13
+ class UpdateArtifactPortfolio(GQLBase):
14
+ update_artifact_portfolio: Optional[
15
+ UpdateArtifactPortfolioUpdateArtifactPortfolio
16
+ ] = Field(alias="updateArtifactPortfolio")
17
+
18
+
19
+ class UpdateArtifactPortfolioUpdateArtifactPortfolio(GQLBase):
20
+ artifact_collection: UpdateArtifactPortfolioUpdateArtifactPortfolioArtifactCollection = Field(
21
+ alias="artifactCollection"
22
+ )
23
+
24
+
25
+ class UpdateArtifactPortfolioUpdateArtifactPortfolioArtifactCollection(GQLBase):
26
+ typename__: Typename[
27
+ Literal["ArtifactCollection", "ArtifactPortfolio", "ArtifactSequence"]
28
+ ]
29
+ id: GQLId
30
+ name: str
31
+ description: Optional[str]
32
+
33
+
34
+ UpdateArtifactPortfolio.model_rebuild()
35
+ UpdateArtifactPortfolioUpdateArtifactPortfolio.model_rebuild()
@@ -0,0 +1,35 @@
1
+ # Generated by ariadne-codegen
2
+ # Source: tools/graphql_codegen/artifacts/
3
+
4
+ from __future__ import annotations
5
+
6
+ from typing import Literal, Optional
7
+
8
+ from pydantic import Field
9
+
10
+ from wandb._pydantic import GQLBase, GQLId, Typename
11
+
12
+
13
+ class UpdateArtifactSequence(GQLBase):
14
+ update_artifact_sequence: Optional[UpdateArtifactSequenceUpdateArtifactSequence] = (
15
+ Field(alias="updateArtifactSequence")
16
+ )
17
+
18
+
19
+ class UpdateArtifactSequenceUpdateArtifactSequence(GQLBase):
20
+ artifact_collection: UpdateArtifactSequenceUpdateArtifactSequenceArtifactCollection = Field(
21
+ alias="artifactCollection"
22
+ )
23
+
24
+
25
+ class UpdateArtifactSequenceUpdateArtifactSequenceArtifactCollection(GQLBase):
26
+ typename__: Typename[
27
+ Literal["ArtifactCollection", "ArtifactPortfolio", "ArtifactSequence"]
28
+ ]
29
+ id: GQLId
30
+ name: str
31
+ description: Optional[str]
32
+
33
+
34
+ UpdateArtifactSequence.model_rebuild()
35
+ UpdateArtifactSequenceUpdateArtifactSequence.model_rebuild()
@@ -1,64 +1,57 @@
1
+ from __future__ import annotations
2
+
3
+ from textwrap import dedent
4
+
5
+ from wandb_graphql.language.printer import print_ast
6
+
7
+ from wandb.apis.public.utils import gql_compat
1
8
  from wandb.sdk.internal.internal_api import Api as InternalApi
2
9
 
3
- ARTIFACTS_TYPES_FRAGMENT = """
4
- fragment ArtifactTypesFragment on ArtifactTypeConnection {
5
- edges {
6
- node {
7
- id
8
- name
9
- description
10
- createdAt
11
- }
12
- cursor
10
+ OMITTABLE_ARTIFACT_FIELDS = frozenset(
11
+ {
12
+ "ttlDurationSeconds",
13
+ "ttlIsInherited",
14
+ "aliases",
15
+ "tags",
16
+ "historyStep",
13
17
  }
14
- pageInfo {
15
- endCursor
16
- hasNextPage
17
- }
18
- }
19
- """
18
+ )
20
19
 
21
- ARTIFACT_FILES_FRAGMENT = """fragment ArtifactFilesFragment on Artifact {
22
- files(names: $fileNames, after: $fileCursor, first: $fileLimit) {
23
- edges {
24
- node {
25
- id
26
- name: displayName
27
- url
28
- sizeBytes
29
- storagePath
30
- mimetype
31
- updatedAt
32
- digest
33
- md5
34
- directUrl
35
- }
36
- cursor
37
- }
38
- pageInfo {
39
- endCursor
40
- hasNextPage
41
- }
42
- }
43
- }"""
20
+
21
+ def omit_artifact_fields(api: InternalApi) -> set[str]:
22
+ """Return names of Artifact fields to remove from GraphQL requests (for server compatibility)."""
23
+ allowed_fields = set(api.server_artifact_introspection())
24
+ return set(OMITTABLE_ARTIFACT_FIELDS - allowed_fields)
44
25
 
45
26
 
46
27
  def _gql_artifact_fragment(include_aliases: bool = True) -> str:
47
28
  """Return a GraphQL query fragment with all parseable Artifact attributes."""
48
- allowed_fields = set(InternalApi().server_artifact_introspection())
49
-
50
- supports_ttl = "ttlIsInherited" in allowed_fields
51
- supports_tags = "tags" in allowed_fields
29
+ omit_fields = omit_artifact_fields(api=InternalApi())
52
30
 
53
- ttl_duration_seconds = "ttlDurationSeconds" if supports_ttl else ""
54
- ttl_is_inherited = "ttlIsInherited" if supports_ttl else ""
31
+ # Respect the `include_aliases` flag
32
+ if not include_aliases:
33
+ omit_fields.add("aliases")
55
34
 
56
- tags = "tags {name}" if supports_tags else ""
57
-
58
- # The goal is to move all artifact aliases fetches to the membership level in the future
59
- # but this is a quick fix to unblock the registry work
60
- aliases = (
61
- """aliases {
35
+ artifact_fragment_str = dedent(
36
+ """\
37
+ fragment ArtifactFragment on Artifact {
38
+ id
39
+ artifactSequence {
40
+ project {
41
+ entityName
42
+ name
43
+ }
44
+ name
45
+ }
46
+ versionIndex
47
+ artifactType {
48
+ name
49
+ }
50
+ description
51
+ metadata
52
+ ttlDurationSeconds
53
+ ttlIsInherited
54
+ aliases {
62
55
  artifactCollection {
63
56
  project {
64
57
  entityName
@@ -67,43 +60,25 @@ def _gql_artifact_fragment(include_aliases: bool = True) -> str:
67
60
  name
68
61
  }
69
62
  alias
70
- }"""
71
- if include_aliases
72
- else ""
73
- )
74
-
75
- return f"""
76
- fragment ArtifactFragment on Artifact {{
77
- id
78
- artifactSequence {{
79
- project {{
80
- entityName
81
- name
82
- }}
83
- name
84
- }}
85
- versionIndex
86
- artifactType {{
63
+ }
64
+ tags {
87
65
  name
88
- }}
89
- description
90
- metadata
91
- {ttl_duration_seconds}
92
- {ttl_is_inherited}
93
- {aliases}
94
- {tags}
66
+ }
67
+ historyStep
95
68
  state
96
- currentManifest {{
97
- file {{
69
+ currentManifest {
70
+ file {
98
71
  directUrl
99
- }}
100
- }}
72
+ }
73
+ }
101
74
  commitHash
102
75
  fileCount
103
76
  createdAt
104
77
  updatedAt
105
- }}
106
- """
78
+ }"""
79
+ )
80
+ compat_doc = gql_compat(artifact_fragment_str, omit_fields=omit_fields)
81
+ return print_ast(compat_doc)
107
82
 
108
83
 
109
84
  def _gql_registry_fragment() -> str:
@@ -22,6 +22,7 @@ if TYPE_CHECKING:
22
22
 
23
23
 
24
24
  REGISTRY_PREFIX: Final[str] = "wandb-registry-"
25
+ MAX_ARTIFACT_METADATA_KEYS: Final[int] = 100
25
26
 
26
27
 
27
28
  # For mypy checks
@@ -19,18 +19,7 @@ from dataclasses import dataclass
19
19
  from datetime import datetime, timedelta
20
20
  from functools import partial
21
21
  from pathlib import PurePosixPath
22
- from typing import (
23
- IO,
24
- Any,
25
- Dict,
26
- Iterator,
27
- Literal,
28
- Optional,
29
- Sequence,
30
- Type,
31
- cast,
32
- final,
33
- )
22
+ from typing import IO, Any, Dict, Iterator, Literal, Sequence, Type, cast, final
34
23
  from urllib.parse import quote, urljoin, urlparse
35
24
 
36
25
  import requests
@@ -42,6 +31,8 @@ from wandb.apis.public import ArtifactCollection, ArtifactFiles, RetryingClient,
42
31
  from wandb.data_types import WBValue
43
32
  from wandb.errors.term import termerror, termlog, termwarn
44
33
  from wandb.proto import wandb_internal_pb2 as pb
34
+ from wandb.proto.wandb_deprecated import Deprecated
35
+ from wandb.sdk import wandb_setup
45
36
  from wandb.sdk.artifacts._graphql_fragments import _gql_artifact_fragment
46
37
  from wandb.sdk.artifacts._validators import (
47
38
  ensure_logged,
@@ -70,7 +61,7 @@ from wandb.sdk.data_types._dtypes import TypeRegistry
70
61
  from wandb.sdk.internal.internal_api import Api as InternalApi
71
62
  from wandb.sdk.internal.thread_local_settings import _thread_local_api_settings
72
63
  from wandb.sdk.lib import filesystem, retry, runid, telemetry
73
- from wandb.sdk.lib.deprecate import Deprecated, deprecate
64
+ from wandb.sdk.lib.deprecate import deprecate
74
65
  from wandb.sdk.lib.hashutil import B64MD5, b64_to_hex_id, md5_file_b64
75
66
  from wandb.sdk.lib.paths import FilePathStr, LogicalPath, StrPath, URIStr
76
67
  from wandb.sdk.lib.runid import generate_id
@@ -200,6 +191,7 @@ class Artifact:
200
191
  self._created_at: str | None = None
201
192
  self._updated_at: str | None = None
202
193
  self._final: bool = False
194
+ self._history_step: int | None = None
203
195
 
204
196
  # Cache.
205
197
  artifact_instance_cache[self._client_id] = self
@@ -302,7 +294,7 @@ class Artifact:
302
294
  name: str,
303
295
  attrs: dict[str, Any],
304
296
  client: RetryingClient,
305
- aliases: Optional[list[str]] = None,
297
+ aliases: list[str] | None = None,
306
298
  ) -> Artifact:
307
299
  # Placeholder is required to skip validation.
308
300
  artifact = cls("placeholder", type="placeholder")
@@ -320,7 +312,7 @@ class Artifact:
320
312
  return artifact
321
313
 
322
314
  def _assign_attrs(
323
- self, attrs: dict[str, Any], aliases: Optional[list[str]] = None
315
+ self, attrs: dict[str, Any], aliases: list[str] | None = None
324
316
  ) -> None:
325
317
  """Update this Artifact's attributes using the server response."""
326
318
  self._id = attrs["id"]
@@ -420,6 +412,7 @@ class Artifact:
420
412
  self._file_count = attrs["fileCount"]
421
413
  self._created_at = attrs["createdAt"]
422
414
  self._updated_at = attrs["updatedAt"]
415
+ self._history_step = attrs.get("historyStep", None)
423
416
 
424
417
  @ensure_logged
425
418
  def new_draft(self) -> Artifact:
@@ -897,6 +890,26 @@ class Artifact:
897
890
  assert self._created_at is not None
898
891
  return self._updated_at or self._created_at
899
892
 
893
+ @property
894
+ @ensure_logged
895
+ def history_step(self) -> int | None:
896
+ """The nearest step at which history metrics were logged for the source run of the artifact.
897
+
898
+ Examples:
899
+ ```python
900
+ run = artifact.logged_by()
901
+ if run and (artifact.history_step is not None):
902
+ history = run.sample_history(
903
+ min_step=artifact.history_step,
904
+ max_step=artifact.history_step + 1,
905
+ keys=["my_metric"],
906
+ )
907
+ ```
908
+ """
909
+ if self._history_step is None:
910
+ return None
911
+ return max(0, self._history_step - 1)
912
+
900
913
  # State management.
901
914
 
902
915
  def finalize(self) -> None:
@@ -942,7 +955,12 @@ class Artifact:
942
955
  with telemetry.context() as tel:
943
956
  tel.feature.artifact_incremental = True
944
957
 
945
- if wandb.run is None:
958
+ singleton = wandb_setup._setup(start_service=False)
959
+
960
+ if run := singleton.most_recent_active_run:
961
+ # TODO: Deprecate and encourage explicit log_artifact().
962
+ run.log_artifact(self)
963
+ else:
946
964
  if settings is None:
947
965
  settings = wandb.Settings(silent="true")
948
966
  with wandb.init( # type: ignore
@@ -957,8 +975,6 @@ class Artifact:
957
975
  with telemetry.context(run=run) as tel:
958
976
  tel.feature.artifact_incremental = True
959
977
  run.log_artifact(self)
960
- else:
961
- wandb.run.log_artifact(self)
962
978
 
963
979
  def _set_save_handle(
964
980
  self,
@@ -1782,15 +1798,10 @@ class Artifact:
1782
1798
 
1783
1799
  Raises:
1784
1800
  ArtifactNotLoggedError: If the artifact is not logged.
1785
- RuntimeError: If the artifact is attempted to be downloaded in offline mode.
1786
1801
  """
1787
1802
  root = FilePathStr(str(root or self._default_root()))
1788
1803
  self._add_download_root(root)
1789
1804
 
1790
- # TODO: we need a better way to check for offline mode across the app, as this is an anti-pattern
1791
- if env.is_offline() or util._is_offline():
1792
- raise RuntimeError("Cannot download artifacts in offline mode.")
1793
-
1794
1805
  # TODO: download artifacts using core when implemented
1795
1806
  # if is_require_core():
1796
1807
  # return self._download_using_core(
@@ -1817,6 +1828,7 @@ class Artifact:
1817
1828
 
1818
1829
  from wandb.sdk.backend.backend import Backend
1819
1830
 
1831
+ # TODO: Create a special stream instead of relying on an existing run.
1820
1832
  if wandb.run is None:
1821
1833
  wl = wandb.setup()
1822
1834
 
@@ -1962,33 +1974,78 @@ class Artifact:
1962
1974
  retryable_exceptions=(requests.RequestException),
1963
1975
  )
1964
1976
  def _fetch_file_urls(self, cursor: str | None, per_page: int | None = 5000) -> Any:
1965
- query = gql(
1966
- """
1967
- query ArtifactFileURLs($id: ID!, $cursor: String, $perPage: Int) {
1968
- artifact(id: $id) {
1969
- files(after: $cursor, first: $perPage) {
1970
- pageInfo {
1971
- hasNextPage
1972
- endCursor
1977
+ if InternalApi()._check_server_feature_with_fallback(
1978
+ pb.ServerFeature.ARTIFACT_COLLECTION_MEMBERSHIP_FILES # type: ignore
1979
+ ):
1980
+ query = gql(
1981
+ """
1982
+ query ArtifactCollectionMembershipFileURLs($entityName: String!, $projectName: String!, \
1983
+ $artifactName: String!, $artifactVersionIndex: String!, $cursor: String, $perPage: Int) {
1984
+ project(name: $projectName, entityName: $entityName) {
1985
+ artifactCollection(name: $artifactName) {
1986
+ artifactMembership(aliasName: $artifactVersionIndex) {
1987
+ files(after: $cursor, first: $perPage) {
1988
+ pageInfo {
1989
+ hasNextPage
1990
+ endCursor
1991
+ }
1992
+ edges {
1993
+ node {
1994
+ name
1995
+ directUrl
1996
+ }
1997
+ }
1998
+ }
1999
+ }
1973
2000
  }
1974
- edges {
1975
- node {
1976
- name
1977
- directUrl
2001
+ }
2002
+ }
2003
+ """
2004
+ )
2005
+ assert self._client is not None
2006
+ response = self._client.execute(
2007
+ query,
2008
+ variable_values={
2009
+ "entityName": self.entity,
2010
+ "projectName": self.project,
2011
+ "artifactName": self.name.split(":")[0],
2012
+ "artifactVersionIndex": self.version,
2013
+ "cursor": cursor,
2014
+ "perPage": per_page,
2015
+ },
2016
+ timeout=60,
2017
+ )
2018
+ return response["project"]["artifactCollection"]["artifactMembership"][
2019
+ "files"
2020
+ ]
2021
+ else:
2022
+ query = gql(
2023
+ """
2024
+ query ArtifactFileURLs($id: ID!, $cursor: String, $perPage: Int) {
2025
+ artifact(id: $id) {
2026
+ files(after: $cursor, first: $perPage) {
2027
+ pageInfo {
2028
+ hasNextPage
2029
+ endCursor
2030
+ }
2031
+ edges {
2032
+ node {
2033
+ name
2034
+ directUrl
2035
+ }
1978
2036
  }
1979
2037
  }
1980
2038
  }
1981
2039
  }
1982
- }
1983
- """
1984
- )
1985
- assert self._client is not None
1986
- response = self._client.execute(
1987
- query,
1988
- variable_values={"id": self.id, "cursor": cursor, "perPage": per_page},
1989
- timeout=60,
1990
- )
1991
- return response["artifact"]["files"]
2040
+ """
2041
+ )
2042
+ assert self._client is not None
2043
+ response = self._client.execute(
2044
+ query,
2045
+ variable_values={"id": self.id, "cursor": cursor, "perPage": per_page},
2046
+ timeout=60,
2047
+ )
2048
+ return response["artifact"]["files"]
1992
2049
 
1993
2050
  @ensure_logged
1994
2051
  def checkout(self, root: str | None = None) -> str:
@@ -2193,16 +2250,20 @@ class Artifact:
2193
2250
  Raises:
2194
2251
  ArtifactNotLoggedError: If the artifact is not logged.
2195
2252
  """
2196
- if wandb.run is None:
2197
- with wandb.init( # type: ignore
2253
+ singleton = wandb_setup._setup(start_service=False)
2254
+
2255
+ if run := singleton.most_recent_active_run:
2256
+ # TODO: Deprecate and encourage explicit link_artifact().
2257
+ run.link_artifact(self, target_path, aliases)
2258
+
2259
+ else:
2260
+ with wandb.init(
2198
2261
  entity=self._source_entity,
2199
2262
  project=self._source_project,
2200
2263
  job_type="auto",
2201
2264
  settings=wandb.Settings(silent="true"),
2202
2265
  ) as run:
2203
2266
  run.link_artifact(self, target_path, aliases)
2204
- else:
2205
- wandb.run.link_artifact(self, target_path, aliases)
2206
2267
 
2207
2268
  @ensure_logged
2208
2269
  def unlink(self) -> None:
@@ -9,8 +9,9 @@ from pathlib import Path
9
9
  from typing import TYPE_CHECKING
10
10
  from urllib.parse import urlparse
11
11
 
12
+ from wandb.proto.wandb_deprecated import Deprecated
12
13
  from wandb.sdk.lib import filesystem
13
- from wandb.sdk.lib.deprecate import Deprecated, deprecate
14
+ from wandb.sdk.lib.deprecate import deprecate
14
15
  from wandb.sdk.lib.hashutil import (
15
16
  B64MD5,
16
17
  ETag,