Skip to content

Commit 647549e

Browse files
feat: retry HTTP 408/502/503/504 by default and default max_retries to 3
Expands the default retry-on set in uipath.llm_client.utils.retry from {429, 529} to {408, 429, 502, 503, 504, 529} and adds the two new exception classes (UiPathRequestTimeoutError, UiPathBadGatewayError) needed to type 408/502 responses. Raises the default max_retries in UiPathHttpxClient/UiPathHttpxAsyncClient (when left as None) and on UiPathBaseLLMClient from 0 to 3, so every provider (OpenAI, Anthropic in all four flavors, Google, all three LangChain Bedrock variants, Vertex AI, Azure OpenAI, Fireworks, LiteLLM) retries transient failures out of the box. Callers can still opt out by passing max_retries=0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d132c3b commit 647549e

15 files changed

Lines changed: 176 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
All notable changes to `uipath_llm_client` (core package) will be documented in this file.
44

5+
## [1.13.0] - 2026-05-27
6+
7+
### Added
8+
- `UiPathRequestTimeoutError` (HTTP 408) and `UiPathBadGatewayError` (HTTP 502) exception classes. Both are registered in `_STATUS_CODE_TO_EXCEPTION`, re-exported from `uipath.llm_client`, and inherit from `UiPathAPIError` for compatibility with existing handlers.
9+
10+
### Changed
11+
- **Default retry set expanded.** `_DEFAULT_RETRY_ON_EXCEPTIONS` in `uipath.llm_client.utils.retry` now covers `UiPathRequestTimeoutError` (408), `UiPathRateLimitError` (429), `UiPathBadGatewayError` (502), `UiPathServiceUnavailableError` (503), `UiPathGatewayTimeoutError` (504), and `UiPathTooManyRequestsError` (529) — up from `{429, 529}`. Applies to every provider client (`UiPathOpenAI`, `UiPathAnthropic*`, `UiPathGoogle`) since they all share the same `UiPathHttpxClient`-backed retry transport. `Retry-After` / `x-retry-after` headers and exponential backoff with jitter behave as before.
12+
- **`UiPathHttpxClient` / `UiPathHttpxAsyncClient` default `max_retries` raised from `0` to `3`.** Callers that pass `max_retries=None` (or omit it entirely) now get 3 retries by default. Pass `max_retries=0` explicitly to opt out — `max_retries=0` continues to disable retries, so the opt-out path is unchanged.
13+
514
## [1.12.2] - 2026-05-24
615

716
### Changed

packages/uipath_langchain_client/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
All notable changes to `uipath_langchain_client` will be documented in this file.
44

5+
## [1.13.0] - 2026-05-27
6+
7+
### Changed
8+
- **`UiPathBaseLLMClient.max_retries` field default raised from `0` to `3`.** Every LangChain chat and embedding client built on this base (`UiPathChat`, `UiPathChatOpenAI`, `UiPathAzureChatOpenAI`, `UiPathChatAnthropic`, `UiPathChatAnthropicBedrock`, `UiPathChatBedrock`, `UiPathChatBedrockConverse`, `UiPathChatVertexAI`, `UiPathChatFireworks`, `UiPathChatLiteLLM`, plus the matching embeddings classes) now retries failed requests 3 times by default. Pass `max_retries=0` explicitly to disable retries — the opt-out path is unchanged. Combined with the expanded default retry set in `uipath-llm-client` 1.13.0, every LangChain client now retries on HTTP 408, 429, 502, 503, 504, and 529 out of the box.
9+
- Bumped `uipath-llm-client` floor to `>=1.13.0` to pick up the expanded default retry set and the new `UiPathRequestTimeoutError` / `UiPathBadGatewayError` typed exceptions.
10+
511
## [1.12.2] - 2026-05-24
612

713
### Changed

packages/uipath_langchain_client/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ readme = "README.md"
66
requires-python = ">=3.11"
77
dependencies = [
88
"langchain>=1.2.15,<2.0.0",
9-
"uipath-llm-client>=1.12.2,<2.0.0",
9+
"uipath-llm-client>=1.13.0,<2.0.0",
1010
]
1111

