Skip to content

Commit 08140ba

Browse files
committed
Address feedback
1 parent a5523db commit 08140ba

File tree

5 files changed

+23
-117
lines changed

5 files changed

+23
-117
lines changed

docs/api/retries.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# `pydantic_ai.retries`
2+
3+
::: pydantic_ai.retries

docs/api/tenacity.md

Lines changed: 0 additions & 23 deletions
This file was deleted.

docs/retries.md

Lines changed: 16 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ seamlessly with httpx clients. You can configure retry behavior for any provider
1313
To use the retry transports, you need to install the `tenacity` dependency:
1414

1515
```bash
16-
pip install tenacity
17-
# or if using uv
18-
uv add tenacity
16+
pip/uv-add 'pydantic-ai-slim[tenacity]'
1917
```
2018

2119
## Usage Example
@@ -32,7 +30,7 @@ from tenacity import (
3230
)
3331
from pydantic_ai import Agent
3432
from pydantic_ai.models.openai import OpenAIModel
35-
from pydantic_ai.tenacity import AsyncTenacityTransport, wait_retry_after
33+
from pydantic_ai.retries import AsyncTenacityTransport, wait_retry_after
3634
from pydantic_ai.providers.openai import OpenAIProvider
3735

3836
def create_retrying_client():
@@ -74,7 +72,7 @@ agent = Agent(model)
7472
The `wait_retry_after` function is a smart wait strategy that automatically respects HTTP `Retry-After` headers:
7573

7674
```python {title="wait_strategy_example.py"}
77-
from pydantic_ai.tenacity import wait_retry_after
75+
from pydantic_ai.retries import wait_retry_after
7876
from tenacity import wait_exponential
7977

8078
# Basic usage - respects Retry-After headers, falls back to exponential backoff
@@ -102,7 +100,7 @@ For asynchronous HTTP clients (recommended for most use cases):
102100
```python {title="async_transport_example.py"}
103101
from httpx import AsyncClient
104102
from tenacity import AsyncRetrying, stop_after_attempt
105-
from pydantic_ai.tenacity import AsyncTenacityTransport
103+
from pydantic_ai.retries import AsyncTenacityTransport
106104

107105
# Create the basic components
108106
async_retrying = AsyncRetrying(stop=stop_after_attempt(3), reraise=True)
@@ -128,7 +126,7 @@ For synchronous HTTP clients:
128126
```python {title="sync_transport_example.py"}
129127
from httpx import Client
130128
from tenacity import Retrying, stop_after_attempt
131-
from pydantic_ai.tenacity import TenacityTransport
129+
from pydantic_ai.retries import TenacityTransport
132130

133131
# Create the basic components
134132
retrying = Retrying(stop=stop_after_attempt(3), reraise=True)
@@ -154,7 +152,7 @@ client = Client(transport=transport)
154152
```python {title="rate_limit_handling.py"}
155153
from httpx import AsyncClient, HTTPStatusError
156154
from tenacity import AsyncRetrying, stop_after_attempt, retry_if_exception_type, wait_exponential
157-
from pydantic_ai.tenacity import AsyncTenacityTransport, wait_retry_after
155+
from pydantic_ai.retries import AsyncTenacityTransport, wait_retry_after
158156

159157
def create_rate_limit_client():
160158
"""Create a client that respects Retry-After headers from rate limiting responses."""
@@ -184,7 +182,7 @@ The `wait_retry_after` function automatically detects `Retry-After` headers in 4
184182
```python {title="network_error_handling.py"}
185183
import httpx
186184
from tenacity import AsyncRetrying, retry_if_exception_type, wait_exponential, stop_after_attempt
187-
from pydantic_ai.tenacity import AsyncTenacityTransport
185+
from pydantic_ai.retries import AsyncTenacityTransport
188186

189187
def create_network_resilient_client():
190188
"""Create a client that handles network errors with retries."""
@@ -212,7 +210,7 @@ client = create_network_resilient_client()
212210
```python {title="custom_retry_logic.py"}
213211
import httpx
214212
from tenacity import AsyncRetrying, wait_exponential, stop_after_attempt
215-
from pydantic_ai.tenacity import AsyncTenacityTransport, wait_retry_after
213+
from pydantic_ai.retries import AsyncTenacityTransport, wait_retry_after
216214

217215
def create_custom_retry_client():
218216
"""Create a client with custom retry logic."""
@@ -249,30 +247,12 @@ The retry transports work with any provider that accepts a custom HTTP client:
249247

250248
### OpenAI
251249

252-
```python {title="openai_with_retries.py"}
253-
from httpx import AsyncClient, HTTPStatusError
254-
from tenacity import AsyncRetrying, stop_after_attempt, retry_if_exception_type
250+
```python {title="openai_with_retries.py" requires="smart_retry_example.py"}
255251
from pydantic_ai import Agent
256252
from pydantic_ai.models.openai import OpenAIModel
257253
from pydantic_ai.providers.openai import OpenAIProvider
258-
from pydantic_ai.tenacity import AsyncTenacityTransport, wait_retry_after
259254

260-
def create_retrying_client():
261-
"""Create a client with retry functionality."""
262-
def should_retry_status(response):
263-
if response.status_code in (429, 502, 503, 504):
264-
response.raise_for_status()
265-
266-
transport = AsyncTenacityTransport(
267-
controller=AsyncRetrying(
268-
retry=retry_if_exception_type(HTTPStatusError),
269-
wait=wait_retry_after(max_wait=300),
270-
stop=stop_after_attempt(5),
271-
reraise=True
272-
),
273-
validate_response=should_retry_status
274-
)
275-
return AsyncClient(transport=transport)
255+
from smart_retry_example import create_retrying_client
276256

277257
client = create_retrying_client()
278258
model = OpenAIModel('gpt-4o', provider=OpenAIProvider(http_client=client))
@@ -281,30 +261,12 @@ agent = Agent(model)
281261

282262
### Anthropic
283263

284-
```python {title="anthropic_with_retries.py"}
285-
from httpx import AsyncClient, HTTPStatusError
286-
from tenacity import AsyncRetrying, stop_after_attempt, retry_if_exception_type
264+
```python {title="anthropic_with_retries.py" requires="smart_retry_example.py"}
287265
from pydantic_ai import Agent
288266
from pydantic_ai.models.anthropic import AnthropicModel
289267
from pydantic_ai.providers.anthropic import AnthropicProvider
290-
from pydantic_ai.tenacity import AsyncTenacityTransport, wait_retry_after
291268

292-
def create_retrying_client():
293-
"""Create a client with retry functionality."""
294-
def should_retry_status(response):
295-
if response.status_code in (429, 502, 503, 504):
296-
response.raise_for_status()
297-
298-
transport = AsyncTenacityTransport(
299-
controller=AsyncRetrying(
300-
retry=retry_if_exception_type(HTTPStatusError),
301-
wait=wait_retry_after(max_wait=300),
302-
stop=stop_after_attempt(5),
303-
reraise=True
304-
),
305-
validate_response=should_retry_status
306-
)
307-
return AsyncClient(transport=transport)
269+
from smart_retry_example import create_retrying_client
308270

309271
client = create_retrying_client()
310272
model = AnthropicModel('claude-3-5-sonnet-20241022', provider=AnthropicProvider(http_client=client))
@@ -313,30 +275,12 @@ agent = Agent(model)
313275

314276
### Any OpenAI-Compatible Provider
315277

316-
```python {title="openai_compatible_with_retries.py"}
317-
from httpx import AsyncClient, HTTPStatusError
318-
from tenacity import AsyncRetrying, stop_after_attempt, retry_if_exception_type
278+
```python {title="openai_compatible_with_retries.py" requires="smart_retry_example.py"}
319279
from pydantic_ai import Agent
320280
from pydantic_ai.models.openai import OpenAIModel
321281
from pydantic_ai.providers.openai import OpenAIProvider
322-
from pydantic_ai.tenacity import AsyncTenacityTransport, wait_retry_after
323282

