From 35aa8a16ad8bba245a7c2230684ec6d14773c223 Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 8 Apr 2024 12:48:21 +0200 Subject: [PATCH] feat(client): add DefaultHttpxClient and DefaultAsyncHttpxClient (#444) --- README.md | 5 ++-- src/anthropic/__init__.py | 3 +++ src/anthropic/_base_client.py | 44 +++++++++++++++++++++++++++++++++-- src/anthropic/_client.py | 8 +++++-- 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 43cfe8e8..28c0fa0d 100644 --- a/README.md +++ b/README.md @@ -496,13 +496,12 @@ You can directly override the [httpx client](https://www.python-httpx.org/api/#c - Additional [advanced](https://www.python-httpx.org/advanced/#client-instances) functionality ```python -import httpx -from anthropic import Anthropic +from anthropic import Anthropic, DefaultHttpxClient client = Anthropic( # Or use the `ANTHROPIC_BASE_URL` env var base_url="http://my.test.server.example.com:8083", - http_client=httpx.Client( + http_client=DefaultHttpxClient( proxies="http://my.test.proxy.example.com", transport=httpx.HTTPTransport(local_address="0.0.0.0"), ), diff --git a/src/anthropic/__init__.py b/src/anthropic/__init__.py index 7d6a3010..bf3fef38 100644 --- a/src/anthropic/__init__.py +++ b/src/anthropic/__init__.py @@ -40,6 +40,7 @@ UnprocessableEntityError, APIResponseValidationError, ) +from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient from ._utils._logs import setup_logging as _setup_logging __all__ = [ @@ -78,6 +79,8 @@ "DEFAULT_TIMEOUT", "DEFAULT_MAX_RETRIES", "DEFAULT_CONNECTION_LIMITS", + "DefaultHttpxClient", + "DefaultAsyncHttpxClient", "HUMAN_PROMPT", "AI_PROMPT", ] diff --git a/src/anthropic/_base_client.py b/src/anthropic/_base_client.py index d4215673..99cdfc58 100644 --- a/src/anthropic/_base_client.py +++ b/src/anthropic/_base_client.py @@ -716,7 +716,27 @@ def _idempotency_key(self) -> str: return f"stainless-python-retry-{uuid.uuid4()}" -class SyncHttpxClientWrapper(httpx.Client): +class _DefaultHttpxClient(httpx.Client): + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + super().__init__(**kwargs) + + +if TYPE_CHECKING: + DefaultHttpxClient = httpx.Client + """An alias to `httpx.Client` that provides the same defaults that this SDK + uses internally. + + This is useful because overriding the `http_client` with your own instance of + `httpx.Client` will result in httpx's defaults being used, not ours. + """ +else: + DefaultHttpxClient = _DefaultHttpxClient + + +class SyncHttpxClientWrapper(DefaultHttpxClient): def __del__(self) -> None: try: self.close() @@ -1262,7 +1282,27 @@ def get_api_list( return self._request_api_list(model, page, opts) -class AsyncHttpxClientWrapper(httpx.AsyncClient): +class _DefaultAsyncHttpxClient(httpx.AsyncClient): + def __init__(self, **kwargs: Any) -> None: + kwargs.setdefault("timeout", DEFAULT_TIMEOUT) + kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS) + kwargs.setdefault("follow_redirects", True) + super().__init__(**kwargs) + + +if TYPE_CHECKING: + DefaultAsyncHttpxClient = httpx.AsyncClient + """An alias to `httpx.AsyncClient` that provides the same defaults that this SDK + uses internally. + + This is useful because overriding the `http_client` with your own instance of + `httpx.AsyncClient` will result in httpx's defaults being used, not ours. + """ +else: + DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient + + +class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient): def __del__(self) -> None: try: # TODO(someday): support non asyncio runtimes here diff --git a/src/anthropic/_client.py b/src/anthropic/_client.py index d847f018..693e3a96 100644 --- a/src/anthropic/_client.py +++ b/src/anthropic/_client.py @@ -80,7 +80,9 @@ def __init__( max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, - # Configure a custom httpx client. See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. + # Configure a custom httpx client. + # We provide a `DefaultHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. + # See the [httpx documentation](https://www.python-httpx.org/api/#client) for more details. http_client: httpx.Client | None = None, # See httpx documentation for [custom transports](https://www.python-httpx.org/advanced/#custom-transports) transport: Transport | None = None, @@ -342,7 +344,9 @@ def __init__( max_retries: int = DEFAULT_MAX_RETRIES, default_headers: Mapping[str, str] | None = None, default_query: Mapping[str, object] | None = None, - # Configure a custom httpx client. See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details. + # Configure a custom httpx client. + # We provide a `DefaultAsyncHttpxClient` class that you can pass to retain the default values we use for `limits`, `timeout` & `follow_redirects`. + # See the [httpx documentation](https://www.python-httpx.org/api/#asyncclient) for more details. http_client: httpx.AsyncClient | None = None, # See httpx documentation for [custom transports](https://www.python-httpx.org/advanced/#custom-transports) transport: AsyncTransport | None = None,