unique-sdk 2026.44.0.dev14__tar.gz → 2026.46.0.dev0__tar.gz

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 (92) hide show
  1. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/PKG-INFO +1 -1
  2. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/pyproject.toml +1 -1
  3. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/__init__.py +1 -0
  4. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/_api_requestor.py +45 -20
  5. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/_error.py +18 -5
  6. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/_util.py +50 -32
  7. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/README.md +0 -0
  8. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/_api_resource.py +0 -0
  9. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/_api_version.py +0 -0
  10. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/_http_client.py +0 -0
  11. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/_list_object.py +0 -0
  12. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/_object_classes.py +0 -0
  13. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/_request_options.py +0 -0
  14. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/_unique_object.py +0 -0
  15. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/_unique_ql.py +0 -0
  16. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/_unique_response.py +0 -0
  17. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/_version.py +0 -0
  18. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/_webhook.py +0 -0
  19. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/__init__.py +0 -0
  20. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_acronyms.py +0 -0
  21. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_agentic_table.py +0 -0
  22. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_analytics_order.py +0 -0
  23. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_benchmarking.py +0 -0
  24. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_briefing.py +0 -0
  25. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_chat_completion.py +0 -0
  26. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_content.py +0 -0
  27. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_context_memory.py +0 -0
  28. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_elicitation.py +0 -0
  29. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_embedding.py +0 -0
  30. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_event.py +0 -0
  31. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_folder.py +0 -0
  32. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_group.py +0 -0
  33. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_integrated.py +0 -0
  34. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_llm_models.py +0 -0
  35. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_mcp.py +0 -0
  36. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_message.py +0 -0
  37. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_message_assessment.py +0 -0
  38. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_message_execution.py +0 -0
  39. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_message_log.py +0 -0
  40. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_message_tool.py +0 -0
  41. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_module.py +0 -0
  42. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_scheduled_task.py +0 -0
  43. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_search.py +0 -0
  44. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_search_string.py +0 -0
  45. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_short_term_memory.py +0 -0
  46. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_space.py +0 -0
  47. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_user.py +0 -0
  48. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/api_resources/_web_search.py +0 -0
  49. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/__init__.py +0 -0
  50. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/__main__.py +0 -0
  51. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/cli.py +0 -0
  52. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/commands/__init__.py +0 -0
  53. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/commands/_citation_manifest.py +0 -0
  54. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/commands/agentic_table.py +0 -0
  55. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/commands/agentic_table_write.py +0 -0
  56. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/commands/browser.py +0 -0
  57. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/commands/cite_file.py +0 -0
  58. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/commands/elicitation.py +0 -0
  59. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/commands/files.py +0 -0
  60. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/commands/folders.py +0 -0
  61. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/commands/mcp.py +0 -0
  62. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/commands/navigation.py +0 -0
  63. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/commands/read.py +0 -0
  64. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/commands/scheduled_tasks.py +0 -0
  65. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/commands/search.py +0 -0
  66. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/commands/subagent.py +0 -0
  67. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/commands/web_search.py +0 -0
  68. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/commands/web_search_config.py +0 -0
  69. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/config.py +0 -0
  70. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/formatting.py +0 -0
  71. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/identity.py +0 -0
  72. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/metadata_filter.py +0 -0
  73. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/paths.py +0 -0
  74. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/shell.py +0 -0
  75. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/skills/unique-cli-agentic-table/SKILL.md +0 -0
  76. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/skills/unique-cli-elicitation/SKILL.md +0 -0
  77. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/skills/unique-cli-file-management/SKILL.md +0 -0
  78. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/skills/unique-cli-mcp/SKILL.md +0 -0
  79. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/skills/unique-cli-scheduled-tasks/SKILL.md +0 -0
  80. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/skills/unique-cli-search/SKILL.md +0 -0
  81. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/skills/unique-cli-subagent/SKILL.md +0 -0
  82. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/skills/unique-cli-uploaded-search/SKILL.md +0 -0
  83. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/skills/unique-cli-web-search/SKILL.md +0 -0
  84. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/state.py +0 -0
  85. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/cli/workspace.py +0 -0
  86. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/utils/analytics_order_run.py +0 -0
  87. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/utils/benchmarking_run.py +0 -0
  88. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/utils/chat_history.py +0 -0
  89. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/utils/chat_in_space.py +0 -0
  90. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/utils/file_io.py +0 -0
  91. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/utils/sources.py +0 -0
  92. {unique_sdk-2026.44.0.dev14 → unique_sdk-2026.46.0.dev0}/unique_sdk/utils/token.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: unique-sdk
