wandb 0.20.2rc20250616__py3-none-win_amd64.whl → 0.21.1__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 (140) hide show
  1. wandb/__init__.py +16 -14
  2. wandb/__init__.pyi +450 -472
  3. wandb/agents/pyagent.py +41 -12
  4. wandb/analytics/sentry.py +7 -2
  5. wandb/apis/importers/mlflow.py +1 -1
  6. wandb/apis/internal.py +3 -0
  7. wandb/apis/paginator.py +17 -4
  8. wandb/apis/public/__init__.py +1 -1
  9. wandb/apis/public/api.py +606 -359
  10. wandb/apis/public/artifacts.py +214 -16
  11. wandb/apis/public/automations.py +19 -3
  12. wandb/apis/public/files.py +177 -38
  13. wandb/apis/public/history.py +67 -15
  14. wandb/apis/public/integrations.py +25 -2
  15. wandb/apis/public/jobs.py +90 -2
  16. wandb/apis/public/projects.py +161 -69
  17. wandb/apis/public/query_generator.py +11 -1
  18. wandb/apis/public/registries/registries_search.py +7 -15
  19. wandb/apis/public/reports.py +147 -13
  20. wandb/apis/public/runs.py +315 -128
  21. wandb/apis/public/sweeps.py +222 -22
  22. wandb/apis/public/teams.py +41 -4
  23. wandb/apis/public/users.py +45 -4
  24. wandb/automations/__init__.py +10 -10
  25. wandb/automations/_filters/run_metrics.py +0 -2
  26. wandb/automations/_utils.py +0 -2
  27. wandb/automations/actions.py +0 -2
  28. wandb/automations/automations.py +0 -2
  29. wandb/automations/events.py +0 -2
  30. wandb/beta/workflows.py +66 -30
  31. wandb/bin/gpu_stats.exe +0 -0
  32. wandb/bin/wandb-core +0 -0
  33. wandb/cli/cli.py +80 -1
  34. wandb/env.py +8 -0
  35. wandb/errors/errors.py +4 -1
  36. wandb/integration/catboost/catboost.py +6 -2
  37. wandb/integration/kfp/kfp_patch.py +3 -1
  38. wandb/integration/lightning/fabric/logger.py +3 -4
  39. wandb/integration/metaflow/__init__.py +6 -0
  40. wandb/integration/metaflow/data_pandas.py +74 -0
  41. wandb/integration/metaflow/errors.py +13 -0
  42. wandb/integration/metaflow/metaflow.py +205 -190
  43. wandb/integration/openai/fine_tuning.py +1 -2
  44. wandb/integration/sb3/sb3.py +3 -3
  45. wandb/integration/ultralytics/callback.py +6 -2
  46. wandb/jupyter.py +5 -5
  47. wandb/plot/__init__.py +2 -0
  48. wandb/plot/bar.py +30 -29
  49. wandb/plot/confusion_matrix.py +75 -71
  50. wandb/plot/custom_chart.py +30 -7
  51. wandb/plot/histogram.py +26 -25
  52. wandb/plot/line.py +33 -32
  53. wandb/plot/line_series.py +100 -103
  54. wandb/plot/pr_curve.py +33 -32
  55. wandb/plot/roc_curve.py +38 -38
  56. wandb/plot/scatter.py +27 -27
  57. wandb/proto/v3/wandb_internal_pb2.py +366 -385
  58. wandb/proto/v3/wandb_settings_pb2.py +2 -2
  59. wandb/proto/v3/wandb_telemetry_pb2.py +4 -4
  60. wandb/proto/v4/wandb_internal_pb2.py +352 -356
  61. wandb/proto/v4/wandb_settings_pb2.py +2 -2
  62. wandb/proto/v4/wandb_telemetry_pb2.py +4 -4
  63. wandb/proto/v5/wandb_internal_pb2.py +352 -356
  64. wandb/proto/v5/wandb_settings_pb2.py +2 -2
  65. wandb/proto/v5/wandb_telemetry_pb2.py +4 -4
  66. wandb/proto/v6/wandb_internal_pb2.py +352 -356
  67. wandb/proto/v6/wandb_settings_pb2.py +2 -2
  68. wandb/proto/v6/wandb_telemetry_pb2.py +4 -4
  69. wandb/proto/wandb_deprecated.py +6 -0
  70. wandb/sdk/artifacts/_generated/__init__.py +12 -1
  71. wandb/sdk/artifacts/_generated/input_types.py +20 -2
  72. wandb/sdk/artifacts/_generated/link_artifact.py +21 -0
  73. wandb/sdk/artifacts/_generated/operations.py +9 -0
  74. wandb/sdk/artifacts/_internal_artifact.py +19 -8
  75. wandb/sdk/artifacts/_validators.py +48 -2
  76. wandb/sdk/artifacts/artifact.py +269 -96
  77. wandb/sdk/data_types/audio.py +38 -10
  78. wandb/sdk/data_types/base_types/media.py +15 -63
  79. wandb/sdk/data_types/base_types/wb_value.py +6 -6
  80. wandb/sdk/data_types/graph.py +48 -14
  81. wandb/sdk/data_types/helper_types/bounding_boxes_2d.py +1 -3
  82. wandb/sdk/data_types/helper_types/image_mask.py +1 -3
  83. wandb/sdk/data_types/histogram.py +34 -21
  84. wandb/sdk/data_types/html.py +35 -12
  85. wandb/sdk/data_types/image.py +104 -68
  86. wandb/sdk/data_types/molecule.py +32 -19
  87. wandb/sdk/data_types/object_3d.py +36 -17
  88. wandb/sdk/data_types/plotly.py +18 -5
  89. wandb/sdk/data_types/saved_model.py +7 -9
  90. wandb/sdk/data_types/table.py +99 -70
  91. wandb/sdk/data_types/trace_tree.py +12 -12
  92. wandb/sdk/data_types/video.py +53 -26
  93. wandb/sdk/integration_utils/auto_logging.py +2 -2
  94. wandb/sdk/interface/interface.py +8 -19
  95. wandb/sdk/interface/interface_shared.py +7 -16
  96. wandb/sdk/internal/datastore.py +18 -18
  97. wandb/sdk/internal/handler.py +3 -5
  98. wandb/sdk/internal/internal_api.py +60 -0
  99. wandb/sdk/internal/job_builder.py +6 -0
  100. wandb/sdk/internal/sender.py +23 -3
  101. wandb/sdk/internal/sender_config.py +9 -0
  102. wandb/sdk/launch/_project_spec.py +3 -3
  103. wandb/sdk/launch/agent/agent.py +11 -4
  104. wandb/sdk/launch/agent/job_status_tracker.py +3 -1
  105. wandb/sdk/launch/agent/run_queue_item_file_saver.py +2 -2
  106. wandb/sdk/launch/create_job.py +3 -1
  107. wandb/sdk/launch/inputs/internal.py +3 -4
  108. wandb/sdk/launch/inputs/schema.py +1 -0
  109. wandb/sdk/launch/runner/kubernetes_monitor.py +1 -0
  110. wandb/sdk/launch/runner/kubernetes_runner.py +328 -1
  111. wandb/sdk/launch/sweeps/scheduler.py +2 -3
  112. wandb/sdk/launch/utils.py +3 -3
  113. wandb/sdk/lib/asyncio_compat.py +3 -0
  114. wandb/sdk/lib/console_capture.py +66 -19
  115. wandb/sdk/lib/deprecate.py +1 -7
  116. wandb/sdk/lib/disabled.py +1 -1
  117. wandb/sdk/lib/hashutil.py +14 -1
  118. wandb/sdk/lib/module.py +7 -13
  119. wandb/sdk/lib/progress.py +0 -19
  120. wandb/sdk/lib/sock_client.py +0 -4
  121. wandb/sdk/wandb_init.py +67 -93
  122. wandb/sdk/wandb_login.py +18 -14
  123. wandb/sdk/wandb_metric.py +2 -0
  124. wandb/sdk/wandb_require.py +0 -1
  125. wandb/sdk/wandb_run.py +429 -527
  126. wandb/sdk/wandb_settings.py +364 -74
  127. wandb/sdk/wandb_setup.py +28 -28
  128. wandb/sdk/wandb_sweep.py +14 -13
  129. wandb/sdk/wandb_watch.py +4 -6
  130. wandb/sync/sync.py +10 -0
  131. wandb/util.py +57 -0
  132. wandb/wandb_run.py +1 -2
  133. {wandb-0.20.2rc20250616.dist-info → wandb-0.21.1.dist-info}/METADATA +1 -1
  134. {wandb-0.20.2rc20250616.dist-info → wandb-0.21.1.dist-info}/RECORD +137 -137
  135. wandb/sdk/wandb_metadata.py +0 -623
  136. wandb/vendor/pynvml/__init__.py +0 -0
  137. wandb/vendor/pynvml/pynvml.py +0 -4779
  138. {wandb-0.20.2rc20250616.dist-info → wandb-0.21.1.dist-info}/WHEEL +0 -0
  139. {wandb-0.20.2rc20250616.dist-info → wandb-0.21.1.dist-info}/entry_points.txt +0 -0
  140. {wandb-0.20.2rc20250616.dist-info → wandb-0.21.1.dist-info}/licenses/LICENSE +0 -0
