wandb 0.20.2rc20250616__py3-none-win32.whl → 0.21.1__py3-none-win32.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- wandb/__init__.py +16 -14
- wandb/__init__.pyi +450 -472
- wandb/agents/pyagent.py +41 -12
- wandb/analytics/sentry.py +7 -2
- wandb/apis/importers/mlflow.py +1 -1
- wandb/apis/internal.py +3 -0
- wandb/apis/paginator.py +17 -4
- wandb/apis/public/__init__.py +1 -1
- wandb/apis/public/api.py +606 -359
- wandb/apis/public/artifacts.py +214 -16
- wandb/apis/public/automations.py +19 -3
- wandb/apis/public/files.py +177 -38
- wandb/apis/public/history.py +67 -15
- wandb/apis/public/integrations.py +25 -2
- wandb/apis/public/jobs.py +90 -2
- wandb/apis/public/projects.py +161 -69
- wandb/apis/public/query_generator.py +11 -1
- wandb/apis/public/registries/registries_search.py +7 -15
- wandb/apis/public/reports.py +147 -13
- wandb/apis/public/runs.py +315 -128
- wandb/apis/public/sweeps.py +222 -22
- wandb/apis/public/teams.py +41 -4
- wandb/apis/public/users.py +45 -4
- wandb/automations/__init__.py +10 -10
- wandb/automations/_filters/run_metrics.py +0 -2
- wandb/automations/_utils.py +0 -2
- wandb/automations/actions.py +0 -2
- wandb/automations/automations.py +0 -2
- wandb/automations/events.py +0 -2
- wandb/beta/workflows.py +66 -30
- wandb/bin/gpu_stats.exe +0 -0
- wandb/bin/wandb-core +0 -0
- wandb/cli/cli.py +80 -1
- wandb/env.py +8 -0
- wandb/errors/errors.py +4 -1
- wandb/integration/catboost/catboost.py +6 -2
- wandb/integration/kfp/kfp_patch.py +3 -1
- wandb/integration/lightning/fabric/logger.py +3 -4
- wandb/integration/metaflow/__init__.py +6 -0
- wandb/integration/metaflow/data_pandas.py +74 -0
- wandb/integration/metaflow/errors.py +13 -0
- wandb/integration/metaflow/metaflow.py +205 -190
- wandb/integration/openai/fine_tuning.py +1 -2
- wandb/integration/sb3/sb3.py +3 -3
- wandb/integration/ultralytics/callback.py +6 -2
- wandb/jupyter.py +5 -5
- wandb/plot/__init__.py +2 -0
- wandb/plot/bar.py +30 -29
- wandb/plot/confusion_matrix.py +75 -71
- wandb/plot/custom_chart.py +30 -7
- wandb/plot/histogram.py +26 -25
- wandb/plot/line.py +33 -32
- wandb/plot/line_series.py +100 -103
- wandb/plot/pr_curve.py +33 -32
- wandb/plot/roc_curve.py +38 -38
- wandb/plot/scatter.py +27 -27
- wandb/proto/v3/wandb_internal_pb2.py +366 -385
- wandb/proto/v3/wandb_settings_pb2.py +2 -2
- wandb/proto/v3/wandb_telemetry_pb2.py +4 -4
- wandb/proto/v4/wandb_internal_pb2.py +352 -356
- wandb/proto/v4/wandb_settings_pb2.py +2 -2
- wandb/proto/v4/wandb_telemetry_pb2.py +4 -4
- wandb/proto/v5/wandb_internal_pb2.py +352 -356
- wandb/proto/v5/wandb_settings_pb2.py +2 -2
- wandb/proto/v5/wandb_telemetry_pb2.py +4 -4
- wandb/proto/v6/wandb_internal_pb2.py +352 -356
- wandb/proto/v6/wandb_settings_pb2.py +2 -2
- wandb/proto/v6/wandb_telemetry_pb2.py +4 -4
- wandb/proto/wandb_deprecated.py +6 -0
- wandb/sdk/artifacts/_generated/__init__.py +12 -1
- wandb/sdk/artifacts/_generated/input_types.py +20 -2
- wandb/sdk/artifacts/_generated/link_artifact.py +21 -0
- wandb/sdk/artifacts/_generated/operations.py +9 -0
- wandb/sdk/artifacts/_internal_artifact.py +19 -8
- wandb/sdk/artifacts/_validators.py +48 -2
- wandb/sdk/artifacts/artifact.py +269 -96
- wandb/sdk/data_types/audio.py +38 -10
- wandb/sdk/data_types/base_types/media.py +15 -63
- wandb/sdk/data_types/base_types/wb_value.py +6 -6
- wandb/sdk/data_types/graph.py +48 -14
- wandb/sdk/data_types/helper_types/bounding_boxes_2d.py +1 -3
- wandb/sdk/data_types/helper_types/image_mask.py +1 -3
- wandb/sdk/data_types/histogram.py +34 -21
- wandb/sdk/data_types/html.py +35 -12
- wandb/sdk/data_types/image.py +104 -68
- wandb/sdk/data_types/molecule.py +32 -19
- wandb/sdk/data_types/object_3d.py +36 -17
- wandb/sdk/data_types/plotly.py +18 -5
- wandb/sdk/data_types/saved_model.py +7 -9
- wandb/sdk/data_types/table.py +99 -70
- wandb/sdk/data_types/trace_tree.py +12 -12
- wandb/sdk/data_types/video.py +53 -26
- wandb/sdk/integration_utils/auto_logging.py +2 -2
- wandb/sdk/interface/interface.py +8 -19
- wandb/sdk/interface/interface_shared.py +7 -16
- wandb/sdk/internal/datastore.py +18 -18
- wandb/sdk/internal/handler.py +3 -5
- wandb/sdk/internal/internal_api.py +60 -0
- wandb/sdk/internal/job_builder.py +6 -0
- wandb/sdk/internal/sender.py +23 -3
- wandb/sdk/internal/sender_config.py +9 -0
- wandb/sdk/launch/_project_spec.py +3 -3
- wandb/sdk/launch/agent/agent.py +11 -4
- wandb/sdk/launch/agent/job_status_tracker.py +3 -1
- wandb/sdk/launch/agent/run_queue_item_file_saver.py +2 -2
- wandb/sdk/launch/create_job.py +3 -1
- wandb/sdk/launch/inputs/internal.py +3 -4
- wandb/sdk/launch/inputs/schema.py +1 -0
- wandb/sdk/launch/runner/kubernetes_monitor.py +1 -0
- wandb/sdk/launch/runner/kubernetes_runner.py +328 -1
- wandb/sdk/launch/sweeps/scheduler.py +2 -3
- wandb/sdk/launch/utils.py +3 -3
- wandb/sdk/lib/asyncio_compat.py +3 -0
- wandb/sdk/lib/console_capture.py +66 -19
- wandb/sdk/lib/deprecate.py +1 -7
- wandb/sdk/lib/disabled.py +1 -1
- wandb/sdk/lib/hashutil.py +14 -1
- wandb/sdk/lib/module.py +7 -13
- wandb/sdk/lib/progress.py +0 -19
- wandb/sdk/lib/sock_client.py +0 -4
- wandb/sdk/wandb_init.py +67 -93
- wandb/sdk/wandb_login.py +18 -14
- wandb/sdk/wandb_metric.py +2 -0
- wandb/sdk/wandb_require.py +0 -1
- wandb/sdk/wandb_run.py +429 -527
- wandb/sdk/wandb_settings.py +364 -74
- wandb/sdk/wandb_setup.py +28 -28
- wandb/sdk/wandb_sweep.py +14 -13
- wandb/sdk/wandb_watch.py +4 -6
- wandb/sync/sync.py +10 -0
- wandb/util.py +57 -0
- wandb/wandb_run.py +1 -2
- {wandb-0.20.2rc20250616.dist-info → wandb-0.21.1.dist-info}/METADATA +1 -1
- {wandb-0.20.2rc20250616.dist-info → wandb-0.21.1.dist-info}/RECORD +137 -137
- wandb/sdk/wandb_metadata.py +0 -623
- wandb/vendor/pynvml/__init__.py +0 -0
- wandb/vendor/pynvml/pynvml.py +0 -4779
- {wandb-0.20.2rc20250616.dist-info → wandb-0.21.1.dist-info}/WHEEL +0 -0
- {wandb-0.20.2rc20250616.dist-info → wandb-0.21.1.dist-info}/entry_points.txt +0 -0
- {wandb-0.20.2rc20250616.dist-info → wandb-0.21.1.dist-info}/licenses/LICENSE +0 -0
wandb/apis/public/runs.py
CHANGED
@@ -1,20 +1,45 @@
|
|
1
|
-
"""Public API
|
1
|
+
"""W&B Public API for Runs.
|
2
|
+
|
3
|
+
This module provides classes for interacting with W&B runs and their associated
|
4
|
+
data.
|
5
|
+
|
6
|
+
Example:
|
7
|
+
```python
|
8
|
+
from wandb.apis.public import Api
|
9
|
+
|
10
|
+
# Get runs matching filters
|
11
|
+
runs = Api().runs(
|
12
|
+
path="entity/project", filters={"state": "finished", "config.batch_size": 32}
|
13
|
+
)
|
14
|
+
|
15
|
+
# Access run data
|
16
|
+
for run in runs:
|
17
|
+
print(f"Run: {run.name}")
|
18
|
+
print(f"Config: {run.config}")
|
19
|
+
print(f"Metrics: {run.summary}")
|
20
|
+
|
21
|
+
# Get history with pandas
|
22
|
+
history_df = run.history(keys=["loss", "accuracy"], pandas=True)
|
23
|
+
|
24
|
+
# Work with artifacts
|
25
|
+
for artifact in run.logged_artifacts():
|
26
|
+
print(f"Artifact: {artifact.name}")
|
27
|
+
```
|
28
|
+
|
29
|
+
Note:
|
30
|
+
This module is part of the W&B Public API and provides read/write access
|
31
|
+
to run data. For logging new runs, use the wandb.init() function from
|
32
|
+
the main wandb package.
|
33
|
+
"""
|
34
|
+
|
35
|
+
from __future__ import annotations
|
2
36
|
|
3
37
|
import json
|
4
38
|
import os
|
5
39
|
import tempfile
|
6
40
|
import time
|
7
41
|
import urllib
|
8
|
-
from typing import
|
9
|
-
TYPE_CHECKING,
|
10
|
-
Any,
|
11
|
-
Collection,
|
12
|
-
Dict,
|
13
|
-
List,
|
14
|
-
Literal,
|
15
|
-
Mapping,
|
16
|
-
Optional,
|
17
|
-
)
|
42
|
+
from typing import TYPE_CHECKING, Any, Collection, Literal, Mapping
|
18
43
|
|
19
44
|
from wandb_gql import gql
|
20
45
|
|
@@ -85,22 +110,101 @@ def _server_provides_internal_id_for_project(client) -> bool:
|
|
85
110
|
]
|
86
111
|
|
87
112
|
|
88
|
-
|
89
|
-
|
113
|
+
@normalize_exceptions
|
114
|
+
def _convert_to_dict(value: Any) -> dict[str, Any]:
|
115
|
+
"""Converts a value to a dictionary.
|
90
116
|
|
91
|
-
|
117
|
+
If the value is already a dictionary, the value is returned unchanged.
|
118
|
+
If the value is a string, bytes, or bytearray, it is parsed as JSON.
|
119
|
+
For any other type, a TypeError is raised.
|
120
|
+
"""
|
121
|
+
if value is None:
|
122
|
+
return {}
|
123
|
+
|
124
|
+
if isinstance(value, dict):
|
125
|
+
return value
|
126
|
+
|
127
|
+
if isinstance(value, (str, bytes, bytearray)):
|
128
|
+
try:
|
129
|
+
return json.loads(value)
|
130
|
+
except json.decoder.JSONDecodeError:
|
131
|
+
# ignore invalid utf-8 or control characters
|
132
|
+
return json.loads(value, strict=False)
|
133
|
+
|
134
|
+
raise TypeError(f"Unable to convert {value} to a dict")
|
135
|
+
|
136
|
+
|
137
|
+
class Runs(SizedPaginator["Run"]):
|
138
|
+
"""A lazy iterator of `Run` objects associated with a project and optional filter.
|
139
|
+
|
140
|
+
Runs are retrieved in pages from the W&B server as needed.
|
141
|
+
|
142
|
+
This is generally used indirectly using the `Api.runs` namespace.
|
143
|
+
|
144
|
+
Args:
|
145
|
+
client: (`wandb.apis.public.RetryingClient`) The API client to use
|
146
|
+
for requests.
|
147
|
+
entity: (str) The entity (username or team) that owns the project.
|
148
|
+
project: (str) The name of the project to fetch runs from.
|
149
|
+
filters: (Optional[Dict[str, Any]]) A dictionary of filters to apply
|
150
|
+
to the runs query.
|
151
|
+
order: (str) Order can be `created_at`, `heartbeat_at`, `config.*.value`, or `summary_metrics.*`.
|
152
|
+
If you prepend order with a + order is ascending (default).
|
153
|
+
If you prepend order with a - order is descending.
|
154
|
+
The default order is run.created_at from oldest to newest.
|
155
|
+
per_page: (int) The number of runs to fetch per request (default is 50).
|
156
|
+
include_sweeps: (bool) Whether to include sweep information in the
|
157
|
+
runs. Defaults to True.
|
158
|
+
|
159
|
+
Examples:
|
160
|
+
```python
|
161
|
+
from wandb.apis.public.runs import Runs
|
162
|
+
from wandb.apis.public import Api
|
163
|
+
|
164
|
+
# Get all runs from a project that satisfy the filters
|
165
|
+
filters = {"state": "finished", "config.optimizer": "adam"}
|
166
|
+
|
167
|
+
runs = Api().runs(
|
168
|
+
client=api.client,
|
169
|
+
entity="entity",
|
170
|
+
project="project_name",
|
171
|
+
filters=filters,
|
172
|
+
)
|
173
|
+
|
174
|
+
# Iterate over runs and print details
|
175
|
+
for run in runs:
|
176
|
+
print(f"Run name: {run.name}")
|
177
|
+
print(f"Run ID: {run.id}")
|
178
|
+
print(f"Run URL: {run.url}")
|
179
|
+
print(f"Run state: {run.state}")
|
180
|
+
print(f"Run config: {run.config}")
|
181
|
+
print(f"Run summary: {run.summary}")
|
182
|
+
print(f"Run history (samples=5): {run.history(samples=5)}")
|
183
|
+
print("----------")
|
184
|
+
|
185
|
+
# Get histories for all runs with specific metrics
|
186
|
+
histories_df = runs.histories(
|
187
|
+
samples=100, # Number of samples per run
|
188
|
+
keys=["loss", "accuracy"], # Metrics to fetch
|
189
|
+
x_axis="_step", # X-axis metric
|
190
|
+
format="pandas", # Return as pandas DataFrame
|
191
|
+
)
|
192
|
+
```
|
92
193
|
"""
|
93
194
|
|
94
195
|
def __init__(
|
95
196
|
self,
|
96
|
-
client:
|
197
|
+
client: RetryingClient,
|
97
198
|
entity: str,
|
98
199
|
project: str,
|
99
|
-
filters:
|
100
|
-
order:
|
200
|
+
filters: dict[str, Any] | None = None,
|
201
|
+
order: str = "+created_at",
|
101
202
|
per_page: int = 50,
|
102
203
|
include_sweeps: bool = True,
|
103
204
|
):
|
205
|
+
if not order:
|
206
|
+
order = "+created_at"
|
207
|
+
|
104
208
|
self.QUERY = gql(
|
105
209
|
f"""#graphql
|
106
210
|
query Runs($project: String!, $entity: String!, $cursor: String, $perPage: Int = 50, $order: String, $filters: JSONString) {{
|
@@ -111,7 +215,7 @@ class Runs(SizedPaginator["Run"]):
|
|
111
215
|
runs(filters: $filters, after: $cursor, first: $perPage, order: $order) {{
|
112
216
|
edges {{
|
113
217
|
node {{
|
114
|
-
{"" if _server_provides_internal_id_for_project(client) else "
|
218
|
+
{"projectId" if _server_provides_internal_id_for_project(client) else ""}
|
115
219
|
...RunFragment
|
116
220
|
}}
|
117
221
|
cursor
|
@@ -143,27 +247,44 @@ class Runs(SizedPaginator["Run"]):
|
|
143
247
|
super().__init__(client, variables, per_page)
|
144
248
|
|
145
249
|
@property
|
146
|
-
def
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
250
|
+
def _length(self):
|
251
|
+
"""Returns the total number of runs.
|
252
|
+
|
253
|
+
<!-- lazydoc-ignore: internal -->
|
254
|
+
"""
|
255
|
+
if not self.last_response:
|
256
|
+
self._load_page()
|
257
|
+
return self.last_response["project"]["runCount"]
|
151
258
|
|
152
259
|
@property
|
153
|
-
def more(self):
|
260
|
+
def more(self) -> bool:
|
261
|
+
"""Returns whether there are more runs to fetch.
|
262
|
+
|
263
|
+
<!-- lazydoc-ignore: internal -->
|
264
|
+
"""
|
154
265
|
if self.last_response:
|
155
|
-
return
|
266
|
+
return bool(
|
267
|
+
self.last_response["project"]["runs"]["pageInfo"]["hasNextPage"]
|
268
|
+
)
|
156
269
|
else:
|
157
270
|
return True
|
158
271
|
|
159
272
|
@property
|
160
273
|
def cursor(self):
|
274
|
+
"""Returns the cursor position for pagination of runs results.
|
275
|
+
|
276
|
+
<!-- lazydoc-ignore: internal -->
|
277
|
+
"""
|
161
278
|
if self.last_response:
|
162
279
|
return self.last_response["project"]["runs"]["edges"][-1]["cursor"]
|
163
280
|
else:
|
164
281
|
return None
|
165
282
|
|
166
283
|
def convert_objects(self):
|
284
|
+
"""Converts GraphQL edges to Runs objects.
|
285
|
+
|
286
|
+
<!-- lazydoc-ignore: internal -->
|
287
|
+
"""
|
167
288
|
objs = []
|
168
289
|
if self.last_response is None or self.last_response.get("project") is None:
|
169
290
|
raise ValueError("Could not find project {}".format(self.project))
|
@@ -201,7 +322,7 @@ class Runs(SizedPaginator["Run"]):
|
|
201
322
|
def histories(
|
202
323
|
self,
|
203
324
|
samples: int = 500,
|
204
|
-
keys:
|
325
|
+
keys: list[str] | None = None,
|
205
326
|
x_axis: str = "_step",
|
206
327
|
format: Literal["default", "pandas", "polars"] = "default",
|
207
328
|
stream: Literal["default", "system"] = "default",
|
@@ -209,15 +330,19 @@ class Runs(SizedPaginator["Run"]):
|
|
209
330
|
"""Return sampled history metrics for all runs that fit the filters conditions.
|
210
331
|
|
211
332
|
Args:
|
212
|
-
samples
|
213
|
-
keys
|
214
|
-
x_axis
|
215
|
-
format
|
216
|
-
|
333
|
+
samples: The number of samples to return per run
|
334
|
+
keys: Only return metrics for specific keys
|
335
|
+
x_axis: Use this metric as the xAxis defaults to _step
|
336
|
+
format: Format to return data in, options are "default", "pandas",
|
337
|
+
"polars"
|
338
|
+
stream: "default" for metrics, "system" for machine metrics
|
217
339
|
Returns:
|
218
|
-
pandas.DataFrame: If format="pandas"
|
219
|
-
|
220
|
-
|
340
|
+
pandas.DataFrame: If `format="pandas"`, returns a `pandas.DataFrame`
|
341
|
+
of history metrics.
|
342
|
+
polars.DataFrame: If `format="polars"`, returns a `polars.DataFrame`
|
343
|
+
of history metrics.
|
344
|
+
list of dicts: If `format="default"`, returns a list of dicts
|
345
|
+
containing history metrics with a `run_id` key.
|
221
346
|
"""
|
222
347
|
if format not in ("default", "pandas", "polars"):
|
223
348
|
raise ValueError(
|
@@ -301,6 +426,14 @@ class Runs(SizedPaginator["Run"]):
|
|
301
426
|
class Run(Attrs):
|
302
427
|
"""A single run associated with an entity and project.
|
303
428
|
|
429
|
+
Args:
|
430
|
+
client: The W&B API client.
|
431
|
+
entity: The entity associated with the run.
|
432
|
+
project: The project associated with the run.
|
433
|
+
run_id: The unique identifier for the run.
|
434
|
+
attrs: The attributes of the run.
|
435
|
+
include_sweeps: Whether to include sweeps in the run.
|
436
|
+
|
304
437
|
Attributes:
|
305
438
|
tags ([str]): a list of tags associated with the run
|
306
439
|
url (str): the url of this run
|
@@ -326,11 +459,11 @@ class Run(Attrs):
|
|
326
459
|
|
327
460
|
def __init__(
|
328
461
|
self,
|
329
|
-
client:
|
462
|
+
client: RetryingClient,
|
330
463
|
entity: str,
|
331
464
|
project: str,
|
332
465
|
run_id: str,
|
333
|
-
attrs:
|
466
|
+
attrs: Mapping | None = None,
|
334
467
|
include_sweeps: bool = True,
|
335
468
|
):
|
336
469
|
"""Initialize a Run object.
|
@@ -354,27 +487,32 @@ class Run(Attrs):
|
|
354
487
|
except OSError:
|
355
488
|
pass
|
356
489
|
self._summary = None
|
357
|
-
self._metadata:
|
490
|
+
self._metadata: dict[str, Any] | None = None
|
358
491
|
self._state = _attrs.get("state", "not found")
|
359
|
-
self.server_provides_internal_id_field:
|
492
|
+
self.server_provides_internal_id_field: bool | None = None
|
493
|
+
self._is_loaded: bool = False
|
360
494
|
|
361
495
|
self.load(force=not _attrs)
|
362
496
|
|
363
497
|
@property
|
364
498
|
def state(self):
|
499
|
+
"""The state of the run. Can be one of: Finished, Failed, Crashed, or Running."""
|
365
500
|
return self._state
|
366
501
|
|
367
502
|
@property
|
368
503
|
def entity(self):
|
504
|
+
"""The entity associated with the run."""
|
369
505
|
return self._entity
|
370
506
|
|
371
507
|
@property
|
372
508
|
def username(self):
|
509
|
+
"""This API is deprecated. Use `entity` instead."""
|
373
510
|
wandb.termwarn("Run.username is deprecated. Please use Run.entity instead.")
|
374
511
|
return self._entity
|
375
512
|
|
376
513
|
@property
|
377
514
|
def storage_id(self):
|
515
|
+
"""The unique storage identifier for the run."""
|
378
516
|
# For compatibility with wandb.Run, which has storage IDs
|
379
517
|
# in self.storage_id and names in self.id.
|
380
518
|
|
@@ -382,33 +520,38 @@ class Run(Attrs):
|
|
382
520
|
|
383
521
|
@property
|
384
522
|
def id(self):
|
523
|
+
"""The unique identifier for the run."""
|
385
524
|
return self._attrs.get("name")
|
386
525
|
|
387
526
|
@id.setter
|
388
527
|
def id(self, new_id):
|
528
|
+
"""Set the unique identifier for the run."""
|
389
529
|
attrs = self._attrs
|
390
530
|
attrs["name"] = new_id
|
391
531
|
return new_id
|
392
532
|
|
393
533
|
@property
|
394
534
|
def name(self):
|
535
|
+
"""The name of the run."""
|
395
536
|
return self._attrs.get("displayName")
|
396
537
|
|
397
538
|
@name.setter
|
398
539
|
def name(self, new_name):
|
540
|
+
"""Set the name of the run."""
|
399
541
|
self._attrs["displayName"] = new_name
|
400
542
|
return new_name
|
401
543
|
|
402
544
|
@classmethod
|
403
545
|
def create(
|
404
546
|
cls,
|
405
|
-
api,
|
406
|
-
run_id=None,
|
407
|
-
project=None,
|
408
|
-
entity=None,
|
547
|
+
api: public.Api,
|
548
|
+
run_id: str | None = None,
|
549
|
+
project: str | None = None,
|
550
|
+
entity: str | None = None,
|
409
551
|
state: Literal["running", "pending"] = "running",
|
410
552
|
):
|
411
553
|
"""Create a run for the given project."""
|
554
|
+
api._sentry.message("Invoking Run.create", level="info")
|
412
555
|
run_id = run_id or runid.generate_id()
|
413
556
|
project = project or api.settings.get("project") or "uncategorized"
|
414
557
|
mutation = gql(
|
@@ -454,25 +597,20 @@ class Run(Attrs):
|
|
454
597
|
)
|
455
598
|
|
456
599
|
def load(self, force=False):
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
project
|
461
|
-
|
462
|
-
{
|
463
|
-
|
600
|
+
if force or not self._attrs:
|
601
|
+
self._is_loaded = False
|
602
|
+
query = gql(f"""#graphql
|
603
|
+
query Run($project: String!, $entity: String!, $name: String!) {{
|
604
|
+
project(name: $project, entityName: $entity) {{
|
605
|
+
run(name: $name) {{
|
606
|
+
{"projectId" if _server_provides_internal_id_for_project(self.client) else ""}
|
607
|
+
...RunFragment
|
608
|
+
}}
|
464
609
|
}}
|
465
610
|
}}
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
"projectId"
|
470
|
-
if _server_provides_internal_id_for_project(self.client)
|
471
|
-
else "",
|
472
|
-
RUN_FRAGMENT,
|
473
|
-
)
|
474
|
-
)
|
475
|
-
if force or not self._attrs:
|
611
|
+
{RUN_FRAGMENT}
|
612
|
+
""")
|
613
|
+
|
476
614
|
response = self._exec(query)
|
477
615
|
if (
|
478
616
|
response is None
|
@@ -481,7 +619,6 @@ class Run(Attrs):
|
|
481
619
|
):
|
482
620
|
raise ValueError("Could not find run {}".format(self))
|
483
621
|
self._attrs = response["project"]["run"]
|
484
|
-
self._state = self._attrs["state"]
|
485
622
|
|
486
623
|
if self._include_sweeps and self.sweep_name and not self.sweep:
|
487
624
|
# There may be a lot of runs. Don't bother pulling them all
|
@@ -494,32 +631,31 @@ class Run(Attrs):
|
|
494
631
|
withRuns=False,
|
495
632
|
)
|
496
633
|
|
634
|
+
if not self._is_loaded:
|
635
|
+
self._load_from_attrs()
|
636
|
+
self._is_loaded = True
|
637
|
+
|
638
|
+
return self._attrs
|
639
|
+
|
640
|
+
def _load_from_attrs(self):
|
641
|
+
self._state = self._attrs.get("state", None)
|
642
|
+
self._attrs["config"] = _convert_to_dict(self._attrs.get("config"))
|
643
|
+
self._attrs["summaryMetrics"] = _convert_to_dict(
|
644
|
+
self._attrs.get("summaryMetrics")
|
645
|
+
)
|
646
|
+
self._attrs["systemMetrics"] = _convert_to_dict(
|
647
|
+
self._attrs.get("systemMetrics")
|
648
|
+
)
|
649
|
+
|
497
650
|
if "projectId" in self._attrs:
|
498
651
|
self._project_internal_id = int(self._attrs["projectId"])
|
499
652
|
else:
|
500
653
|
self._project_internal_id = None
|
501
654
|
|
502
|
-
try:
|
503
|
-
self._attrs["summaryMetrics"] = (
|
504
|
-
json.loads(self._attrs["summaryMetrics"])
|
505
|
-
if self._attrs.get("summaryMetrics")
|
506
|
-
else {}
|
507
|
-
)
|
508
|
-
except json.decoder.JSONDecodeError:
|
509
|
-
# ignore invalid utf-8 or control characters
|
510
|
-
self._attrs["summaryMetrics"] = json.loads(
|
511
|
-
self._attrs["summaryMetrics"],
|
512
|
-
strict=False,
|
513
|
-
)
|
514
|
-
self._attrs["systemMetrics"] = (
|
515
|
-
json.loads(self._attrs["systemMetrics"])
|
516
|
-
if self._attrs.get("systemMetrics")
|
517
|
-
else {}
|
518
|
-
)
|
519
655
|
if self._attrs.get("user"):
|
520
656
|
self.user = public.User(self.client, self._attrs["user"])
|
521
657
|
config_user, config_raw = {}, {}
|
522
|
-
for key, value in
|
658
|
+
for key, value in self._attrs.get("config").items():
|
523
659
|
config = config_raw if key in WANDB_INTERNAL_KEYS else config_user
|
524
660
|
if isinstance(value, dict) and "value" in value:
|
525
661
|
config[key] = value["value"]
|
@@ -528,10 +664,10 @@ class Run(Attrs):
|
|
528
664
|
config_raw.update(config_user)
|
529
665
|
self._attrs["config"] = config_user
|
530
666
|
self._attrs["rawconfig"] = config_raw
|
531
|
-
return self._attrs
|
532
667
|
|
533
668
|
@normalize_exceptions
|
534
669
|
def wait_until_finished(self):
|
670
|
+
"""Check the state of the run until it is finished."""
|
535
671
|
query = gql(
|
536
672
|
"""
|
537
673
|
query RunState($project: String!, $entity: String!, $name: String!) {
|
@@ -582,7 +718,12 @@ class Run(Attrs):
|
|
582
718
|
|
583
719
|
@normalize_exceptions
|
584
720
|
def delete(self, delete_artifacts=False):
|
585
|
-
"""Delete the given run from the wandb backend.
|
721
|
+
"""Delete the given run from the wandb backend.
|
722
|
+
|
723
|
+
Args:
|
724
|
+
delete_artifacts (bool, optional): Whether to delete the artifacts
|
725
|
+
associated with the run.
|
726
|
+
"""
|
586
727
|
mutation = gql(
|
587
728
|
"""
|
588
729
|
mutation DeleteRun(
|
@@ -611,10 +752,15 @@ class Run(Attrs):
|
|
611
752
|
)
|
612
753
|
|
613
754
|
def save(self):
|
755
|
+
"""Persist changes to the run object to the W&B backend."""
|
614
756
|
self.update()
|
615
757
|
|
616
758
|
@property
|
617
759
|
def json_config(self):
|
760
|
+
"""Return the run config as a JSON string.
|
761
|
+
|
762
|
+
<!-- lazydoc-ignore: internal -->
|
763
|
+
"""
|
618
764
|
config = {}
|
619
765
|
if "_wandb" in self.rawconfig:
|
620
766
|
config["_wandb"] = {"value": self.rawconfig["_wandb"], "desc": None}
|
@@ -660,17 +806,35 @@ class Run(Attrs):
|
|
660
806
|
return [json.loads(line) for line in response["project"]["run"][node]]
|
661
807
|
|
662
808
|
@normalize_exceptions
|
663
|
-
def files(
|
664
|
-
|
809
|
+
def files(
|
810
|
+
self,
|
811
|
+
names: list[str] | None = None,
|
812
|
+
pattern: str | None = None,
|
813
|
+
per_page: int = 50,
|
814
|
+
):
|
815
|
+
"""Returns a `Files` object for all files in the run which match the given criteria.
|
816
|
+
|
817
|
+
You can specify a list of exact file names to match, or a pattern to match against.
|
818
|
+
If both are provided, the pattern will be ignored.
|
665
819
|
|
666
820
|
Args:
|
667
821
|
names (list): names of the requested files, if empty returns all files
|
822
|
+
pattern (str, optional): Pattern to match when returning files from W&B.
|
823
|
+
This pattern uses mySQL's LIKE syntax,
|
824
|
+
so matching all files that end with .json would be "%.json".
|
825
|
+
If both names and pattern are provided, a ValueError will be raised.
|
668
826
|
per_page (int): number of results per page.
|
669
827
|
|
670
828
|
Returns:
|
671
829
|
A `Files` object, which is an iterator over `File` objects.
|
672
830
|
"""
|
673
|
-
return public.Files(
|
831
|
+
return public.Files(
|
832
|
+
self.client,
|
833
|
+
self,
|
834
|
+
names or [],
|
835
|
+
pattern=pattern,
|
836
|
+
per_page=per_page,
|
837
|
+
)
|
674
838
|
|
675
839
|
@normalize_exceptions
|
676
840
|
def file(self, name):
|
@@ -686,16 +850,17 @@ class Run(Attrs):
|
|
686
850
|
|
687
851
|
@normalize_exceptions
|
688
852
|
def upload_file(self, path, root="."):
|
689
|
-
"""Upload a file.
|
853
|
+
"""Upload a local file to W&B, associating it with this run.
|
690
854
|
|
691
855
|
Args:
|
692
|
-
path (str):
|
693
|
-
root (str):
|
694
|
-
|
856
|
+
path (str): Path to the file to upload. Can be absolute or relative.
|
857
|
+
root (str): The root path to save the file relative to. For example,
|
858
|
+
if you want to have the file saved in the run as "my_dir/file.txt"
|
695
859
|
and you're currently in "my_dir" you would set root to "../".
|
860
|
+
Defaults to current directory (".").
|
696
861
|
|
697
862
|
Returns:
|
698
|
-
A `File`
|
863
|
+
A `File` object representing the uploaded file.
|
699
864
|
"""
|
700
865
|
api = InternalApi(
|
701
866
|
default_settings={"entity": self.entity, "project": self.project},
|
@@ -704,8 +869,9 @@ class Run(Attrs):
|
|
704
869
|
api.set_current_run_id(self.id)
|
705
870
|
root = os.path.abspath(root)
|
706
871
|
name = os.path.relpath(path, root)
|
872
|
+
upload_path = util.make_file_path_upload_safe(name)
|
707
873
|
with open(os.path.join(root, name), "rb") as f:
|
708
|
-
api.push({LogicalPath(
|
874
|
+
api.push({LogicalPath(upload_path): f})
|
709
875
|
return public.Files(self.client, self, [name])[0]
|
710
876
|
|
711
877
|
@normalize_exceptions
|
@@ -754,15 +920,6 @@ class Run(Attrs):
|
|
754
920
|
def scan_history(self, keys=None, page_size=1000, min_step=None, max_step=None):
|
755
921
|
"""Returns an iterable collection of all history records for a run.
|
756
922
|
|
757
|
-
Example:
|
758
|
-
Export all the loss values for an example run
|
759
|
-
|
760
|
-
```python
|
761
|
-
run = api.run("l2k2/examples-numpy-boston/i0wt6xua")
|
762
|
-
history = run.scan_history(keys=["Loss"])
|
763
|
-
losses = [row["Loss"] for row in history]
|
764
|
-
```
|
765
|
-
|
766
923
|
Args:
|
767
924
|
keys ([str], optional): only fetch these keys, and only fetch rows that have all of keys defined.
|
768
925
|
page_size (int, optional): size of pages to fetch from the api.
|
@@ -771,6 +928,15 @@ class Run(Attrs):
|
|
771
928
|
|
772
929
|
Returns:
|
773
930
|
An iterable collection over history records (dict).
|
931
|
+
|
932
|
+
Example:
|
933
|
+
Export all the loss values for an example run
|
934
|
+
|
935
|
+
```python
|
936
|
+
run = api.run("entity/project-name/run-id")
|
937
|
+
history = run.scan_history(keys=["Loss"])
|
938
|
+
losses = [row["Loss"] for row in history]
|
939
|
+
```
|
774
940
|
"""
|
775
941
|
if keys is not None and not isinstance(keys, list):
|
776
942
|
wandb.termerror("keys must be specified in a list")
|
@@ -820,23 +986,27 @@ class Run(Attrs):
|
|
820
986
|
An iterable collection of all Artifact objects logged as outputs during this run.
|
821
987
|
|
822
988
|
Example:
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
989
|
+
```python
|
990
|
+
import wandb
|
991
|
+
import tempfile
|
992
|
+
|
993
|
+
with tempfile.NamedTemporaryFile(mode="w", delete=False, suffix=".txt") as tmp:
|
994
|
+
tmp.write("This is a test artifact")
|
995
|
+
tmp_path = tmp.name
|
996
|
+
run = wandb.init(project="artifact-example")
|
997
|
+
artifact = wandb.Artifact("test_artifact", type="dataset")
|
998
|
+
artifact.add_file(tmp_path)
|
999
|
+
run.log_artifact(artifact)
|
1000
|
+
run.finish()
|
1001
|
+
|
1002
|
+
api = wandb.Api()
|
1003
|
+
|
1004
|
+
finished_run = api.run(f"{run.entity}/{run.project}/{run.id}")
|
1005
|
+
|
1006
|
+
for logged_artifact in finished_run.logged_artifacts():
|
1007
|
+
print(logged_artifact.name)
|
1008
|
+
```
|
1009
|
+
|
840
1010
|
"""
|
841
1011
|
return public.RunArtifacts(self.client, self, mode="logged", per_page=per_page)
|
842
1012
|
|
@@ -855,15 +1025,19 @@ class Run(Attrs):
|
|
855
1025
|
An iterable collection of Artifact objects explicitly used as inputs in this run.
|
856
1026
|
|
857
1027
|
Example:
|
858
|
-
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
|
865
|
-
|
866
|
-
|
1028
|
+
```python
|
1029
|
+
import wandb
|
1030
|
+
|
1031
|
+
run = wandb.init(project="artifact-example")
|
1032
|
+
run.use_artifact("test_artifact:latest")
|
1033
|
+
run.finish()
|
1034
|
+
|
1035
|
+
api = wandb.Api()
|
1036
|
+
finished_run = api.run(f"{run.entity}/{run.project}/{run.id}")
|
1037
|
+
for used_artifact in finished_run.used_artifacts():
|
1038
|
+
print(used_artifact.name)
|
1039
|
+
test_artifact
|
1040
|
+
```
|
867
1041
|
"""
|
868
1042
|
return public.RunArtifacts(self.client, self, mode="used", per_page=per_page)
|
869
1043
|
|
@@ -881,7 +1055,7 @@ class Run(Attrs):
|
|
881
1055
|
feature's artifact swapping functionality.
|
882
1056
|
|
883
1057
|
Returns:
|
884
|
-
|
1058
|
+
An `Artifact` object.
|
885
1059
|
"""
|
886
1060
|
api = InternalApi(
|
887
1061
|
default_settings={"entity": self.entity, "project": self.project},
|
@@ -908,9 +1082,9 @@ class Run(Attrs):
|
|
908
1082
|
@normalize_exceptions
|
909
1083
|
def log_artifact(
|
910
1084
|
self,
|
911
|
-
artifact:
|
912
|
-
aliases:
|
913
|
-
tags:
|
1085
|
+
artifact: wandb.Artifact,
|
1086
|
+
aliases: Collection[str] | None = None,
|
1087
|
+
tags: Collection[str] | None = None,
|
914
1088
|
):
|
915
1089
|
"""Declare an artifact as output of a run.
|
916
1090
|
|
@@ -954,6 +1128,7 @@ class Run(Attrs):
|
|
954
1128
|
|
955
1129
|
@property
|
956
1130
|
def summary(self):
|
1131
|
+
"""A mutable dict-like property that holds summary values associated with the run."""
|
957
1132
|
if self._summary is None:
|
958
1133
|
from wandb.old.summary import HTTPSummary
|
959
1134
|
|
@@ -963,6 +1138,7 @@ class Run(Attrs):
|
|
963
1138
|
|
964
1139
|
@property
|
965
1140
|
def path(self):
|
1141
|
+
"""The path of the run. The path is a list containing the entity, project, and run_id."""
|
966
1142
|
return [
|
967
1143
|
urllib.parse.quote_plus(str(self.entity)),
|
968
1144
|
urllib.parse.quote_plus(str(self.project)),
|
@@ -971,12 +1147,22 @@ class Run(Attrs):
|
|
971
1147
|
|
972
1148
|
@property
|
973
1149
|
def url(self):
|
1150
|
+
"""The URL of the run.
|
1151
|
+
|
1152
|
+
The run URL is generated from the entity, project, and run_id. For
|
1153
|
+
SaaS users, it takes the form of `https://wandb.ai/entity/project/run_id`.
|
1154
|
+
"""
|
974
1155
|
path = self.path
|
975
1156
|
path.insert(2, "runs")
|
976
1157
|
return self.client.app_url + "/".join(path)
|
977
1158
|
|
978
1159
|
@property
|
979
1160
|
def metadata(self):
|
1161
|
+
"""Metadata about the run from wandb-metadata.json.
|
1162
|
+
|
1163
|
+
Metadata includes the run's description, tags, start time, memory
|
1164
|
+
usage and more.
|
1165
|
+
"""
|
980
1166
|
if self._metadata is None:
|
981
1167
|
try:
|
982
1168
|
f = self.file("wandb-metadata.json")
|
@@ -992,6 +1178,7 @@ class Run(Attrs):
|
|
992
1178
|
|
993
1179
|
@property
|
994
1180
|
def lastHistoryStep(self): # noqa: N802
|
1181
|
+
"""Returns the last step logged in the run's history."""
|
995
1182
|
query = gql(
|
996
1183
|
"""
|
997
1184
|
query RunHistoryKeys($project: String!, $entity: String!, $name: String!) {
|