3
- Version: 2026.44.0.dev14
3
+ Version: 2026.46.0.dev0
4
4
  Summary:
5
5
  Author: Martin Fadler, Konstantin Krauss, Andreas Hauri
6
6
  Author-email: Martin Fadler <martin.fadler@unique.ch>, Konstantin Krauss <konstantin@unique.ch>, Andreas Hauri <andreas@unique.ch>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "unique_sdk"
3
- version = "2026.44.0.dev14"
3
+ version = "2026.46.0.dev0"
4
4
  description = ""
5
5
  readme = "README.md"
6
6
  license = { text = "MIT" }
@@ -34,6 +34,7 @@ from unique_sdk._error import APIConnectionError as APIConnectionError
34
34
  from unique_sdk._error import APIError as APIError
35
35
  from unique_sdk._error import AuthenticationError as AuthenticationError
36
36
  from unique_sdk._error import InvalidRequestError as InvalidRequestError
37
+ from unique_sdk._error import RateLimitError as RateLimitError
37
38
  from unique_sdk._error import (
38
39
  SignatureVerificationError as SignatureVerificationError,
39
40
  )
@@ -392,7 +392,15 @@ class APIRequestor(object):
392
392
  cause = error_data.get("cause", {})
393
393
  if isinstance(cause, str):
394
394
  cause = {"error": {"message": cause}, "status": rcode}
395
- status = cause.get("status", rcode)
395
+
396
+ cause_error = cause.get("error") or {}
397
+ cause_code = cause_error.get("code")
398
+ cause_message = cause_error.get("message")
399
+
400
+ message = error_data.get("message") or cause_message
401
+ code = error_data.get("code") or cause_code
402
+ params = error_data.get("params") or cause_error.get("params")
403
+ status = rcode if error_data.get("code") else cause.get("status", rcode)
396
404
 
397
405
  _util.log_info(
398
406
  "Unique error received",
@@ -402,46 +410,63 @@ class APIRequestor(object):
402
410
  error_params=_util.redacted_error_params(error_data.get("params")),
403
411
  )
404
412
 
405
- error = cause.get("error", {}) if cause else {}
406
- error_code = error.get("code", "<Unknown code>")
407
- error_message = error.get("message", "<No message>")
408
- original_error = f"{error_code}: {error_message}"
413
+ original_error = None
414
+ if cause_code or cause_message:
415
+ original_error = (
416
+ f"{cause_code or '<Unknown code>'}: {cause_message or '<No message>'}"
417
+ )
409
418
 
410
- if status in [400, 404]:
411
- return _error.InvalidRequestError(
412
- message=error_data.get("message"),
413
- params=error_data.get("params"),
414
- code=error_data.get("code"),
419
+ if status == 429:
420
+ return _error.RateLimitError(
421
+ message=message,
422
+ params=params,
423
+ code=code,
415
424
  http_body=rbody,
416
425
  http_status=rcode,
417
426
  json_body=resp,
418
427
  headers=rheaders,
419
428
  original_error=original_error,
420
429
  )
421
- elif status == 401:
422
- return _error.AuthenticationError(
423
- message=error_data.get("message"),
430
+ if status in [400, 404]:
431
+ return _error.InvalidRequestError(
432
+ message=message,
433
+ params=params,
434
+ code=code,
424
435
  http_body=rbody,
425
- http_status=status,
436
+ http_status=rcode,
426
437
  json_body=resp,
427
438
  headers=rheaders,
428
439
  original_error=original_error,
429
440
  )
430
- elif status == 403:
431
- return _error.PermissionError(
432
- message=error_data.get("message"),
441
+ if status == 401:
442
+ return _error.AuthenticationError(
443
+ message=message,
433
444
  http_body=rbody,
434
445
  http_status=status,
435
446
  json_body=resp,
436
447
  headers=rheaders,
448
+ code=code,
437
449
  original_error=original_error,
450
+ params=params,
438
451
  )
439
- else:
440
- return _error.APIError(
441
- message=error_data.get("message"),
452
+ if status == 403:
453
+ return _error.PermissionError(
454
+ message=message,
442
455
  http_body=rbody,
443
456
  http_status=status,
444
457
  json_body=resp,
445
458
  headers=rheaders,
459
+ code=code,
446
460
  original_error=original_error,
461
+ params=params,
447
462
  )
463
+ return _error.APIError(
464
+ message=message,
465
+ http_body=rbody,
466
+ http_status=status,
467
+ json_body=resp,
468
+ headers=rheaders,
469
+ code=code,
470
+ original_error=original_error,
471
+ params=params,
472
+ )
@@ -1,4 +1,4 @@
1
- from typing import cast
1
+ from typing import Any, cast
2
2
 
3
3
 
4
4
  class UniqueError(Exception):
@@ -10,6 +10,7 @@ class UniqueError(Exception):
10
10
  code: str | None
11
11
  request_id: str | None
12
12
  original_error: Exception | str | None
13
+ params: dict[str, Any] | str | None
13
14
 
14
15
  def __init__(
15
16
  self,
@@ -20,6 +21,7 @@ class UniqueError(Exception):
20
21
  headers: dict[str, str] | None = None,
21
22
  code: str | None = None,
22
23
  original_error: Exception | str | None = None,
24
+ params: dict[str, Any] | str | None = None,
23
25
  ):
24
26
  super(UniqueError, self).__init__(message)
25
27
 
@@ -41,6 +43,7 @@ class UniqueError(Exception):
41
43
  self.code = code
42
44
  self.request_id = self.headers.get("request-id", None)
43
45
  self.original_error = original_error
46
+ self.params = params
44
47
 
45
48
  def __str__(self):
46
49
  msg = self._message or "<empty message>"
@@ -51,7 +54,7 @@ class UniqueError(Exception):
51
54
 
52
55
  class UniqueErrorWithParamsCode(UniqueError):
53
56
  # str included: InvalidRequestError callers pass plain strings (e.g. "id")
54
- params: dict[str, str] | str | None
57
+ params: dict[str, Any] | str | None
55
58
 
56
59
  def __init__(
57
60
  self,
@@ -62,12 +65,18 @@ class UniqueErrorWithParamsCode(UniqueError):
62
65
  headers: dict[str, str] | None = None,
63
66
  code: str | None = None,
64
67
  original_error: Exception | str | None = None,
65
- params: dict[str, str] | str | None = None,
68
+ params: dict[str, Any] | str | None = None,
66
69
  ):
67
70
  super().__init__(
68
- message, http_body, http_status, json_body, headers, code, original_error
71
+ message,
72
+ http_body,
73
+ http_status,
74
+ json_body,
75
+ headers,
76
+ code,
77
+ original_error,
78
+ params,
69
79
  )
70
- self.params = params
71
80
 
72
81
  def __repr__(self):
73
82
  return "%s(message=%r, params=%r, code=%r, http_status=%r, request_id=%r)" % (
@@ -112,6 +121,10 @@ class PermissionError(UniqueError):
112
121
  pass
113
122
 
114
123
 
124
+ class RateLimitError(APIError):
125
+ pass
126
+
127
+
115
128
  class InvalidRequestError(UniqueErrorWithParamsCode):
116
129
  def __init__(
117
130
  self,
@@ -333,6 +333,35 @@ class class_method_variant(object):
333
333
  return _wrapper
334
334
 
335
335
 
336
+ def _should_retry_error(
337
+ error: UniqueError,
338
+ error_messages: list[str],
339
+ should_retry_5xx: bool,
340
+ ) -> bool:
341
+ status = error.http_status
342
+ if status is not None and 400 <= status < 500:
343
+ return False
344
+ if isinstance(error.params, Mapping) and error.params.get("retryable") is False:
345
+ return False
346
+ if should_retry_5xx and status is not None and 500 <= status < 600:
347
+ return True
348
+ return any(err_msg.lower() in str(error).lower() for err_msg in error_messages)
349
+
350
+
351
+ def _log_retry(error: UniqueError, attempt: int, max_retries: int) -> None:
352
+ # str(error) can carry upstream provider text via original_error.
353
+ params = error.params if isinstance(error.params, Mapping) else {}
354
+ logger.warning(
355
+ "Retrying Unique API call, attempt %d of %d: "
356
+ "code=%s http_status=%s correlation_id=%s",
357
+ attempt + 1,
358
+ max_retries,
359
+ error.code,
360
+ error.http_status,
361
+ params.get("correlationId"),
362
+ )
363
+
364
+
336
365
  def retry_on_error(
337
366
  error_messages: list[str],
338
367
  max_retries: int = 3,
@@ -341,6 +370,13 @@ def retry_on_error(
341
370
  error_class=APIError,
342
371
  should_retry_5xx=False,
343
372
  ):
373
+ """Retry ``APIError`` and ``APIConnectionError``.
374
+
375
+ 4xx responses and errors with ``params.retryable`` false are not retried.
376
+ After the last attempt ``error_class`` is raised with the last error as
377
+ its ``__cause__``.
378
+ """
379
+
344
380
  def decorator(func: Callable[..., Any]) -> Callable[..., Any]:
345
381
  @wraps(func)
346
382
  async def async_wrapper(*args, **kwargs) -> Any:
@@ -350,25 +386,16 @@ def retry_on_error(
350
386
  return await func(*args, **kwargs)
351
387
  except (APIError, APIConnectionError) as e:
352
388
  e = cast(UniqueError, e)
353
- logger.error(f"Retrying because of {e}")
354
- should_retry = any(
355
- err_msg.lower() in str(e).lower() for err_msg in error_messages
356
- )
357
- # Add 5xx check if `should_retry_5xx` is True
358
- has_valid_status = (
359
- hasattr(e, "http_status") and e.http_status is not None
360
- )
361
- if should_retry_5xx and has_valid_status:
362
- should_retry = should_retry or (
363
- 500 <= int(cast(int, e.http_status)) < 600
364
- )
365
-
366
- if not should_retry:
367
- raise e # Raise the error if no retry condition is met
389
+ if not _should_retry_error(e, error_messages, should_retry_5xx):
390
+ raise
368
391
 
369
392
  attempts += 1
370
393
  if attempts >= max_retries:
371
- raise error_class(f"Failed after {max_retries} attempts: {e}")
394
+ raise error_class(
395
+ f"Failed after {max_retries} attempts: {e}"
396
+ ) from e
397
+
398
+ _log_retry(e, attempts, max_retries)
372
399
 
373
400
  # Calculate exponential backoff with some randomness (jitter)
374
401
  delay = initial_delay * (backoff_factor ** (attempts - 1))
@@ -382,25 +409,16 @@ def retry_on_error(
382
409
  return func(*args, **kwargs)
383
410
  except (APIError, APIConnectionError) as e:
384
411
  e = cast(UniqueError, e)
385
- logger.error(f"Retrying because of {e}")
386
- should_retry = any(
387
- err_msg.lower() in str(e).lower() for err_msg in error_messages
388
- )
389
- # Add 5xx check if `should_retry_5xx` is True
390
- has_valid_status = (
391
- hasattr(e, "http_status") and e.http_status is not None
392
- )
393
- if should_retry_5xx and has_valid_status:
394
- should_retry = should_retry or (
395
- 500 <= int(cast(int, e.http_status)) < 600
396
- )
397
-
398
- if not should_retry:
399
- raise e # Raise the error if no retry condition is met
412
+ if not _should_retry_error(e, error_messages, should_retry_5xx):
413
+ raise
400
414
 
401
415
  attempts += 1
402
416
  if attempts >= max_retries:
403
- raise error_class(f"Failed after {max_retries} attempts: {e}")
417
+ raise error_class(
418
+ f"Failed after {max_retries} attempts: {e}"
419
+ ) from e
420
+
421
+ _log_retry(e, attempts, max_retries)
404
422
 
405
423
  # Calculate exponential backoff with some randomness (jitter)
406
424
  delay = initial_delay * (backoff_factor ** (attempts - 1))