@@ -34,6 +34,7 @@ from wandb.apis.public import ArtifactCollection, ArtifactFiles, Run
34
34
  from wandb.apis.public.utils import gql_compat
35
35
  from wandb.data_types import WBValue
36
36
  from wandb.errors import CommError
37
+ from wandb.errors.errors import UnsupportedError
37
38
  from wandb.errors.term import termerror, termlog, termwarn
38
39
  from wandb.proto import wandb_internal_pb2 as pb
39
40
  from wandb.proto.wandb_deprecated import Deprecated
@@ -61,20 +62,25 @@ from ._generated import (
61
62
  ADD_ALIASES_GQL,
62
63
  DELETE_ALIASES_GQL,
63
64
  FETCH_LINKED_ARTIFACTS_GQL,
65
+ LINK_ARTIFACT_GQL,
64
66
  UPDATE_ARTIFACT_GQL,
65
67
  ArtifactAliasInput,
66
68
  ArtifactCollectionAliasInput,
67
69
  FetchLinkedArtifacts,
70
+ LinkArtifact,
71
+ LinkArtifactInput,
68
72
  TagInput,
69
73
  UpdateArtifact,
70
74
  )
71
75
  from ._graphql_fragments import _gql_artifact_fragment, omit_artifact_fields
72
76
  from ._validators import (
73
77
  LINKED_ARTIFACT_COLLECTION_TYPE,
78
+ ArtifactPath,
74
79
  _LinkArtifactFields,
75
80
  ensure_logged,
76
81
  ensure_not_finalized,
77
82
  is_artifact_registry_project,
83
+ remove_registry_prefix,
78
84
  validate_aliases,
79
85
  validate_artifact_name,
80
86
  validate_artifact_type,
@@ -126,25 +132,26 @@ class Artifact:
126
132
 
127
133
  Construct an empty W&B Artifact. Populate an artifacts contents with methods that
128
134
  begin with `add`. Once the artifact has all the desired files, you can call
129
- `wandb.log_artifact()` to log it.
135
+ `run.log_artifact()` to log it.
130
136
 
131
137
  Args:
132
- name: A human-readable name for the artifact. Use the name to identify
138
+ name (str): A human-readable name for the artifact. Use the name to identify
133
139
  a specific artifact in the W&B App UI or programmatically. You can
134
140
  interactively reference an artifact with the `use_artifact` Public API.
135
141
  A name can contain letters, numbers, underscores, hyphens, and dots.
136
142
  The name must be unique across a project.
137
- type: The artifact's type. Use the type of an artifact to both organize
143
+ type (str): The artifact's type. Use the type of an artifact to both organize
138
144
  and differentiate artifacts. You can use any string that contains letters,
139
145
  numbers, underscores, hyphens, and dots. Common types include `dataset` or `model`.
140
- Note: Some types are reserved for internal use and cannot be set by users.
141
- Such types include `job` and types that start with `wandb-`.
142
- description: A description of the artifact. For Model or Dataset Artifacts,
146
+ Include `model` within your type string if you want to link the artifact
147
+ to the W&B Model Registry. Note that some types reserved for internal use
148
+ and cannot be set by users. Such types include `job` and types that start with `wandb-`.
149
+ description (str | None) = None: A description of the artifact. For Model or Dataset Artifacts,
143
150
  add documentation for your standardized team model or dataset card. View
144
151
  an artifact's description programmatically with the `Artifact.description`
145
152
  attribute or programmatically with the W&B App UI. W&B renders the
146
153
  description as markdown in the W&B App.
147
- metadata: Additional information about an artifact. Specify metadata as a
154
+ metadata (dict[str, Any] | None) = None: Additional information about an artifact. Specify metadata as a
148
155
  dictionary of key-value pairs. You can specify no more than 100 total keys.
149
156
  incremental: Use `Artifact.new_draft()` method instead to modify an
150
157
  existing artifact.
@@ -277,6 +284,87 @@ class Artifact:
277
284
  name = "{}:v{}".format(src_collection["name"], attrs["versionIndex"])
278
285
  return cls._from_attrs(entity_name, project_name, name, attrs, client)
279
286
 
287
+ @classmethod
288
+ def _membership_from_name(
289
+ cls,
290
+ *,
291
+ entity: str,
292
+ project: str,
293
+ name: str,
294
+ client: RetryingClient,
295
+ ) -> Artifact:
296
+ if not InternalApi()._server_supports(
297
+ pb.ServerFeature.PROJECT_ARTIFACT_COLLECTION_MEMBERSHIP
298
+ ):
299
+ raise UnsupportedError(
300
+ "querying for the artifact collection membership is not supported "
301
+ "by this version of wandb server. Consider updating to the latest version."
302
+ )
303
+
304
+ query = gql(
305
+ f"""
306
+ query ArtifactByName($entityName: String!, $projectName: String!, $name: String!) {{
307
+ project(name: $projectName, entityName: $entityName) {{
308
+ artifactCollectionMembership(name: $name) {{
309
+ id
310
+ artifactCollection {{
311
+ id
312
+ name
313
+ project {{
314
+ id
315
+ entityName
316
+ name
317
+ }}
318
+ }}
319
+ artifact {{
320
+ ...ArtifactFragment
321
+ }}
322
+ }}
323
+ }}
324
+ }}
325
+ {_gql_artifact_fragment()}
326
+ """
327
+ )
328
+
329
+ query_variable_values: dict[str, Any] = {
330
+ "entityName": entity,
331
+ "projectName": project,
332
+ "name": name,
333
+ }
334
+ response = client.execute(
335
+ query,
336
+ variable_values=query_variable_values,
337
+ )
338
+ if not (project_attrs := response.get("project")):
339
+ raise ValueError(f"project {project!r} not found under entity {entity!r}")
340
+ if not (acm_attrs := project_attrs.get("artifactCollectionMembership")):
341
+ entity_project = f"{entity}/{project}"
342
+ raise ValueError(
343
+ f"artifact membership {name!r} not found in {entity_project!r}"
344
+ )
345
+ if not (ac_attrs := acm_attrs.get("artifactCollection")):
346
+ raise ValueError("artifact collection not found")
347
+ if not (
348
+ (ac_name := ac_attrs.get("name"))
349
+ and (ac_project_attrs := ac_attrs.get("project"))
350
+ ):
351
+ raise ValueError("artifact collection project not found")
352
+ ac_project = ac_project_attrs.get("name")
353
+ ac_entity = ac_project_attrs.get("entityName")
354
+ if is_artifact_registry_project(ac_project) and project == "model-registry":
355
+ wandb.termwarn(
356
+ "This model registry has been migrated and will be discontinued. "
357
+ f"Your request was redirected to the corresponding artifact `{ac_name}` in the new registry. "
358
+ f"Please update your paths to point to the migrated registry directly, `{ac_project}/{ac_name}`."
359
+ )
360
+ entity = ac_entity
361
+ project = ac_project
362
+ if not (attrs := acm_attrs.get("artifact")):
363
+ entity_project = f"{entity}/{project}"
364
+ raise ValueError(f"artifact {name!r} not found in {entity_project!r}")
365
+
366
+ return cls._from_attrs(entity, project, name, attrs, client)
367
+
280
368
  @classmethod
281
369
  def _from_name(
282
370
  cls,
@@ -287,14 +375,31 @@ class Artifact:
287
375
  client: RetryingClient,
288
376
  enable_tracking: bool = False,
289
377
  ) -> Artifact:
378
+ if InternalApi()._server_supports(
379
+ pb.ServerFeature.PROJECT_ARTIFACT_COLLECTION_MEMBERSHIP
380
+ ):
381
+ return cls._membership_from_name(
382
+ entity=entity,
383
+ project=project,
384
+ name=name,
385
+ client=client,
386
+ )
387
+
388
+ query_variable_values: dict[str, Any] = {
389
+ "entityName": entity,
390
+ "projectName": project,
391
+ "name": name,
392
+ }
393
+ query_vars = ["$entityName: String!", "$projectName: String!", "$name: String!"]
394
+ query_args = ["name: $name"]
395
+
290
396
  server_supports_enabling_artifact_usage_tracking = (
291
397
  InternalApi().server_project_type_introspection()
292
398
  )
293
- query_vars = ["$entityName: String!", "$projectName: String!", "$name: String!"]
294
- query_args = ["name: $name"]
295
399
  if server_supports_enabling_artifact_usage_tracking:
296
400
  query_vars.append("$enableTracking: Boolean")
297
401
  query_args.append("enableTracking: $enableTracking")
402
+ query_variable_values["enableTracking"] = enable_tracking
298
403
 
299
404
  vars_str = ", ".join(query_vars)
300
405
  args_str = ", ".join(query_args)
@@ -311,14 +416,6 @@ class Artifact:
311
416
  {_gql_artifact_fragment()}
312
417
  """
313
418
  )
314
- query_variable_values: dict[str, Any] = {
315
- "entityName": entity,
316
- "projectName": project,
317
- "name": name,
318
- }
319
- if server_supports_enabling_artifact_usage_tracking:
320
- query_variable_values["enableTracking"] = enable_tracking
321
-
322
419
  response = client.execute(
323
420
  query,
324
421
  variable_values=query_variable_values,
@@ -329,6 +426,7 @@ class Artifact:
329
426
  attrs = project_attrs.get("artifact")
330
427
  if not attrs:
331
428
  raise ValueError(f"artifact '{name}' not found in '{entity}/{project}'")
429
+
332
430
  return cls._from_attrs(entity, project, name, attrs, client)
333
431
 
334
432
  @classmethod
@@ -743,7 +841,7 @@ class Artifact:
743
841
  )
744
842
  return urljoin(
745
843
  base_url,
746
- f"orgs/{org.display_name}/registry/{self._type}?selectionPath={selection_path}&view=membership&version={self._version}",
844
+ f"orgs/{org.display_name}/registry/{remove_registry_prefix(self.project)}?selectionPath={selection_path}&view=membership&version={self.version}",
747
845
  )
748
846
 
749
847
  def _construct_model_registry_url(self, base_url: str) -> str:
@@ -828,7 +926,8 @@ class Artifact:
828
926
  TTL and there is no custom policy set on an artifact.
829
927
 
830
928
  Raises:
831
- ArtifactNotLoggedError: Unable to fetch inherited TTL if the artifact has not been logged or saved
929
+ ArtifactNotLoggedError: Unable to fetch inherited TTL if the
930
+ artifact has not been logged or saved.
832
931
  """
833
932
  if self._ttl_is_inherited and (self.is_draft() or self._ttl_changed):
834
933
  raise ArtifactNotLoggedError(f"{type(self).__name__}.ttl", self)
@@ -880,7 +979,9 @@ class Artifact:
880
979
  @property
881
980
  @ensure_logged
882
981
  def aliases(self) -> list[str]:
883
- """List of one or more semantically-friendly references or identifying "nicknames" assigned to an artifact version.
982
+ """List of one or more semantically-friendly references or
983
+
984
+ identifying "nicknames" assigned to an artifact version.
884
985
 
885
986
  Aliases are mutable references that you can programmatically reference.
886
987
  Change an artifact's alias with the W&B App UI or programmatically.
@@ -916,6 +1017,10 @@ class Artifact:
916
1017
 
917
1018
  @property
918
1019
  def distributed_id(self) -> str | None:
1020
+ """The distributed ID of the artifact.
1021
+
1022
+ <!-- lazydoc-ignore: internal -->
1023
+ """
919
1024
  return self._distributed_id
920
1025
 
921
1026
  @distributed_id.setter
@@ -924,6 +1029,10 @@ class Artifact:
924
1029
 
925
1030
  @property
926
1031
  def incremental(self) -> bool:
1032
+ """Boolean flag indicating if the artifact is an incremental artifact.
1033
+
1034
+ <!-- lazydoc-ignore: internal -->
1035
+ """
927
1036
  return self._incremental
928
1037
 
929
1038
  @property
@@ -1043,15 +1152,15 @@ class Artifact:
1043
1152
  """The nearest step at which history metrics were logged for the source run of the artifact.
1044
1153
 
1045
1154
  Examples:
1046
- ```python
1047
- run = artifact.logged_by()
1048
- if run and (artifact.history_step is not None):
1049
- history = run.sample_history(
1050
- min_step=artifact.history_step,
1051
- max_step=artifact.history_step + 1,
1052
- keys=["my_metric"],
1053
- )
1054
- ```
1155
+ ```python
1156
+ run = artifact.logged_by()
1157
+ if run and (artifact.history_step is not None):
1158
+ history = run.sample_history(
1159
+ min_step=artifact.history_step,
1160
+ max_step=artifact.history_step + 1,
1161
+ keys=["my_metric"],
1162
+ )
1163
+ ```
1055
1164
  """
1056
1165
  if self._history_step is None:
1057
1166
  return None
@@ -1072,7 +1181,8 @@ class Artifact:
1072
1181
  def is_draft(self) -> bool:
1073
1182
  """Check if artifact is not saved.
1074
1183
 
1075
- Returns: Boolean. `False` if artifact is saved. `True` if artifact is not saved.
1184
+ Returns:
1185
+ Boolean. `False` if artifact is saved. `True` if artifact is not saved.
1076
1186
  """
1077
1187
  return self._state == ArtifactState.PENDING
1078
1188
 
@@ -1320,7 +1430,7 @@ class Artifact:
1320
1430
  name: The artifact relative name to get.
1321
1431
 
1322
1432
  Returns:
1323
- W&B object that can be logged with `wandb.log()` and visualized in the W&B UI.
1433
+ W&B object that can be logged with `run.log()` and visualized in the W&B UI.
1324
1434
 
1325
1435
  Raises:
1326
1436
  ArtifactNotLoggedError: If the artifact isn't logged or the run is offline.
@@ -1338,8 +1448,9 @@ class Artifact:
1338
1448
  The added manifest entry
1339
1449
 
1340
1450
  Raises:
1341
- ArtifactFinalizedError: You cannot make changes to the current artifact
1342
- version because it is finalized. Log a new artifact version instead.
1451
+ ArtifactFinalizedError: You cannot make changes to the current
1452
+ artifact version because it is finalized. Log a new artifact
1453
+ version instead.
1343
1454
  """
1344
1455
  return self.add(item, name)
1345
1456
 
@@ -1356,12 +1467,13 @@ class Artifact:
1356
1467
  encoding: The encoding used to open the new file.
1357
1468
 
1358
1469
  Returns:
1359
- A new file object that can be written to. Upon closing, the file will be
1360
- automatically added to the artifact.
1470
+ A new file object that can be written to. Upon closing, the file
1471
+ is automatically added to the artifact.
1361
1472
 
1362
1473
  Raises:
1363
- ArtifactFinalizedError: You cannot make changes to the current artifact
1364
- version because it is finalized. Log a new artifact version instead.
1474
+ ArtifactFinalizedError: You cannot make changes to the current
1475
+ artifact version because it is finalized. Log a new artifact
1476
+ version instead.
1365
1477
  """
1366
1478
  overwrite: bool = "x" not in mode
1367
1479
 
@@ -1400,22 +1512,26 @@ class Artifact:
1400
1512
 
1401
1513
  Args:
1402
1514
  local_path: The path to the file being added.
1403
- name: The path within the artifact to use for the file being added. Defaults
1404
- to the basename of the file.
1515
+ name: The path within the artifact to use for the file being added.
1516
+ Defaults to the basename of the file.
1405
1517
  is_tmp: If true, then the file is renamed deterministically to avoid
1406
1518
  collisions.
1407
- skip_cache: If `True`, W&B will not copy files to the cache after uploading.
1408
- policy: By default, set to "mutable". If set to "mutable", create a temporary copy of the
1409
- file to prevent corruption during upload. If set to "immutable", disable
1410
- protection and rely on the user not to delete or change the file.
1519
+ skip_cache: If `True`, do not copy files to the cache
1520
+ after uploading.
1521
+ policy: By default, set to "mutable". If set to "mutable",
1522
+ create a temporary copy of the file to prevent corruption
1523
+ during upload. If set to "immutable", disable
1524
+ protection and rely on the user not to delete or change the
1525
+ file.
1411
1526
  overwrite: If `True`, overwrite the file if it already exists.
1412
1527
 
1413
1528
  Returns:
1414
1529
  The added manifest entry.
1415
1530
 
1416
1531
  Raises:
1417
- ArtifactFinalizedError: You cannot make changes to the current artifact
1418
- version because it is finalized. Log a new artifact version instead.
1532
+ ArtifactFinalizedError: You cannot make changes to the current
1533
+ artifact version because it is finalized. Log a new artifact
1534
+ version instead.
1419
1535
  ValueError: Policy must be "mutable" or "immutable"
1420
1536
  """
1421
1537
  if not os.path.isfile(local_path):
@@ -1452,21 +1568,23 @@ class Artifact:
1452
1568
 
1453
1569
  Args:
1454
1570
  local_path: The path of the local directory.
1455
- name: The subdirectory name within an artifact. The name you specify appears
1456
- in the W&B App UI nested by artifact's `type`.
1571
+ name: The subdirectory name within an artifact. The name you
1572
+ specify appears in the W&B App UI nested by artifact's `type`.
1457
1573
  Defaults to the root of the artifact.
1458
- skip_cache: If set to `True`, W&B will not copy/move files to the cache while uploading
1459
- policy: "mutable" | "immutable". By default, "mutable"
1460
- "mutable": Create a temporary copy of the file to prevent corruption during upload.
1461
- "immutable": Disable protection, rely on the user not to delete or change the file.
1574
+ skip_cache: If set to `True`, W&B will not copy/move files to
1575
+ the cache while uploading
1576
+ policy: By default, "mutable".
1577
+ - mutable: Create a temporary copy of the file to prevent corruption during upload.
1578
+ - immutable: Disable protection, rely on the user not to delete or change the file.
1462
1579
  merge: If `False` (default), throws ValueError if a file was already added in a previous add_dir call
1463
1580
  and its content has changed. If `True`, overwrites existing files with changed content.
1464
1581
  Always adds new files and never removes files. To replace an entire directory, pass a name when adding the directory
1465
1582
  using `add_dir(local_path, name=my_prefix)` and call `remove(my_prefix)` to remove the directory, then add it again.
1466
1583
 
1467
1584
  Raises:
1468
- ArtifactFinalizedError: You cannot make changes to the current artifact
1469
- version because it is finalized. Log a new artifact version instead.
1585
+ ArtifactFinalizedError: You cannot make changes to the current
1586
+ artifact version because it is finalized. Log a new artifact
1587
+ version instead.
1470
1588
  ValueError: Policy must be "mutable" or "immutable"
1471
1589
  """
1472
1590
  if not os.path.isdir(local_path):
@@ -1525,13 +1643,14 @@ class Artifact:
1525
1643
 
1526
1644
  - http(s): The size and digest of the file will be inferred by the
1527
1645
  `Content-Length` and the `ETag` response headers returned by the server.
1528
- - s3: The checksum and size are pulled from the object metadata. If bucket
1529
- versioning is enabled, then the version ID is also tracked.
1646
+ - s3: The checksum and size are pulled from the object metadata.
1647
+ If bucket versioning is enabled, then the version ID is also tracked.
1530
1648
  - gs: The checksum and size are pulled from the object metadata. If bucket
1531
1649
  versioning is enabled, then the version ID is also tracked.
1532
- - https, domain matching `*.blob.core.windows.net` (Azure): The checksum and size
1533
- are be pulled from the blob metadata. If storage account versioning is
1534
- enabled, then the version ID is also tracked.
1650
+ - https, domain matching `*.blob.core.windows.net`
1651
+ - Azure: The checksum and size are be pulled from the blob metadata.
1652
+ If storage account versioning is enabled, then the version ID is
1653
+ also tracked.
1535
1654
  - file: The checksum and size are pulled from the file system. This scheme
1536
1655
  is useful if you have an NFS share or other externally mounted volume
1537
1656
  containing files you wish to track but not necessarily upload.
@@ -1552,16 +1671,18 @@ class Artifact:
1552
1671
  setting `checksum=False` when adding reference objects, in which case
1553
1672
  a new version will only be created if the reference URI changes.
1554
1673
  max_objects: The maximum number of objects to consider when adding a
1555
- reference that points to directory or bucket store prefix. By default,
1556
- the maximum number of objects allowed for Amazon S3,
1557
- GCS, Azure, and local files is 10,000,000. Other URI schemas do not have a maximum.
1674
+ reference that points to directory or bucket store prefix.
1675
+ By default, the maximum number of objects allowed for Amazon S3,
1676
+ GCS, Azure, and local files is 10,000,000. Other URI schemas
1677
+ do not have a maximum.
1558
1678
 
1559
1679
  Returns:
1560
1680
  The added manifest entries.
1561
1681
 
1562
1682
  Raises:
1563
- ArtifactFinalizedError: You cannot make changes to the current artifact
1564
- version because it is finalized. Log a new artifact version instead.
1683
+ ArtifactFinalizedError: You cannot make changes to the current
1684
+ artifact version because it is finalized. Log a new artifact
1685
+ version instead.
1565
1686
  """
1566
1687
  if name is not None:
1567
1688
  name = LogicalPath(name)
@@ -1598,17 +1719,19 @@ class Artifact:
1598
1719
 
1599
1720
  Args:
1600
1721
  obj: The object to add. Currently support one of Bokeh, JoinedTable,
1601
- PartitionedTable, Table, Classes, ImageMask, BoundingBoxes2D, Audio,
1602
- Image, Video, Html, Object3D
1722
+ PartitionedTable, Table, Classes, ImageMask, BoundingBoxes2D,
1723
+ Audio, Image, Video, Html, Object3D
1603
1724
  name: The path within the artifact to add the object.
1604
- overwrite: If True, overwrite existing objects with the same file path (if applicable).
1725
+ overwrite: If True, overwrite existing objects with the same file
1726
+ path if applicable.
1605
1727
 
1606
1728
  Returns:
1607
1729
  The added manifest entry
1608
1730
 
1609
1731
  Raises:
1610
- ArtifactFinalizedError: You cannot make changes to the current artifact
1611
- version because it is finalized. Log a new artifact version instead.
1732
+ ArtifactFinalizedError: You cannot make changes to the current
1733
+ artifact version because it is finalized. Log a new artifact
1734
+ version instead.
1612
1735
  """
1613
1736
  name = LogicalPath(name)
1614
1737
 
@@ -1723,13 +1846,14 @@ class Artifact:
1723
1846
  """Remove an item from the artifact.
1724
1847
 
1725
1848
  Args:
1726
- item: The item to remove. Can be a specific manifest entry or the name of an
1727
- artifact-relative path. If the item matches a directory all items in
1728
- that directory will be removed.
1849
+ item: The item to remove. Can be a specific manifest entry
1850
+ or the name of an artifact-relative path. If the item
1851
+ matches a directory all items in that directory will be removed.
1729
1852
 
1730
1853
  Raises:
1731
- ArtifactFinalizedError: You cannot make changes to the current artifact
1732
- version because it is finalized. Log a new artifact version instead.
1854
+ ArtifactFinalizedError: You cannot make changes to the current
1855
+ artifact version because it is finalized. Log a new artifact
1856
+ version instead.
1733
1857
  FileNotFoundError: If the item isn't found in the artifact.
1734
1858
  """
1735
1859
  if isinstance(item, ArtifactManifestEntry):
@@ -1785,10 +1909,12 @@ class Artifact:
1785
1909
  name: The artifact relative name to retrieve.
1786
1910
 
1787
1911
  Returns:
1788
- W&B object that can be logged with `wandb.log()` and visualized in the W&B UI.
1912
+ W&B object that can be logged with `run.log()` and
1913
+ visualized in the W&B UI.
1789
1914
 
1790
1915
  Raises:
1791
- ArtifactNotLoggedError: if the artifact isn't logged or the run is offline
1916
+ ArtifactNotLoggedError: if the artifact isn't logged or the
1917
+ run is offline.
1792
1918
  """
1793
1919
  entry, wb_class = self._get_obj_entry(name)
1794
1920
  if entry is None or wb_class is None:
@@ -2224,7 +2350,7 @@ class Artifact:
2224
2350
 
2225
2351
  Args:
2226
2352
  root: The root directory to store the file. Defaults to
2227
- './artifacts/self.name/'.
2353
+ `./artifacts/self.name/`.
2228
2354
 
2229
2355
  Returns:
2230
2356
  The full path of the downloaded file.
@@ -2290,16 +2416,16 @@ class Artifact:
2290
2416
  def delete(self, delete_aliases: bool = False) -> None:
2291
2417
  """Delete an artifact and its files.
2292
2418
 
2293
- If called on a linked artifact (i.e. a member of a portfolio collection): only the link is deleted, and the
2419
+ If called on a linked artifact, only the link is deleted, and the
2294
2420
  source artifact is unaffected.
2295
2421
 
2296
2422
  Use `artifact.unlink()` instead of `artifact.delete()` to remove a link between a source artifact and a linked artifact.
2297
2423
 
2298
2424
  Args:
2299
- delete_aliases: If set to `True`, deletes all aliases associated with the artifact.
2300
- Otherwise, this raises an exception if the artifact has existing
2301
- aliases.
2302
- This parameter is ignored if the artifact is linked (i.e. a member of a portfolio collection).
2425
+ delete_aliases: If set to `True`, deletes all aliases associated
2426
+ with the artifact. Otherwise, this raises an exception if
2427
+ the artifact has existing aliases. This parameter is ignored
2428
+ if the artifact is linked (a member of a portfolio collection).
2303
2429
 
2304
2430
  Raises:
2305
2431
  ArtifactNotLoggedError: If the artifact is not logged.
@@ -2352,8 +2478,8 @@ class Artifact:
2352
2478
  portfolio inside a project, set `target_path` to the following
2353
2479
  schema `{"model-registry"}/{Registered Model Name}` or
2354
2480
  `{entity}/{"model-registry"}/{Registered Model Name}`.
2355
- aliases: A list of strings that uniquely identifies the artifact inside the
2356
- specified portfolio.
2481
+ aliases: A list of strings that uniquely identifies the artifact
2482
+ inside the specified portfolio.
2357
2483
 
2358
2484
  Raises:
2359
2485
  ArtifactNotLoggedError: If the artifact is not logged.
@@ -2361,33 +2487,80 @@ class Artifact:
2361
2487
  Returns:
2362
2488
  The linked artifact if linking was successful, otherwise None.
2363
2489
  """
2490
+ from wandb import Api
2491
+
2364
2492
  if self.is_link:
2365
2493
  wandb.termwarn(
2366
2494
  "Linking to a link artifact will result in directly linking to the source artifact of that link artifact."
2367
2495
  )
2368
2496
 
2369
- if run := wandb_setup.singleton().most_recent_active_run:
2370
- # TODO: Deprecate and encourage explicit link_artifact().
2371
- return run.link_artifact(self, target_path, aliases)
2497
+ if self._client is None:
2498
+ raise ValueError("Client not initialized for artifact mutations")
2499
+
2500
+ # Save the artifact first if necessary
2501
+ if self.is_draft():
2502
+ if not self._is_draft_save_started():
2503
+ self.save(project=self.source_project)
2504
+
2505
+ # Wait until the artifact is committed before trying to link it.
2506
+ self.wait()
2507
+
2508
+ api = Api(overrides={"entity": self.source_entity})
2509
+
2510
+ target = ArtifactPath.from_str(target_path).with_defaults(
2511
+ project=api.settings.get("project") or "uncategorized",
2512
+ )
2513
+
2514
+ # Parse the entity (first part of the path) appropriately,
2515
+ # depending on whether we're linking to a registry
2516
+ if target.project and is_artifact_registry_project(target.project):
2517
+ # In a Registry linking, the entity is used to fetch the organization of the artifact
2518
+ # therefore the source artifact's entity is passed to the backend
2519
+ organization = target.prefix or api.settings.get("organization") or ""
2372
2520
 
2521
+ target.prefix = InternalApi()._resolve_org_entity_name(
2522
+ self.source_entity, organization
2523
+ )
2373
2524
  else:
2374
- with wandb.init(
2375
- entity=self._source_entity,
2376
- project=self._source_project,
2377
- job_type="auto",
2378
- settings=wandb.Settings(silent="true"),
2379
- ) as run:
2380
- return run.link_artifact(self, target_path, aliases)
2525
+ target = target.with_defaults(prefix=self.source_entity)
2381
2526
 
2382
- return None
2527
+ # Prepare the validated GQL input, send it
2528
+ alias_inputs = [
2529
+ ArtifactAliasInput(artifact_collection_name=target.name, alias=a)
2530
+ for a in (aliases or [])
2531
+ ]
2532
+ gql_input = LinkArtifactInput(
2533
+ artifact_id=self.id,
2534
+ artifact_portfolio_name=target.name,
2535
+ entity_name=target.prefix,
2536
+ project_name=target.project,
2537
+ aliases=alias_inputs,
2538
+ )
2539
+ gql_vars = {"input": gql_input.model_dump(exclude_none=True)}
2540
+ gql_op = gql(LINK_ARTIFACT_GQL)
2541
+ data = self._client.execute(gql_op, variable_values=gql_vars)
2542
+
2543
+ result = LinkArtifact.model_validate(data).link_artifact
2544
+ if not (result and (version_idx := result.version_index) is not None):
2545
+ raise ValueError("Unable to parse linked artifact version from response")
2546
+
2547
+ # Fetch the linked artifact to return it
2548
+ linked_path = f"{target.to_str()}:v{version_idx}"
2549
+
2550
+ try:
2551
+ return api._artifact(linked_path)
2552
+ except Exception as e:
2553
+ wandb.termerror(f"Error fetching link artifact after linking: {e}")
2554
+ return None
2383
2555
 
2384
2556
  @ensure_logged
2385
2557
  def unlink(self) -> None:
2386
- """Unlink this artifact if it is currently a member of a portfolio (a promoted collection of artifacts).
2558
+ """Unlink this artifact if it is currently a member of a promoted collection of artifacts.
2387
2559
 
2388
2560
  Raises:
2389
2561
  ArtifactNotLoggedError: If the artifact is not logged.
2390
- ValueError: If the artifact is not linked, i.e. it is not a member of a portfolio collection.
2562
+ ValueError: If the artifact is not linked, in other words,
2563
+ it is not a member of a portfolio collection.
2391
2564
  """
2392
2565
  # Fail early if this isn't a linked artifact to begin with
2393
2566
  if not self.is_link: