Skip to content

Commit 977528d

Browse files
feat(client): add support for aiohttp
1 parent ad5d7c0 commit 977528d

18 files changed

+200
-18
lines changed

README.md

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,56 @@ asyncio.run(main())
100100

101101
Functionality between the synchronous and asynchronous clients is otherwise identical.
102102

103+
### With aiohttp
104+
105+
By default, the async client uses `httpx` for HTTP requests. However, for improved concurrency performance you may also use `aiohttp` as the HTTP backend.
106+
107+
You can enable this by installing `aiohttp`:
108+
109+
```sh
110+
# install from PyPI
111+
pip install --pre openlayer[aiohttp]
112+
```
113+
114+
Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`:
115+
116+
```python
117+
import os
118+
import asyncio
119+
from openlayer import DefaultAioHttpClient
120+
from openlayer import AsyncOpenlayer
121+
122+
123+
async def main() -> None:
124+
async with AsyncOpenlayer(
125+
api_key=os.environ.get("OPENLAYER_API_KEY"), # This is the default and can be omitted
126+
http_client=DefaultAioHttpClient(),
127+
) as client:
128+
response = await client.inference_pipelines.data.stream(
129+
inference_pipeline_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
130+
config={
131+
"input_variable_names": ["user_query"],
132+
"output_column_name": "output",
133+
"num_of_token_column_name": "tokens",
134+
"cost_column_name": "cost",
135+
"timestamp_column_name": "timestamp",
136+
},
137+
rows=[
138+
{
139+
"user_query": "what is the meaning of life?",
140+
"output": "42",
141+
"tokens": 7,
142+
"cost": 0.02,
143+
"timestamp": 1610000000,
144+
}
145+
],
146+
)
147+
print(response.success)
148+
149+
150+
asyncio.run(main())
151+
```
152+
103153
## Using types
104154

105155
Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typing.html#typing.TypedDict). Responses are [Pydantic models](https://docs.pydantic.dev) which also provide helper methods for things like:

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ classifiers = [
4343
Homepage = "https://github.com/openlayer-ai/openlayer-python"
4444
Repository = "https://github.com/openlayer-ai/openlayer-python"
4545

46+
[project.optional-dependencies]
47+
aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.6"]
4648

4749
[tool.rye]
4850
managed = true

requirements-dev.lock

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,24 @@
1010
# universal: false
1111

1212
-e file:.
13+
aiohappyeyeballs==2.6.1
14+
# via aiohttp
15+
aiohttp==3.12.13
16+
# via httpx-aiohttp
17+
# via openlayer
18+
aiosignal==1.3.2
19+
# via aiohttp
1320
annotated-types==0.6.0
1421
# via pydantic
1522
anyio==4.4.0
1623
# via httpx
1724
# via openlayer
1825
argcomplete==3.1.2
1926
# via nox
27+
async-timeout==5.0.1
28+
# via aiohttp
29+
attrs==25.3.0
30+
# via aiohttp
2031
certifi==2023.7.22
2132
# via httpcore
2233
# via httpx
@@ -37,24 +48,34 @@ execnet==2.1.1
3748
# via pytest-xdist
3849
filelock==3.12.4
3950
# via virtualenv
51+
frozenlist==1.7.0
52+
# via aiohttp
53+
# via aiosignal
4054
h11==0.14.0
4155
# via httpcore
4256
httpcore==1.0.2
4357
# via httpx
4458
httpx==0.28.1
59+
# via httpx-aiohttp
4560
# via openlayer
4661
# via respx
62+
httpx-aiohttp==0.1.6
63+
# via openlayer
4764
idna==3.4
4865
# via anyio
4966
# via httpx
5067
# via requests
68+
# via yarl
5169
importlib-metadata==7.0.0
5270
iniconfig==2.0.0
5371
# via pytest
5472
markdown-it-py==3.0.0
5573
# via rich
5674
mdurl==0.1.2
5775
# via markdown-it-py
76+
multidict==6.5.0
77+
# via aiohttp
78+
# via yarl
5879
mypy==1.14.1
5980
mypy-extensions==1.0.0
6081
# via mypy
@@ -75,6 +96,9 @@ platformdirs==3.11.0
7596
# via virtualenv
7697
pluggy==1.5.0
7798
# via pytest
99+
propcache==0.3.2
100+
# via aiohttp
101+
# via yarl
78102
pyarrow==14.0.1
79103
# via openlayer
80104
pydantic==2.10.3
@@ -119,6 +143,7 @@ tqdm==4.67.1
119143
# via openlayer
120144
typing-extensions==4.12.2
121145
# via anyio
146+
# via multidict
122147
# via mypy
123148
# via openlayer
124149
# via pydantic
@@ -130,5 +155,7 @@ urllib3==2.2.3
130155
# via requests
131156
virtualenv==20.24.5
132157
# via nox
158+
yarl==1.20.1
159+
# via aiohttp
133160
zipp==3.17.0
134161
# via importlib-metadata

requirements.lock

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,22 @@
1010
# universal: false
1111

1212
-e file:.
13+
aiohappyeyeballs==2.6.1
14+
# via aiohttp
15+
aiohttp==3.12.13
16+
# via httpx-aiohttp
17+
# via openlayer
18+
aiosignal==1.3.2
19+
# via aiohttp
1320
annotated-types==0.6.0
1421
# via pydantic
1522
anyio==4.4.0
1623
# via httpx
1724
# via openlayer
25+
async-timeout==5.0.1
26+
# via aiohttp
27+
attrs==25.3.0
28+
# via aiohttp
1829
certifi==2023.7.22
1930
# via httpcore
2031
# via httpx
@@ -25,22 +36,35 @@ distro==1.8.0
2536
# via openlayer
2637
exceptiongroup==1.2.2
2738
# via anyio
39+
frozenlist==1.7.0
40+
# via aiohttp
41+
# via aiosignal
2842
h11==0.14.0
2943
# via httpcore
3044
httpcore==1.0.2
3145
# via httpx
3246
httpx==0.28.1
47+
# via httpx-aiohttp
48+
# via openlayer
49+
httpx-aiohttp==0.1.6
3350
# via openlayer
3451
idna==3.4
3552
# via anyio
3653
# via httpx
3754
# via requests
55+
# via yarl
56+
multidict==6.5.0
57+
# via aiohttp
58+
# via yarl
3859
numpy==1.26.4
3960
# via openlayer
4061
# via pandas
4162
# via pyarrow
4263
pandas==2.2.2
4364
# via openlayer
65+
propcache==0.3.2
66+
# via aiohttp
67+
# via yarl
4468
pyarrow==14.0.1
4569
# via openlayer
4670
pydantic==2.10.3
@@ -66,10 +90,13 @@ tqdm==4.67.1
6690
# via openlayer
6791
typing-extensions==4.12.2
6892
# via anyio
93+
# via multidict
6994
# via openlayer
7095
# via pydantic
7196
# via pydantic-core
7297
tzdata==2024.1
7398
# via pandas
7499
urllib3==2.2.3
75100
# via requests
101+
yarl==1.20.1
102+
# via aiohttp

src/openlayer/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
UnprocessableEntityError,
3737
APIResponseValidationError,
3838
)
39-
from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient
39+
from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient
4040
from ._utils._logs import setup_logging as _setup_logging
4141

4242
__all__ = [
@@ -78,6 +78,7 @@
7878
"DEFAULT_CONNECTION_LIMITS",
7979
"DefaultHttpxClient",
8080
"DefaultAsyncHttpxClient",
81+
"DefaultAioHttpClient",
8182
]
8283

8384
if not _t.TYPE_CHECKING:

src/openlayer/_base_client.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1289,6 +1289,24 @@ def __init__(self, **kwargs: Any) -> None:
12891289
super().__init__(**kwargs)
12901290

12911291

1292+
try:
1293+
import httpx_aiohttp
1294+
except ImportError:
1295+
1296+
class _DefaultAioHttpClient(httpx.AsyncClient):
1297+
def __init__(self, **_kwargs: Any) -> None:
1298+
raise RuntimeError("To use the aiohttp client you must have installed the package with the `aiohttp` extra")
1299+
else:
1300+
1301+
class _DefaultAioHttpClient(httpx_aiohttp.HttpxAiohttpClient): # type: ignore
1302+
def __init__(self, **kwargs: Any) -> None:
1303+
kwargs.setdefault("timeout", DEFAULT_TIMEOUT)
1304+
kwargs.setdefault("limits", DEFAULT_CONNECTION_LIMITS)
1305+
kwargs.setdefault("follow_redirects", True)
1306+
1307+
super().__init__(**kwargs)
1308+
1309+
12921310
if TYPE_CHECKING:
12931311
DefaultAsyncHttpxClient = httpx.AsyncClient
12941312
"""An alias to `httpx.AsyncClient` that provides the same defaults that this SDK
@@ -1297,8 +1315,12 @@ def __init__(self, **kwargs: Any) -> None:
12971315
This is useful because overriding the `http_client` with your own instance of
12981316
`httpx.AsyncClient` will result in httpx's defaults being used, not ours.
12991317
"""
1318+
1319+
DefaultAioHttpClient = httpx.AsyncClient
1320+
"""An alias to `httpx.AsyncClient` that changes the default HTTP transport to `aiohttp`."""
13001321
else:
13011322
DefaultAsyncHttpxClient = _DefaultAsyncHttpxClient
1323+
DefaultAioHttpClient = _DefaultAioHttpClient
13021324

13031325

13041326
class AsyncHttpxClientWrapper(DefaultAsyncHttpxClient):

tests/api_resources/commits/test_test_results.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,9 @@ def test_path_params_list(self, client: Openlayer) -> None:
6969

7070

7171
class TestAsyncTestResults:
72-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
72+
parametrize = pytest.mark.parametrize(
73+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
74+
)
7375

7476
@parametrize
7577
async def test_method_list(self, async_client: AsyncOpenlayer) -> None:

tests/api_resources/inference_pipelines/test_data.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,9 @@ def test_path_params_stream(self, client: Openlayer) -> None:
132132

133133

134134
class TestAsyncData:
135-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
135+
parametrize = pytest.mark.parametrize(
136+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
137+
)
136138

137139
@parametrize
138140
async def test_method_stream(self, async_client: AsyncOpenlayer) -> None:

tests/api_resources/inference_pipelines/test_rows.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,9 @@ def test_path_params_update(self, client: Openlayer) -> None:
8181

8282

8383
class TestAsyncRows:
84-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
84+
parametrize = pytest.mark.parametrize(
85+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
86+
)
8587

8688
@parametrize
8789
async def test_method_update(self, async_client: AsyncOpenlayer) -> None:

tests/api_resources/inference_pipelines/test_test_results.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,9 @@ def test_path_params_list(self, client: Openlayer) -> None:
6868

6969

7070
class TestAsyncTestResults:
71-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
71+
parametrize = pytest.mark.parametrize(
72+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
73+
)
7274

7375
@parametrize
7476
async def test_method_list(self, async_client: AsyncOpenlayer) -> None:

tests/api_resources/projects/test_commits.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,9 @@ def test_path_params_list(self, client: Openlayer) -> None:
123123

124124

125125
class TestAsyncCommits:
126-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
126+
parametrize = pytest.mark.parametrize(
127+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
128+
)
127129

128130
@parametrize
129131
async def test_method_create(self, async_client: AsyncOpenlayer) -> None:

tests/api_resources/projects/test_inference_pipelines.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,9 @@ def test_path_params_list(self, client: Openlayer) -> None:
137137

138138

139139
class TestAsyncInferencePipelines:
140-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
140+
parametrize = pytest.mark.parametrize(
141+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
142+
)
141143

142144
@parametrize
143145
async def test_method_create(self, async_client: AsyncOpenlayer) -> None:

tests/api_resources/projects/test_tests.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,9 @@ def test_path_params_list(self, client: Openlayer) -> None:
209209

210210

211211
class TestAsyncTests:
212-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
212+
parametrize = pytest.mark.parametrize(
213+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
214+
)
213215

214216
@parametrize
215217
async def test_method_create(self, async_client: AsyncOpenlayer) -> None:

tests/api_resources/storage/test_presigned_url.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ def test_streaming_response_create(self, client: Openlayer) -> None:
5050

5151

5252
class TestAsyncPresignedURL:
53-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
53+
parametrize = pytest.mark.parametrize(
54+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
55+
)
5456

5557
@parametrize
5658
async def test_method_create(self, async_client: AsyncOpenlayer) -> None:

tests/api_resources/test_commits.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ def test_path_params_retrieve(self, client: Openlayer) -> None:
5757

5858

5959
class TestAsyncCommits:
60-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
60+
parametrize = pytest.mark.parametrize(
61+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
62+
)
6163

6264
@parametrize
6365
async def test_method_retrieve(self, async_client: AsyncOpenlayer) -> None:

tests/api_resources/test_inference_pipelines.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,9 @@ def test_path_params_delete(self, client: Openlayer) -> None:
154154

155155

156156
class TestAsyncInferencePipelines:
157-
parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
157+
parametrize = pytest.mark.parametrize(
158+
"async_client", [False, True, {"http_client": "aiohttp"}], indirect=True, ids=["loose", "strict", "aiohttp"]
159+
)
158160

159161
@parametrize
160162
async def test_method_retrieve(self, async_client: AsyncOpenlayer) -> None:

0 commit comments

Comments
 (0)