llm-retry-py
Exponential backoff with full jitter for LLM API calls.
Most retry libraries are framework middleware or async-only. This one is
a small function you wrap around any callable. Sync and async loops
share the same RetryPolicy. Built-in retryable-code sets for Anthropic,
OpenAI, AWS Bedrock, and Google Gemini ship in llm_retry.predicates.
Sibling to the Rust crate
llm-retry.
Install
pip install llm-retry-py
Use
Sync:
from llm_retry import retry, RetryPolicy, predicates
def call_anthropic():
return client.messages.create(model="claude-sonnet-4-5", ...)
resp = retry(
call_anthropic,
policy=RetryPolicy(),
should_retry=lambda e: predicates.is_anthropic_retryable(str(e)),
)
Async (works with any asyncio code, no runtime lock-in beyond asyncio.sleep):
from llm_retry import retry_async, RetryPolicy, predicates
async def call_openai():
return await client.chat.completions.create(...)
resp = await retry_async(
call_openai,
policy=RetryPolicy(),
should_retry=lambda e: predicates.is_openai_retryable(str(e)),
)
Custom predicate (BYO matcher):
import httpx
def should_retry(e: Exception) -> bool:
if isinstance(e, httpx.TimeoutException):
return True
if isinstance(e, httpx.HTTPStatusError):
return predicates.is_http_status_retryable(e.response.status_code)
return False
resp = retry(call, policy=RetryPolicy(), should_retry=should_retry)
RetryPolicy
Defaults: 6 attempts, 500ms base delay, 30s cap, full jitter.
from llm_retry import RetryPolicy, Jitter
policy = RetryPolicy(
max_attempts=8,
base_delay_ms=250,
max_delay_ms=60_000,
jitter=Jitter.FULL, # FULL (default, AWS-recommended), EQUAL, or NONE
)
Backoff for attempt i (0-indexed) is min(base * 2**i, max), then jittered.
| Jitter | Resulting delay |
|---|---|
NONE |
capped |
EQUAL |
capped/2 + uniform(0, capped/2) |
FULL |
uniform(0, capped) (recommended) |
Presets
from llm_retry import predicates
predicates.is_anthropic_retryable("rate_limit_error") # True
predicates.is_openai_retryable("server_error") # True
predicates.is_bedrock_retryable("ThrottlingException") # True
predicates.is_gemini_retryable("RESOURCE_EXHAUSTED") # True
predicates.is_http_status_retryable(503) # True
The underlying lists are public constants you can extend:
from llm_retry.predicates import ANTHROPIC_RETRYABLE, contains_any
my_codes = ANTHROPIC_RETRYABLE + ("my_custom_transient_code",)
should_retry = lambda e: contains_any(str(e), my_codes)
Error type
A retry that does not succeed raises RetryExhausted:
from llm_retry import RetryExhausted
try:
resp = retry(call, policy=RetryPolicy(), should_retry=lambda e: True)
except RetryExhausted as exc:
exc.attempts # how many tries ran
exc.last_error # the final exception raised by `call`
If the predicate returns False for the first error, that error is re-raised unchanged (no wrapping).
What it does NOT do
- No HTTP client. Wrap any callable that raises.
- No circuit breaker. Layer one on top if you want.
- No deadline (
stop after N seconds total). Combine with your ownasyncio.wait_fororsignal.alarm. - No structured logging hooks. Add them in your callable.
License
MIT
Metadata
Release files for llm-retry-py 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| llm_retry_py-0.1.0.tar.gz | 8.9 kB | Without provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| llm_retry_py-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 16.8 kB
Release files / llm_retry_py-0.1.0.tar.gz
| Download URL | llm_retry_py-0.1.0.tar.gz |
|---|---|
| Size | 8.9 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
54f2549812a73ba28b3248f775399721d02e5c01fb7649bbea64121fcc1ba7ca
|
|
BLAKE2b-256 checksum How to use checksums |
e8d080ddd01a6e4946612470c03ebf08584ae8529a74e9ca3dfbebec4daa6593
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.5
|
Release files / llm_retry_py-0.1.0-py3-none-any.whl
| Download URL | llm_retry_py-0.1.0-py3-none-any.whl |
|---|---|
| Size | 7.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
20859fdbce5ad9057cc4bca56fe828cdb8650d4710a932ed86d81985c718fa5a
|
|
BLAKE2b-256 checksum How to use checksums |
dae88ca34f9ef423861818a33c29e49570bbd7363804313cf4983f66937dac44
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.14.5
|
Security for llm-retry-py 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.