wandb 0.15.9__py3-none-any.whl → 0.15.11__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- wandb/__init__.py +5 -1
- wandb/apis/public.py +137 -17
- wandb/apis/reports/_panels.py +1 -1
- wandb/apis/reports/blocks.py +1 -0
- wandb/apis/reports/report.py +27 -5
- wandb/cli/cli.py +52 -41
- wandb/docker/__init__.py +17 -0
- wandb/docker/auth.py +1 -1
- wandb/env.py +24 -4
- wandb/filesync/step_checksum.py +3 -3
- wandb/integration/openai/openai.py +3 -0
- wandb/integration/ultralytics/__init__.py +9 -0
- wandb/integration/ultralytics/bbox_utils.py +196 -0
- wandb/integration/ultralytics/callback.py +458 -0
- wandb/integration/ultralytics/classification_utils.py +66 -0
- wandb/integration/ultralytics/mask_utils.py +141 -0
- wandb/integration/ultralytics/pose_utils.py +92 -0
- wandb/integration/xgboost/xgboost.py +3 -3
- wandb/integration/yolov8/__init__.py +0 -7
- wandb/integration/yolov8/yolov8.py +22 -3
- wandb/old/settings.py +7 -0
- wandb/plot/line_series.py +0 -1
- wandb/proto/v3/wandb_internal_pb2.py +353 -300
- wandb/proto/v3/wandb_server_pb2.py +37 -41
- wandb/proto/v3/wandb_settings_pb2.py +2 -2
- wandb/proto/v3/wandb_telemetry_pb2.py +16 -16
- wandb/proto/v4/wandb_internal_pb2.py +272 -260
- wandb/proto/v4/wandb_server_pb2.py +37 -40
- wandb/proto/v4/wandb_settings_pb2.py +2 -2
- wandb/proto/v4/wandb_telemetry_pb2.py +16 -16
- wandb/proto/wandb_internal_codegen.py +7 -31
- wandb/sdk/artifacts/artifact.py +321 -189
- wandb/sdk/artifacts/artifact_cache.py +14 -0
- wandb/sdk/artifacts/artifact_manifest.py +5 -4
- wandb/sdk/artifacts/artifact_manifest_entry.py +37 -9
- wandb/sdk/artifacts/artifact_manifests/artifact_manifest_v1.py +1 -9
- wandb/sdk/artifacts/artifact_saver.py +13 -50
- wandb/sdk/artifacts/artifact_ttl.py +6 -0
- wandb/sdk/artifacts/artifacts_cache.py +119 -93
- wandb/sdk/artifacts/staging.py +25 -0
- wandb/sdk/artifacts/storage_handlers/s3_handler.py +12 -7
- wandb/sdk/artifacts/storage_handlers/wb_local_artifact_handler.py +2 -3
- wandb/sdk/artifacts/storage_policies/__init__.py +4 -0
- wandb/sdk/artifacts/storage_policies/register.py +1 -0
- wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +4 -3
- wandb/sdk/artifacts/storage_policy.py +4 -2
- wandb/sdk/backend/backend.py +0 -16
- wandb/sdk/data_types/image.py +3 -1
- wandb/sdk/integration_utils/auto_logging.py +38 -13
- wandb/sdk/interface/interface.py +16 -135
- wandb/sdk/interface/interface_shared.py +9 -147
- wandb/sdk/interface/interface_sock.py +0 -26
- wandb/sdk/internal/file_pusher.py +20 -3
- wandb/sdk/internal/file_stream.py +3 -1
- wandb/sdk/internal/handler.py +53 -70
- wandb/sdk/internal/internal_api.py +220 -130
- wandb/sdk/internal/job_builder.py +41 -37
- wandb/sdk/internal/sender.py +7 -25
- wandb/sdk/internal/system/assets/disk.py +144 -11
- wandb/sdk/internal/system/system_info.py +6 -2
- wandb/sdk/launch/__init__.py +5 -0
- wandb/sdk/launch/{launch.py → _launch.py} +53 -54
- wandb/sdk/launch/{launch_add.py → _launch_add.py} +34 -31
- wandb/sdk/launch/_project_spec.py +13 -2
- wandb/sdk/launch/agent/agent.py +103 -59
- wandb/sdk/launch/agent/run_queue_item_file_saver.py +6 -4
- wandb/sdk/launch/builder/build.py +19 -1
- wandb/sdk/launch/builder/docker_builder.py +5 -1
- wandb/sdk/launch/builder/kaniko_builder.py +5 -1
- wandb/sdk/launch/create_job.py +20 -5
- wandb/sdk/launch/loader.py +14 -5
- wandb/sdk/launch/runner/abstract.py +0 -2
- wandb/sdk/launch/runner/kubernetes_monitor.py +329 -0
- wandb/sdk/launch/runner/kubernetes_runner.py +66 -209
- wandb/sdk/launch/runner/local_container.py +5 -2
- wandb/sdk/launch/runner/local_process.py +4 -1
- wandb/sdk/launch/sweeps/scheduler.py +43 -25
- wandb/sdk/launch/sweeps/utils.py +5 -3
- wandb/sdk/launch/utils.py +3 -1
- wandb/sdk/lib/_settings_toposort_generate.py +3 -9
- wandb/sdk/lib/_settings_toposort_generated.py +27 -3
- wandb/sdk/lib/_wburls_generated.py +1 -0
- wandb/sdk/lib/filenames.py +27 -6
- wandb/sdk/lib/filesystem.py +181 -7
- wandb/sdk/lib/fsm.py +5 -3
- wandb/sdk/lib/gql_request.py +3 -0
- wandb/sdk/lib/ipython.py +7 -0
- wandb/sdk/lib/wburls.py +1 -0
- wandb/sdk/service/port_file.py +2 -15
- wandb/sdk/service/server.py +7 -55
- wandb/sdk/service/service.py +56 -26
- wandb/sdk/service/service_base.py +1 -1
- wandb/sdk/service/streams.py +11 -5
- wandb/sdk/verify/verify.py +2 -2
- wandb/sdk/wandb_init.py +8 -2
- wandb/sdk/wandb_manager.py +4 -14
- wandb/sdk/wandb_run.py +143 -53
- wandb/sdk/wandb_settings.py +148 -35
- wandb/testing/relay.py +85 -38
- wandb/util.py +87 -4
- wandb/wandb_torch.py +24 -38
- {wandb-0.15.9.dist-info → wandb-0.15.11.dist-info}/METADATA +48 -23
- {wandb-0.15.9.dist-info → wandb-0.15.11.dist-info}/RECORD +107 -103
- {wandb-0.15.9.dist-info → wandb-0.15.11.dist-info}/WHEEL +1 -1
- wandb/proto/v3/wandb_server_pb2_grpc.py +0 -1422
- wandb/proto/v4/wandb_server_pb2_grpc.py +0 -1422
- wandb/proto/wandb_server_pb2_grpc.py +0 -8
- wandb/sdk/artifacts/storage_policies/s3_bucket_policy.py +0 -61
- wandb/sdk/interface/interface_grpc.py +0 -460
- wandb/sdk/service/server_grpc.py +0 -444
- wandb/sdk/service/service_grpc.py +0 -73
- {wandb-0.15.9.dist-info → wandb-0.15.11.dist-info}/LICENSE +0 -0
- {wandb-0.15.9.dist-info → wandb-0.15.11.dist-info}/entry_points.txt +0 -0
- {wandb-0.15.9.dist-info → wandb-0.15.11.dist-info}/top_level.txt +0 -0
@@ -2,6 +2,8 @@ import ast
|
|
2
2
|
import asyncio
|
3
3
|
import base64
|
4
4
|
import datetime
|
5
|
+
import functools
|
6
|
+
import http.client
|
5
7
|
import json
|
6
8
|
import logging
|
7
9
|
import os
|
@@ -113,6 +115,7 @@ if TYPE_CHECKING:
|
|
113
115
|
entity: Optional[str]
|
114
116
|
project: Optional[str]
|
115
117
|
_extra_http_headers: Optional[Mapping[str, str]]
|
118
|
+
_proxies: Optional[Mapping[str, str]]
|
116
119
|
|
117
120
|
_Response = MutableMapping
|
118
121
|
SweepState = Literal["RUNNING", "PAUSED", "CANCELED", "FINISHED"]
|
@@ -130,6 +133,33 @@ else:
|
|
130
133
|
# def keys(self) -> Iterable: ...
|
131
134
|
# def __getitem__(self, name: str) -> Any: ...
|
132
135
|
|
136
|
+
httpclient_logger = logging.getLogger("http.client")
|
137
|
+
if os.environ.get("WANDB_DEBUG"):
|
138
|
+
httpclient_logger.setLevel(logging.DEBUG)
|
139
|
+
|
140
|
+
|
141
|
+
def check_httpclient_logger_handler() -> None:
|
142
|
+
# Only enable http.client logging if WANDB_DEBUG is set
|
143
|
+
if not os.environ.get("WANDB_DEBUG"):
|
144
|
+
return
|
145
|
+
if httpclient_logger.handlers:
|
146
|
+
return
|
147
|
+
|
148
|
+
# Enable HTTPConnection debug logging to the logging framework
|
149
|
+
level = logging.DEBUG
|
150
|
+
|
151
|
+
def httpclient_log(*args: Any) -> None:
|
152
|
+
httpclient_logger.log(level, " ".join(args))
|
153
|
+
|
154
|
+
# mask the print() built-in in the http.client module to use logging instead
|
155
|
+
http.client.print = httpclient_log # type: ignore[attr-defined]
|
156
|
+
# enable debugging
|
157
|
+
http.client.HTTPConnection.debuglevel = 1
|
158
|
+
|
159
|
+
root_logger = logging.getLogger("wandb")
|
160
|
+
if root_logger.handlers:
|
161
|
+
httpclient_logger.addHandler(root_logger.handlers[0])
|
162
|
+
|
133
163
|
|
134
164
|
def check_httpx_exc_retriable(exc: Exception) -> bool:
|
135
165
|
retriable_codes = (308, 408, 409, 429, 500, 502, 503, 504)
|
@@ -170,7 +200,8 @@ class Api:
|
|
170
200
|
Override the settings here.
|
171
201
|
"""
|
172
202
|
|
173
|
-
HTTP_TIMEOUT = env.get_http_timeout(
|
203
|
+
HTTP_TIMEOUT = env.get_http_timeout(20)
|
204
|
+
FILE_PUSHER_TIMEOUT = env.get_file_pusher_timeout()
|
174
205
|
_global_context: context.Context
|
175
206
|
_local_data: _ThreadLocalData
|
176
207
|
|
@@ -204,6 +235,7 @@ class Api:
|
|
204
235
|
"entity": None,
|
205
236
|
"project": None,
|
206
237
|
"_extra_http_headers": None,
|
238
|
+
"_proxies": None,
|
207
239
|
}
|
208
240
|
self.retry_timedelta = retry_timedelta
|
209
241
|
# todo: Old Settings do not follow the SupportsKeysAndGetItem Protocol
|
@@ -222,12 +254,13 @@ class Api:
|
|
222
254
|
"heartbeat_seconds": 30,
|
223
255
|
}
|
224
256
|
|
225
|
-
# todo: remove
|
257
|
+
# todo: remove these hacky hacks after settings refactor is complete
|
226
258
|
# keeping this code here to limit scope and so that it is easy to remove later
|
227
|
-
extra_http_headers = self.settings(
|
228
|
-
"
|
229
|
-
)
|
230
|
-
|
259
|
+
extra_http_headers = self.settings("_extra_http_headers") or json.loads(
|
260
|
+
self._environ.get("WANDB__EXTRA_HTTP_HEADERS", "{}")
|
261
|
+
)
|
262
|
+
proxies = self.settings("_proxies") or json.loads(
|
263
|
+
self._environ.get("WANDB__PROXIES", "{}")
|
231
264
|
)
|
232
265
|
|
233
266
|
auth = None
|
@@ -249,6 +282,7 @@ class Api:
|
|
249
282
|
auth=auth,
|
250
283
|
url=f"{self.settings('base_url')}/graphql",
|
251
284
|
cookies=_thread_local_api_settings.cookies,
|
285
|
+
proxies=proxies,
|
252
286
|
)
|
253
287
|
)
|
254
288
|
|
@@ -267,6 +301,13 @@ class Api:
|
|
267
301
|
self._current_run_id: Optional[str] = None
|
268
302
|
self._file_stream_api = None
|
269
303
|
self._upload_file_session = requests.Session()
|
304
|
+
if self.FILE_PUSHER_TIMEOUT:
|
305
|
+
self._upload_file_session.put = functools.partial( # type: ignore
|
306
|
+
self._upload_file_session.put,
|
307
|
+
timeout=self.FILE_PUSHER_TIMEOUT,
|
308
|
+
)
|
309
|
+
if proxies:
|
310
|
+
self._upload_file_session.proxies.update(proxies)
|
270
311
|
# This Retry class is initialized once for each Api instance, so this
|
271
312
|
# defaults to retrying 1 million times per process or 7 days
|
272
313
|
self.upload_file_retry = normalize_exceptions(
|
@@ -285,6 +326,8 @@ class Api:
|
|
285
326
|
self.mutation_types: Optional[List[str]] = None
|
286
327
|
self.server_info_types: Optional[List[str]] = None
|
287
328
|
self.server_use_artifact_input_info: Optional[List[str]] = None
|
329
|
+
self.server_create_artifact_input_info: Optional[List[str]] = None
|
330
|
+
self.server_artifact_fields_info: Optional[List[str]] = None
|
288
331
|
self._max_cli_version: Optional[str] = None
|
289
332
|
self._server_settings_type: Optional[List[str]] = None
|
290
333
|
self.fail_run_queue_item_input_info: Optional[List[str]] = None
|
@@ -1023,6 +1066,8 @@ class Api:
|
|
1023
1066
|
run (str, optional): The run to download
|
1024
1067
|
entity (str, optional): The entity to scope this project to.
|
1025
1068
|
"""
|
1069
|
+
check_httpclient_logger_handler()
|
1070
|
+
|
1026
1071
|
query = gql(
|
1027
1072
|
"""
|
1028
1073
|
query RunConfigs(
|
@@ -1306,7 +1351,7 @@ class Api:
|
|
1306
1351
|
|
1307
1352
|
@normalize_exceptions
|
1308
1353
|
def create_default_resource_config(
|
1309
|
-
self, entity: str,
|
1354
|
+
self, entity: str, resource: str, config: str
|
1310
1355
|
) -> Optional[Dict[str, Any]]:
|
1311
1356
|
if not self.create_default_resource_config_introspection():
|
1312
1357
|
raise Exception()
|
@@ -1314,14 +1359,12 @@ class Api:
|
|
1314
1359
|
"""
|
1315
1360
|
mutation createDefaultResourceConfig(
|
1316
1361
|
$entityName: String!
|
1317
|
-
$projectName: String
|
1318
1362
|
$resource: String!
|
1319
1363
|
$config: JSONString!
|
1320
1364
|
) {
|
1321
1365
|
createDefaultResourceConfig(
|
1322
1366
|
input: {
|
1323
1367
|
entityName: $entityName
|
1324
|
-
projectName: $projectName
|
1325
1368
|
resource: $resource
|
1326
1369
|
config: $config
|
1327
1370
|
}
|
@@ -1334,7 +1377,6 @@ class Api:
|
|
1334
1377
|
)
|
1335
1378
|
variable_values = {
|
1336
1379
|
"entityName": entity,
|
1337
|
-
"projectName": project,
|
1338
1380
|
"resource": resource,
|
1339
1381
|
"config": config,
|
1340
1382
|
}
|
@@ -1650,6 +1692,7 @@ class Api:
|
|
1650
1692
|
project: str,
|
1651
1693
|
queues: List[str],
|
1652
1694
|
agent_config: Dict[str, Any],
|
1695
|
+
version: str,
|
1653
1696
|
gorilla_agent_support: bool,
|
1654
1697
|
) -> dict:
|
1655
1698
|
project_queues = self.get_project_run_queues(entity, project)
|
@@ -1690,22 +1733,28 @@ class Api:
|
|
1690
1733
|
"hostname": hostname,
|
1691
1734
|
}
|
1692
1735
|
|
1693
|
-
mutation_params = """
|
1694
|
-
|
1695
|
-
|
1696
|
-
|
1736
|
+
mutation_params = """
|
1737
|
+
$entity: String!,
|
1738
|
+
$project: String!,
|
1739
|
+
$queues: [ID!]!,
|
1740
|
+
$hostname: String!
|
1741
|
+
"""
|
1697
1742
|
|
1698
|
-
mutation_input = """
|
1699
|
-
|
1700
|
-
|
1701
|
-
|
1743
|
+
mutation_input = """
|
1744
|
+
entityName: $entity,
|
1745
|
+
projectName: $project,
|
1746
|
+
runQueues: $queues,
|
1747
|
+
hostname: $hostname
|
1748
|
+
"""
|
1702
1749
|
|
1703
1750
|
if "agentConfig" in self.create_launch_agent_fields_introspection():
|
1704
1751
|
variable_values["agentConfig"] = json.dumps(agent_config)
|
1705
|
-
mutation_params += ""
|
1706
|
-
|
1707
|
-
|
1708
|
-
|
1752
|
+
mutation_params += ", $agentConfig: JSONString"
|
1753
|
+
mutation_input += ", agentConfig: $agentConfig"
|
1754
|
+
if "version" in self.create_launch_agent_fields_introspection():
|
1755
|
+
variable_values["version"] = version
|
1756
|
+
mutation_params += ", $version: String"
|
1757
|
+
mutation_input += ", version: $version"
|
1709
1758
|
|
1710
1759
|
mutation = gql(
|
1711
1760
|
f"""
|
@@ -2333,6 +2382,7 @@ class Api:
|
|
2333
2382
|
Returns:
|
2334
2383
|
A tuple of the content length and the streaming response
|
2335
2384
|
"""
|
2385
|
+
check_httpclient_logger_handler()
|
2336
2386
|
auth = None
|
2337
2387
|
if _thread_local_api_settings.cookies is None:
|
2338
2388
|
auth = ("user", self.api_key or "")
|
@@ -2426,10 +2476,15 @@ class Api:
|
|
2426
2476
|
Returns:
|
2427
2477
|
The `requests` library response object
|
2428
2478
|
"""
|
2479
|
+
check_httpclient_logger_handler()
|
2429
2480
|
try:
|
2481
|
+
if env.is_debug(env=self._environ):
|
2482
|
+
logger.debug("upload_file: %s", url)
|
2430
2483
|
response = self._upload_file_session.put(
|
2431
2484
|
url, data=upload_chunk, headers=extra_headers
|
2432
2485
|
)
|
2486
|
+
if env.is_debug(env=self._environ):
|
2487
|
+
logger.debug("upload_file: %s complete", url)
|
2433
2488
|
response.raise_for_status()
|
2434
2489
|
except requests.exceptions.RequestException as e:
|
2435
2490
|
logger.error(f"upload_file exception {url}: {e}")
|
@@ -2476,6 +2531,7 @@ class Api:
|
|
2476
2531
|
Returns:
|
2477
2532
|
The `requests` library response object
|
2478
2533
|
"""
|
2534
|
+
check_httpclient_logger_handler()
|
2479
2535
|
extra_headers = extra_headers.copy() if extra_headers else {}
|
2480
2536
|
response: Optional[requests.Response] = None
|
2481
2537
|
progress = Progress(file, callback=callback)
|
@@ -2488,9 +2544,13 @@ class Api:
|
|
2488
2544
|
"Azure uploads over 256MB require the azure SDK, install with pip install wandb[azure]",
|
2489
2545
|
repeat=False,
|
2490
2546
|
)
|
2547
|
+
if env.is_debug(env=self._environ):
|
2548
|
+
logger.debug("upload_file: %s", url)
|
2491
2549
|
response = self._upload_file_session.put(
|
2492
2550
|
url, data=progress, headers=extra_headers
|
2493
2551
|
)
|
2552
|
+
if env.is_debug(env=self._environ):
|
2553
|
+
logger.debug("upload_file: %s complete", url)
|
2494
2554
|
response.raise_for_status()
|
2495
2555
|
except requests.exceptions.RequestException as e:
|
2496
2556
|
logger.error(f"upload_file exception {url}: {e}")
|
@@ -2505,7 +2565,7 @@ class Api:
|
|
2505
2565
|
and status_code == 400
|
2506
2566
|
and "RequestTimeout" in str(response_content)
|
2507
2567
|
)
|
2508
|
-
# We need to rewind the file for the next retry (the file passed in is
|
2568
|
+
# We need to rewind the file for the next retry (the file passed in is `seek`'ed to 0)
|
2509
2569
|
progress.rewind()
|
2510
2570
|
# Retry errors from cloud storage or local network issues
|
2511
2571
|
if (
|
@@ -2542,6 +2602,7 @@ class Api:
|
|
2542
2602
|
- This method doesn't wrap retryable errors in `TransientError`.
|
2543
2603
|
It leaves that determination to the caller.
|
2544
2604
|
"""
|
2605
|
+
check_httpclient_logger_handler()
|
2545
2606
|
must_delegate = False
|
2546
2607
|
|
2547
2608
|
if httpx is None:
|
@@ -3154,7 +3215,6 @@ class Api:
|
|
3154
3215
|
description
|
3155
3216
|
state
|
3156
3217
|
createdAt
|
3157
|
-
labels
|
3158
3218
|
metadata
|
3159
3219
|
}
|
3160
3220
|
}
|
@@ -3235,6 +3295,127 @@ class Api:
|
|
3235
3295
|
_id: Optional[str] = response["createArtifactType"]["artifactType"]["id"]
|
3236
3296
|
return _id
|
3237
3297
|
|
3298
|
+
def server_artifact_introspection(self) -> List:
|
3299
|
+
query_string = """
|
3300
|
+
query ProbeServerArtifact {
|
3301
|
+
ArtifactInfoType: __type(name:"Artifact") {
|
3302
|
+
fields {
|
3303
|
+
name
|
3304
|
+
}
|
3305
|
+
}
|
3306
|
+
}
|
3307
|
+
"""
|
3308
|
+
|
3309
|
+
if self.server_artifact_fields_info is None:
|
3310
|
+
query = gql(query_string)
|
3311
|
+
res = self.gql(query)
|
3312
|
+
input_fields = res.get("ArtifactInfoType", {}).get("fields", [{}])
|
3313
|
+
self.server_artifact_fields_info = [
|
3314
|
+
field["name"] for field in input_fields if "name" in field
|
3315
|
+
]
|
3316
|
+
|
3317
|
+
return self.server_artifact_fields_info
|
3318
|
+
|
3319
|
+
def server_create_artifact_introspection(self) -> List:
|
3320
|
+
query_string = """
|
3321
|
+
query ProbeServerCreateArtifactInput {
|
3322
|
+
CreateArtifactInputInfoType: __type(name:"CreateArtifactInput") {
|
3323
|
+
inputFields{
|
3324
|
+
name
|
3325
|
+
}
|
3326
|
+
}
|
3327
|
+
}
|
3328
|
+
"""
|
3329
|
+
|
3330
|
+
if self.server_create_artifact_input_info is None:
|
3331
|
+
query = gql(query_string)
|
3332
|
+
res = self.gql(query)
|
3333
|
+
input_fields = res.get("CreateArtifactInputInfoType", {}).get(
|
3334
|
+
"inputFields", [{}]
|
3335
|
+
)
|
3336
|
+
self.server_create_artifact_input_info = [
|
3337
|
+
field["name"] for field in input_fields if "name" in field
|
3338
|
+
]
|
3339
|
+
|
3340
|
+
return self.server_create_artifact_input_info
|
3341
|
+
|
3342
|
+
def _get_create_artifact_mutation(
|
3343
|
+
self,
|
3344
|
+
fields: List,
|
3345
|
+
history_step: Optional[int],
|
3346
|
+
distributed_id: Optional[str],
|
3347
|
+
) -> str:
|
3348
|
+
types = ""
|
3349
|
+
values = ""
|
3350
|
+
|
3351
|
+
if "historyStep" in fields and history_step not in [0, None]:
|
3352
|
+
types += "$historyStep: Int64!,"
|
3353
|
+
values += "historyStep: $historyStep,"
|
3354
|
+
|
3355
|
+
if distributed_id:
|
3356
|
+
types += "$distributedID: String,"
|
3357
|
+
values += "distributedID: $distributedID,"
|
3358
|
+
|
3359
|
+
if "clientID" in fields:
|
3360
|
+
types += "$clientID: ID!,"
|
3361
|
+
values += "clientID: $clientID,"
|
3362
|
+
|
3363
|
+
if "sequenceClientID" in fields:
|
3364
|
+
types += "$sequenceClientID: ID!,"
|
3365
|
+
values += "sequenceClientID: $sequenceClientID,"
|
3366
|
+
|
3367
|
+
if "enableDigestDeduplication" in fields:
|
3368
|
+
values += "enableDigestDeduplication: true,"
|
3369
|
+
|
3370
|
+
if "ttlDurationSeconds" in fields:
|
3371
|
+
types += "$ttlDurationSeconds: Int64,"
|
3372
|
+
values += "ttlDurationSeconds: $ttlDurationSeconds,"
|
3373
|
+
|
3374
|
+
query_template = """
|
3375
|
+
mutation CreateArtifact(
|
3376
|
+
$artifactTypeName: String!,
|
3377
|
+
$artifactCollectionNames: [String!],
|
3378
|
+
$entityName: String!,
|
3379
|
+
$projectName: String!,
|
3380
|
+
$runName: String,
|
3381
|
+
$description: String,
|
3382
|
+
$digest: String!,
|
3383
|
+
$aliases: [ArtifactAliasInput!],
|
3384
|
+
$metadata: JSONString,
|
3385
|
+
_CREATE_ARTIFACT_ADDITIONAL_TYPE_
|
3386
|
+
) {
|
3387
|
+
createArtifact(input: {
|
3388
|
+
artifactTypeName: $artifactTypeName,
|
3389
|
+
artifactCollectionNames: $artifactCollectionNames,
|
3390
|
+
entityName: $entityName,
|
3391
|
+
projectName: $projectName,
|
3392
|
+
runName: $runName,
|
3393
|
+
description: $description,
|
3394
|
+
digest: $digest,
|
3395
|
+
digestAlgorithm: MANIFEST_MD5,
|
3396
|
+
aliases: $aliases,
|
3397
|
+
metadata: $metadata,
|
3398
|
+
_CREATE_ARTIFACT_ADDITIONAL_VALUE_
|
3399
|
+
}) {
|
3400
|
+
artifact {
|
3401
|
+
id
|
3402
|
+
state
|
3403
|
+
artifactSequence {
|
3404
|
+
id
|
3405
|
+
latestArtifact {
|
3406
|
+
id
|
3407
|
+
versionIndex
|
3408
|
+
}
|
3409
|
+
}
|
3410
|
+
}
|
3411
|
+
}
|
3412
|
+
}
|
3413
|
+
"""
|
3414
|
+
|
3415
|
+
return query_template.replace(
|
3416
|
+
"_CREATE_ARTIFACT_ADDITIONAL_TYPE_", types
|
3417
|
+
).replace("_CREATE_ARTIFACT_ADDITIONAL_VALUE_", values)
|
3418
|
+
|
3238
3419
|
def create_artifact(
|
3239
3420
|
self,
|
3240
3421
|
artifact_type_name: str,
|
@@ -3246,104 +3427,23 @@ class Api:
|
|
3246
3427
|
project_name: Optional[str] = None,
|
3247
3428
|
run_name: Optional[str] = None,
|
3248
3429
|
description: Optional[str] = None,
|
3249
|
-
labels: Optional[List[str]] = None,
|
3250
3430
|
metadata: Optional[Dict] = None,
|
3431
|
+
ttl_duration_seconds: Optional[int] = None,
|
3251
3432
|
aliases: Optional[List[Dict[str, str]]] = None,
|
3252
3433
|
distributed_id: Optional[str] = None,
|
3253
3434
|
is_user_created: Optional[bool] = False,
|
3254
|
-
enable_digest_deduplication: Optional[bool] = False,
|
3255
3435
|
history_step: Optional[int] = None,
|
3256
3436
|
) -> Tuple[Dict, Dict]:
|
3257
|
-
|
3258
|
-
|
3259
|
-
|
3260
|
-
|
3261
|
-
|
3262
|
-
)
|
3263
|
-
can_handle_client_id = max_cli_version is None or parse_version(
|
3264
|
-
"0.11.0"
|
3265
|
-
) <= parse_version(max_cli_version)
|
3266
|
-
can_handle_dedupe = max_cli_version is None or parse_version(
|
3267
|
-
"0.12.10"
|
3268
|
-
) <= parse_version(max_cli_version)
|
3269
|
-
can_handle_history = max_cli_version is None or parse_version(
|
3270
|
-
"0.12.12"
|
3271
|
-
) <= parse_version(max_cli_version)
|
3272
|
-
|
3273
|
-
mutation = gql(
|
3274
|
-
"""
|
3275
|
-
mutation CreateArtifact(
|
3276
|
-
$artifactTypeName: String!,
|
3277
|
-
$artifactCollectionNames: [String!],
|
3278
|
-
$entityName: String!,
|
3279
|
-
$projectName: String!,
|
3280
|
-
$runName: String,
|
3281
|
-
$description: String,
|
3282
|
-
$digest: String!,
|
3283
|
-
$labels: JSONString,
|
3284
|
-
$aliases: [ArtifactAliasInput!],
|
3285
|
-
$metadata: JSONString,
|
3286
|
-
{}
|
3287
|
-
{}
|
3288
|
-
{}
|
3289
|
-
{}
|
3290
|
-
{}
|
3291
|
-
) {{
|
3292
|
-
createArtifact(input: {{
|
3293
|
-
artifactTypeName: $artifactTypeName,
|
3294
|
-
artifactCollectionNames: $artifactCollectionNames,
|
3295
|
-
entityName: $entityName,
|
3296
|
-
projectName: $projectName,
|
3297
|
-
runName: $runName,
|
3298
|
-
description: $description,
|
3299
|
-
digest: $digest,
|
3300
|
-
digestAlgorithm: MANIFEST_MD5,
|
3301
|
-
labels: $labels,
|
3302
|
-
aliases: $aliases,
|
3303
|
-
metadata: $metadata,
|
3304
|
-
{}
|
3305
|
-
{}
|
3306
|
-
{}
|
3307
|
-
{}
|
3308
|
-
{}
|
3309
|
-
}}) {{
|
3310
|
-
artifact {{
|
3311
|
-
id
|
3312
|
-
digest
|
3313
|
-
state
|
3314
|
-
aliases {{
|
3315
|
-
artifactCollectionName
|
3316
|
-
alias
|
3317
|
-
}}
|
3318
|
-
artifactSequence {{
|
3319
|
-
id
|
3320
|
-
latestArtifact {{
|
3321
|
-
id
|
3322
|
-
versionIndex
|
3323
|
-
}}
|
3324
|
-
}}
|
3325
|
-
}}
|
3326
|
-
}}
|
3327
|
-
}}
|
3328
|
-
""".format(
|
3329
|
-
"$historyStep: Int64!,"
|
3330
|
-
if can_handle_history and history_step not in [0, None]
|
3331
|
-
else "",
|
3332
|
-
"$distributedID: String," if distributed_id else "",
|
3333
|
-
"$clientID: ID!," if can_handle_client_id else "",
|
3334
|
-
"$sequenceClientID: ID!," if can_handle_client_id else "",
|
3335
|
-
"$enableDigestDeduplication: Boolean," if can_handle_dedupe else "",
|
3336
|
-
# line sep
|
3337
|
-
"historyStep: $historyStep,"
|
3338
|
-
if can_handle_history and history_step not in [0, None]
|
3339
|
-
else "",
|
3340
|
-
"distributedID: $distributedID," if distributed_id else "",
|
3341
|
-
"clientID: $clientID," if can_handle_client_id else "",
|
3342
|
-
"sequenceClientID: $sequenceClientID," if can_handle_client_id else "",
|
3343
|
-
"enableDigestDeduplication: $enableDigestDeduplication,"
|
3344
|
-
if can_handle_dedupe
|
3345
|
-
else "",
|
3437
|
+
fields = self.server_create_artifact_introspection()
|
3438
|
+
artifact_fields = self.server_artifact_introspection()
|
3439
|
+
if "ttlIsInherited" not in artifact_fields and ttl_duration_seconds:
|
3440
|
+
wandb.termwarn(
|
3441
|
+
"Server not compatible with setting Artifact TTLs, please upgrade the server to use Artifact TTL"
|
3346
3442
|
)
|
3443
|
+
# ttlDurationSeconds is only usable if ttlIsInherited is also present
|
3444
|
+
ttl_duration_seconds = None
|
3445
|
+
query_template = self._get_create_artifact_mutation(
|
3446
|
+
fields, history_step, distributed_id
|
3347
3447
|
)
|
3348
3448
|
|
3349
3449
|
entity_name = entity_name or self.settings("entity")
|
@@ -3353,6 +3453,7 @@ class Api:
|
|
3353
3453
|
if aliases is None:
|
3354
3454
|
aliases = []
|
3355
3455
|
|
3456
|
+
mutation = gql(query_template)
|
3356
3457
|
response = self.gql(
|
3357
3458
|
mutation,
|
3358
3459
|
variable_values={
|
@@ -3366,25 +3467,15 @@ class Api:
|
|
3366
3467
|
"digest": digest,
|
3367
3468
|
"description": description,
|
3368
3469
|
"aliases": [alias for alias in aliases],
|
3369
|
-
"labels": json.dumps(util.make_safe_for_json(labels))
|
3370
|
-
if labels
|
3371
|
-
else None,
|
3372
3470
|
"metadata": json.dumps(util.make_safe_for_json(metadata))
|
3373
3471
|
if metadata
|
3374
3472
|
else None,
|
3473
|
+
"ttlDurationSeconds": ttl_duration_seconds,
|
3375
3474
|
"distributedID": distributed_id,
|
3376
|
-
"enableDigestDeduplication": enable_digest_deduplication,
|
3377
3475
|
"historyStep": history_step,
|
3378
3476
|
},
|
3379
3477
|
)
|
3380
3478
|
av = response["createArtifact"]["artifact"]
|
3381
|
-
# TODO: make this a part of the graph
|
3382
|
-
av["version"] = "latest"
|
3383
|
-
for alias in av["aliases"]:
|
3384
|
-
if alias["artifactCollectionName"] == artifact_collection_name and re.match(
|
3385
|
-
r"^v\d+$", alias["alias"]
|
3386
|
-
):
|
3387
|
-
av["version"] = alias["alias"]
|
3388
3479
|
latest = response["createArtifact"]["artifact"]["artifactSequence"].get(
|
3389
3480
|
"latestArtifact"
|
3390
3481
|
)
|
@@ -3778,11 +3869,9 @@ class Api:
|
|
3778
3869
|
assert state in ("RUNNING", "PAUSED", "CANCELED", "FINISHED")
|
3779
3870
|
s = self.sweep(sweep=sweep, entity=entity, project=project, specs="{}")
|
3780
3871
|
curr_state = s["state"].upper()
|
3781
|
-
if state == "
|
3782
|
-
raise Exception("Cannot resume %s sweep." % curr_state.lower())
|
3783
|
-
elif state == "PAUSED" and curr_state not in ("PAUSED", "RUNNING"):
|
3872
|
+
if state == "PAUSED" and curr_state not in ("PAUSED", "RUNNING"):
|
3784
3873
|
raise Exception("Cannot pause %s sweep." % curr_state.lower())
|
3785
|
-
elif curr_state not in ("RUNNING", "PAUSED", "PENDING"):
|
3874
|
+
elif state != "RUNNING" and curr_state not in ("RUNNING", "PAUSED", "PENDING"):
|
3786
3875
|
raise Exception("Sweep already %s." % curr_state.lower())
|
3787
3876
|
sweep_id = s["id"]
|
3788
3877
|
mutation = gql(
|
@@ -3862,6 +3951,7 @@ class Api:
|
|
3862
3951
|
|
3863
3952
|
def _status_request(self, url: str, length: int) -> requests.Response:
|
3864
3953
|
"""Ask google how much we've uploaded."""
|
3954
|
+
check_httpclient_logger_handler()
|
3865
3955
|
return requests.put(
|
3866
3956
|
url=url,
|
3867
3957
|
headers={"Content-Length": "0", "Content-Range": "bytes */%i" % length},
|