diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c373724d..46b9b6b2 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.1.0-alpha.8" + ".": "0.1.0-alpha.9" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index a49f5bb2..fd9a8aa2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.1.0-alpha.9 (2025-01-05) + +Full Changelog: [v0.1.0-alpha.8...v0.1.0-alpha.9](https://github.com/mixedbread-ai/mixedbread-python/compare/v0.1.0-alpha.8...v0.1.0-alpha.9) + +### Features + +* **api:** update via SDK Studio ([#37](https://github.com/mixedbread-ai/mixedbread-python/issues/37)) ([10c8c52](https://github.com/mixedbread-ai/mixedbread-python/commit/10c8c528a68b765bbc5c01d0cca990c5c52544d2)) + ## 0.1.0-alpha.8 (2025-01-05) Full Changelog: [v0.1.0-alpha.7...v0.1.0-alpha.8](https://github.com/mixedbread-ai/mixedbread-python/compare/v0.1.0-alpha.7...v0.1.0-alpha.8) diff --git a/api.md b/api.md index bd4879bb..42d6e9d5 100644 --- a/api.md +++ b/api.md @@ -66,6 +66,7 @@ Methods: - client.files.update(file_id, \*\*params) -> FileObject - client.files.list(\*\*params) -> SyncPage[FileObject] - client.files.delete(file_id) -> FileDeleteResponse +- client.files.content(file_id) -> BinaryAPIResponse # VectorStores diff --git a/pyproject.toml b/pyproject.toml index 38aa5647..86b6fed0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "mixedbread" -version = "0.1.0-alpha.8" +version = "0.1.0-alpha.9" description = "The official Python library for the mixedbread API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/mixedbread/_version.py b/src/mixedbread/_version.py index bf05b31f..a46fdaba 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.1.0-alpha.8" # x-release-please-version +__version__ = "0.1.0-alpha.9" # x-release-please-version diff --git a/src/mixedbread/resources/files.py b/src/mixedbread/resources/files.py index 082f397f..b9e0be88 100644 --- a/src/mixedbread/resources/files.py +++ b/src/mixedbread/resources/files.py @@ -17,10 +17,18 @@ from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import ( + BinaryAPIResponse, + AsyncBinaryAPIResponse, + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, to_raw_response_wrapper, to_streamed_response_wrapper, async_to_raw_response_wrapper, + to_custom_raw_response_wrapper, async_to_streamed_response_wrapper, + to_custom_streamed_response_wrapper, + async_to_custom_raw_response_wrapper, + async_to_custom_streamed_response_wrapper, ) from ..pagination import SyncPage, AsyncPage from .._base_client import AsyncPaginator, make_request_options @@ -274,6 +282,46 @@ def delete( cast_to=FileDeleteResponse, ) + def content( + self, + file_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> BinaryAPIResponse: + """ + Download a specific file by its ID. + + Args: file_id: The ID of the file to download. + + Returns: FileStreamResponse: The response containing the file to be downloaded. + + Args: + file_id: The ID of the file to download + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})} + return self._get( + f"/v1/files/{file_id}/content", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=BinaryAPIResponse, + ) + class AsyncFilesResource(AsyncAPIResource): @cached_property @@ -519,6 +567,46 @@ async def delete( cast_to=FileDeleteResponse, ) + async def content( + self, + file_id: str, + *, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN, + ) -> AsyncBinaryAPIResponse: + """ + Download a specific file by its ID. + + Args: file_id: The ID of the file to download. + + Returns: FileStreamResponse: The response containing the file to be downloaded. + + Args: + file_id: The ID of the file to download + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + """ + if not file_id: + raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}") + extra_headers = {"Accept": "application/octet-stream", **(extra_headers or {})} + return await self._get( + f"/v1/files/{file_id}/content", + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=AsyncBinaryAPIResponse, + ) + class FilesResourceWithRawResponse: def __init__(self, files: FilesResource) -> None: @@ -539,6 +627,10 @@ def __init__(self, files: FilesResource) -> None: self.delete = to_raw_response_wrapper( files.delete, ) + self.content = to_custom_raw_response_wrapper( + files.content, + BinaryAPIResponse, + ) class AsyncFilesResourceWithRawResponse: @@ -560,6 +652,10 @@ def __init__(self, files: AsyncFilesResource) -> None: self.delete = async_to_raw_response_wrapper( files.delete, ) + self.content = async_to_custom_raw_response_wrapper( + files.content, + AsyncBinaryAPIResponse, + ) class FilesResourceWithStreamingResponse: @@ -581,6 +677,10 @@ def __init__(self, files: FilesResource) -> None: self.delete = to_streamed_response_wrapper( files.delete, ) + self.content = to_custom_streamed_response_wrapper( + files.content, + StreamedBinaryAPIResponse, + ) class AsyncFilesResourceWithStreamingResponse: @@ -602,3 +702,7 @@ def __init__(self, files: AsyncFilesResource) -> None: self.delete = async_to_streamed_response_wrapper( files.delete, ) + self.content = async_to_custom_streamed_response_wrapper( + files.content, + AsyncStreamedBinaryAPIResponse, + ) diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py index 435993cc..84dc5789 100644 --- a/tests/api_resources/test_files.py +++ b/tests/api_resources/test_files.py @@ -5,11 +5,19 @@ import os from typing import Any, cast +import httpx import pytest +from respx import MockRouter from mixedbread import Mixedbread, AsyncMixedbread from tests.utils import assert_matches_type from mixedbread.types import FileObject, FileDeleteResponse +from mixedbread._response import ( + BinaryAPIResponse, + AsyncBinaryAPIResponse, + StreamedBinaryAPIResponse, + AsyncStreamedBinaryAPIResponse, +) from mixedbread.pagination import SyncPage, AsyncPage base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010") @@ -200,6 +208,62 @@ def test_path_params_delete(self, client: Mixedbread) -> None: "", ) + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_method_content(self, client: Mixedbread, respx_mock: MockRouter) -> None: + respx_mock.get("/v1/files/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e/content").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + file = client.files.content( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert file.is_closed + assert file.json() == {"foo": "bar"} + assert cast(Any, file.is_closed) is True + assert isinstance(file, BinaryAPIResponse) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_raw_response_content(self, client: Mixedbread, respx_mock: MockRouter) -> None: + respx_mock.get("/v1/files/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e/content").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + + file = client.files.with_raw_response.content( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert file.is_closed is True + assert file.http_request.headers.get("X-Stainless-Lang") == "python" + assert file.json() == {"foo": "bar"} + assert isinstance(file, BinaryAPIResponse) + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_streaming_response_content(self, client: Mixedbread, respx_mock: MockRouter) -> None: + respx_mock.get("/v1/files/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e/content").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + with client.files.with_streaming_response.content( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as file: + assert not file.is_closed + assert file.http_request.headers.get("X-Stainless-Lang") == "python" + + assert file.json() == {"foo": "bar"} + assert cast(Any, file.is_closed) is True + assert isinstance(file, StreamedBinaryAPIResponse) + + assert cast(Any, file.is_closed) is True + + @parametrize + @pytest.mark.respx(base_url=base_url) + def test_path_params_content(self, client: Mixedbread) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + client.files.with_raw_response.content( + "", + ) + class TestAsyncFiles: parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"]) @@ -385,3 +449,59 @@ async def test_path_params_delete(self, async_client: AsyncMixedbread) -> None: await async_client.files.with_raw_response.delete( "", ) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_method_content(self, async_client: AsyncMixedbread, respx_mock: MockRouter) -> None: + respx_mock.get("/v1/files/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e/content").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + file = await async_client.files.content( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + assert file.is_closed + assert await file.json() == {"foo": "bar"} + assert cast(Any, file.is_closed) is True + assert isinstance(file, AsyncBinaryAPIResponse) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_raw_response_content(self, async_client: AsyncMixedbread, respx_mock: MockRouter) -> None: + respx_mock.get("/v1/files/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e/content").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + + file = await async_client.files.with_raw_response.content( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) + + assert file.is_closed is True + assert file.http_request.headers.get("X-Stainless-Lang") == "python" + assert await file.json() == {"foo": "bar"} + assert isinstance(file, AsyncBinaryAPIResponse) + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_streaming_response_content(self, async_client: AsyncMixedbread, respx_mock: MockRouter) -> None: + respx_mock.get("/v1/files/182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e/content").mock( + return_value=httpx.Response(200, json={"foo": "bar"}) + ) + async with async_client.files.with_streaming_response.content( + "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e", + ) as file: + assert not file.is_closed + assert file.http_request.headers.get("X-Stainless-Lang") == "python" + + assert await file.json() == {"foo": "bar"} + assert cast(Any, file.is_closed) is True + assert isinstance(file, AsyncStreamedBinaryAPIResponse) + + assert cast(Any, file.is_closed) is True + + @parametrize + @pytest.mark.respx(base_url=base_url) + async def test_path_params_content(self, async_client: AsyncMixedbread) -> None: + with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"): + await async_client.files.with_raw_response.content( + "", + )