1212
[project.optional-dependencies]
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
__title__ = "UiPath LangChain Client"
22
__description__ = "A Python client for interacting with UiPath's LLM services via LangChain."
3-
__version__ = "1.12.2"
3+
__version__ = "1.13.0"

packages/uipath_langchain_client/src/uipath_langchain_client/base_client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,8 +145,8 @@ class UiPathBaseLLMClient(BaseModel, ABC):
145145
description="Client-side request timeout in seconds",
146146
)
147147
max_retries: int = Field(
148-
default=0,
149-
description="Maximum number of retries for failed requests",
148+
default=3,
149+
description="Maximum number of retries for failed requests. Pass 0 to disable retries.",
150150
)
151151
retry_config: RetryConfig | None = Field(
152152
default=None,

src/uipath/llm_client/__init__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,15 @@
3939
from uipath.llm_client.utils.exceptions import (
4040
UiPathAPIError,
4141
UiPathAuthenticationError,
42+
UiPathBadGatewayError,
4243
UiPathBadRequestError,
4344
UiPathConflictError,
4445
UiPathGatewayTimeoutError,
4546
UiPathInternalServerError,
4647
UiPathNotFoundError,
4748
UiPathPermissionDeniedError,
4849
UiPathRateLimitError,
50+
UiPathRequestTimeoutError,
4951
UiPathRequestTooLargeError,
5052
UiPathServiceUnavailableError,
5153
UiPathTooManyRequestsError,
@@ -69,13 +71,15 @@
6971
# Exceptions
7072
"UiPathAPIError",
7173
"UiPathAuthenticationError",
74+
"UiPathBadGatewayError",
7275
"UiPathBadRequestError",
7376
"UiPathConflictError",
7477
"UiPathGatewayTimeoutError",
7578
"UiPathInternalServerError",
7679
"UiPathNotFoundError",
7780
"UiPathPermissionDeniedError",
7881
"UiPathRateLimitError",
82+
"UiPathRequestTimeoutError",
7983
"UiPathRequestTooLargeError",
8084
"UiPathServiceUnavailableError",
8185
"UiPathTooManyRequestsError",
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
__title__ = "UiPath LLM Client"
22
__description__ = "A Python client for interacting with UiPath's LLM services."
3-
__version__ = "1.12.2"
3+
__version__ = "1.13.0"

src/uipath/llm_client/httpx_client.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@
6868
# Sentinel to distinguish "not provided" from an explicit ``None`` / ``False``.
6969
_UNSET: Any = object()
7070

71+
# Default applied when ``max_retries`` is left as ``None``. Callers can still
72+
# opt out by passing ``max_retries=0`` explicitly.
73+
_DEFAULT_MAX_RETRIES: typing.Final[int] = 3
74+
7175

7276
class UiPathHttpxClient(Client):
7377
"""Synchronous HTTP client configured for UiPath LLM services.
@@ -135,8 +139,8 @@ def __init__(
135139
captured_headers: Case-insensitive header name prefixes to capture from
136140
responses. Captured headers are stored in a ContextVar and can be
137141
retrieved with get_captured_response_headers(). Defaults to ("x-uipath-",).
138-
max_retries: Maximum retry attempts for failed requests. Defaults to 0
139-
(retries disabled). Set to a positive integer to enable retries.
142+
max_retries: Maximum retry attempts for failed requests. Defaults to 3
143+
when left as ``None``. Pass ``0`` to disable retries explicitly.
140144
retry_config: Custom retry configuration (backoff, retryable status codes).
141145
logger: Logger instance for request/response logging.
142146
auth: HTTP authentication (same as httpx.Client). Derived from
@@ -193,7 +197,7 @@ def __init__(
193197
# Setup retry transport if not provided
194198
if transport is None:
195199
transport = RetryableHTTPTransport(
196-
max_retries=max_retries if max_retries is not None else 0,
200+
max_retries=max_retries if max_retries is not None else _DEFAULT_MAX_RETRIES,
197201
retry_config=retry_config,
198202
logger=logger,
199203
)
@@ -354,7 +358,7 @@ def __init__(
354358
# Setup retry transport if not provided
355359
if transport is None:
356360
transport = RetryableAsyncHTTPTransport(
357-
max_retries=max_retries if max_retries is not None else 0,
361+
max_retries=max_retries if max_retries is not None else _DEFAULT_MAX_RETRIES,
358362
retry_config=retry_config,
359363
logger=logger,
360364
)

src/uipath/llm_client/utils/exceptions.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,12 @@ class UiPathNotFoundError(UiPathAPIError):
118118
status_code: Literal[404] = 404 # pyright: ignore[reportIncompatibleVariableOverride]
119119

120120

121+
class UiPathRequestTimeoutError(UiPathAPIError):
122+
"""HTTP 408 Request Timeout error."""
123+
124+
status_code: Literal[408] = 408 # pyright: ignore[reportIncompatibleVariableOverride]
125+
126+
121127
class UiPathConflictError(UiPathAPIError):
122128
"""HTTP 409 Conflict error."""
123129

@@ -211,6 +217,12 @@ class UiPathInternalServerError(UiPathAPIError):
211217
status_code: Literal[500] = 500 # pyright: ignore[reportIncompatibleVariableOverride]
212218

213219

220+
class UiPathBadGatewayError(UiPathAPIError):
221+
"""HTTP 502 Bad Gateway error."""
222+
223+
status_code: Literal[502] = 502 # pyright: ignore[reportIncompatibleVariableOverride]
224+
225+
214226
class UiPathServiceUnavailableError(UiPathAPIError):
215227
"""HTTP 503 Service Unavailable error."""
216228

@@ -234,11 +246,13 @@ class UiPathTooManyRequestsError(UiPathAPIError):
234246
401: UiPathAuthenticationError,
235247
403: UiPathPermissionDeniedError,
236248
404: UiPathNotFoundError,
249+
408: UiPathRequestTimeoutError,
237250
409: UiPathConflictError,
238251
413: UiPathRequestTooLargeError,
239252
422: UiPathUnprocessableEntityError,
240253
429: UiPathRateLimitError,
241254
500: UiPathInternalServerError,
255+
502: UiPathBadGatewayError,
242256
503: UiPathServiceUnavailableError,
243257
504: UiPathGatewayTimeoutError,
244258
529: UiPathTooManyRequestsError,
@@ -266,11 +280,13 @@ def raise_for_status() -> Response:
266280
"UiPathAuthenticationError",
267281
"UiPathPermissionDeniedError",
268282
"UiPathNotFoundError",
283+
"UiPathRequestTimeoutError",
269284
"UiPathConflictError",
270285
"UiPathRequestTooLargeError",
271286
"UiPathUnprocessableEntityError",
272287
"UiPathRateLimitError",
273288
"UiPathInternalServerError",
289+
"UiPathBadGatewayError",
274290
"UiPathServiceUnavailableError",
275291
"UiPathGatewayTimeoutError",
276292
"UiPathTooManyRequestsError",

src/uipath/llm_client/utils/retry.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,13 +49,22 @@
4949

5050
from uipath.llm_client.utils.exceptions import (
5151
UiPathAPIError,
52+
UiPathBadGatewayError,
53+
UiPathGatewayTimeoutError,
5254
UiPathRateLimitError,
55+
UiPathRequestTimeoutError,
56+
UiPathServiceUnavailableError,
5357
UiPathTooManyRequestsError,
5458
)
5559

5660
# Default retry configuration values
61+
# Status codes retried by default: 408, 429, 502, 503, 504, 529.
5762
_DEFAULT_RETRY_ON_EXCEPTIONS: tuple[type[Exception], ...] = (
63+
UiPathRequestTimeoutError,
5864
UiPathRateLimitError,
65+
UiPathBadGatewayError,
66+
UiPathServiceUnavailableError,
67+
UiPathGatewayTimeoutError,
5968
UiPathTooManyRequestsError,
6069
)
6170
_DEFAULT_INITIAL_DELAY: float = 2.0
@@ -127,7 +136,7 @@ class RetryConfig(TypedDict):
127136
128137
Attributes:
129138
retry_on_exceptions: Tuple of exception types to retry on.
130-
Defaults to (UiPathRateLimitError,).
139+
Defaults to the typed exceptions for HTTP 408, 429, 502, 503, 504, 529.
131140
initial_delay: Initial delay in seconds before first retry.
132141
Defaults to 2.0.
133142
max_delay: Maximum delay in seconds between retries.

0 commit comments

Comments
 (0)