diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 2b28d6ec..6d78745c 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.8.1" + ".": "0.9.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 8b1b4e11..f54fc2cc 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 49 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/mixedbread%2Fmixedbread-258cd9419639d2d3d821e9c63a336fb7fecc362f376a7f606868445bcef995cd.yml -openapi_spec_hash: e2c4cf1543194f94f3b0baad6a5735ba +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/mixedbread%2Fmixedbread-e3a6c9f052ee6bdfe6eb3db18628fd95c5c9bf18a656089f5f0add0490b90763.yml +openapi_spec_hash: d3c1aa7b05aa7395dc80a2a72cbbbd84 config_hash: ca0dfb431a44ea42464c42b224addf36 diff --git a/CHANGELOG.md b/CHANGELOG.md index fe78a957..25f8d152 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## 0.9.0 (2025-06-25) + +Full Changelog: [v0.8.1...v0.9.0](https://github.com/mixedbread-ai/mixedbread-python/compare/v0.8.1...v0.9.0) + +### Features + +* **api:** api update ([d6af863](https://github.com/mixedbread-ai/mixedbread-python/commit/d6af863ca9125062cb4b13172781499c987a8685)) +* **client:** add support for aiohttp ([94e0fc5](https://github.com/mixedbread-ai/mixedbread-python/commit/94e0fc58df85c64265612832cefa956c7723a79c)) + + +### Chores + +* **tests:** skip some failing tests on the latest python versions ([d2bb62a](https://github.com/mixedbread-ai/mixedbread-python/commit/d2bb62a9bbf7f10ee048b0f21d49841b1e1ed1a0)) + + +### Documentation + +* **client:** fix httpx.Timeout documentation reference ([855e94a](https://github.com/mixedbread-ai/mixedbread-python/commit/855e94ac632b0da2a8460af1964bb50b3ee37edb)) + ## 0.8.1 (2025-06-18) Full Changelog: [v0.8.0...v0.8.1](https://github.com/mixedbread-ai/mixedbread-python/compare/v0.8.0...v0.8.1) diff --git a/README.md b/README.md index 9481c5b1..901b0d7d 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,38 @@ 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 mixedbread[aiohttp] +``` + +Then you can enable it by instantiating the client with `http_client=DefaultAioHttpClient()`: + +```python +import os +import asyncio +from mixedbread import DefaultAioHttpClient +from mixedbread import AsyncMixedbread + + +async def main() -> None: + async with AsyncMixedbread( + api_key=os.environ.get("MXBAI_API_KEY"), # This is the default and can be omitted + http_client=DefaultAioHttpClient(), + ) as client: + vector_store = await client.vector_stores.create() + print(vector_store.id) + + +asyncio.run(main()) +``` + ## Using types 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: @@ -239,7 +271,7 @@ client.with_options(max_retries=5).vector_stores.create() ### Timeouts By default requests time out after 1 minute. You can configure this with a `timeout` option, -which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/#fine-tuning-the-configuration) object: +which accepts a float or an [`httpx.Timeout`](https://www.python-httpx.org/advanced/timeouts/#fine-tuning-the-configuration) object: ```python from mixedbread import Mixedbread diff --git a/pyproject.toml b/pyproject.toml index 0bdfeffa..15422725 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "mixedbread" -version = "0.8.1" +version = "0.9.0" description = "The official Python library for the Mixedbread API" dynamic = ["readme"] license = "Apache-2.0" @@ -37,6 +37,8 @@ classifiers = [ Homepage = "https://github.com/mixedbread-ai/mixedbread-python" Repository = "https://github.com/mixedbread-ai/mixedbread-python" +[project.optional-dependencies] +aiohttp = ["aiohttp", "httpx_aiohttp>=0.1.6"] [tool.rye] managed = true diff --git a/requirements-dev.lock b/requirements-dev.lock index e4090452..ca2c5c9e 100644 --- a/requirements-dev.lock +++ b/requirements-dev.lock @@ -10,6 +10,13 @@ # universal: false -e file:. +aiohappyeyeballs==2.6.1 + # via aiohttp +aiohttp==3.12.8 + # via httpx-aiohttp + # via mixedbread +aiosignal==1.3.2 + # via aiohttp annotated-types==0.6.0 # via pydantic anyio==4.4.0 @@ -17,6 +24,10 @@ anyio==4.4.0 # via mixedbread argcomplete==3.1.2 # via nox +async-timeout==5.0.1 + # via aiohttp +attrs==25.3.0 + # via aiohttp certifi==2023.7.22 # via httpcore # via httpx @@ -34,16 +45,23 @@ execnet==2.1.1 # via pytest-xdist filelock==3.12.4 # via virtualenv +frozenlist==1.6.2 + # via aiohttp + # via aiosignal h11==0.14.0 # via httpcore httpcore==1.0.2 # via httpx httpx==0.28.1 + # via httpx-aiohttp # via mixedbread # via respx +httpx-aiohttp==0.1.6 + # via mixedbread idna==3.4 # via anyio # via httpx + # via yarl importlib-metadata==7.0.0 iniconfig==2.0.0 # via pytest @@ -51,6 +69,9 @@ markdown-it-py==3.0.0 # via rich mdurl==0.1.2 # via markdown-it-py +multidict==6.4.4 + # via aiohttp + # via yarl mypy==1.14.1 mypy-extensions==1.0.0 # via mypy @@ -65,6 +86,9 @@ platformdirs==3.11.0 # via virtualenv pluggy==1.5.0 # via pytest +propcache==0.3.1 + # via aiohttp + # via yarl pydantic==2.10.3 # via mixedbread pydantic-core==2.27.1 @@ -98,11 +122,14 @@ tomli==2.0.2 typing-extensions==4.12.2 # via anyio # via mixedbread + # via multidict # via mypy # via pydantic # via pydantic-core # via pyright virtualenv==20.24.5 # via nox +yarl==1.20.0 + # via aiohttp zipp==3.17.0 # via importlib-metadata diff --git a/requirements.lock b/requirements.lock index f81f62cc..2b940d36 100644 --- a/requirements.lock +++ b/requirements.lock @@ -10,11 +10,22 @@ # universal: false -e file:. +aiohappyeyeballs==2.6.1 + # via aiohttp +aiohttp==3.12.8 + # via httpx-aiohttp + # via mixedbread +aiosignal==1.3.2 + # via aiohttp annotated-types==0.6.0 # via pydantic anyio==4.4.0 # via httpx # via mixedbread +async-timeout==5.0.1 + # via aiohttp +attrs==25.3.0 + # via aiohttp certifi==2023.7.22 # via httpcore # via httpx @@ -22,15 +33,28 @@ distro==1.8.0 # via mixedbread exceptiongroup==1.2.2 # via anyio +frozenlist==1.6.2 + # via aiohttp + # via aiosignal h11==0.14.0 # via httpcore httpcore==1.0.2 # via httpx httpx==0.28.1 + # via httpx-aiohttp + # via mixedbread +httpx-aiohttp==0.1.6 # via mixedbread idna==3.4 # via anyio # via httpx + # via yarl +multidict==6.4.4 + # via aiohttp + # via yarl +propcache==0.3.1 + # via aiohttp + # via yarl pydantic==2.10.3 # via mixedbread pydantic-core==2.27.1 @@ -41,5 +65,8 @@ sniffio==1.3.0 typing-extensions==4.12.2 # via anyio # via mixedbread + # via multidict # via pydantic # via pydantic-core +yarl==1.20.0 + # via aiohttp diff --git a/src/mixedbread/__init__.py b/src/mixedbread/__init__.py index 022e49ef..df2a2fb2 100644 --- a/src/mixedbread/__init__.py +++ b/src/mixedbread/__init__.py @@ -37,7 +37,7 @@ UnprocessableEntityError, APIResponseValidationError, ) -from ._base_client import DefaultHttpxClient, DefaultAsyncHttpxClient +from ._base_client import DefaultHttpxClient, DefaultAioHttpClient, DefaultAsyncHttpxClient from ._utils._logs import setup_logging as _setup_logging __all__ = [ @@ -80,6 +80,7 @@ "DEFAULT_CONNECTION_LIMITS", "DefaultHttpxClient", "DefaultAsyncHttpxClient", + "DefaultAioHttpClient", ] if not _t.TYPE_CHECKING: diff --git a/src/mixedbread/_base_client.py b/src/mixedbread/_base_client.py index 44036510..8ec40870 100644 --- a/src/mixedbread/_base_client.py +++ b/src/mixedbread/_base_client.py @@ -1289,6 +1289,24 @@ def __init__(self, **kwargs: Any) -> None: 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 @@ -1297,8 +1315,12 @@ def __init__(self, **kwargs: Any) -> None: 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): diff --git a/src/mixedbread/_version.py b/src/mixedbread/_version.py index cebff629..9205ddcd 100644 --- a/src/mixedbread/_version.py +++ b/src/mixedbread/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "mixedbread" -__version__ = "0.8.1" # x-release-please-version +__version__ = "0.9.0" # x-release-please-version diff --git a/src/mixedbread/types/data_sources/data_source_connector.py b/src/mixedbread/types/data_sources/data_source_connector.py index d4276970..bda39237 100644 --- a/src/mixedbread/types/data_sources/data_source_connector.py +++ b/src/mixedbread/types/data_sources/data_source_connector.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import Dict, Optional from datetime import datetime from typing_extensions import Literal @@ -46,7 +46,7 @@ class DataSourceConnector(BaseModel): status: Literal["idle", "pending", "in_progress", "cancelled", "completed", "failed"] """The sync status of the connector""" - error: Optional[str] = None + error: Optional[Dict[str, object]] = None """The sync error of the connector""" object: Optional[Literal["data_source.connector"]] = None diff --git a/src/mixedbread/types/scored_audio_url_input_chunk.py b/src/mixedbread/types/scored_audio_url_input_chunk.py index b514f1bd..8f11be23 100644 --- a/src/mixedbread/types/scored_audio_url_input_chunk.py +++ b/src/mixedbread/types/scored_audio_url_input_chunk.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import Dict, Optional from typing_extensions import Literal from .._models import BaseModel @@ -20,6 +20,9 @@ class ScoredAudioURLInputChunk(BaseModel): mime_type: Optional[str] = None """mime type of the chunk""" + generated_metadata: Optional[Dict[str, object]] = None + """metadata of the chunk""" + model: Optional[str] = None """model used for this chunk""" diff --git a/src/mixedbread/types/scored_image_url_input_chunk.py b/src/mixedbread/types/scored_image_url_input_chunk.py index f160a46d..8be62ae0 100644 --- a/src/mixedbread/types/scored_image_url_input_chunk.py +++ b/src/mixedbread/types/scored_image_url_input_chunk.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import Dict, Optional from typing_extensions import Literal from .._models import BaseModel @@ -23,6 +23,9 @@ class ScoredImageURLInputChunk(BaseModel): mime_type: Optional[str] = None """mime type of the chunk""" + generated_metadata: Optional[Dict[str, object]] = None + """metadata of the chunk""" + model: Optional[str] = None """model used for this chunk""" diff --git a/src/mixedbread/types/scored_text_input_chunk.py b/src/mixedbread/types/scored_text_input_chunk.py index 42870062..398a8a7c 100644 --- a/src/mixedbread/types/scored_text_input_chunk.py +++ b/src/mixedbread/types/scored_text_input_chunk.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import Dict, Optional from typing_extensions import Literal from .._models import BaseModel @@ -15,6 +15,9 @@ class ScoredTextInputChunk(BaseModel): mime_type: Optional[str] = None """mime type of the chunk""" + generated_metadata: Optional[Dict[str, object]] = None + """metadata of the chunk""" + model: Optional[str] = None """model used for this chunk""" diff --git a/src/mixedbread/types/scored_video_url_input_chunk.py b/src/mixedbread/types/scored_video_url_input_chunk.py index 14836909..bd0bea16 100644 --- a/src/mixedbread/types/scored_video_url_input_chunk.py +++ b/src/mixedbread/types/scored_video_url_input_chunk.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Optional +from typing import Dict, Optional from typing_extensions import Literal from .._models import BaseModel @@ -20,6 +20,9 @@ class ScoredVideoURLInputChunk(BaseModel): mime_type: Optional[str] = None """mime type of the chunk""" + generated_metadata: Optional[Dict[str, object]] = None + """metadata of the chunk""" + model: Optional[str] = None """model used for this chunk""" diff --git a/tests/api_resources/data_sources/test_connectors.py b/tests/api_resources/data_sources/test_connectors.py index fb95955d..5520ee52 100644 --- a/tests/api_resources/data_sources/test_connectors.py +++ b/tests/api_resources/data_sources/test_connectors.py @@ -280,7 +280,9 @@ def test_path_params_delete(self, client: Mixedbread) -> None: class TestAsyncConnectors: - 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: AsyncMixedbread) -> None: diff --git a/tests/api_resources/extractions/test_content.py b/tests/api_resources/extractions/test_content.py index daf0b307..63ee1aed 100644 --- a/tests/api_resources/extractions/test_content.py +++ b/tests/api_resources/extractions/test_content.py @@ -62,7 +62,9 @@ def test_streaming_response_create(self, client: Mixedbread) -> None: 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 async def test_method_create(self, async_client: AsyncMixedbread) -> None: diff --git a/tests/api_resources/extractions/test_jobs.py b/tests/api_resources/extractions/test_jobs.py index 312512c7..b79a66a4 100644 --- a/tests/api_resources/extractions/test_jobs.py +++ b/tests/api_resources/extractions/test_jobs.py @@ -91,7 +91,9 @@ def test_path_params_retrieve(self, client: Mixedbread) -> None: 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: AsyncMixedbread) -> None: diff --git a/tests/api_resources/extractions/test_schema.py b/tests/api_resources/extractions/test_schema.py index 9e538d07..3de149ff 100644 --- a/tests/api_resources/extractions/test_schema.py +++ b/tests/api_resources/extractions/test_schema.py @@ -116,7 +116,9 @@ def test_streaming_response_validate(self, client: Mixedbread) -> None: class TestAsyncSchema: - 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: AsyncMixedbread) -> None: diff --git a/tests/api_resources/parsing/test_jobs.py b/tests/api_resources/parsing/test_jobs.py index 6ddcf2da..64e12e9c 100644 --- a/tests/api_resources/parsing/test_jobs.py +++ b/tests/api_resources/parsing/test_jobs.py @@ -213,7 +213,9 @@ def test_path_params_cancel(self, client: Mixedbread) -> None: 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: AsyncMixedbread) -> None: diff --git a/tests/api_resources/test_api_keys.py b/tests/api_resources/test_api_keys.py index 531fadfe..7da5d8aa 100644 --- a/tests/api_resources/test_api_keys.py +++ b/tests/api_resources/test_api_keys.py @@ -239,7 +239,9 @@ def test_path_params_revoke(self, client: Mixedbread) -> None: class TestAsyncAPIKeys: - 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: AsyncMixedbread) -> None: diff --git a/tests/api_resources/test_chat.py b/tests/api_resources/test_chat.py index 65341369..a60009fc 100644 --- a/tests/api_resources/test_chat.py +++ b/tests/api_resources/test_chat.py @@ -43,7 +43,9 @@ def test_streaming_response_create_completion(self, client: Mixedbread) -> None: class TestAsyncChat: - 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_completion(self, async_client: AsyncMixedbread) -> None: diff --git a/tests/api_resources/test_client.py b/tests/api_resources/test_client.py index e05d9df6..7a05f7de 100644 --- a/tests/api_resources/test_client.py +++ b/tests/api_resources/test_client.py @@ -141,7 +141,9 @@ def test_streaming_response_rerank(self, client: Mixedbread) -> None: class TestAsyncClient: - 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_embed(self, async_client: AsyncMixedbread) -> None: diff --git a/tests/api_resources/test_data_sources.py b/tests/api_resources/test_data_sources.py index 28f502e2..fa43c6ee 100644 --- a/tests/api_resources/test_data_sources.py +++ b/tests/api_resources/test_data_sources.py @@ -320,7 +320,9 @@ def test_path_params_delete(self, client: Mixedbread) -> None: class TestAsyncDataSources: - 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: AsyncMixedbread) -> None: diff --git a/tests/api_resources/test_embeddings.py b/tests/api_resources/test_embeddings.py index 9aab6efd..f962e880 100644 --- a/tests/api_resources/test_embeddings.py +++ b/tests/api_resources/test_embeddings.py @@ -65,7 +65,9 @@ def test_streaming_response_create(self, client: Mixedbread) -> None: 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: AsyncMixedbread) -> None: diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py index 3df12533..dd9440c4 100644 --- a/tests/api_resources/test_files.py +++ b/tests/api_resources/test_files.py @@ -266,7 +266,9 @@ def test_path_params_content(self, client: Mixedbread) -> None: 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: AsyncMixedbread) -> None: diff --git a/tests/api_resources/test_vector_stores.py b/tests/api_resources/test_vector_stores.py index 538f55dc..c6872f10 100644 --- a/tests/api_resources/test_vector_stores.py +++ b/tests/api_resources/test_vector_stores.py @@ -405,7 +405,9 @@ def test_streaming_response_search(self, client: Mixedbread) -> None: 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: AsyncMixedbread) -> None: diff --git a/tests/api_resources/vector_stores/test_files.py b/tests/api_resources/vector_stores/test_files.py index 6a4128de..8a80638e 100644 --- a/tests/api_resources/vector_stores/test_files.py +++ b/tests/api_resources/vector_stores/test_files.py @@ -318,7 +318,9 @@ def test_streaming_response_search(self, client: Mixedbread) -> None: 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: AsyncMixedbread) -> None: diff --git a/tests/conftest.py b/tests/conftest.py index 61ded35a..9b35601c 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,10 +6,12 @@ import logging from typing import TYPE_CHECKING, Iterator, AsyncIterator +import httpx import pytest from pytest_asyncio import is_async_test -from mixedbread import Mixedbread, AsyncMixedbread +from mixedbread import Mixedbread, AsyncMixedbread, DefaultAioHttpClient +from mixedbread._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[Mixedbread]: @pytest.fixture(scope="session") async def async_client(request: FixtureRequest) -> AsyncIterator[AsyncMixedbread]: - strict = getattr(request, "param", True) - if not isinstance(strict, bool): - raise TypeError(f"Unexpected fixture parameter type {type(strict)}, expected {bool}") - - async with AsyncMixedbread(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 AsyncMixedbread( + base_url=base_url, api_key=api_key, _strict_response_validation=strict, http_client=http_client + ) as client: yield client diff --git a/tests/test_client.py b/tests/test_client.py index 7c75ca18..8f60c0c0 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -191,6 +191,7 @@ def test_copy_signature(self) -> None: copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" + @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") def test_copy_build_request(self) -> None: options = FinalRequestOptions(method="get", url="/foo") @@ -1009,6 +1010,7 @@ def test_copy_signature(self) -> None: copy_param = copy_signature.parameters.get(name) assert copy_param is not None, f"copy() signature is missing the {name} param" + @pytest.mark.skipif(sys.version_info >= (3, 10), reason="fails because of a memory leak that started from 3.12") def test_copy_build_request(self) -> None: options = FinalRequestOptions(method="get", url="/foo")