Commit c62e9907
Changed files (44)
src
tests
api_resources
beta
chat
completions
containers
evals
fine_tuning
responses
uploads
vector_stores
src/openai/__init__.py
@@ -32,7 +32,7 @@ from ._exceptions import (
APIResponseValidationError,
ContentFilterFinishReasonError,
)
-from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient
+from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient
from ._utils._logs import setup_logging as _setup_logging
from ._legacy_response import HttpxBinaryResponseContent as HttpxBinaryResponseContent
@@ -77,6 +77,7 @@ __all__ = [
"DEFAULT_CONNECTION_LIMITS",
"DefaultHttpxClient",
"DefaultAsyncHttpxClient",
+ "DefaultAioHttpClient",
]
if not _t.TYPE_CHECKING:
src/openai/_base_client.py
@@ -1306,6 +1306,24 @@ class _DefaultAsyncHttpxClient(httpx.AsyncClient):
super().__init__(**kwargs)
+try:
+ import httpx_aiohttp
+except ImportError:
+
+ class _DefaultAioHttpClient(httpx.AsyncClient):
+ def __init__(self, **_kwargs: Any) -> None:
+ raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra")
+else:
+
+ class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore
+ 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
@@ -1314,8 +1332,12 @@ if TYPE_CHECKING:
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.
"""
+
+ DefaultAioHttpClient = httpx.AsyncClient
+ """An alias to `httpx.AsyncClient` that changes the default HTTP transport to `aiohttp`."""
else:
DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient
+ DefaultAioHttpClient = _DefaultAioHttpClient
class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient):
tests/api_resources/audio/test_speech.py
@@ -83,7 +83,9 @@ class TestSpeech:
class TestAsyncSpeech:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
@pytest.mark.respx(base_url=base_url)
tests/api_resources/audio/test_transcriptions.py
@@ -121,7 +121,9 @@ class TestTranscriptions:
class TestAsyncTranscriptions:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/audio/test_translations.py
@@ -64,7 +64,9 @@ class TestTranslations:
class TestAsyncTranslations:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/beta/realtime/test_sessions.py
@@ -90,7 +90,9 @@ class TestSessions:
class TestAsyncSessions:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/beta/realtime/test_transcription_sessions.py
@@ -74,7 +74,9 @@ class TestTranscriptionSessions:
class TestAsyncTranscriptionSessions:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/beta/threads/runs/test_steps.py
@@ -167,7 +167,9 @@ class TestSteps:
class TestAsyncSteps:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/beta/threads/test_messages.py
@@ -321,7 +321,9 @@ class TestMessages:
class TestAsyncMessages:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/beta/threads/test_runs.py
@@ -568,7 +568,9 @@ class TestRuns:
class TestAsyncRuns:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/beta/test_assistants.py
@@ -253,7 +253,9 @@ class TestAssistants:
class TestAsyncAssistants:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/beta/test_realtime.py
@@ -14,4 +14,6 @@ class TestRealtime:
class TestAsyncRealtime:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
tests/api_resources/beta/test_threads.py
@@ -420,7 +420,9 @@ class TestThreads:
class TestAsyncThreads:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/chat/completions/test_messages.py
@@ -68,7 +68,9 @@ class TestMessages:
class TestAsyncMessages:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_list(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/chat/test_completions.py
@@ -447,7 +447,9 @@ class TestCompletions:
class TestAsyncCompletions:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/containers/files/test_content.py
@@ -86,7 +86,9 @@ class TestContent:
class TestAsyncContent:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
@pytest.mark.respx(base_url=base_url)
tests/api_resources/containers/test_files.py
@@ -215,7 +215,9 @@ class TestFiles:
class TestAsyncFiles:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/evals/runs/test_output_items.py
@@ -140,7 +140,9 @@ class TestOutputItems:
class TestAsyncOutputItems:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/evals/test_runs.py
@@ -306,7 +306,9 @@ class TestRuns:
class TestAsyncRuns:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/fine_tuning/alpha/test_graders.py
@@ -151,7 +151,9 @@ class TestGraders:
class TestAsyncGraders:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_run(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/fine_tuning/checkpoints/test_permissions.py
@@ -169,7 +169,9 @@ class TestPermissions:
class TestAsyncPermissions:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/fine_tuning/jobs/test_checkpoints.py
@@ -67,7 +67,9 @@ class TestCheckpoints:
class TestAsyncCheckpoints:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_list(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/fine_tuning/test_jobs.py
@@ -354,7 +354,9 @@ class TestJobs:
class TestAsyncJobs:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/responses/test_input_items.py
@@ -70,7 +70,9 @@ class TestInputItems:
class TestAsyncInputItems:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_list(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/uploads/test_parts.py
@@ -61,7 +61,9 @@ class TestParts:
class TestAsyncParts:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/vector_stores/test_file_batches.py
@@ -232,7 +232,9 @@ class TestFileBatches:
class TestAsyncFileBatches:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/vector_stores/test_files.py
@@ -323,7 +323,9 @@ class TestFiles:
class TestAsyncFiles:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/test_batches.py
@@ -176,7 +176,9 @@ class TestBatches:
class TestAsyncBatches:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/test_completions.py
@@ -137,7 +137,9 @@ class TestCompletions:
class TestAsyncCompletions:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/test_containers.py
@@ -177,7 +177,9 @@ class TestContainers:
class TestAsyncContainers:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/test_embeddings.py
@@ -64,7 +64,9 @@ class TestEmbeddings:
class TestAsyncEmbeddings:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/test_evals.py
@@ -297,7 +297,9 @@ class TestEvals:
class TestAsyncEvals:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/test_files.py
@@ -260,7 +260,9 @@ class TestFiles:
class TestAsyncFiles:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/test_images.py
@@ -163,7 +163,9 @@ class TestImages:
class TestAsyncImages:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create_variation(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/test_models.py
@@ -121,7 +121,9 @@ class TestModels:
class TestAsyncModels:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/test_moderations.py
@@ -58,7 +58,9 @@ class TestModerations:
class TestAsyncModerations:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/test_responses.py
@@ -352,7 +352,9 @@ class TestResponses:
class TestAsyncResponses:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create_overload_1(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/test_uploads.py
@@ -148,7 +148,9 @@ class TestUploads:
class TestAsyncUploads:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/api_resources/test_vector_stores.py
@@ -286,7 +286,9 @@ class TestVectorStores:
class TestAsyncVectorStores:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+ parametrize = pytest.mark.parametrize(
+ "async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
+ )
@parametrize
async def test_method_create(self, async_client: AsyncOpenAI) -> None:
tests/conftest.py
@@ -6,10 +6,12 @@ import os
import logging
from typing import TYPE_CHECKING, Iterator, AsyncIterator
+import httpx
import pytest
from pytest_asyncio import is_async_test
-from openai import OpenAI, AsyncOpenAI
+from openai import OpenAI, AsyncOpenAI, DefaultAioHttpClient
+from openai._utils import is_dict
if TYPE_CHECKING:
from _pytest.fixtures import FixtureRequest # pyright: ignore[reportPrivateImportUsage]
@@ -27,6 +29,19 @@ def pytest_collection_modifyitems(items: list[pytest.Function]) -> None:
for async_test in pytest_asyncio_tests:
async_test.add_marker(session_scope_marker, append=False)
+ # We skip tests that use both the aiohttp client and respx_mock as respx_mock
+ # doesn't support custom transports.
+ for item in items:
+ if "async_client" not in item.fixturenames or "respx_mock" not in item.fixturenames:
+ continue
+
+ if not hasattr(item, "callspec"):
+ continue
+
+ async_client_param = item.callspec.params.get("async_client")
+ if is_dict(async_client_param) and async_client_param.get("http_client") == "aiohttp":
+ item.add_marker(pytest.mark.skip(reason="aiohttp client is not compatible with respx_mock"))
+
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -45,9 +60,25 @@ def client(request: FixtureRequest) -> Iterator[OpenAI]:
@pytest.fixture(scope="session")
async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncOpenAI]:
- strict = getattr(request, "param", True)
- if not isinstance(strict, bool):
- raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}")
-
- async with AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=strict) as client:
+ param = getattr(request, "param", True)
+
+ # defaults
+ strict = True
+ http_client: None | httpx.AsyncClient = None
+
+ if isinstance(param, bool):
+ strict = param
+ elif is_dict(param):
+ strict = param.get("strict", True)
+ assert isinstance(strict, bool)
+
+ http_client_type = param.get("http_client", "httpx")
+ if http_client_type == "aiohttp":
+ http_client = DefaultAioHttpClient()
+ else:
+ raise TypeError(f"Unexpected fixture parameter type {type(param)}, expected bool or dict")
+
+ async with AsyncOpenAI(
+ base_url=base_url, api_key=api_key, _strict_response_validation=strict, http_client=http_client
+ ) as client:
yield client
pyproject.toml
@@ -43,6 +43,7 @@ Repository = "https://github.com/openai/openai-python"
openai = "openai.cli:main"
[project.optional-dependencies]
+aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.6"]
realtime = ["websockets >= 13, < 16"]
datalib = ["numpy >= 1", "pandas >= 1.2.3", "pandas-stubs >= 1.1.0.11"]
voice_helpers = ["sounddevice>=0.5.1", "numpy>=2.0.2"]
README.md
@@ -145,6 +145,45 @@ asyncio.run(main())
Functionality between the synchronous and asynchronous clients is otherwise identical.
+### With aiohttp
+
+By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.
+
+You can enable this by installing `aiohttp`:
+
+```sh
+# install from PyPI
+pip install openai[aiohttp]
+```
+
+Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:
+
+```python
+import os
+import asyncio
+from openai import DefaultAioHttpClient
+from openai import AsyncOpenAI
+
+
+async def main() -> None:
+ async with AsyncOpenAI(
+ api_key=os.environ.get("OPENAI_API_KEY"), # This is the default and can be omitted
+ http_client=DefaultAioHttpClient(),
+ ) as client:
+ chat_completion = await client.chat.completions.create(
+ messages=[
+ {
+ "role": "user",
+ "content": "Say this is a test",
+ }
+ ],
+ model="gpt-4o",
+ )
+
+
+asyncio.run(main())
+```
+
## Streaming responses
We provide support for streaming responses using Server Side Events (SSE).
requirements-dev.lock
@@ -10,6 +10,13 @@
# universal: false
-e file:.
+aiohappyeyeballs==2.6.1
+ # via aiohttp
+aiohttp==3.12.13
+ # via httpx-aiohttp
+ # via openai
+aiosignal==1.3.2
+ # via aiohttp
annotated-types==0.6.0
# via pydantic
anyio==4.1.0
@@ -19,7 +26,10 @@ argcomplete==3.1.2
# via nox
asttokens==2.4.1
# via inline-snapshot
+async-timeout==5.0.1
+ # via aiohttp
attrs==24.2.0
+ # via aiohttp
# via outcome
# via trio
azure-core==1.31.0
@@ -60,18 +70,25 @@ executing==2.1.0
# via inline-snapshot
filelock==3.12.4
# via virtualenv
+frozenlist==1.7.0
+ # via aiohttp
+ # via aiosignal
h11==0.14.0
# via httpcore
httpcore==1.0.2
# via httpx
httpx==0.28.1
+ # via httpx-aiohttp
# via openai
# via respx
+httpx-aiohttp==0.1.6
+ # via openai
idna==3.4
# via anyio
# via httpx
# via requests
# via trio
+ # via yarl
importlib-metadata==7.0.0
iniconfig==2.0.0
# via pytest
@@ -87,6 +104,9 @@ msal==1.31.0
# via msal-extensions
msal-extensions==1.2.0
# via azure-identity
+multidict==6.5.0
+ # via aiohttp
+ # via yarl
mypy==1.14.1
mypy-extensions==1.0.0
# via black
@@ -118,6 +138,9 @@ pluggy==1.5.0
# via pytest
portalocker==2.10.1
# via msal-extensions
+propcache==0.3.2
+ # via aiohttp
+ # via yarl
pycparser==2.22
# via cffi
pydantic==2.10.3
@@ -181,6 +204,7 @@ typing-extensions==4.12.2
# via azure-core
# via azure-identity
# via black
+ # via multidict
# via mypy
# via openai
# via pydantic
@@ -194,5 +218,7 @@ virtualenv==20.24.5
# via nox
websockets==15.0.1
# via openai
+yarl==1.20.1
+ # via aiohttp
zipp==3.17.0
# via importlib-metadata
requirements.lock
@@ -10,11 +10,22 @@
# universal: false
-e file:.
+aiohappyeyeballs==2.6.1
+ # via aiohttp
+aiohttp==3.12.13
+ # via httpx-aiohttp
+ # via openai
+aiosignal==1.3.2
+ # via aiohttp
annotated-types==0.6.0
# via pydantic
anyio==4.1.0
# via httpx
# via openai
+async-timeout==5.0.1
+ # via aiohttp
+attrs==25.3.0
+ # via aiohttp
certifi==2023.7.22
# via httpcore
# via httpx
@@ -24,17 +35,27 @@ distro==1.8.0
# via openai
exceptiongroup==1.2.2
# via anyio
+frozenlist==1.7.0
+ # via aiohttp
+ # via aiosignal
h11==0.14.0
# via httpcore
httpcore==1.0.2
# via httpx
httpx==0.28.1
+ # via httpx-aiohttp
+ # via openai
+httpx-aiohttp==0.1.6
# via openai
idna==3.4
# via anyio
# via httpx
+ # via yarl
jiter==0.6.1
# via openai
+multidict==6.5.0
+ # via aiohttp
+ # via yarl
numpy==2.0.2
# via openai
# via pandas
@@ -43,6 +64,9 @@ pandas==2.2.3
# via openai
pandas-stubs==2.2.2.240807
# via openai
+propcache==0.3.2
+ # via aiohttp
+ # via yarl
pycparser==2.22
# via cffi
pydantic==2.10.3
@@ -65,6 +89,7 @@ tqdm==4.66.5
types-pytz==2024.2.0.20241003
# via pandas-stubs
typing-extensions==4.12.2
+ # via multidict
# via openai
# via pydantic
# via pydantic-core
@@ -72,3 +97,5 @@ tzdata==2024.1
# via pandas
websockets==15.0.1
# via openai
+yarl==1.20.1
+ # via aiohttp