diff --git a/.release-please-manifest.json b/.release-please-manifest.json index a6fc929e..b8dda9bf 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.28.1" + ".": "0.29.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index 252dd5ab..e8f16265 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-c950bdc025566e8000354aef54c6f1c6c4ecbd92f9b6aca231701b8bebdf917e.yml -openapi_spec_hash: d76b267c95385a291cdb9401a9bdb95e +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/mixedbread%2Fmixedbread-a2720b37997685cf019808be37ab86f7071f55ac09c80916293489f6d3644160.yml +openapi_spec_hash: 61c24c03f1df5276b03e7bcce5266c21 config_hash: 22524d9aa927566851636768f6a861cd diff --git a/CHANGELOG.md b/CHANGELOG.md index b04977a5..fa3d31f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,19 @@ # Changelog +## 0.29.0 (2025-09-01) + +Full Changelog: [v0.28.1...v0.29.0](https://github.com/mixedbread-ai/mixedbread-python/compare/v0.28.1...v0.29.0) + +### Features + +* **api:** api update ([9869487](https://github.com/mixedbread-ai/mixedbread-python/commit/986948712a20dd99f1e5e3c40e8180ab11a727dd)) + + +### Chores + +* **internal:** add Sequence related utils ([cc3f90d](https://github.com/mixedbread-ai/mixedbread-python/commit/cc3f90d4f28cc313c2a20acd628149d56b7be6bd)) +* **internal:** update pyright exclude list ([8d57f64](https://github.com/mixedbread-ai/mixedbread-python/commit/8d57f64f771bfec95fad5f0d481ce4538fabeb1e)) + ## 0.28.1 (2025-08-27) Full Changelog: [v0.28.0...v0.28.1](https://github.com/mixedbread-ai/mixedbread-python/compare/v0.28.0...v0.28.1) diff --git a/pyproject.toml b/pyproject.toml index e8a36dfb..38780c53 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "mixedbread" -version = "0.28.1" +version = "0.29.0" description = "The official Python library for the Mixedbread API" dynamic = ["readme"] license = "Apache-2.0" @@ -148,6 +148,7 @@ exclude = [ "_dev", ".venv", ".nox", + ".git", ] reportImplicitOverride = true diff --git a/src/mixedbread/_types.py b/src/mixedbread/_types.py index 6fdd2f2b..baa4a78b 100644 --- a/src/mixedbread/_types.py +++ b/src/mixedbread/_types.py @@ -13,10 +13,21 @@ Mapping, TypeVar, Callable, + Iterator, Optional, Sequence, ) -from typing_extensions import Set, Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable +from typing_extensions import ( + Set, + Literal, + Protocol, + TypeAlias, + TypedDict, + SupportsIndex, + overload, + override, + runtime_checkable, +) import httpx import pydantic @@ -217,3 +228,26 @@ class _GenericAlias(Protocol): class HttpxSendArgs(TypedDict, total=False): auth: httpx.Auth follow_redirects: bool + + +_T_co = TypeVar("_T_co", covariant=True) + + +if TYPE_CHECKING: + # This works because str.__contains__ does not accept object (either in typeshed or at runtime) + # https://github.com/hauntsaninja/useful_types/blob/5e9710f3875107d068e7679fd7fec9cfab0eff3b/useful_types/__init__.py#L285 + class SequenceNotStr(Protocol[_T_co]): + @overload + def __getitem__(self, index: SupportsIndex, /) -> _T_co: ... + @overload + def __getitem__(self, index: slice, /) -> Sequence[_T_co]: ... + def __contains__(self, value: object, /) -> bool: ... + def __len__(self) -> int: ... + def __iter__(self) -> Iterator[_T_co]: ... + def index(self, value: Any, start: int = 0, stop: int = ..., /) -> int: ... + def count(self, value: Any, /) -> int: ... + def __reversed__(self) -> Iterator[_T_co]: ... +else: + # just point this to a normal `Sequence` at runtime to avoid having to special case + # deserializing our custom sequence type + SequenceNotStr = Sequence diff --git a/src/mixedbread/_utils/__init__.py b/src/mixedbread/_utils/__init__.py index d4fda26f..ca547ce5 100644 --- a/src/mixedbread/_utils/__init__.py +++ b/src/mixedbread/_utils/__init__.py @@ -38,6 +38,7 @@ extract_type_arg as extract_type_arg, is_iterable_type as is_iterable_type, is_required_type as is_required_type, + is_sequence_type as is_sequence_type, is_annotated_type as is_annotated_type, is_type_alias_type as is_type_alias_type, strip_annotated_type as strip_annotated_type, diff --git a/src/mixedbread/_utils/_typing.py b/src/mixedbread/_utils/_typing.py index 1bac9542..845cd6b2 100644 --- a/src/mixedbread/_utils/_typing.py +++ b/src/mixedbread/_utils/_typing.py @@ -26,6 +26,11 @@ def is_list_type(typ: type) -> bool: return (get_origin(typ) or typ) == list +def is_sequence_type(typ: type) -> bool: + origin = get_origin(typ) or typ + return origin == typing_extensions.Sequence or origin == typing.Sequence or origin == _c_abc.Sequence + + def is_iterable_type(typ: type) -> bool: """If the given type is `typing.Iterable[T]`""" origin = get_origin(typ) or typ diff --git a/src/mixedbread/_version.py b/src/mixedbread/_version.py index 12cfe033..95fda5ff 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.28.1" # x-release-please-version +__version__ = "0.29.0" # x-release-please-version diff --git a/src/mixedbread/types/vector_stores/vector_store_file.py b/src/mixedbread/types/vector_stores/vector_store_file.py index 96171171..a5378318 100644 --- a/src/mixedbread/types/vector_stores/vector_store_file.py +++ b/src/mixedbread/types/vector_stores/vector_store_file.py @@ -12,9 +12,12 @@ "VectorStoreFile", "Chunk", "ChunkTextInputChunk", - "ChunkImageURLInputChunkBase", - "ChunkAudioURLInputChunkBase", - "ChunkVideoURLInputChunkBase", + "ChunkImageURLInputChunk", + "ChunkImageURLInputChunkImageURL", + "ChunkAudioURLInputChunk", + "ChunkAudioURLInputChunkAudioURL", + "ChunkVideoURLInputChunk", + "ChunkVideoURLInputChunkVideoURL", ] @@ -41,7 +44,15 @@ class ChunkTextInputChunk(BaseModel): """Text content to process""" -class ChunkImageURLInputChunkBase(BaseModel): +class ChunkImageURLInputChunkImageURL(BaseModel): + url: str + """The image URL. Can be either a URL or a Data URI.""" + + format: Optional[str] = None + """The image format/mimetype""" + + +class ChunkImageURLInputChunk(BaseModel): chunk_index: int """position of the chunk in a file""" @@ -63,8 +74,16 @@ class ChunkImageURLInputChunkBase(BaseModel): summary: Optional[str] = None """summary of the image""" + image_url: ChunkImageURLInputChunkImageURL + """The image input specification.""" + + +class ChunkAudioURLInputChunkAudioURL(BaseModel): + url: str + """The audio URL. Can be either a URL or a Data URI.""" -class ChunkAudioURLInputChunkBase(BaseModel): + +class ChunkAudioURLInputChunk(BaseModel): chunk_index: int """position of the chunk in a file""" @@ -86,8 +105,16 @@ class ChunkAudioURLInputChunkBase(BaseModel): summary: Optional[str] = None """summary of the audio""" + audio_url: ChunkAudioURLInputChunkAudioURL + """The audio input specification.""" + + +class ChunkVideoURLInputChunkVideoURL(BaseModel): + url: str + """The video URL. Can be either a URL or a Data URI.""" -class ChunkVideoURLInputChunkBase(BaseModel): + +class ChunkVideoURLInputChunk(BaseModel): chunk_index: int """position of the chunk in a file""" @@ -109,9 +136,12 @@ class ChunkVideoURLInputChunkBase(BaseModel): summary: Optional[str] = None """summary of the video""" + video_url: ChunkVideoURLInputChunkVideoURL + """The video input specification.""" + Chunk: TypeAlias = Annotated[ - Union[ChunkTextInputChunk, ChunkImageURLInputChunkBase, ChunkAudioURLInputChunkBase, ChunkVideoURLInputChunkBase], + Union[ChunkTextInputChunk, ChunkImageURLInputChunk, ChunkAudioURLInputChunk, ChunkVideoURLInputChunk], PropertyInfo(discriminator="type"), ] diff --git a/tests/utils.py b/tests/utils.py index bd4c1519..020c34e2 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -4,7 +4,7 @@ import inspect import traceback import contextlib -from typing import Any, TypeVar, Iterator, cast +from typing import Any, TypeVar, Iterator, Sequence, cast from datetime import date, datetime from typing_extensions import Literal, get_args, get_origin, assert_type @@ -15,6 +15,7 @@ is_list_type, is_union_type, extract_type_arg, + is_sequence_type, is_annotated_type, is_type_alias_type, ) @@ -71,6 +72,13 @@ def assert_matches_type( if is_list_type(type_): return _assert_list_type(type_, value) + if is_sequence_type(type_): + assert isinstance(value, Sequence) + inner_type = get_args(type_)[0] + for entry in value: # type: ignore + assert_type(inner_type, entry) # type: ignore + return + if origin == str: assert isinstance(value, str) elif origin == int: