Skip to content

Commit ec08992

Browse files
authored
Use different HTTP clients based on providers (#1242)
1 parent 0ce5c1b commit ec08992

File tree

11 files changed

+42
-20
lines changed

11 files changed

+42
-20
lines changed

pydantic_ai_slim/pydantic_ai/models/__init__.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -431,32 +431,41 @@ def infer_model(model: Model | KnownModelName) -> Model:
431431
raise UserError(f'Unknown model: {model}')
432432

433433

434-
def cached_async_http_client(timeout: int = 600, connect: int = 5) -> httpx.AsyncClient:
435-
"""Cached HTTPX async client so multiple agents and calls can share the same client.
434+
def cached_async_http_client(*, provider: str | None = None, timeout: int = 600, connect: int = 5) -> httpx.AsyncClient:
435+
"""Cached HTTPX async client that creates a separate client for each provider.
436+
437+
The client is cached based on the provider parameter. If provider is None, it's used for non-provider specific
438+
requests (like downloading images). Multiple agents and calls can share the same client when they use the same provider.
436439
437440
There are good reasons why in production you should use a `httpx.AsyncClient` as an async context manager as
438441
described in [encode/httpx#2026](https://github.com/encode/httpx/pull/2026), but when experimenting or showing
439-
examples, it's very useful not to, this allows multiple Agents to use a single client.
442+
examples, it's very useful not to.
440443
441444
The default timeouts match those of OpenAI,
442445
see <https://github.com/openai/openai-python/blob/v1.54.4/src/openai/_constants.py#L9>.
443446
"""
444-
client = _cached_async_http_client(timeout=timeout, connect=connect)
447+
client = _cached_async_http_client(provider=provider, timeout=timeout, connect=connect)
445448
if client.is_closed:
446449
# This happens if the context manager is used, so we need to create a new client.
447450
_cached_async_http_client.cache_clear()
448-
client = _cached_async_http_client(timeout=timeout, connect=connect)
451+
client = _cached_async_http_client(provider=provider, timeout=timeout, connect=connect)
449452
return client
450453

451454

452455
@cache
453-
def _cached_async_http_client(timeout: int = 600, connect: int = 5) -> httpx.AsyncClient:
456+
def _cached_async_http_client(provider: str | None, timeout: int = 600, connect: int = 5) -> httpx.AsyncClient:
454457
return httpx.AsyncClient(
458+
transport=_cached_async_http_transport(),
455459
timeout=httpx.Timeout(timeout=timeout, connect=connect),
456460
headers={'User-Agent': get_user_agent()},
457461
)
458462

459463

464+
@cache
465+
def _cached_async_http_transport() -> httpx.AsyncHTTPTransport:
466+
return httpx.AsyncHTTPTransport()
467+
468+
460469
@cache
461470
def get_user_agent() -> str:
462471
"""Get the user agent string for the HTTP client."""

pydantic_ai_slim/pydantic_ai/providers/anthropic.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,5 @@ def __init__(
7070
if http_client is not None:
7171
self._client = AsyncAnthropic(api_key=api_key, http_client=http_client)
7272
else:
73-
self._client = AsyncAnthropic(api_key=api_key, http_client=cached_async_http_client())
73+
http_client = cached_async_http_client(provider='anthropic')
74+
self._client = AsyncAnthropic(api_key=api_key, http_client=http_client)

pydantic_ai_slim/pydantic_ai/providers/azure.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def __init__(
9797
'Must provide one of the `api_version` argument or the `OPENAI_API_VERSION` environment variable'
9898
)
9999

100-
http_client = http_client or cached_async_http_client()
100+
http_client = http_client or cached_async_http_client(provider='azure')
101101
self._client = AsyncAzureOpenAI(
102102
azure_endpoint=azure_endpoint,
103103
api_key=api_key,

pydantic_ai_slim/pydantic_ai/providers/cohere.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,5 @@ def __init__(
6666
if http_client is not None:
6767
self._client = AsyncClientV2(api_key=api_key, httpx_client=http_client, base_url=base_url)
6868
else:
69-
self._client = AsyncClientV2(
70-
api_key=api_key, httpx_client=cached_async_http_client(), base_url=base_url
71-
)
69+
http_client = cached_async_http_client(provider='cohere')
70+
self._client = AsyncClientV2(api_key=api_key, httpx_client=http_client, base_url=base_url)

pydantic_ai_slim/pydantic_ai/providers/deepseek.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,5 @@ def __init__(
6565
elif http_client is not None:
6666
self._client = AsyncOpenAI(base_url=self.base_url, api_key=api_key, http_client=http_client)
6767
else:
68-
self._client = AsyncOpenAI(base_url=self.base_url, api_key=api_key, http_client=cached_async_http_client())
68+
http_client = cached_async_http_client(provider='deepseek')
69+
self._client = AsyncOpenAI(base_url=self.base_url, api_key=api_key, http_client=http_client)

pydantic_ai_slim/pydantic_ai/providers/google_gla.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ def __init__(self, api_key: str | None = None, http_client: httpx.AsyncClient |
3939
'to use the Google GLA provider.'
4040
)
4141

42-
self._client = http_client or cached_async_http_client()
42+
self._client = http_client or cached_async_http_client(provider='google-gla')
4343
self._client.base_url = self.base_url
4444
# https://cloud.google.com/docs/authentication/api-keys-use#using-with-rest
4545
self._client.headers['X-Goog-Api-Key'] = api_key

pydantic_ai_slim/pydantic_ai/providers/google_vertex.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def __init__(
9797
if service_account_file and service_account_info:
9898
raise ValueError('Only one of `service_account_file` or `service_account_info` can be provided.')
9999

100-
self._client = http_client or cached_async_http_client()
100+
self._client = http_client or cached_async_http_client(provider='google-vertex')
101101
self.service_account_file = service_account_file
102102
self.service_account_info = service_account_info
103103
self.project_id = project_id

pydantic_ai_slim/pydantic_ai/providers/groq.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,5 @@ def __init__(
7171
elif http_client is not None:
7272
self._client = AsyncGroq(base_url=self.base_url, api_key=api_key, http_client=http_client)
7373
else:
74-
self._client = AsyncGroq(
75-
base_url=self.base_url, api_key=api_key, http_client=cached_async_http_client()
76-
)
74+
http_client = cached_async_http_client(provider='groq')
75+
self._client = AsyncGroq(base_url=self.base_url, api_key=api_key, http_client=http_client)

pydantic_ai_slim/pydantic_ai/providers/mistral.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,5 @@ def __init__(
6969
elif http_client is not None:
7070
self._client = Mistral(api_key=api_key, async_client=http_client)
7171
else:
72-
self._client = Mistral(api_key=api_key, async_client=cached_async_http_client())
72+
http_client = cached_async_http_client(provider='mistral')
73+
self._client = Mistral(api_key=api_key, async_client=http_client)

pydantic_ai_slim/pydantic_ai/providers/openai.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,5 @@ def __init__(
6363
elif http_client is not None:
6464
self._client = AsyncOpenAI(base_url=base_url, api_key=api_key, http_client=http_client)
6565
else:
66-
self._client = AsyncOpenAI(base_url=base_url, api_key=api_key, http_client=cached_async_http_client())
66+
http_client = cached_async_http_client(provider='openai')
67+
self._client = AsyncOpenAI(base_url=base_url, api_key=api_key, http_client=http_client)

0 commit comments

Comments
 (0)