324-
def create_retrying_client():
325-
"""Create a client with retry functionality."""
326-
def should_retry_status(response):
327-
if response.status_code in (429, 502, 503, 504):
328-
response.raise_for_status()
329-
330-
transport = AsyncTenacityTransport(
331-
controller=AsyncRetrying(
332-
retry=retry_if_exception_type(HTTPStatusError),
333-
wait=wait_retry_after(max_wait=300),
334-
stop=stop_after_attempt(5),
335-
reraise=True
336-
),
337-
validate_response=should_retry_status
338-
)
339-
return AsyncClient(transport=transport)
283+
from smart_retry_example import create_retrying_client
340284

341285
client = create_retrying_client()
342286
model = OpenAIModel(
@@ -368,30 +312,12 @@ agent = Agent(model)
368312

369313
The retry transports will re-raise the last exception if all retry attempts fail. Make sure to handle these appropriately in your application:
370314

371-
```python {title="error_handling_example.py"}
372-
from httpx import AsyncClient, HTTPStatusError
373-
from tenacity import AsyncRetrying, stop_after_attempt, retry_if_exception_type
315+
```python {title="error_handling_example.py" requires="smart_retry_example.py"}
374316
from pydantic_ai import Agent
375317
from pydantic_ai.models.openai import OpenAIModel
376318
from pydantic_ai.providers.openai import OpenAIProvider
377-
from pydantic_ai.tenacity import AsyncTenacityTransport, wait_retry_after
378319

379-
def create_retrying_client():
380-
"""Create a client with retry functionality."""
381-
def should_retry_status(response):
382-
if response.status_code in (429, 502, 503, 504):
383-
response.raise_for_status()
384-
385-
transport = AsyncTenacityTransport(
386-
controller=AsyncRetrying(
387-
retry=retry_if_exception_type(HTTPStatusError),
388-
wait=wait_retry_after(max_wait=300),
389-
stop=stop_after_attempt(3), # Low for demonstration
390-
reraise=True
391-
),
392-
validate_response=should_retry_status
393-
)
394-
return AsyncClient(transport=transport)
320+
from smart_retry_example import create_retrying_client
395321

396322
client = create_retrying_client()
397323
model = OpenAIModel('gpt-4o', provider=OpenAIProvider(http_client=client))

pydantic_ai_slim/pydantic_ai/tenacity.py renamed to pydantic_ai_slim/pydantic_ai/retries.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ class TenacityTransport(BaseTransport):
5858
```python
5959
from httpx import Client, HTTPTransport, HTTPStatusError
6060
from tenacity import Retrying, stop_after_attempt, retry_if_exception_type
61-
from pydantic_ai.tenacity import TenacityTransport, wait_retry_after
61+
from pydantic_ai.retries import TenacityTransport, wait_retry_after
6262
6363
transport = TenacityTransport(
6464
HTTPTransport(),
@@ -130,7 +130,7 @@ class AsyncTenacityTransport(AsyncBaseTransport):
130130
```python
131131
from httpx import AsyncClient, HTTPStatusError
132132
from tenacity import AsyncRetrying, stop_after_attempt, retry_if_exception_type
133-
from pydantic_ai.tenacity import AsyncTenacityTransport, wait_retry_after
133+
from pydantic_ai.retries import AsyncTenacityTransport, wait_retry_after
134134
135135
transport = AsyncTenacityTransport(
136136
AsyncRetrying(
@@ -203,7 +203,7 @@ def wait_retry_after(
203203
```python
204204
from httpx import AsyncClient, HTTPStatusError
205205
from tenacity import AsyncRetrying, stop_after_attempt, retry_if_exception_type
206-
from pydantic_ai.tenacity import AsyncTenacityTransport, wait_retry_after
206+
from pydantic_ai.retries import AsyncTenacityTransport, wait_retry_after
207207
208208
transport = AsyncTenacityTransport(
209209
AsyncRetrying(

tests/test_tenacity.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
wait_fixed,
2222
)
2323

24-
from pydantic_ai.tenacity import AsyncTenacityTransport, TenacityTransport, wait_retry_after
24+
from pydantic_ai.retries import AsyncTenacityTransport, TenacityTransport, wait_retry_after
2525

2626
pytestmark = pytest.mark.skipif(not imports_successful(), reason='install tenacity to run tenacity tests')
2727

0 commit comments

Comments
 (0)