From 69cdfc319fff7ebf28cdd13cc6c1761b7d97811d Mon Sep 17 00:00:00 2001 From: Stainless Bot <107565488+stainless-bot@users.noreply.github.com> Date: Mon, 8 Apr 2024 12:50:01 +0200 Subject: [PATCH] feat(client): add DefaultHttpxClient and DefaultAsyncHttpxClient (#1302) --- README.md | 5 ++--- src/openai/__init__.py | 3 +++ src/openai/_base_client.py | 44 ++++++++++++++++++++++++++++++++++++-- src/openai/_client.py | 8 +++++-- 4 files changed, 53 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 5264026dc..f007d9187 100644 --- a/README.md +++ b/README.md @@ -549,13 +549,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 openai import OpenAI +from openai import OpenAI, DefaultHttpxClient client = OpenAI( # Or use the `OPENAI_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/openai/__init__.py b/src/openai/__init__.py index cd05a749d..1daa26f7b 100644 --- a/src/openai/__init__.py +++ b/src/openai/__init__.py @@ -29,6 +29,7 @@ UnprocessableEntityError, APIResponseValidationError, ) +from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient from ._utils._logs import setup_logging as _setup_logging __all__ = [ @@ -67,6 +68,8 @@ "DEFAULT_TIMEOUT", "DEFAULT_MAX_RETRIES", "DEFAULT_CONNECTION_LIMITS", + "DefaultHttpxClient", + "DefaultAsyncHttpxClient", ] from .lib import azure as _azure diff --git a/src/openai/_base_client.py b/src/openai/_base_client.py index 502ed7c7a..0bb284a21 100644 --- a/src/openai/_base_client.py +++ b/src/openai/_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/openai/_client.py b/src/openai/_client.py index 7fe2c9af7..e9169df72 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -74,7 +74,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, # Enable or disable schema validation for data returned by the API. # When enabled an error APIResponseValidationError is raised @@ -272,7 +274,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, # Enable or disable schema validation for data returned by the API. # When enabled an error APIResponseValidationError is raised