diff --git a/.release-please-manifest.json b/.release-please-manifest.json index a36746b8..caf5ca3f 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.25.0" + ".": "0.26.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index fe2990a2..33d7691d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 48 +configured_endpoints: 49 openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/mixedbread%2Fmixedbread-4983c57f5143aaeadfbb4af35595570e613035ae500ff1597fdf600cc2a48959.yml openapi_spec_hash: ecc0a6cfe61c3c959d034d094f36befe -config_hash: d98614cc5eec7ec8807fcc86c1799d62 +config_hash: ac27678c24558b71ee39b3db54fa12da diff --git a/CHANGELOG.md b/CHANGELOG.md index 6255bdc8..2c6504ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.26.0 (2025-08-11) + +Full Changelog: [v0.25.0...v0.26.0](https://github.com/mixedbread-ai/mixedbread-python/compare/v0.25.0...v0.26.0) + +### Features + +* **api:** update via SDK Studio ([47ef496](https://github.com/mixedbread-ai/mixedbread-python/commit/47ef49688096a0f5223d9d9ceee30706c206b717)) + ## 0.25.0 (2025-08-11) Full Changelog: [v0.24.0...v0.25.0](https://github.com/mixedbread-ai/mixedbread-python/compare/v0.24.0...v0.25.0) diff --git a/api.md b/api.md index e2f621af..f251791d 100644 --- a/api.md +++ b/api.md @@ -63,6 +63,7 @@ from mixedbread.types.vector_stores import ( ScoredVectorStoreFile, VectorStoreFileStatus, VectorStoreFile, + FileListResponse, FileDeleteResponse, FileSearchResponse, ) @@ -72,6 +73,7 @@ Methods: - client.vector_stores.files.create(vector_store_identifier, \*\*params) -> VectorStoreFile - client.vector_stores.files.retrieve(file_id, \*, vector_store_identifier, \*\*params) -> VectorStoreFile +- client.vector_stores.files.list(vector_store_identifier, \*\*params) -> FileListResponse - client.vector_stores.files.delete(file_id, \*, vector_store_identifier) -> FileDeleteResponse - client.vector_stores.files.search(\*\*params) -> FileSearchResponse diff --git a/pyproject.toml b/pyproject.toml index 666c8a59..4e15d3ed 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "mixedbread" -version = "0.25.0" +version = "0.26.0" 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 8b346a75..f10c0174 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.25.0" # x-release-please-version +__version__ = "0.26.0" # x-release-please-version diff --git a/src/mixedbread/resources/vector_stores/files.py b/src/mixedbread/resources/vector_stores/files.py index 97873d48..7f1ef7c7 100644 --- a/src/mixedbread/resources/vector_stores/files.py +++ b/src/mixedbread/resources/vector_stores/files.py @@ -19,10 +19,12 @@ async_to_streamed_response_wrapper, ) from ..._base_client import make_request_options -from ...types.vector_stores import file_create_params, file_search_params, file_retrieve_params +from ...types.vector_stores import file_list_params, file_create_params, file_search_params, file_retrieve_params from ...types.vector_stores.vector_store_file import VectorStoreFile +from ...types.vector_stores.file_list_response import FileListResponse from ...types.vector_stores.file_delete_response import FileDeleteResponse from ...types.vector_stores.file_search_response import FileSearchResponse +from ...types.vector_stores.vector_store_file_status import VectorStoreFileStatus __all__ = ["FilesResource", "AsyncFilesResource"] @@ -160,6 +162,79 @@ def retrieve( cast_to=VectorStoreFile, ) + def list( + self, + vector_store_identifier: str, + *, + limit: int | NotGiven = NOT_GIVEN, + after: Optional[str] | NotGiven = NOT_GIVEN, + before: Optional[str] | NotGiven = NOT_GIVEN, + include_total: bool | NotGiven = NOT_GIVEN, + statuses: Optional[List[VectorStoreFileStatus]] | NotGiven = NOT_GIVEN, + metadata_filter: Optional[file_list_params.MetadataFilter] | NotGiven = NOT_GIVEN, + # 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, + ) -> FileListResponse: + """ + List files indexed in a vector store with pagination and metadata filter. + + Args: vector_store_identifier: The ID or name of the vector store pagination: + Pagination parameters and metadata filter + + Returns: VectorStoreFileListResponse: Paginated list of vector store files + + Args: + vector_store_identifier: The ID or name of the vector store + + limit: Maximum number of items to return per page (1-100) + + after: Cursor for forward pagination - get items after this position. Use last_cursor + from previous response. + + before: Cursor for backward pagination - get items before this position. Use + first_cursor from previous response. + + include_total: Whether to include total count in response (expensive operation) + + statuses: Status to filter by + + metadata_filter: Metadata filter to apply to the query + + 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 vector_store_identifier: + raise ValueError( + f"Expected a non-empty value for `vector_store_identifier` but received {vector_store_identifier!r}" + ) + return self._post( + f"/v1/vector_stores/{vector_store_identifier}/files/list", + body=maybe_transform( + { + "limit": limit, + "after": after, + "before": before, + "include_total": include_total, + "statuses": statuses, + "metadata_filter": metadata_filter, + }, + file_list_params.FileListParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileListResponse, + ) + def delete( self, file_id: str, @@ -525,6 +600,79 @@ async def retrieve( cast_to=VectorStoreFile, ) + async def list( + self, + vector_store_identifier: str, + *, + limit: int | NotGiven = NOT_GIVEN, + after: Optional[str] | NotGiven = NOT_GIVEN, + before: Optional[str] | NotGiven = NOT_GIVEN, + include_total: bool | NotGiven = NOT_GIVEN, + statuses: Optional[List[VectorStoreFileStatus]] | NotGiven = NOT_GIVEN, + metadata_filter: Optional[file_list_params.MetadataFilter] | NotGiven = NOT_GIVEN, + # 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, + ) -> FileListResponse: + """ + List files indexed in a vector store with pagination and metadata filter. + + Args: vector_store_identifier: The ID or name of the vector store pagination: + Pagination parameters and metadata filter + + Returns: VectorStoreFileListResponse: Paginated list of vector store files + + Args: + vector_store_identifier: The ID or name of the vector store + + limit: Maximum number of items to return per page (1-100) + + after: Cursor for forward pagination - get items after this position. Use last_cursor + from previous response. + + before: Cursor for backward pagination - get items before this position. Use + first_cursor from previous response. + + include_total: Whether to include total count in response (expensive operation) + + statuses: Status to filter by + + metadata_filter: Metadata filter to apply to the query + + 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 vector_store_identifier: + raise ValueError( + f"Expected a non-empty value for `vector_store_identifier` but received {vector_store_identifier!r}" + ) + return await self._post( + f"/v1/vector_stores/{vector_store_identifier}/files/list", + body=await async_maybe_transform( + { + "limit": limit, + "after": after, + "before": before, + "include_total": include_total, + "statuses": statuses, + "metadata_filter": metadata_filter, + }, + file_list_params.FileListParams, + ), + options=make_request_options( + extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout + ), + cast_to=FileListResponse, + ) + async def delete( self, file_id: str, @@ -769,6 +917,9 @@ def __init__(self, files: FilesResource) -> None: self.retrieve = to_raw_response_wrapper( files.retrieve, ) + self.list = to_raw_response_wrapper( + files.list, + ) self.delete = to_raw_response_wrapper( files.delete, ) @@ -787,6 +938,9 @@ def __init__(self, files: AsyncFilesResource) -> None: self.retrieve = async_to_raw_response_wrapper( files.retrieve, ) + self.list = async_to_raw_response_wrapper( + files.list, + ) self.delete = async_to_raw_response_wrapper( files.delete, ) @@ -805,6 +959,9 @@ def __init__(self, files: FilesResource) -> None: self.retrieve = to_streamed_response_wrapper( files.retrieve, ) + self.list = to_streamed_response_wrapper( + files.list, + ) self.delete = to_streamed_response_wrapper( files.delete, ) @@ -823,6 +980,9 @@ def __init__(self, files: AsyncFilesResource) -> None: self.retrieve = async_to_streamed_response_wrapper( files.retrieve, ) + self.list = async_to_streamed_response_wrapper( + files.list, + ) self.delete = async_to_streamed_response_wrapper( files.delete, ) diff --git a/src/mixedbread/types/vector_stores/__init__.py b/src/mixedbread/types/vector_stores/__init__.py index a75412b9..dcfaa05b 100644 --- a/src/mixedbread/types/vector_stores/__init__.py +++ b/src/mixedbread/types/vector_stores/__init__.py @@ -2,8 +2,10 @@ from __future__ import annotations +from .file_list_params import FileListParams as FileListParams from .vector_store_file import VectorStoreFile as VectorStoreFile from .file_create_params import FileCreateParams as FileCreateParams +from .file_list_response import FileListResponse as FileListResponse from .file_search_params import FileSearchParams as FileSearchParams from .rerank_config_param import RerankConfigParam as RerankConfigParam from .file_delete_response import FileDeleteResponse as FileDeleteResponse diff --git a/src/mixedbread/types/vector_stores/file_list_params.py b/src/mixedbread/types/vector_stores/file_list_params.py new file mode 100644 index 00000000..a4ac03c8 --- /dev/null +++ b/src/mixedbread/types/vector_stores/file_list_params.py @@ -0,0 +1,44 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing import List, Union, Iterable, Optional +from typing_extensions import TypeAlias, TypedDict + +from .vector_store_file_status import VectorStoreFileStatus +from ..shared_params.search_filter_condition import SearchFilterCondition + +__all__ = ["FileListParams", "MetadataFilter", "MetadataFilterUnionMember2"] + + +class FileListParams(TypedDict, total=False): + limit: int + """Maximum number of items to return per page (1-100)""" + + after: Optional[str] + """Cursor for forward pagination - get items after this position. + + Use last_cursor from previous response. + """ + + before: Optional[str] + """Cursor for backward pagination - get items before this position. + + Use first_cursor from previous response. + """ + + include_total: bool + """Whether to include total count in response (expensive operation)""" + + statuses: Optional[List[VectorStoreFileStatus]] + """Status to filter by""" + + metadata_filter: Optional[MetadataFilter] + """Metadata filter to apply to the query""" + + +MetadataFilterUnionMember2: TypeAlias = Union["SearchFilter", SearchFilterCondition] + +MetadataFilter: TypeAlias = Union["SearchFilter", SearchFilterCondition, Iterable[MetadataFilterUnionMember2]] + +from ..shared_params.search_filter import SearchFilter diff --git a/src/mixedbread/types/vector_stores/file_list_response.py b/src/mixedbread/types/vector_stores/file_list_response.py new file mode 100644 index 00000000..252d1c93 --- /dev/null +++ b/src/mixedbread/types/vector_stores/file_list_response.py @@ -0,0 +1,69 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from typing import List, Optional +from typing_extensions import Literal + +from ..._models import BaseModel +from .vector_store_file import VectorStoreFile + +__all__ = ["FileListResponse", "Pagination"] + + +class Pagination(BaseModel): + has_more: bool + """ + Contextual direction-aware flag: True if more items exist in the requested + pagination direction. For 'after': more items after this page. For 'before': + more items before this page. + """ + + first_cursor: Optional[str] = None + """Cursor of the first item in this page. + + Use for backward pagination. None if page is empty. + """ + + last_cursor: Optional[str] = None + """Cursor of the last item in this page. + + Use for forward pagination. None if page is empty. + """ + + total: Optional[int] = None + """Total number of items available across all pages. + + Only included when include_total=true was requested. Expensive operation - use + sparingly. + """ + + +class FileListResponse(BaseModel): + pagination: Pagination + """Response model for cursor-based pagination. + + Examples: Forward pagination response: { "has_more": true, "first_cursor": + "eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMSIsImlkIjoiYWJjMTIzIn0=", "last_cursor": + "eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMCIsImlkIjoieHl6Nzg5In0=", "total": null } + + Final page response: + { + "has_more": false, + "first_cursor": "eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0yOSIsImlkIjoibGFzdDEyMyJ9", + "last_cursor": "eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0yOCIsImlkIjoiZmluYWw0NTYifQ==", + "total": 42 + } + + Empty results: + { + "has_more": false, + "first_cursor": null, + "last_cursor": null, + "total": 0 + } + """ + + object: Optional[Literal["list"]] = None + """The object type of the response""" + + data: List[VectorStoreFile] + """The list of vector store files""" diff --git a/tests/api_resources/vector_stores/test_files.py b/tests/api_resources/vector_stores/test_files.py index 2c837f92..15a1c34b 100644 --- a/tests/api_resources/vector_stores/test_files.py +++ b/tests/api_resources/vector_stores/test_files.py @@ -11,6 +11,7 @@ from tests.utils import assert_matches_type from mixedbread.types.vector_stores import ( VectorStoreFile, + FileListResponse, FileDeleteResponse, FileSearchResponse, ) @@ -137,6 +138,63 @@ def test_path_params_retrieve(self, client: Mixedbread) -> None: vector_store_identifier="vector_store_identifier", ) + @parametrize + def test_method_list(self, client: Mixedbread) -> None: + file = client.vector_stores.files.list( + vector_store_identifier="vector_store_identifier", + ) + assert_matches_type(FileListResponse, file, path=["response"]) + + @parametrize + def test_method_list_with_all_params(self, client: Mixedbread) -> None: + file = client.vector_stores.files.list( + vector_store_identifier="vector_store_identifier", + limit=10, + after="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==", + before="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==", + include_total=False, + statuses=["pending"], + metadata_filter={ + "all": [], + "any": [], + "none": [], + }, + ) + assert_matches_type(FileListResponse, file, path=["response"]) + + @parametrize + def test_raw_response_list(self, client: Mixedbread) -> None: + response = client.vector_stores.files.with_raw_response.list( + vector_store_identifier="vector_store_identifier", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = response.parse() + assert_matches_type(FileListResponse, file, path=["response"]) + + @parametrize + def test_streaming_response_list(self, client: Mixedbread) -> None: + with client.vector_stores.files.with_streaming_response.list( + vector_store_identifier="vector_store_identifier", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = response.parse() + assert_matches_type(FileListResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_path_params_list(self, client: Mixedbread) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `vector_store_identifier` but received ''" + ): + client.vector_stores.files.with_raw_response.list( + vector_store_identifier="", + ) + @parametrize def test_method_delete(self, client: Mixedbread) -> None: file = client.vector_stores.files.delete( @@ -365,6 +423,63 @@ async def test_path_params_retrieve(self, async_client: AsyncMixedbread) -> None vector_store_identifier="vector_store_identifier", ) + @parametrize + async def test_method_list(self, async_client: AsyncMixedbread) -> None: + file = await async_client.vector_stores.files.list( + vector_store_identifier="vector_store_identifier", + ) + assert_matches_type(FileListResponse, file, path=["response"]) + + @parametrize + async def test_method_list_with_all_params(self, async_client: AsyncMixedbread) -> None: + file = await async_client.vector_stores.files.list( + vector_store_identifier="vector_store_identifier", + limit=10, + after="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==", + before="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==", + include_total=False, + statuses=["pending"], + metadata_filter={ + "all": [], + "any": [], + "none": [], + }, + ) + assert_matches_type(FileListResponse, file, path=["response"]) + + @parametrize + async def test_raw_response_list(self, async_client: AsyncMixedbread) -> None: + response = await async_client.vector_stores.files.with_raw_response.list( + vector_store_identifier="vector_store_identifier", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + file = await response.parse() + assert_matches_type(FileListResponse, file, path=["response"]) + + @parametrize + async def test_streaming_response_list(self, async_client: AsyncMixedbread) -> None: + async with async_client.vector_stores.files.with_streaming_response.list( + vector_store_identifier="vector_store_identifier", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + file = await response.parse() + assert_matches_type(FileListResponse, file, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_path_params_list(self, async_client: AsyncMixedbread) -> None: + with pytest.raises( + ValueError, match=r"Expected a non-empty value for `vector_store_identifier` but received ''" + ): + await async_client.vector_stores.files.with_raw_response.list( + vector_store_identifier="", + ) + @parametrize async def test_method_delete(self, async_client: AsyncMixedbread) -> None: file = await async_client.vector_stores.files.delete(