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.
Files changed (114) hide show
  1. wandb/__init__.py +5 -1
  2. wandb/apis/public.py +137 -17
  3. wandb/apis/reports/_panels.py +1 -1
  4. wandb/apis/reports/blocks.py +1 -0
  5. wandb/apis/reports/report.py +27 -5
  6. wandb/cli/cli.py +52 -41
  7. wandb/docker/__init__.py +17 -0
  8. wandb/docker/auth.py +1 -1
  9. wandb/env.py +24 -4
  10. wandb/filesync/step_checksum.py +3 -3
  11. wandb/integration/openai/openai.py +3 -0
  12. wandb/integration/ultralytics/__init__.py +9 -0
  13. wandb/integration/ultralytics/bbox_utils.py +196 -0
  14. wandb/integration/ultralytics/callback.py +458 -0
  15. wandb/integration/ultralytics/classification_utils.py +66 -0
  16. wandb/integration/ultralytics/mask_utils.py +141 -0
  17. wandb/integration/ultralytics/pose_utils.py +92 -0
  18. wandb/integration/xgboost/xgboost.py +3 -3
  19. wandb/integration/yolov8/__init__.py +0 -7
  20. wandb/integration/yolov8/yolov8.py +22 -3
  21. wandb/old/settings.py +7 -0
  22. wandb/plot/line_series.py +0 -1
  23. wandb/proto/v3/wandb_internal_pb2.py +353 -300
  24. wandb/proto/v3/wandb_server_pb2.py +37 -41
  25. wandb/proto/v3/wandb_settings_pb2.py +2 -2
  26. wandb/proto/v3/wandb_telemetry_pb2.py +16 -16
  27. wandb/proto/v4/wandb_internal_pb2.py +272 -260
  28. wandb/proto/v4/wandb_server_pb2.py +37 -40
  29. wandb/proto/v4/wandb_settings_pb2.py +2 -2
  30. wandb/proto/v4/wandb_telemetry_pb2.py +16 -16
  31. wandb/proto/wandb_internal_codegen.py +7 -31
  32. wandb/sdk/artifacts/artifact.py +321 -189
  33. wandb/sdk/artifacts/artifact_cache.py +14 -0
  34. wandb/sdk/artifacts/artifact_manifest.py +5 -4
  35. wandb/sdk/artifacts/artifact_manifest_entry.py +37 -9
  36. wandb/sdk/artifacts/artifact_manifests/artifact_manifest_v1.py +1 -9
  37. wandb/sdk/artifacts/artifact_saver.py +13 -50
  38. wandb/sdk/artifacts/artifact_ttl.py +6 -0
  39. wandb/sdk/artifacts/artifacts_cache.py +119 -93
  40. wandb/sdk/artifacts/staging.py +25 -0
  41. wandb/sdk/artifacts/storage_handlers/s3_handler.py +12 -7
  42. wandb/sdk/artifacts/storage_handlers/wb_local_artifact_handler.py +2 -3
  43. wandb/sdk/artifacts/storage_policies/__init__.py +4 -0
  44. wandb/sdk/artifacts/storage_policies/register.py +1 -0
  45. wandb/sdk/artifacts/storage_policies/wandb_storage_policy.py +4 -3
  46. wandb/sdk/artifacts/storage_policy.py +4 -2
  47. wandb/sdk/backend/backend.py +0 -16
  48. wandb/sdk/data_types/image.py +3 -1
  49. wandb/sdk/integration_utils/auto_logging.py +38 -13
  50. wandb/sdk/interface/interface.py +16 -135
  51. wandb/sdk/interface/interface_shared.py +9 -147
  52. wandb/sdk/interface/interface_sock.py +0 -26
  53. wandb/sdk/internal/file_pusher.py +20 -3
  54. wandb/sdk/internal/file_stream.py +3 -1
  55. wandb/sdk/internal/handler.py +53 -70
  56. wandb/sdk/internal/internal_api.py +220 -130
  57. wandb/sdk/internal/job_builder.py +41 -37
  58. wandb/sdk/internal/sender.py +7 -25
  59. wandb/sdk/internal/system/assets/disk.py +144 -11
  60. wandb/sdk/internal/system/system_info.py +6 -2
  61. wandb/sdk/launch/__init__.py +5 -0
  62. wandb/sdk/launch/{launch.py → _launch.py} +53 -54
  63. wandb/sdk/launch/{launch_add.py → _launch_add.py} +34 -31
  64. wandb/sdk/launch/_project_spec.py +13 -2
  65. wandb/sdk/launch/agent/agent.py +103 -59
  66. wandb/sdk/launch/agent/run_queue_item_file_saver.py +6 -4
  67. wandb/sdk/launch/builder/build.py +19 -1
  68. wandb/sdk/launch/builder/docker_builder.py +5 -1
  69. wandb/sdk/launch/builder/kaniko_builder.py +5 -1
  70. wandb/sdk/launch/create_job.py +20 -5
  71. wandb/sdk/launch/loader.py +14 -5
  72. wandb/sdk/launch/runner/abstract.py +0 -2
  73. wandb/sdk/launch/runner/kubernetes_monitor.py +329 -0
  74. wandb/sdk/launch/runner/kubernetes_runner.py +66 -209
  75. wandb/sdk/launch/runner/local_container.py +5 -2
  76. wandb/sdk/launch/runner/local_process.py +4 -1
  77. wandb/sdk/launch/sweeps/scheduler.py +43 -25
  78. wandb/sdk/launch/sweeps/utils.py +5 -3
  79. wandb/sdk/launch/utils.py +3 -1
  80. wandb/sdk/lib/_settings_toposort_generate.py +3 -9
  81. wandb/sdk/lib/_settings_toposort_generated.py +27 -3
  82. wandb/sdk/lib/_wburls_generated.py +1 -0
  83. wandb/sdk/lib/filenames.py +27 -6
  84. wandb/sdk/lib/filesystem.py +181 -7
  85. wandb/sdk/lib/fsm.py +5 -3
  86. wandb/sdk/lib/gql_request.py +3 -0
  87. wandb/sdk/lib/ipython.py +7 -0
  88. wandb/sdk/lib/wburls.py +1 -0
  89. wandb/sdk/service/port_file.py +2 -15
  90. wandb/sdk/service/server.py +7 -55
  91. wandb/sdk/service/service.py +56 -26
  92. wandb/sdk/service/service_base.py +1 -1
  93. wandb/sdk/service/streams.py +11 -5
  94. wandb/sdk/verify/verify.py +2 -2
  95. wandb/sdk/wandb_init.py +8 -2
  96. wandb/sdk/wandb_manager.py +4 -14
  97. wandb/sdk/wandb_run.py +143 -53
  98. wandb/sdk/wandb_settings.py +148 -35
  99. wandb/testing/relay.py +85 -38
  100. wandb/util.py +87 -4
  101. wandb/wandb_torch.py +24 -38
  102. {wandb-0.15.9.dist-info → wandb-0.15.11.dist-info}/METADATA +48 -23
  103. {wandb-0.15.9.dist-info → wandb-0.15.11.dist-info}/RECORD +107 -103
  104. {wandb-0.15.9.dist-info → wandb-0.15.11.dist-info}/WHEEL +1 -1
  105. wandb/proto/v3/wandb_server_pb2_grpc.py +0 -1422
  106. wandb/proto/v4/wandb_server_pb2_grpc.py +0 -1422
  107. wandb/proto/wandb_server_pb2_grpc.py +0 -8
  108. wandb/sdk/artifacts/storage_policies/s3_bucket_policy.py +0 -61
  109. wandb/sdk/interface/interface_grpc.py +0 -460
  110. wandb/sdk/service/server_grpc.py +0 -444
  111. wandb/sdk/service/service_grpc.py +0 -73
  112. {wandb-0.15.9.dist-info → wandb-0.15.11.dist-info}/LICENSE +0 -0
  113. {wandb-0.15.9.dist-info → wandb-0.15.11.dist-info}/entry_points.txt +0 -0
  114. {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(10)
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 this hacky hack after settings refactor is complete
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
- "_extra_http_headers"
229
- ) or wandb.sdk.wandb_settings._str_as_json(
230
- self._environ.get("WANDB__EXTRA_HTTP_HEADERS", {})
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, project: str, resource: str, config: 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 = """$entity: String!,
1694
- $project: String!,
1695
- $queues: [ID!]!,
1696
- $hostname: String!"""
1736
+ mutation_params = """
1737
+ $entity: String!,
1738
+ $project: String!,
1739
+ $queues: [ID!]!,
1740
+ $hostname: String!
1741
+ """
1697
1742
 
1698
- mutation_input = """entityName: $entity,
1699
- projectName: $project,
1700
- runQueues: $queues,
1701
- hostname: $hostname"""
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
- $agentConfig: JSONString"""
1707
- mutation_input += """,
1708
- agentConfig: $agentConfig"""
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 seeked to 0)
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
- from pkg_resources import parse_version
3258
-
3259
- _, server_info = self.viewer_server_info()
3260
- max_cli_version = server_info.get("cliVersionInfo", {}).get(
3261
- "max_cli_version", None
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 == "RUNNING" and curr_state in ("CANCELED", "FINISHED"):
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},