diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
index ac9a2e75..ff261bad 100644
--- a/.devcontainer/Dockerfile
+++ b/.devcontainer/Dockerfile
@@ -3,7 +3,7 @@ FROM mcr.microsoft.com/vscode/devcontainers/python:0-${VARIANT}
USER vscode
-RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.35.0" RYE_INSTALL_OPTION="--yes" bash
+RUN curl -sSf https://rye.astral.sh/get | RYE_VERSION="0.44.0" RYE_INSTALL_OPTION="--yes" bash
ENV PATH=/home/vscode/.rye/shims:$PATH
-RUN echo "[[ -d .venv ]] && source .venv/bin/activate" >> /home/vscode/.bashrc
+RUN echo "[[ -d .venv ]] && source .venv/bin/activate || export PATH=\$PATH" >> /home/vscode/.bashrc
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index bbeb30b1..c17fdc16 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -24,6 +24,9 @@
}
}
}
+ },
+ "features": {
+ "ghcr.io/devcontainers/features/node:1": {}
}
// Features to add to the dev container. More info: https://containers.dev/features.
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 40293964..fefaf86b 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,28 +1,32 @@
name: CI
on:
push:
- branches:
- - main
+ branches-ignore:
+ - 'generated'
+ - 'codegen/**'
+ - 'integrated/**'
+ - 'stl-preview-head/**'
+ - 'stl-preview-base/**'
pull_request:
- branches:
- - main
- - next
+ branches-ignore:
+ - 'stl-preview-head/**'
+ - 'stl-preview-base/**'
jobs:
lint:
+ timeout-minutes: 10
name: lint
- runs-on: ubuntu-latest
-
-
+ runs-on: ${{ github.repository == 'stainless-sdks/mixedbread-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
+ if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Install Rye
run: |
curl -sSf https://rye.astral.sh/get | bash
echo "$HOME/.rye/shims" >> $GITHUB_PATH
env:
- RYE_VERSION: '0.35.0'
+ RYE_VERSION: '0.44.0'
RYE_INSTALL_OPTION: '--yes'
- name: Install dependencies
@@ -30,19 +34,61 @@ jobs:
- name: Run lints
run: ./scripts/lint
+
+ build:
+ if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
+ timeout-minutes: 10
+ name: build
+ permissions:
+ contents: read
+ id-token: write
+ runs-on: ${{ github.repository == 'stainless-sdks/mixedbread-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Install Rye
+ run: |
+ curl -sSf https://rye.astral.sh/get | bash
+ echo "$HOME/.rye/shims" >> $GITHUB_PATH
+ env:
+ RYE_VERSION: '0.44.0'
+ RYE_INSTALL_OPTION: '--yes'
+
+ - name: Install dependencies
+ run: rye sync --all-features
+
+ - name: Run build
+ run: rye build
+
+ - name: Get GitHub OIDC Token
+ if: github.repository == 'stainless-sdks/mixedbread-python'
+ id: github-oidc
+ uses: actions/github-script@v8
+ with:
+ script: core.setOutput('github_token', await core.getIDToken());
+
+ - name: Upload tarball
+ if: github.repository == 'stainless-sdks/mixedbread-python'
+ env:
+ URL: https://pkg.stainless.com/s
+ AUTH: ${{ steps.github-oidc.outputs.github_token }}
+ SHA: ${{ github.sha }}
+ run: ./scripts/utils/upload-artifact.sh
+
test:
+ timeout-minutes: 10
name: test
- runs-on: ubuntu-latest
-
+ runs-on: ${{ github.repository == 'stainless-sdks/mixedbread-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
+ if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v6
- name: Install Rye
run: |
curl -sSf https://rye.astral.sh/get | bash
echo "$HOME/.rye/shims" >> $GITHUB_PATH
env:
- RYE_VERSION: '0.35.0'
+ RYE_VERSION: '0.44.0'
RYE_INSTALL_OPTION: '--yes'
- name: Bootstrap
@@ -50,4 +96,3 @@ jobs:
- name: Run tests
run: ./scripts/test
-
diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml
new file mode 100644
index 00000000..4bed0df6
--- /dev/null
+++ b/.github/workflows/publish-pypi.yml
@@ -0,0 +1,31 @@
+# This workflow is triggered when a GitHub release is created.
+# It can also be run manually to re-publish to PyPI in case it failed for some reason.
+# You can run this workflow by navigating to https://www.github.com/mixedbread-ai/mixedbread-python/actions/workflows/publish-pypi.yml
+name: Publish PyPI
+on:
+ workflow_dispatch:
+
+ release:
+ types: [published]
+
+jobs:
+ publish:
+ name: publish
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Install Rye
+ run: |
+ curl -sSf https://rye.astral.sh/get | bash
+ echo "$HOME/.rye/shims" >> $GITHUB_PATH
+ env:
+ RYE_VERSION: '0.44.0'
+ RYE_INSTALL_OPTION: '--yes'
+
+ - name: Publish to PyPI
+ run: |
+ bash ./bin/publish-pypi
+ env:
+ PYPI_TOKEN: ${{ secrets.MIXEDBREAD_PYPI_TOKEN || secrets.PYPI_TOKEN }}
diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml
new file mode 100644
index 00000000..5f4defaf
--- /dev/null
+++ b/.github/workflows/release-doctor.yml
@@ -0,0 +1,21 @@
+name: Release Doctor
+on:
+ pull_request:
+ branches:
+ - main
+ workflow_dispatch:
+
+jobs:
+ release_doctor:
+ name: release doctor
+ runs-on: ubuntu-latest
+ if: github.repository == 'mixedbread-ai/mixedbread-python' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next')
+
+ steps:
+ - uses: actions/checkout@v6
+
+ - name: Check release environment
+ run: |
+ bash ./bin/check-release-environment
+ env:
+ PYPI_TOKEN: ${{ secrets.MIXEDBREAD_PYPI_TOKEN || secrets.PYPI_TOKEN }}
diff --git a/.gitignore b/.gitignore
index 87797408..95ceb189 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,4 @@
.prism.log
-.vscode
_dev
__pycache__
diff --git a/.release-please-manifest.json b/.release-please-manifest.json
new file mode 100644
index 00000000..fea34540
--- /dev/null
+++ b/.release-please-manifest.json
@@ -0,0 +1,3 @@
+{
+ ".": "1.0.0"
+}
\ No newline at end of file
diff --git a/.stats.yml b/.stats.yml
index 17a58af2..8e94f914 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,2 +1,4 @@
-configured_endpoints: 30
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/mixedbread%2Fmixedbread-a9b8728ee918c023a0e1458cfab7f26721d4ac851c533c6ce2f53411bc1576d1.yml
+configured_endpoints: 56
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/mixedbread%2Fmixedbread-83afe8d98b70a903eb9aa13b512b18aa3df9e5ec4c784bded17ac78d0a163c3c.yml
+openapi_spec_hash: 90b33b757e12f21c94705b6243054b5f
+config_hash: c32ffa6858a02d7f23f6f3dda0b461ed
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000..5b010307
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,3 @@
+{
+ "python.analysis.importFormat": "relative",
+}
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5ba0d641..ed6fa9a1 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -17,8 +17,7 @@ $ rye sync --all-features
You can then run scripts using `rye run python script.py` or by activating the virtual environment:
```sh
-$ rye shell
-# or manually activate - https://docs.python.org/3/library/venv.html#how-venvs-work
+# Activate the virtual environment - https://docs.python.org/3/library/venv.html#how-venvs-work
$ source .venv/bin/activate
# now you can omit the `rye run` prefix
@@ -63,7 +62,7 @@ If you’d like to use the repository from source, you can either install from g
To install via git:
```sh
-$ pip install git+ssh://git@github.com/stainless-sdks/mixedbread-python.git
+$ pip install git+ssh://git@github.com/mixedbread-ai/mixedbread-python.git
```
Alternatively, you can build from source and install the wheel file:
@@ -89,8 +88,7 @@ $ pip install ./path-to-wheel-file.whl
Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests.
```sh
-# you will need npm installed
-$ npx prism mock path/to/your/openapi.yml
+$ ./scripts/mock
```
```sh
@@ -121,7 +119,7 @@ the changes aren't made through the automated pipeline, you may want to make rel
### Publish with a GitHub workflow
-You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/stainless-sdks/mixedbread-python/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up.
+You can release to package managers by using [the `Publish PyPI` GitHub action](https://www.github.com/mixedbread-ai/mixedbread-python/actions/workflows/publish-pypi.yml). This requires a setup organization or repository secret to be set up.
### Publish manually
diff --git a/LICENSE b/LICENSE
index 16abae3f..4c96cdcc 100644
--- a/LICENSE
+++ b/LICENSE
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
- Copyright 2025 Mixedbread
+ Copyright 2026 Mixedbread
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/README.md b/README.md
index a22d9f2a..33fcbd13 100644
--- a/README.md
+++ b/README.md
@@ -1,48 +1,46 @@
-# Mixedbread Python API library
+# Mixedbread API Python SDK API library
-[](https://pypi.org/project/mixedbread/)
+
+[)](https://pypi.org/project/mixedbread/)
-The Mixedbread Python library provides convenient access to the Mixedbread REST API from any Python 3.8+
+The Mixedbread API Python SDK library provides convenient access to the Mixedbread REST API from any Python 3.9+
application. The library includes type definitions for all request params and response fields,
and offers both synchronous and asynchronous clients powered by [httpx](https://github.com/encode/httpx).
-It is generated with [Stainless](https://www.stainlessapi.com/).
+It is generated with [Stainless](https://www.stainless.com/).
## Documentation
-The REST API documentation can be found on [docs.mixedbread.com](https://docs.mixedbread.com). The full API of this library can be found in [api.md](api.md).
+The REST API documentation can be found on [mixedbread.com](https://mixedbread.com/docs). The full API of this library can be found in [api.md](api.md).
## Installation
```sh
-# install from this staging repo
-pip install git+ssh://git@github.com/stainless-sdks/mixedbread-python.git
+# install from PyPI
+pip install mixedbread
```
-> [!NOTE]
-> Once this package is [published to PyPI](https://app.stainlessapi.com/docs/guides/publish), this will become: `pip install --pre mixedbread`
-
## Usage
The full API of this library can be found in [api.md](api.md).
```python
+import os
from mixedbread import Mixedbread
client = Mixedbread(
- # defaults to "production".
- environment="environment_1",
+ api_key=os.environ.get("MXBAI_API_KEY"), # This is the default and can be omitted
+ # or 'production' | 'local'; defaults to "production".
+ environment="development",
)
-file_object = client.files.create(
- file=b"raw file contents",
-)
-print(file_object.id)
+store = client.stores.create()
+print(store.id)
```
While you can provide an `api_key` keyword argument,
we recommend using [python-dotenv](https://pypi.org/project/python-dotenv/)
-to add `API_KEY="My API Key"` to your `.env` file
+to add `MXBAI_API_KEY="My API Key"` to your `.env` file
so that your API Key is not stored in source control.
## Async usage
@@ -50,20 +48,20 @@ so that your API Key is not stored in source control.
Simply import `AsyncMixedbread` instead of `Mixedbread` and use `await` with each API call:
```python
+import os
import asyncio
from mixedbread import AsyncMixedbread
client = AsyncMixedbread(
- # defaults to "production".
- environment="environment_1",
+ api_key=os.environ.get("MXBAI_API_KEY"), # This is the default and can be omitted
+ # or 'production' | 'local'; defaults to "production".
+ environment="development",
)
async def main() -> None:
- file_object = await client.files.create(
- file=b"raw file contents",
- )
- print(file_object.id)
+ store = await client.stores.create()
+ print(store.id)
asyncio.run(main())
@@ -71,6 +69,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:
+ store = await client.stores.create()
+ print(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:
@@ -80,6 +110,101 @@ Nested request parameters are [TypedDicts](https://docs.python.org/3/library/typ
Typed requests and responses provide autocomplete and documentation within your editor. If you would like to see type errors in VS Code to help catch bugs earlier, set `python.analysis.typeCheckingMode` to `basic`.
+## Pagination
+
+List methods in the Mixedbread API are paginated.
+
+This library provides auto-paginating iterators with each list response, so you do not have to request successive pages manually:
+
+```python
+from mixedbread import Mixedbread
+
+client = Mixedbread()
+
+all_stores = []
+# Automatically fetches more pages as needed.
+for store in client.stores.list():
+ # Do something with store here
+ all_stores.append(store)
+print(all_stores)
+```
+
+Or, asynchronously:
+
+```python
+import asyncio
+from mixedbread import AsyncMixedbread
+
+client = AsyncMixedbread()
+
+
+async def main() -> None:
+ all_stores = []
+ # Iterate through items across all pages, issuing requests as needed.
+ async for store in client.stores.list():
+ all_stores.append(store)
+ print(all_stores)
+
+
+asyncio.run(main())
+```
+
+Alternatively, you can use the `.has_next_page()`, `.next_page_info()`, or `.get_next_page()` methods for more granular control working with pages:
+
+```python
+first_page = await client.stores.list()
+if first_page.has_next_page():
+ print(f"will fetch next page using these details: {first_page.next_page_info()}")
+ next_page = await first_page.get_next_page()
+ print(f"number of items we just fetched: {len(next_page.data)}")
+
+# Remove `await` for non-async usage.
+```
+
+Or just work directly with the returned data:
+
+```python
+first_page = await client.stores.list()
+
+print(f"next page cursor: {first_page.pagination.last_cursor}") # => "next page cursor: ..."
+for store in first_page.data:
+ print(store.id)
+
+# Remove `await` for non-async usage.
+```
+
+## Nested params
+
+Nested parameters are dictionaries, typed using `TypedDict`, for example:
+
+```python
+from mixedbread import Mixedbread
+
+client = Mixedbread()
+
+store = client.stores.create(
+ expires_after={},
+)
+print(store.expires_after)
+```
+
+## File uploads
+
+Request parameters that correspond to file uploads can be passed as `bytes`, or a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance or a tuple of `(filename, contents, media type)`.
+
+```python
+from pathlib import Path
+from mixedbread import Mixedbread
+
+client = Mixedbread()
+
+client.files.create(
+ file=Path("/path/to/file"),
+)
+```
+
+The async client uses the exact same interface. If you pass a [`PathLike`](https://docs.python.org/3/library/os.html#os.PathLike) instance, the file contents will be read asynchronously automatically.
+
## Handling errors
When the library is unable to connect to the API (for example, due to network connection problems or a timeout), a subclass of `mixedbread.APIConnectionError` is raised.
@@ -96,9 +221,7 @@ from mixedbread import Mixedbread
client = Mixedbread()
try:
- client.files.create(
- file=b"raw file contents",
- )
+ client.stores.create()
except mixedbread.APIConnectionError as e:
print("The server could not be reached")
print(e.__cause__) # an underlying Exception, likely raised within httpx.
@@ -141,15 +264,13 @@ client = Mixedbread(
)
# Or, configure per-request:
-client.with_options(max_retries=5).files.create(
- file=b"raw file contents",
-)
+client.with_options(max_retries=5).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
@@ -166,9 +287,7 @@ client = Mixedbread(
)
# Override per-request:
-client.with_options(timeout=5.0).files.create(
- file=b"raw file contents",
-)
+client.with_options(timeout=5.0).stores.create()
```
On timeout, an `APITimeoutError` is thrown.
@@ -209,18 +328,16 @@ The "raw" Response object can be accessed by prefixing `.with_raw_response.` to
from mixedbread import Mixedbread
client = Mixedbread()
-response = client.files.with_raw_response.create(
- file=b'raw file contents',
-)
+response = client.stores.with_raw_response.create()
print(response.headers.get('X-My-Header'))
-file = response.parse() # get the object that `files.create()` would have returned
-print(file.id)
+store = response.parse() # get the object that `stores.create()` would have returned
+print(store.id)
```
-These methods return an [`APIResponse`](https://github.com/stainless-sdks/mixedbread-python/tree/main/src/mixedbread/_response.py) object.
+These methods return an [`APIResponse`](https://github.com/mixedbread-ai/mixedbread-python/tree/main/src/mixedbread/_response.py) object.
-The async client returns an [`AsyncAPIResponse`](https://github.com/stainless-sdks/mixedbread-python/tree/main/src/mixedbread/_response.py) with the same structure, the only difference being `await`able methods for reading the response content.
+The async client returns an [`AsyncAPIResponse`](https://github.com/mixedbread-ai/mixedbread-python/tree/main/src/mixedbread/_response.py) with the same structure, the only difference being `await`able methods for reading the response content.
#### `.with_streaming_response`
@@ -229,9 +346,7 @@ The above interface eagerly reads the full response body when you make the reque
To stream the response body, use `.with_streaming_response` instead, which requires a context manager and only reads the response body once you call `.read()`, `.text()`, `.json()`, `.iter_bytes()`, `.iter_text()`, `.iter_lines()` or `.parse()`. In the async client, these are async methods.
```python
-with client.files.with_streaming_response.create(
- file=b"raw file contents",
-) as response:
+with client.stores.with_streaming_response.create() as response:
print(response.headers.get("X-My-Header"))
for line in response.iter_lines():
@@ -326,7 +441,7 @@ This package generally follows [SemVer](https://semver.org/spec/v2.0.0.html) con
We take backwards-compatibility seriously and work hard to ensure you can rely on a smooth upgrade experience.
-We are keen for your feedback; please open an [issue](https://www.github.com/stainless-sdks/mixedbread-python/issues) with questions, bugs, or suggestions.
+We are keen for your feedback; please open an [issue](https://www.github.com/mixedbread-ai/mixedbread-python/issues) with questions, bugs, or suggestions.
### Determining the installed version
@@ -341,7 +456,7 @@ print(mixedbread.__version__)
## Requirements
-Python 3.8 or higher.
+Python 3.9 or higher.
## Contributing
diff --git a/SECURITY.md b/SECURITY.md
index 61396288..9485d544 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -2,9 +2,9 @@
## Reporting Security Issues
-This SDK is generated by [Stainless Software Inc](http://stainlessapi.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken.
+This SDK is generated by [Stainless Software Inc](http://stainless.com). Stainless takes security seriously, and encourages you to report any security vulnerability promptly so that appropriate action can be taken.
-To report a security issue, please contact the Stainless team at security@stainlessapi.com.
+To report a security issue, please contact the Stainless team at security@stainless.com.
## Responsible Disclosure
@@ -16,11 +16,13 @@ before making any information public.
## Reporting Non-SDK Related Security Issues
If you encounter security issues that are not directly related to SDKs but pertain to the services
-or products provided by Mixedbread please follow the respective company's security reporting guidelines.
+or products provided by Mixedbread, please follow the respective company's security reporting guidelines.
### Mixedbread Terms and Policies
-Please contact dev-feedback@mixedbread.com for any questions or concerns regarding security of our services.
+Our Security Policy can be found at [Security Policy URL](https://mixedbread.com/pages/privacy).
+
+Please contact support@mixedbread.com for any questions or concerns regarding the security of our services.
---
diff --git a/api.md b/api.md
index ab0ed54c..53e03831 100644
--- a/api.md
+++ b/api.md
@@ -1,108 +1,149 @@
-# ServiceInfo
+# Shared Types
+
+```python
+from mixedbread.types import SearchFilter, SearchFilterCondition, Usage
+```
+
+# Mixedbread
Types:
```python
-from mixedbread.types import InfoResponse
+from mixedbread.types import (
+ Embedding,
+ EmbeddingCreateResponse,
+ MultiEncodingEmbedding,
+ InfoResponse,
+ RerankResponse,
+)
```
Methods:
-- client.service_info.retrieve() -> InfoResponse
+- client.embed(\*\*params) -> EmbeddingCreateResponse
+- client.info() -> InfoResponse
+- client.rerank(\*\*params) -> RerankResponse
-# Files
+# Stores
Types:
```python
-from mixedbread.types import FileDeleted, FileObject, FileListResponse
+from mixedbread.types import (
+ ExpiresAfter,
+ ScoredAudioURLInputChunk,
+ ScoredImageURLInputChunk,
+ ScoredTextInputChunk,
+ ScoredVideoURLInputChunk,
+ Store,
+ StoreChunkSearchOptions,
+ StoreDeleteResponse,
+ StoreMetadataFacetsResponse,
+ StoreQuestionAnsweringResponse,
+ StoreSearchResponse,
+)
```
Methods:
-- client.files.create(\*\*params) -> FileObject
-- client.files.retrieve(file_id) -> FileObject
-- client.files.update(file_id, \*\*params) -> FileObject
-- client.files.list(\*\*params) -> FileListResponse
-- client.files.delete(file_id) -> FileDeleted
-
-## Content
-
-Methods:
-
-- client.files.content.retrieve(file_id) -> BinaryAPIResponse
+- client.stores.create(\*\*params) -> Store
+- client.stores.retrieve(store_identifier) -> Store
+- client.stores.update(store_identifier, \*\*params) -> Store
+- client.stores.list(\*\*params) -> SyncCursor[Store]
+- client.stores.delete(store_identifier) -> StoreDeleteResponse
+- client.stores.metadata_facets(\*\*params) -> StoreMetadataFacetsResponse
+- client.stores.question_answering(\*\*params) -> StoreQuestionAnsweringResponse
+- client.stores.search(\*\*params) -> StoreSearchResponse
-# Completions
+## Files
Types:
```python
-from mixedbread.types import CompletionCreateResponse
+from mixedbread.types.stores import (
+ ScoredStoreFile,
+ StoreFileStatus,
+ StoreFile,
+ FileListResponse,
+ FileDeleteResponse,
+ FileSearchResponse,
+)
```
Methods:
-- client.completions.create() -> object
+- client.stores.files.create(store_identifier, \*\*params) -> StoreFile
+- client.stores.files.retrieve(file_identifier, \*, store_identifier, \*\*params) -> StoreFile
+- client.stores.files.update(file_identifier, \*, store_identifier, \*\*params) -> StoreFile
+- client.stores.files.list(store_identifier, \*\*params) -> FileListResponse
+- client.stores.files.delete(file_identifier, \*, store_identifier) -> FileDeleteResponse
+- client.stores.files.search(\*\*params) -> FileSearchResponse
-# VectorStores
+# Parsing
+
+## Jobs
Types:
```python
-from mixedbread.types import (
- VectorStore,
- VectorStoreDeleted,
- VectorStoreListResponse,
- VectorStoreQuestionAnsweringResponse,
- VectorStoreSearchResponse,
+from mixedbread.types.parsing import (
+ ChunkingStrategy,
+ ElementType,
+ ParsingJobStatus,
+ ParsingJob,
+ ReturnFormat,
+ JobListResponse,
+ JobDeleteResponse,
)
```
Methods:
-- client.vector_stores.create(\*\*params) -> VectorStore
-- client.vector_stores.retrieve(vector_store_id) -> VectorStore
-- client.vector_stores.update(vector_store_id, \*\*params) -> VectorStore
-- client.vector_stores.list(\*\*params) -> VectorStoreListResponse
-- client.vector_stores.delete(vector_store_id) -> VectorStoreDeleted
-- client.vector_stores.question_answering(\*\*params) -> object
-- client.vector_stores.search(\*\*params) -> VectorStoreSearchResponse
+- client.parsing.jobs.create(\*\*params) -> ParsingJob
+- client.parsing.jobs.retrieve(job_id) -> ParsingJob
+- client.parsing.jobs.list(\*\*params) -> SyncCursor[JobListResponse]
+- client.parsing.jobs.delete(job_id) -> JobDeleteResponse
+- client.parsing.jobs.cancel(job_id) -> ParsingJob
-## Files
+# Files
Types:
```python
-from mixedbread.types.vector_stores import (
- VectorStoreFile,
- VectorStoreFileDeleted,
- FileListResponse,
- FileSearchResponse,
-)
+from mixedbread.types import FileObject, PaginationWithTotal, FileDeleteResponse
```
Methods:
-- client.vector_stores.files.create(vector_store_id, \*\*params) -> VectorStoreFile
-- client.vector_stores.files.retrieve(file_id, \*, vector_store_id) -> VectorStoreFile
-- client.vector_stores.files.list(vector_store_id, \*\*params) -> FileListResponse
-- client.vector_stores.files.delete(file_id, \*, vector_store_id) -> VectorStoreFileDeleted
-- client.vector_stores.files.search(\*\*params) -> FileSearchResponse
-
-# Parsing
+- client.files.create(\*\*params) -> FileObject
+- client.files.retrieve(file_id) -> FileObject
+- client.files.update(file_id, \*\*params) -> FileObject
+- client.files.list(\*\*params) -> SyncCursor[FileObject]
+- client.files.delete(file_id) -> FileDeleteResponse
+- client.files.content(file_id) -> BinaryAPIResponse
-## Jobs
+## Uploads
Types:
```python
-from mixedbread.types.parsing import ParsingJob
+from mixedbread.types.files import (
+ MultipartUploadPart,
+ MultipartUploadPartURL,
+ UploadCreateResponse,
+ UploadRetrieveResponse,
+ UploadListResponse,
+ UploadAbortResponse,
+)
```
Methods:
-- client.parsing.jobs.create(\*\*params) -> ParsingJob
-- client.parsing.jobs.retrieve(job_id) -> ParsingJob
+- client.files.uploads.create(\*\*params) -> UploadCreateResponse
+- client.files.uploads.retrieve(upload_id) -> UploadRetrieveResponse
+- client.files.uploads.list() -> UploadListResponse
+- client.files.uploads.abort(upload_id) -> UploadAbortResponse
+- client.files.uploads.complete(upload_id, \*\*params) -> FileObject
# Extractions
@@ -138,7 +179,7 @@ Methods:
Types:
```python
-from mixedbread.types.extractions import ExtractionResult
+from mixedbread.types.extractions import ExtractionResult, ImageURLInput, TextInput
```
Methods:
@@ -150,21 +191,72 @@ Methods:
Types:
```python
-from mixedbread.types import EmbeddingCreateResponse
+from mixedbread.types import EncodingFormat
```
Methods:
- client.embeddings.create(\*\*params) -> EmbeddingCreateResponse
-# Reranking
+# DataSources
+
+Types:
+
+```python
+from mixedbread.types import (
+ DataSource,
+ DataSourceOauth2Params,
+ DataSourceType,
+ LinearDataSource,
+ NotionDataSource,
+ Oauth2Params,
+ DataSourceDeleteResponse,
+)
+```
+
+Methods:
+
+- client.data_sources.create(\*\*params) -> DataSource
+- client.data_sources.retrieve(data_source_id) -> DataSource
+- client.data_sources.update(data_source_id, \*\*params) -> DataSource
+- client.data_sources.list(\*\*params) -> SyncCursor[DataSource]
+- client.data_sources.delete(data_source_id) -> DataSourceDeleteResponse
+
+## Connectors
+
+Types:
+
+```python
+from mixedbread.types.data_sources import DataSourceConnector, ConnectorDeleteResponse
+```
+
+Methods:
+
+- client.data_sources.connectors.create(data_source_id, \*\*params) -> DataSourceConnector
+- client.data_sources.connectors.retrieve(connector_id, \*, data_source_id) -> DataSourceConnector
+- client.data_sources.connectors.update(connector_id, \*, data_source_id, \*\*params) -> DataSourceConnector
+- client.data_sources.connectors.list(data_source_id, \*\*params) -> SyncCursor[DataSourceConnector]
+- client.data_sources.connectors.delete(connector_id, \*, data_source_id) -> ConnectorDeleteResponse
+
+# APIKeys
Types:
```python
-from mixedbread.types import RerankingCreateResponse
+from mixedbread.types import APIKey, APIKeyCreated, APIKeyDeleteResponse
```
Methods:
-- client.reranking.create(\*\*params) -> RerankingCreateResponse
+- client.api_keys.create(\*\*params) -> APIKeyCreated
+- client.api_keys.retrieve(api_key_id) -> APIKey
+- client.api_keys.list(\*\*params) -> SyncLimitOffset[APIKey]
+- client.api_keys.delete(api_key_id) -> APIKeyDeleteResponse
+- client.api_keys.reroll(api_key_id) -> APIKeyCreated
+- client.api_keys.revoke(api_key_id) -> APIKey
+
+# Chat
+
+Methods:
+
+- client.chat.create_completion() -> object
diff --git a/bin/check-release-environment b/bin/check-release-environment
new file mode 100644
index 00000000..b845b0f4
--- /dev/null
+++ b/bin/check-release-environment
@@ -0,0 +1,21 @@
+#!/usr/bin/env bash
+
+errors=()
+
+if [ -z "${PYPI_TOKEN}" ]; then
+ errors+=("The PYPI_TOKEN secret has not been set. Please set it in either this repository's secrets or your organization secrets.")
+fi
+
+lenErrors=${#errors[@]}
+
+if [[ lenErrors -gt 0 ]]; then
+ echo -e "Found the following errors in the release environment:\n"
+
+ for error in "${errors[@]}"; do
+ echo -e "- $error\n"
+ done
+
+ exit 1
+fi
+
+echo "The environment is ready to push releases!"
diff --git a/bin/publish-pypi b/bin/publish-pypi
index 05bfccbb..826054e9 100644
--- a/bin/publish-pypi
+++ b/bin/publish-pypi
@@ -3,7 +3,4 @@
set -eux
mkdir -p dist
rye build --clean
-# Patching importlib-metadata version until upstream library version is updated
-# https://github.com/pypa/twine/issues/977#issuecomment-2189800841
-"$HOME/.rye/self/bin/python3" -m pip install 'importlib-metadata==7.2.1'
rye publish --yes --token=$PYPI_TOKEN
diff --git a/mypy.ini b/mypy.ini
deleted file mode 100644
index 4f4fe9c6..00000000
--- a/mypy.ini
+++ /dev/null
@@ -1,50 +0,0 @@
-[mypy]
-pretty = True
-show_error_codes = True
-
-# Exclude _files.py because mypy isn't smart enough to apply
-# the correct type narrowing and as this is an internal module
-# it's fine to just use Pyright.
-#
-# We also exclude our `tests` as mypy doesn't always infer
-# types correctly and Pyright will still catch any type errors.
-exclude = ^(src/mixedbread/_files\.py|_dev/.*\.py|tests/.*)$
-
-strict_equality = True
-implicit_reexport = True
-check_untyped_defs = True
-no_implicit_optional = True
-
-warn_return_any = True
-warn_unreachable = True
-warn_unused_configs = True
-
-# Turn these options off as it could cause conflicts
-# with the Pyright options.
-warn_unused_ignores = False
-warn_redundant_casts = False
-
-disallow_any_generics = True
-disallow_untyped_defs = True
-disallow_untyped_calls = True
-disallow_subclassing_any = True
-disallow_incomplete_defs = True
-disallow_untyped_decorators = True
-cache_fine_grained = True
-
-# By default, mypy reports an error if you assign a value to the result
-# of a function call that doesn't return anything. We do this in our test
-# cases:
-# ```
-# result = ...
-# assert result is None
-# ```
-# Changing this codegen to make mypy happy would increase complexity
-# and would not be worth it.
-disable_error_code = func-returns-value
-
-# https://github.com/python/mypy/issues/12162
-[mypy.overrides]
-module = "black.files.*"
-ignore_errors = true
-ignore_missing_imports = true
diff --git a/pyproject.toml b/pyproject.toml
index 9b12293b..e563f7e5 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,29 +1,32 @@
[project]
name = "mixedbread"
-version = "0.1.0-alpha.11"
-description = "The official Python library for the mixedbread API"
+version = "1.0.0"
+description = "The official Python library for the Mixedbread API"
dynamic = ["readme"]
license = "Apache-2.0"
authors = [
-{ name = "Mixedbread", email = "dev-feedback@mixedbread.com" },
+{ name = "Mixedbread", email = "support@mixedbread.com" },
]
+
dependencies = [
- "httpx>=0.23.0, <1",
- "pydantic>=1.9.0, <3",
- "typing-extensions>=4.10, <5",
- "anyio>=3.5.0, <5",
- "distro>=1.7.0, <2",
- "sniffio",
+ "httpx>=0.23.0, <1",
+ "pydantic>=1.9.0, <3",
+ "typing-extensions>=4.10, <5",
+ "anyio>=3.5.0, <5",
+ "distro>=1.7.0, <2",
+ "sniffio",
]
-requires-python = ">= 3.8"
+
+requires-python = ">= 3.9"
classifiers = [
"Typing :: Typed",
"Intended Audience :: Developers",
- "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
"Operating System :: OS Independent",
"Operating System :: POSIX",
"Operating System :: MacOS",
@@ -34,17 +37,18 @@ classifiers = [
]
[project.urls]
-Homepage = "https://github.com/stainless-sdks/mixedbread-python"
-Repository = "https://github.com/stainless-sdks/mixedbread-python"
-
+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.9"]
[tool.rye]
managed = true
# version pins are in requirements-dev.lock
dev-dependencies = [
- "pyright>=1.1.359",
- "mypy",
+ "pyright==1.1.399",
+ "mypy==1.17",
"respx",
"pytest",
"pytest-asyncio",
@@ -54,7 +58,7 @@ dev-dependencies = [
"dirty-equals>=0.6.0",
"importlib-metadata>=6.7.0",
"rich>=13.7.1",
- "nest_asyncio==1.6.0",
+ "pytest-xdist>=3.6.1",
]
[tool.rye.scripts]
@@ -65,7 +69,7 @@ format = { chain = [
# run formatting again to fix any inconsistencies when imports are stripped
"format:ruff",
]}
-"format:docs" = "python scripts/utils/ruffen-docs.py README.md api.md"
+"format:docs" = "bash -c 'python scripts/utils/ruffen-docs.py README.md $(find . -type f -name api.md)'"
"format:ruff" = "ruff format"
"lint" = { chain = [
@@ -87,7 +91,7 @@ typecheck = { chain = [
"typecheck:mypy" = "mypy ."
[build-system]
-requires = ["hatchling", "hatch-fancy-pypi-readme"]
+requires = ["hatchling==1.26.3", "hatch-fancy-pypi-readme"]
build-backend = "hatchling.build"
[tool.hatch.build]
@@ -122,13 +126,14 @@ path = "README.md"
[[tool.hatch.metadata.hooks.fancy-pypi-readme.substitutions]]
# replace relative links with absolute links
pattern = '\[(.+?)\]\(((?!https?://)\S+?)\)'
-replacement = '[\1](https://github.com/stainless-sdks/mixedbread-python/tree/main/\g<2>)'
+replacement = '[\1](https://github.com/mixedbread-ai/mixedbread-python/tree/main/\g<2>)'
[tool.pytest.ini_options]
testpaths = ["tests"]
-addopts = "--tb=short"
+addopts = "--tb=short -n auto"
xfail_strict = true
asyncio_mode = "auto"
+asyncio_default_fixture_loop_scope = "session"
filterwarnings = [
"error"
]
@@ -138,24 +143,77 @@ filterwarnings = [
# there are a couple of flags that are still disabled by
# default in strict mode as they are experimental and niche.
typeCheckingMode = "strict"
-pythonVersion = "3.8"
+pythonVersion = "3.9"
exclude = [
"_dev",
".venv",
".nox",
+ ".git",
]
reportImplicitOverride = true
+reportOverlappingOverload = false
reportImportCycles = false
reportPrivateUsage = false
+[tool.mypy]
+pretty = true
+show_error_codes = true
+
+# Exclude _files.py because mypy isn't smart enough to apply
+# the correct type narrowing and as this is an internal module
+# it's fine to just use Pyright.
+#
+# We also exclude our `tests` as mypy doesn't always infer
+# types correctly and Pyright will still catch any type errors.
+exclude = ['src/mixedbread/_files.py', '_dev/.*.py', 'tests/.*']
+
+strict_equality = true
+implicit_reexport = true
+check_untyped_defs = true
+no_implicit_optional = true
+
+warn_return_any = true
+warn_unreachable = true
+warn_unused_configs = true
+
+# Turn these options off as it could cause conflicts
+# with the Pyright options.
+warn_unused_ignores = false
+warn_redundant_casts = false
+
+disallow_any_generics = true
+disallow_untyped_defs = true
+disallow_untyped_calls = true
+disallow_subclassing_any = true
+disallow_incomplete_defs = true
+disallow_untyped_decorators = true
+cache_fine_grained = true
+
+# By default, mypy reports an error if you assign a value to the result
+# of a function call that doesn't return anything. We do this in our test
+# cases:
+# ```
+# result = ...
+# assert result is None
+# ```
+# Changing this codegen to make mypy happy would increase complexity
+# and would not be worth it.
+disable_error_code = "func-returns-value,overload-cannot-match"
+
+# https://github.com/python/mypy/issues/12162
+[[tool.mypy.overrides]]
+module = "black.files.*"
+ignore_errors = true
+ignore_missing_imports = true
+
[tool.ruff]
line-length = 120
output-format = "grouped"
-target-version = "py37"
+target-version = "py38"
[tool.ruff.format]
docstring-code-format = true
@@ -168,6 +226,8 @@ select = [
"B",
# remove unused imports
"F401",
+ # check for missing future annotations
+ "FA102",
# bare except statements
"E722",
# unused arguments
@@ -176,7 +236,7 @@ select = [
"T201",
"T203",
# misuse of typing.TYPE_CHECKING
- "TCH004",
+ "TC004",
# import rules
"TID251",
]
@@ -190,6 +250,8 @@ unfixable = [
"T203",
]
+extend-safe-fixes = ["FA102"]
+
[tool.ruff.lint.flake8-tidy-imports.banned-api]
"functools.lru_cache".msg = "This function does not retain type information for the wrapped function's arguments; The `lru_cache` function from `_utils` should be used instead"
diff --git a/release-please-config.json b/release-please-config.json
new file mode 100644
index 00000000..0e2bc44e
--- /dev/null
+++ b/release-please-config.json
@@ -0,0 +1,66 @@
+{
+ "packages": {
+ ".": {}
+ },
+ "$schema": "https://raw.githubusercontent.com/stainless-api/release-please/main/schemas/config.json",
+ "include-v-in-tag": true,
+ "include-component-in-tag": false,
+ "versioning": "prerelease",
+ "prerelease": true,
+ "bump-minor-pre-major": true,
+ "bump-patch-for-minor-pre-major": false,
+ "pull-request-header": "Automated Release PR",
+ "pull-request-title-pattern": "release: ${version}",
+ "changelog-sections": [
+ {
+ "type": "feat",
+ "section": "Features"
+ },
+ {
+ "type": "fix",
+ "section": "Bug Fixes"
+ },
+ {
+ "type": "perf",
+ "section": "Performance Improvements"
+ },
+ {
+ "type": "revert",
+ "section": "Reverts"
+ },
+ {
+ "type": "chore",
+ "section": "Chores"
+ },
+ {
+ "type": "docs",
+ "section": "Documentation"
+ },
+ {
+ "type": "style",
+ "section": "Styles"
+ },
+ {
+ "type": "refactor",
+ "section": "Refactors"
+ },
+ {
+ "type": "test",
+ "section": "Tests",
+ "hidden": true
+ },
+ {
+ "type": "build",
+ "section": "Build System"
+ },
+ {
+ "type": "ci",
+ "section": "Continuous Integration",
+ "hidden": true
+ }
+ ],
+ "release-type": "python",
+ "extra-files": [
+ "src/mixedbread/_version.py"
+ ]
+}
\ No newline at end of file
diff --git a/requirements-dev.lock b/requirements-dev.lock
index f054d9bd..35a7b124 100644
--- a/requirements-dev.lock
+++ b/requirements-dev.lock
@@ -7,97 +7,143 @@
# all-features: true
# with-sources: false
# generate-hashes: false
+# universal: false
-e file:.
-annotated-types==0.6.0
+aiohappyeyeballs==2.6.1
+ # via aiohttp
+aiohttp==3.13.3
+ # via httpx-aiohttp
+ # via mixedbread
+aiosignal==1.4.0
+ # via aiohttp
+annotated-types==0.7.0
# via pydantic
-anyio==4.4.0
+anyio==4.12.1
# via httpx
# via mixedbread
-argcomplete==3.1.2
+argcomplete==3.6.3
+ # via nox
+async-timeout==5.0.1
+ # via aiohttp
+attrs==25.4.0
+ # via aiohttp
# via nox
-certifi==2023.7.22
+backports-asyncio-runner==1.2.0
+ # via pytest-asyncio
+certifi==2026.1.4
# via httpcore
# via httpx
-colorlog==6.7.0
+colorlog==6.10.1
+ # via nox
+dependency-groups==1.3.1
# via nox
-dirty-equals==0.6.0
-distlib==0.3.7
+dirty-equals==0.11
+distlib==0.4.0
# via virtualenv
-distro==1.8.0
+distro==1.9.0
# via mixedbread
-exceptiongroup==1.2.2
+exceptiongroup==1.3.1
# via anyio
# via pytest
-filelock==3.12.4
+execnet==2.1.2
+ # via pytest-xdist
+filelock==3.19.1
# via virtualenv
-h11==0.14.0
+frozenlist==1.8.0
+ # via aiohttp
+ # via aiosignal
+h11==0.16.0
# via httpcore
-httpcore==1.0.2
+httpcore==1.0.9
# via httpx
httpx==0.28.1
+ # via httpx-aiohttp
# via mixedbread
# via respx
-idna==3.4
+httpx-aiohttp==0.1.12
+ # via mixedbread
+humanize==4.13.0
+ # via nox
+idna==3.11
# via anyio
# via httpx
-importlib-metadata==7.0.0
-iniconfig==2.0.0
+ # via yarl
+importlib-metadata==8.7.1
+iniconfig==2.1.0
# via pytest
markdown-it-py==3.0.0
# via rich
mdurl==0.1.2
# via markdown-it-py
-mypy==1.13.0
-mypy-extensions==1.0.0
+multidict==6.7.0
+ # via aiohttp
+ # via yarl
+mypy==1.17.0
+mypy-extensions==1.1.0
# via mypy
-nest-asyncio==1.6.0
-nodeenv==1.8.0
+nodeenv==1.10.0
# via pyright
-nox==2023.4.22
-packaging==23.2
+nox==2025.11.12
+packaging==25.0
+ # via dependency-groups
# via nox
# via pytest
-platformdirs==3.11.0
+pathspec==1.0.3
+ # via mypy
+platformdirs==4.4.0
# via virtualenv
-pluggy==1.5.0
+pluggy==1.6.0
# via pytest
-pydantic==2.10.3
+propcache==0.4.1
+ # via aiohttp
+ # via yarl
+pydantic==2.12.5
# via mixedbread
-pydantic-core==2.27.1
+pydantic-core==2.41.5
# via pydantic
-pygments==2.18.0
+pygments==2.19.2
+ # via pytest
# via rich
-pyright==1.1.390
-pytest==8.3.3
+pyright==1.1.399
+pytest==8.4.2
# via pytest-asyncio
-pytest-asyncio==0.24.0
-python-dateutil==2.8.2
+ # via pytest-xdist
+pytest-asyncio==1.2.0
+pytest-xdist==3.8.0
+python-dateutil==2.9.0.post0
# via time-machine
-pytz==2023.3.post1
- # via dirty-equals
respx==0.22.0
-rich==13.7.1
-ruff==0.6.9
-setuptools==68.2.2
- # via nodeenv
-six==1.16.0
+rich==14.2.0
+ruff==0.14.13
+six==1.17.0
# via python-dateutil
-sniffio==1.3.0
- # via anyio
+sniffio==1.3.1
# via mixedbread
-time-machine==2.9.0
-tomli==2.0.2
+time-machine==2.19.0
+tomli==2.4.0
+ # via dependency-groups
# via mypy
+ # via nox
# via pytest
-typing-extensions==4.12.2
+typing-extensions==4.15.0
+ # via aiosignal
# via anyio
+ # via exceptiongroup
# via mixedbread
+ # via multidict
# via mypy
# via pydantic
# via pydantic-core
# via pyright
-virtualenv==20.24.5
+ # via pytest-asyncio
+ # via typing-inspection
+ # via virtualenv
+typing-inspection==0.4.2
+ # via pydantic
+virtualenv==20.36.1
# via nox
-zipp==3.17.0
+yarl==1.22.0
+ # via aiohttp
+zipp==3.23.0
# via importlib-metadata
diff --git a/requirements.lock b/requirements.lock
index 69e71cb9..ce962fbc 100644
--- a/requirements.lock
+++ b/requirements.lock
@@ -7,38 +7,70 @@
# all-features: true
# with-sources: false
# generate-hashes: false
+# universal: false
-e file:.
-annotated-types==0.6.0
+aiohappyeyeballs==2.6.1
+ # via aiohttp
+aiohttp==3.13.3
+ # via httpx-aiohttp
+ # via mixedbread
+aiosignal==1.4.0
+ # via aiohttp
+annotated-types==0.7.0
# via pydantic
-anyio==4.4.0
+anyio==4.12.1
# via httpx
# via mixedbread
-certifi==2023.7.22
+async-timeout==5.0.1
+ # via aiohttp
+attrs==25.4.0
+ # via aiohttp
+certifi==2026.1.4
# via httpcore
# via httpx
-distro==1.8.0
+distro==1.9.0
# via mixedbread
-exceptiongroup==1.2.2
+exceptiongroup==1.3.1
# via anyio
-h11==0.14.0
+frozenlist==1.8.0
+ # via aiohttp
+ # via aiosignal
+h11==0.16.0
# via httpcore
-httpcore==1.0.2
+httpcore==1.0.9
# via httpx
httpx==0.28.1
+ # via httpx-aiohttp
+ # via mixedbread
+httpx-aiohttp==0.1.12
# via mixedbread
-idna==3.4
+idna==3.11
# via anyio
# via httpx
-pydantic==2.10.3
+ # via yarl
+multidict==6.7.0
+ # via aiohttp
+ # via yarl
+propcache==0.4.1
+ # via aiohttp
+ # via yarl
+pydantic==2.12.5
# via mixedbread
-pydantic-core==2.27.1
+pydantic-core==2.41.5
# via pydantic
-sniffio==1.3.0
- # via anyio
+sniffio==1.3.1
# via mixedbread
-typing-extensions==4.12.2
+typing-extensions==4.15.0
+ # via aiosignal
# via anyio
+ # via exceptiongroup
# via mixedbread
+ # via multidict
# via pydantic
# via pydantic-core
+ # via typing-inspection
+typing-inspection==0.4.2
+ # via pydantic
+yarl==1.22.0
+ # via aiohttp
diff --git a/scripts/bootstrap b/scripts/bootstrap
index 8c5c60eb..b430fee3 100755
--- a/scripts/bootstrap
+++ b/scripts/bootstrap
@@ -4,10 +4,18 @@ set -e
cd "$(dirname "$0")/.."
-if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ]; then
+if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then
brew bundle check >/dev/null 2>&1 || {
- echo "==> Installing Homebrew dependencies…"
- brew bundle
+ echo -n "==> Install Homebrew dependencies? (y/N): "
+ read -r response
+ case "$response" in
+ [yY][eE][sS]|[yY])
+ brew bundle
+ ;;
+ *)
+ ;;
+ esac
+ echo
}
fi
diff --git a/scripts/lint b/scripts/lint
index 75fa7747..51bbef96 100755
--- a/scripts/lint
+++ b/scripts/lint
@@ -4,9 +4,13 @@ set -e
cd "$(dirname "$0")/.."
-echo "==> Running lints"
-rye run lint
+if [ "$1" = "--fix" ]; then
+ echo "==> Running lints with --fix"
+ rye run fix:ruff
+else
+ echo "==> Running lints"
+ rye run lint
+fi
echo "==> Making sure it imports"
rye run python -c 'import mixedbread'
-
diff --git a/scripts/mock b/scripts/mock
index d2814ae6..0b28f6ea 100755
--- a/scripts/mock
+++ b/scripts/mock
@@ -21,7 +21,7 @@ echo "==> Starting mock server with URL ${URL}"
# Run prism mock on the given spec
if [ "$1" == "--daemon" ]; then
- npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL" &> .prism.log &
+ npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log &
# Wait for server to come online
echo -n "Waiting for server"
@@ -37,5 +37,5 @@ if [ "$1" == "--daemon" ]; then
echo
else
- npm exec --package=@stainless-api/prism-cli@5.8.5 -- prism mock "$URL"
+ npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL"
fi
diff --git a/scripts/test b/scripts/test
index 4fa5698b..dbeda2d2 100755
--- a/scripts/test
+++ b/scripts/test
@@ -43,7 +43,7 @@ elif ! prism_is_running ; then
echo -e "To run the server, pass in the path or url of your OpenAPI"
echo -e "spec to the prism command:"
echo
- echo -e " \$ ${YELLOW}npm exec --package=@stoplight/prism-cli@~5.3.2 -- prism mock path/to/your.openapi.yml${NC}"
+ echo -e " \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}"
echo
exit 1
@@ -52,6 +52,8 @@ else
echo
fi
+export DEFER_PYDANTIC_BUILD=false
+
echo "==> Running tests"
rye run pytest "$@"
diff --git a/scripts/utils/ruffen-docs.py b/scripts/utils/ruffen-docs.py
index 37b3d94f..0cf2bd2f 100644
--- a/scripts/utils/ruffen-docs.py
+++ b/scripts/utils/ruffen-docs.py
@@ -47,7 +47,7 @@ def _md_match(match: Match[str]) -> str:
with _collect_error(match):
code = format_code_block(code)
code = textwrap.indent(code, match["indent"])
- return f'{match["before"]}{code}{match["after"]}'
+ return f"{match['before']}{code}{match['after']}"
def _pycon_match(match: Match[str]) -> str:
code = ""
@@ -97,7 +97,7 @@ def finish_fragment() -> None:
def _md_pycon_match(match: Match[str]) -> str:
code = _pycon_match(match)
code = textwrap.indent(code, match["indent"])
- return f'{match["before"]}{code}{match["after"]}'
+ return f"{match['before']}{code}{match['after']}"
src = MD_RE.sub(_md_match, src)
src = MD_PYCON_RE.sub(_md_pycon_match, src)
diff --git a/scripts/utils/upload-artifact.sh b/scripts/utils/upload-artifact.sh
new file mode 100755
index 00000000..5af44df7
--- /dev/null
+++ b/scripts/utils/upload-artifact.sh
@@ -0,0 +1,27 @@
+#!/usr/bin/env bash
+set -exuo pipefail
+
+FILENAME=$(basename dist/*.whl)
+
+RESPONSE=$(curl -X POST "$URL?filename=$FILENAME" \
+ -H "Authorization: Bearer $AUTH" \
+ -H "Content-Type: application/json")
+
+SIGNED_URL=$(echo "$RESPONSE" | jq -r '.url')
+
+if [[ "$SIGNED_URL" == "null" ]]; then
+ echo -e "\033[31mFailed to get signed URL.\033[0m"
+ exit 1
+fi
+
+UPLOAD_RESPONSE=$(curl -v -X PUT \
+ -H "Content-Type: binary/octet-stream" \
+ --data-binary "@dist/$FILENAME" "$SIGNED_URL" 2>&1)
+
+if echo "$UPLOAD_RESPONSE" | grep -q "HTTP/[0-9.]* 200"; then
+ echo -e "\033[32mUploaded build to Stainless storage.\033[0m"
+ echo -e "\033[32mInstallation: pip install 'https://pkg.stainless.com/s/mixedbread-python/$SHA/$FILENAME'\033[0m"
+else
+ echo -e "\033[31mFailed to upload artifact.\033[0m"
+ exit 1
+fi
diff --git a/src/mixedbread/__init__.py b/src/mixedbread/__init__.py
index c6b40046..82ae8048 100644
--- a/src/mixedbread/__init__.py
+++ b/src/mixedbread/__init__.py
@@ -1,7 +1,9 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+import typing as _t
+
from . import types
-from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes
+from ._types import NOT_GIVEN, Omit, NoneType, NotGiven, Transport, ProxiesTypes, omit, not_given
from ._utils import file_from_path
from ._client import (
ENVIRONMENTS,
@@ -35,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__ = [
@@ -47,7 +49,9 @@
"ProxiesTypes",
"NotGiven",
"NOT_GIVEN",
+ "not_given",
"Omit",
+ "omit",
"MixedbreadError",
"APIError",
"APIStatusError",
@@ -78,8 +82,12 @@
"DEFAULT_CONNECTION_LIMITS",
"DefaultHttpxClient",
"DefaultAsyncHttpxClient",
+ "DefaultAioHttpClient",
]
+if not _t.TYPE_CHECKING:
+ from ._utils._resources_proxy import resources as resources
+
_setup_logging()
# Update the __module__ attribute for exported symbols so that
diff --git a/src/mixedbread/_base_client.py b/src/mixedbread/_base_client.py
index 0be06c72..dded30e9 100644
--- a/src/mixedbread/_base_client.py
+++ b/src/mixedbread/_base_client.py
@@ -36,14 +36,13 @@
import httpx
import distro
import pydantic
-from httpx import URL, Limits
+from httpx import URL
from pydantic import PrivateAttr
from . import _exceptions
from ._qs import Querystring
from ._files import to_httpx_files, async_to_httpx_files
from ._types import (
- NOT_GIVEN,
Body,
Omit,
Query,
@@ -51,19 +50,19 @@
Timeout,
NotGiven,
ResponseT,
- Transport,
AnyMapping,
PostParser,
- ProxiesTypes,
+ BinaryTypes,
RequestFiles,
HttpxSendArgs,
- AsyncTransport,
RequestOptions,
+ AsyncBinaryTypes,
HttpxRequestFiles,
ModelBuilderProtocol,
+ not_given,
)
from ._utils import is_dict, is_list, asyncify, is_given, lru_cache, is_mapping
-from ._compat import model_copy, model_dump
+from ._compat import PYDANTIC_V1, model_copy, model_dump
from ._models import GenericModel, FinalRequestOptions, validate_type, construct_type
from ._response import (
APIResponse,
@@ -87,6 +86,7 @@
APIConnectionError,
APIResponseValidationError,
)
+from ._utils._json import openapi_dumps
log: logging.Logger = logging.getLogger(__name__)
@@ -102,7 +102,11 @@
_AsyncStreamT = TypeVar("_AsyncStreamT", bound=AsyncStream[Any])
if TYPE_CHECKING:
- from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT
+ from httpx._config import (
+ DEFAULT_TIMEOUT_CONFIG, # pyright: ignore[reportPrivateImportUsage]
+ )
+
+ HTTPX_DEFAULT_TIMEOUT = DEFAULT_TIMEOUT_CONFIG
else:
try:
from httpx._config import DEFAULT_TIMEOUT_CONFIG as HTTPX_DEFAULT_TIMEOUT
@@ -119,6 +123,7 @@ class PageInfo:
url: URL | NotGiven
params: Query | NotGiven
+ json: Body | NotGiven
@overload
def __init__(
@@ -134,19 +139,30 @@ def __init__(
params: Query,
) -> None: ...
+ @overload
def __init__(
self,
*,
- url: URL | NotGiven = NOT_GIVEN,
- params: Query | NotGiven = NOT_GIVEN,
+ json: Body,
+ ) -> None: ...
+
+ def __init__(
+ self,
+ *,
+ url: URL | NotGiven = not_given,
+ json: Body | NotGiven = not_given,
+ params: Query | NotGiven = not_given,
) -> None:
self.url = url
+ self.json = json
self.params = params
@override
def __repr__(self) -> str:
if self.url:
return f"{self.__class__.__name__}(url={self.url})"
+ if self.json:
+ return f"{self.__class__.__name__}(json={self.json})"
return f"{self.__class__.__name__}(params={self.params})"
@@ -195,6 +211,19 @@ def _info_to_options(self, info: PageInfo) -> FinalRequestOptions:
options.url = str(url)
return options
+ if not isinstance(info.json, NotGiven):
+ if not is_mapping(info.json):
+ raise TypeError("Pagination is only supported with mappings")
+
+ if not options.json_data:
+ options.json_data = {**info.json}
+ else:
+ if not is_mapping(options.json_data):
+ raise TypeError("Pagination is only supported with mappings")
+
+ options.json_data = {**options.json_data, **info.json}
+ return options
+
raise ValueError("Unexpected PageInfo state")
@@ -207,6 +236,9 @@ def _set_private_attributes(
model: Type[_T],
options: FinalRequestOptions,
) -> None:
+ if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None:
+ self.__pydantic_private__ = {}
+
self._model = model
self._client = client
self._options = options
@@ -292,6 +324,9 @@ def _set_private_attributes(
client: AsyncAPIClient,
options: FinalRequestOptions,
) -> None:
+ if (not PYDANTIC_V1) and getattr(self, "__pydantic_private__", None) is None:
+ self.__pydantic_private__ = {}
+
self._model = model
self._client = client
self._options = options
@@ -331,9 +366,6 @@ class BaseClient(Generic[_HttpxClientT, _DefaultStreamT]):
_base_url: URL
max_retries: int
timeout: Union[float, Timeout, None]
- _limits: httpx.Limits
- _proxies: ProxiesTypes | None
- _transport: Transport | AsyncTransport | None
_strict_response_validation: bool
_idempotency_header: str | None
_default_stream_cls: type[_DefaultStreamT] | None = None
@@ -346,9 +378,6 @@ def __init__(
_strict_response_validation: bool,
max_retries: int = DEFAULT_MAX_RETRIES,
timeout: float | Timeout | None = DEFAULT_TIMEOUT,
- limits: httpx.Limits,
- transport: Transport | AsyncTransport | None,
- proxies: ProxiesTypes | None,
custom_headers: Mapping[str, str] | None = None,
custom_query: Mapping[str, object] | None = None,
) -> None:
@@ -356,9 +385,6 @@ def __init__(
self._base_url = self._enforce_trailing_slash(URL(base_url))
self.max_retries = max_retries
self.timeout = timeout
- self._limits = limits
- self._proxies = proxies
- self._transport = transport
self._custom_headers = custom_headers or {}
self._custom_query = custom_query or {}
self._strict_response_validation = _strict_response_validation
@@ -415,13 +441,20 @@ def _build_headers(self, options: FinalRequestOptions, *, retries_taken: int = 0
headers = httpx.Headers(headers_dict)
idempotency_header = self._idempotency_header
- if idempotency_header and options.method.lower() != "get" and idempotency_header not in headers:
- headers[idempotency_header] = options.idempotency_key or self._idempotency_key()
+ if idempotency_header and options.idempotency_key and idempotency_header not in headers:
+ headers[idempotency_header] = options.idempotency_key
- # Don't set the retry count header if it was already set or removed by the caller. We check
+ # Don't set these headers if they were already set or removed by the caller. We check
# `custom_headers`, which can contain `Omit()`, instead of `headers` to account for the removal case.
- if "x-stainless-retry-count" not in (header.lower() for header in custom_headers):
+ lower_custom_headers = [header.lower() for header in custom_headers]
+ if "x-stainless-retry-count" not in lower_custom_headers:
headers["x-stainless-retry-count"] = str(retries_taken)
+ if "x-stainless-read-timeout" not in lower_custom_headers:
+ timeout = self.timeout if isinstance(options.timeout, NotGiven) else options.timeout
+ if isinstance(timeout, Timeout):
+ timeout = timeout.read
+ if timeout is not None:
+ headers["x-stainless-read-timeout"] = str(timeout)
return headers
@@ -448,8 +481,19 @@ def _build_request(
retries_taken: int = 0,
) -> httpx.Request:
if log.isEnabledFor(logging.DEBUG):
- log.debug("Request options: %s", model_dump(options, exclude_unset=True))
-
+ log.debug(
+ "Request options: %s",
+ model_dump(
+ options,
+ exclude_unset=True,
+ # Pydantic v1 can't dump every type we support in content, so we exclude it for now.
+ exclude={
+ "content",
+ }
+ if PYDANTIC_V1
+ else {},
+ ),
+ )
kwargs: dict[str, Any] = {}
json_data = options.json_data
@@ -500,6 +544,26 @@ def _build_request(
# work around https://github.com/encode/httpx/discussions/2880
kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")}
+ is_body_allowed = options.method.lower() != "get"
+
+ if is_body_allowed:
+ if options.content is not None and json_data is not None:
+ raise TypeError("Passing both `content` and `json_data` is not supported")
+ if options.content is not None and files is not None:
+ raise TypeError("Passing both `content` and `files` is not supported")
+ if options.content is not None:
+ kwargs["content"] = options.content
+ elif isinstance(json_data, bytes):
+ kwargs["content"] = json_data
+ elif not files:
+ # Don't set content when JSON is sent as multipart/form-data,
+ # since httpx's content param overrides other body arguments
+ kwargs["content"] = openapi_dumps(json_data) if is_given(json_data) and json_data is not None else None
+ kwargs["files"] = files
+ else:
+ headers.pop("Content-Type", None)
+ kwargs.pop("data", None)
+
# TODO: report this error to httpx
return self._client.build_request( # pyright: ignore[reportUnknownMemberType]
headers=headers,
@@ -511,8 +575,6 @@ def _build_request(
# so that passing a `TypedDict` doesn't cause an error.
# https://github.com/microsoft/pyright/issues/3526#event-6715453066
params=self.qs.stringify(cast(Mapping[str, Any], params)) if params else None,
- json=json_data,
- files=files,
**kwargs,
)
@@ -556,7 +618,7 @@ def _maybe_override_cast_to(self, cast_to: type[ResponseT], options: FinalReques
# we internally support defining a temporary header to override the
# default `cast_to` type for use with `.with_raw_response` and `.with_streaming_response`
# see _response.py for implementation details
- override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, NOT_GIVEN)
+ override_cast_to = headers.pop(OVERRIDE_CAST_TO_HEADER, not_given)
if is_given(override_cast_to):
options.headers = headers
return cast(Type[ResponseT], override_cast_to)
@@ -786,47 +848,12 @@ def __init__(
version: str,
base_url: str | URL,
max_retries: int = DEFAULT_MAX_RETRIES,
- timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
- transport: Transport | None = None,
- proxies: ProxiesTypes | None = None,
- limits: Limits | None = None,
+ timeout: float | Timeout | None | NotGiven = not_given,
http_client: httpx.Client | None = None,
custom_headers: Mapping[str, str] | None = None,
custom_query: Mapping[str, object] | None = None,
_strict_response_validation: bool,
) -> None:
- kwargs: dict[str, Any] = {}
- if limits is not None:
- warnings.warn(
- "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead",
- category=DeprecationWarning,
- stacklevel=3,
- )
- if http_client is not None:
- raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`")
- else:
- limits = DEFAULT_CONNECTION_LIMITS
-
- if transport is not None:
- kwargs["transport"] = transport
- warnings.warn(
- "The `transport` argument is deprecated. The `http_client` argument should be passed instead",
- category=DeprecationWarning,
- stacklevel=3,
- )
- if http_client is not None:
- raise ValueError("The `http_client` argument is mutually exclusive with `transport`")
-
- if proxies is not None:
- kwargs["proxies"] = proxies
- warnings.warn(
- "The `proxies` argument is deprecated. The `http_client` argument should be passed instead",
- category=DeprecationWarning,
- stacklevel=3,
- )
- if http_client is not None:
- raise ValueError("The `http_client` argument is mutually exclusive with `proxies`")
-
if not is_given(timeout):
# if the user passed in a custom http client with a non-default
# timeout set then we use that timeout.
@@ -847,12 +874,9 @@ def __init__(
super().__init__(
version=version,
- limits=limits,
# cast to a valid type because mypy doesn't understand our type narrowing
timeout=cast(Timeout, timeout),
- proxies=proxies,
base_url=base_url,
- transport=transport,
max_retries=max_retries,
custom_query=custom_query,
custom_headers=custom_headers,
@@ -862,9 +886,6 @@ def __init__(
base_url=base_url,
# cast to a valid type because mypy doesn't understand our type narrowing
timeout=cast(Timeout, timeout),
- limits=limits,
- follow_redirects=True,
- **kwargs, # type: ignore
)
def is_closed(self) -> bool:
@@ -914,7 +935,6 @@ def request(
self,
cast_to: Type[ResponseT],
options: FinalRequestOptions,
- remaining_retries: Optional[int] = None,
*,
stream: Literal[True],
stream_cls: Type[_StreamT],
@@ -925,7 +945,6 @@ def request(
self,
cast_to: Type[ResponseT],
options: FinalRequestOptions,
- remaining_retries: Optional[int] = None,
*,
stream: Literal[False] = False,
) -> ResponseT: ...
@@ -935,7 +954,6 @@ def request(
self,
cast_to: Type[ResponseT],
options: FinalRequestOptions,
- remaining_retries: Optional[int] = None,
*,
stream: bool = False,
stream_cls: Type[_StreamT] | None = None,
@@ -945,121 +963,112 @@ def request(
self,
cast_to: Type[ResponseT],
options: FinalRequestOptions,
- remaining_retries: Optional[int] = None,
*,
stream: bool = False,
stream_cls: type[_StreamT] | None = None,
) -> ResponseT | _StreamT:
- if remaining_retries is not None:
- retries_taken = options.get_max_retries(self.max_retries) - remaining_retries
- else:
- retries_taken = 0
-
- return self._request(
- cast_to=cast_to,
- options=options,
- stream=stream,
- stream_cls=stream_cls,
- retries_taken=retries_taken,
- )
+ cast_to = self._maybe_override_cast_to(cast_to, options)
- def _request(
- self,
- *,
- cast_to: Type[ResponseT],
- options: FinalRequestOptions,
- retries_taken: int,
- stream: bool,
- stream_cls: type[_StreamT] | None,
- ) -> ResponseT | _StreamT:
# create a copy of the options we were given so that if the
# options are mutated later & we then retry, the retries are
# given the original options
input_options = model_copy(options)
+ if input_options.idempotency_key is None and input_options.method.lower() != "get":
+ # ensure the idempotency key is reused between requests
+ input_options.idempotency_key = self._idempotency_key()
- cast_to = self._maybe_override_cast_to(cast_to, options)
- options = self._prepare_options(options)
+ response: httpx.Response | None = None
+ max_retries = input_options.get_max_retries(self.max_retries)
- remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
- request = self._build_request(options, retries_taken=retries_taken)
- self._prepare_request(request)
+ retries_taken = 0
+ for retries_taken in range(max_retries + 1):
+ options = model_copy(input_options)
+ options = self._prepare_options(options)
- kwargs: HttpxSendArgs = {}
- if self.custom_auth is not None:
- kwargs["auth"] = self.custom_auth
+ remaining_retries = max_retries - retries_taken
+ request = self._build_request(options, retries_taken=retries_taken)
+ self._prepare_request(request)
- log.debug("Sending HTTP Request: %s %s", request.method, request.url)
+ kwargs: HttpxSendArgs = {}
+ if self.custom_auth is not None:
+ kwargs["auth"] = self.custom_auth
- try:
- response = self._client.send(
- request,
- stream=stream or self._should_stream_response_body(request=request),
- **kwargs,
- )
- except httpx.TimeoutException as err:
- log.debug("Encountered httpx.TimeoutException", exc_info=True)
+ if options.follow_redirects is not None:
+ kwargs["follow_redirects"] = options.follow_redirects
- if remaining_retries > 0:
- return self._retry_request(
- input_options,
- cast_to,
- retries_taken=retries_taken,
- stream=stream,
- stream_cls=stream_cls,
- response_headers=None,
- )
-
- log.debug("Raising timeout error")
- raise APITimeoutError(request=request) from err
- except Exception as err:
- log.debug("Encountered Exception", exc_info=True)
+ log.debug("Sending HTTP Request: %s %s", request.method, request.url)
- if remaining_retries > 0:
- return self._retry_request(
- input_options,
- cast_to,
- retries_taken=retries_taken,
- stream=stream,
- stream_cls=stream_cls,
- response_headers=None,
+ response = None
+ try:
+ response = self._client.send(
+ request,
+ stream=stream or self._should_stream_response_body(request=request),
+ **kwargs,
)
+ except httpx.TimeoutException as err:
+ log.debug("Encountered httpx.TimeoutException", exc_info=True)
+
+ if remaining_retries > 0:
+ self._sleep_for_retry(
+ retries_taken=retries_taken,
+ max_retries=max_retries,
+ options=input_options,
+ response=None,
+ )
+ continue
+
+ log.debug("Raising timeout error")
+ raise APITimeoutError(request=request) from err
+ except Exception as err:
+ log.debug("Encountered Exception", exc_info=True)
+
+ if remaining_retries > 0:
+ self._sleep_for_retry(
+ retries_taken=retries_taken,
+ max_retries=max_retries,
+ options=input_options,
+ response=None,
+ )
+ continue
+
+ log.debug("Raising connection error")
+ raise APIConnectionError(request=request) from err
+
+ log.debug(
+ 'HTTP Response: %s %s "%i %s" %s',
+ request.method,
+ request.url,
+ response.status_code,
+ response.reason_phrase,
+ response.headers,
+ )
- log.debug("Raising connection error")
- raise APIConnectionError(request=request) from err
-
- log.debug(
- 'HTTP Response: %s %s "%i %s" %s',
- request.method,
- request.url,
- response.status_code,
- response.reason_phrase,
- response.headers,
- )
+ try:
+ response.raise_for_status()
+ except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
+ log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
+
+ if remaining_retries > 0 and self._should_retry(err.response):
+ err.response.close()
+ self._sleep_for_retry(
+ retries_taken=retries_taken,
+ max_retries=max_retries,
+ options=input_options,
+ response=response,
+ )
+ continue
- try:
- response.raise_for_status()
- except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
- log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
-
- if remaining_retries > 0 and self._should_retry(err.response):
- err.response.close()
- return self._retry_request(
- input_options,
- cast_to,
- retries_taken=retries_taken,
- response_headers=err.response.headers,
- stream=stream,
- stream_cls=stream_cls,
- )
+ # If the response is streamed then we need to explicitly read the response
+ # to completion before attempting to access the response text.
+ if not err.response.is_closed:
+ err.response.read()
- # If the response is streamed then we need to explicitly read the response
- # to completion before attempting to access the response text.
- if not err.response.is_closed:
- err.response.read()
+ log.debug("Re-raising status error")
+ raise self._make_status_error_from_response(err.response) from None
- log.debug("Re-raising status error")
- raise self._make_status_error_from_response(err.response) from None
+ break
+ assert response is not None, "could not resolve response (should never happen)"
return self._process_response(
cast_to=cast_to,
options=options,
@@ -1069,37 +1078,20 @@ def _request(
retries_taken=retries_taken,
)
- def _retry_request(
- self,
- options: FinalRequestOptions,
- cast_to: Type[ResponseT],
- *,
- retries_taken: int,
- response_headers: httpx.Headers | None,
- stream: bool,
- stream_cls: type[_StreamT] | None,
- ) -> ResponseT | _StreamT:
- remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
+ def _sleep_for_retry(
+ self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None
+ ) -> None:
+ remaining_retries = max_retries - retries_taken
if remaining_retries == 1:
log.debug("1 retry left")
else:
log.debug("%i retries left", remaining_retries)
- timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers)
+ timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None)
log.info("Retrying request to %s in %f seconds", options.url, timeout)
- # In a synchronous context we are blocking the entire thread. Up to the library user to run the client in a
- # different thread if necessary.
time.sleep(timeout)
- return self._request(
- options=options,
- cast_to=cast_to,
- retries_taken=retries_taken + 1,
- stream=stream,
- stream_cls=stream_cls,
- )
-
def _process_response(
self,
*,
@@ -1112,7 +1104,14 @@ def _process_response(
) -> ResponseT:
origin = get_origin(cast_to) or cast_to
- if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse):
+ if (
+ inspect.isclass(origin)
+ and issubclass(origin, BaseAPIResponse)
+ # we only want to actually return the custom BaseAPIResponse class if we're
+ # returning the raw response, or if we're not streaming SSE, as if we're streaming
+ # SSE then `cast_to` doesn't actively reflect the type we need to parse into
+ and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER)))
+ ):
if not issubclass(origin, APIResponse):
raise TypeError(f"API Response types must subclass {APIResponse}; Received {origin}")
@@ -1218,6 +1217,7 @@ def post(
*,
cast_to: Type[ResponseT],
body: Body | None = None,
+ content: BinaryTypes | None = None,
options: RequestOptions = {},
files: RequestFiles | None = None,
stream: Literal[False] = False,
@@ -1230,6 +1230,7 @@ def post(
*,
cast_to: Type[ResponseT],
body: Body | None = None,
+ content: BinaryTypes | None = None,
options: RequestOptions = {},
files: RequestFiles | None = None,
stream: Literal[True],
@@ -1243,6 +1244,7 @@ def post(
*,
cast_to: Type[ResponseT],
body: Body | None = None,
+ content: BinaryTypes | None = None,
options: RequestOptions = {},
files: RequestFiles | None = None,
stream: bool,
@@ -1255,13 +1257,25 @@ def post(
*,
cast_to: Type[ResponseT],
body: Body | None = None,
+ content: BinaryTypes | None = None,
options: RequestOptions = {},
files: RequestFiles | None = None,
stream: bool = False,
stream_cls: type[_StreamT] | None = None,
) -> ResponseT | _StreamT:
+ if body is not None and content is not None:
+ raise TypeError("Passing both `body` and `content` is not supported")
+ if files is not None and content is not None:
+ raise TypeError("Passing both `files` and `content` is not supported")
+ if isinstance(body, bytes):
+ warnings.warn(
+ "Passing raw bytes as `body` is deprecated and will be removed in a future version. "
+ "Please pass raw bytes via the `content` parameter instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
opts = FinalRequestOptions.construct(
- method="post", url=path, json_data=body, files=to_httpx_files(files), **options
+ method="post", url=path, json_data=body, content=content, files=to_httpx_files(files), **options
)
return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls))
@@ -1271,9 +1285,24 @@ def patch(
*,
cast_to: Type[ResponseT],
body: Body | None = None,
+ content: BinaryTypes | None = None,
+ files: RequestFiles | None = None,
options: RequestOptions = {},
) -> ResponseT:
- opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options)
+ if body is not None and content is not None:
+ raise TypeError("Passing both `body` and `content` is not supported")
+ if files is not None and content is not None:
+ raise TypeError("Passing both `files` and `content` is not supported")
+ if isinstance(body, bytes):
+ warnings.warn(
+ "Passing raw bytes as `body` is deprecated and will be removed in a future version. "
+ "Please pass raw bytes via the `content` parameter instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ opts = FinalRequestOptions.construct(
+ method="patch", url=path, json_data=body, content=content, files=to_httpx_files(files), **options
+ )
return self.request(cast_to, opts)
def put(
@@ -1282,11 +1311,23 @@ def put(
*,
cast_to: Type[ResponseT],
body: Body | None = None,
+ content: BinaryTypes | None = None,
files: RequestFiles | None = None,
options: RequestOptions = {},
) -> ResponseT:
+ if body is not None and content is not None:
+ raise TypeError("Passing both `body` and `content` is not supported")
+ if files is not None and content is not None:
+ raise TypeError("Passing both `files` and `content` is not supported")
+ if isinstance(body, bytes):
+ warnings.warn(
+ "Passing raw bytes as `body` is deprecated and will be removed in a future version. "
+ "Please pass raw bytes via the `content` parameter instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
opts = FinalRequestOptions.construct(
- method="put", url=path, json_data=body, files=to_httpx_files(files), **options
+ method="put", url=path, json_data=body, content=content, files=to_httpx_files(files), **options
)
return self.request(cast_to, opts)
@@ -1296,9 +1337,19 @@ def delete(
*,
cast_to: Type[ResponseT],
body: Body | None = None,
+ content: BinaryTypes | None = None,
options: RequestOptions = {},
) -> ResponseT:
- opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options)
+ if body is not None and content is not None:
+ raise TypeError("Passing both `body` and `content` is not supported")
+ if isinstance(body, bytes):
+ warnings.warn(
+ "Passing raw bytes as `body` is deprecated and will be removed in a future version. "
+ "Please pass raw bytes via the `content` parameter instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options)
return self.request(cast_to, opts)
def get_api_list(
@@ -1323,6 +1374,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
@@ -1331,8 +1400,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):
@@ -1358,46 +1431,11 @@ def __init__(
base_url: str | URL,
_strict_response_validation: bool,
max_retries: int = DEFAULT_MAX_RETRIES,
- timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
- transport: AsyncTransport | None = None,
- proxies: ProxiesTypes | None = None,
- limits: Limits | None = None,
+ timeout: float | Timeout | None | NotGiven = not_given,
http_client: httpx.AsyncClient | None = None,
custom_headers: Mapping[str, str] | None = None,
custom_query: Mapping[str, object] | None = None,
) -> None:
- kwargs: dict[str, Any] = {}
- if limits is not None:
- warnings.warn(
- "The `connection_pool_limits` argument is deprecated. The `http_client` argument should be passed instead",
- category=DeprecationWarning,
- stacklevel=3,
- )
- if http_client is not None:
- raise ValueError("The `http_client` argument is mutually exclusive with `connection_pool_limits`")
- else:
- limits = DEFAULT_CONNECTION_LIMITS
-
- if transport is not None:
- kwargs["transport"] = transport
- warnings.warn(
- "The `transport` argument is deprecated. The `http_client` argument should be passed instead",
- category=DeprecationWarning,
- stacklevel=3,
- )
- if http_client is not None:
- raise ValueError("The `http_client` argument is mutually exclusive with `transport`")
-
- if proxies is not None:
- kwargs["proxies"] = proxies
- warnings.warn(
- "The `proxies` argument is deprecated. The `http_client` argument should be passed instead",
- category=DeprecationWarning,
- stacklevel=3,
- )
- if http_client is not None:
- raise ValueError("The `http_client` argument is mutually exclusive with `proxies`")
-
if not is_given(timeout):
# if the user passed in a custom http client with a non-default
# timeout set then we use that timeout.
@@ -1419,11 +1457,8 @@ def __init__(
super().__init__(
version=version,
base_url=base_url,
- limits=limits,
# cast to a valid type because mypy doesn't understand our type narrowing
timeout=cast(Timeout, timeout),
- proxies=proxies,
- transport=transport,
max_retries=max_retries,
custom_query=custom_query,
custom_headers=custom_headers,
@@ -1433,9 +1468,6 @@ def __init__(
base_url=base_url,
# cast to a valid type because mypy doesn't understand our type narrowing
timeout=cast(Timeout, timeout),
- limits=limits,
- follow_redirects=True,
- **kwargs, # type: ignore
)
def is_closed(self) -> bool:
@@ -1484,7 +1516,6 @@ async def request(
options: FinalRequestOptions,
*,
stream: Literal[False] = False,
- remaining_retries: Optional[int] = None,
) -> ResponseT: ...
@overload
@@ -1495,7 +1526,6 @@ async def request(
*,
stream: Literal[True],
stream_cls: type[_AsyncStreamT],
- remaining_retries: Optional[int] = None,
) -> _AsyncStreamT: ...
@overload
@@ -1506,7 +1536,6 @@ async def request(
*,
stream: bool,
stream_cls: type[_AsyncStreamT] | None = None,
- remaining_retries: Optional[int] = None,
) -> ResponseT | _AsyncStreamT: ...
async def request(
@@ -1516,116 +1545,114 @@ async def request(
*,
stream: bool = False,
stream_cls: type[_AsyncStreamT] | None = None,
- remaining_retries: Optional[int] = None,
- ) -> ResponseT | _AsyncStreamT:
- if remaining_retries is not None:
- retries_taken = options.get_max_retries(self.max_retries) - remaining_retries
- else:
- retries_taken = 0
-
- return await self._request(
- cast_to=cast_to,
- options=options,
- stream=stream,
- stream_cls=stream_cls,
- retries_taken=retries_taken,
- )
-
- async def _request(
- self,
- cast_to: Type[ResponseT],
- options: FinalRequestOptions,
- *,
- stream: bool,
- stream_cls: type[_AsyncStreamT] | None,
- retries_taken: int,
) -> ResponseT | _AsyncStreamT:
if self._platform is None:
# `get_platform` can make blocking IO calls so we
# execute it earlier while we are in an async context
self._platform = await asyncify(get_platform)()
+ cast_to = self._maybe_override_cast_to(cast_to, options)
+
# create a copy of the options we were given so that if the
# options are mutated later & we then retry, the retries are
# given the original options
input_options = model_copy(options)
+ if input_options.idempotency_key is None and input_options.method.lower() != "get":
+ # ensure the idempotency key is reused between requests
+ input_options.idempotency_key = self._idempotency_key()
- cast_to = self._maybe_override_cast_to(cast_to, options)
- options = await self._prepare_options(options)
+ response: httpx.Response | None = None
+ max_retries = input_options.get_max_retries(self.max_retries)
- remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
- request = self._build_request(options, retries_taken=retries_taken)
- await self._prepare_request(request)
+ retries_taken = 0
+ for retries_taken in range(max_retries + 1):
+ options = model_copy(input_options)
+ options = await self._prepare_options(options)
- kwargs: HttpxSendArgs = {}
- if self.custom_auth is not None:
- kwargs["auth"] = self.custom_auth
+ remaining_retries = max_retries - retries_taken
+ request = self._build_request(options, retries_taken=retries_taken)
+ await self._prepare_request(request)
- try:
- response = await self._client.send(
- request,
- stream=stream or self._should_stream_response_body(request=request),
- **kwargs,
- )
- except httpx.TimeoutException as err:
- log.debug("Encountered httpx.TimeoutException", exc_info=True)
+ kwargs: HttpxSendArgs = {}
+ if self.custom_auth is not None:
+ kwargs["auth"] = self.custom_auth
- if remaining_retries > 0:
- return await self._retry_request(
- input_options,
- cast_to,
- retries_taken=retries_taken,
- stream=stream,
- stream_cls=stream_cls,
- response_headers=None,
- )
+ if options.follow_redirects is not None:
+ kwargs["follow_redirects"] = options.follow_redirects
- log.debug("Raising timeout error")
- raise APITimeoutError(request=request) from err
- except Exception as err:
- log.debug("Encountered Exception", exc_info=True)
+ log.debug("Sending HTTP Request: %s %s", request.method, request.url)
- if remaining_retries > 0:
- return await self._retry_request(
- input_options,
- cast_to,
- retries_taken=retries_taken,
- stream=stream,
- stream_cls=stream_cls,
- response_headers=None,
+ response = None
+ try:
+ response = await self._client.send(
+ request,
+ stream=stream or self._should_stream_response_body(request=request),
+ **kwargs,
)
+ except httpx.TimeoutException as err:
+ log.debug("Encountered httpx.TimeoutException", exc_info=True)
+
+ if remaining_retries > 0:
+ await self._sleep_for_retry(
+ retries_taken=retries_taken,
+ max_retries=max_retries,
+ options=input_options,
+ response=None,
+ )
+ continue
+
+ log.debug("Raising timeout error")
+ raise APITimeoutError(request=request) from err
+ except Exception as err:
+ log.debug("Encountered Exception", exc_info=True)
+
+ if remaining_retries > 0:
+ await self._sleep_for_retry(
+ retries_taken=retries_taken,
+ max_retries=max_retries,
+ options=input_options,
+ response=None,
+ )
+ continue
+
+ log.debug("Raising connection error")
+ raise APIConnectionError(request=request) from err
+
+ log.debug(
+ 'HTTP Response: %s %s "%i %s" %s',
+ request.method,
+ request.url,
+ response.status_code,
+ response.reason_phrase,
+ response.headers,
+ )
- log.debug("Raising connection error")
- raise APIConnectionError(request=request) from err
-
- log.debug(
- 'HTTP Request: %s %s "%i %s"', request.method, request.url, response.status_code, response.reason_phrase
- )
+ try:
+ response.raise_for_status()
+ except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
+ log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
+
+ if remaining_retries > 0 and self._should_retry(err.response):
+ await err.response.aclose()
+ await self._sleep_for_retry(
+ retries_taken=retries_taken,
+ max_retries=max_retries,
+ options=input_options,
+ response=response,
+ )
+ continue
- try:
- response.raise_for_status()
- except httpx.HTTPStatusError as err: # thrown on 4xx and 5xx status code
- log.debug("Encountered httpx.HTTPStatusError", exc_info=True)
-
- if remaining_retries > 0 and self._should_retry(err.response):
- await err.response.aclose()
- return await self._retry_request(
- input_options,
- cast_to,
- retries_taken=retries_taken,
- response_headers=err.response.headers,
- stream=stream,
- stream_cls=stream_cls,
- )
+ # If the response is streamed then we need to explicitly read the response
+ # to completion before attempting to access the response text.
+ if not err.response.is_closed:
+ await err.response.aread()
- # If the response is streamed then we need to explicitly read the response
- # to completion before attempting to access the response text.
- if not err.response.is_closed:
- await err.response.aread()
+ log.debug("Re-raising status error")
+ raise self._make_status_error_from_response(err.response) from None
- log.debug("Re-raising status error")
- raise self._make_status_error_from_response(err.response) from None
+ break
+ assert response is not None, "could not resolve response (should never happen)"
return await self._process_response(
cast_to=cast_to,
options=options,
@@ -1635,35 +1662,20 @@ async def _request(
retries_taken=retries_taken,
)
- async def _retry_request(
- self,
- options: FinalRequestOptions,
- cast_to: Type[ResponseT],
- *,
- retries_taken: int,
- response_headers: httpx.Headers | None,
- stream: bool,
- stream_cls: type[_AsyncStreamT] | None,
- ) -> ResponseT | _AsyncStreamT:
- remaining_retries = options.get_max_retries(self.max_retries) - retries_taken
+ async def _sleep_for_retry(
+ self, *, retries_taken: int, max_retries: int, options: FinalRequestOptions, response: httpx.Response | None
+ ) -> None:
+ remaining_retries = max_retries - retries_taken
if remaining_retries == 1:
log.debug("1 retry left")
else:
log.debug("%i retries left", remaining_retries)
- timeout = self._calculate_retry_timeout(remaining_retries, options, response_headers)
+ timeout = self._calculate_retry_timeout(remaining_retries, options, response.headers if response else None)
log.info("Retrying request to %s in %f seconds", options.url, timeout)
await anyio.sleep(timeout)
- return await self._request(
- options=options,
- cast_to=cast_to,
- retries_taken=retries_taken + 1,
- stream=stream,
- stream_cls=stream_cls,
- )
-
async def _process_response(
self,
*,
@@ -1676,7 +1688,14 @@ async def _process_response(
) -> ResponseT:
origin = get_origin(cast_to) or cast_to
- if inspect.isclass(origin) and issubclass(origin, BaseAPIResponse):
+ if (
+ inspect.isclass(origin)
+ and issubclass(origin, BaseAPIResponse)
+ # we only want to actually return the custom BaseAPIResponse class if we're
+ # returning the raw response, or if we're not streaming SSE, as if we're streaming
+ # SSE then `cast_to` doesn't actively reflect the type we need to parse into
+ and (not stream or bool(response.request.headers.get(RAW_RESPONSE_HEADER)))
+ ):
if not issubclass(origin, AsyncAPIResponse):
raise TypeError(f"API Response types must subclass {AsyncAPIResponse}; Received {origin}")
@@ -1770,6 +1789,7 @@ async def post(
*,
cast_to: Type[ResponseT],
body: Body | None = None,
+ content: AsyncBinaryTypes | None = None,
files: RequestFiles | None = None,
options: RequestOptions = {},
stream: Literal[False] = False,
@@ -1782,6 +1802,7 @@ async def post(
*,
cast_to: Type[ResponseT],
body: Body | None = None,
+ content: AsyncBinaryTypes | None = None,
files: RequestFiles | None = None,
options: RequestOptions = {},
stream: Literal[True],
@@ -1795,6 +1816,7 @@ async def post(
*,
cast_to: Type[ResponseT],
body: Body | None = None,
+ content: AsyncBinaryTypes | None = None,
files: RequestFiles | None = None,
options: RequestOptions = {},
stream: bool,
@@ -1807,13 +1829,25 @@ async def post(
*,
cast_to: Type[ResponseT],
body: Body | None = None,
+ content: AsyncBinaryTypes | None = None,
files: RequestFiles | None = None,
options: RequestOptions = {},
stream: bool = False,
stream_cls: type[_AsyncStreamT] | None = None,
) -> ResponseT | _AsyncStreamT:
+ if body is not None and content is not None:
+ raise TypeError("Passing both `body` and `content` is not supported")
+ if files is not None and content is not None:
+ raise TypeError("Passing both `files` and `content` is not supported")
+ if isinstance(body, bytes):
+ warnings.warn(
+ "Passing raw bytes as `body` is deprecated and will be removed in a future version. "
+ "Please pass raw bytes via the `content` parameter instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
opts = FinalRequestOptions.construct(
- method="post", url=path, json_data=body, files=await async_to_httpx_files(files), **options
+ method="post", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options
)
return await self.request(cast_to, opts, stream=stream, stream_cls=stream_cls)
@@ -1823,9 +1857,29 @@ async def patch(
*,
cast_to: Type[ResponseT],
body: Body | None = None,
+ content: AsyncBinaryTypes | None = None,
+ files: RequestFiles | None = None,
options: RequestOptions = {},
) -> ResponseT:
- opts = FinalRequestOptions.construct(method="patch", url=path, json_data=body, **options)
+ if body is not None and content is not None:
+ raise TypeError("Passing both `body` and `content` is not supported")
+ if files is not None and content is not None:
+ raise TypeError("Passing both `files` and `content` is not supported")
+ if isinstance(body, bytes):
+ warnings.warn(
+ "Passing raw bytes as `body` is deprecated and will be removed in a future version. "
+ "Please pass raw bytes via the `content` parameter instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ opts = FinalRequestOptions.construct(
+ method="patch",
+ url=path,
+ json_data=body,
+ content=content,
+ files=await async_to_httpx_files(files),
+ **options,
+ )
return await self.request(cast_to, opts)
async def put(
@@ -1834,11 +1888,23 @@ async def put(
*,
cast_to: Type[ResponseT],
body: Body | None = None,
+ content: AsyncBinaryTypes | None = None,
files: RequestFiles | None = None,
options: RequestOptions = {},
) -> ResponseT:
+ if body is not None and content is not None:
+ raise TypeError("Passing both `body` and `content` is not supported")
+ if files is not None and content is not None:
+ raise TypeError("Passing both `files` and `content` is not supported")
+ if isinstance(body, bytes):
+ warnings.warn(
+ "Passing raw bytes as `body` is deprecated and will be removed in a future version. "
+ "Please pass raw bytes via the `content` parameter instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
opts = FinalRequestOptions.construct(
- method="put", url=path, json_data=body, files=await async_to_httpx_files(files), **options
+ method="put", url=path, json_data=body, content=content, files=await async_to_httpx_files(files), **options
)
return await self.request(cast_to, opts)
@@ -1848,9 +1914,19 @@ async def delete(
*,
cast_to: Type[ResponseT],
body: Body | None = None,
+ content: AsyncBinaryTypes | None = None,
options: RequestOptions = {},
) -> ResponseT:
- opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, **options)
+ if body is not None and content is not None:
+ raise TypeError("Passing both `body` and `content` is not supported")
+ if isinstance(body, bytes):
+ warnings.warn(
+ "Passing raw bytes as `body` is deprecated and will be removed in a future version. "
+ "Please pass raw bytes via the `content` parameter instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ opts = FinalRequestOptions.construct(method="delete", url=path, json_data=body, content=content, **options)
return await self.request(cast_to, opts)
def get_api_list(
@@ -1874,8 +1950,8 @@ def make_request_options(
extra_query: Query | None = None,
extra_body: Body | None = None,
idempotency_key: str | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- post_parser: PostParser | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ post_parser: PostParser | NotGiven = not_given,
) -> RequestOptions:
"""Create a dict of type RequestOptions without keys of NotGiven values."""
options: RequestOptions = {}
diff --git a/src/mixedbread/_client.py b/src/mixedbread/_client.py
index 3c02ec0f..7391953a 100644
--- a/src/mixedbread/_client.py
+++ b/src/mixedbread/_client.py
@@ -3,39 +3,65 @@
from __future__ import annotations
import os
-from typing import Any, Dict, Union, Mapping, cast
+from typing import TYPE_CHECKING, Any, Dict, List, Union, Mapping, Iterable, Optional, cast
from typing_extensions import Self, Literal, override
import httpx
from . import _exceptions
from ._qs import Querystring
+from .types import client_embed_params, client_rerank_params
from ._types import (
- NOT_GIVEN,
+ Body,
Omit,
+ Query,
+ Headers,
Timeout,
NotGiven,
Transport,
ProxiesTypes,
RequestOptions,
+ SequenceNotStr,
+ omit,
+ not_given,
)
from ._utils import (
is_given,
+ maybe_transform,
get_async_library,
+ async_maybe_transform,
)
+from ._compat import cached_property
from ._version import __version__
-from .resources import reranking, embeddings, completions, service_info
+from ._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
from ._streaming import Stream as Stream, AsyncStream as AsyncStream
from ._exceptions import APIStatusError, MixedbreadError
from ._base_client import (
DEFAULT_MAX_RETRIES,
SyncAPIClient,
AsyncAPIClient,
+ make_request_options,
)
-from .resources.files import files
-from .resources.parsing import parsing
-from .resources.extractions import extractions
-from .resources.vector_stores import vector_stores
+from .types.info_response import InfoResponse
+from .types.encoding_format import EncodingFormat
+from .types.rerank_response import RerankResponse
+from .types.embedding_create_response import EmbeddingCreateResponse
+
+if TYPE_CHECKING:
+ from .resources import chat, files, stores, parsing, api_keys, embeddings, extractions, data_sources
+ from .resources.chat import ChatResource, AsyncChatResource
+ from .resources.api_keys import APIKeysResource, AsyncAPIKeysResource
+ from .resources.embeddings import EmbeddingsResource, AsyncEmbeddingsResource
+ from .resources.files.files import FilesResource, AsyncFilesResource
+ from .resources.stores.stores import StoresResource, AsyncStoresResource
+ from .resources.parsing.parsing import ParsingResource, AsyncParsingResource
+ from .resources.extractions.extractions import ExtractionsResource, AsyncExtractionsResource
+ from .resources.data_sources.data_sources import DataSourcesResource, AsyncDataSourcesResource
__all__ = [
"ENVIRONMENTS",
@@ -50,35 +76,25 @@
]
ENVIRONMENTS: Dict[str, str] = {
- "production": "https://api.mixedbread.ai",
- "environment_1": "http://127.0.0.1:8000",
+ "production": "https://api.mixedbread.com",
+ "development": "https://api.dev.mixedbread.com",
+ "local": "http://127.0.0.1:8000",
}
class Mixedbread(SyncAPIClient):
- service_info: service_info.ServiceInfoResource
- files: files.FilesResource
- completions: completions.CompletionsResource
- vector_stores: vector_stores.VectorStoresResource
- parsing: parsing.ParsingResource
- extractions: extractions.ExtractionsResource
- embeddings: embeddings.EmbeddingsResource
- reranking: reranking.RerankingResource
- with_raw_response: MixedbreadWithRawResponse
- with_streaming_response: MixedbreadWithStreamedResponse
-
# client options
api_key: str
- _environment: Literal["production", "environment_1"] | NotGiven
+ _environment: Literal["production", "development", "local"] | NotGiven
def __init__(
self,
*,
api_key: str | None = None,
- environment: Literal["production", "environment_1"] | NotGiven = NOT_GIVEN,
- base_url: str | httpx.URL | None | NotGiven = NOT_GIVEN,
- timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
+ environment: Literal["production", "development", "local"] | NotGiven = not_given,
+ base_url: str | httpx.URL | None | NotGiven = not_given,
+ timeout: float | Timeout | None | NotGiven = not_given,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
@@ -96,15 +112,15 @@ def __init__(
# part of our public interface in the future.
_strict_response_validation: bool = False,
) -> None:
- """Construct a new synchronous mixedbread client instance.
+ """Construct a new synchronous Mixedbread client instance.
- This automatically infers the `api_key` argument from the `API_KEY` environment variable if it is not provided.
+ This automatically infers the `api_key` argument from the `MXBAI_API_KEY` environment variable if it is not provided.
"""
if api_key is None:
- api_key = os.environ.get("API_KEY")
+ api_key = os.environ.get("MXBAI_API_KEY")
if api_key is None:
raise MixedbreadError(
- "The api_key client option must be set either by passing api_key to the client or by setting the API_KEY environment variable"
+ "The api_key client option must be set either by passing api_key to the client or by setting the MXBAI_API_KEY environment variable"
)
self.api_key = api_key
@@ -145,21 +161,66 @@ def __init__(
_strict_response_validation=_strict_response_validation,
)
- self.service_info = service_info.ServiceInfoResource(self)
- self.files = files.FilesResource(self)
- self.completions = completions.CompletionsResource(self)
- self.vector_stores = vector_stores.VectorStoresResource(self)
- self.parsing = parsing.ParsingResource(self)
- self.extractions = extractions.ExtractionsResource(self)
- self.embeddings = embeddings.EmbeddingsResource(self)
- self.reranking = reranking.RerankingResource(self)
- self.with_raw_response = MixedbreadWithRawResponse(self)
- self.with_streaming_response = MixedbreadWithStreamedResponse(self)
+ @cached_property
+ def stores(self) -> StoresResource:
+ from .resources.stores import StoresResource
+
+ return StoresResource(self)
+
+ @cached_property
+ def parsing(self) -> ParsingResource:
+ from .resources.parsing import ParsingResource
+
+ return ParsingResource(self)
+
+ @cached_property
+ def files(self) -> FilesResource:
+ from .resources.files import FilesResource
+
+ return FilesResource(self)
+
+ @cached_property
+ def extractions(self) -> ExtractionsResource:
+ from .resources.extractions import ExtractionsResource
+
+ return ExtractionsResource(self)
+
+ @cached_property
+ def embeddings(self) -> EmbeddingsResource:
+ from .resources.embeddings import EmbeddingsResource
+
+ return EmbeddingsResource(self)
+
+ @cached_property
+ def data_sources(self) -> DataSourcesResource:
+ from .resources.data_sources import DataSourcesResource
+
+ return DataSourcesResource(self)
+
+ @cached_property
+ def api_keys(self) -> APIKeysResource:
+ from .resources.api_keys import APIKeysResource
+
+ return APIKeysResource(self)
+
+ @cached_property
+ def chat(self) -> ChatResource:
+ from .resources.chat import ChatResource
+
+ return ChatResource(self)
+
+ @cached_property
+ def with_raw_response(self) -> MixedbreadWithRawResponse:
+ return MixedbreadWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> MixedbreadWithStreamedResponse:
+ return MixedbreadWithStreamedResponse(self)
@property
@override
def qs(self) -> Querystring:
- return Querystring(array_format="comma")
+ return Querystring(array_format="repeat")
@property
@override
@@ -180,11 +241,11 @@ def copy(
self,
*,
api_key: str | None = None,
- environment: Literal["production", "environment_1"] | None = None,
+ environment: Literal["production", "development", "local"] | None = None,
base_url: str | httpx.URL | None = None,
- timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | Timeout | None | NotGiven = not_given,
http_client: httpx.Client | None = None,
- max_retries: int | NotGiven = NOT_GIVEN,
+ max_retries: int | NotGiven = not_given,
default_headers: Mapping[str, str] | None = None,
set_default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
@@ -229,6 +290,161 @@ def copy(
# client.with_options(timeout=10).foo.create(...)
with_options = copy
+ def embed(
+ self,
+ *,
+ model: str,
+ input: Union[str, SequenceNotStr[str]],
+ dimensions: Optional[int] | Omit = omit,
+ prompt: Optional[str] | Omit = omit,
+ normalized: bool | Omit = omit,
+ encoding_format: Union[EncodingFormat, List[EncodingFormat]] | Omit = omit,
+ # 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,
+ ) -> EmbeddingCreateResponse:
+ """
+ Create embeddings for text or images using the specified model, encoding format,
+ and normalization.
+
+ Args: params: The parameters for creating embeddings.
+
+ Returns: EmbeddingCreateResponse: The response containing the embeddings.
+
+ Args:
+ model: The model to use for creating embeddings.
+
+ input: The input to create embeddings for.
+
+ dimensions: The number of dimensions to use for the embeddings.
+
+ prompt: The prompt to use for the embedding creation.
+
+ normalized: Whether to normalize the embeddings.
+
+ encoding_format: The encoding format(s) of the embeddings. Can be a single format or a list of
+ formats.
+
+ 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
+ """
+ return self.post(
+ "/v1/embeddings",
+ body=maybe_transform(
+ {
+ "model": model,
+ "input": input,
+ "dimensions": dimensions,
+ "prompt": prompt,
+ "normalized": normalized,
+ "encoding_format": encoding_format,
+ },
+ client_embed_params.ClientEmbedParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=EmbeddingCreateResponse,
+ )
+
+ def info(
+ self,
+ *,
+ # 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,
+ ) -> InfoResponse:
+ """
+ Returns service information, including name and version.
+
+ Returns: InfoResponse: A response containing the service name and version.
+ """
+ return self.get(
+ "/",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=InfoResponse,
+ )
+
+ def rerank(
+ self,
+ *,
+ model: str | Omit = omit,
+ query: str,
+ input: SequenceNotStr[Union[str, Iterable[object], object]],
+ rank_fields: Optional[SequenceNotStr[str]] | Omit = omit,
+ top_k: int | Omit = omit,
+ return_input: bool | Omit = omit,
+ rewrite_query: bool | Omit = omit,
+ # 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,
+ ) -> RerankResponse:
+ """
+ Rerank different kind of documents for a given query.
+
+ Args: params: RerankParams: The parameters for reranking.
+
+ Returns: RerankResponse: The reranked documents for the input query.
+
+ Args:
+ model: The model to use for reranking documents.
+
+ query: The query to rerank the documents.
+
+ input: The input documents to rerank.
+
+ rank_fields: The fields of the documents to rank.
+
+ top_k: The number of documents to return.
+
+ return_input: Whether to return the documents.
+
+ rewrite_query: Wether or not to rewrite the query before passing it to the reranking model
+
+ 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
+ """
+ return self.post(
+ "/v1/reranking",
+ body=maybe_transform(
+ {
+ "model": model,
+ "query": query,
+ "input": input,
+ "rank_fields": rank_fields,
+ "top_k": top_k,
+ "return_input": return_input,
+ "rewrite_query": rewrite_query,
+ },
+ client_rerank_params.ClientRerankParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=RerankResponse,
+ )
+
@override
def _make_status_error(
self,
@@ -264,29 +480,18 @@ def _make_status_error(
class AsyncMixedbread(AsyncAPIClient):
- service_info: service_info.AsyncServiceInfoResource
- files: files.AsyncFilesResource
- completions: completions.AsyncCompletionsResource
- vector_stores: vector_stores.AsyncVectorStoresResource
- parsing: parsing.AsyncParsingResource
- extractions: extractions.AsyncExtractionsResource
- embeddings: embeddings.AsyncEmbeddingsResource
- reranking: reranking.AsyncRerankingResource
- with_raw_response: AsyncMixedbreadWithRawResponse
- with_streaming_response: AsyncMixedbreadWithStreamedResponse
-
# client options
api_key: str
- _environment: Literal["production", "environment_1"] | NotGiven
+ _environment: Literal["production", "development", "local"] | NotGiven
def __init__(
self,
*,
api_key: str | None = None,
- environment: Literal["production", "environment_1"] | NotGiven = NOT_GIVEN,
- base_url: str | httpx.URL | None | NotGiven = NOT_GIVEN,
- timeout: Union[float, Timeout, None, NotGiven] = NOT_GIVEN,
+ environment: Literal["production", "development", "local"] | NotGiven = not_given,
+ base_url: str | httpx.URL | None | NotGiven = not_given,
+ timeout: float | Timeout | None | NotGiven = not_given,
max_retries: int = DEFAULT_MAX_RETRIES,
default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
@@ -304,15 +509,15 @@ def __init__(
# part of our public interface in the future.
_strict_response_validation: bool = False,
) -> None:
- """Construct a new async mixedbread client instance.
+ """Construct a new async AsyncMixedbread client instance.
- This automatically infers the `api_key` argument from the `API_KEY` environment variable if it is not provided.
+ This automatically infers the `api_key` argument from the `MXBAI_API_KEY` environment variable if it is not provided.
"""
if api_key is None:
- api_key = os.environ.get("API_KEY")
+ api_key = os.environ.get("MXBAI_API_KEY")
if api_key is None:
raise MixedbreadError(
- "The api_key client option must be set either by passing api_key to the client or by setting the API_KEY environment variable"
+ "The api_key client option must be set either by passing api_key to the client or by setting the MXBAI_API_KEY environment variable"
)
self.api_key = api_key
@@ -353,21 +558,66 @@ def __init__(
_strict_response_validation=_strict_response_validation,
)
- self.service_info = service_info.AsyncServiceInfoResource(self)
- self.files = files.AsyncFilesResource(self)
- self.completions = completions.AsyncCompletionsResource(self)
- self.vector_stores = vector_stores.AsyncVectorStoresResource(self)
- self.parsing = parsing.AsyncParsingResource(self)
- self.extractions = extractions.AsyncExtractionsResource(self)
- self.embeddings = embeddings.AsyncEmbeddingsResource(self)
- self.reranking = reranking.AsyncRerankingResource(self)
- self.with_raw_response = AsyncMixedbreadWithRawResponse(self)
- self.with_streaming_response = AsyncMixedbreadWithStreamedResponse(self)
+ @cached_property
+ def stores(self) -> AsyncStoresResource:
+ from .resources.stores import AsyncStoresResource
+
+ return AsyncStoresResource(self)
+
+ @cached_property
+ def parsing(self) -> AsyncParsingResource:
+ from .resources.parsing import AsyncParsingResource
+
+ return AsyncParsingResource(self)
+
+ @cached_property
+ def files(self) -> AsyncFilesResource:
+ from .resources.files import AsyncFilesResource
+
+ return AsyncFilesResource(self)
+
+ @cached_property
+ def extractions(self) -> AsyncExtractionsResource:
+ from .resources.extractions import AsyncExtractionsResource
+
+ return AsyncExtractionsResource(self)
+
+ @cached_property
+ def embeddings(self) -> AsyncEmbeddingsResource:
+ from .resources.embeddings import AsyncEmbeddingsResource
+
+ return AsyncEmbeddingsResource(self)
+
+ @cached_property
+ def data_sources(self) -> AsyncDataSourcesResource:
+ from .resources.data_sources import AsyncDataSourcesResource
+
+ return AsyncDataSourcesResource(self)
+
+ @cached_property
+ def api_keys(self) -> AsyncAPIKeysResource:
+ from .resources.api_keys import AsyncAPIKeysResource
+
+ return AsyncAPIKeysResource(self)
+
+ @cached_property
+ def chat(self) -> AsyncChatResource:
+ from .resources.chat import AsyncChatResource
+
+ return AsyncChatResource(self)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncMixedbreadWithRawResponse:
+ return AsyncMixedbreadWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncMixedbreadWithStreamedResponse:
+ return AsyncMixedbreadWithStreamedResponse(self)
@property
@override
def qs(self) -> Querystring:
- return Querystring(array_format="comma")
+ return Querystring(array_format="repeat")
@property
@override
@@ -388,11 +638,11 @@ def copy(
self,
*,
api_key: str | None = None,
- environment: Literal["production", "environment_1"] | None = None,
+ environment: Literal["production", "development", "local"] | None = None,
base_url: str | httpx.URL | None = None,
- timeout: float | Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | Timeout | None | NotGiven = not_given,
http_client: httpx.AsyncClient | None = None,
- max_retries: int | NotGiven = NOT_GIVEN,
+ max_retries: int | NotGiven = not_given,
default_headers: Mapping[str, str] | None = None,
set_default_headers: Mapping[str, str] | None = None,
default_query: Mapping[str, object] | None = None,
@@ -437,6 +687,161 @@ def copy(
# client.with_options(timeout=10).foo.create(...)
with_options = copy
+ async def embed(
+ self,
+ *,
+ model: str,
+ input: Union[str, SequenceNotStr[str]],
+ dimensions: Optional[int] | Omit = omit,
+ prompt: Optional[str] | Omit = omit,
+ normalized: bool | Omit = omit,
+ encoding_format: Union[EncodingFormat, List[EncodingFormat]] | Omit = omit,
+ # 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,
+ ) -> EmbeddingCreateResponse:
+ """
+ Create embeddings for text or images using the specified model, encoding format,
+ and normalization.
+
+ Args: params: The parameters for creating embeddings.
+
+ Returns: EmbeddingCreateResponse: The response containing the embeddings.
+
+ Args:
+ model: The model to use for creating embeddings.
+
+ input: The input to create embeddings for.
+
+ dimensions: The number of dimensions to use for the embeddings.
+
+ prompt: The prompt to use for the embedding creation.
+
+ normalized: Whether to normalize the embeddings.
+
+ encoding_format: The encoding format(s) of the embeddings. Can be a single format or a list of
+ formats.
+
+ 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
+ """
+ return await self.post(
+ "/v1/embeddings",
+ body=await async_maybe_transform(
+ {
+ "model": model,
+ "input": input,
+ "dimensions": dimensions,
+ "prompt": prompt,
+ "normalized": normalized,
+ "encoding_format": encoding_format,
+ },
+ client_embed_params.ClientEmbedParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=EmbeddingCreateResponse,
+ )
+
+ async def info(
+ self,
+ *,
+ # 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,
+ ) -> InfoResponse:
+ """
+ Returns service information, including name and version.
+
+ Returns: InfoResponse: A response containing the service name and version.
+ """
+ return await self.get(
+ "/",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=InfoResponse,
+ )
+
+ async def rerank(
+ self,
+ *,
+ model: str | Omit = omit,
+ query: str,
+ input: SequenceNotStr[Union[str, Iterable[object], object]],
+ rank_fields: Optional[SequenceNotStr[str]] | Omit = omit,
+ top_k: int | Omit = omit,
+ return_input: bool | Omit = omit,
+ rewrite_query: bool | Omit = omit,
+ # 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,
+ ) -> RerankResponse:
+ """
+ Rerank different kind of documents for a given query.
+
+ Args: params: RerankParams: The parameters for reranking.
+
+ Returns: RerankResponse: The reranked documents for the input query.
+
+ Args:
+ model: The model to use for reranking documents.
+
+ query: The query to rerank the documents.
+
+ input: The input documents to rerank.
+
+ rank_fields: The fields of the documents to rank.
+
+ top_k: The number of documents to return.
+
+ return_input: Whether to return the documents.
+
+ rewrite_query: Wether or not to rewrite the query before passing it to the reranking model
+
+ 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
+ """
+ return await self.post(
+ "/v1/reranking",
+ body=await async_maybe_transform(
+ {
+ "model": model,
+ "query": query,
+ "input": input,
+ "rank_fields": rank_fields,
+ "top_k": top_k,
+ "return_input": return_input,
+ "rewrite_query": rewrite_query,
+ },
+ client_rerank_params.ClientRerankParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=RerankResponse,
+ )
+
@override
def _make_status_error(
self,
@@ -472,51 +877,263 @@ def _make_status_error(
class MixedbreadWithRawResponse:
+ _client: Mixedbread
+
def __init__(self, client: Mixedbread) -> None:
- self.service_info = service_info.ServiceInfoResourceWithRawResponse(client.service_info)
- self.files = files.FilesResourceWithRawResponse(client.files)
- self.completions = completions.CompletionsResourceWithRawResponse(client.completions)
- self.vector_stores = vector_stores.VectorStoresResourceWithRawResponse(client.vector_stores)
- self.parsing = parsing.ParsingResourceWithRawResponse(client.parsing)
- self.extractions = extractions.ExtractionsResourceWithRawResponse(client.extractions)
- self.embeddings = embeddings.EmbeddingsResourceWithRawResponse(client.embeddings)
- self.reranking = reranking.RerankingResourceWithRawResponse(client.reranking)
+ self._client = client
+
+ self.embed = to_raw_response_wrapper(
+ client.embed,
+ )
+ self.info = to_raw_response_wrapper(
+ client.info,
+ )
+ self.rerank = to_raw_response_wrapper(
+ client.rerank,
+ )
+
+ @cached_property
+ def stores(self) -> stores.StoresResourceWithRawResponse:
+ from .resources.stores import StoresResourceWithRawResponse
+
+ return StoresResourceWithRawResponse(self._client.stores)
+
+ @cached_property
+ def parsing(self) -> parsing.ParsingResourceWithRawResponse:
+ from .resources.parsing import ParsingResourceWithRawResponse
+
+ return ParsingResourceWithRawResponse(self._client.parsing)
+
+ @cached_property
+ def files(self) -> files.FilesResourceWithRawResponse:
+ from .resources.files import FilesResourceWithRawResponse
+
+ return FilesResourceWithRawResponse(self._client.files)
+
+ @cached_property
+ def extractions(self) -> extractions.ExtractionsResourceWithRawResponse:
+ from .resources.extractions import ExtractionsResourceWithRawResponse
+
+ return ExtractionsResourceWithRawResponse(self._client.extractions)
+
+ @cached_property
+ def embeddings(self) -> embeddings.EmbeddingsResourceWithRawResponse:
+ from .resources.embeddings import EmbeddingsResourceWithRawResponse
+
+ return EmbeddingsResourceWithRawResponse(self._client.embeddings)
+
+ @cached_property
+ def data_sources(self) -> data_sources.DataSourcesResourceWithRawResponse:
+ from .resources.data_sources import DataSourcesResourceWithRawResponse
+
+ return DataSourcesResourceWithRawResponse(self._client.data_sources)
+
+ @cached_property
+ def api_keys(self) -> api_keys.APIKeysResourceWithRawResponse:
+ from .resources.api_keys import APIKeysResourceWithRawResponse
+
+ return APIKeysResourceWithRawResponse(self._client.api_keys)
+
+ @cached_property
+ def chat(self) -> chat.ChatResourceWithRawResponse:
+ from .resources.chat import ChatResourceWithRawResponse
+
+ return ChatResourceWithRawResponse(self._client.chat)
class AsyncMixedbreadWithRawResponse:
+ _client: AsyncMixedbread
+
def __init__(self, client: AsyncMixedbread) -> None:
- self.service_info = service_info.AsyncServiceInfoResourceWithRawResponse(client.service_info)
- self.files = files.AsyncFilesResourceWithRawResponse(client.files)
- self.completions = completions.AsyncCompletionsResourceWithRawResponse(client.completions)
- self.vector_stores = vector_stores.AsyncVectorStoresResourceWithRawResponse(client.vector_stores)
- self.parsing = parsing.AsyncParsingResourceWithRawResponse(client.parsing)
- self.extractions = extractions.AsyncExtractionsResourceWithRawResponse(client.extractions)
- self.embeddings = embeddings.AsyncEmbeddingsResourceWithRawResponse(client.embeddings)
- self.reranking = reranking.AsyncRerankingResourceWithRawResponse(client.reranking)
+ self._client = client
+
+ self.embed = async_to_raw_response_wrapper(
+ client.embed,
+ )
+ self.info = async_to_raw_response_wrapper(
+ client.info,
+ )
+ self.rerank = async_to_raw_response_wrapper(
+ client.rerank,
+ )
+
+ @cached_property
+ def stores(self) -> stores.AsyncStoresResourceWithRawResponse:
+ from .resources.stores import AsyncStoresResourceWithRawResponse
+
+ return AsyncStoresResourceWithRawResponse(self._client.stores)
+
+ @cached_property
+ def parsing(self) -> parsing.AsyncParsingResourceWithRawResponse:
+ from .resources.parsing import AsyncParsingResourceWithRawResponse
+
+ return AsyncParsingResourceWithRawResponse(self._client.parsing)
+
+ @cached_property
+ def files(self) -> files.AsyncFilesResourceWithRawResponse:
+ from .resources.files import AsyncFilesResourceWithRawResponse
+
+ return AsyncFilesResourceWithRawResponse(self._client.files)
+
+ @cached_property
+ def extractions(self) -> extractions.AsyncExtractionsResourceWithRawResponse:
+ from .resources.extractions import AsyncExtractionsResourceWithRawResponse
+
+ return AsyncExtractionsResourceWithRawResponse(self._client.extractions)
+
+ @cached_property
+ def embeddings(self) -> embeddings.AsyncEmbeddingsResourceWithRawResponse:
+ from .resources.embeddings import AsyncEmbeddingsResourceWithRawResponse
+
+ return AsyncEmbeddingsResourceWithRawResponse(self._client.embeddings)
+
+ @cached_property
+ def data_sources(self) -> data_sources.AsyncDataSourcesResourceWithRawResponse:
+ from .resources.data_sources import AsyncDataSourcesResourceWithRawResponse
+
+ return AsyncDataSourcesResourceWithRawResponse(self._client.data_sources)
+
+ @cached_property
+ def api_keys(self) -> api_keys.AsyncAPIKeysResourceWithRawResponse:
+ from .resources.api_keys import AsyncAPIKeysResourceWithRawResponse
+
+ return AsyncAPIKeysResourceWithRawResponse(self._client.api_keys)
+
+ @cached_property
+ def chat(self) -> chat.AsyncChatResourceWithRawResponse:
+ from .resources.chat import AsyncChatResourceWithRawResponse
+
+ return AsyncChatResourceWithRawResponse(self._client.chat)
class MixedbreadWithStreamedResponse:
+ _client: Mixedbread
+
def __init__(self, client: Mixedbread) -> None:
- self.service_info = service_info.ServiceInfoResourceWithStreamingResponse(client.service_info)
- self.files = files.FilesResourceWithStreamingResponse(client.files)
- self.completions = completions.CompletionsResourceWithStreamingResponse(client.completions)
- self.vector_stores = vector_stores.VectorStoresResourceWithStreamingResponse(client.vector_stores)
- self.parsing = parsing.ParsingResourceWithStreamingResponse(client.parsing)
- self.extractions = extractions.ExtractionsResourceWithStreamingResponse(client.extractions)
- self.embeddings = embeddings.EmbeddingsResourceWithStreamingResponse(client.embeddings)
- self.reranking = reranking.RerankingResourceWithStreamingResponse(client.reranking)
+ self._client = client
+
+ self.embed = to_streamed_response_wrapper(
+ client.embed,
+ )
+ self.info = to_streamed_response_wrapper(
+ client.info,
+ )
+ self.rerank = to_streamed_response_wrapper(
+ client.rerank,
+ )
+
+ @cached_property
+ def stores(self) -> stores.StoresResourceWithStreamingResponse:
+ from .resources.stores import StoresResourceWithStreamingResponse
+
+ return StoresResourceWithStreamingResponse(self._client.stores)
+
+ @cached_property
+ def parsing(self) -> parsing.ParsingResourceWithStreamingResponse:
+ from .resources.parsing import ParsingResourceWithStreamingResponse
+
+ return ParsingResourceWithStreamingResponse(self._client.parsing)
+
+ @cached_property
+ def files(self) -> files.FilesResourceWithStreamingResponse:
+ from .resources.files import FilesResourceWithStreamingResponse
+
+ return FilesResourceWithStreamingResponse(self._client.files)
+
+ @cached_property
+ def extractions(self) -> extractions.ExtractionsResourceWithStreamingResponse:
+ from .resources.extractions import ExtractionsResourceWithStreamingResponse
+
+ return ExtractionsResourceWithStreamingResponse(self._client.extractions)
+
+ @cached_property
+ def embeddings(self) -> embeddings.EmbeddingsResourceWithStreamingResponse:
+ from .resources.embeddings import EmbeddingsResourceWithStreamingResponse
+
+ return EmbeddingsResourceWithStreamingResponse(self._client.embeddings)
+
+ @cached_property
+ def data_sources(self) -> data_sources.DataSourcesResourceWithStreamingResponse:
+ from .resources.data_sources import DataSourcesResourceWithStreamingResponse
+
+ return DataSourcesResourceWithStreamingResponse(self._client.data_sources)
+
+ @cached_property
+ def api_keys(self) -> api_keys.APIKeysResourceWithStreamingResponse:
+ from .resources.api_keys import APIKeysResourceWithStreamingResponse
+
+ return APIKeysResourceWithStreamingResponse(self._client.api_keys)
+
+ @cached_property
+ def chat(self) -> chat.ChatResourceWithStreamingResponse:
+ from .resources.chat import ChatResourceWithStreamingResponse
+
+ return ChatResourceWithStreamingResponse(self._client.chat)
class AsyncMixedbreadWithStreamedResponse:
+ _client: AsyncMixedbread
+
def __init__(self, client: AsyncMixedbread) -> None:
- self.service_info = service_info.AsyncServiceInfoResourceWithStreamingResponse(client.service_info)
- self.files = files.AsyncFilesResourceWithStreamingResponse(client.files)
- self.completions = completions.AsyncCompletionsResourceWithStreamingResponse(client.completions)
- self.vector_stores = vector_stores.AsyncVectorStoresResourceWithStreamingResponse(client.vector_stores)
- self.parsing = parsing.AsyncParsingResourceWithStreamingResponse(client.parsing)
- self.extractions = extractions.AsyncExtractionsResourceWithStreamingResponse(client.extractions)
- self.embeddings = embeddings.AsyncEmbeddingsResourceWithStreamingResponse(client.embeddings)
- self.reranking = reranking.AsyncRerankingResourceWithStreamingResponse(client.reranking)
+ self._client = client
+
+ self.embed = async_to_streamed_response_wrapper(
+ client.embed,
+ )
+ self.info = async_to_streamed_response_wrapper(
+ client.info,
+ )
+ self.rerank = async_to_streamed_response_wrapper(
+ client.rerank,
+ )
+
+ @cached_property
+ def stores(self) -> stores.AsyncStoresResourceWithStreamingResponse:
+ from .resources.stores import AsyncStoresResourceWithStreamingResponse
+
+ return AsyncStoresResourceWithStreamingResponse(self._client.stores)
+
+ @cached_property
+ def parsing(self) -> parsing.AsyncParsingResourceWithStreamingResponse:
+ from .resources.parsing import AsyncParsingResourceWithStreamingResponse
+
+ return AsyncParsingResourceWithStreamingResponse(self._client.parsing)
+
+ @cached_property
+ def files(self) -> files.AsyncFilesResourceWithStreamingResponse:
+ from .resources.files import AsyncFilesResourceWithStreamingResponse
+
+ return AsyncFilesResourceWithStreamingResponse(self._client.files)
+
+ @cached_property
+ def extractions(self) -> extractions.AsyncExtractionsResourceWithStreamingResponse:
+ from .resources.extractions import AsyncExtractionsResourceWithStreamingResponse
+
+ return AsyncExtractionsResourceWithStreamingResponse(self._client.extractions)
+
+ @cached_property
+ def embeddings(self) -> embeddings.AsyncEmbeddingsResourceWithStreamingResponse:
+ from .resources.embeddings import AsyncEmbeddingsResourceWithStreamingResponse
+
+ return AsyncEmbeddingsResourceWithStreamingResponse(self._client.embeddings)
+
+ @cached_property
+ def data_sources(self) -> data_sources.AsyncDataSourcesResourceWithStreamingResponse:
+ from .resources.data_sources import AsyncDataSourcesResourceWithStreamingResponse
+
+ return AsyncDataSourcesResourceWithStreamingResponse(self._client.data_sources)
+
+ @cached_property
+ def api_keys(self) -> api_keys.AsyncAPIKeysResourceWithStreamingResponse:
+ from .resources.api_keys import AsyncAPIKeysResourceWithStreamingResponse
+
+ return AsyncAPIKeysResourceWithStreamingResponse(self._client.api_keys)
+
+ @cached_property
+ def chat(self) -> chat.AsyncChatResourceWithStreamingResponse:
+ from .resources.chat import AsyncChatResourceWithStreamingResponse
+
+ return AsyncChatResourceWithStreamingResponse(self._client.chat)
Client = Mixedbread
diff --git a/src/mixedbread/_compat.py b/src/mixedbread/_compat.py
index 92d9ee61..786ff42a 100644
--- a/src/mixedbread/_compat.py
+++ b/src/mixedbread/_compat.py
@@ -12,14 +12,13 @@
_T = TypeVar("_T")
_ModelT = TypeVar("_ModelT", bound=pydantic.BaseModel)
-# --------------- Pydantic v2 compatibility ---------------
+# --------------- Pydantic v2, v3 compatibility ---------------
# Pyright incorrectly reports some of our functions as overriding a method when they don't
# pyright: reportIncompatibleMethodOverride=false
-PYDANTIC_V2 = pydantic.VERSION.startswith("2.")
+PYDANTIC_V1 = pydantic.VERSION.startswith("1.")
-# v1 re-exports
if TYPE_CHECKING:
def parse_date(value: date | StrBytesIntFloat) -> date: # noqa: ARG001
@@ -44,90 +43,92 @@ def is_typeddict(type_: type[Any]) -> bool: # noqa: ARG001
...
else:
- if PYDANTIC_V2:
- from pydantic.v1.typing import (
+ # v1 re-exports
+ if PYDANTIC_V1:
+ from pydantic.typing import (
get_args as get_args,
is_union as is_union,
get_origin as get_origin,
is_typeddict as is_typeddict,
is_literal_type as is_literal_type,
)
- from pydantic.v1.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime
+ from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime
else:
- from pydantic.typing import (
+ from ._utils import (
get_args as get_args,
is_union as is_union,
get_origin as get_origin,
+ parse_date as parse_date,
is_typeddict as is_typeddict,
+ parse_datetime as parse_datetime,
is_literal_type as is_literal_type,
)
- from pydantic.datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime
# refactored config
if TYPE_CHECKING:
from pydantic import ConfigDict as ConfigDict
else:
- if PYDANTIC_V2:
- from pydantic import ConfigDict
- else:
+ if PYDANTIC_V1:
# TODO: provide an error message here?
ConfigDict = None
+ else:
+ from pydantic import ConfigDict as ConfigDict
# renamed methods / properties
def parse_obj(model: type[_ModelT], value: object) -> _ModelT:
- if PYDANTIC_V2:
- return model.model_validate(value)
- else:
+ if PYDANTIC_V1:
return cast(_ModelT, model.parse_obj(value)) # pyright: ignore[reportDeprecated, reportUnnecessaryCast]
+ else:
+ return model.model_validate(value)
def field_is_required(field: FieldInfo) -> bool:
- if PYDANTIC_V2:
- return field.is_required()
- return field.required # type: ignore
+ if PYDANTIC_V1:
+ return field.required # type: ignore
+ return field.is_required()
def field_get_default(field: FieldInfo) -> Any:
value = field.get_default()
- if PYDANTIC_V2:
- from pydantic_core import PydanticUndefined
-
- if value == PydanticUndefined:
- return None
+ if PYDANTIC_V1:
return value
+ from pydantic_core import PydanticUndefined
+
+ if value == PydanticUndefined:
+ return None
return value
def field_outer_type(field: FieldInfo) -> Any:
- if PYDANTIC_V2:
- return field.annotation
- return field.outer_type_ # type: ignore
+ if PYDANTIC_V1:
+ return field.outer_type_ # type: ignore
+ return field.annotation
def get_model_config(model: type[pydantic.BaseModel]) -> Any:
- if PYDANTIC_V2:
- return model.model_config
- return model.__config__ # type: ignore
+ if PYDANTIC_V1:
+ return model.__config__ # type: ignore
+ return model.model_config
def get_model_fields(model: type[pydantic.BaseModel]) -> dict[str, FieldInfo]:
- if PYDANTIC_V2:
- return model.model_fields
- return model.__fields__ # type: ignore
+ if PYDANTIC_V1:
+ return model.__fields__ # type: ignore
+ return model.model_fields
def model_copy(model: _ModelT, *, deep: bool = False) -> _ModelT:
- if PYDANTIC_V2:
- return model.model_copy(deep=deep)
- return model.copy(deep=deep) # type: ignore
+ if PYDANTIC_V1:
+ return model.copy(deep=deep) # type: ignore
+ return model.model_copy(deep=deep)
def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str:
- if PYDANTIC_V2:
- return model.model_dump_json(indent=indent)
- return model.json(indent=indent) # type: ignore
+ if PYDANTIC_V1:
+ return model.json(indent=indent) # type: ignore
+ return model.model_dump_json(indent=indent)
def model_dump(
@@ -138,30 +139,30 @@ def model_dump(
exclude_defaults: bool = False,
warnings: bool = True,
mode: Literal["json", "python"] = "python",
+ by_alias: bool | None = None,
) -> dict[str, Any]:
- if PYDANTIC_V2 or hasattr(model, "model_dump"):
+ if (not PYDANTIC_V1) or hasattr(model, "model_dump"):
return model.model_dump(
mode=mode,
exclude=exclude,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
# warnings are not supported in Pydantic v1
- warnings=warnings if PYDANTIC_V2 else True,
+ warnings=True if PYDANTIC_V1 else warnings,
+ by_alias=by_alias,
)
return cast(
"dict[str, Any]",
model.dict( # pyright: ignore[reportDeprecated, reportUnnecessaryCast]
- exclude=exclude,
- exclude_unset=exclude_unset,
- exclude_defaults=exclude_defaults,
+ exclude=exclude, exclude_unset=exclude_unset, exclude_defaults=exclude_defaults, by_alias=bool(by_alias)
),
)
def model_parse(model: type[_ModelT], data: Any) -> _ModelT:
- if PYDANTIC_V2:
- return model.model_validate(data)
- return model.parse_obj(data) # pyright: ignore[reportDeprecated]
+ if PYDANTIC_V1:
+ return model.parse_obj(data) # pyright: ignore[reportDeprecated]
+ return model.model_validate(data)
# generic models
@@ -170,17 +171,16 @@ def model_parse(model: type[_ModelT], data: Any) -> _ModelT:
class GenericModel(pydantic.BaseModel): ...
else:
- if PYDANTIC_V2:
+ if PYDANTIC_V1:
+ import pydantic.generics
+
+ class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ...
+ else:
# there no longer needs to be a distinction in v2 but
# we still have to create our own subclass to avoid
# inconsistent MRO ordering errors
class GenericModel(pydantic.BaseModel): ...
- else:
- import pydantic.generics
-
- class GenericModel(pydantic.generics.GenericModel, pydantic.BaseModel): ...
-
# cached properties
if TYPE_CHECKING:
diff --git a/src/mixedbread/_constants.py b/src/mixedbread/_constants.py
index a2ac3b6f..6ddf2c71 100644
--- a/src/mixedbread/_constants.py
+++ b/src/mixedbread/_constants.py
@@ -6,7 +6,7 @@
OVERRIDE_CAST_TO_HEADER = "____stainless_override_cast_to"
# default timeout is 1 minute
-DEFAULT_TIMEOUT = httpx.Timeout(timeout=60.0, connect=5.0)
+DEFAULT_TIMEOUT = httpx.Timeout(timeout=60, connect=5.0)
DEFAULT_MAX_RETRIES = 2
DEFAULT_CONNECTION_LIMITS = httpx.Limits(max_connections=100, max_keepalive_connections=20)
diff --git a/src/mixedbread/_files.py b/src/mixedbread/_files.py
index 715cc207..729ea489 100644
--- a/src/mixedbread/_files.py
+++ b/src/mixedbread/_files.py
@@ -34,7 +34,7 @@ def assert_is_file_content(obj: object, *, key: str | None = None) -> None:
if not is_file_content(obj):
prefix = f"Expected entry at `{key}`" if key is not None else f"Expected file input `{obj!r}`"
raise RuntimeError(
- f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead."
+ f"{prefix} to be bytes, an io.IOBase instance, PathLike or a tuple but received {type(obj)} instead. See https://github.com/mixedbread-ai/mixedbread-python/tree/main#file-uploads"
) from None
@@ -69,12 +69,12 @@ def _transform_file(file: FileTypes) -> HttpxFileTypes:
return file
if is_tuple_t(file):
- return (file[0], _read_file_content(file[1]), *file[2:])
+ return (file[0], read_file_content(file[1]), *file[2:])
raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple")
-def _read_file_content(file: FileContent) -> HttpxFileContent:
+def read_file_content(file: FileContent) -> HttpxFileContent:
if isinstance(file, os.PathLike):
return pathlib.Path(file).read_bytes()
return file
@@ -111,12 +111,12 @@ async def _async_transform_file(file: FileTypes) -> HttpxFileTypes:
return file
if is_tuple_t(file):
- return (file[0], await _async_read_file_content(file[1]), *file[2:])
+ return (file[0], await async_read_file_content(file[1]), *file[2:])
raise TypeError(f"Expected file types input to be a FileContent type or to be a tuple")
-async def _async_read_file_content(file: FileContent) -> HttpxFileContent:
+async def async_read_file_content(file: FileContent) -> HttpxFileContent:
if isinstance(file, os.PathLike):
return await anyio.Path(file).read_bytes()
diff --git a/src/mixedbread/_models.py b/src/mixedbread/_models.py
index 9a918aab..29070e05 100644
--- a/src/mixedbread/_models.py
+++ b/src/mixedbread/_models.py
@@ -2,9 +2,24 @@
import os
import inspect
-from typing import TYPE_CHECKING, Any, Type, Union, Generic, TypeVar, Callable, cast
+import weakref
+from typing import (
+ IO,
+ TYPE_CHECKING,
+ Any,
+ Type,
+ Union,
+ Generic,
+ TypeVar,
+ Callable,
+ Iterable,
+ Optional,
+ AsyncIterable,
+ cast,
+)
from datetime import date, datetime
from typing_extensions import (
+ List,
Unpack,
Literal,
ClassVar,
@@ -19,7 +34,6 @@
)
import pydantic
-import pydantic.generics
from pydantic.fields import FieldInfo
from ._types import (
@@ -50,7 +64,7 @@
strip_annotated_type,
)
from ._compat import (
- PYDANTIC_V2,
+ PYDANTIC_V1,
ConfigDict,
GenericModel as BaseGenericModel,
get_args,
@@ -65,7 +79,7 @@
from ._constants import RAW_RESPONSE_HEADER
if TYPE_CHECKING:
- from pydantic_core.core_schema import ModelField, LiteralSchema, ModelFieldsSchema
+ from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema
__all__ = ["BaseModel", "GenericModel"]
@@ -81,11 +95,7 @@ class _ConfigProtocol(Protocol):
class BaseModel(pydantic.BaseModel):
- if PYDANTIC_V2:
- model_config: ClassVar[ConfigDict] = ConfigDict(
- extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true"))
- )
- else:
+ if PYDANTIC_V1:
@property
@override
@@ -95,6 +105,10 @@ def model_fields_set(self) -> set[str]:
class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated]
extra: Any = pydantic.Extra.allow # type: ignore
+ else:
+ model_config: ClassVar[ConfigDict] = ConfigDict(
+ extra="allow", defer_build=coerce_boolean(os.environ.get("DEFER_PYDANTIC_BUILD", "true"))
+ )
def to_dict(
self,
@@ -172,7 +186,7 @@ def to_json(
@override
def __str__(self) -> str:
# mypy complains about an invalid self arg
- return f'{self.__repr_name__()}({self.__repr_str__(", ")})' # type: ignore[misc]
+ return f"{self.__repr_name__()}({self.__repr_str__(', ')})" # type: ignore[misc]
# Override the 'construct' method in a way that supports recursive parsing without validation.
# Based on https://github.com/samuelcolvin/pydantic/issues/1168#issuecomment-817742836.
@@ -208,28 +222,32 @@ def construct( # pyright: ignore[reportIncompatibleMethodOverride]
else:
fields_values[name] = field_get_default(field)
+ extra_field_type = _get_extra_fields_type(__cls)
+
_extra = {}
for key, value in values.items():
if key not in model_fields:
- if PYDANTIC_V2:
- _extra[key] = value
- else:
+ parsed = construct_type(value=value, type_=extra_field_type) if extra_field_type is not None else value
+
+ if PYDANTIC_V1:
_fields_set.add(key)
- fields_values[key] = value
+ fields_values[key] = parsed
+ else:
+ _extra[key] = parsed
object.__setattr__(m, "__dict__", fields_values)
- if PYDANTIC_V2:
- # these properties are copied from Pydantic's `model_construct()` method
- object.__setattr__(m, "__pydantic_private__", None)
- object.__setattr__(m, "__pydantic_extra__", _extra)
- object.__setattr__(m, "__pydantic_fields_set__", _fields_set)
- else:
+ if PYDANTIC_V1:
# init_private_attributes() does not exist in v2
m._init_private_attributes() # type: ignore
# copied from Pydantic v1's `construct()` method
object.__setattr__(m, "__fields_set__", _fields_set)
+ else:
+ # these properties are copied from Pydantic's `model_construct()` method
+ object.__setattr__(m, "__pydantic_private__", None)
+ object.__setattr__(m, "__pydantic_extra__", _extra)
+ object.__setattr__(m, "__pydantic_fields_set__", _fields_set)
return m
@@ -239,7 +257,7 @@ def construct( # pyright: ignore[reportIncompatibleMethodOverride]
# although not in practice
model_construct = construct
- if not PYDANTIC_V2:
+ if PYDANTIC_V1:
# we define aliases for some of the new pydantic v2 methods so
# that we can just document these methods without having to specify
# a specific pydantic version as some users may not know which
@@ -252,13 +270,15 @@ def model_dump(
mode: Literal["json", "python"] | str = "python",
include: IncEx | None = None,
exclude: IncEx | None = None,
- by_alias: bool = False,
+ context: Any | None = None,
+ by_alias: bool | None = None,
exclude_unset: bool = False,
exclude_defaults: bool = False,
exclude_none: bool = False,
+ exclude_computed_fields: bool = False,
round_trip: bool = False,
warnings: bool | Literal["none", "warn", "error"] = True,
- context: dict[str, Any] | None = None,
+ fallback: Callable[[Any], Any] | None = None,
serialize_as_any: bool = False,
) -> dict[str, Any]:
"""Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump
@@ -267,16 +287,24 @@ def model_dump(
Args:
mode: The mode in which `to_python` should run.
- If mode is 'json', the dictionary will only contain JSON serializable types.
- If mode is 'python', the dictionary may contain any Python objects.
- include: A list of fields to include in the output.
- exclude: A list of fields to exclude from the output.
+ If mode is 'json', the output will only contain JSON serializable types.
+ If mode is 'python', the output may contain non-JSON-serializable Python objects.
+ include: A set of fields to include in the output.
+ exclude: A set of fields to exclude from the output.
+ context: Additional context to pass to the serializer.
by_alias: Whether to use the field's alias in the dictionary key if defined.
- exclude_unset: Whether to exclude fields that are unset or None from the output.
- exclude_defaults: Whether to exclude fields that are set to their default value from the output.
- exclude_none: Whether to exclude fields that have a value of `None` from the output.
- round_trip: Whether to enable serialization and deserialization round-trip support.
- warnings: Whether to log warnings when invalid fields are encountered.
+ exclude_unset: Whether to exclude fields that have not been explicitly set.
+ exclude_defaults: Whether to exclude fields that are set to their default value.
+ exclude_none: Whether to exclude fields that have a value of `None`.
+ exclude_computed_fields: Whether to exclude computed fields.
+ While this can be useful for round-tripping, it is usually recommended to use the dedicated
+ `round_trip` parameter instead.
+ round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T].
+ warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors,
+ "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError].
+ fallback: A function to call when an unknown value is encountered. If not provided,
+ a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised.
+ serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.
Returns:
A dictionary representation of the model.
@@ -291,31 +319,38 @@ def model_dump(
raise ValueError("context is only supported in Pydantic v2")
if serialize_as_any != False:
raise ValueError("serialize_as_any is only supported in Pydantic v2")
+ if fallback is not None:
+ raise ValueError("fallback is only supported in Pydantic v2")
+ if exclude_computed_fields != False:
+ raise ValueError("exclude_computed_fields is only supported in Pydantic v2")
dumped = super().dict( # pyright: ignore[reportDeprecated]
include=include,
exclude=exclude,
- by_alias=by_alias,
+ by_alias=by_alias if by_alias is not None else False,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude_none=exclude_none,
)
- return cast(dict[str, Any], json_safe(dumped)) if mode == "json" else dumped
+ return cast("dict[str, Any]", json_safe(dumped)) if mode == "json" else dumped
@override
def model_dump_json(
self,
*,
indent: int | None = None,
+ ensure_ascii: bool = False,
include: IncEx | None = None,
exclude: IncEx | None = None,
- by_alias: bool = False,
+ context: Any | None = None,
+ by_alias: bool | None = None,
exclude_unset: bool = False,
exclude_defaults: bool = False,
exclude_none: bool = False,
+ exclude_computed_fields: bool = False,
round_trip: bool = False,
warnings: bool | Literal["none", "warn", "error"] = True,
- context: dict[str, Any] | None = None,
+ fallback: Callable[[Any], Any] | None = None,
serialize_as_any: bool = False,
) -> str:
"""Usage docs: https://docs.pydantic.dev/2.4/concepts/serialization/#modelmodel_dump_json
@@ -344,11 +379,17 @@ def model_dump_json(
raise ValueError("context is only supported in Pydantic v2")
if serialize_as_any != False:
raise ValueError("serialize_as_any is only supported in Pydantic v2")
+ if fallback is not None:
+ raise ValueError("fallback is only supported in Pydantic v2")
+ if ensure_ascii != False:
+ raise ValueError("ensure_ascii is only supported in Pydantic v2")
+ if exclude_computed_fields != False:
+ raise ValueError("exclude_computed_fields is only supported in Pydantic v2")
return super().json( # type: ignore[reportDeprecated]
indent=indent,
include=include,
exclude=exclude,
- by_alias=by_alias,
+ by_alias=by_alias if by_alias is not None else False,
exclude_unset=exclude_unset,
exclude_defaults=exclude_defaults,
exclude_none=exclude_none,
@@ -359,15 +400,32 @@ def _construct_field(value: object, field: FieldInfo, key: str) -> object:
if value is None:
return field_get_default(field)
- if PYDANTIC_V2:
- type_ = field.annotation
- else:
+ if PYDANTIC_V1:
type_ = cast(type, field.outer_type_) # type: ignore
+ else:
+ type_ = field.annotation # type: ignore
if type_ is None:
raise RuntimeError(f"Unexpected field type is None for {key}")
- return construct_type(value=value, type_=type_)
+ return construct_type(value=value, type_=type_, metadata=getattr(field, "metadata", None))
+
+
+def _get_extra_fields_type(cls: type[pydantic.BaseModel]) -> type | None:
+ if PYDANTIC_V1:
+ # TODO
+ return None
+
+ schema = cls.__pydantic_core_schema__
+ if schema["type"] == "model":
+ fields = schema["schema"]
+ if fields["type"] == "model-fields":
+ extras = fields.get("extras_schema")
+ if extras and "cls" in extras:
+ # mypy can't narrow the type
+ return extras["cls"] # type: ignore[no-any-return]
+
+ return None
def is_basemodel(type_: type) -> bool:
@@ -421,20 +479,28 @@ def construct_type_unchecked(*, value: object, type_: type[_T]) -> _T:
return cast(_T, construct_type(value=value, type_=type_))
-def construct_type(*, value: object, type_: object) -> object:
+def construct_type(*, value: object, type_: object, metadata: Optional[List[Any]] = None) -> object:
"""Loose coercion to the expected type with construction of nested values.
If the given value does not match the expected type then it is returned as-is.
"""
+
+ # store a reference to the original type we were given before we extract any inner
+ # types so that we can properly resolve forward references in `TypeAliasType` annotations
+ original_type = None
+
# we allow `object` as the input type because otherwise, passing things like
# `Literal['value']` will be reported as a type error by type checkers
type_ = cast("type[object]", type_)
if is_type_alias_type(type_):
+ original_type = type_ # type: ignore[unreachable]
type_ = type_.__value__ # type: ignore[unreachable]
# unwrap `Annotated[T, ...]` -> `T`
- if is_annotated_type(type_):
- meta: tuple[Any, ...] = get_args(type_)[1:]
+ if metadata is not None and len(metadata) > 0:
+ meta: tuple[Any, ...] = tuple(metadata)
+ elif is_annotated_type(type_):
+ meta = get_args(type_)[1:]
type_ = extract_type_arg(type_, 0)
else:
meta = tuple()
@@ -446,7 +512,7 @@ def construct_type(*, value: object, type_: object) -> object:
if is_union(origin):
try:
- return validate_type(type_=cast("type[object]", type_), value=value)
+ return validate_type(type_=cast("type[object]", original_type or type_), value=value)
except Exception:
pass
@@ -538,6 +604,9 @@ class CachedDiscriminatorType(Protocol):
__discriminator__: DiscriminatorDetails
+DISCRIMINATOR_CACHE: weakref.WeakKeyDictionary[type, DiscriminatorDetails] = weakref.WeakKeyDictionary()
+
+
class DiscriminatorDetails:
field_name: str
"""The name of the discriminator field in the variant class, e.g.
@@ -580,8 +649,9 @@ def __init__(
def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any, ...]) -> DiscriminatorDetails | None:
- if isinstance(union, CachedDiscriminatorType):
- return union.__discriminator__
+ cached = DISCRIMINATOR_CACHE.get(union)
+ if cached is not None:
+ return cached
discriminator_field_name: str | None = None
@@ -599,30 +669,30 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any,
for variant in get_args(union):
variant = strip_annotated_type(variant)
if is_basemodel_type(variant):
- if PYDANTIC_V2:
- field = _extract_field_schema_pv2(variant, discriminator_field_name)
- if not field:
+ if PYDANTIC_V1:
+ field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast]
+ if not field_info:
continue
# Note: if one variant defines an alias then they all should
- discriminator_alias = field.get("serialization_alias")
-
- field_schema = field["schema"]
+ discriminator_alias = field_info.alias
- if field_schema["type"] == "literal":
- for entry in cast("LiteralSchema", field_schema)["expected"]:
+ if (annotation := getattr(field_info, "annotation", None)) and is_literal_type(annotation):
+ for entry in get_args(annotation):
if isinstance(entry, str):
mapping[entry] = variant
else:
- field_info = cast("dict[str, FieldInfo]", variant.__fields__).get(discriminator_field_name) # pyright: ignore[reportDeprecated, reportUnnecessaryCast]
- if not field_info:
+ field = _extract_field_schema_pv2(variant, discriminator_field_name)
+ if not field:
continue
# Note: if one variant defines an alias then they all should
- discriminator_alias = field_info.alias
+ discriminator_alias = field.get("serialization_alias")
+
+ field_schema = field["schema"]
- if field_info.annotation and is_literal_type(field_info.annotation):
- for entry in get_args(field_info.annotation):
+ if field_schema["type"] == "literal":
+ for entry in cast("LiteralSchema", field_schema)["expected"]:
if isinstance(entry, str):
mapping[entry] = variant
@@ -634,21 +704,24 @@ def _build_discriminated_union_meta(*, union: type, meta_annotations: tuple[Any,
discriminator_field=discriminator_field_name,
discriminator_alias=discriminator_alias,
)
- cast(CachedDiscriminatorType, union).__discriminator__ = details
+ DISCRIMINATOR_CACHE.setdefault(union, details)
return details
def _extract_field_schema_pv2(model: type[BaseModel], field_name: str) -> ModelField | None:
schema = model.__pydantic_core_schema__
+ if schema["type"] == "definitions":
+ schema = schema["schema"]
+
if schema["type"] != "model":
return None
+ schema = cast("ModelSchema", schema)
fields_schema = schema["schema"]
if fields_schema["type"] != "model-fields":
return None
fields_schema = cast("ModelFieldsSchema", fields_schema)
-
field = fields_schema["fields"].get(field_name)
if not field:
return None
@@ -672,7 +745,7 @@ def set_pydantic_config(typ: Any, config: pydantic.ConfigDict) -> None:
setattr(typ, "__pydantic_config__", config) # noqa: B010
-# our use of subclasssing here causes weirdness for type checkers,
+# our use of subclassing here causes weirdness for type checkers,
# so we just pretend that we don't subclass
if TYPE_CHECKING:
GenericModel = BaseModel
@@ -682,7 +755,7 @@ class GenericModel(BaseGenericModel, BaseModel):
pass
-if PYDANTIC_V2:
+if not PYDANTIC_V1:
from pydantic import TypeAdapter as _TypeAdapter
_CachedTypeAdapter = cast("TypeAdapter[object]", lru_cache(maxsize=None)(_TypeAdapter))
@@ -727,8 +800,10 @@ class FinalRequestOptionsInput(TypedDict, total=False):
timeout: float | Timeout | None
files: HttpxRequestFiles | None
idempotency_key: str
+ content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None]
json_data: Body
extra_json: AnyMapping
+ follow_redirects: bool
@final
@@ -742,18 +817,20 @@ class FinalRequestOptions(pydantic.BaseModel):
files: Union[HttpxRequestFiles, None] = None
idempotency_key: Union[str, None] = None
post_parser: Union[Callable[[Any], Any], NotGiven] = NotGiven()
+ follow_redirects: Union[bool, None] = None
+ content: Union[bytes, bytearray, IO[bytes], Iterable[bytes], AsyncIterable[bytes], None] = None
# It should be noted that we cannot use `json` here as that would override
# a BaseModel method in an incompatible fashion.
json_data: Union[Body, None] = None
extra_json: Union[AnyMapping, None] = None
- if PYDANTIC_V2:
- model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True)
- else:
+ if PYDANTIC_V1:
class Config(pydantic.BaseConfig): # pyright: ignore[reportDeprecated]
arbitrary_types_allowed: bool = True
+ else:
+ model_config: ClassVar[ConfigDict] = ConfigDict(arbitrary_types_allowed=True)
def get_max_retries(self, max_retries: int) -> int:
if isinstance(self.max_retries, NotGiven):
@@ -786,9 +863,9 @@ def construct( # type: ignore
key: strip_not_given(value)
for key, value in values.items()
}
- if PYDANTIC_V2:
- return super().model_construct(_fields_set, **kwargs)
- return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated]
+ if PYDANTIC_V1:
+ return cast(FinalRequestOptions, super().construct(_fields_set, **kwargs)) # pyright: ignore[reportDeprecated]
+ return super().model_construct(_fields_set, **kwargs)
if not TYPE_CHECKING:
# type checkers incorrectly complain about this assignment
diff --git a/src/mixedbread/_qs.py b/src/mixedbread/_qs.py
index 274320ca..ada6fd3f 100644
--- a/src/mixedbread/_qs.py
+++ b/src/mixedbread/_qs.py
@@ -4,7 +4,7 @@
from urllib.parse import parse_qs, urlencode
from typing_extensions import Literal, get_args
-from ._types import NOT_GIVEN, NotGiven, NotGivenOr
+from ._types import NotGiven, not_given
from ._utils import flatten
_T = TypeVar("_T")
@@ -41,8 +41,8 @@ def stringify(
self,
params: Params,
*,
- array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN,
- nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN,
+ array_format: ArrayFormat | NotGiven = not_given,
+ nested_format: NestedFormat | NotGiven = not_given,
) -> str:
return urlencode(
self.stringify_items(
@@ -56,8 +56,8 @@ def stringify_items(
self,
params: Params,
*,
- array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN,
- nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN,
+ array_format: ArrayFormat | NotGiven = not_given,
+ nested_format: NestedFormat | NotGiven = not_given,
) -> list[tuple[str, str]]:
opts = Options(
qs=self,
@@ -143,8 +143,8 @@ def __init__(
self,
qs: Querystring = _qs,
*,
- array_format: NotGivenOr[ArrayFormat] = NOT_GIVEN,
- nested_format: NotGivenOr[NestedFormat] = NOT_GIVEN,
+ array_format: ArrayFormat | NotGiven = not_given,
+ nested_format: NestedFormat | NotGiven = not_given,
) -> None:
self.array_format = qs.array_format if isinstance(array_format, NotGiven) else array_format
self.nested_format = qs.nested_format if isinstance(nested_format, NotGiven) else nested_format
diff --git a/src/mixedbread/_response.py b/src/mixedbread/_response.py
index 3221a254..50547a3b 100644
--- a/src/mixedbread/_response.py
+++ b/src/mixedbread/_response.py
@@ -136,6 +136,8 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T:
if cast_to and is_annotated_type(cast_to):
cast_to = extract_type_arg(cast_to, 0)
+ origin = get_origin(cast_to) or cast_to
+
if self._is_sse_stream:
if to:
if not is_stream_class_type(to):
@@ -150,6 +152,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T:
),
response=self.http_response,
client=cast(Any, self._client),
+ options=self._options,
),
)
@@ -160,6 +163,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T:
cast_to=extract_stream_chunk_type(self._stream_cls),
response=self.http_response,
client=cast(Any, self._client),
+ options=self._options,
),
)
@@ -173,6 +177,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T:
cast_to=cast_to,
response=self.http_response,
client=cast(Any, self._client),
+ options=self._options,
),
)
@@ -195,8 +200,6 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T:
if cast_to == bool:
return cast(R, response.text.lower() == "true")
- origin = get_origin(cast_to) or cast_to
-
if origin == APIResponse:
raise RuntimeError("Unexpected state - cast_to is `APIResponse`")
@@ -210,7 +213,13 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T:
raise ValueError(f"Subclasses of httpx.Response cannot be passed to `cast_to`")
return cast(R, response)
- if inspect.isclass(origin) and not issubclass(origin, BaseModel) and issubclass(origin, pydantic.BaseModel):
+ if (
+ inspect.isclass(
+ origin # pyright: ignore[reportUnknownArgumentType]
+ )
+ and not issubclass(origin, BaseModel)
+ and issubclass(origin, pydantic.BaseModel)
+ ):
raise TypeError(
"Pydantic models must subclass our base model type, e.g. `from mixedbread import BaseModel`"
)
@@ -229,7 +238,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T:
# split is required to handle cases where additional information is included
# in the response, e.g. application/json; charset=utf-8
content_type, *_ = response.headers.get("content-type", "*").split(";")
- if content_type != "application/json":
+ if not content_type.endswith("json"):
if is_basemodel(cast_to):
try:
data = response.json()
diff --git a/src/mixedbread/_streaming.py b/src/mixedbread/_streaming.py
index e5834ace..235a94a8 100644
--- a/src/mixedbread/_streaming.py
+++ b/src/mixedbread/_streaming.py
@@ -4,7 +4,7 @@
import json
import inspect
from types import TracebackType
-from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, AsyncIterator, cast
+from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, Optional, AsyncIterator, cast
from typing_extensions import Self, Protocol, TypeGuard, override, get_origin, runtime_checkable
import httpx
@@ -13,6 +13,7 @@
if TYPE_CHECKING:
from ._client import Mixedbread, AsyncMixedbread
+ from ._models import FinalRequestOptions
_T = TypeVar("_T")
@@ -22,7 +23,7 @@ class Stream(Generic[_T]):
"""Provides the core interface to iterate over a synchronous stream response."""
response: httpx.Response
-
+ _options: Optional[FinalRequestOptions] = None
_decoder: SSEBytesDecoder
def __init__(
@@ -31,10 +32,12 @@ def __init__(
cast_to: type[_T],
response: httpx.Response,
client: Mixedbread,
+ options: Optional[FinalRequestOptions] = None,
) -> None:
self.response = response
self._cast_to = cast_to
self._client = client
+ self._options = options
self._decoder = client._make_sse_decoder()
self._iterator = self.__stream__()
@@ -54,12 +57,12 @@ def __stream__(self) -> Iterator[_T]:
process_data = self._client._process_response_data
iterator = self._iter_events()
- for sse in iterator:
- yield process_data(data=sse.json(), cast_to=cast_to, response=response)
-
- # Ensure the entire stream is consumed
- for _sse in iterator:
- ...
+ try:
+ for sse in iterator:
+ yield process_data(data=sse.json(), cast_to=cast_to, response=response)
+ finally:
+ # Ensure the response is closed even if the consumer doesn't read all data
+ response.close()
def __enter__(self) -> Self:
return self
@@ -85,7 +88,7 @@ class AsyncStream(Generic[_T]):
"""Provides the core interface to iterate over an asynchronous stream response."""
response: httpx.Response
-
+ _options: Optional[FinalRequestOptions] = None
_decoder: SSEDecoder | SSEBytesDecoder
def __init__(
@@ -94,10 +97,12 @@ def __init__(
cast_to: type[_T],
response: httpx.Response,
client: AsyncMixedbread,
+ options: Optional[FinalRequestOptions] = None,
) -> None:
self.response = response
self._cast_to = cast_to
self._client = client
+ self._options = options
self._decoder = client._make_sse_decoder()
self._iterator = self.__stream__()
@@ -118,12 +123,12 @@ async def __stream__(self) -> AsyncIterator[_T]:
process_data = self._client._process_response_data
iterator = self._iter_events()
- async for sse in iterator:
- yield process_data(data=sse.json(), cast_to=cast_to, response=response)
-
- # Ensure the entire stream is consumed
- async for _sse in iterator:
- ...
+ try:
+ async for sse in iterator:
+ yield process_data(data=sse.json(), cast_to=cast_to, response=response)
+ finally:
+ # Ensure the response is closed even if the consumer doesn't read all data
+ await response.aclose()
async def __aenter__(self) -> Self:
return self
diff --git a/src/mixedbread/_types.py b/src/mixedbread/_types.py
index b1e781f8..fa2086c9 100644
--- a/src/mixedbread/_types.py
+++ b/src/mixedbread/_types.py
@@ -13,10 +13,23 @@
Mapping,
TypeVar,
Callable,
+ Iterable,
+ Iterator,
Optional,
Sequence,
+ AsyncIterable,
+)
+from typing_extensions import (
+ Set,
+ Literal,
+ Protocol,
+ TypeAlias,
+ TypedDict,
+ SupportsIndex,
+ overload,
+ override,
+ runtime_checkable,
)
-from typing_extensions import Set, Literal, Protocol, TypeAlias, TypedDict, override, runtime_checkable
import httpx
import pydantic
@@ -45,6 +58,13 @@
else:
Base64FileInput = Union[IO[bytes], PathLike]
FileContent = Union[IO[bytes], bytes, PathLike] # PathLike is not subscriptable in Python 3.8.
+
+
+# Used for sending raw binary data / streaming data in request bodies
+# e.g. for file uploads without multipart encoding
+BinaryTypes = Union[bytes, bytearray, IO[bytes], Iterable[bytes]]
+AsyncBinaryTypes = Union[bytes, bytearray, IO[bytes], AsyncIterable[bytes]]
+
FileTypes = Union[
# file (or bytes)
FileContent,
@@ -100,23 +120,27 @@ class RequestOptions(TypedDict, total=False):
params: Query
extra_json: AnyMapping
idempotency_key: str
+ follow_redirects: bool
# Sentinel class used until PEP 0661 is accepted
class NotGiven:
"""
- A sentinel singleton class used to distinguish omitted keyword arguments
- from those passed in with the value None (which may have different behavior).
+ For parameters with a meaningful None value, we need to distinguish between
+ the user explicitly passing None, and the user not passing the parameter at
+ all.
+
+ User code shouldn't need to use not_given directly.
For example:
```py
- def get(timeout: Union[int, NotGiven, None] = NotGiven()) -> Response: ...
+ def create(timeout: Timeout | None | NotGiven = not_given): ...
- get(timeout=1) # 1s timeout
- get(timeout=None) # No timeout
- get() # Default timeout behavior, which may not be statically known at the method definition.
+ create(timeout=1) # 1s timeout
+ create(timeout=None) # No timeout
+ create() # Default timeout behavior
```
"""
@@ -128,13 +152,14 @@ def __repr__(self) -> str:
return "NOT_GIVEN"
-NotGivenOr = Union[_T, NotGiven]
+not_given = NotGiven()
+# for backwards compatibility:
NOT_GIVEN = NotGiven()
class Omit:
- """In certain situations you need to be able to represent a case where a default value has
- to be explicitly removed and `None` is not an appropriate substitute, for example:
+ """
+ To explicitly omit something from being sent in a request, use `omit`.
```py
# as the default `Content-Type` header is `application/json` that will be sent
@@ -144,8 +169,8 @@ class Omit:
# to look something like: 'multipart/form-data; boundary=0d8382fcf5f8c3be01ca2e11002d2983'
client.post(..., headers={"Content-Type": "multipart/form-data"})
- # instead you can remove the default `application/json` header by passing Omit
- client.post(..., headers={"Content-Type": Omit()})
+ # instead you can remove the default `application/json` header by passing omit
+ client.post(..., headers={"Content-Type": omit})
```
"""
@@ -153,6 +178,9 @@ def __bool__(self) -> Literal[False]:
return False
+omit = Omit()
+
+
@runtime_checkable
class ModelBuilderProtocol(Protocol):
@classmethod
@@ -215,3 +243,28 @@ 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
+ #
+ # Note: index() and count() methods are intentionally omitted to allow pyright to properly
+ # infer TypedDict types when dict literals are used in lists assigned to SequenceNotStr.
+ 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 __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..dc64e29a 100644
--- a/src/mixedbread/_utils/__init__.py
+++ b/src/mixedbread/_utils/__init__.py
@@ -10,7 +10,6 @@
lru_cache as lru_cache,
is_mapping as is_mapping,
is_tuple_t as is_tuple_t,
- parse_date as parse_date,
is_iterable as is_iterable,
is_sequence as is_sequence,
coerce_float as coerce_float,
@@ -23,7 +22,6 @@
coerce_boolean as coerce_boolean,
coerce_integer as coerce_integer,
file_from_path as file_from_path,
- parse_datetime as parse_datetime,
strip_not_given as strip_not_given,
deepcopy_minimal as deepcopy_minimal,
get_async_library as get_async_library,
@@ -32,12 +30,20 @@
maybe_coerce_boolean as maybe_coerce_boolean,
maybe_coerce_integer as maybe_coerce_integer,
)
+from ._compat import (
+ get_args as get_args,
+ is_union as is_union,
+ get_origin as get_origin,
+ is_typeddict as is_typeddict,
+ is_literal_type as is_literal_type,
+)
from ._typing import (
is_list_type as is_list_type,
is_union_type as is_union_type,
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,
@@ -55,3 +61,4 @@
function_has_argument as function_has_argument,
assert_signatures_in_sync as assert_signatures_in_sync,
)
+from ._datetime_parse import parse_date as parse_date, parse_datetime as parse_datetime
diff --git a/src/mixedbread/_utils/_compat.py b/src/mixedbread/_utils/_compat.py
new file mode 100644
index 00000000..2c70b299
--- /dev/null
+++ b/src/mixedbread/_utils/_compat.py
@@ -0,0 +1,45 @@
+from __future__ import annotations
+
+import sys
+import typing_extensions
+from typing import Any, Type, Union, Literal, Optional
+from datetime import date, datetime
+from typing_extensions import get_args as _get_args, get_origin as _get_origin
+
+from .._types import StrBytesIntFloat
+from ._datetime_parse import parse_date as _parse_date, parse_datetime as _parse_datetime
+
+_LITERAL_TYPES = {Literal, typing_extensions.Literal}
+
+
+def get_args(tp: type[Any]) -> tuple[Any, ...]:
+ return _get_args(tp)
+
+
+def get_origin(tp: type[Any]) -> type[Any] | None:
+ return _get_origin(tp)
+
+
+def is_union(tp: Optional[Type[Any]]) -> bool:
+ if sys.version_info < (3, 10):
+ return tp is Union # type: ignore[comparison-overlap]
+ else:
+ import types
+
+ return tp is Union or tp is types.UnionType # type: ignore[comparison-overlap]
+
+
+def is_typeddict(tp: Type[Any]) -> bool:
+ return typing_extensions.is_typeddict(tp)
+
+
+def is_literal_type(tp: Type[Any]) -> bool:
+ return get_origin(tp) in _LITERAL_TYPES
+
+
+def parse_date(value: Union[date, StrBytesIntFloat]) -> date:
+ return _parse_date(value)
+
+
+def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime:
+ return _parse_datetime(value)
diff --git a/src/mixedbread/_utils/_datetime_parse.py b/src/mixedbread/_utils/_datetime_parse.py
new file mode 100644
index 00000000..7cb9d9e6
--- /dev/null
+++ b/src/mixedbread/_utils/_datetime_parse.py
@@ -0,0 +1,136 @@
+"""
+This file contains code from https://github.com/pydantic/pydantic/blob/main/pydantic/v1/datetime_parse.py
+without the Pydantic v1 specific errors.
+"""
+
+from __future__ import annotations
+
+import re
+from typing import Dict, Union, Optional
+from datetime import date, datetime, timezone, timedelta
+
+from .._types import StrBytesIntFloat
+
+date_expr = r"(?P\d{4})-(?P\d{1,2})-(?P\d{1,2})"
+time_expr = (
+ r"(?P\d{1,2}):(?P\d{1,2})"
+ r"(?::(?P\d{1,2})(?:\.(?P\d{1,6})\d{0,6})?)?"
+ r"(?PZ|[+-]\d{2}(?::?\d{2})?)?$"
+)
+
+date_re = re.compile(f"{date_expr}$")
+datetime_re = re.compile(f"{date_expr}[T ]{time_expr}")
+
+
+EPOCH = datetime(1970, 1, 1)
+# if greater than this, the number is in ms, if less than or equal it's in seconds
+# (in seconds this is 11th October 2603, in ms it's 20th August 1970)
+MS_WATERSHED = int(2e10)
+# slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9
+MAX_NUMBER = int(3e20)
+
+
+def _get_numeric(value: StrBytesIntFloat, native_expected_type: str) -> Union[None, int, float]:
+ if isinstance(value, (int, float)):
+ return value
+ try:
+ return float(value)
+ except ValueError:
+ return None
+ except TypeError:
+ raise TypeError(f"invalid type; expected {native_expected_type}, string, bytes, int or float") from None
+
+
+def _from_unix_seconds(seconds: Union[int, float]) -> datetime:
+ if seconds > MAX_NUMBER:
+ return datetime.max
+ elif seconds < -MAX_NUMBER:
+ return datetime.min
+
+ while abs(seconds) > MS_WATERSHED:
+ seconds /= 1000
+ dt = EPOCH + timedelta(seconds=seconds)
+ return dt.replace(tzinfo=timezone.utc)
+
+
+def _parse_timezone(value: Optional[str]) -> Union[None, int, timezone]:
+ if value == "Z":
+ return timezone.utc
+ elif value is not None:
+ offset_mins = int(value[-2:]) if len(value) > 3 else 0
+ offset = 60 * int(value[1:3]) + offset_mins
+ if value[0] == "-":
+ offset = -offset
+ return timezone(timedelta(minutes=offset))
+ else:
+ return None
+
+
+def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime:
+ """
+ Parse a datetime/int/float/string and return a datetime.datetime.
+
+ This function supports time zone offsets. When the input contains one,
+ the output uses a timezone with a fixed offset from UTC.
+
+ Raise ValueError if the input is well formatted but not a valid datetime.
+ Raise ValueError if the input isn't well formatted.
+ """
+ if isinstance(value, datetime):
+ return value
+
+ number = _get_numeric(value, "datetime")
+ if number is not None:
+ return _from_unix_seconds(number)
+
+ if isinstance(value, bytes):
+ value = value.decode()
+
+ assert not isinstance(value, (float, int))
+
+ match = datetime_re.match(value)
+ if match is None:
+ raise ValueError("invalid datetime format")
+
+ kw = match.groupdict()
+ if kw["microsecond"]:
+ kw["microsecond"] = kw["microsecond"].ljust(6, "0")
+
+ tzinfo = _parse_timezone(kw.pop("tzinfo"))
+ kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None}
+ kw_["tzinfo"] = tzinfo
+
+ return datetime(**kw_) # type: ignore
+
+
+def parse_date(value: Union[date, StrBytesIntFloat]) -> date:
+ """
+ Parse a date/int/float/string and return a datetime.date.
+
+ Raise ValueError if the input is well formatted but not a valid date.
+ Raise ValueError if the input isn't well formatted.
+ """
+ if isinstance(value, date):
+ if isinstance(value, datetime):
+ return value.date()
+ else:
+ return value
+
+ number = _get_numeric(value, "date")
+ if number is not None:
+ return _from_unix_seconds(number).date()
+
+ if isinstance(value, bytes):
+ value = value.decode()
+
+ assert not isinstance(value, (float, int))
+ match = date_re.match(value)
+ if match is None:
+ raise ValueError("invalid date format")
+
+ kw = {k: int(v) for k, v in match.groupdict().items()}
+
+ try:
+ return date(**kw)
+ except ValueError:
+ raise ValueError("invalid date format") from None
diff --git a/src/mixedbread/_utils/_json.py b/src/mixedbread/_utils/_json.py
new file mode 100644
index 00000000..60584214
--- /dev/null
+++ b/src/mixedbread/_utils/_json.py
@@ -0,0 +1,35 @@
+import json
+from typing import Any
+from datetime import datetime
+from typing_extensions import override
+
+import pydantic
+
+from .._compat import model_dump
+
+
+def openapi_dumps(obj: Any) -> bytes:
+ """
+ Serialize an object to UTF-8 encoded JSON bytes.
+
+ Extends the standard json.dumps with support for additional types
+ commonly used in the SDK, such as `datetime`, `pydantic.BaseModel`, etc.
+ """
+ return json.dumps(
+ obj,
+ cls=_CustomEncoder,
+ # Uses the same defaults as httpx's JSON serialization
+ ensure_ascii=False,
+ separators=(",", ":"),
+ allow_nan=False,
+ ).encode()
+
+
+class _CustomEncoder(json.JSONEncoder):
+ @override
+ def default(self, o: Any) -> Any:
+ if isinstance(o, datetime):
+ return o.isoformat()
+ if isinstance(o, pydantic.BaseModel):
+ return model_dump(o, exclude_unset=True, mode="json", by_alias=True)
+ return super().default(o)
diff --git a/src/mixedbread/_utils/_proxy.py b/src/mixedbread/_utils/_proxy.py
index ffd883e9..0f239a33 100644
--- a/src/mixedbread/_utils/_proxy.py
+++ b/src/mixedbread/_utils/_proxy.py
@@ -46,7 +46,10 @@ def __dir__(self) -> Iterable[str]:
@property # type: ignore
@override
def __class__(self) -> type: # pyright: ignore
- proxied = self.__get_proxied__()
+ try:
+ proxied = self.__get_proxied__()
+ except Exception:
+ return type(self)
if issubclass(type(proxied), LazyProxy):
return type(proxied)
return proxied.__class__
diff --git a/src/mixedbread/_utils/_resources_proxy.py b/src/mixedbread/_utils/_resources_proxy.py
new file mode 100644
index 00000000..ad97bb05
--- /dev/null
+++ b/src/mixedbread/_utils/_resources_proxy.py
@@ -0,0 +1,24 @@
+from __future__ import annotations
+
+from typing import Any
+from typing_extensions import override
+
+from ._proxy import LazyProxy
+
+
+class ResourcesProxy(LazyProxy[Any]):
+ """A proxy for the `mixedbread.resources` module.
+
+ This is used so that we can lazily import `mixedbread.resources` only when
+ needed *and* so that users can just import `mixedbread` and reference `mixedbread.resources`
+ """
+
+ @override
+ def __load__(self) -> Any:
+ import importlib
+
+ mod = importlib.import_module("mixedbread.resources")
+ return mod
+
+
+resources = ResourcesProxy().__as_proxied__()
diff --git a/src/mixedbread/_utils/_sync.py b/src/mixedbread/_utils/_sync.py
index 8b3aaf2b..f6027c18 100644
--- a/src/mixedbread/_utils/_sync.py
+++ b/src/mixedbread/_utils/_sync.py
@@ -1,47 +1,34 @@
from __future__ import annotations
-import sys
import asyncio
import functools
-import contextvars
-from typing import Any, TypeVar, Callable, Awaitable
+from typing import TypeVar, Callable, Awaitable
from typing_extensions import ParamSpec
+import anyio
+import sniffio
+import anyio.to_thread
+
T_Retval = TypeVar("T_Retval")
T_ParamSpec = ParamSpec("T_ParamSpec")
-if sys.version_info >= (3, 9):
- to_thread = asyncio.to_thread
-else:
- # backport of https://docs.python.org/3/library/asyncio-task.html#asyncio.to_thread
- # for Python 3.8 support
- async def to_thread(
- func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs
- ) -> Any:
- """Asynchronously run function *func* in a separate thread.
-
- Any *args and **kwargs supplied for this function are directly passed
- to *func*. Also, the current :class:`contextvars.Context` is propagated,
- allowing context variables from the main thread to be accessed in the
- separate thread.
+async def to_thread(
+ func: Callable[T_ParamSpec, T_Retval], /, *args: T_ParamSpec.args, **kwargs: T_ParamSpec.kwargs
+) -> T_Retval:
+ if sniffio.current_async_library() == "asyncio":
+ return await asyncio.to_thread(func, *args, **kwargs)
- Returns a coroutine that can be awaited to get the eventual result of *func*.
- """
- loop = asyncio.events.get_running_loop()
- ctx = contextvars.copy_context()
- func_call = functools.partial(ctx.run, func, *args, **kwargs)
- return await loop.run_in_executor(None, func_call)
+ return await anyio.to_thread.run_sync(
+ functools.partial(func, *args, **kwargs),
+ )
# inspired by `asyncer`, https://github.com/tiangolo/asyncer
def asyncify(function: Callable[T_ParamSpec, T_Retval]) -> Callable[T_ParamSpec, Awaitable[T_Retval]]:
"""
Take a blocking function and create an async one that receives the same
- positional and keyword arguments. For python version 3.9 and above, it uses
- asyncio.to_thread to run the function in a separate thread. For python version
- 3.8, it uses locally defined copy of the asyncio.to_thread function which was
- introduced in python 3.9.
+ positional and keyword arguments.
Usage:
diff --git a/src/mixedbread/_utils/_transform.py b/src/mixedbread/_utils/_transform.py
index a6b62cad..52075492 100644
--- a/src/mixedbread/_utils/_transform.py
+++ b/src/mixedbread/_utils/_transform.py
@@ -5,27 +5,31 @@
import pathlib
from typing import Any, Mapping, TypeVar, cast
from datetime import date, datetime
-from typing_extensions import Literal, get_args, override, get_type_hints
+from typing_extensions import Literal, get_args, override, get_type_hints as _get_type_hints
import anyio
import pydantic
from ._utils import (
is_list,
+ is_given,
+ lru_cache,
is_mapping,
is_iterable,
+ is_sequence,
)
from .._files import is_base64_file_input
+from ._compat import get_origin, is_typeddict
from ._typing import (
is_list_type,
is_union_type,
extract_type_arg,
is_iterable_type,
is_required_type,
+ is_sequence_type,
is_annotated_type,
strip_annotated_type,
)
-from .._compat import model_dump, is_typeddict
_T = TypeVar("_T")
@@ -108,6 +112,7 @@ class Params(TypedDict, total=False):
return cast(_T, transformed)
+@lru_cache(maxsize=8096)
def _get_annotated_type(type_: type) -> type | None:
"""If the given type is an `Annotated` type then it is returned, if not `None` is returned.
@@ -126,7 +131,7 @@ def _get_annotated_type(type_: type) -> type | None:
def _maybe_transform_key(key: str, type_: type) -> str:
"""Transform the given `data` based on the annotations provided in `type_`.
- Note: this function only looks at `Annotated` types that contain `PropertInfo` metadata.
+ Note: this function only looks at `Annotated` types that contain `PropertyInfo` metadata.
"""
annotated_type = _get_annotated_type(type_)
if annotated_type is None:
@@ -142,6 +147,10 @@ def _maybe_transform_key(key: str, type_: type) -> str:
return key
+def _no_transform_needed(annotation: type) -> bool:
+ return annotation == float or annotation == int
+
+
def _transform_recursive(
data: object,
*,
@@ -160,18 +169,27 @@ def _transform_recursive(
Defaults to the same value as the `annotation` argument.
"""
+ from .._compat import model_dump
+
if inner_type is None:
inner_type = annotation
stripped_type = strip_annotated_type(inner_type)
+ origin = get_origin(stripped_type) or stripped_type
if is_typeddict(stripped_type) and is_mapping(data):
return _transform_typeddict(data, stripped_type)
+ if origin == dict and is_mapping(data):
+ items_type = get_args(stripped_type)[1]
+ return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()}
+
if (
# List[T]
(is_list_type(stripped_type) and is_list(data))
# Iterable[T]
or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str))
+ # Sequence[T]
+ or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str))
):
# dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually
# intended as an iterable, so we don't transform it.
@@ -179,6 +197,15 @@ def _transform_recursive(
return cast(object, data)
inner_type = extract_type_arg(stripped_type, 0)
+ if _no_transform_needed(inner_type):
+ # for some types there is no need to transform anything, so we can get a small
+ # perf boost from skipping that work.
+ #
+ # but we still need to convert to a list to ensure the data is json-serializable
+ if is_list(data):
+ return data
+ return list(data)
+
return [_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data]
if is_union_type(stripped_type):
@@ -240,6 +267,11 @@ def _transform_typeddict(
result: dict[str, object] = {}
annotations = get_type_hints(expected_type, include_extras=True)
for key, value in data.items():
+ if not is_given(value):
+ # we don't need to include omitted values here as they'll
+ # be stripped out before the request is sent anyway
+ continue
+
type_ = annotations.get(key)
if type_ is None:
# we do not have a type annotation for this field, leave it as is
@@ -303,18 +335,27 @@ async def _async_transform_recursive(
Defaults to the same value as the `annotation` argument.
"""
+ from .._compat import model_dump
+
if inner_type is None:
inner_type = annotation
stripped_type = strip_annotated_type(inner_type)
+ origin = get_origin(stripped_type) or stripped_type
if is_typeddict(stripped_type) and is_mapping(data):
return await _async_transform_typeddict(data, stripped_type)
+ if origin == dict and is_mapping(data):
+ items_type = get_args(stripped_type)[1]
+ return {key: _transform_recursive(value, annotation=items_type) for key, value in data.items()}
+
if (
# List[T]
(is_list_type(stripped_type) and is_list(data))
# Iterable[T]
or (is_iterable_type(stripped_type) and is_iterable(data) and not isinstance(data, str))
+ # Sequence[T]
+ or (is_sequence_type(stripped_type) and is_sequence(data) and not isinstance(data, str))
):
# dicts are technically iterable, but it is an iterable on the keys of the dict and is not usually
# intended as an iterable, so we don't transform it.
@@ -322,6 +363,15 @@ async def _async_transform_recursive(
return cast(object, data)
inner_type = extract_type_arg(stripped_type, 0)
+ if _no_transform_needed(inner_type):
+ # for some types there is no need to transform anything, so we can get a small
+ # perf boost from skipping that work.
+ #
+ # but we still need to convert to a list to ensure the data is json-serializable
+ if is_list(data):
+ return data
+ return list(data)
+
return [await _async_transform_recursive(d, annotation=annotation, inner_type=inner_type) for d in data]
if is_union_type(stripped_type):
@@ -383,6 +433,11 @@ async def _async_transform_typeddict(
result: dict[str, object] = {}
annotations = get_type_hints(expected_type, include_extras=True)
for key, value in data.items():
+ if not is_given(value):
+ # we don't need to include omitted values here as they'll
+ # be stripped out before the request is sent anyway
+ continue
+
type_ = annotations.get(key)
if type_ is None:
# we do not have a type annotation for this field, leave it as is
@@ -390,3 +445,13 @@ async def _async_transform_typeddict(
else:
result[_maybe_transform_key(key, type_)] = await _async_transform_recursive(value, annotation=type_)
return result
+
+
+@lru_cache(maxsize=8096)
+def get_type_hints(
+ obj: Any,
+ globalns: dict[str, Any] | None = None,
+ localns: Mapping[str, Any] | None = None,
+ include_extras: bool = False,
+) -> dict[str, Any]:
+ return _get_type_hints(obj, globalns=globalns, localns=localns, include_extras=include_extras)
diff --git a/src/mixedbread/_utils/_typing.py b/src/mixedbread/_utils/_typing.py
index 278749b1..193109f3 100644
--- a/src/mixedbread/_utils/_typing.py
+++ b/src/mixedbread/_utils/_typing.py
@@ -13,8 +13,9 @@
get_origin,
)
+from ._utils import lru_cache
from .._types import InheritsGeneric
-from .._compat import is_union as _is_union
+from ._compat import is_union as _is_union
def is_annotated_type(typ: type) -> bool:
@@ -25,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
@@ -66,6 +72,7 @@ def is_type_alias_type(tp: Any, /) -> TypeIs[typing_extensions.TypeAliasType]:
# Extracts T from Annotated[T, ...] or from Required[Annotated[T, ...]]
+@lru_cache(maxsize=8096)
def strip_annotated_type(typ: type) -> type:
if is_required_type(typ) or is_annotated_type(typ):
return strip_annotated_type(cast(type, get_args(typ)[0]))
@@ -108,7 +115,7 @@ class MyResponse(Foo[_T]):
```
"""
cls = cast(object, get_origin(typ) or typ)
- if cls in generic_bases:
+ if cls in generic_bases: # pyright: ignore[reportUnnecessaryContains]
# we're given the class directly
return extract_type_arg(typ, index)
diff --git a/src/mixedbread/_utils/_utils.py b/src/mixedbread/_utils/_utils.py
index e5811bba..eec7f4a1 100644
--- a/src/mixedbread/_utils/_utils.py
+++ b/src/mixedbread/_utils/_utils.py
@@ -21,8 +21,7 @@
import sniffio
-from .._types import NotGiven, FileTypes, NotGivenOr, HeadersLike
-from .._compat import parse_date as parse_date, parse_datetime as parse_datetime
+from .._types import Omit, NotGiven, FileTypes, HeadersLike
_T = TypeVar("_T")
_TupleT = TypeVar("_TupleT", bound=Tuple[object, ...])
@@ -64,7 +63,7 @@ def _extract_items(
try:
key = path[index]
except IndexError:
- if isinstance(obj, NotGiven):
+ if not is_given(obj):
# no value was provided - we can safely ignore
return []
@@ -72,8 +71,16 @@ def _extract_items(
from .._files import assert_is_file_content
# We have exhausted the path, return the entry we found.
- assert_is_file_content(obj, key=flattened_key)
assert flattened_key is not None
+
+ if is_list(obj):
+ files: list[tuple[str, FileTypes]] = []
+ for entry in obj:
+ assert_is_file_content(entry, key=flattened_key + "[]" if flattened_key else "")
+ files.append((flattened_key + "[]", cast(FileTypes, entry)))
+ return files
+
+ assert_is_file_content(obj, key=flattened_key)
return [(flattened_key, cast(FileTypes, obj))]
index += 1
@@ -119,14 +126,14 @@ def _extract_items(
return []
-def is_given(obj: NotGivenOr[_T]) -> TypeGuard[_T]:
- return not isinstance(obj, NotGiven)
+def is_given(obj: _T | NotGiven | Omit) -> TypeGuard[_T]:
+ return not isinstance(obj, NotGiven) and not isinstance(obj, Omit)
# Type safe methods for narrowing types with TypeVars.
# The default narrowing for isinstance(obj, dict) is dict[unknown, unknown],
# however this cause Pyright to rightfully report errors. As we know we don't
-# care about the contained types we can safely use `object` in it's place.
+# care about the contained types we can safely use `object` in its place.
#
# There are two separate functions defined, `is_*` and `is_*_t` for different use cases.
# `is_*` is for when you're dealing with an unknown input
diff --git a/src/mixedbread/_version.py b/src/mixedbread/_version.py
index c367ca09..a6b0880b 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.11"
+__version__ = "1.0.0" # x-release-please-version
diff --git a/src/mixedbread/pagination.py b/src/mixedbread/pagination.py
new file mode 100644
index 00000000..a983df1e
--- /dev/null
+++ b/src/mixedbread/pagination.py
@@ -0,0 +1,194 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Generic, TypeVar, Optional
+from typing_extensions import override
+
+from ._models import BaseModel
+from ._base_client import BasePage, PageInfo, BaseSyncPage, BaseAsyncPage
+
+__all__ = [
+ "LimitOffsetPagination",
+ "SyncLimitOffset",
+ "AsyncLimitOffset",
+ "CursorPagination",
+ "SyncCursor",
+ "AsyncCursor",
+]
+
+_T = TypeVar("_T")
+
+
+class LimitOffsetPagination(BaseModel):
+ total: Optional[int] = None
+
+ offset: Optional[int] = None
+
+
+class SyncLimitOffset(BaseSyncPage[_T], BasePage[_T], Generic[_T]):
+ data: List[_T]
+ pagination: Optional[LimitOffsetPagination] = None
+
+ @override
+ def _get_page_items(self) -> List[_T]:
+ data = self.data
+ if not data:
+ return []
+ return data
+
+ @override
+ def next_page_info(self) -> Optional[PageInfo]:
+ offset = None
+ if self.pagination is not None:
+ if self.pagination.offset is not None:
+ offset = self.pagination.offset
+ if offset is None:
+ return None # type: ignore[unreachable]
+
+ length = len(self._get_page_items())
+ current_count = offset + length
+
+ total = None
+ if self.pagination is not None:
+ if self.pagination.total is not None:
+ total = self.pagination.total
+ if total is None:
+ return None
+
+ if current_count < total:
+ return PageInfo(params={"offset": current_count})
+
+ return None
+
+
+class AsyncLimitOffset(BaseAsyncPage[_T], BasePage[_T], Generic[_T]):
+ data: List[_T]
+ pagination: Optional[LimitOffsetPagination] = None
+
+ @override
+ def _get_page_items(self) -> List[_T]:
+ data = self.data
+ if not data:
+ return []
+ return data
+
+ @override
+ def next_page_info(self) -> Optional[PageInfo]:
+ offset = None
+ if self.pagination is not None:
+ if self.pagination.offset is not None:
+ offset = self.pagination.offset
+ if offset is None:
+ return None # type: ignore[unreachable]
+
+ length = len(self._get_page_items())
+ current_count = offset + length
+
+ total = None
+ if self.pagination is not None:
+ if self.pagination.total is not None:
+ total = self.pagination.total
+ if total is None:
+ return None
+
+ if current_count < total:
+ return PageInfo(params={"offset": current_count})
+
+ return None
+
+
+class CursorPagination(BaseModel):
+ first_cursor: Optional[str] = None
+
+ last_cursor: Optional[str] = None
+
+ has_more: Optional[bool] = None
+
+ total: Optional[int] = None
+
+
+class SyncCursor(BaseSyncPage[_T], BasePage[_T], Generic[_T]):
+ data: List[_T]
+ pagination: Optional[CursorPagination] = None
+
+ @override
+ def _get_page_items(self) -> List[_T]:
+ data = self.data
+ if not data:
+ return []
+ return data
+
+ @override
+ def has_next_page(self) -> bool:
+ has_more = None
+ if self.pagination is not None:
+ if self.pagination.has_more is not None:
+ has_more = self.pagination.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
+ @override
+ def next_page_info(self) -> Optional[PageInfo]:
+ if self._options.params.get("before"):
+ first_cursor = None
+ if self.pagination is not None:
+ if self.pagination.first_cursor is not None:
+ first_cursor = self.pagination.first_cursor
+ if not first_cursor:
+ return None
+
+ return PageInfo(params={"before": first_cursor})
+
+ last_cursor = None
+ if self.pagination is not None:
+ if self.pagination.last_cursor is not None:
+ last_cursor = self.pagination.last_cursor
+ if not last_cursor:
+ return None
+
+ return PageInfo(params={"after": last_cursor})
+
+
+class AsyncCursor(BaseAsyncPage[_T], BasePage[_T], Generic[_T]):
+ data: List[_T]
+ pagination: Optional[CursorPagination] = None
+
+ @override
+ def _get_page_items(self) -> List[_T]:
+ data = self.data
+ if not data:
+ return []
+ return data
+
+ @override
+ def has_next_page(self) -> bool:
+ has_more = None
+ if self.pagination is not None:
+ if self.pagination.has_more is not None:
+ has_more = self.pagination.has_more
+ if has_more is not None and has_more is False:
+ return False
+
+ return super().has_next_page()
+
+ @override
+ def next_page_info(self) -> Optional[PageInfo]:
+ if self._options.params.get("before"):
+ first_cursor = None
+ if self.pagination is not None:
+ if self.pagination.first_cursor is not None:
+ first_cursor = self.pagination.first_cursor
+ if not first_cursor:
+ return None
+
+ return PageInfo(params={"before": first_cursor})
+
+ last_cursor = None
+ if self.pagination is not None:
+ if self.pagination.last_cursor is not None:
+ last_cursor = self.pagination.last_cursor
+ if not last_cursor:
+ return None
+
+ return PageInfo(params={"after": last_cursor})
diff --git a/src/mixedbread/resources/__init__.py b/src/mixedbread/resources/__init__.py
index 3dda4b1c..a9073d39 100644
--- a/src/mixedbread/resources/__init__.py
+++ b/src/mixedbread/resources/__init__.py
@@ -1,5 +1,13 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+from .chat import (
+ ChatResource,
+ AsyncChatResource,
+ ChatResourceWithRawResponse,
+ AsyncChatResourceWithRawResponse,
+ ChatResourceWithStreamingResponse,
+ AsyncChatResourceWithStreamingResponse,
+)
from .files import (
FilesResource,
AsyncFilesResource,
@@ -8,6 +16,14 @@
FilesResourceWithStreamingResponse,
AsyncFilesResourceWithStreamingResponse,
)
+from .stores import (
+ StoresResource,
+ AsyncStoresResource,
+ StoresResourceWithRawResponse,
+ AsyncStoresResourceWithRawResponse,
+ StoresResourceWithStreamingResponse,
+ AsyncStoresResourceWithStreamingResponse,
+)
from .parsing import (
ParsingResource,
AsyncParsingResource,
@@ -16,13 +32,13 @@
ParsingResourceWithStreamingResponse,
AsyncParsingResourceWithStreamingResponse,
)
-from .reranking import (
- RerankingResource,
- AsyncRerankingResource,
- RerankingResourceWithRawResponse,
- AsyncRerankingResourceWithRawResponse,
- RerankingResourceWithStreamingResponse,
- AsyncRerankingResourceWithStreamingResponse,
+from .api_keys import (
+ APIKeysResource,
+ AsyncAPIKeysResource,
+ APIKeysResourceWithRawResponse,
+ AsyncAPIKeysResourceWithRawResponse,
+ APIKeysResourceWithStreamingResponse,
+ AsyncAPIKeysResourceWithStreamingResponse,
)
from .embeddings import (
EmbeddingsResource,
@@ -32,14 +48,6 @@
EmbeddingsResourceWithStreamingResponse,
AsyncEmbeddingsResourceWithStreamingResponse,
)
-from .completions import (
- CompletionsResource,
- AsyncCompletionsResource,
- CompletionsResourceWithRawResponse,
- AsyncCompletionsResourceWithRawResponse,
- CompletionsResourceWithStreamingResponse,
- AsyncCompletionsResourceWithStreamingResponse,
-)
from .extractions import (
ExtractionsResource,
AsyncExtractionsResource,
@@ -48,54 +56,34 @@
ExtractionsResourceWithStreamingResponse,
AsyncExtractionsResourceWithStreamingResponse,
)
-from .service_info import (
- ServiceInfoResource,
- AsyncServiceInfoResource,
- ServiceInfoResourceWithRawResponse,
- AsyncServiceInfoResourceWithRawResponse,
- ServiceInfoResourceWithStreamingResponse,
- AsyncServiceInfoResourceWithStreamingResponse,
-)
-from .vector_stores import (
- VectorStoresResource,
- AsyncVectorStoresResource,
- VectorStoresResourceWithRawResponse,
- AsyncVectorStoresResourceWithRawResponse,
- VectorStoresResourceWithStreamingResponse,
- AsyncVectorStoresResourceWithStreamingResponse,
+from .data_sources import (
+ DataSourcesResource,
+ AsyncDataSourcesResource,
+ DataSourcesResourceWithRawResponse,
+ AsyncDataSourcesResourceWithRawResponse,
+ DataSourcesResourceWithStreamingResponse,
+ AsyncDataSourcesResourceWithStreamingResponse,
)
__all__ = [
- "ServiceInfoResource",
- "AsyncServiceInfoResource",
- "ServiceInfoResourceWithRawResponse",
- "AsyncServiceInfoResourceWithRawResponse",
- "ServiceInfoResourceWithStreamingResponse",
- "AsyncServiceInfoResourceWithStreamingResponse",
- "FilesResource",
- "AsyncFilesResource",
- "FilesResourceWithRawResponse",
- "AsyncFilesResourceWithRawResponse",
- "FilesResourceWithStreamingResponse",
- "AsyncFilesResourceWithStreamingResponse",
- "CompletionsResource",
- "AsyncCompletionsResource",
- "CompletionsResourceWithRawResponse",
- "AsyncCompletionsResourceWithRawResponse",
- "CompletionsResourceWithStreamingResponse",
- "AsyncCompletionsResourceWithStreamingResponse",
- "VectorStoresResource",
- "AsyncVectorStoresResource",
- "VectorStoresResourceWithRawResponse",
- "AsyncVectorStoresResourceWithRawResponse",
- "VectorStoresResourceWithStreamingResponse",
- "AsyncVectorStoresResourceWithStreamingResponse",
+ "StoresResource",
+ "AsyncStoresResource",
+ "StoresResourceWithRawResponse",
+ "AsyncStoresResourceWithRawResponse",
+ "StoresResourceWithStreamingResponse",
+ "AsyncStoresResourceWithStreamingResponse",
"ParsingResource",
"AsyncParsingResource",
"ParsingResourceWithRawResponse",
"AsyncParsingResourceWithRawResponse",
"ParsingResourceWithStreamingResponse",
"AsyncParsingResourceWithStreamingResponse",
+ "FilesResource",
+ "AsyncFilesResource",
+ "FilesResourceWithRawResponse",
+ "AsyncFilesResourceWithRawResponse",
+ "FilesResourceWithStreamingResponse",
+ "AsyncFilesResourceWithStreamingResponse",
"ExtractionsResource",
"AsyncExtractionsResource",
"ExtractionsResourceWithRawResponse",
@@ -108,10 +96,22 @@
"AsyncEmbeddingsResourceWithRawResponse",
"EmbeddingsResourceWithStreamingResponse",
"AsyncEmbeddingsResourceWithStreamingResponse",
- "RerankingResource",
- "AsyncRerankingResource",
- "RerankingResourceWithRawResponse",
- "AsyncRerankingResourceWithRawResponse",
- "RerankingResourceWithStreamingResponse",
- "AsyncRerankingResourceWithStreamingResponse",
+ "DataSourcesResource",
+ "AsyncDataSourcesResource",
+ "DataSourcesResourceWithRawResponse",
+ "AsyncDataSourcesResourceWithRawResponse",
+ "DataSourcesResourceWithStreamingResponse",
+ "AsyncDataSourcesResourceWithStreamingResponse",
+ "APIKeysResource",
+ "AsyncAPIKeysResource",
+ "APIKeysResourceWithRawResponse",
+ "AsyncAPIKeysResourceWithRawResponse",
+ "APIKeysResourceWithStreamingResponse",
+ "AsyncAPIKeysResourceWithStreamingResponse",
+ "ChatResource",
+ "AsyncChatResource",
+ "ChatResourceWithRawResponse",
+ "AsyncChatResourceWithRawResponse",
+ "ChatResourceWithStreamingResponse",
+ "AsyncChatResourceWithStreamingResponse",
]
diff --git a/src/mixedbread/resources/api_keys.py b/src/mixedbread/resources/api_keys.py
new file mode 100644
index 00000000..66408456
--- /dev/null
+++ b/src/mixedbread/resources/api_keys.py
@@ -0,0 +1,691 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable, Optional
+from datetime import datetime
+
+import httpx
+
+from ..types import api_key_list_params, api_key_create_params
+from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from .._utils import maybe_transform, async_maybe_transform
+from .._compat import cached_property
+from .._resource import SyncAPIResource, AsyncAPIResource
+from .._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..pagination import SyncLimitOffset, AsyncLimitOffset
+from .._base_client import AsyncPaginator, make_request_options
+from ..types.api_key import APIKey
+from ..types.api_key_created import APIKeyCreated
+from ..types.api_key_delete_response import APIKeyDeleteResponse
+
+__all__ = ["APIKeysResource", "AsyncAPIKeysResource"]
+
+
+class APIKeysResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> APIKeysResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
+ """
+ return APIKeysResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> APIKeysResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
+ """
+ return APIKeysResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ name: str | Omit = omit,
+ scope: Optional[Iterable[api_key_create_params.Scope]] | Omit = omit,
+ expires_at: Union[str, datetime, None] | Omit = omit,
+ # 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,
+ ) -> APIKeyCreated:
+ """
+ Create a new API key.
+
+ Args: params: The parameters for creating the API key.
+
+ Returns: ApiKeyCreated: The response containing the details of the created API
+ key.
+
+ Args:
+ name: A name/description for the API key
+
+ scope: The scope of the API key
+
+ expires_at: Optional expiration datetime
+
+ 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
+ """
+ return self._post(
+ "/v1/api-keys",
+ body=maybe_transform(
+ {
+ "name": name,
+ "scope": scope,
+ "expires_at": expires_at,
+ },
+ api_key_create_params.APIKeyCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=APIKeyCreated,
+ )
+
+ def retrieve(
+ self,
+ api_key_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,
+ ) -> APIKey:
+ """
+ Retrieve details of a specific API key by its ID.
+
+ Args: api_key_id: The ID of the API key to retrieve.
+
+ Returns: ApiKey: The response containing the API key details.
+
+ Args:
+ api_key_id: The ID of the API key to retrieve
+
+ 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 api_key_id:
+ raise ValueError(f"Expected a non-empty value for `api_key_id` but received {api_key_id!r}")
+ return self._get(
+ f"/v1/api-keys/{api_key_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=APIKey,
+ )
+
+ def list(
+ self,
+ *,
+ limit: int | Omit = omit,
+ offset: int | Omit = omit,
+ # 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,
+ ) -> SyncLimitOffset[APIKey]:
+ """
+ List all API keys for the authenticated user.
+
+ Args: pagination: The pagination options
+
+ Returns: A list of API keys belonging to the user.
+
+ Args:
+ limit: Maximum number of items to return per page
+
+ offset: Offset of the first item to return
+
+ 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
+ """
+ return self._get_api_list(
+ "/v1/api-keys",
+ page=SyncLimitOffset[APIKey],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "limit": limit,
+ "offset": offset,
+ },
+ api_key_list_params.APIKeyListParams,
+ ),
+ ),
+ model=APIKey,
+ )
+
+ def delete(
+ self,
+ api_key_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,
+ ) -> APIKeyDeleteResponse:
+ """
+ Delete a specific API key by its ID.
+
+ Args: api_key_id: The ID of the API key to delete.
+
+ Returns: ApiKeyDeleted: The response containing the details of the deleted API
+ key.
+
+ Args:
+ api_key_id: The ID of the API key to delete
+
+ 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 api_key_id:
+ raise ValueError(f"Expected a non-empty value for `api_key_id` but received {api_key_id!r}")
+ return self._delete(
+ f"/v1/api-keys/{api_key_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=APIKeyDeleteResponse,
+ )
+
+ def reroll(
+ self,
+ api_key_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,
+ ) -> APIKeyCreated:
+ """
+ Reroll the secret for a specific API key by its ID.
+
+ This generates a new secret key, invalidating the old one.
+
+ Args: api_key_id: The ID of the API key to reroll.
+
+ Returns: ApiKeyCreated: The response containing the API key details with the new
+ secret key.
+
+ Args:
+ api_key_id: The ID of the API key to reroll
+
+ 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 api_key_id:
+ raise ValueError(f"Expected a non-empty value for `api_key_id` but received {api_key_id!r}")
+ return self._post(
+ f"/v1/api-keys/{api_key_id}/reroll",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=APIKeyCreated,
+ )
+
+ def revoke(
+ self,
+ api_key_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,
+ ) -> APIKey:
+ """
+ Revoke a specific API key by its ID.
+
+ Args: api_key_id: The ID of the API key to revoke.
+
+ Returns: ApiKey: The response containing the details of the revoked API key.
+
+ Args:
+ api_key_id: The ID of the API key to revoke
+
+ 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 api_key_id:
+ raise ValueError(f"Expected a non-empty value for `api_key_id` but received {api_key_id!r}")
+ return self._post(
+ f"/v1/api-keys/{api_key_id}/revoke",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=APIKey,
+ )
+
+
+class AsyncAPIKeysResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncAPIKeysResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncAPIKeysResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncAPIKeysResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
+ """
+ return AsyncAPIKeysResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ name: str | Omit = omit,
+ scope: Optional[Iterable[api_key_create_params.Scope]] | Omit = omit,
+ expires_at: Union[str, datetime, None] | Omit = omit,
+ # 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,
+ ) -> APIKeyCreated:
+ """
+ Create a new API key.
+
+ Args: params: The parameters for creating the API key.
+
+ Returns: ApiKeyCreated: The response containing the details of the created API
+ key.
+
+ Args:
+ name: A name/description for the API key
+
+ scope: The scope of the API key
+
+ expires_at: Optional expiration datetime
+
+ 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
+ """
+ return await self._post(
+ "/v1/api-keys",
+ body=await async_maybe_transform(
+ {
+ "name": name,
+ "scope": scope,
+ "expires_at": expires_at,
+ },
+ api_key_create_params.APIKeyCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=APIKeyCreated,
+ )
+
+ async def retrieve(
+ self,
+ api_key_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,
+ ) -> APIKey:
+ """
+ Retrieve details of a specific API key by its ID.
+
+ Args: api_key_id: The ID of the API key to retrieve.
+
+ Returns: ApiKey: The response containing the API key details.
+
+ Args:
+ api_key_id: The ID of the API key to retrieve
+
+ 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 api_key_id:
+ raise ValueError(f"Expected a non-empty value for `api_key_id` but received {api_key_id!r}")
+ return await self._get(
+ f"/v1/api-keys/{api_key_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=APIKey,
+ )
+
+ def list(
+ self,
+ *,
+ limit: int | Omit = omit,
+ offset: int | Omit = omit,
+ # 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,
+ ) -> AsyncPaginator[APIKey, AsyncLimitOffset[APIKey]]:
+ """
+ List all API keys for the authenticated user.
+
+ Args: pagination: The pagination options
+
+ Returns: A list of API keys belonging to the user.
+
+ Args:
+ limit: Maximum number of items to return per page
+
+ offset: Offset of the first item to return
+
+ 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
+ """
+ return self._get_api_list(
+ "/v1/api-keys",
+ page=AsyncLimitOffset[APIKey],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "limit": limit,
+ "offset": offset,
+ },
+ api_key_list_params.APIKeyListParams,
+ ),
+ ),
+ model=APIKey,
+ )
+
+ async def delete(
+ self,
+ api_key_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,
+ ) -> APIKeyDeleteResponse:
+ """
+ Delete a specific API key by its ID.
+
+ Args: api_key_id: The ID of the API key to delete.
+
+ Returns: ApiKeyDeleted: The response containing the details of the deleted API
+ key.
+
+ Args:
+ api_key_id: The ID of the API key to delete
+
+ 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 api_key_id:
+ raise ValueError(f"Expected a non-empty value for `api_key_id` but received {api_key_id!r}")
+ return await self._delete(
+ f"/v1/api-keys/{api_key_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=APIKeyDeleteResponse,
+ )
+
+ async def reroll(
+ self,
+ api_key_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,
+ ) -> APIKeyCreated:
+ """
+ Reroll the secret for a specific API key by its ID.
+
+ This generates a new secret key, invalidating the old one.
+
+ Args: api_key_id: The ID of the API key to reroll.
+
+ Returns: ApiKeyCreated: The response containing the API key details with the new
+ secret key.
+
+ Args:
+ api_key_id: The ID of the API key to reroll
+
+ 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 api_key_id:
+ raise ValueError(f"Expected a non-empty value for `api_key_id` but received {api_key_id!r}")
+ return await self._post(
+ f"/v1/api-keys/{api_key_id}/reroll",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=APIKeyCreated,
+ )
+
+ async def revoke(
+ self,
+ api_key_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,
+ ) -> APIKey:
+ """
+ Revoke a specific API key by its ID.
+
+ Args: api_key_id: The ID of the API key to revoke.
+
+ Returns: ApiKey: The response containing the details of the revoked API key.
+
+ Args:
+ api_key_id: The ID of the API key to revoke
+
+ 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 api_key_id:
+ raise ValueError(f"Expected a non-empty value for `api_key_id` but received {api_key_id!r}")
+ return await self._post(
+ f"/v1/api-keys/{api_key_id}/revoke",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=APIKey,
+ )
+
+
+class APIKeysResourceWithRawResponse:
+ def __init__(self, api_keys: APIKeysResource) -> None:
+ self._api_keys = api_keys
+
+ self.create = to_raw_response_wrapper(
+ api_keys.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ api_keys.retrieve,
+ )
+ self.list = to_raw_response_wrapper(
+ api_keys.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ api_keys.delete,
+ )
+ self.reroll = to_raw_response_wrapper(
+ api_keys.reroll,
+ )
+ self.revoke = to_raw_response_wrapper(
+ api_keys.revoke,
+ )
+
+
+class AsyncAPIKeysResourceWithRawResponse:
+ def __init__(self, api_keys: AsyncAPIKeysResource) -> None:
+ self._api_keys = api_keys
+
+ self.create = async_to_raw_response_wrapper(
+ api_keys.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ api_keys.retrieve,
+ )
+ self.list = async_to_raw_response_wrapper(
+ api_keys.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ api_keys.delete,
+ )
+ self.reroll = async_to_raw_response_wrapper(
+ api_keys.reroll,
+ )
+ self.revoke = async_to_raw_response_wrapper(
+ api_keys.revoke,
+ )
+
+
+class APIKeysResourceWithStreamingResponse:
+ def __init__(self, api_keys: APIKeysResource) -> None:
+ self._api_keys = api_keys
+
+ self.create = to_streamed_response_wrapper(
+ api_keys.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ api_keys.retrieve,
+ )
+ self.list = to_streamed_response_wrapper(
+ api_keys.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ api_keys.delete,
+ )
+ self.reroll = to_streamed_response_wrapper(
+ api_keys.reroll,
+ )
+ self.revoke = to_streamed_response_wrapper(
+ api_keys.revoke,
+ )
+
+
+class AsyncAPIKeysResourceWithStreamingResponse:
+ def __init__(self, api_keys: AsyncAPIKeysResource) -> None:
+ self._api_keys = api_keys
+
+ self.create = async_to_streamed_response_wrapper(
+ api_keys.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ api_keys.retrieve,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ api_keys.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ api_keys.delete,
+ )
+ self.reroll = async_to_streamed_response_wrapper(
+ api_keys.reroll,
+ )
+ self.revoke = async_to_streamed_response_wrapper(
+ api_keys.revoke,
+ )
diff --git a/src/mixedbread/resources/completions.py b/src/mixedbread/resources/chat.py
similarity index 61%
rename from src/mixedbread/resources/completions.py
rename to src/mixedbread/resources/chat.py
index 0132a58f..a5788f8f 100644
--- a/src/mixedbread/resources/completions.py
+++ b/src/mixedbread/resources/chat.py
@@ -4,7 +4,7 @@
import httpx
-from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from .._types import Body, Query, Headers, NotGiven, not_given
from .._compat import cached_property
from .._resource import SyncAPIResource, AsyncAPIResource
from .._response import (
@@ -15,30 +15,30 @@
)
from .._base_client import make_request_options
-__all__ = ["CompletionsResource", "AsyncCompletionsResource"]
+__all__ = ["ChatResource", "AsyncChatResource"]
-class CompletionsResource(SyncAPIResource):
+class ChatResource(SyncAPIResource):
@cached_property
- def with_raw_response(self) -> CompletionsResourceWithRawResponse:
+ def with_raw_response(self) -> ChatResourceWithRawResponse:
"""
- This property can be used as a prefix for any HTTP method call to return the
+ This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
"""
- return CompletionsResourceWithRawResponse(self)
+ return ChatResourceWithRawResponse(self)
@cached_property
- def with_streaming_response(self) -> CompletionsResourceWithStreamingResponse:
+ def with_streaming_response(self) -> ChatResourceWithStreamingResponse:
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
"""
- return CompletionsResourceWithStreamingResponse(self)
+ return ChatResourceWithStreamingResponse(self)
- def create(
+ def create_completion(
self,
*,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -46,7 +46,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> object:
"""
Create a chat completion using the provided parameters.
@@ -73,27 +73,27 @@ def create(
)
-class AsyncCompletionsResource(AsyncAPIResource):
+class AsyncChatResource(AsyncAPIResource):
@cached_property
- def with_raw_response(self) -> AsyncCompletionsResourceWithRawResponse:
+ def with_raw_response(self) -> AsyncChatResourceWithRawResponse:
"""
- This property can be used as a prefix for any HTTP method call to return the
+ This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
"""
- return AsyncCompletionsResourceWithRawResponse(self)
+ return AsyncChatResourceWithRawResponse(self)
@cached_property
- def with_streaming_response(self) -> AsyncCompletionsResourceWithStreamingResponse:
+ def with_streaming_response(self) -> AsyncChatResourceWithStreamingResponse:
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
"""
- return AsyncCompletionsResourceWithStreamingResponse(self)
+ return AsyncChatResourceWithStreamingResponse(self)
- async def create(
+ async def create_completion(
self,
*,
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -101,7 +101,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> object:
"""
Create a chat completion using the provided parameters.
@@ -128,37 +128,37 @@ async def create(
)
-class CompletionsResourceWithRawResponse:
- def __init__(self, completions: CompletionsResource) -> None:
- self._completions = completions
+class ChatResourceWithRawResponse:
+ def __init__(self, chat: ChatResource) -> None:
+ self._chat = chat
- self.create = to_raw_response_wrapper(
- completions.create,
+ self.create_completion = to_raw_response_wrapper(
+ chat.create_completion,
)
-class AsyncCompletionsResourceWithRawResponse:
- def __init__(self, completions: AsyncCompletionsResource) -> None:
- self._completions = completions
+class AsyncChatResourceWithRawResponse:
+ def __init__(self, chat: AsyncChatResource) -> None:
+ self._chat = chat
- self.create = async_to_raw_response_wrapper(
- completions.create,
+ self.create_completion = async_to_raw_response_wrapper(
+ chat.create_completion,
)
-class CompletionsResourceWithStreamingResponse:
- def __init__(self, completions: CompletionsResource) -> None:
- self._completions = completions
+class ChatResourceWithStreamingResponse:
+ def __init__(self, chat: ChatResource) -> None:
+ self._chat = chat
- self.create = to_streamed_response_wrapper(
- completions.create,
+ self.create_completion = to_streamed_response_wrapper(
+ chat.create_completion,
)
-class AsyncCompletionsResourceWithStreamingResponse:
- def __init__(self, completions: AsyncCompletionsResource) -> None:
- self._completions = completions
+class AsyncChatResourceWithStreamingResponse:
+ def __init__(self, chat: AsyncChatResource) -> None:
+ self._chat = chat
- self.create = async_to_streamed_response_wrapper(
- completions.create,
+ self.create_completion = async_to_streamed_response_wrapper(
+ chat.create_completion,
)
diff --git a/src/mixedbread/resources/data_sources/__init__.py b/src/mixedbread/resources/data_sources/__init__.py
new file mode 100644
index 00000000..6e04f6ec
--- /dev/null
+++ b/src/mixedbread/resources/data_sources/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .connectors import (
+ ConnectorsResource,
+ AsyncConnectorsResource,
+ ConnectorsResourceWithRawResponse,
+ AsyncConnectorsResourceWithRawResponse,
+ ConnectorsResourceWithStreamingResponse,
+ AsyncConnectorsResourceWithStreamingResponse,
+)
+from .data_sources import (
+ DataSourcesResource,
+ AsyncDataSourcesResource,
+ DataSourcesResourceWithRawResponse,
+ AsyncDataSourcesResourceWithRawResponse,
+ DataSourcesResourceWithStreamingResponse,
+ AsyncDataSourcesResourceWithStreamingResponse,
+)
+
+__all__ = [
+ "ConnectorsResource",
+ "AsyncConnectorsResource",
+ "ConnectorsResourceWithRawResponse",
+ "AsyncConnectorsResourceWithRawResponse",
+ "ConnectorsResourceWithStreamingResponse",
+ "AsyncConnectorsResourceWithStreamingResponse",
+ "DataSourcesResource",
+ "AsyncDataSourcesResource",
+ "DataSourcesResourceWithRawResponse",
+ "AsyncDataSourcesResourceWithRawResponse",
+ "DataSourcesResourceWithStreamingResponse",
+ "AsyncDataSourcesResourceWithStreamingResponse",
+]
diff --git a/src/mixedbread/resources/data_sources/connectors.py b/src/mixedbread/resources/data_sources/connectors.py
new file mode 100644
index 00000000..bd913e76
--- /dev/null
+++ b/src/mixedbread/resources/data_sources/connectors.py
@@ -0,0 +1,749 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Union, Optional
+
+import httpx
+
+from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ..._utils import maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...pagination import SyncCursor, AsyncCursor
+from ..._base_client import AsyncPaginator, make_request_options
+from ...types.data_sources import connector_list_params, connector_create_params, connector_update_params
+from ...types.data_sources.data_source_connector import DataSourceConnector
+from ...types.data_sources.connector_delete_response import ConnectorDeleteResponse
+
+__all__ = ["ConnectorsResource", "AsyncConnectorsResource"]
+
+
+class ConnectorsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> ConnectorsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
+ """
+ return ConnectorsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> ConnectorsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
+ """
+ return ConnectorsResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ data_source_id: str,
+ *,
+ store_id: str,
+ name: str | Omit = omit,
+ trigger_sync: bool | Omit = omit,
+ metadata: object | Omit = omit,
+ polling_interval: Union[int, str, None] | Omit = omit,
+ # 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,
+ ) -> DataSourceConnector:
+ """
+ Create a new connector.
+
+ Args: data_source_id: The ID of the data source to create a connector for.
+ params: The connector to create.
+
+ Returns: The created connector.
+
+ Args:
+ data_source_id: The ID of the data source to create a connector for
+
+ store_id: The ID of the store
+
+ name: The name of the connector
+
+ trigger_sync: Whether the connector should be synced after creation
+
+ metadata: The metadata of the connector
+
+ polling_interval: Polling interval for the connector. Defaults to 30 minutes if not specified. Can
+ be provided as:
+
+ - int: Number of seconds (e.g., 1800 for 30 minutes)
+ - str: Duration string (e.g., '30m', '1h', '2d') or ISO 8601 format (e.g.,
+ 'PT30M', 'P1D') Valid range: 15 seconds to 30 days
+
+ 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 data_source_id:
+ raise ValueError(f"Expected a non-empty value for `data_source_id` but received {data_source_id!r}")
+ return self._post(
+ f"/v1/data_sources/{data_source_id}/connectors",
+ body=maybe_transform(
+ {
+ "store_id": store_id,
+ "name": name,
+ "trigger_sync": trigger_sync,
+ "metadata": metadata,
+ "polling_interval": polling_interval,
+ },
+ connector_create_params.ConnectorCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DataSourceConnector,
+ )
+
+ def retrieve(
+ self,
+ connector_id: str,
+ *,
+ data_source_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,
+ ) -> DataSourceConnector:
+ """
+ Get a connector by ID.
+
+ Args: data_source_id: The ID of the data source to get a connector for.
+ connector_id: The ID of the connector to get.
+
+ Returns: The connector.
+
+ Args:
+ data_source_id: The ID of the data source to get a connector for
+
+ connector_id: The ID of the connector to get
+
+ 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 data_source_id:
+ raise ValueError(f"Expected a non-empty value for `data_source_id` but received {data_source_id!r}")
+ if not connector_id:
+ raise ValueError(f"Expected a non-empty value for `connector_id` but received {connector_id!r}")
+ return self._get(
+ f"/v1/data_sources/{data_source_id}/connectors/{connector_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DataSourceConnector,
+ )
+
+ def update(
+ self,
+ connector_id: str,
+ *,
+ data_source_id: str,
+ name: Optional[str] | Omit = omit,
+ metadata: Optional[Dict[str, object]] | Omit = omit,
+ trigger_sync: Optional[bool] | Omit = omit,
+ polling_interval: Union[int, str, None] | Omit = omit,
+ # 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,
+ ) -> DataSourceConnector:
+ """
+ Update a connector.
+
+ Args: data_source_id: The ID of the data source to update a connector for.
+ connector_id: The ID of the connector to update. params: The connector to
+ update.
+
+ Returns: The updated connector.
+
+ Args:
+ data_source_id: The ID of the data source to update a connector for
+
+ connector_id: The ID of the connector to update
+
+ name: The name of the connector
+
+ metadata: The metadata of the connector
+
+ trigger_sync: Whether the connector should be synced after update
+
+ polling_interval: Polling interval for the connector. Defaults to 30 minutes if not specified. Can
+ be provided as:
+
+ - int: Number of seconds (e.g., 1800 for 30 minutes)
+ - str: Duration string (e.g., '30m', '1h', '2d') or ISO 8601 format (e.g.,
+ 'PT30M', 'P1D') Valid range: 15 seconds to 30 days
+
+ 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 data_source_id:
+ raise ValueError(f"Expected a non-empty value for `data_source_id` but received {data_source_id!r}")
+ if not connector_id:
+ raise ValueError(f"Expected a non-empty value for `connector_id` but received {connector_id!r}")
+ return self._put(
+ f"/v1/data_sources/{data_source_id}/connectors/{connector_id}",
+ body=maybe_transform(
+ {
+ "name": name,
+ "metadata": metadata,
+ "trigger_sync": trigger_sync,
+ "polling_interval": polling_interval,
+ },
+ connector_update_params.ConnectorUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DataSourceConnector,
+ )
+
+ def list(
+ self,
+ data_source_id: str,
+ *,
+ limit: int | Omit = omit,
+ after: Optional[str] | Omit = omit,
+ before: Optional[str] | Omit = omit,
+ include_total: bool | Omit = omit,
+ # 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,
+ ) -> SyncCursor[DataSourceConnector]:
+ """
+ Get all connectors for a data source.
+
+ Args: data_source_id: The ID of the data source to get connectors for.
+ pagination: The pagination options.
+
+ Returns: The list of connectors.
+
+ Args:
+ data_source_id: The ID of the data source to get connectors for
+
+ 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)
+
+ 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 data_source_id:
+ raise ValueError(f"Expected a non-empty value for `data_source_id` but received {data_source_id!r}")
+ return self._get_api_list(
+ f"/v1/data_sources/{data_source_id}/connectors",
+ page=SyncCursor[DataSourceConnector],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "limit": limit,
+ "after": after,
+ "before": before,
+ "include_total": include_total,
+ },
+ connector_list_params.ConnectorListParams,
+ ),
+ ),
+ model=DataSourceConnector,
+ )
+
+ def delete(
+ self,
+ connector_id: str,
+ *,
+ data_source_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,
+ ) -> ConnectorDeleteResponse:
+ """
+ Delete a connector.
+
+ Args: data_source_id: The ID of the data source to delete a connector for.
+ connector_id: The ID of the connector to delete.
+
+ Returns: The deleted connector.
+
+ Args:
+ data_source_id: The ID of the data source to delete a connector for
+
+ connector_id: The ID of the connector to delete
+
+ 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 data_source_id:
+ raise ValueError(f"Expected a non-empty value for `data_source_id` but received {data_source_id!r}")
+ if not connector_id:
+ raise ValueError(f"Expected a non-empty value for `connector_id` but received {connector_id!r}")
+ return self._delete(
+ f"/v1/data_sources/{data_source_id}/connectors/{connector_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ConnectorDeleteResponse,
+ )
+
+
+class AsyncConnectorsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncConnectorsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncConnectorsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncConnectorsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
+ """
+ return AsyncConnectorsResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ data_source_id: str,
+ *,
+ store_id: str,
+ name: str | Omit = omit,
+ trigger_sync: bool | Omit = omit,
+ metadata: object | Omit = omit,
+ polling_interval: Union[int, str, None] | Omit = omit,
+ # 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,
+ ) -> DataSourceConnector:
+ """
+ Create a new connector.
+
+ Args: data_source_id: The ID of the data source to create a connector for.
+ params: The connector to create.
+
+ Returns: The created connector.
+
+ Args:
+ data_source_id: The ID of the data source to create a connector for
+
+ store_id: The ID of the store
+
+ name: The name of the connector
+
+ trigger_sync: Whether the connector should be synced after creation
+
+ metadata: The metadata of the connector
+
+ polling_interval: Polling interval for the connector. Defaults to 30 minutes if not specified. Can
+ be provided as:
+
+ - int: Number of seconds (e.g., 1800 for 30 minutes)
+ - str: Duration string (e.g., '30m', '1h', '2d') or ISO 8601 format (e.g.,
+ 'PT30M', 'P1D') Valid range: 15 seconds to 30 days
+
+ 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 data_source_id:
+ raise ValueError(f"Expected a non-empty value for `data_source_id` but received {data_source_id!r}")
+ return await self._post(
+ f"/v1/data_sources/{data_source_id}/connectors",
+ body=await async_maybe_transform(
+ {
+ "store_id": store_id,
+ "name": name,
+ "trigger_sync": trigger_sync,
+ "metadata": metadata,
+ "polling_interval": polling_interval,
+ },
+ connector_create_params.ConnectorCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DataSourceConnector,
+ )
+
+ async def retrieve(
+ self,
+ connector_id: str,
+ *,
+ data_source_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,
+ ) -> DataSourceConnector:
+ """
+ Get a connector by ID.
+
+ Args: data_source_id: The ID of the data source to get a connector for.
+ connector_id: The ID of the connector to get.
+
+ Returns: The connector.
+
+ Args:
+ data_source_id: The ID of the data source to get a connector for
+
+ connector_id: The ID of the connector to get
+
+ 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 data_source_id:
+ raise ValueError(f"Expected a non-empty value for `data_source_id` but received {data_source_id!r}")
+ if not connector_id:
+ raise ValueError(f"Expected a non-empty value for `connector_id` but received {connector_id!r}")
+ return await self._get(
+ f"/v1/data_sources/{data_source_id}/connectors/{connector_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DataSourceConnector,
+ )
+
+ async def update(
+ self,
+ connector_id: str,
+ *,
+ data_source_id: str,
+ name: Optional[str] | Omit = omit,
+ metadata: Optional[Dict[str, object]] | Omit = omit,
+ trigger_sync: Optional[bool] | Omit = omit,
+ polling_interval: Union[int, str, None] | Omit = omit,
+ # 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,
+ ) -> DataSourceConnector:
+ """
+ Update a connector.
+
+ Args: data_source_id: The ID of the data source to update a connector for.
+ connector_id: The ID of the connector to update. params: The connector to
+ update.
+
+ Returns: The updated connector.
+
+ Args:
+ data_source_id: The ID of the data source to update a connector for
+
+ connector_id: The ID of the connector to update
+
+ name: The name of the connector
+
+ metadata: The metadata of the connector
+
+ trigger_sync: Whether the connector should be synced after update
+
+ polling_interval: Polling interval for the connector. Defaults to 30 minutes if not specified. Can
+ be provided as:
+
+ - int: Number of seconds (e.g., 1800 for 30 minutes)
+ - str: Duration string (e.g., '30m', '1h', '2d') or ISO 8601 format (e.g.,
+ 'PT30M', 'P1D') Valid range: 15 seconds to 30 days
+
+ 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 data_source_id:
+ raise ValueError(f"Expected a non-empty value for `data_source_id` but received {data_source_id!r}")
+ if not connector_id:
+ raise ValueError(f"Expected a non-empty value for `connector_id` but received {connector_id!r}")
+ return await self._put(
+ f"/v1/data_sources/{data_source_id}/connectors/{connector_id}",
+ body=await async_maybe_transform(
+ {
+ "name": name,
+ "metadata": metadata,
+ "trigger_sync": trigger_sync,
+ "polling_interval": polling_interval,
+ },
+ connector_update_params.ConnectorUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DataSourceConnector,
+ )
+
+ def list(
+ self,
+ data_source_id: str,
+ *,
+ limit: int | Omit = omit,
+ after: Optional[str] | Omit = omit,
+ before: Optional[str] | Omit = omit,
+ include_total: bool | Omit = omit,
+ # 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,
+ ) -> AsyncPaginator[DataSourceConnector, AsyncCursor[DataSourceConnector]]:
+ """
+ Get all connectors for a data source.
+
+ Args: data_source_id: The ID of the data source to get connectors for.
+ pagination: The pagination options.
+
+ Returns: The list of connectors.
+
+ Args:
+ data_source_id: The ID of the data source to get connectors for
+
+ 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)
+
+ 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 data_source_id:
+ raise ValueError(f"Expected a non-empty value for `data_source_id` but received {data_source_id!r}")
+ return self._get_api_list(
+ f"/v1/data_sources/{data_source_id}/connectors",
+ page=AsyncCursor[DataSourceConnector],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "limit": limit,
+ "after": after,
+ "before": before,
+ "include_total": include_total,
+ },
+ connector_list_params.ConnectorListParams,
+ ),
+ ),
+ model=DataSourceConnector,
+ )
+
+ async def delete(
+ self,
+ connector_id: str,
+ *,
+ data_source_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,
+ ) -> ConnectorDeleteResponse:
+ """
+ Delete a connector.
+
+ Args: data_source_id: The ID of the data source to delete a connector for.
+ connector_id: The ID of the connector to delete.
+
+ Returns: The deleted connector.
+
+ Args:
+ data_source_id: The ID of the data source to delete a connector for
+
+ connector_id: The ID of the connector to delete
+
+ 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 data_source_id:
+ raise ValueError(f"Expected a non-empty value for `data_source_id` but received {data_source_id!r}")
+ if not connector_id:
+ raise ValueError(f"Expected a non-empty value for `connector_id` but received {connector_id!r}")
+ return await self._delete(
+ f"/v1/data_sources/{data_source_id}/connectors/{connector_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ConnectorDeleteResponse,
+ )
+
+
+class ConnectorsResourceWithRawResponse:
+ def __init__(self, connectors: ConnectorsResource) -> None:
+ self._connectors = connectors
+
+ self.create = to_raw_response_wrapper(
+ connectors.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ connectors.retrieve,
+ )
+ self.update = to_raw_response_wrapper(
+ connectors.update,
+ )
+ self.list = to_raw_response_wrapper(
+ connectors.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ connectors.delete,
+ )
+
+
+class AsyncConnectorsResourceWithRawResponse:
+ def __init__(self, connectors: AsyncConnectorsResource) -> None:
+ self._connectors = connectors
+
+ self.create = async_to_raw_response_wrapper(
+ connectors.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ connectors.retrieve,
+ )
+ self.update = async_to_raw_response_wrapper(
+ connectors.update,
+ )
+ self.list = async_to_raw_response_wrapper(
+ connectors.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ connectors.delete,
+ )
+
+
+class ConnectorsResourceWithStreamingResponse:
+ def __init__(self, connectors: ConnectorsResource) -> None:
+ self._connectors = connectors
+
+ self.create = to_streamed_response_wrapper(
+ connectors.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ connectors.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ connectors.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ connectors.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ connectors.delete,
+ )
+
+
+class AsyncConnectorsResourceWithStreamingResponse:
+ def __init__(self, connectors: AsyncConnectorsResource) -> None:
+ self._connectors = connectors
+
+ self.create = async_to_streamed_response_wrapper(
+ connectors.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ connectors.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ connectors.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ connectors.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ connectors.delete,
+ )
diff --git a/src/mixedbread/resources/data_sources/data_sources.py b/src/mixedbread/resources/data_sources/data_sources.py
new file mode 100644
index 00000000..185eaedb
--- /dev/null
+++ b/src/mixedbread/resources/data_sources/data_sources.py
@@ -0,0 +1,949 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, overload
+
+import httpx
+
+from ...types import Oauth2Params, data_source_list_params, data_source_create_params, data_source_update_params
+from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ..._utils import required_args, maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from .connectors import (
+ ConnectorsResource,
+ AsyncConnectorsResource,
+ ConnectorsResourceWithRawResponse,
+ AsyncConnectorsResourceWithRawResponse,
+ ConnectorsResourceWithStreamingResponse,
+ AsyncConnectorsResourceWithStreamingResponse,
+)
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...pagination import SyncCursor, AsyncCursor
+from ..._base_client import AsyncPaginator, make_request_options
+from ...types.data_source import DataSource
+from ...types.oauth2_params import Oauth2Params
+from ...types.data_source_delete_response import DataSourceDeleteResponse
+
+__all__ = ["DataSourcesResource", "AsyncDataSourcesResource"]
+
+
+class DataSourcesResource(SyncAPIResource):
+ @cached_property
+ def connectors(self) -> ConnectorsResource:
+ return ConnectorsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> DataSourcesResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
+ """
+ return DataSourcesResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> DataSourcesResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
+ """
+ return DataSourcesResourceWithStreamingResponse(self)
+
+ @overload
+ def create(
+ self,
+ *,
+ type: Literal["notion"] | Omit = omit,
+ name: str,
+ metadata: object | Omit = omit,
+ auth_params: Optional[data_source_create_params.NotionDataSourceAuthParams] | Omit = omit,
+ # 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,
+ ) -> DataSource:
+ """
+ Create a new data source.
+
+ Args: params: The data source to create.
+
+ Returns: The created data source.
+
+ Args:
+ type: The type of data source to create
+
+ name: The name of the data source
+
+ metadata: The metadata of the data source
+
+ auth_params: The authentication parameters of the data source. Notion supports OAuth2 and API
+ key.
+
+ 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
+ """
+ ...
+
+ @overload
+ def create(
+ self,
+ *,
+ type: Literal["linear"] | Omit = omit,
+ name: str,
+ metadata: object | Omit = omit,
+ auth_params: Optional[Oauth2Params] | Omit = omit,
+ # 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,
+ ) -> DataSource:
+ """
+ Create a new data source.
+
+ Args: params: The data source to create.
+
+ Returns: The created data source.
+
+ Args:
+ type: The type of data source to create
+
+ name: The name of the data source
+
+ metadata: The metadata of the data source
+
+ auth_params: Base class for OAuth2 create or update parameters.
+
+ 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
+ """
+ ...
+
+ @required_args(["name"])
+ def create(
+ self,
+ *,
+ type: Literal["notion"] | Literal["linear"] | Omit = omit,
+ name: str,
+ metadata: object | Omit = omit,
+ auth_params: Optional[data_source_create_params.NotionDataSourceAuthParams]
+ | Optional[Oauth2Params]
+ | Omit = omit,
+ # 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,
+ ) -> DataSource:
+ return self._post(
+ "/v1/data_sources/",
+ body=maybe_transform(
+ {
+ "type": type,
+ "name": name,
+ "metadata": metadata,
+ "auth_params": auth_params,
+ },
+ data_source_create_params.DataSourceCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DataSource,
+ )
+
+ def retrieve(
+ self,
+ data_source_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,
+ ) -> DataSource:
+ """
+ Get a data source by ID.
+
+ Args: data_source_id: The ID of the data source to fetch.
+
+ Returns: The data source.
+
+ Args:
+ data_source_id: The ID of the data source to fetch
+
+ 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 data_source_id:
+ raise ValueError(f"Expected a non-empty value for `data_source_id` but received {data_source_id!r}")
+ return self._get(
+ f"/v1/data_sources/{data_source_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DataSource,
+ )
+
+ @overload
+ def update(
+ self,
+ data_source_id: str,
+ *,
+ type: Literal["notion"] | Omit = omit,
+ name: str,
+ metadata: object | Omit = omit,
+ auth_params: Optional[data_source_update_params.NotionDataSourceAuthParams] | Omit = omit,
+ # 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,
+ ) -> DataSource:
+ """Update a data source.
+
+ Args: data_source_id: The ID of the data source to update.
+
+ params: The data
+ source to update.
+
+ Returns: The updated data source.
+
+ Args:
+ data_source_id: The ID of the data source to update
+
+ type: The type of data source to create
+
+ name: The name of the data source
+
+ metadata: The metadata of the data source
+
+ auth_params: The authentication parameters of the data source. Notion supports OAuth2 and API
+ key.
+
+ 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
+ """
+ ...
+
+ @overload
+ def update(
+ self,
+ data_source_id: str,
+ *,
+ type: Literal["linear"] | Omit = omit,
+ name: str,
+ metadata: object | Omit = omit,
+ auth_params: Optional[Oauth2Params] | Omit = omit,
+ # 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,
+ ) -> DataSource:
+ """Update a data source.
+
+ Args: data_source_id: The ID of the data source to update.
+
+ params: The data
+ source to update.
+
+ Returns: The updated data source.
+
+ Args:
+ data_source_id: The ID of the data source to update
+
+ type: The type of data source to create
+
+ name: The name of the data source
+
+ metadata: The metadata of the data source
+
+ auth_params: Base class for OAuth2 create or update parameters.
+
+ 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
+ """
+ ...
+
+ @required_args(["name"])
+ def update(
+ self,
+ data_source_id: str,
+ *,
+ type: Literal["notion"] | Literal["linear"] | Omit = omit,
+ name: str,
+ metadata: object | Omit = omit,
+ auth_params: Optional[data_source_update_params.NotionDataSourceAuthParams]
+ | Optional[Oauth2Params]
+ | Omit = omit,
+ # 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,
+ ) -> DataSource:
+ if not data_source_id:
+ raise ValueError(f"Expected a non-empty value for `data_source_id` but received {data_source_id!r}")
+ return self._put(
+ f"/v1/data_sources/{data_source_id}",
+ body=maybe_transform(
+ {
+ "type": type,
+ "name": name,
+ "metadata": metadata,
+ "auth_params": auth_params,
+ },
+ data_source_update_params.DataSourceUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DataSource,
+ )
+
+ def list(
+ self,
+ *,
+ limit: int | Omit = omit,
+ after: Optional[str] | Omit = omit,
+ before: Optional[str] | Omit = omit,
+ include_total: bool | Omit = omit,
+ # 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,
+ ) -> SyncCursor[DataSource]:
+ """
+ Get all data sources.
+
+ Returns: The list of data sources.
+
+ Args:
+ 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)
+
+ 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
+ """
+ return self._get_api_list(
+ "/v1/data_sources/",
+ page=SyncCursor[DataSource],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "limit": limit,
+ "after": after,
+ "before": before,
+ "include_total": include_total,
+ },
+ data_source_list_params.DataSourceListParams,
+ ),
+ ),
+ model=DataSource,
+ )
+
+ def delete(
+ self,
+ data_source_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,
+ ) -> DataSourceDeleteResponse:
+ """
+ Delete a data source.
+
+ Args: data_source_id: The ID of the data source to delete.
+
+ Args:
+ data_source_id: The ID of the data source to delete
+
+ 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 data_source_id:
+ raise ValueError(f"Expected a non-empty value for `data_source_id` but received {data_source_id!r}")
+ return self._delete(
+ f"/v1/data_sources/{data_source_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DataSourceDeleteResponse,
+ )
+
+
+class AsyncDataSourcesResource(AsyncAPIResource):
+ @cached_property
+ def connectors(self) -> AsyncConnectorsResource:
+ return AsyncConnectorsResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncDataSourcesResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncDataSourcesResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncDataSourcesResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
+ """
+ return AsyncDataSourcesResourceWithStreamingResponse(self)
+
+ @overload
+ async def create(
+ self,
+ *,
+ type: Literal["notion"] | Omit = omit,
+ name: str,
+ metadata: object | Omit = omit,
+ auth_params: Optional[data_source_create_params.NotionDataSourceAuthParams] | Omit = omit,
+ # 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,
+ ) -> DataSource:
+ """
+ Create a new data source.
+
+ Args: params: The data source to create.
+
+ Returns: The created data source.
+
+ Args:
+ type: The type of data source to create
+
+ name: The name of the data source
+
+ metadata: The metadata of the data source
+
+ auth_params: The authentication parameters of the data source. Notion supports OAuth2 and API
+ key.
+
+ 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
+ """
+ ...
+
+ @overload
+ async def create(
+ self,
+ *,
+ type: Literal["linear"] | Omit = omit,
+ name: str,
+ metadata: object | Omit = omit,
+ auth_params: Optional[Oauth2Params] | Omit = omit,
+ # 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,
+ ) -> DataSource:
+ """
+ Create a new data source.
+
+ Args: params: The data source to create.
+
+ Returns: The created data source.
+
+ Args:
+ type: The type of data source to create
+
+ name: The name of the data source
+
+ metadata: The metadata of the data source
+
+ auth_params: Base class for OAuth2 create or update parameters.
+
+ 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
+ """
+ ...
+
+ @required_args(["name"])
+ async def create(
+ self,
+ *,
+ type: Literal["notion"] | Literal["linear"] | Omit = omit,
+ name: str,
+ metadata: object | Omit = omit,
+ auth_params: Optional[data_source_create_params.NotionDataSourceAuthParams]
+ | Optional[Oauth2Params]
+ | Omit = omit,
+ # 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,
+ ) -> DataSource:
+ return await self._post(
+ "/v1/data_sources/",
+ body=await async_maybe_transform(
+ {
+ "type": type,
+ "name": name,
+ "metadata": metadata,
+ "auth_params": auth_params,
+ },
+ data_source_create_params.DataSourceCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DataSource,
+ )
+
+ async def retrieve(
+ self,
+ data_source_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,
+ ) -> DataSource:
+ """
+ Get a data source by ID.
+
+ Args: data_source_id: The ID of the data source to fetch.
+
+ Returns: The data source.
+
+ Args:
+ data_source_id: The ID of the data source to fetch
+
+ 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 data_source_id:
+ raise ValueError(f"Expected a non-empty value for `data_source_id` but received {data_source_id!r}")
+ return await self._get(
+ f"/v1/data_sources/{data_source_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DataSource,
+ )
+
+ @overload
+ async def update(
+ self,
+ data_source_id: str,
+ *,
+ type: Literal["notion"] | Omit = omit,
+ name: str,
+ metadata: object | Omit = omit,
+ auth_params: Optional[data_source_update_params.NotionDataSourceAuthParams] | Omit = omit,
+ # 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,
+ ) -> DataSource:
+ """Update a data source.
+
+ Args: data_source_id: The ID of the data source to update.
+
+ params: The data
+ source to update.
+
+ Returns: The updated data source.
+
+ Args:
+ data_source_id: The ID of the data source to update
+
+ type: The type of data source to create
+
+ name: The name of the data source
+
+ metadata: The metadata of the data source
+
+ auth_params: The authentication parameters of the data source. Notion supports OAuth2 and API
+ key.
+
+ 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
+ """
+ ...
+
+ @overload
+ async def update(
+ self,
+ data_source_id: str,
+ *,
+ type: Literal["linear"] | Omit = omit,
+ name: str,
+ metadata: object | Omit = omit,
+ auth_params: Optional[Oauth2Params] | Omit = omit,
+ # 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,
+ ) -> DataSource:
+ """Update a data source.
+
+ Args: data_source_id: The ID of the data source to update.
+
+ params: The data
+ source to update.
+
+ Returns: The updated data source.
+
+ Args:
+ data_source_id: The ID of the data source to update
+
+ type: The type of data source to create
+
+ name: The name of the data source
+
+ metadata: The metadata of the data source
+
+ auth_params: Base class for OAuth2 create or update parameters.
+
+ 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
+ """
+ ...
+
+ @required_args(["name"])
+ async def update(
+ self,
+ data_source_id: str,
+ *,
+ type: Literal["notion"] | Literal["linear"] | Omit = omit,
+ name: str,
+ metadata: object | Omit = omit,
+ auth_params: Optional[data_source_update_params.NotionDataSourceAuthParams]
+ | Optional[Oauth2Params]
+ | Omit = omit,
+ # 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,
+ ) -> DataSource:
+ if not data_source_id:
+ raise ValueError(f"Expected a non-empty value for `data_source_id` but received {data_source_id!r}")
+ return await self._put(
+ f"/v1/data_sources/{data_source_id}",
+ body=await async_maybe_transform(
+ {
+ "type": type,
+ "name": name,
+ "metadata": metadata,
+ "auth_params": auth_params,
+ },
+ data_source_update_params.DataSourceUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DataSource,
+ )
+
+ def list(
+ self,
+ *,
+ limit: int | Omit = omit,
+ after: Optional[str] | Omit = omit,
+ before: Optional[str] | Omit = omit,
+ include_total: bool | Omit = omit,
+ # 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,
+ ) -> AsyncPaginator[DataSource, AsyncCursor[DataSource]]:
+ """
+ Get all data sources.
+
+ Returns: The list of data sources.
+
+ Args:
+ 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)
+
+ 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
+ """
+ return self._get_api_list(
+ "/v1/data_sources/",
+ page=AsyncCursor[DataSource],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "limit": limit,
+ "after": after,
+ "before": before,
+ "include_total": include_total,
+ },
+ data_source_list_params.DataSourceListParams,
+ ),
+ ),
+ model=DataSource,
+ )
+
+ async def delete(
+ self,
+ data_source_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,
+ ) -> DataSourceDeleteResponse:
+ """
+ Delete a data source.
+
+ Args: data_source_id: The ID of the data source to delete.
+
+ Args:
+ data_source_id: The ID of the data source to delete
+
+ 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 data_source_id:
+ raise ValueError(f"Expected a non-empty value for `data_source_id` but received {data_source_id!r}")
+ return await self._delete(
+ f"/v1/data_sources/{data_source_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=DataSourceDeleteResponse,
+ )
+
+
+class DataSourcesResourceWithRawResponse:
+ def __init__(self, data_sources: DataSourcesResource) -> None:
+ self._data_sources = data_sources
+
+ self.create = to_raw_response_wrapper(
+ data_sources.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ data_sources.retrieve,
+ )
+ self.update = to_raw_response_wrapper(
+ data_sources.update,
+ )
+ self.list = to_raw_response_wrapper(
+ data_sources.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ data_sources.delete,
+ )
+
+ @cached_property
+ def connectors(self) -> ConnectorsResourceWithRawResponse:
+ return ConnectorsResourceWithRawResponse(self._data_sources.connectors)
+
+
+class AsyncDataSourcesResourceWithRawResponse:
+ def __init__(self, data_sources: AsyncDataSourcesResource) -> None:
+ self._data_sources = data_sources
+
+ self.create = async_to_raw_response_wrapper(
+ data_sources.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ data_sources.retrieve,
+ )
+ self.update = async_to_raw_response_wrapper(
+ data_sources.update,
+ )
+ self.list = async_to_raw_response_wrapper(
+ data_sources.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ data_sources.delete,
+ )
+
+ @cached_property
+ def connectors(self) -> AsyncConnectorsResourceWithRawResponse:
+ return AsyncConnectorsResourceWithRawResponse(self._data_sources.connectors)
+
+
+class DataSourcesResourceWithStreamingResponse:
+ def __init__(self, data_sources: DataSourcesResource) -> None:
+ self._data_sources = data_sources
+
+ self.create = to_streamed_response_wrapper(
+ data_sources.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ data_sources.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ data_sources.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ data_sources.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ data_sources.delete,
+ )
+
+ @cached_property
+ def connectors(self) -> ConnectorsResourceWithStreamingResponse:
+ return ConnectorsResourceWithStreamingResponse(self._data_sources.connectors)
+
+
+class AsyncDataSourcesResourceWithStreamingResponse:
+ def __init__(self, data_sources: AsyncDataSourcesResource) -> None:
+ self._data_sources = data_sources
+
+ self.create = async_to_streamed_response_wrapper(
+ data_sources.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ data_sources.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ data_sources.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ data_sources.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ data_sources.delete,
+ )
+
+ @cached_property
+ def connectors(self) -> AsyncConnectorsResourceWithStreamingResponse:
+ return AsyncConnectorsResourceWithStreamingResponse(self._data_sources.connectors)
diff --git a/src/mixedbread/resources/embeddings.py b/src/mixedbread/resources/embeddings.py
index fb15b5c6..d2c74634 100644
--- a/src/mixedbread/resources/embeddings.py
+++ b/src/mixedbread/resources/embeddings.py
@@ -3,16 +3,12 @@
from __future__ import annotations
from typing import List, Union, Optional
-from typing_extensions import Literal
import httpx
from ..types import embedding_create_params
-from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
-from .._utils import (
- maybe_transform,
- async_maybe_transform,
-)
+from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
+from .._utils import maybe_transform, async_maybe_transform
from .._compat import cached_property
from .._resource import SyncAPIResource, AsyncAPIResource
from .._response import (
@@ -22,6 +18,7 @@
async_to_streamed_response_wrapper,
)
from .._base_client import make_request_options
+from ..types.encoding_format import EncodingFormat
from ..types.embedding_create_response import EmbeddingCreateResponse
__all__ = ["EmbeddingsResource", "AsyncEmbeddingsResource"]
@@ -31,10 +28,10 @@ class EmbeddingsResource(SyncAPIResource):
@cached_property
def with_raw_response(self) -> EmbeddingsResourceWithRawResponse:
"""
- This property can be used as a prefix for any HTTP method call to return the
+ This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
"""
return EmbeddingsResourceWithRawResponse(self)
@@ -43,29 +40,25 @@ def with_streaming_response(self) -> EmbeddingsResourceWithStreamingResponse:
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
"""
return EmbeddingsResourceWithStreamingResponse(self)
def create(
self,
*,
- input: embedding_create_params.Input,
model: str,
- dimensions: Optional[int] | NotGiven = NOT_GIVEN,
- encoding_format: Union[
- Literal["float", "float16", "base64", "binary", "ubinary", "int8", "uint8"],
- List[Literal["float", "float16", "base64", "binary", "ubinary", "int8", "uint8"]],
- ]
- | NotGiven = NOT_GIVEN,
- normalized: bool | NotGiven = NOT_GIVEN,
- prompt: Optional[str] | NotGiven = NOT_GIVEN,
+ input: Union[str, SequenceNotStr[str]],
+ dimensions: Optional[int] | Omit = omit,
+ prompt: Optional[str] | Omit = omit,
+ normalized: bool | Omit = omit,
+ encoding_format: Union[EncodingFormat, List[EncodingFormat]] | Omit = omit,
# 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,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> EmbeddingCreateResponse:
"""
Create embeddings for text or images using the specified model, encoding format,
@@ -76,17 +69,18 @@ def create(
Returns: EmbeddingCreateResponse: The response containing the embeddings.
Args:
- input: The input to create embeddings for.
-
model: The model to use for creating embeddings.
+ input: The input to create embeddings for.
+
dimensions: The number of dimensions to use for the embeddings.
- encoding_format: The encoding format of the embeddings.
+ prompt: The prompt to use for the embedding creation.
normalized: Whether to normalize the embeddings.
- prompt: The prompt to use for the embedding creation.
+ encoding_format: The encoding format(s) of the embeddings. Can be a single format or a list of
+ formats.
extra_headers: Send extra headers
@@ -100,12 +94,12 @@ def create(
"/v1/embeddings",
body=maybe_transform(
{
- "input": input,
"model": model,
+ "input": input,
"dimensions": dimensions,
- "encoding_format": encoding_format,
- "normalized": normalized,
"prompt": prompt,
+ "normalized": normalized,
+ "encoding_format": encoding_format,
},
embedding_create_params.EmbeddingCreateParams,
),
@@ -120,10 +114,10 @@ class AsyncEmbeddingsResource(AsyncAPIResource):
@cached_property
def with_raw_response(self) -> AsyncEmbeddingsResourceWithRawResponse:
"""
- This property can be used as a prefix for any HTTP method call to return the
+ This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
"""
return AsyncEmbeddingsResourceWithRawResponse(self)
@@ -132,29 +126,25 @@ def with_streaming_response(self) -> AsyncEmbeddingsResourceWithStreamingRespons
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
"""
return AsyncEmbeddingsResourceWithStreamingResponse(self)
async def create(
self,
*,
- input: embedding_create_params.Input,
model: str,
- dimensions: Optional[int] | NotGiven = NOT_GIVEN,
- encoding_format: Union[
- Literal["float", "float16", "base64", "binary", "ubinary", "int8", "uint8"],
- List[Literal["float", "float16", "base64", "binary", "ubinary", "int8", "uint8"]],
- ]
- | NotGiven = NOT_GIVEN,
- normalized: bool | NotGiven = NOT_GIVEN,
- prompt: Optional[str] | NotGiven = NOT_GIVEN,
+ input: Union[str, SequenceNotStr[str]],
+ dimensions: Optional[int] | Omit = omit,
+ prompt: Optional[str] | Omit = omit,
+ normalized: bool | Omit = omit,
+ encoding_format: Union[EncodingFormat, List[EncodingFormat]] | Omit = omit,
# 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,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> EmbeddingCreateResponse:
"""
Create embeddings for text or images using the specified model, encoding format,
@@ -165,17 +155,18 @@ async def create(
Returns: EmbeddingCreateResponse: The response containing the embeddings.
Args:
- input: The input to create embeddings for.
-
model: The model to use for creating embeddings.
+ input: The input to create embeddings for.
+
dimensions: The number of dimensions to use for the embeddings.
- encoding_format: The encoding format of the embeddings.
+ prompt: The prompt to use for the embedding creation.
normalized: Whether to normalize the embeddings.
- prompt: The prompt to use for the embedding creation.
+ encoding_format: The encoding format(s) of the embeddings. Can be a single format or a list of
+ formats.
extra_headers: Send extra headers
@@ -189,12 +180,12 @@ async def create(
"/v1/embeddings",
body=await async_maybe_transform(
{
- "input": input,
"model": model,
+ "input": input,
"dimensions": dimensions,
- "encoding_format": encoding_format,
- "normalized": normalized,
"prompt": prompt,
+ "normalized": normalized,
+ "encoding_format": encoding_format,
},
embedding_create_params.EmbeddingCreateParams,
),
diff --git a/src/mixedbread/resources/extractions/content.py b/src/mixedbread/resources/extractions/content.py
index 9cefef45..b2111bb3 100644
--- a/src/mixedbread/resources/extractions/content.py
+++ b/src/mixedbread/resources/extractions/content.py
@@ -2,13 +2,12 @@
from __future__ import annotations
+from typing import Dict, Union, Iterable, Optional
+
import httpx
-from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
-from ..._utils import (
- maybe_transform,
- async_maybe_transform,
-)
+from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
+from ..._utils import maybe_transform, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -28,10 +27,10 @@ class ContentResource(SyncAPIResource):
@cached_property
def with_raw_response(self) -> ContentResourceWithRawResponse:
"""
- This property can be used as a prefix for any HTTP method call to return the
+ This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
"""
return ContentResourceWithRawResponse(self)
@@ -40,21 +39,22 @@ def with_streaming_response(self) -> ContentResourceWithStreamingResponse:
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
"""
return ContentResourceWithStreamingResponse(self)
def create(
self,
*,
- content: str,
- json_schema: object,
+ content: Union[str, SequenceNotStr[str], Iterable[content_create_params.ContentUnionMember2]],
+ json_schema: Dict[str, object],
+ instructions: Optional[str] | Omit = omit,
# 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,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> ExtractionResult:
"""
Extract content from a string using the provided schema.
@@ -68,6 +68,8 @@ def create(
json_schema: The JSON schema to use for extraction
+ instructions: Additional instructions for the extraction
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -82,6 +84,7 @@ def create(
{
"content": content,
"json_schema": json_schema,
+ "instructions": instructions,
},
content_create_params.ContentCreateParams,
),
@@ -96,10 +99,10 @@ class AsyncContentResource(AsyncAPIResource):
@cached_property
def with_raw_response(self) -> AsyncContentResourceWithRawResponse:
"""
- This property can be used as a prefix for any HTTP method call to return the
+ This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
"""
return AsyncContentResourceWithRawResponse(self)
@@ -108,21 +111,22 @@ def with_streaming_response(self) -> AsyncContentResourceWithStreamingResponse:
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
"""
return AsyncContentResourceWithStreamingResponse(self)
async def create(
self,
*,
- content: str,
- json_schema: object,
+ content: Union[str, SequenceNotStr[str], Iterable[content_create_params.ContentUnionMember2]],
+ json_schema: Dict[str, object],
+ instructions: Optional[str] | Omit = omit,
# 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,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> ExtractionResult:
"""
Extract content from a string using the provided schema.
@@ -136,6 +140,8 @@ async def create(
json_schema: The JSON schema to use for extraction
+ instructions: Additional instructions for the extraction
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -150,6 +156,7 @@ async def create(
{
"content": content,
"json_schema": json_schema,
+ "instructions": instructions,
},
content_create_params.ContentCreateParams,
),
diff --git a/src/mixedbread/resources/extractions/extractions.py b/src/mixedbread/resources/extractions/extractions.py
index 1b6da3b1..eebb3e40 100644
--- a/src/mixedbread/resources/extractions/extractions.py
+++ b/src/mixedbread/resources/extractions/extractions.py
@@ -48,10 +48,10 @@ def content(self) -> ContentResource:
@cached_property
def with_raw_response(self) -> ExtractionsResourceWithRawResponse:
"""
- This property can be used as a prefix for any HTTP method call to return the
+ This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
"""
return ExtractionsResourceWithRawResponse(self)
@@ -60,7 +60,7 @@ def with_streaming_response(self) -> ExtractionsResourceWithStreamingResponse:
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
"""
return ExtractionsResourceWithStreamingResponse(self)
@@ -81,10 +81,10 @@ def content(self) -> AsyncContentResource:
@cached_property
def with_raw_response(self) -> AsyncExtractionsResourceWithRawResponse:
"""
- This property can be used as a prefix for any HTTP method call to return the
+ This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
"""
return AsyncExtractionsResourceWithRawResponse(self)
@@ -93,7 +93,7 @@ def with_streaming_response(self) -> AsyncExtractionsResourceWithStreamingRespon
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
"""
return AsyncExtractionsResourceWithStreamingResponse(self)
diff --git a/src/mixedbread/resources/extractions/jobs.py b/src/mixedbread/resources/extractions/jobs.py
index 8ac9016f..ae48bbf3 100644
--- a/src/mixedbread/resources/extractions/jobs.py
+++ b/src/mixedbread/resources/extractions/jobs.py
@@ -2,13 +2,12 @@
from __future__ import annotations
+from typing import Dict
+
import httpx
-from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
-from ..._utils import (
- maybe_transform,
- async_maybe_transform,
-)
+from ..._types import Body, Query, Headers, NotGiven, not_given
+from ..._utils import maybe_transform, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -28,10 +27,10 @@ class JobsResource(SyncAPIResource):
@cached_property
def with_raw_response(self) -> JobsResourceWithRawResponse:
"""
- This property can be used as a prefix for any HTTP method call to return the
+ This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
"""
return JobsResourceWithRawResponse(self)
@@ -40,7 +39,7 @@ def with_streaming_response(self) -> JobsResourceWithStreamingResponse:
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
"""
return JobsResourceWithStreamingResponse(self)
@@ -48,13 +47,13 @@ def create(
self,
*,
file_id: str,
- json_schema: object,
+ json_schema: Dict[str, object],
# 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,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> ExtractionJob:
"""
Start an extraction job for the provided file and schema.
@@ -100,7 +99,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> ExtractionJob:
"""
Get detailed information about a specific extraction job.
@@ -135,10 +134,10 @@ class AsyncJobsResource(AsyncAPIResource):
@cached_property
def with_raw_response(self) -> AsyncJobsResourceWithRawResponse:
"""
- This property can be used as a prefix for any HTTP method call to return the
+ This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
"""
return AsyncJobsResourceWithRawResponse(self)
@@ -147,7 +146,7 @@ def with_streaming_response(self) -> AsyncJobsResourceWithStreamingResponse:
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
"""
return AsyncJobsResourceWithStreamingResponse(self)
@@ -155,13 +154,13 @@ async def create(
self,
*,
file_id: str,
- json_schema: object,
+ json_schema: Dict[str, object],
# 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,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> ExtractionJob:
"""
Start an extraction job for the provided file and schema.
@@ -207,7 +206,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> ExtractionJob:
"""
Get detailed information about a specific extraction job.
diff --git a/src/mixedbread/resources/extractions/schema.py b/src/mixedbread/resources/extractions/schema.py
index c0e507e0..6eabe54e 100644
--- a/src/mixedbread/resources/extractions/schema.py
+++ b/src/mixedbread/resources/extractions/schema.py
@@ -2,13 +2,12 @@
from __future__ import annotations
+from typing import Dict
+
import httpx
-from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
-from ..._utils import (
- maybe_transform,
- async_maybe_transform,
-)
+from ..._types import Body, Query, Headers, NotGiven, not_given
+from ..._utils import maybe_transform, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -30,10 +29,10 @@ class SchemaResource(SyncAPIResource):
@cached_property
def with_raw_response(self) -> SchemaResourceWithRawResponse:
"""
- This property can be used as a prefix for any HTTP method call to return the
+ This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
"""
return SchemaResourceWithRawResponse(self)
@@ -42,7 +41,7 @@ def with_streaming_response(self) -> SchemaResourceWithStreamingResponse:
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
"""
return SchemaResourceWithStreamingResponse(self)
@@ -55,7 +54,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> CreatedJsonSchema:
"""
Create a schema with the provided parameters.
@@ -87,13 +86,13 @@ def create(
def enhance(
self,
*,
- json_schema: object,
+ json_schema: Dict[str, object],
# 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,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> EnhancedJsonSchema:
"""
Enhance a schema by enriching the descriptions to aid extraction.
@@ -125,13 +124,13 @@ def enhance(
def validate(
self,
*,
- json_schema: object,
+ json_schema: Dict[str, object],
# 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,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> ValidatedJsonSchema:
"""
Validate a schema.
@@ -165,10 +164,10 @@ class AsyncSchemaResource(AsyncAPIResource):
@cached_property
def with_raw_response(self) -> AsyncSchemaResourceWithRawResponse:
"""
- This property can be used as a prefix for any HTTP method call to return the
+ This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
"""
return AsyncSchemaResourceWithRawResponse(self)
@@ -177,7 +176,7 @@ def with_streaming_response(self) -> AsyncSchemaResourceWithStreamingResponse:
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
"""
return AsyncSchemaResourceWithStreamingResponse(self)
@@ -190,7 +189,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> CreatedJsonSchema:
"""
Create a schema with the provided parameters.
@@ -222,13 +221,13 @@ async def create(
async def enhance(
self,
*,
- json_schema: object,
+ json_schema: Dict[str, object],
# 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,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> EnhancedJsonSchema:
"""
Enhance a schema by enriching the descriptions to aid extraction.
@@ -260,13 +259,13 @@ async def enhance(
async def validate(
self,
*,
- json_schema: object,
+ json_schema: Dict[str, object],
# 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,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> ValidatedJsonSchema:
"""
Validate a schema.
diff --git a/src/mixedbread/resources/files/__init__.py b/src/mixedbread/resources/files/__init__.py
index d1f68da6..13fe2123 100644
--- a/src/mixedbread/resources/files/__init__.py
+++ b/src/mixedbread/resources/files/__init__.py
@@ -8,22 +8,22 @@
FilesResourceWithStreamingResponse,
AsyncFilesResourceWithStreamingResponse,
)
-from .content import (
- ContentResource,
- AsyncContentResource,
- ContentResourceWithRawResponse,
- AsyncContentResourceWithRawResponse,
- ContentResourceWithStreamingResponse,
- AsyncContentResourceWithStreamingResponse,
+from .uploads import (
+ UploadsResource,
+ AsyncUploadsResource,
+ UploadsResourceWithRawResponse,
+ AsyncUploadsResourceWithRawResponse,
+ UploadsResourceWithStreamingResponse,
+ AsyncUploadsResourceWithStreamingResponse,
)
__all__ = [
- "ContentResource",
- "AsyncContentResource",
- "ContentResourceWithRawResponse",
- "AsyncContentResourceWithRawResponse",
- "ContentResourceWithStreamingResponse",
- "AsyncContentResourceWithStreamingResponse",
+ "UploadsResource",
+ "AsyncUploadsResource",
+ "UploadsResourceWithRawResponse",
+ "AsyncUploadsResourceWithRawResponse",
+ "UploadsResourceWithStreamingResponse",
+ "AsyncUploadsResourceWithStreamingResponse",
"FilesResource",
"AsyncFilesResource",
"FilesResourceWithRawResponse",
diff --git a/src/mixedbread/resources/files/content.py b/src/mixedbread/resources/files/content.py
deleted file mode 100644
index a55cb4c1..00000000
--- a/src/mixedbread/resources/files/content.py
+++ /dev/null
@@ -1,184 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-import httpx
-
-from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
-from ..._compat import cached_property
-from ..._resource import SyncAPIResource, AsyncAPIResource
-from ..._response import (
- BinaryAPIResponse,
- AsyncBinaryAPIResponse,
- StreamedBinaryAPIResponse,
- AsyncStreamedBinaryAPIResponse,
- to_custom_raw_response_wrapper,
- to_custom_streamed_response_wrapper,
- async_to_custom_raw_response_wrapper,
- async_to_custom_streamed_response_wrapper,
-)
-from ..._base_client import make_request_options
-
-__all__ = ["ContentResource", "AsyncContentResource"]
-
-
-class ContentResource(SyncAPIResource):
- @cached_property
- def with_raw_response(self) -> ContentResourceWithRawResponse:
- """
- This property can be used as a prefix for any HTTP method call to return the
- the raw response object instead of the parsed content.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
- """
- return ContentResourceWithRawResponse(self)
-
- @cached_property
- def with_streaming_response(self) -> ContentResourceWithStreamingResponse:
- """
- An alternative to `.with_raw_response` that doesn't eagerly read the response body.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
- """
- return ContentResourceWithStreamingResponse(self)
-
- def retrieve(
- 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 AsyncContentResource(AsyncAPIResource):
- @cached_property
- def with_raw_response(self) -> AsyncContentResourceWithRawResponse:
- """
- This property can be used as a prefix for any HTTP method call to return the
- the raw response object instead of the parsed content.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
- """
- return AsyncContentResourceWithRawResponse(self)
-
- @cached_property
- def with_streaming_response(self) -> AsyncContentResourceWithStreamingResponse:
- """
- An alternative to `.with_raw_response` that doesn't eagerly read the response body.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
- """
- return AsyncContentResourceWithStreamingResponse(self)
-
- async def retrieve(
- 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 ContentResourceWithRawResponse:
- def __init__(self, content: ContentResource) -> None:
- self._content = content
-
- self.retrieve = to_custom_raw_response_wrapper(
- content.retrieve,
- BinaryAPIResponse,
- )
-
-
-class AsyncContentResourceWithRawResponse:
- def __init__(self, content: AsyncContentResource) -> None:
- self._content = content
-
- self.retrieve = async_to_custom_raw_response_wrapper(
- content.retrieve,
- AsyncBinaryAPIResponse,
- )
-
-
-class ContentResourceWithStreamingResponse:
- def __init__(self, content: ContentResource) -> None:
- self._content = content
-
- self.retrieve = to_custom_streamed_response_wrapper(
- content.retrieve,
- StreamedBinaryAPIResponse,
- )
-
-
-class AsyncContentResourceWithStreamingResponse:
- def __init__(self, content: AsyncContentResource) -> None:
- self._content = content
-
- self.retrieve = async_to_custom_streamed_response_wrapper(
- content.retrieve,
- AsyncStreamedBinaryAPIResponse,
- )
diff --git a/src/mixedbread/resources/files/files.py b/src/mixedbread/resources/files/files.py
index 1ec24376..7aec6bff 100644
--- a/src/mixedbread/resources/files/files.py
+++ b/src/mixedbread/resources/files/files.py
@@ -2,54 +2,57 @@
from __future__ import annotations
-from typing import Mapping, cast
+from typing import Mapping, Optional, cast
import httpx
from ...types import file_list_params, file_create_params, file_update_params
-from .content import (
- ContentResource,
- AsyncContentResource,
- ContentResourceWithRawResponse,
- AsyncContentResourceWithRawResponse,
- ContentResourceWithStreamingResponse,
- AsyncContentResourceWithStreamingResponse,
-)
-from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes
-from ..._utils import (
- extract_files,
- maybe_transform,
- deepcopy_minimal,
- async_maybe_transform,
+from .uploads import (
+ UploadsResource,
+ AsyncUploadsResource,
+ UploadsResourceWithRawResponse,
+ AsyncUploadsResourceWithRawResponse,
+ UploadsResourceWithStreamingResponse,
+ AsyncUploadsResourceWithStreamingResponse,
)
+from ..._types import Body, Omit, Query, Headers, NotGiven, FileTypes, omit, not_given
+from ..._utils import extract_files, maybe_transform, deepcopy_minimal, async_maybe_transform
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 ..._base_client import make_request_options
+from ...pagination import SyncCursor, AsyncCursor
+from ..._base_client import AsyncPaginator, make_request_options
from ...types.file_object import FileObject
-from ...types.file_deleted import FileDeleted
-from ...types.file_list_response import FileListResponse
+from ...types.file_delete_response import FileDeleteResponse
__all__ = ["FilesResource", "AsyncFilesResource"]
class FilesResource(SyncAPIResource):
@cached_property
- def content(self) -> ContentResource:
- return ContentResource(self._client)
+ def uploads(self) -> UploadsResource:
+ return UploadsResource(self._client)
@cached_property
def with_raw_response(self) -> FilesResourceWithRawResponse:
"""
- This property can be used as a prefix for any HTTP method call to return the
+ This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
"""
return FilesResourceWithRawResponse(self)
@@ -58,7 +61,7 @@ def with_streaming_response(self) -> FilesResourceWithStreamingResponse:
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
"""
return FilesResourceWithStreamingResponse(self)
@@ -71,7 +74,7 @@ def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> FileObject:
"""
Upload a new file.
@@ -116,7 +119,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> FileObject:
"""
Retrieve details of a specific file by its ID.
@@ -156,7 +159,7 @@ def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> FileObject:
"""
Update the details of a specific file.
@@ -199,15 +202,18 @@ def update(
def list(
self,
*,
- limit: int | NotGiven = NOT_GIVEN,
- offset: int | NotGiven = NOT_GIVEN,
+ limit: int | Omit = omit,
+ after: Optional[str] | Omit = omit,
+ before: Optional[str] | Omit = omit,
+ include_total: bool | Omit = omit,
+ q: Optional[str] | Omit = omit,
# 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:
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> SyncCursor[FileObject]:
"""
List all files for the authenticated user.
@@ -216,9 +222,17 @@ def list(
Returns: A list of files belonging to the user.
Args:
- limit: Maximum number of items to return per page
+ 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.
- offset: Offset of the first item to return
+ include_total: Whether to include total count in response (expensive operation)
+
+ q: Search query for fuzzy matching over name and description fields
extra_headers: Send extra headers
@@ -228,8 +242,9 @@ def list(
timeout: Override the client-level default timeout for this request, in seconds
"""
- return self._get(
+ return self._get_api_list(
"/v1/files",
+ page=SyncCursor[FileObject],
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
@@ -238,12 +253,15 @@ def list(
query=maybe_transform(
{
"limit": limit,
- "offset": offset,
+ "after": after,
+ "before": before,
+ "include_total": include_total,
+ "q": q,
},
file_list_params.FileListParams,
),
),
- cast_to=FileListResponse,
+ model=FileObject,
)
def delete(
@@ -255,8 +273,8 @@ def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> FileDeleted:
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FileDeleteResponse:
"""
Delete a specific file by its ID.
@@ -282,22 +300,62 @@ def delete(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=FileDeleted,
+ 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
- def content(self) -> AsyncContentResource:
- return AsyncContentResource(self._client)
+ def uploads(self) -> AsyncUploadsResource:
+ return AsyncUploadsResource(self._client)
@cached_property
def with_raw_response(self) -> AsyncFilesResourceWithRawResponse:
"""
- This property can be used as a prefix for any HTTP method call to return the
+ This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
"""
return AsyncFilesResourceWithRawResponse(self)
@@ -306,7 +364,7 @@ def with_streaming_response(self) -> AsyncFilesResourceWithStreamingResponse:
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
"""
return AsyncFilesResourceWithStreamingResponse(self)
@@ -319,7 +377,7 @@ async def create(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> FileObject:
"""
Upload a new file.
@@ -364,7 +422,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> FileObject:
"""
Retrieve details of a specific file by its ID.
@@ -404,7 +462,7 @@ async def update(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> FileObject:
"""
Update the details of a specific file.
@@ -444,18 +502,21 @@ async def update(
cast_to=FileObject,
)
- async def list(
+ def list(
self,
*,
- limit: int | NotGiven = NOT_GIVEN,
- offset: int | NotGiven = NOT_GIVEN,
+ limit: int | Omit = omit,
+ after: Optional[str] | Omit = omit,
+ before: Optional[str] | Omit = omit,
+ include_total: bool | Omit = omit,
+ q: Optional[str] | Omit = omit,
# 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:
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> AsyncPaginator[FileObject, AsyncCursor[FileObject]]:
"""
List all files for the authenticated user.
@@ -464,9 +525,17 @@ async def list(
Returns: A list of files belonging to the user.
Args:
- limit: Maximum number of items to return per page
+ 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.
- offset: Offset of the first item to return
+ include_total: Whether to include total count in response (expensive operation)
+
+ q: Search query for fuzzy matching over name and description fields
extra_headers: Send extra headers
@@ -476,22 +545,26 @@ async def list(
timeout: Override the client-level default timeout for this request, in seconds
"""
- return await self._get(
+ return self._get_api_list(
"/v1/files",
+ page=AsyncCursor[FileObject],
options=make_request_options(
extra_headers=extra_headers,
extra_query=extra_query,
extra_body=extra_body,
timeout=timeout,
- query=await async_maybe_transform(
+ query=maybe_transform(
{
"limit": limit,
- "offset": offset,
+ "after": after,
+ "before": before,
+ "include_total": include_total,
+ "q": q,
},
file_list_params.FileListParams,
),
),
- cast_to=FileListResponse,
+ model=FileObject,
)
async def delete(
@@ -503,8 +576,8 @@ async def delete(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
- ) -> FileDeleted:
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
+ ) -> FileDeleteResponse:
"""
Delete a specific file by its ID.
@@ -530,7 +603,47 @@ async def delete(
options=make_request_options(
extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
),
- cast_to=FileDeleted,
+ 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,
)
@@ -553,10 +666,14 @@ def __init__(self, files: FilesResource) -> None:
self.delete = to_raw_response_wrapper(
files.delete,
)
+ self.content = to_custom_raw_response_wrapper(
+ files.content,
+ BinaryAPIResponse,
+ )
@cached_property
- def content(self) -> ContentResourceWithRawResponse:
- return ContentResourceWithRawResponse(self._files.content)
+ def uploads(self) -> UploadsResourceWithRawResponse:
+ return UploadsResourceWithRawResponse(self._files.uploads)
class AsyncFilesResourceWithRawResponse:
@@ -578,10 +695,14 @@ 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,
+ )
@cached_property
- def content(self) -> AsyncContentResourceWithRawResponse:
- return AsyncContentResourceWithRawResponse(self._files.content)
+ def uploads(self) -> AsyncUploadsResourceWithRawResponse:
+ return AsyncUploadsResourceWithRawResponse(self._files.uploads)
class FilesResourceWithStreamingResponse:
@@ -603,10 +724,14 @@ def __init__(self, files: FilesResource) -> None:
self.delete = to_streamed_response_wrapper(
files.delete,
)
+ self.content = to_custom_streamed_response_wrapper(
+ files.content,
+ StreamedBinaryAPIResponse,
+ )
@cached_property
- def content(self) -> ContentResourceWithStreamingResponse:
- return ContentResourceWithStreamingResponse(self._files.content)
+ def uploads(self) -> UploadsResourceWithStreamingResponse:
+ return UploadsResourceWithStreamingResponse(self._files.uploads)
class AsyncFilesResourceWithStreamingResponse:
@@ -628,7 +753,11 @@ 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,
+ )
@cached_property
- def content(self) -> AsyncContentResourceWithStreamingResponse:
- return AsyncContentResourceWithStreamingResponse(self._files.content)
+ def uploads(self) -> AsyncUploadsResourceWithStreamingResponse:
+ return AsyncUploadsResourceWithStreamingResponse(self._files.uploads)
diff --git a/src/mixedbread/resources/files/uploads.py b/src/mixedbread/resources/files/uploads.py
new file mode 100644
index 00000000..dc410621
--- /dev/null
+++ b/src/mixedbread/resources/files/uploads.py
@@ -0,0 +1,520 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable
+
+import httpx
+
+from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ..._utils import maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...types.files import upload_create_params, upload_complete_params
+from ..._base_client import make_request_options
+from ...types.file_object import FileObject
+from ...types.files.upload_list_response import UploadListResponse
+from ...types.files.upload_abort_response import UploadAbortResponse
+from ...types.files.upload_create_response import UploadCreateResponse
+from ...types.files.upload_retrieve_response import UploadRetrieveResponse
+from ...types.files.multipart_upload_part_param import MultipartUploadPartParam
+
+__all__ = ["UploadsResource", "AsyncUploadsResource"]
+
+
+class UploadsResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> UploadsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
+ """
+ return UploadsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> UploadsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
+ """
+ return UploadsResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ filename: str,
+ file_size: int,
+ mime_type: str,
+ part_count: int | Omit = omit,
+ # 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,
+ ) -> UploadCreateResponse:
+ """
+ Initiate a multipart upload and receive presigned URLs for uploading parts
+ directly to storage.
+
+ Args:
+ filename: Name of the file including extension
+
+ file_size: Total size of the file in bytes
+
+ mime_type: MIME type of the file
+
+ part_count: Number of parts to split the upload into
+
+ 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
+ """
+ return self._post(
+ "/v1/files/uploads",
+ body=maybe_transform(
+ {
+ "filename": filename,
+ "file_size": file_size,
+ "mime_type": mime_type,
+ "part_count": part_count,
+ },
+ upload_create_params.UploadCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=UploadCreateResponse,
+ )
+
+ def retrieve(
+ self,
+ upload_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,
+ ) -> UploadRetrieveResponse:
+ """
+ Get a multipart upload's details with fresh presigned URLs for any parts not yet
+ uploaded.
+
+ Args:
+ upload_id: The ID of the multipart upload
+
+ 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 upload_id:
+ raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}")
+ return self._get(
+ f"/v1/files/uploads/{upload_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=UploadRetrieveResponse,
+ )
+
+ def list(
+ self,
+ *,
+ # 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,
+ ) -> UploadListResponse:
+ """List all in-progress multipart uploads for the authenticated organization."""
+ return self._get(
+ "/v1/files/uploads",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=UploadListResponse,
+ )
+
+ def abort(
+ self,
+ upload_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,
+ ) -> UploadAbortResponse:
+ """
+ Abort a multipart upload and clean up any uploaded parts.
+
+ Args:
+ upload_id: The ID of the multipart upload to abort
+
+ 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 upload_id:
+ raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}")
+ return self._post(
+ f"/v1/files/uploads/{upload_id}/abort",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=UploadAbortResponse,
+ )
+
+ def complete(
+ self,
+ upload_id: str,
+ *,
+ parts: Iterable[MultipartUploadPartParam],
+ # 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,
+ ) -> FileObject:
+ """Complete a multipart upload after all parts have been uploaded.
+
+ Creates the file
+ object and returns it.
+
+ Args:
+ upload_id: The ID of the multipart upload
+
+ parts: List of completed parts with their ETags
+
+ 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 upload_id:
+ raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}")
+ return self._post(
+ f"/v1/files/uploads/{upload_id}/complete",
+ body=maybe_transform({"parts": parts}, upload_complete_params.UploadCompleteParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=FileObject,
+ )
+
+
+class AsyncUploadsResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncUploadsResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncUploadsResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncUploadsResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
+ """
+ return AsyncUploadsResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ filename: str,
+ file_size: int,
+ mime_type: str,
+ part_count: int | Omit = omit,
+ # 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,
+ ) -> UploadCreateResponse:
+ """
+ Initiate a multipart upload and receive presigned URLs for uploading parts
+ directly to storage.
+
+ Args:
+ filename: Name of the file including extension
+
+ file_size: Total size of the file in bytes
+
+ mime_type: MIME type of the file
+
+ part_count: Number of parts to split the upload into
+
+ 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
+ """
+ return await self._post(
+ "/v1/files/uploads",
+ body=await async_maybe_transform(
+ {
+ "filename": filename,
+ "file_size": file_size,
+ "mime_type": mime_type,
+ "part_count": part_count,
+ },
+ upload_create_params.UploadCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=UploadCreateResponse,
+ )
+
+ async def retrieve(
+ self,
+ upload_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,
+ ) -> UploadRetrieveResponse:
+ """
+ Get a multipart upload's details with fresh presigned URLs for any parts not yet
+ uploaded.
+
+ Args:
+ upload_id: The ID of the multipart upload
+
+ 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 upload_id:
+ raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}")
+ return await self._get(
+ f"/v1/files/uploads/{upload_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=UploadRetrieveResponse,
+ )
+
+ async def list(
+ self,
+ *,
+ # 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,
+ ) -> UploadListResponse:
+ """List all in-progress multipart uploads for the authenticated organization."""
+ return await self._get(
+ "/v1/files/uploads",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=UploadListResponse,
+ )
+
+ async def abort(
+ self,
+ upload_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,
+ ) -> UploadAbortResponse:
+ """
+ Abort a multipart upload and clean up any uploaded parts.
+
+ Args:
+ upload_id: The ID of the multipart upload to abort
+
+ 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 upload_id:
+ raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}")
+ return await self._post(
+ f"/v1/files/uploads/{upload_id}/abort",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=UploadAbortResponse,
+ )
+
+ async def complete(
+ self,
+ upload_id: str,
+ *,
+ parts: Iterable[MultipartUploadPartParam],
+ # 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,
+ ) -> FileObject:
+ """Complete a multipart upload after all parts have been uploaded.
+
+ Creates the file
+ object and returns it.
+
+ Args:
+ upload_id: The ID of the multipart upload
+
+ parts: List of completed parts with their ETags
+
+ 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 upload_id:
+ raise ValueError(f"Expected a non-empty value for `upload_id` but received {upload_id!r}")
+ return await self._post(
+ f"/v1/files/uploads/{upload_id}/complete",
+ body=await async_maybe_transform({"parts": parts}, upload_complete_params.UploadCompleteParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=FileObject,
+ )
+
+
+class UploadsResourceWithRawResponse:
+ def __init__(self, uploads: UploadsResource) -> None:
+ self._uploads = uploads
+
+ self.create = to_raw_response_wrapper(
+ uploads.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ uploads.retrieve,
+ )
+ self.list = to_raw_response_wrapper(
+ uploads.list,
+ )
+ self.abort = to_raw_response_wrapper(
+ uploads.abort,
+ )
+ self.complete = to_raw_response_wrapper(
+ uploads.complete,
+ )
+
+
+class AsyncUploadsResourceWithRawResponse:
+ def __init__(self, uploads: AsyncUploadsResource) -> None:
+ self._uploads = uploads
+
+ self.create = async_to_raw_response_wrapper(
+ uploads.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ uploads.retrieve,
+ )
+ self.list = async_to_raw_response_wrapper(
+ uploads.list,
+ )
+ self.abort = async_to_raw_response_wrapper(
+ uploads.abort,
+ )
+ self.complete = async_to_raw_response_wrapper(
+ uploads.complete,
+ )
+
+
+class UploadsResourceWithStreamingResponse:
+ def __init__(self, uploads: UploadsResource) -> None:
+ self._uploads = uploads
+
+ self.create = to_streamed_response_wrapper(
+ uploads.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ uploads.retrieve,
+ )
+ self.list = to_streamed_response_wrapper(
+ uploads.list,
+ )
+ self.abort = to_streamed_response_wrapper(
+ uploads.abort,
+ )
+ self.complete = to_streamed_response_wrapper(
+ uploads.complete,
+ )
+
+
+class AsyncUploadsResourceWithStreamingResponse:
+ def __init__(self, uploads: AsyncUploadsResource) -> None:
+ self._uploads = uploads
+
+ self.create = async_to_streamed_response_wrapper(
+ uploads.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ uploads.retrieve,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ uploads.list,
+ )
+ self.abort = async_to_streamed_response_wrapper(
+ uploads.abort,
+ )
+ self.complete = async_to_streamed_response_wrapper(
+ uploads.complete,
+ )
diff --git a/src/mixedbread/resources/parsing/jobs.py b/src/mixedbread/resources/parsing/jobs.py
index ace6052c..97b70a8c 100644
--- a/src/mixedbread/resources/parsing/jobs.py
+++ b/src/mixedbread/resources/parsing/jobs.py
@@ -7,11 +7,8 @@
import httpx
-from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
-from ..._utils import (
- maybe_transform,
- async_maybe_transform,
-)
+from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given
+from ..._utils import maybe_transform, async_maybe_transform
from ..._compat import cached_property
from ..._resource import SyncAPIResource, AsyncAPIResource
from ..._response import (
@@ -20,9 +17,16 @@
async_to_raw_response_wrapper,
async_to_streamed_response_wrapper,
)
-from ..._base_client import make_request_options
-from ...types.parsing import job_create_params
+from ...pagination import SyncCursor, AsyncCursor
+from ..._base_client import AsyncPaginator, make_request_options
+from ...types.parsing import ReturnFormat, ChunkingStrategy, job_list_params, job_create_params
from ...types.parsing.parsing_job import ParsingJob
+from ...types.parsing.element_type import ElementType
+from ...types.parsing.return_format import ReturnFormat
+from ...types.parsing.chunking_strategy import ChunkingStrategy
+from ...types.parsing.job_list_response import JobListResponse
+from ...types.parsing.parsing_job_status import ParsingJobStatus
+from ...types.parsing.job_delete_response import JobDeleteResponse
__all__ = ["JobsResource", "AsyncJobsResource"]
@@ -31,10 +35,10 @@ class JobsResource(SyncAPIResource):
@cached_property
def with_raw_response(self) -> JobsResourceWithRawResponse:
"""
- This property can be used as a prefix for any HTTP method call to return the
+ This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
"""
return JobsResourceWithRawResponse(self)
@@ -43,7 +47,7 @@ def with_streaming_response(self) -> JobsResourceWithStreamingResponse:
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
"""
return JobsResourceWithStreamingResponse(self)
@@ -51,32 +55,16 @@ def create(
self,
*,
file_id: str,
- chunking_strategy: Literal["page"] | NotGiven = NOT_GIVEN,
- element_types: Optional[
- List[
- Literal[
- "caption",
- "footnote",
- "formula",
- "list-item",
- "page-footer",
- "page-header",
- "picture",
- "section-header",
- "table",
- "text",
- "title",
- ]
- ]
- ]
- | NotGiven = NOT_GIVEN,
- return_format: Literal["html", "markdown", "plain"] | NotGiven = NOT_GIVEN,
+ element_types: Optional[List[ElementType]] | Omit = omit,
+ chunking_strategy: ChunkingStrategy | Omit = omit,
+ return_format: ReturnFormat | Omit = omit,
+ mode: Literal["fast", "high_quality"] | Omit = omit,
# 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,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> ParsingJob:
"""
Start a parse job for the provided file.
@@ -88,12 +76,14 @@ def create(
Args:
file_id: The ID of the file to parse
- chunking_strategy: The strategy to use for chunking the content
-
element_types: The elements to extract from the document
+ chunking_strategy: The strategy to use for chunking the content
+
return_format: The format of the returned content
+ mode: The strategy to use for OCR
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -107,9 +97,10 @@ def create(
body=maybe_transform(
{
"file_id": file_id,
- "chunking_strategy": chunking_strategy,
"element_types": element_types,
+ "chunking_strategy": chunking_strategy,
"return_format": return_format,
+ "mode": mode,
},
job_create_params.JobCreateParams,
),
@@ -128,7 +119,7 @@ def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> ParsingJob:
"""
Get detailed information about a specific parse job.
@@ -158,15 +149,163 @@ def retrieve(
cast_to=ParsingJob,
)
+ def list(
+ self,
+ *,
+ limit: int | Omit = omit,
+ after: Optional[str] | Omit = omit,
+ before: Optional[str] | Omit = omit,
+ include_total: bool | Omit = omit,
+ statuses: Optional[List[ParsingJobStatus]] | Omit = omit,
+ q: Optional[str] | Omit = omit,
+ # 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,
+ ) -> SyncCursor[JobListResponse]:
+ """List parsing jobs with pagination.
+
+ Args: limit: The number of items to return.
+
+ offset: The number of items to skip.
+
+ Returns: List of parsing jobs with pagination.
+
+ Args:
+ 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
+
+ q: Search query to filter by
+
+ 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
+ """
+ return self._get_api_list(
+ "/v1/parsing/jobs",
+ page=SyncCursor[JobListResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "limit": limit,
+ "after": after,
+ "before": before,
+ "include_total": include_total,
+ "statuses": statuses,
+ "q": q,
+ },
+ job_list_params.JobListParams,
+ ),
+ ),
+ model=JobListResponse,
+ )
+
+ def delete(
+ self,
+ job_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,
+ ) -> JobDeleteResponse:
+ """
+ Delete a specific parse job.
+
+ Args: job_id: The ID of the parse job to delete.
+
+ Returns: The deleted parsing job.
+
+ Args:
+ job_id: The ID of the parse job to delete
+
+ 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 job_id:
+ raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}")
+ return self._delete(
+ f"/v1/parsing/jobs/{job_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=JobDeleteResponse,
+ )
+
+ def cancel(
+ self,
+ job_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,
+ ) -> ParsingJob:
+ """
+ Cancel a specific parse job.
+
+ Args: job_id: The ID of the parse job to cancel.
+
+ Returns: The cancelled parsing job.
+
+ Args:
+ job_id: The ID of the parse job to cancel
+
+ 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 job_id:
+ raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}")
+ return self._patch(
+ f"/v1/parsing/jobs/{job_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ParsingJob,
+ )
+
class AsyncJobsResource(AsyncAPIResource):
@cached_property
def with_raw_response(self) -> AsyncJobsResourceWithRawResponse:
"""
- This property can be used as a prefix for any HTTP method call to return the
+ This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
"""
return AsyncJobsResourceWithRawResponse(self)
@@ -175,7 +314,7 @@ def with_streaming_response(self) -> AsyncJobsResourceWithStreamingResponse:
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
"""
return AsyncJobsResourceWithStreamingResponse(self)
@@ -183,32 +322,16 @@ async def create(
self,
*,
file_id: str,
- chunking_strategy: Literal["page"] | NotGiven = NOT_GIVEN,
- element_types: Optional[
- List[
- Literal[
- "caption",
- "footnote",
- "formula",
- "list-item",
- "page-footer",
- "page-header",
- "picture",
- "section-header",
- "table",
- "text",
- "title",
- ]
- ]
- ]
- | NotGiven = NOT_GIVEN,
- return_format: Literal["html", "markdown", "plain"] | NotGiven = NOT_GIVEN,
+ element_types: Optional[List[ElementType]] | Omit = omit,
+ chunking_strategy: ChunkingStrategy | Omit = omit,
+ return_format: ReturnFormat | Omit = omit,
+ mode: Literal["fast", "high_quality"] | Omit = omit,
# 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,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> ParsingJob:
"""
Start a parse job for the provided file.
@@ -220,12 +343,14 @@ async def create(
Args:
file_id: The ID of the file to parse
- chunking_strategy: The strategy to use for chunking the content
-
element_types: The elements to extract from the document
+ chunking_strategy: The strategy to use for chunking the content
+
return_format: The format of the returned content
+ mode: The strategy to use for OCR
+
extra_headers: Send extra headers
extra_query: Add additional query parameters to the request
@@ -239,9 +364,10 @@ async def create(
body=await async_maybe_transform(
{
"file_id": file_id,
- "chunking_strategy": chunking_strategy,
"element_types": element_types,
+ "chunking_strategy": chunking_strategy,
"return_format": return_format,
+ "mode": mode,
},
job_create_params.JobCreateParams,
),
@@ -260,7 +386,7 @@ async def retrieve(
extra_headers: Headers | None = None,
extra_query: Query | None = None,
extra_body: Body | None = None,
- timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
+ timeout: float | httpx.Timeout | None | NotGiven = not_given,
) -> ParsingJob:
"""
Get detailed information about a specific parse job.
@@ -290,6 +416,154 @@ async def retrieve(
cast_to=ParsingJob,
)
+ def list(
+ self,
+ *,
+ limit: int | Omit = omit,
+ after: Optional[str] | Omit = omit,
+ before: Optional[str] | Omit = omit,
+ include_total: bool | Omit = omit,
+ statuses: Optional[List[ParsingJobStatus]] | Omit = omit,
+ q: Optional[str] | Omit = omit,
+ # 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,
+ ) -> AsyncPaginator[JobListResponse, AsyncCursor[JobListResponse]]:
+ """List parsing jobs with pagination.
+
+ Args: limit: The number of items to return.
+
+ offset: The number of items to skip.
+
+ Returns: List of parsing jobs with pagination.
+
+ Args:
+ 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
+
+ q: Search query to filter by
+
+ 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
+ """
+ return self._get_api_list(
+ "/v1/parsing/jobs",
+ page=AsyncCursor[JobListResponse],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "limit": limit,
+ "after": after,
+ "before": before,
+ "include_total": include_total,
+ "statuses": statuses,
+ "q": q,
+ },
+ job_list_params.JobListParams,
+ ),
+ ),
+ model=JobListResponse,
+ )
+
+ async def delete(
+ self,
+ job_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,
+ ) -> JobDeleteResponse:
+ """
+ Delete a specific parse job.
+
+ Args: job_id: The ID of the parse job to delete.
+
+ Returns: The deleted parsing job.
+
+ Args:
+ job_id: The ID of the parse job to delete
+
+ 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 job_id:
+ raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}")
+ return await self._delete(
+ f"/v1/parsing/jobs/{job_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=JobDeleteResponse,
+ )
+
+ async def cancel(
+ self,
+ job_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,
+ ) -> ParsingJob:
+ """
+ Cancel a specific parse job.
+
+ Args: job_id: The ID of the parse job to cancel.
+
+ Returns: The cancelled parsing job.
+
+ Args:
+ job_id: The ID of the parse job to cancel
+
+ 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 job_id:
+ raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}")
+ return await self._patch(
+ f"/v1/parsing/jobs/{job_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=ParsingJob,
+ )
+
class JobsResourceWithRawResponse:
def __init__(self, jobs: JobsResource) -> None:
@@ -301,6 +575,15 @@ def __init__(self, jobs: JobsResource) -> None:
self.retrieve = to_raw_response_wrapper(
jobs.retrieve,
)
+ self.list = to_raw_response_wrapper(
+ jobs.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ jobs.delete,
+ )
+ self.cancel = to_raw_response_wrapper(
+ jobs.cancel,
+ )
class AsyncJobsResourceWithRawResponse:
@@ -313,6 +596,15 @@ def __init__(self, jobs: AsyncJobsResource) -> None:
self.retrieve = async_to_raw_response_wrapper(
jobs.retrieve,
)
+ self.list = async_to_raw_response_wrapper(
+ jobs.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ jobs.delete,
+ )
+ self.cancel = async_to_raw_response_wrapper(
+ jobs.cancel,
+ )
class JobsResourceWithStreamingResponse:
@@ -325,6 +617,15 @@ def __init__(self, jobs: JobsResource) -> None:
self.retrieve = to_streamed_response_wrapper(
jobs.retrieve,
)
+ self.list = to_streamed_response_wrapper(
+ jobs.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ jobs.delete,
+ )
+ self.cancel = to_streamed_response_wrapper(
+ jobs.cancel,
+ )
class AsyncJobsResourceWithStreamingResponse:
@@ -337,3 +638,12 @@ def __init__(self, jobs: AsyncJobsResource) -> None:
self.retrieve = async_to_streamed_response_wrapper(
jobs.retrieve,
)
+ self.list = async_to_streamed_response_wrapper(
+ jobs.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ jobs.delete,
+ )
+ self.cancel = async_to_streamed_response_wrapper(
+ jobs.cancel,
+ )
diff --git a/src/mixedbread/resources/parsing/parsing.py b/src/mixedbread/resources/parsing/parsing.py
index be6f103f..069df23c 100644
--- a/src/mixedbread/resources/parsing/parsing.py
+++ b/src/mixedbread/resources/parsing/parsing.py
@@ -24,10 +24,10 @@ def jobs(self) -> JobsResource:
@cached_property
def with_raw_response(self) -> ParsingResourceWithRawResponse:
"""
- This property can be used as a prefix for any HTTP method call to return the
+ This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
"""
return ParsingResourceWithRawResponse(self)
@@ -36,7 +36,7 @@ def with_streaming_response(self) -> ParsingResourceWithStreamingResponse:
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
"""
return ParsingResourceWithStreamingResponse(self)
@@ -49,10 +49,10 @@ def jobs(self) -> AsyncJobsResource:
@cached_property
def with_raw_response(self) -> AsyncParsingResourceWithRawResponse:
"""
- This property can be used as a prefix for any HTTP method call to return the
+ This property can be used as a prefix for any HTTP method call to return
the raw response object instead of the parsed content.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
"""
return AsyncParsingResourceWithRawResponse(self)
@@ -61,7 +61,7 @@ def with_streaming_response(self) -> AsyncParsingResourceWithStreamingResponse:
"""
An alternative to `.with_raw_response` that doesn't eagerly read the response body.
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
"""
return AsyncParsingResourceWithStreamingResponse(self)
diff --git a/src/mixedbread/resources/reranking.py b/src/mixedbread/resources/reranking.py
deleted file mode 100644
index 73185c1c..00000000
--- a/src/mixedbread/resources/reranking.py
+++ /dev/null
@@ -1,230 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing import List, Union, Optional
-
-import httpx
-
-from ..types import reranking_create_params
-from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
-from .._utils import (
- maybe_transform,
- async_maybe_transform,
-)
-from .._compat import cached_property
-from .._resource import SyncAPIResource, AsyncAPIResource
-from .._response import (
- to_raw_response_wrapper,
- to_streamed_response_wrapper,
- async_to_raw_response_wrapper,
- async_to_streamed_response_wrapper,
-)
-from .._base_client import make_request_options
-from ..types.reranking_create_response import RerankingCreateResponse
-
-__all__ = ["RerankingResource", "AsyncRerankingResource"]
-
-
-class RerankingResource(SyncAPIResource):
- @cached_property
- def with_raw_response(self) -> RerankingResourceWithRawResponse:
- """
- This property can be used as a prefix for any HTTP method call to return the
- the raw response object instead of the parsed content.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
- """
- return RerankingResourceWithRawResponse(self)
-
- @cached_property
- def with_streaming_response(self) -> RerankingResourceWithStreamingResponse:
- """
- An alternative to `.with_raw_response` that doesn't eagerly read the response body.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
- """
- return RerankingResourceWithStreamingResponse(self)
-
- def create(
- self,
- *,
- input: List[Union[str, object]],
- query: str,
- model: str | NotGiven = NOT_GIVEN,
- rank_fields: Optional[List[str]] | NotGiven = NOT_GIVEN,
- return_input: bool | NotGiven = NOT_GIVEN,
- top_k: int | 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,
- ) -> RerankingCreateResponse:
- """
- Rerank different kind of documents for a given query.
-
- Args: params: RerankingCreateParams: The parameters for reranking.
-
- Returns: RerankingCreateResponse: The reranked documents for the input query.
-
- Args:
- input: The input documents to rerank.
-
- query: The query to rerank the documents.
-
- model: The model to use for reranking documents.
-
- rank_fields: The fields of the documents to rank.
-
- return_input: Whether to return the documents.
-
- top_k: The number of documents to return.
-
- 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
- """
- return self._post(
- "/v1/reranking",
- body=maybe_transform(
- {
- "input": input,
- "query": query,
- "model": model,
- "rank_fields": rank_fields,
- "return_input": return_input,
- "top_k": top_k,
- },
- reranking_create_params.RerankingCreateParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=RerankingCreateResponse,
- )
-
-
-class AsyncRerankingResource(AsyncAPIResource):
- @cached_property
- def with_raw_response(self) -> AsyncRerankingResourceWithRawResponse:
- """
- This property can be used as a prefix for any HTTP method call to return the
- the raw response object instead of the parsed content.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
- """
- return AsyncRerankingResourceWithRawResponse(self)
-
- @cached_property
- def with_streaming_response(self) -> AsyncRerankingResourceWithStreamingResponse:
- """
- An alternative to `.with_raw_response` that doesn't eagerly read the response body.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
- """
- return AsyncRerankingResourceWithStreamingResponse(self)
-
- async def create(
- self,
- *,
- input: List[Union[str, object]],
- query: str,
- model: str | NotGiven = NOT_GIVEN,
- rank_fields: Optional[List[str]] | NotGiven = NOT_GIVEN,
- return_input: bool | NotGiven = NOT_GIVEN,
- top_k: int | 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,
- ) -> RerankingCreateResponse:
- """
- Rerank different kind of documents for a given query.
-
- Args: params: RerankingCreateParams: The parameters for reranking.
-
- Returns: RerankingCreateResponse: The reranked documents for the input query.
-
- Args:
- input: The input documents to rerank.
-
- query: The query to rerank the documents.
-
- model: The model to use for reranking documents.
-
- rank_fields: The fields of the documents to rank.
-
- return_input: Whether to return the documents.
-
- top_k: The number of documents to return.
-
- 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
- """
- return await self._post(
- "/v1/reranking",
- body=await async_maybe_transform(
- {
- "input": input,
- "query": query,
- "model": model,
- "rank_fields": rank_fields,
- "return_input": return_input,
- "top_k": top_k,
- },
- reranking_create_params.RerankingCreateParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=RerankingCreateResponse,
- )
-
-
-class RerankingResourceWithRawResponse:
- def __init__(self, reranking: RerankingResource) -> None:
- self._reranking = reranking
-
- self.create = to_raw_response_wrapper(
- reranking.create,
- )
-
-
-class AsyncRerankingResourceWithRawResponse:
- def __init__(self, reranking: AsyncRerankingResource) -> None:
- self._reranking = reranking
-
- self.create = async_to_raw_response_wrapper(
- reranking.create,
- )
-
-
-class RerankingResourceWithStreamingResponse:
- def __init__(self, reranking: RerankingResource) -> None:
- self._reranking = reranking
-
- self.create = to_streamed_response_wrapper(
- reranking.create,
- )
-
-
-class AsyncRerankingResourceWithStreamingResponse:
- def __init__(self, reranking: AsyncRerankingResource) -> None:
- self._reranking = reranking
-
- self.create = async_to_streamed_response_wrapper(
- reranking.create,
- )
diff --git a/src/mixedbread/resources/service_info.py b/src/mixedbread/resources/service_info.py
deleted file mode 100644
index 6ca3b45f..00000000
--- a/src/mixedbread/resources/service_info.py
+++ /dev/null
@@ -1,143 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-import httpx
-
-from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven
-from .._compat import cached_property
-from .._resource import SyncAPIResource, AsyncAPIResource
-from .._response import (
- to_raw_response_wrapper,
- to_streamed_response_wrapper,
- async_to_raw_response_wrapper,
- async_to_streamed_response_wrapper,
-)
-from .._base_client import make_request_options
-from ..types.info_response import InfoResponse
-
-__all__ = ["ServiceInfoResource", "AsyncServiceInfoResource"]
-
-
-class ServiceInfoResource(SyncAPIResource):
- @cached_property
- def with_raw_response(self) -> ServiceInfoResourceWithRawResponse:
- """
- This property can be used as a prefix for any HTTP method call to return the
- the raw response object instead of the parsed content.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
- """
- return ServiceInfoResourceWithRawResponse(self)
-
- @cached_property
- def with_streaming_response(self) -> ServiceInfoResourceWithStreamingResponse:
- """
- An alternative to `.with_raw_response` that doesn't eagerly read the response body.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
- """
- return ServiceInfoResourceWithStreamingResponse(self)
-
- def retrieve(
- self,
- *,
- # 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,
- ) -> InfoResponse:
- """
- Returns service information, including name and version.
-
- Returns: InfoResponse: A response containing the service name and version.
- """
- return self._get(
- "/",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=InfoResponse,
- )
-
-
-class AsyncServiceInfoResource(AsyncAPIResource):
- @cached_property
- def with_raw_response(self) -> AsyncServiceInfoResourceWithRawResponse:
- """
- This property can be used as a prefix for any HTTP method call to return the
- the raw response object instead of the parsed content.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
- """
- return AsyncServiceInfoResourceWithRawResponse(self)
-
- @cached_property
- def with_streaming_response(self) -> AsyncServiceInfoResourceWithStreamingResponse:
- """
- An alternative to `.with_raw_response` that doesn't eagerly read the response body.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
- """
- return AsyncServiceInfoResourceWithStreamingResponse(self)
-
- async def retrieve(
- self,
- *,
- # 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,
- ) -> InfoResponse:
- """
- Returns service information, including name and version.
-
- Returns: InfoResponse: A response containing the service name and version.
- """
- return await self._get(
- "/",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=InfoResponse,
- )
-
-
-class ServiceInfoResourceWithRawResponse:
- def __init__(self, service_info: ServiceInfoResource) -> None:
- self._service_info = service_info
-
- self.retrieve = to_raw_response_wrapper(
- service_info.retrieve,
- )
-
-
-class AsyncServiceInfoResourceWithRawResponse:
- def __init__(self, service_info: AsyncServiceInfoResource) -> None:
- self._service_info = service_info
-
- self.retrieve = async_to_raw_response_wrapper(
- service_info.retrieve,
- )
-
-
-class ServiceInfoResourceWithStreamingResponse:
- def __init__(self, service_info: ServiceInfoResource) -> None:
- self._service_info = service_info
-
- self.retrieve = to_streamed_response_wrapper(
- service_info.retrieve,
- )
-
-
-class AsyncServiceInfoResourceWithStreamingResponse:
- def __init__(self, service_info: AsyncServiceInfoResource) -> None:
- self._service_info = service_info
-
- self.retrieve = async_to_streamed_response_wrapper(
- service_info.retrieve,
- )
diff --git a/src/mixedbread/resources/vector_stores/__init__.py b/src/mixedbread/resources/stores/__init__.py
similarity index 50%
rename from src/mixedbread/resources/vector_stores/__init__.py
rename to src/mixedbread/resources/stores/__init__.py
index 85d202da..8ec1ec90 100644
--- a/src/mixedbread/resources/vector_stores/__init__.py
+++ b/src/mixedbread/resources/stores/__init__.py
@@ -8,13 +8,13 @@
FilesResourceWithStreamingResponse,
AsyncFilesResourceWithStreamingResponse,
)
-from .vector_stores import (
- VectorStoresResource,
- AsyncVectorStoresResource,
- VectorStoresResourceWithRawResponse,
- AsyncVectorStoresResourceWithRawResponse,
- VectorStoresResourceWithStreamingResponse,
- AsyncVectorStoresResourceWithStreamingResponse,
+from .stores import (
+ StoresResource,
+ AsyncStoresResource,
+ StoresResourceWithRawResponse,
+ AsyncStoresResourceWithRawResponse,
+ StoresResourceWithStreamingResponse,
+ AsyncStoresResourceWithStreamingResponse,
)
__all__ = [
@@ -24,10 +24,10 @@
"AsyncFilesResourceWithRawResponse",
"FilesResourceWithStreamingResponse",
"AsyncFilesResourceWithStreamingResponse",
- "VectorStoresResource",
- "AsyncVectorStoresResource",
- "VectorStoresResourceWithRawResponse",
- "AsyncVectorStoresResourceWithRawResponse",
- "VectorStoresResourceWithStreamingResponse",
- "AsyncVectorStoresResourceWithStreamingResponse",
+ "StoresResource",
+ "AsyncStoresResource",
+ "StoresResourceWithRawResponse",
+ "AsyncStoresResourceWithRawResponse",
+ "StoresResourceWithStreamingResponse",
+ "AsyncStoresResourceWithStreamingResponse",
]
diff --git a/src/mixedbread/resources/stores/files.py b/src/mixedbread/resources/stores/files.py
new file mode 100644
index 00000000..87b884a9
--- /dev/null
+++ b/src/mixedbread/resources/stores/files.py
@@ -0,0 +1,891 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, List, Union, Iterable, Optional
+
+import httpx
+
+from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
+from ..._utils import maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ..._base_client import make_request_options
+from ...types.stores import (
+ file_list_params,
+ file_create_params,
+ file_search_params,
+ file_update_params,
+ file_retrieve_params,
+)
+from ...types.stores.store_file import StoreFile
+from ...types.stores.store_file_status import StoreFileStatus
+from ...types.stores.file_list_response import FileListResponse
+from ...types.stores.file_delete_response import FileDeleteResponse
+from ...types.stores.file_search_response import FileSearchResponse
+
+__all__ = ["FilesResource", "AsyncFilesResource"]
+
+
+class FilesResource(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> FilesResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
+ """
+ return FilesResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> FilesResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
+ """
+ return FilesResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ store_identifier: str,
+ *,
+ metadata: object | Omit = omit,
+ config: file_create_params.Config | Omit = omit,
+ external_id: Optional[str] | Omit = omit,
+ overwrite: bool | Omit = omit,
+ file_id: str,
+ experimental: Optional[file_create_params.Experimental] | Omit = omit,
+ # 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,
+ ) -> StoreFile:
+ """Upload a file to a store.
+
+ Args: store_identifier: The ID or name of the store.
+
+ file_add_params: The file
+ to add to the store.
+
+ Returns: VectorStoreFile: The uploaded file details.
+
+ Args:
+ store_identifier: The ID or name of the store
+
+ metadata: Optional metadata for the file
+
+ config: Configuration for adding the file
+
+ external_id: External identifier for this file in the store
+
+ overwrite: If true, overwrite an existing file with the same external_id
+
+ file_id: ID of the file to add
+
+ experimental: Configuration for a file.
+
+ 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 store_identifier:
+ raise ValueError(f"Expected a non-empty value for `store_identifier` but received {store_identifier!r}")
+ return self._post(
+ f"/v1/stores/{store_identifier}/files",
+ body=maybe_transform(
+ {
+ "metadata": metadata,
+ "config": config,
+ "external_id": external_id,
+ "overwrite": overwrite,
+ "file_id": file_id,
+ "experimental": experimental,
+ },
+ file_create_params.FileCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=StoreFile,
+ )
+
+ def retrieve(
+ self,
+ file_identifier: str,
+ *,
+ store_identifier: str,
+ return_chunks: Union[bool, Iterable[int]] | Omit = omit,
+ # 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,
+ ) -> StoreFile:
+ """Get a file from a store.
+
+ Args: store_identifier: The ID or name of the store.
+
+ file_id: The ID or name of
+ the file. options: Get file options.
+
+ Returns: VectorStoreFile: The file details.
+
+ Args:
+ store_identifier: The ID or name of the store
+
+ file_identifier: The ID or name of the file
+
+ return_chunks: Whether to return the chunks for the file. If a list of integers is provided,
+ only the chunks at the specified indices will be returned.
+
+ 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 store_identifier:
+ raise ValueError(f"Expected a non-empty value for `store_identifier` but received {store_identifier!r}")
+ if not file_identifier:
+ raise ValueError(f"Expected a non-empty value for `file_identifier` but received {file_identifier!r}")
+ return self._get(
+ f"/v1/stores/{store_identifier}/files/{file_identifier}",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform({"return_chunks": return_chunks}, file_retrieve_params.FileRetrieveParams),
+ ),
+ cast_to=StoreFile,
+ )
+
+ def update(
+ self,
+ file_identifier: str,
+ *,
+ store_identifier: str,
+ metadata: Optional[Dict[str, object]] | Omit = omit,
+ # 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,
+ ) -> StoreFile:
+ """
+ Update metadata on a file within a store.
+
+ Args: store_identifier: The ID or name of the store. file_identifier: The ID or
+ name of the file to update. update_params: Metadata update payload.
+
+ Returns: StoreFile: The updated file details.
+
+ Args:
+ store_identifier: The ID or name of the store
+
+ file_identifier: The ID or name of the file to update
+
+ metadata: Updated metadata for the file
+
+ 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 store_identifier:
+ raise ValueError(f"Expected a non-empty value for `store_identifier` but received {store_identifier!r}")
+ if not file_identifier:
+ raise ValueError(f"Expected a non-empty value for `file_identifier` but received {file_identifier!r}")
+ return self._patch(
+ f"/v1/stores/{store_identifier}/files/{file_identifier}",
+ body=maybe_transform({"metadata": metadata}, file_update_params.FileUpdateParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=StoreFile,
+ )
+
+ def list(
+ self,
+ store_identifier: str,
+ *,
+ limit: int | Omit = omit,
+ after: Optional[str] | Omit = omit,
+ before: Optional[str] | Omit = omit,
+ include_total: bool | Omit = omit,
+ statuses: Optional[List[StoreFileStatus]] | Omit = omit,
+ metadata_filter: Optional[file_list_params.MetadataFilter] | Omit = omit,
+ q: Optional[str] | Omit = omit,
+ # 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:
+ store_identifier: The ID or name of the 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
+
+ q: Search query for fuzzy matching over name and external_id fields
+
+ 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 store_identifier:
+ raise ValueError(f"Expected a non-empty value for `store_identifier` but received {store_identifier!r}")
+ return self._post(
+ f"/v1/stores/{store_identifier}/files/list",
+ body=maybe_transform(
+ {
+ "limit": limit,
+ "after": after,
+ "before": before,
+ "include_total": include_total,
+ "statuses": statuses,
+ "metadata_filter": metadata_filter,
+ "q": q,
+ },
+ 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_identifier: str,
+ *,
+ store_identifier: 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,
+ ) -> FileDeleteResponse:
+ """Delete a file from a store.
+
+ Args: store_identifier: The ID or name of the store.
+
+ file_id: The ID or name of
+ the file to delete.
+
+ Returns: VectorStoreFileDeleted: The deleted file details.
+
+ Args:
+ store_identifier: The ID or name of the store
+
+ file_identifier: The ID or name of the file to delete
+
+ 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 store_identifier:
+ raise ValueError(f"Expected a non-empty value for `store_identifier` but received {store_identifier!r}")
+ if not file_identifier:
+ raise ValueError(f"Expected a non-empty value for `file_identifier` but received {file_identifier!r}")
+ return self._delete(
+ f"/v1/stores/{store_identifier}/files/{file_identifier}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=FileDeleteResponse,
+ )
+
+ def search(
+ self,
+ *,
+ query: file_search_params.Query,
+ store_identifiers: SequenceNotStr[str],
+ top_k: int | Omit = omit,
+ filters: Optional[file_search_params.Filters] | Omit = omit,
+ file_ids: Union[Iterable[object], SequenceNotStr[str], None] | Omit = omit,
+ search_options: file_search_params.SearchOptions | Omit = omit,
+ # 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,
+ ) -> FileSearchResponse:
+ """
+ Search for files within a store based on semantic similarity.
+
+ Args: store_identifier: The ID or name of the store to search within
+ search_params: Search configuration including query text, pagination, and
+ filters
+
+ Returns: StoreFileSearchResponse: List of matching files with relevance scores
+
+ Args:
+ query: Search query text
+
+ store_identifiers: IDs or names of stores to search
+
+ top_k: Number of results to return
+
+ filters: Optional filter conditions
+
+ file_ids: Optional list of file IDs to filter chunks by (inclusion filter)
+
+ search_options: Search configuration options
+
+ 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
+ """
+ return self._post(
+ "/v1/stores/files/search",
+ body=maybe_transform(
+ {
+ "query": query,
+ "store_identifiers": store_identifiers,
+ "top_k": top_k,
+ "filters": filters,
+ "file_ids": file_ids,
+ "search_options": search_options,
+ },
+ file_search_params.FileSearchParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=FileSearchResponse,
+ )
+
+
+class AsyncFilesResource(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncFilesResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncFilesResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncFilesResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
+ """
+ return AsyncFilesResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ store_identifier: str,
+ *,
+ metadata: object | Omit = omit,
+ config: file_create_params.Config | Omit = omit,
+ external_id: Optional[str] | Omit = omit,
+ overwrite: bool | Omit = omit,
+ file_id: str,
+ experimental: Optional[file_create_params.Experimental] | Omit = omit,
+ # 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,
+ ) -> StoreFile:
+ """Upload a file to a store.
+
+ Args: store_identifier: The ID or name of the store.
+
+ file_add_params: The file
+ to add to the store.
+
+ Returns: VectorStoreFile: The uploaded file details.
+
+ Args:
+ store_identifier: The ID or name of the store
+
+ metadata: Optional metadata for the file
+
+ config: Configuration for adding the file
+
+ external_id: External identifier for this file in the store
+
+ overwrite: If true, overwrite an existing file with the same external_id
+
+ file_id: ID of the file to add
+
+ experimental: Configuration for a file.
+
+ 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 store_identifier:
+ raise ValueError(f"Expected a non-empty value for `store_identifier` but received {store_identifier!r}")
+ return await self._post(
+ f"/v1/stores/{store_identifier}/files",
+ body=await async_maybe_transform(
+ {
+ "metadata": metadata,
+ "config": config,
+ "external_id": external_id,
+ "overwrite": overwrite,
+ "file_id": file_id,
+ "experimental": experimental,
+ },
+ file_create_params.FileCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=StoreFile,
+ )
+
+ async def retrieve(
+ self,
+ file_identifier: str,
+ *,
+ store_identifier: str,
+ return_chunks: Union[bool, Iterable[int]] | Omit = omit,
+ # 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,
+ ) -> StoreFile:
+ """Get a file from a store.
+
+ Args: store_identifier: The ID or name of the store.
+
+ file_id: The ID or name of
+ the file. options: Get file options.
+
+ Returns: VectorStoreFile: The file details.
+
+ Args:
+ store_identifier: The ID or name of the store
+
+ file_identifier: The ID or name of the file
+
+ return_chunks: Whether to return the chunks for the file. If a list of integers is provided,
+ only the chunks at the specified indices will be returned.
+
+ 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 store_identifier:
+ raise ValueError(f"Expected a non-empty value for `store_identifier` but received {store_identifier!r}")
+ if not file_identifier:
+ raise ValueError(f"Expected a non-empty value for `file_identifier` but received {file_identifier!r}")
+ return await self._get(
+ f"/v1/stores/{store_identifier}/files/{file_identifier}",
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=await async_maybe_transform(
+ {"return_chunks": return_chunks}, file_retrieve_params.FileRetrieveParams
+ ),
+ ),
+ cast_to=StoreFile,
+ )
+
+ async def update(
+ self,
+ file_identifier: str,
+ *,
+ store_identifier: str,
+ metadata: Optional[Dict[str, object]] | Omit = omit,
+ # 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,
+ ) -> StoreFile:
+ """
+ Update metadata on a file within a store.
+
+ Args: store_identifier: The ID or name of the store. file_identifier: The ID or
+ name of the file to update. update_params: Metadata update payload.
+
+ Returns: StoreFile: The updated file details.
+
+ Args:
+ store_identifier: The ID or name of the store
+
+ file_identifier: The ID or name of the file to update
+
+ metadata: Updated metadata for the file
+
+ 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 store_identifier:
+ raise ValueError(f"Expected a non-empty value for `store_identifier` but received {store_identifier!r}")
+ if not file_identifier:
+ raise ValueError(f"Expected a non-empty value for `file_identifier` but received {file_identifier!r}")
+ return await self._patch(
+ f"/v1/stores/{store_identifier}/files/{file_identifier}",
+ body=await async_maybe_transform({"metadata": metadata}, file_update_params.FileUpdateParams),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=StoreFile,
+ )
+
+ async def list(
+ self,
+ store_identifier: str,
+ *,
+ limit: int | Omit = omit,
+ after: Optional[str] | Omit = omit,
+ before: Optional[str] | Omit = omit,
+ include_total: bool | Omit = omit,
+ statuses: Optional[List[StoreFileStatus]] | Omit = omit,
+ metadata_filter: Optional[file_list_params.MetadataFilter] | Omit = omit,
+ q: Optional[str] | Omit = omit,
+ # 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:
+ store_identifier: The ID or name of the 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
+
+ q: Search query for fuzzy matching over name and external_id fields
+
+ 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 store_identifier:
+ raise ValueError(f"Expected a non-empty value for `store_identifier` but received {store_identifier!r}")
+ return await self._post(
+ f"/v1/stores/{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,
+ "q": q,
+ },
+ 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_identifier: str,
+ *,
+ store_identifier: 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,
+ ) -> FileDeleteResponse:
+ """Delete a file from a store.
+
+ Args: store_identifier: The ID or name of the store.
+
+ file_id: The ID or name of
+ the file to delete.
+
+ Returns: VectorStoreFileDeleted: The deleted file details.
+
+ Args:
+ store_identifier: The ID or name of the store
+
+ file_identifier: The ID or name of the file to delete
+
+ 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 store_identifier:
+ raise ValueError(f"Expected a non-empty value for `store_identifier` but received {store_identifier!r}")
+ if not file_identifier:
+ raise ValueError(f"Expected a non-empty value for `file_identifier` but received {file_identifier!r}")
+ return await self._delete(
+ f"/v1/stores/{store_identifier}/files/{file_identifier}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=FileDeleteResponse,
+ )
+
+ async def search(
+ self,
+ *,
+ query: file_search_params.Query,
+ store_identifiers: SequenceNotStr[str],
+ top_k: int | Omit = omit,
+ filters: Optional[file_search_params.Filters] | Omit = omit,
+ file_ids: Union[Iterable[object], SequenceNotStr[str], None] | Omit = omit,
+ search_options: file_search_params.SearchOptions | Omit = omit,
+ # 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,
+ ) -> FileSearchResponse:
+ """
+ Search for files within a store based on semantic similarity.
+
+ Args: store_identifier: The ID or name of the store to search within
+ search_params: Search configuration including query text, pagination, and
+ filters
+
+ Returns: StoreFileSearchResponse: List of matching files with relevance scores
+
+ Args:
+ query: Search query text
+
+ store_identifiers: IDs or names of stores to search
+
+ top_k: Number of results to return
+
+ filters: Optional filter conditions
+
+ file_ids: Optional list of file IDs to filter chunks by (inclusion filter)
+
+ search_options: Search configuration options
+
+ 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
+ """
+ return await self._post(
+ "/v1/stores/files/search",
+ body=await async_maybe_transform(
+ {
+ "query": query,
+ "store_identifiers": store_identifiers,
+ "top_k": top_k,
+ "filters": filters,
+ "file_ids": file_ids,
+ "search_options": search_options,
+ },
+ file_search_params.FileSearchParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=FileSearchResponse,
+ )
+
+
+class FilesResourceWithRawResponse:
+ def __init__(self, files: FilesResource) -> None:
+ self._files = files
+
+ self.create = to_raw_response_wrapper(
+ files.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ files.retrieve,
+ )
+ self.update = to_raw_response_wrapper(
+ files.update,
+ )
+ self.list = to_raw_response_wrapper(
+ files.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ files.delete,
+ )
+ self.search = to_raw_response_wrapper(
+ files.search,
+ )
+
+
+class AsyncFilesResourceWithRawResponse:
+ def __init__(self, files: AsyncFilesResource) -> None:
+ self._files = files
+
+ self.create = async_to_raw_response_wrapper(
+ files.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ files.retrieve,
+ )
+ self.update = async_to_raw_response_wrapper(
+ files.update,
+ )
+ self.list = async_to_raw_response_wrapper(
+ files.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ files.delete,
+ )
+ self.search = async_to_raw_response_wrapper(
+ files.search,
+ )
+
+
+class FilesResourceWithStreamingResponse:
+ def __init__(self, files: FilesResource) -> None:
+ self._files = files
+
+ self.create = to_streamed_response_wrapper(
+ files.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ files.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ files.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ files.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ files.delete,
+ )
+ self.search = to_streamed_response_wrapper(
+ files.search,
+ )
+
+
+class AsyncFilesResourceWithStreamingResponse:
+ def __init__(self, files: AsyncFilesResource) -> None:
+ self._files = files
+
+ self.create = async_to_streamed_response_wrapper(
+ files.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ files.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ files.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ files.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ files.delete,
+ )
+ self.search = async_to_streamed_response_wrapper(
+ files.search,
+ )
diff --git a/src/mixedbread/resources/stores/stores.py b/src/mixedbread/resources/stores/stores.py
new file mode 100644
index 00000000..41eac718
--- /dev/null
+++ b/src/mixedbread/resources/stores/stores.py
@@ -0,0 +1,1213 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable, Optional
+
+import httpx
+
+from .files import (
+ FilesResource,
+ AsyncFilesResource,
+ FilesResourceWithRawResponse,
+ AsyncFilesResourceWithRawResponse,
+ FilesResourceWithStreamingResponse,
+ AsyncFilesResourceWithStreamingResponse,
+)
+from ...types import (
+ store_list_params,
+ store_create_params,
+ store_search_params,
+ store_update_params,
+ store_metadata_facets_params,
+ store_question_answering_params,
+)
+from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given
+from ..._utils import maybe_transform, async_maybe_transform
+from ..._compat import cached_property
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import (
+ to_raw_response_wrapper,
+ to_streamed_response_wrapper,
+ async_to_raw_response_wrapper,
+ async_to_streamed_response_wrapper,
+)
+from ...pagination import SyncCursor, AsyncCursor
+from ...types.store import Store
+from ..._base_client import AsyncPaginator, make_request_options
+from ...types.expires_after_param import ExpiresAfterParam
+from ...types.store_delete_response import StoreDeleteResponse
+from ...types.store_search_response import StoreSearchResponse
+from ...types.store_metadata_facets_response import StoreMetadataFacetsResponse
+from ...types.store_chunk_search_options_param import StoreChunkSearchOptionsParam
+from ...types.store_question_answering_response import StoreQuestionAnsweringResponse
+
+__all__ = ["StoresResource", "AsyncStoresResource"]
+
+
+class StoresResource(SyncAPIResource):
+ @cached_property
+ def files(self) -> FilesResource:
+ return FilesResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> StoresResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
+ """
+ return StoresResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> StoresResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
+ """
+ return StoresResourceWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ name: Optional[str] | Omit = omit,
+ description: Optional[str] | Omit = omit,
+ is_public: bool | Omit = omit,
+ expires_after: Optional[ExpiresAfterParam] | Omit = omit,
+ metadata: object | Omit = omit,
+ config: Optional[store_create_params.Config] | Omit = omit,
+ file_ids: Optional[SequenceNotStr[str]] | Omit = omit,
+ # 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,
+ ) -> Store:
+ """
+ Create a new vector store.
+
+ Args: vector_store_create: VectorStoreCreate object containing the name,
+ description, and metadata.
+
+ Returns: VectorStore: The response containing the created vector store details.
+
+ Args:
+ name: Name for the new store. Can only contain lowercase letters, numbers, periods
+ (.), and hyphens (-).
+
+ description: Description of the store
+
+ is_public: Whether the store can be accessed by anyone with valid login credentials
+
+ expires_after: Represents an expiration policy for a store.
+
+ metadata: Optional metadata key-value pairs
+
+ config: Configuration for a store.
+
+ file_ids: Optional list of file IDs
+
+ 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
+ """
+ return self._post(
+ "/v1/stores",
+ body=maybe_transform(
+ {
+ "name": name,
+ "description": description,
+ "is_public": is_public,
+ "expires_after": expires_after,
+ "metadata": metadata,
+ "config": config,
+ "file_ids": file_ids,
+ },
+ store_create_params.StoreCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Store,
+ )
+
+ def retrieve(
+ self,
+ store_identifier: 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,
+ ) -> Store:
+ """
+ Get a store by ID or name.
+
+ Args: store_identifier: The ID or name of the store to retrieve.
+
+ Returns: Store: The response containing the store details.
+
+ Args:
+ store_identifier: The ID or name of the store
+
+ 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 store_identifier:
+ raise ValueError(f"Expected a non-empty value for `store_identifier` but received {store_identifier!r}")
+ return self._get(
+ f"/v1/stores/{store_identifier}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Store,
+ )
+
+ def update(
+ self,
+ store_identifier: str,
+ *,
+ name: Optional[str] | Omit = omit,
+ description: Optional[str] | Omit = omit,
+ is_public: Optional[bool] | Omit = omit,
+ expires_after: Optional[ExpiresAfterParam] | Omit = omit,
+ metadata: object | Omit = omit,
+ # 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,
+ ) -> Store:
+ """
+ Update a store by ID or name.
+
+ Args: store_identifier: The ID or name of the store to update. store_update:
+ StoreCreate object containing the name, description, and metadata.
+
+ Returns: Store: The response containing the updated store details.
+
+ Args:
+ store_identifier: The ID or name of the store
+
+ name: New name for the store. Can only contain lowercase letters, numbers, periods
+ (.), and hyphens (-).
+
+ description: New description
+
+ is_public: Whether the store can be accessed by anyone with valid login credentials
+
+ expires_after: Represents an expiration policy for a store.
+
+ metadata: Optional metadata key-value pairs
+
+ 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 store_identifier:
+ raise ValueError(f"Expected a non-empty value for `store_identifier` but received {store_identifier!r}")
+ return self._put(
+ f"/v1/stores/{store_identifier}",
+ body=maybe_transform(
+ {
+ "name": name,
+ "description": description,
+ "is_public": is_public,
+ "expires_after": expires_after,
+ "metadata": metadata,
+ },
+ store_update_params.StoreUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Store,
+ )
+
+ def list(
+ self,
+ *,
+ limit: int | Omit = omit,
+ after: Optional[str] | Omit = omit,
+ before: Optional[str] | Omit = omit,
+ include_total: bool | Omit = omit,
+ q: Optional[str] | Omit = omit,
+ # 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,
+ ) -> SyncCursor[Store]:
+ """List all stores with optional search.
+
+ Args: pagination: The pagination options.
+
+ q: Optional search query to filter
+ vector stores.
+
+ Returns: StoreListResponse: The list of stores.
+
+ Args:
+ 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)
+
+ q: Search query for fuzzy matching over name and description fields
+
+ 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
+ """
+ return self._get_api_list(
+ "/v1/stores",
+ page=SyncCursor[Store],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "limit": limit,
+ "after": after,
+ "before": before,
+ "include_total": include_total,
+ "q": q,
+ },
+ store_list_params.StoreListParams,
+ ),
+ ),
+ model=Store,
+ )
+
+ def delete(
+ self,
+ store_identifier: 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,
+ ) -> StoreDeleteResponse:
+ """
+ Delete a store by ID or name.
+
+ Args: store_identifier: The ID or name of the store to delete.
+
+ Returns: Store: The response containing the deleted store details.
+
+ Args:
+ store_identifier: The ID or name of the store to delete
+
+ 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 store_identifier:
+ raise ValueError(f"Expected a non-empty value for `store_identifier` but received {store_identifier!r}")
+ return self._delete(
+ f"/v1/stores/{store_identifier}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=StoreDeleteResponse,
+ )
+
+ def metadata_facets(
+ self,
+ *,
+ query: Optional[str] | Omit = omit,
+ store_identifiers: SequenceNotStr[str],
+ top_k: int | Omit = omit,
+ filters: Optional[store_metadata_facets_params.Filters] | Omit = omit,
+ file_ids: Union[Iterable[object], SequenceNotStr[str], None] | Omit = omit,
+ search_options: StoreChunkSearchOptionsParam | Omit = omit,
+ facets: Optional[SequenceNotStr[str]] | Omit = omit,
+ # 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,
+ ) -> StoreMetadataFacetsResponse:
+ """
+ Get metadata facets
+
+ Args:
+ query: Search query text
+
+ store_identifiers: IDs or names of stores to search
+
+ top_k: Number of results to return
+
+ filters: Optional filter conditions
+
+ file_ids: Optional list of file IDs to filter chunks by (inclusion filter)
+
+ search_options: Search configuration options
+
+ facets: Optional list of facets to return. Use dot for nested fields.
+
+ 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
+ """
+ return self._post(
+ "/v1/stores/metadata-facets",
+ body=maybe_transform(
+ {
+ "query": query,
+ "store_identifiers": store_identifiers,
+ "top_k": top_k,
+ "filters": filters,
+ "file_ids": file_ids,
+ "search_options": search_options,
+ "facets": facets,
+ },
+ store_metadata_facets_params.StoreMetadataFacetsParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=StoreMetadataFacetsResponse,
+ )
+
+ def question_answering(
+ self,
+ *,
+ query: str | Omit = omit,
+ store_identifiers: SequenceNotStr[str],
+ top_k: int | Omit = omit,
+ filters: Optional[store_question_answering_params.Filters] | Omit = omit,
+ file_ids: Union[Iterable[object], SequenceNotStr[str], None] | Omit = omit,
+ search_options: StoreChunkSearchOptionsParam | Omit = omit,
+ stream: bool | Omit = omit,
+ qa_options: store_question_answering_params.QaOptions | Omit = omit,
+ # 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,
+ ) -> StoreQuestionAnsweringResponse:
+ """Question answering
+
+ Args:
+ query: Question to answer.
+
+ If not provided, the question will be extracted from the
+ passed messages.
+
+ store_identifiers: IDs or names of stores to search
+
+ top_k: Number of results to return
+
+ filters: Optional filter conditions
+
+ file_ids: Optional list of file IDs to filter chunks by (inclusion filter)
+
+ search_options: Search configuration options
+
+ stream: Whether to stream the answer
+
+ qa_options: Question answering configuration options
+
+ 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
+ """
+ return self._post(
+ "/v1/stores/question-answering",
+ body=maybe_transform(
+ {
+ "query": query,
+ "store_identifiers": store_identifiers,
+ "top_k": top_k,
+ "filters": filters,
+ "file_ids": file_ids,
+ "search_options": search_options,
+ "stream": stream,
+ "qa_options": qa_options,
+ },
+ store_question_answering_params.StoreQuestionAnsweringParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=StoreQuestionAnsweringResponse,
+ )
+
+ def search(
+ self,
+ *,
+ query: store_search_params.Query,
+ store_identifiers: SequenceNotStr[str],
+ top_k: int | Omit = omit,
+ filters: Optional[store_search_params.Filters] | Omit = omit,
+ file_ids: Union[Iterable[object], SequenceNotStr[str], None] | Omit = omit,
+ search_options: StoreChunkSearchOptionsParam | Omit = omit,
+ # 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,
+ ) -> StoreSearchResponse:
+ """
+ Perform semantic search across store chunks.
+
+ This endpoint searches through store chunks using semantic similarity matching.
+ It supports complex search queries with filters and returns relevance-scored
+ results.
+
+ For the special 'mixedbread/web' store, this endpoint performs web search using
+ a mixture of different providers instead of semantic search. Web search results
+ are always reranked for consistent scoring.
+
+ Args: search_params: Search configuration including: - query text or
+ embeddings - store_identifiers: List of store identifiers to search - file_ids:
+ Optional list of file IDs to filter chunks by (or tuple of list and condition
+ operator) - metadata filters - pagination parameters - sorting preferences
+ \\__state: API state dependency \\__ctx: Service context dependency
+
+ Returns: StoreSearchResponse containing: - List of matched chunks with relevance
+ scores - Pagination details including total result count
+
+ Raises: HTTPException (400): If search parameters are invalid HTTPException
+ (404): If no vector stores are found to search
+
+ Args:
+ query: Search query text
+
+ store_identifiers: IDs or names of stores to search
+
+ top_k: Number of results to return
+
+ filters: Optional filter conditions
+
+ file_ids: Optional list of file IDs to filter chunks by (inclusion filter)
+
+ search_options: Search configuration options
+
+ 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
+ """
+ return self._post(
+ "/v1/stores/search",
+ body=maybe_transform(
+ {
+ "query": query,
+ "store_identifiers": store_identifiers,
+ "top_k": top_k,
+ "filters": filters,
+ "file_ids": file_ids,
+ "search_options": search_options,
+ },
+ store_search_params.StoreSearchParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=StoreSearchResponse,
+ )
+
+
+class AsyncStoresResource(AsyncAPIResource):
+ @cached_property
+ def files(self) -> AsyncFilesResource:
+ return AsyncFilesResource(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncStoresResourceWithRawResponse:
+ """
+ This property can be used as a prefix for any HTTP method call to return
+ the raw response object instead of the parsed content.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#accessing-raw-response-data-eg-headers
+ """
+ return AsyncStoresResourceWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncStoresResourceWithStreamingResponse:
+ """
+ An alternative to `.with_raw_response` that doesn't eagerly read the response body.
+
+ For more information, see https://www.github.com/mixedbread-ai/mixedbread-python#with_streaming_response
+ """
+ return AsyncStoresResourceWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ name: Optional[str] | Omit = omit,
+ description: Optional[str] | Omit = omit,
+ is_public: bool | Omit = omit,
+ expires_after: Optional[ExpiresAfterParam] | Omit = omit,
+ metadata: object | Omit = omit,
+ config: Optional[store_create_params.Config] | Omit = omit,
+ file_ids: Optional[SequenceNotStr[str]] | Omit = omit,
+ # 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,
+ ) -> Store:
+ """
+ Create a new vector store.
+
+ Args: vector_store_create: VectorStoreCreate object containing the name,
+ description, and metadata.
+
+ Returns: VectorStore: The response containing the created vector store details.
+
+ Args:
+ name: Name for the new store. Can only contain lowercase letters, numbers, periods
+ (.), and hyphens (-).
+
+ description: Description of the store
+
+ is_public: Whether the store can be accessed by anyone with valid login credentials
+
+ expires_after: Represents an expiration policy for a store.
+
+ metadata: Optional metadata key-value pairs
+
+ config: Configuration for a store.
+
+ file_ids: Optional list of file IDs
+
+ 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
+ """
+ return await self._post(
+ "/v1/stores",
+ body=await async_maybe_transform(
+ {
+ "name": name,
+ "description": description,
+ "is_public": is_public,
+ "expires_after": expires_after,
+ "metadata": metadata,
+ "config": config,
+ "file_ids": file_ids,
+ },
+ store_create_params.StoreCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Store,
+ )
+
+ async def retrieve(
+ self,
+ store_identifier: 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,
+ ) -> Store:
+ """
+ Get a store by ID or name.
+
+ Args: store_identifier: The ID or name of the store to retrieve.
+
+ Returns: Store: The response containing the store details.
+
+ Args:
+ store_identifier: The ID or name of the store
+
+ 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 store_identifier:
+ raise ValueError(f"Expected a non-empty value for `store_identifier` but received {store_identifier!r}")
+ return await self._get(
+ f"/v1/stores/{store_identifier}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Store,
+ )
+
+ async def update(
+ self,
+ store_identifier: str,
+ *,
+ name: Optional[str] | Omit = omit,
+ description: Optional[str] | Omit = omit,
+ is_public: Optional[bool] | Omit = omit,
+ expires_after: Optional[ExpiresAfterParam] | Omit = omit,
+ metadata: object | Omit = omit,
+ # 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,
+ ) -> Store:
+ """
+ Update a store by ID or name.
+
+ Args: store_identifier: The ID or name of the store to update. store_update:
+ StoreCreate object containing the name, description, and metadata.
+
+ Returns: Store: The response containing the updated store details.
+
+ Args:
+ store_identifier: The ID or name of the store
+
+ name: New name for the store. Can only contain lowercase letters, numbers, periods
+ (.), and hyphens (-).
+
+ description: New description
+
+ is_public: Whether the store can be accessed by anyone with valid login credentials
+
+ expires_after: Represents an expiration policy for a store.
+
+ metadata: Optional metadata key-value pairs
+
+ 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 store_identifier:
+ raise ValueError(f"Expected a non-empty value for `store_identifier` but received {store_identifier!r}")
+ return await self._put(
+ f"/v1/stores/{store_identifier}",
+ body=await async_maybe_transform(
+ {
+ "name": name,
+ "description": description,
+ "is_public": is_public,
+ "expires_after": expires_after,
+ "metadata": metadata,
+ },
+ store_update_params.StoreUpdateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Store,
+ )
+
+ def list(
+ self,
+ *,
+ limit: int | Omit = omit,
+ after: Optional[str] | Omit = omit,
+ before: Optional[str] | Omit = omit,
+ include_total: bool | Omit = omit,
+ q: Optional[str] | Omit = omit,
+ # 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,
+ ) -> AsyncPaginator[Store, AsyncCursor[Store]]:
+ """List all stores with optional search.
+
+ Args: pagination: The pagination options.
+
+ q: Optional search query to filter
+ vector stores.
+
+ Returns: StoreListResponse: The list of stores.
+
+ Args:
+ 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)
+
+ q: Search query for fuzzy matching over name and description fields
+
+ 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
+ """
+ return self._get_api_list(
+ "/v1/stores",
+ page=AsyncCursor[Store],
+ options=make_request_options(
+ extra_headers=extra_headers,
+ extra_query=extra_query,
+ extra_body=extra_body,
+ timeout=timeout,
+ query=maybe_transform(
+ {
+ "limit": limit,
+ "after": after,
+ "before": before,
+ "include_total": include_total,
+ "q": q,
+ },
+ store_list_params.StoreListParams,
+ ),
+ ),
+ model=Store,
+ )
+
+ async def delete(
+ self,
+ store_identifier: 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,
+ ) -> StoreDeleteResponse:
+ """
+ Delete a store by ID or name.
+
+ Args: store_identifier: The ID or name of the store to delete.
+
+ Returns: Store: The response containing the deleted store details.
+
+ Args:
+ store_identifier: The ID or name of the store to delete
+
+ 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 store_identifier:
+ raise ValueError(f"Expected a non-empty value for `store_identifier` but received {store_identifier!r}")
+ return await self._delete(
+ f"/v1/stores/{store_identifier}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=StoreDeleteResponse,
+ )
+
+ async def metadata_facets(
+ self,
+ *,
+ query: Optional[str] | Omit = omit,
+ store_identifiers: SequenceNotStr[str],
+ top_k: int | Omit = omit,
+ filters: Optional[store_metadata_facets_params.Filters] | Omit = omit,
+ file_ids: Union[Iterable[object], SequenceNotStr[str], None] | Omit = omit,
+ search_options: StoreChunkSearchOptionsParam | Omit = omit,
+ facets: Optional[SequenceNotStr[str]] | Omit = omit,
+ # 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,
+ ) -> StoreMetadataFacetsResponse:
+ """
+ Get metadata facets
+
+ Args:
+ query: Search query text
+
+ store_identifiers: IDs or names of stores to search
+
+ top_k: Number of results to return
+
+ filters: Optional filter conditions
+
+ file_ids: Optional list of file IDs to filter chunks by (inclusion filter)
+
+ search_options: Search configuration options
+
+ facets: Optional list of facets to return. Use dot for nested fields.
+
+ 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
+ """
+ return await self._post(
+ "/v1/stores/metadata-facets",
+ body=await async_maybe_transform(
+ {
+ "query": query,
+ "store_identifiers": store_identifiers,
+ "top_k": top_k,
+ "filters": filters,
+ "file_ids": file_ids,
+ "search_options": search_options,
+ "facets": facets,
+ },
+ store_metadata_facets_params.StoreMetadataFacetsParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=StoreMetadataFacetsResponse,
+ )
+
+ async def question_answering(
+ self,
+ *,
+ query: str | Omit = omit,
+ store_identifiers: SequenceNotStr[str],
+ top_k: int | Omit = omit,
+ filters: Optional[store_question_answering_params.Filters] | Omit = omit,
+ file_ids: Union[Iterable[object], SequenceNotStr[str], None] | Omit = omit,
+ search_options: StoreChunkSearchOptionsParam | Omit = omit,
+ stream: bool | Omit = omit,
+ qa_options: store_question_answering_params.QaOptions | Omit = omit,
+ # 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,
+ ) -> StoreQuestionAnsweringResponse:
+ """Question answering
+
+ Args:
+ query: Question to answer.
+
+ If not provided, the question will be extracted from the
+ passed messages.
+
+ store_identifiers: IDs or names of stores to search
+
+ top_k: Number of results to return
+
+ filters: Optional filter conditions
+
+ file_ids: Optional list of file IDs to filter chunks by (inclusion filter)
+
+ search_options: Search configuration options
+
+ stream: Whether to stream the answer
+
+ qa_options: Question answering configuration options
+
+ 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
+ """
+ return await self._post(
+ "/v1/stores/question-answering",
+ body=await async_maybe_transform(
+ {
+ "query": query,
+ "store_identifiers": store_identifiers,
+ "top_k": top_k,
+ "filters": filters,
+ "file_ids": file_ids,
+ "search_options": search_options,
+ "stream": stream,
+ "qa_options": qa_options,
+ },
+ store_question_answering_params.StoreQuestionAnsweringParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=StoreQuestionAnsweringResponse,
+ )
+
+ async def search(
+ self,
+ *,
+ query: store_search_params.Query,
+ store_identifiers: SequenceNotStr[str],
+ top_k: int | Omit = omit,
+ filters: Optional[store_search_params.Filters] | Omit = omit,
+ file_ids: Union[Iterable[object], SequenceNotStr[str], None] | Omit = omit,
+ search_options: StoreChunkSearchOptionsParam | Omit = omit,
+ # 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,
+ ) -> StoreSearchResponse:
+ """
+ Perform semantic search across store chunks.
+
+ This endpoint searches through store chunks using semantic similarity matching.
+ It supports complex search queries with filters and returns relevance-scored
+ results.
+
+ For the special 'mixedbread/web' store, this endpoint performs web search using
+ a mixture of different providers instead of semantic search. Web search results
+ are always reranked for consistent scoring.
+
+ Args: search_params: Search configuration including: - query text or
+ embeddings - store_identifiers: List of store identifiers to search - file_ids:
+ Optional list of file IDs to filter chunks by (or tuple of list and condition
+ operator) - metadata filters - pagination parameters - sorting preferences
+ \\__state: API state dependency \\__ctx: Service context dependency
+
+ Returns: StoreSearchResponse containing: - List of matched chunks with relevance
+ scores - Pagination details including total result count
+
+ Raises: HTTPException (400): If search parameters are invalid HTTPException
+ (404): If no vector stores are found to search
+
+ Args:
+ query: Search query text
+
+ store_identifiers: IDs or names of stores to search
+
+ top_k: Number of results to return
+
+ filters: Optional filter conditions
+
+ file_ids: Optional list of file IDs to filter chunks by (inclusion filter)
+
+ search_options: Search configuration options
+
+ 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
+ """
+ return await self._post(
+ "/v1/stores/search",
+ body=await async_maybe_transform(
+ {
+ "query": query,
+ "store_identifiers": store_identifiers,
+ "top_k": top_k,
+ "filters": filters,
+ "file_ids": file_ids,
+ "search_options": search_options,
+ },
+ store_search_params.StoreSearchParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=StoreSearchResponse,
+ )
+
+
+class StoresResourceWithRawResponse:
+ def __init__(self, stores: StoresResource) -> None:
+ self._stores = stores
+
+ self.create = to_raw_response_wrapper(
+ stores.create,
+ )
+ self.retrieve = to_raw_response_wrapper(
+ stores.retrieve,
+ )
+ self.update = to_raw_response_wrapper(
+ stores.update,
+ )
+ self.list = to_raw_response_wrapper(
+ stores.list,
+ )
+ self.delete = to_raw_response_wrapper(
+ stores.delete,
+ )
+ self.metadata_facets = to_raw_response_wrapper(
+ stores.metadata_facets,
+ )
+ self.question_answering = to_raw_response_wrapper(
+ stores.question_answering,
+ )
+ self.search = to_raw_response_wrapper(
+ stores.search,
+ )
+
+ @cached_property
+ def files(self) -> FilesResourceWithRawResponse:
+ return FilesResourceWithRawResponse(self._stores.files)
+
+
+class AsyncStoresResourceWithRawResponse:
+ def __init__(self, stores: AsyncStoresResource) -> None:
+ self._stores = stores
+
+ self.create = async_to_raw_response_wrapper(
+ stores.create,
+ )
+ self.retrieve = async_to_raw_response_wrapper(
+ stores.retrieve,
+ )
+ self.update = async_to_raw_response_wrapper(
+ stores.update,
+ )
+ self.list = async_to_raw_response_wrapper(
+ stores.list,
+ )
+ self.delete = async_to_raw_response_wrapper(
+ stores.delete,
+ )
+ self.metadata_facets = async_to_raw_response_wrapper(
+ stores.metadata_facets,
+ )
+ self.question_answering = async_to_raw_response_wrapper(
+ stores.question_answering,
+ )
+ self.search = async_to_raw_response_wrapper(
+ stores.search,
+ )
+
+ @cached_property
+ def files(self) -> AsyncFilesResourceWithRawResponse:
+ return AsyncFilesResourceWithRawResponse(self._stores.files)
+
+
+class StoresResourceWithStreamingResponse:
+ def __init__(self, stores: StoresResource) -> None:
+ self._stores = stores
+
+ self.create = to_streamed_response_wrapper(
+ stores.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ stores.retrieve,
+ )
+ self.update = to_streamed_response_wrapper(
+ stores.update,
+ )
+ self.list = to_streamed_response_wrapper(
+ stores.list,
+ )
+ self.delete = to_streamed_response_wrapper(
+ stores.delete,
+ )
+ self.metadata_facets = to_streamed_response_wrapper(
+ stores.metadata_facets,
+ )
+ self.question_answering = to_streamed_response_wrapper(
+ stores.question_answering,
+ )
+ self.search = to_streamed_response_wrapper(
+ stores.search,
+ )
+
+ @cached_property
+ def files(self) -> FilesResourceWithStreamingResponse:
+ return FilesResourceWithStreamingResponse(self._stores.files)
+
+
+class AsyncStoresResourceWithStreamingResponse:
+ def __init__(self, stores: AsyncStoresResource) -> None:
+ self._stores = stores
+
+ self.create = async_to_streamed_response_wrapper(
+ stores.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ stores.retrieve,
+ )
+ self.update = async_to_streamed_response_wrapper(
+ stores.update,
+ )
+ self.list = async_to_streamed_response_wrapper(
+ stores.list,
+ )
+ self.delete = async_to_streamed_response_wrapper(
+ stores.delete,
+ )
+ self.metadata_facets = async_to_streamed_response_wrapper(
+ stores.metadata_facets,
+ )
+ self.question_answering = async_to_streamed_response_wrapper(
+ stores.question_answering,
+ )
+ self.search = async_to_streamed_response_wrapper(
+ stores.search,
+ )
+
+ @cached_property
+ def files(self) -> AsyncFilesResourceWithStreamingResponse:
+ return AsyncFilesResourceWithStreamingResponse(self._stores.files)
diff --git a/src/mixedbread/resources/vector_stores/files.py b/src/mixedbread/resources/vector_stores/files.py
deleted file mode 100644
index cc7f699c..00000000
--- a/src/mixedbread/resources/vector_stores/files.py
+++ /dev/null
@@ -1,683 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing import List
-
-import httpx
-
-from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
-from ..._utils import (
- maybe_transform,
- async_maybe_transform,
-)
-from ..._compat import cached_property
-from ..._resource import SyncAPIResource, AsyncAPIResource
-from ..._response import (
- to_raw_response_wrapper,
- to_streamed_response_wrapper,
- async_to_raw_response_wrapper,
- async_to_streamed_response_wrapper,
-)
-from ..._base_client import make_request_options
-from ...types.vector_stores import file_list_params, file_create_params, file_search_params
-from ...types.vector_stores.vector_store_file import VectorStoreFile
-from ...types.vector_stores.file_list_response import FileListResponse
-from ...types.vector_stores.file_search_response import FileSearchResponse
-from ...types.vector_stores.vector_store_file_deleted import VectorStoreFileDeleted
-
-__all__ = ["FilesResource", "AsyncFilesResource"]
-
-
-class FilesResource(SyncAPIResource):
- @cached_property
- def with_raw_response(self) -> FilesResourceWithRawResponse:
- """
- This property can be used as a prefix for any HTTP method call to return the
- the raw response object instead of the parsed content.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
- """
- return FilesResourceWithRawResponse(self)
-
- @cached_property
- def with_streaming_response(self) -> FilesResourceWithStreamingResponse:
- """
- An alternative to `.with_raw_response` that doesn't eagerly read the response body.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
- """
- return FilesResourceWithStreamingResponse(self)
-
- def create(
- self,
- vector_store_id: str,
- *,
- file_id: str,
- metadata: object | 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,
- ) -> VectorStoreFile:
- """
- Upload a new file to a vector store for indexing.
-
- Args: vector_store_id: The ID of the vector store to upload to file: The file to
- upload and index
-
- Returns: VectorStoreFile: Details of the uploaded and indexed file
-
- Args:
- vector_store_id: The ID of the vector store
-
- file_id: ID of the file to add
-
- metadata: Optional metadata for the file
-
- 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_id:
- raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}")
- return self._post(
- f"/v1/vector_stores/{vector_store_id}/files",
- body=maybe_transform(
- {
- "file_id": file_id,
- "metadata": metadata,
- },
- file_create_params.FileCreateParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=VectorStoreFile,
- )
-
- def retrieve(
- self,
- file_id: str,
- *,
- vector_store_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,
- ) -> VectorStoreFile:
- """
- Get details of a specific file in a vector store.
-
- Args: vector_store_id: The ID of the vector store file_id: The ID of the file
-
- Returns: VectorStoreFile: Details of the vector store file
-
- Args:
- vector_store_id: The ID of the vector store
-
- file_id: The ID of the file
-
- 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_id:
- raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}")
- if not file_id:
- raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}")
- return self._get(
- f"/v1/vector_stores/{vector_store_id}/files/{file_id}",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=VectorStoreFile,
- )
-
- def list(
- self,
- vector_store_id: str,
- *,
- limit: int | NotGiven = NOT_GIVEN,
- offset: int | 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.
-
- Args: vector_store_id: The ID of the vector store pagination: Pagination
- parameters
-
- Returns: VectorStoreFileListResponse: Paginated list of vector store files
-
- Args:
- vector_store_id: The ID of the vector store
-
- limit: Maximum number of items to return per page
-
- offset: Offset of the first item to return
-
- 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_id:
- raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}")
- return self._get(
- f"/v1/vector_stores/{vector_store_id}/files",
- options=make_request_options(
- extra_headers=extra_headers,
- extra_query=extra_query,
- extra_body=extra_body,
- timeout=timeout,
- query=maybe_transform(
- {
- "limit": limit,
- "offset": offset,
- },
- file_list_params.FileListParams,
- ),
- ),
- cast_to=FileListResponse,
- )
-
- def delete(
- self,
- file_id: str,
- *,
- vector_store_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,
- ) -> VectorStoreFileDeleted:
- """
- Delete a file from a vector store.
-
- Args: vector_store_id: The ID of the vector store file_id: The ID of the file to
- delete
-
- Returns: VectorStoreFileDeleted: The deleted file
-
- Args:
- vector_store_id: The ID of the vector store
-
- file_id: The ID of the file to delete
-
- 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_id:
- raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}")
- if not file_id:
- raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}")
- return self._delete(
- f"/v1/vector_stores/{vector_store_id}/files/{file_id}",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=VectorStoreFileDeleted,
- )
-
- def search(
- self,
- *,
- query: str,
- vector_store_ids: List[str],
- search_options: file_search_params.SearchOptions | NotGiven = NOT_GIVEN,
- top_k: int | 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,
- ) -> FileSearchResponse:
- """
- Perform semantic search across complete vector store files.
-
- This endpoint searches through vector store files using semantic similarity
- matching. Unlike chunk search, it returns complete matching files rather than
- individual chunks. Supports complex search queries with filters and returns
- relevance-scored results.
-
- Args: search_params: Search configuration including: - query text or
- embeddings - metadata filters - pagination parameters - sorting preferences
- \\__state: API state dependency \\__ctx: Service context dependency
-
- Returns: VectorStoreSearchFileResponse containing: - List of matched files with
- relevance scores - Pagination details including total result count
-
- Raises: HTTPException (400): If search parameters are invalid HTTPException
- (404): If no vector stores are found to search
-
- Args:
- query: Search query text
-
- vector_store_ids: IDs of vector stores to search
-
- search_options: Search configuration options
-
- top_k: Number of results to return
-
- 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
- """
- return self._post(
- "/v1/vector_stores/files/search",
- body=maybe_transform(
- {
- "query": query,
- "vector_store_ids": vector_store_ids,
- "search_options": search_options,
- "top_k": top_k,
- },
- file_search_params.FileSearchParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=FileSearchResponse,
- )
-
-
-class AsyncFilesResource(AsyncAPIResource):
- @cached_property
- def with_raw_response(self) -> AsyncFilesResourceWithRawResponse:
- """
- This property can be used as a prefix for any HTTP method call to return the
- the raw response object instead of the parsed content.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
- """
- return AsyncFilesResourceWithRawResponse(self)
-
- @cached_property
- def with_streaming_response(self) -> AsyncFilesResourceWithStreamingResponse:
- """
- An alternative to `.with_raw_response` that doesn't eagerly read the response body.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
- """
- return AsyncFilesResourceWithStreamingResponse(self)
-
- async def create(
- self,
- vector_store_id: str,
- *,
- file_id: str,
- metadata: object | 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,
- ) -> VectorStoreFile:
- """
- Upload a new file to a vector store for indexing.
-
- Args: vector_store_id: The ID of the vector store to upload to file: The file to
- upload and index
-
- Returns: VectorStoreFile: Details of the uploaded and indexed file
-
- Args:
- vector_store_id: The ID of the vector store
-
- file_id: ID of the file to add
-
- metadata: Optional metadata for the file
-
- 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_id:
- raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}")
- return await self._post(
- f"/v1/vector_stores/{vector_store_id}/files",
- body=await async_maybe_transform(
- {
- "file_id": file_id,
- "metadata": metadata,
- },
- file_create_params.FileCreateParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=VectorStoreFile,
- )
-
- async def retrieve(
- self,
- file_id: str,
- *,
- vector_store_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,
- ) -> VectorStoreFile:
- """
- Get details of a specific file in a vector store.
-
- Args: vector_store_id: The ID of the vector store file_id: The ID of the file
-
- Returns: VectorStoreFile: Details of the vector store file
-
- Args:
- vector_store_id: The ID of the vector store
-
- file_id: The ID of the file
-
- 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_id:
- raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}")
- if not file_id:
- raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}")
- return await self._get(
- f"/v1/vector_stores/{vector_store_id}/files/{file_id}",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=VectorStoreFile,
- )
-
- async def list(
- self,
- vector_store_id: str,
- *,
- limit: int | NotGiven = NOT_GIVEN,
- offset: int | 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.
-
- Args: vector_store_id: The ID of the vector store pagination: Pagination
- parameters
-
- Returns: VectorStoreFileListResponse: Paginated list of vector store files
-
- Args:
- vector_store_id: The ID of the vector store
-
- limit: Maximum number of items to return per page
-
- offset: Offset of the first item to return
-
- 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_id:
- raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}")
- return await self._get(
- f"/v1/vector_stores/{vector_store_id}/files",
- options=make_request_options(
- extra_headers=extra_headers,
- extra_query=extra_query,
- extra_body=extra_body,
- timeout=timeout,
- query=await async_maybe_transform(
- {
- "limit": limit,
- "offset": offset,
- },
- file_list_params.FileListParams,
- ),
- ),
- cast_to=FileListResponse,
- )
-
- async def delete(
- self,
- file_id: str,
- *,
- vector_store_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,
- ) -> VectorStoreFileDeleted:
- """
- Delete a file from a vector store.
-
- Args: vector_store_id: The ID of the vector store file_id: The ID of the file to
- delete
-
- Returns: VectorStoreFileDeleted: The deleted file
-
- Args:
- vector_store_id: The ID of the vector store
-
- file_id: The ID of the file to delete
-
- 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_id:
- raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}")
- if not file_id:
- raise ValueError(f"Expected a non-empty value for `file_id` but received {file_id!r}")
- return await self._delete(
- f"/v1/vector_stores/{vector_store_id}/files/{file_id}",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=VectorStoreFileDeleted,
- )
-
- async def search(
- self,
- *,
- query: str,
- vector_store_ids: List[str],
- search_options: file_search_params.SearchOptions | NotGiven = NOT_GIVEN,
- top_k: int | 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,
- ) -> FileSearchResponse:
- """
- Perform semantic search across complete vector store files.
-
- This endpoint searches through vector store files using semantic similarity
- matching. Unlike chunk search, it returns complete matching files rather than
- individual chunks. Supports complex search queries with filters and returns
- relevance-scored results.
-
- Args: search_params: Search configuration including: - query text or
- embeddings - metadata filters - pagination parameters - sorting preferences
- \\__state: API state dependency \\__ctx: Service context dependency
-
- Returns: VectorStoreSearchFileResponse containing: - List of matched files with
- relevance scores - Pagination details including total result count
-
- Raises: HTTPException (400): If search parameters are invalid HTTPException
- (404): If no vector stores are found to search
-
- Args:
- query: Search query text
-
- vector_store_ids: IDs of vector stores to search
-
- search_options: Search configuration options
-
- top_k: Number of results to return
-
- 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
- """
- return await self._post(
- "/v1/vector_stores/files/search",
- body=await async_maybe_transform(
- {
- "query": query,
- "vector_store_ids": vector_store_ids,
- "search_options": search_options,
- "top_k": top_k,
- },
- file_search_params.FileSearchParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=FileSearchResponse,
- )
-
-
-class FilesResourceWithRawResponse:
- def __init__(self, files: FilesResource) -> None:
- self._files = files
-
- self.create = to_raw_response_wrapper(
- files.create,
- )
- self.retrieve = to_raw_response_wrapper(
- files.retrieve,
- )
- self.list = to_raw_response_wrapper(
- files.list,
- )
- self.delete = to_raw_response_wrapper(
- files.delete,
- )
- self.search = to_raw_response_wrapper(
- files.search,
- )
-
-
-class AsyncFilesResourceWithRawResponse:
- def __init__(self, files: AsyncFilesResource) -> None:
- self._files = files
-
- self.create = async_to_raw_response_wrapper(
- files.create,
- )
- 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,
- )
- self.search = async_to_raw_response_wrapper(
- files.search,
- )
-
-
-class FilesResourceWithStreamingResponse:
- def __init__(self, files: FilesResource) -> None:
- self._files = files
-
- self.create = to_streamed_response_wrapper(
- files.create,
- )
- self.retrieve = to_streamed_response_wrapper(
- files.retrieve,
- )
- self.list = to_streamed_response_wrapper(
- files.list,
- )
- self.delete = to_streamed_response_wrapper(
- files.delete,
- )
- self.search = to_streamed_response_wrapper(
- files.search,
- )
-
-
-class AsyncFilesResourceWithStreamingResponse:
- def __init__(self, files: AsyncFilesResource) -> None:
- self._files = files
-
- self.create = async_to_streamed_response_wrapper(
- files.create,
- )
- 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,
- )
- self.search = async_to_streamed_response_wrapper(
- files.search,
- )
diff --git a/src/mixedbread/resources/vector_stores/vector_stores.py b/src/mixedbread/resources/vector_stores/vector_stores.py
deleted file mode 100644
index 94bd80cc..00000000
--- a/src/mixedbread/resources/vector_stores/vector_stores.py
+++ /dev/null
@@ -1,967 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing import List, Optional
-
-import httpx
-
-from .files import (
- FilesResource,
- AsyncFilesResource,
- FilesResourceWithRawResponse,
- AsyncFilesResourceWithRawResponse,
- FilesResourceWithStreamingResponse,
- AsyncFilesResourceWithStreamingResponse,
-)
-from ...types import (
- vector_store_list_params,
- vector_store_create_params,
- vector_store_search_params,
- vector_store_update_params,
- vector_store_question_answering_params,
-)
-from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
-from ..._utils import (
- maybe_transform,
- async_maybe_transform,
-)
-from ..._compat import cached_property
-from ..._resource import SyncAPIResource, AsyncAPIResource
-from ..._response import (
- to_raw_response_wrapper,
- to_streamed_response_wrapper,
- async_to_raw_response_wrapper,
- async_to_streamed_response_wrapper,
-)
-from ..._base_client import make_request_options
-from ...types.vector_store import VectorStore
-from ...types.vector_store_deleted import VectorStoreDeleted
-from ...types.vector_store_list_response import VectorStoreListResponse
-from ...types.vector_store_search_response import VectorStoreSearchResponse
-
-__all__ = ["VectorStoresResource", "AsyncVectorStoresResource"]
-
-
-class VectorStoresResource(SyncAPIResource):
- @cached_property
- def files(self) -> FilesResource:
- return FilesResource(self._client)
-
- @cached_property
- def with_raw_response(self) -> VectorStoresResourceWithRawResponse:
- """
- This property can be used as a prefix for any HTTP method call to return the
- the raw response object instead of the parsed content.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
- """
- return VectorStoresResourceWithRawResponse(self)
-
- @cached_property
- def with_streaming_response(self) -> VectorStoresResourceWithStreamingResponse:
- """
- An alternative to `.with_raw_response` that doesn't eagerly read the response body.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
- """
- return VectorStoresResourceWithStreamingResponse(self)
-
- def create(
- self,
- *,
- description: Optional[str] | NotGiven = NOT_GIVEN,
- expires_after: Optional[vector_store_create_params.ExpiresAfter] | NotGiven = NOT_GIVEN,
- file_ids: Optional[List[str]] | NotGiven = NOT_GIVEN,
- metadata: object | NotGiven = NOT_GIVEN,
- name: Optional[str] | 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,
- ) -> VectorStore:
- """
- Create a new vector store.
-
- Args: vector_store_create: VectorStoreCreate object containing the name,
- description, and metadata.
-
- Returns: VectorStore: The response containing the created vector store details.
-
- Args:
- description: Description of the vector store
-
- expires_after: Represents an expiration policy for a vector store.
-
- file_ids: Optional list of file IDs
-
- metadata: Optional metadata key-value pairs
-
- name: Name for the new vector store
-
- 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
- """
- return self._post(
- "/v1/vector_stores",
- body=maybe_transform(
- {
- "description": description,
- "expires_after": expires_after,
- "file_ids": file_ids,
- "metadata": metadata,
- "name": name,
- },
- vector_store_create_params.VectorStoreCreateParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=VectorStore,
- )
-
- def retrieve(
- self,
- vector_store_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,
- ) -> VectorStore:
- """
- Get a vector store by ID.
-
- Args: vector_store_id: The ID of the vector store to retrieve.
-
- Returns: VectorStore: The response containing the vector store details.
-
- Args:
- vector_store_id: The ID of the vector store
-
- 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_id:
- raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}")
- return self._get(
- f"/v1/vector_stores/{vector_store_id}",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=VectorStore,
- )
-
- def update(
- self,
- vector_store_id: str,
- *,
- description: Optional[str] | NotGiven = NOT_GIVEN,
- expires_after: Optional[vector_store_update_params.ExpiresAfter] | NotGiven = NOT_GIVEN,
- metadata: object | NotGiven = NOT_GIVEN,
- name: Optional[str] | 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,
- ) -> VectorStore:
- """
- Update a vector store by ID.
-
- Args: vector_store_id: The ID of the vector store to update.
- vector_store_update: VectorStoreCreate object containing the name, description,
- and metadata.
-
- Returns: VectorStore: The response containing the updated vector store details.
-
- Args:
- vector_store_id: The ID of the vector store
-
- description: New description
-
- expires_after: Represents an expiration policy for a vector store.
-
- metadata: Optional metadata key-value pairs
-
- name: New name for the vector store
-
- 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_id:
- raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}")
- return self._put(
- f"/v1/vector_stores/{vector_store_id}",
- body=maybe_transform(
- {
- "description": description,
- "expires_after": expires_after,
- "metadata": metadata,
- "name": name,
- },
- vector_store_update_params.VectorStoreUpdateParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=VectorStore,
- )
-
- def list(
- self,
- *,
- limit: int | NotGiven = NOT_GIVEN,
- offset: int | 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,
- ) -> VectorStoreListResponse:
- """
- List all vector stores.
-
- Args: pagination: The pagination options.
-
- Returns: VectorStoreListResponse: The list of vector stores.
-
- Args:
- limit: Maximum number of items to return per page
-
- offset: Offset of the first item to return
-
- 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
- """
- return self._get(
- "/v1/vector_stores",
- options=make_request_options(
- extra_headers=extra_headers,
- extra_query=extra_query,
- extra_body=extra_body,
- timeout=timeout,
- query=maybe_transform(
- {
- "limit": limit,
- "offset": offset,
- },
- vector_store_list_params.VectorStoreListParams,
- ),
- ),
- cast_to=VectorStoreListResponse,
- )
-
- def delete(
- self,
- vector_store_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,
- ) -> VectorStoreDeleted:
- """
- Delete a vector store by ID.
-
- Args: vector_store_id: The ID of the vector store to delete.
-
- Returns: VectorStore: The response containing the deleted vector store details.
-
- Args:
- vector_store_id: The ID of the vector store to delete
-
- 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_id:
- raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}")
- return self._delete(
- f"/v1/vector_stores/{vector_store_id}",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=VectorStoreDeleted,
- )
-
- def question_answering(
- self,
- *,
- vector_store_ids: List[str],
- qa_options: vector_store_question_answering_params.QaOptions | NotGiven = NOT_GIVEN,
- query: str | NotGiven = NOT_GIVEN,
- search_options: vector_store_question_answering_params.SearchOptions | NotGiven = NOT_GIVEN,
- stream: bool | NotGiven = NOT_GIVEN,
- top_k: int | 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,
- ) -> object:
- """
- Question answering
-
- Args:
- vector_store_ids: IDs of vector stores to search
-
- qa_options: Question answering configuration options
-
- query: Question to answer. If not provided, the question will be extracted from the
- passed messages.
-
- search_options: Search configuration options
-
- stream: Whether to stream the answer
-
- top_k: Number of results to return
-
- 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
- """
- return self._post(
- "/v1/vector_stores/question-answering",
- body=maybe_transform(
- {
- "vector_store_ids": vector_store_ids,
- "qa_options": qa_options,
- "query": query,
- "search_options": search_options,
- "stream": stream,
- "top_k": top_k,
- },
- vector_store_question_answering_params.VectorStoreQuestionAnsweringParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=object,
- )
-
- def search(
- self,
- *,
- query: str,
- vector_store_ids: List[str],
- search_options: vector_store_search_params.SearchOptions | NotGiven = NOT_GIVEN,
- top_k: int | 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,
- ) -> VectorStoreSearchResponse:
- """
- Perform semantic search across vector store chunks.
-
- This endpoint searches through vector store chunks using semantic similarity
- matching. It supports complex search queries with filters and returns
- relevance-scored results.
-
- Args: search_params: Search configuration including: - query text or
- embeddings - metadata filters - pagination parameters - sorting preferences
- \\__state: API state dependency \\__ctx: Service context dependency
-
- Returns: VectorStoreSearchChunkResponse containing: - List of matched chunks
- with relevance scores - Pagination details including total result count
-
- Raises: HTTPException (400): If search parameters are invalid HTTPException
- (404): If no vector stores are found to search
-
- Args:
- query: Search query text
-
- vector_store_ids: IDs of vector stores to search
-
- search_options: Search configuration options
-
- top_k: Number of results to return
-
- 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
- """
- return self._post(
- "/v1/vector_stores/search",
- body=maybe_transform(
- {
- "query": query,
- "vector_store_ids": vector_store_ids,
- "search_options": search_options,
- "top_k": top_k,
- },
- vector_store_search_params.VectorStoreSearchParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=VectorStoreSearchResponse,
- )
-
-
-class AsyncVectorStoresResource(AsyncAPIResource):
- @cached_property
- def files(self) -> AsyncFilesResource:
- return AsyncFilesResource(self._client)
-
- @cached_property
- def with_raw_response(self) -> AsyncVectorStoresResourceWithRawResponse:
- """
- This property can be used as a prefix for any HTTP method call to return the
- the raw response object instead of the parsed content.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#accessing-raw-response-data-eg-headers
- """
- return AsyncVectorStoresResourceWithRawResponse(self)
-
- @cached_property
- def with_streaming_response(self) -> AsyncVectorStoresResourceWithStreamingResponse:
- """
- An alternative to `.with_raw_response` that doesn't eagerly read the response body.
-
- For more information, see https://www.github.com/stainless-sdks/mixedbread-python#with_streaming_response
- """
- return AsyncVectorStoresResourceWithStreamingResponse(self)
-
- async def create(
- self,
- *,
- description: Optional[str] | NotGiven = NOT_GIVEN,
- expires_after: Optional[vector_store_create_params.ExpiresAfter] | NotGiven = NOT_GIVEN,
- file_ids: Optional[List[str]] | NotGiven = NOT_GIVEN,
- metadata: object | NotGiven = NOT_GIVEN,
- name: Optional[str] | 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,
- ) -> VectorStore:
- """
- Create a new vector store.
-
- Args: vector_store_create: VectorStoreCreate object containing the name,
- description, and metadata.
-
- Returns: VectorStore: The response containing the created vector store details.
-
- Args:
- description: Description of the vector store
-
- expires_after: Represents an expiration policy for a vector store.
-
- file_ids: Optional list of file IDs
-
- metadata: Optional metadata key-value pairs
-
- name: Name for the new vector store
-
- 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
- """
- return await self._post(
- "/v1/vector_stores",
- body=await async_maybe_transform(
- {
- "description": description,
- "expires_after": expires_after,
- "file_ids": file_ids,
- "metadata": metadata,
- "name": name,
- },
- vector_store_create_params.VectorStoreCreateParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=VectorStore,
- )
-
- async def retrieve(
- self,
- vector_store_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,
- ) -> VectorStore:
- """
- Get a vector store by ID.
-
- Args: vector_store_id: The ID of the vector store to retrieve.
-
- Returns: VectorStore: The response containing the vector store details.
-
- Args:
- vector_store_id: The ID of the vector store
-
- 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_id:
- raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}")
- return await self._get(
- f"/v1/vector_stores/{vector_store_id}",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=VectorStore,
- )
-
- async def update(
- self,
- vector_store_id: str,
- *,
- description: Optional[str] | NotGiven = NOT_GIVEN,
- expires_after: Optional[vector_store_update_params.ExpiresAfter] | NotGiven = NOT_GIVEN,
- metadata: object | NotGiven = NOT_GIVEN,
- name: Optional[str] | 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,
- ) -> VectorStore:
- """
- Update a vector store by ID.
-
- Args: vector_store_id: The ID of the vector store to update.
- vector_store_update: VectorStoreCreate object containing the name, description,
- and metadata.
-
- Returns: VectorStore: The response containing the updated vector store details.
-
- Args:
- vector_store_id: The ID of the vector store
-
- description: New description
-
- expires_after: Represents an expiration policy for a vector store.
-
- metadata: Optional metadata key-value pairs
-
- name: New name for the vector store
-
- 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_id:
- raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}")
- return await self._put(
- f"/v1/vector_stores/{vector_store_id}",
- body=await async_maybe_transform(
- {
- "description": description,
- "expires_after": expires_after,
- "metadata": metadata,
- "name": name,
- },
- vector_store_update_params.VectorStoreUpdateParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=VectorStore,
- )
-
- async def list(
- self,
- *,
- limit: int | NotGiven = NOT_GIVEN,
- offset: int | 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,
- ) -> VectorStoreListResponse:
- """
- List all vector stores.
-
- Args: pagination: The pagination options.
-
- Returns: VectorStoreListResponse: The list of vector stores.
-
- Args:
- limit: Maximum number of items to return per page
-
- offset: Offset of the first item to return
-
- 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
- """
- return await self._get(
- "/v1/vector_stores",
- options=make_request_options(
- extra_headers=extra_headers,
- extra_query=extra_query,
- extra_body=extra_body,
- timeout=timeout,
- query=await async_maybe_transform(
- {
- "limit": limit,
- "offset": offset,
- },
- vector_store_list_params.VectorStoreListParams,
- ),
- ),
- cast_to=VectorStoreListResponse,
- )
-
- async def delete(
- self,
- vector_store_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,
- ) -> VectorStoreDeleted:
- """
- Delete a vector store by ID.
-
- Args: vector_store_id: The ID of the vector store to delete.
-
- Returns: VectorStore: The response containing the deleted vector store details.
-
- Args:
- vector_store_id: The ID of the vector store to delete
-
- 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_id:
- raise ValueError(f"Expected a non-empty value for `vector_store_id` but received {vector_store_id!r}")
- return await self._delete(
- f"/v1/vector_stores/{vector_store_id}",
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=VectorStoreDeleted,
- )
-
- async def question_answering(
- self,
- *,
- vector_store_ids: List[str],
- qa_options: vector_store_question_answering_params.QaOptions | NotGiven = NOT_GIVEN,
- query: str | NotGiven = NOT_GIVEN,
- search_options: vector_store_question_answering_params.SearchOptions | NotGiven = NOT_GIVEN,
- stream: bool | NotGiven = NOT_GIVEN,
- top_k: int | 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,
- ) -> object:
- """
- Question answering
-
- Args:
- vector_store_ids: IDs of vector stores to search
-
- qa_options: Question answering configuration options
-
- query: Question to answer. If not provided, the question will be extracted from the
- passed messages.
-
- search_options: Search configuration options
-
- stream: Whether to stream the answer
-
- top_k: Number of results to return
-
- 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
- """
- return await self._post(
- "/v1/vector_stores/question-answering",
- body=await async_maybe_transform(
- {
- "vector_store_ids": vector_store_ids,
- "qa_options": qa_options,
- "query": query,
- "search_options": search_options,
- "stream": stream,
- "top_k": top_k,
- },
- vector_store_question_answering_params.VectorStoreQuestionAnsweringParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=object,
- )
-
- async def search(
- self,
- *,
- query: str,
- vector_store_ids: List[str],
- search_options: vector_store_search_params.SearchOptions | NotGiven = NOT_GIVEN,
- top_k: int | 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,
- ) -> VectorStoreSearchResponse:
- """
- Perform semantic search across vector store chunks.
-
- This endpoint searches through vector store chunks using semantic similarity
- matching. It supports complex search queries with filters and returns
- relevance-scored results.
-
- Args: search_params: Search configuration including: - query text or
- embeddings - metadata filters - pagination parameters - sorting preferences
- \\__state: API state dependency \\__ctx: Service context dependency
-
- Returns: VectorStoreSearchChunkResponse containing: - List of matched chunks
- with relevance scores - Pagination details including total result count
-
- Raises: HTTPException (400): If search parameters are invalid HTTPException
- (404): If no vector stores are found to search
-
- Args:
- query: Search query text
-
- vector_store_ids: IDs of vector stores to search
-
- search_options: Search configuration options
-
- top_k: Number of results to return
-
- 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
- """
- return await self._post(
- "/v1/vector_stores/search",
- body=await async_maybe_transform(
- {
- "query": query,
- "vector_store_ids": vector_store_ids,
- "search_options": search_options,
- "top_k": top_k,
- },
- vector_store_search_params.VectorStoreSearchParams,
- ),
- options=make_request_options(
- extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
- ),
- cast_to=VectorStoreSearchResponse,
- )
-
-
-class VectorStoresResourceWithRawResponse:
- def __init__(self, vector_stores: VectorStoresResource) -> None:
- self._vector_stores = vector_stores
-
- self.create = to_raw_response_wrapper(
- vector_stores.create,
- )
- self.retrieve = to_raw_response_wrapper(
- vector_stores.retrieve,
- )
- self.update = to_raw_response_wrapper(
- vector_stores.update,
- )
- self.list = to_raw_response_wrapper(
- vector_stores.list,
- )
- self.delete = to_raw_response_wrapper(
- vector_stores.delete,
- )
- self.question_answering = to_raw_response_wrapper(
- vector_stores.question_answering,
- )
- self.search = to_raw_response_wrapper(
- vector_stores.search,
- )
-
- @cached_property
- def files(self) -> FilesResourceWithRawResponse:
- return FilesResourceWithRawResponse(self._vector_stores.files)
-
-
-class AsyncVectorStoresResourceWithRawResponse:
- def __init__(self, vector_stores: AsyncVectorStoresResource) -> None:
- self._vector_stores = vector_stores
-
- self.create = async_to_raw_response_wrapper(
- vector_stores.create,
- )
- self.retrieve = async_to_raw_response_wrapper(
- vector_stores.retrieve,
- )
- self.update = async_to_raw_response_wrapper(
- vector_stores.update,
- )
- self.list = async_to_raw_response_wrapper(
- vector_stores.list,
- )
- self.delete = async_to_raw_response_wrapper(
- vector_stores.delete,
- )
- self.question_answering = async_to_raw_response_wrapper(
- vector_stores.question_answering,
- )
- self.search = async_to_raw_response_wrapper(
- vector_stores.search,
- )
-
- @cached_property
- def files(self) -> AsyncFilesResourceWithRawResponse:
- return AsyncFilesResourceWithRawResponse(self._vector_stores.files)
-
-
-class VectorStoresResourceWithStreamingResponse:
- def __init__(self, vector_stores: VectorStoresResource) -> None:
- self._vector_stores = vector_stores
-
- self.create = to_streamed_response_wrapper(
- vector_stores.create,
- )
- self.retrieve = to_streamed_response_wrapper(
- vector_stores.retrieve,
- )
- self.update = to_streamed_response_wrapper(
- vector_stores.update,
- )
- self.list = to_streamed_response_wrapper(
- vector_stores.list,
- )
- self.delete = to_streamed_response_wrapper(
- vector_stores.delete,
- )
- self.question_answering = to_streamed_response_wrapper(
- vector_stores.question_answering,
- )
- self.search = to_streamed_response_wrapper(
- vector_stores.search,
- )
-
- @cached_property
- def files(self) -> FilesResourceWithStreamingResponse:
- return FilesResourceWithStreamingResponse(self._vector_stores.files)
-
-
-class AsyncVectorStoresResourceWithStreamingResponse:
- def __init__(self, vector_stores: AsyncVectorStoresResource) -> None:
- self._vector_stores = vector_stores
-
- self.create = async_to_streamed_response_wrapper(
- vector_stores.create,
- )
- self.retrieve = async_to_streamed_response_wrapper(
- vector_stores.retrieve,
- )
- self.update = async_to_streamed_response_wrapper(
- vector_stores.update,
- )
- self.list = async_to_streamed_response_wrapper(
- vector_stores.list,
- )
- self.delete = async_to_streamed_response_wrapper(
- vector_stores.delete,
- )
- self.question_answering = async_to_streamed_response_wrapper(
- vector_stores.question_answering,
- )
- self.search = async_to_streamed_response_wrapper(
- vector_stores.search,
- )
-
- @cached_property
- def files(self) -> AsyncFilesResourceWithStreamingResponse:
- return AsyncFilesResourceWithStreamingResponse(self._vector_stores.files)
diff --git a/src/mixedbread/types/__init__.py b/src/mixedbread/types/__init__.py
index 7afb0f50..306c151e 100644
--- a/src/mixedbread/types/__init__.py
+++ b/src/mixedbread/types/__init__.py
@@ -2,25 +2,63 @@
from __future__ import annotations
+from . import shared
+from .. import _compat
+from .store import Store as Store
+from .shared import Usage as Usage, SearchFilter as SearchFilter, SearchFilterCondition as SearchFilterCondition
+from .api_key import APIKey as APIKey
+from .embedding import Embedding as Embedding
+from .data_source import DataSource as DataSource
from .file_object import FileObject as FileObject
-from .file_deleted import FileDeleted as FileDeleted
-from .vector_store import VectorStore as VectorStore
+from .expires_after import ExpiresAfter as ExpiresAfter
from .info_response import InfoResponse as InfoResponse
+from .oauth2_params import Oauth2Params as Oauth2Params
+from .api_key_created import APIKeyCreated as APIKeyCreated
+from .encoding_format import EncodingFormat as EncodingFormat
+from .rerank_response import RerankResponse as RerankResponse
+from .data_source_type import DataSourceType as DataSourceType
from .file_list_params import FileListParams as FileListParams
+from .store_list_params import StoreListParams as StoreListParams
from .file_create_params import FileCreateParams as FileCreateParams
-from .file_list_response import FileListResponse as FileListResponse
from .file_update_params import FileUpdateParams as FileUpdateParams
-from .vector_store_deleted import VectorStoreDeleted as VectorStoreDeleted
+from .api_key_list_params import APIKeyListParams as APIKeyListParams
+from .client_embed_params import ClientEmbedParams as ClientEmbedParams
+from .expires_after_param import ExpiresAfterParam as ExpiresAfterParam
+from .store_create_params import StoreCreateParams as StoreCreateParams
+from .store_search_params import StoreSearchParams as StoreSearchParams
+from .store_update_params import StoreUpdateParams as StoreUpdateParams
+from .client_rerank_params import ClientRerankParams as ClientRerankParams
+from .file_delete_response import FileDeleteResponse as FileDeleteResponse
+from .api_key_create_params import APIKeyCreateParams as APIKeyCreateParams
+from .pagination_with_total import PaginationWithTotal as PaginationWithTotal
+from .store_delete_response import StoreDeleteResponse as StoreDeleteResponse
+from .store_search_response import StoreSearchResponse as StoreSearchResponse
+from .api_key_delete_response import APIKeyDeleteResponse as APIKeyDeleteResponse
+from .data_source_list_params import DataSourceListParams as DataSourceListParams
from .embedding_create_params import EmbeddingCreateParams as EmbeddingCreateParams
-from .reranking_create_params import RerankingCreateParams as RerankingCreateParams
-from .vector_store_list_params import VectorStoreListParams as VectorStoreListParams
+from .scored_text_input_chunk import ScoredTextInputChunk as ScoredTextInputChunk
+from .linear_data_source_param import LinearDataSourceParam as LinearDataSourceParam
+from .multi_encoding_embedding import MultiEncodingEmbedding as MultiEncodingEmbedding
+from .notion_data_source_param import NotionDataSourceParam as NotionDataSourceParam
+from .data_source_create_params import DataSourceCreateParams as DataSourceCreateParams
+from .data_source_oauth2_params import DataSourceOauth2Params as DataSourceOauth2Params
+from .data_source_update_params import DataSourceUpdateParams as DataSourceUpdateParams
from .embedding_create_response import EmbeddingCreateResponse as EmbeddingCreateResponse
-from .reranking_create_response import RerankingCreateResponse as RerankingCreateResponse
-from .vector_store_create_params import VectorStoreCreateParams as VectorStoreCreateParams
-from .vector_store_list_response import VectorStoreListResponse as VectorStoreListResponse
-from .vector_store_search_params import VectorStoreSearchParams as VectorStoreSearchParams
-from .vector_store_update_params import VectorStoreUpdateParams as VectorStoreUpdateParams
-from .vector_store_search_response import VectorStoreSearchResponse as VectorStoreSearchResponse
-from .vector_store_question_answering_params import (
- VectorStoreQuestionAnsweringParams as VectorStoreQuestionAnsweringParams,
-)
+from .data_source_delete_response import DataSourceDeleteResponse as DataSourceDeleteResponse
+from .scored_audio_url_input_chunk import ScoredAudioURLInputChunk as ScoredAudioURLInputChunk
+from .scored_image_url_input_chunk import ScoredImageURLInputChunk as ScoredImageURLInputChunk
+from .scored_video_url_input_chunk import ScoredVideoURLInputChunk as ScoredVideoURLInputChunk
+from .store_metadata_facets_params import StoreMetadataFacetsParams as StoreMetadataFacetsParams
+from .store_metadata_facets_response import StoreMetadataFacetsResponse as StoreMetadataFacetsResponse
+from .store_question_answering_params import StoreQuestionAnsweringParams as StoreQuestionAnsweringParams
+from .store_chunk_search_options_param import StoreChunkSearchOptionsParam as StoreChunkSearchOptionsParam
+from .store_question_answering_response import StoreQuestionAnsweringResponse as StoreQuestionAnsweringResponse
+
+# Rebuild cyclical models only after all modules are imported.
+# This ensures that, when building the deferred (due to cyclical references) model schema,
+# Pydantic can resolve the necessary references.
+# See: https://github.com/pydantic/pydantic/issues/11250 for more context.
+if _compat.PYDANTIC_V1:
+ shared.search_filter.SearchFilter.update_forward_refs() # type: ignore
+else:
+ shared.search_filter.SearchFilter.model_rebuild(_parent_namespace_depth=0)
diff --git a/src/mixedbread/types/api_key.py b/src/mixedbread/types/api_key.py
new file mode 100644
index 00000000..ab7b375d
--- /dev/null
+++ b/src/mixedbread/types/api_key.py
@@ -0,0 +1,48 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from .._models import BaseModel
+
+__all__ = ["APIKey", "Scope"]
+
+
+class Scope(BaseModel):
+ method: Literal["read", "write", "delete", "list", "create", "search"]
+
+ resource_type: Optional[Literal["store"]] = None
+
+ resource_id: Optional[str] = None
+
+
+class APIKey(BaseModel):
+ """Response model for an API key."""
+
+ id: str
+ """The ID of the API key"""
+
+ name: str
+ """The name of the API key"""
+
+ redacted_value: str
+ """The redacted value of the API key"""
+
+ expires_at: Optional[datetime] = None
+ """The expiration datetime of the API key"""
+
+ created_at: datetime
+ """The creation datetime of the API key"""
+
+ updated_at: datetime
+ """The last update datetime of the API key"""
+
+ last_active_at: Optional[datetime] = None
+ """The last active datetime of the API key"""
+
+ object: Optional[Literal["api_key"]] = None
+ """The type of the object"""
+
+ scope: Optional[List[Scope]] = None
+ """The scope of the API key"""
diff --git a/src/mixedbread/types/api_key_create_params.py b/src/mixedbread/types/api_key_create_params.py
new file mode 100644
index 00000000..829cd517
--- /dev/null
+++ b/src/mixedbread/types/api_key_create_params.py
@@ -0,0 +1,30 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable, Optional
+from datetime import datetime
+from typing_extensions import Literal, Required, Annotated, TypedDict
+
+from .._utils import PropertyInfo
+
+__all__ = ["APIKeyCreateParams", "Scope"]
+
+
+class APIKeyCreateParams(TypedDict, total=False):
+ name: str
+ """A name/description for the API key"""
+
+ scope: Optional[Iterable[Scope]]
+ """The scope of the API key"""
+
+ expires_at: Annotated[Union[str, datetime, None], PropertyInfo(format="iso8601")]
+ """Optional expiration datetime"""
+
+
+class Scope(TypedDict, total=False):
+ method: Required[Literal["read", "write", "delete", "list", "create", "search"]]
+
+ resource_type: Optional[Literal["store"]]
+
+ resource_id: Optional[str]
diff --git a/src/mixedbread/types/api_key_created.py b/src/mixedbread/types/api_key_created.py
new file mode 100644
index 00000000..3efdb33a
--- /dev/null
+++ b/src/mixedbread/types/api_key_created.py
@@ -0,0 +1,51 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from .._models import BaseModel
+
+__all__ = ["APIKeyCreated", "Scope"]
+
+
+class Scope(BaseModel):
+ method: Literal["read", "write", "delete", "list", "create", "search"]
+
+ resource_type: Optional[Literal["store"]] = None
+
+ resource_id: Optional[str] = None
+
+
+class APIKeyCreated(BaseModel):
+ """Response model for creating an API key."""
+
+ id: str
+ """The ID of the API key"""
+
+ name: str
+ """The name of the API key"""
+
+ redacted_value: str
+ """The redacted value of the API key"""
+
+ expires_at: Optional[datetime] = None
+ """The expiration datetime of the API key"""
+
+ created_at: datetime
+ """The creation datetime of the API key"""
+
+ updated_at: datetime
+ """The last update datetime of the API key"""
+
+ last_active_at: Optional[datetime] = None
+ """The last active datetime of the API key"""
+
+ object: Optional[Literal["api_key"]] = None
+ """The type of the object"""
+
+ scope: Optional[List[Scope]] = None
+ """The scope of the API key"""
+
+ value: str
+ """The value of the API key"""
diff --git a/src/mixedbread/types/api_key_delete_response.py b/src/mixedbread/types/api_key_delete_response.py
new file mode 100644
index 00000000..31e87669
--- /dev/null
+++ b/src/mixedbread/types/api_key_delete_response.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from .._models import BaseModel
+
+__all__ = ["APIKeyDeleteResponse"]
+
+
+class APIKeyDeleteResponse(BaseModel):
+ """Response model for deleting an API key."""
+
+ id: str
+ """The ID of the deleted API key"""
+
+ deleted: bool
+ """Whether the API key was deleted"""
+
+ object: Optional[Literal["api_key"]] = None
+ """The type of the object deleted"""
diff --git a/src/mixedbread/types/vector_stores/file_list_params.py b/src/mixedbread/types/api_key_list_params.py
similarity index 78%
rename from src/mixedbread/types/vector_stores/file_list_params.py
rename to src/mixedbread/types/api_key_list_params.py
index 7ecfdf54..748045a4 100644
--- a/src/mixedbread/types/vector_stores/file_list_params.py
+++ b/src/mixedbread/types/api_key_list_params.py
@@ -4,10 +4,10 @@
from typing_extensions import TypedDict
-__all__ = ["FileListParams"]
+__all__ = ["APIKeyListParams"]
-class FileListParams(TypedDict, total=False):
+class APIKeyListParams(TypedDict, total=False):
limit: int
"""Maximum number of items to return per page"""
diff --git a/src/mixedbread/types/client_embed_params.py b/src/mixedbread/types/client_embed_params.py
new file mode 100644
index 00000000..7a5080bc
--- /dev/null
+++ b/src/mixedbread/types/client_embed_params.py
@@ -0,0 +1,34 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import List, Union, Optional
+from typing_extensions import Required, TypedDict
+
+from .._types import SequenceNotStr
+from .encoding_format import EncodingFormat
+
+__all__ = ["ClientEmbedParams"]
+
+
+class ClientEmbedParams(TypedDict, total=False):
+ model: Required[str]
+ """The model to use for creating embeddings."""
+
+ input: Required[Union[str, SequenceNotStr[str]]]
+ """The input to create embeddings for."""
+
+ dimensions: Optional[int]
+ """The number of dimensions to use for the embeddings."""
+
+ prompt: Optional[str]
+ """The prompt to use for the embedding creation."""
+
+ normalized: bool
+ """Whether to normalize the embeddings."""
+
+ encoding_format: Union[EncodingFormat, List[EncodingFormat]]
+ """The encoding format(s) of the embeddings.
+
+ Can be a single format or a list of formats.
+ """
diff --git a/src/mixedbread/types/reranking_create_params.py b/src/mixedbread/types/client_rerank_params.py
similarity index 57%
rename from src/mixedbread/types/reranking_create_params.py
rename to src/mixedbread/types/client_rerank_params.py
index cbf9514e..ee628e54 100644
--- a/src/mixedbread/types/reranking_create_params.py
+++ b/src/mixedbread/types/client_rerank_params.py
@@ -2,27 +2,32 @@
from __future__ import annotations
-from typing import List, Union, Optional
+from typing import Union, Iterable, Optional
from typing_extensions import Required, TypedDict
-__all__ = ["RerankingCreateParams"]
+from .._types import SequenceNotStr
+__all__ = ["ClientRerankParams"]
-class RerankingCreateParams(TypedDict, total=False):
- input: Required[List[Union[str, object]]]
- """The input documents to rerank."""
+
+class ClientRerankParams(TypedDict, total=False):
+ model: str
+ """The model to use for reranking documents."""
query: Required[str]
"""The query to rerank the documents."""
- model: str
- """The model to use for reranking documents."""
+ input: Required[SequenceNotStr[Union[str, Iterable[object], object]]]
+ """The input documents to rerank."""
- rank_fields: Optional[List[str]]
+ rank_fields: Optional[SequenceNotStr[str]]
"""The fields of the documents to rank."""
+ top_k: int
+ """The number of documents to return."""
+
return_input: bool
"""Whether to return the documents."""
- top_k: int
- """The number of documents to return."""
+ rewrite_query: bool
+ """Wether or not to rewrite the query before passing it to the reranking model"""
diff --git a/src/mixedbread/types/data_source.py b/src/mixedbread/types/data_source.py
new file mode 100644
index 00000000..d219ef28
--- /dev/null
+++ b/src/mixedbread/types/data_source.py
@@ -0,0 +1,54 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Union, Optional
+from datetime import datetime
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from .._utils import PropertyInfo
+from .._models import BaseModel
+from .data_source_type import DataSourceType
+from .data_source_oauth2_params import DataSourceOauth2Params
+
+__all__ = ["DataSource", "AuthParams", "AuthParamsDataSourceAPIKeyParams"]
+
+
+class AuthParamsDataSourceAPIKeyParams(BaseModel):
+ """Authentication parameters for a API key data source."""
+
+ type: Optional[Literal["api_key"]] = None
+
+ api_key: str
+ """The API key"""
+
+
+AuthParams: TypeAlias = Annotated[
+ Union[DataSourceOauth2Params, AuthParamsDataSourceAPIKeyParams, None], PropertyInfo(discriminator="type")
+]
+
+
+class DataSource(BaseModel):
+ """Service-level representation of a data source."""
+
+ id: str
+ """The ID of the data source"""
+
+ created_at: datetime
+ """The creation time of the data source"""
+
+ updated_at: datetime
+ """The last update time of the data source"""
+
+ type: DataSourceType
+ """The type of data source"""
+
+ name: str
+ """The name of the data source"""
+
+ metadata: object
+ """The metadata of the data source"""
+
+ auth_params: Optional[AuthParams] = None
+ """Authentication parameters"""
+
+ object: Optional[Literal["data_source"]] = None
+ """The type of the object"""
diff --git a/src/mixedbread/types/data_source_create_params.py b/src/mixedbread/types/data_source_create_params.py
new file mode 100644
index 00000000..9424a1de
--- /dev/null
+++ b/src/mixedbread/types/data_source_create_params.py
@@ -0,0 +1,62 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from .oauth2_params import Oauth2Params
+
+__all__ = [
+ "DataSourceCreateParams",
+ "NotionDataSource",
+ "NotionDataSourceAuthParams",
+ "NotionDataSourceAuthParamsAPIKeyCreateOrUpdateParams",
+ "LinearDataSource",
+]
+
+
+class NotionDataSource(TypedDict, total=False):
+ type: Literal["notion"]
+ """The type of data source to create"""
+
+ name: Required[str]
+ """The name of the data source"""
+
+ metadata: object
+ """The metadata of the data source"""
+
+ auth_params: Optional[NotionDataSourceAuthParams]
+ """The authentication parameters of the data source.
+
+ Notion supports OAuth2 and API key.
+ """
+
+
+class NotionDataSourceAuthParamsAPIKeyCreateOrUpdateParams(TypedDict, total=False):
+ """Base class for API key create or update parameters."""
+
+ type: Literal["api_key"]
+
+ api_key: Required[str]
+ """The API key"""
+
+
+NotionDataSourceAuthParams: TypeAlias = Union[Oauth2Params, NotionDataSourceAuthParamsAPIKeyCreateOrUpdateParams]
+
+
+class LinearDataSource(TypedDict, total=False):
+ type: Literal["linear"]
+ """The type of data source to create"""
+
+ name: Required[str]
+ """The name of the data source"""
+
+ metadata: object
+ """The metadata of the data source"""
+
+ auth_params: Optional[Oauth2Params]
+ """Base class for OAuth2 create or update parameters."""
+
+
+DataSourceCreateParams: TypeAlias = Union[NotionDataSource, LinearDataSource]
diff --git a/src/mixedbread/types/data_source_delete_response.py b/src/mixedbread/types/data_source_delete_response.py
new file mode 100644
index 00000000..98a38e0c
--- /dev/null
+++ b/src/mixedbread/types/data_source_delete_response.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from .._models import BaseModel
+
+__all__ = ["DataSourceDeleteResponse"]
+
+
+class DataSourceDeleteResponse(BaseModel):
+ """Deleted data source."""
+
+ id: str
+ """The ID of the data source"""
+
+ deleted: Optional[bool] = None
+ """Whether the data source was deleted"""
+
+ object: Optional[Literal["data_source"]] = None
+ """The type of the object"""
diff --git a/src/mixedbread/types/data_source_list_params.py b/src/mixedbread/types/data_source_list_params.py
new file mode 100644
index 00000000..07eb80f4
--- /dev/null
+++ b/src/mixedbread/types/data_source_list_params.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import TypedDict
+
+__all__ = ["DataSourceListParams"]
+
+
+class DataSourceListParams(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)"""
diff --git a/src/mixedbread/types/data_source_oauth2_params.py b/src/mixedbread/types/data_source_oauth2_params.py
new file mode 100644
index 00000000..b19f4a83
--- /dev/null
+++ b/src/mixedbread/types/data_source_oauth2_params.py
@@ -0,0 +1,36 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from .._models import BaseModel
+
+__all__ = ["DataSourceOauth2Params"]
+
+
+class DataSourceOauth2Params(BaseModel):
+ """Authentication parameters for a OAuth data source."""
+
+ type: Optional[Literal["oauth2"]] = None
+
+ created_at: Optional[datetime] = None
+ """The timestamp when the OAuth2 credentials were created"""
+
+ scope: Optional[str] = None
+ """The OAuth2 scope"""
+
+ access_token: Optional[str] = None
+ """The OAuth2 access token"""
+
+ refresh_token: Optional[str] = None
+ """The OAuth2 refresh token"""
+
+ token_type: Optional[str] = None
+ """The OAuth2 token type"""
+
+ expires_on: Optional[datetime] = None
+ """The OAuth2 token expiration timestamp"""
+
+ additional_params: Optional[Dict[str, object]] = None
+ """Additional parameters for the OAuth2 flow"""
diff --git a/src/mixedbread/types/data_source_type.py b/src/mixedbread/types/data_source_type.py
new file mode 100644
index 00000000..0851808b
--- /dev/null
+++ b/src/mixedbread/types/data_source_type.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["DataSourceType"]
+
+DataSourceType: TypeAlias = Literal["notion", "linear"]
diff --git a/src/mixedbread/types/data_source_update_params.py b/src/mixedbread/types/data_source_update_params.py
new file mode 100644
index 00000000..96880db4
--- /dev/null
+++ b/src/mixedbread/types/data_source_update_params.py
@@ -0,0 +1,62 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from .oauth2_params import Oauth2Params
+
+__all__ = [
+ "DataSourceUpdateParams",
+ "NotionDataSource",
+ "NotionDataSourceAuthParams",
+ "NotionDataSourceAuthParamsAPIKeyCreateOrUpdateParams",
+ "LinearDataSource",
+]
+
+
+class NotionDataSource(TypedDict, total=False):
+ type: Literal["notion"]
+ """The type of data source to create"""
+
+ name: Required[str]
+ """The name of the data source"""
+
+ metadata: object
+ """The metadata of the data source"""
+
+ auth_params: Optional[NotionDataSourceAuthParams]
+ """The authentication parameters of the data source.
+
+ Notion supports OAuth2 and API key.
+ """
+
+
+class NotionDataSourceAuthParamsAPIKeyCreateOrUpdateParams(TypedDict, total=False):
+ """Base class for API key create or update parameters."""
+
+ type: Literal["api_key"]
+
+ api_key: Required[str]
+ """The API key"""
+
+
+NotionDataSourceAuthParams: TypeAlias = Union[Oauth2Params, NotionDataSourceAuthParamsAPIKeyCreateOrUpdateParams]
+
+
+class LinearDataSource(TypedDict, total=False):
+ type: Literal["linear"]
+ """The type of data source to create"""
+
+ name: Required[str]
+ """The name of the data source"""
+
+ metadata: object
+ """The metadata of the data source"""
+
+ auth_params: Optional[Oauth2Params]
+ """Base class for OAuth2 create or update parameters."""
+
+
+DataSourceUpdateParams: TypeAlias = Union[NotionDataSource, LinearDataSource]
diff --git a/src/mixedbread/types/data_sources/__init__.py b/src/mixedbread/types/data_sources/__init__.py
new file mode 100644
index 00000000..c35b6f2e
--- /dev/null
+++ b/src/mixedbread/types/data_sources/__init__.py
@@ -0,0 +1,9 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .connector_list_params import ConnectorListParams as ConnectorListParams
+from .data_source_connector import DataSourceConnector as DataSourceConnector
+from .connector_create_params import ConnectorCreateParams as ConnectorCreateParams
+from .connector_update_params import ConnectorUpdateParams as ConnectorUpdateParams
+from .connector_delete_response import ConnectorDeleteResponse as ConnectorDeleteResponse
diff --git a/src/mixedbread/types/data_sources/connector_create_params.py b/src/mixedbread/types/data_sources/connector_create_params.py
new file mode 100644
index 00000000..185c3894
--- /dev/null
+++ b/src/mixedbread/types/data_sources/connector_create_params.py
@@ -0,0 +1,32 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union
+from typing_extensions import Required, TypedDict
+
+__all__ = ["ConnectorCreateParams"]
+
+
+class ConnectorCreateParams(TypedDict, total=False):
+ store_id: Required[str]
+ """The ID of the store"""
+
+ name: str
+ """The name of the connector"""
+
+ trigger_sync: bool
+ """Whether the connector should be synced after creation"""
+
+ metadata: object
+ """The metadata of the connector"""
+
+ polling_interval: Union[int, str, None]
+ """Polling interval for the connector.
+
+ Defaults to 30 minutes if not specified. Can be provided as:
+
+ - int: Number of seconds (e.g., 1800 for 30 minutes)
+ - str: Duration string (e.g., '30m', '1h', '2d') or ISO 8601 format (e.g.,
+ 'PT30M', 'P1D') Valid range: 15 seconds to 30 days
+ """
diff --git a/src/mixedbread/types/data_sources/connector_delete_response.py b/src/mixedbread/types/data_sources/connector_delete_response.py
new file mode 100644
index 00000000..079ccf7f
--- /dev/null
+++ b/src/mixedbread/types/data_sources/connector_delete_response.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["ConnectorDeleteResponse"]
+
+
+class ConnectorDeleteResponse(BaseModel):
+ """Deleted connector."""
+
+ id: str
+ """The ID of the connector"""
+
+ deleted: Optional[bool] = None
+ """Whether the connector was deleted"""
+
+ object: Optional[Literal["data_source.connector"]] = None
+ """The type of the object"""
diff --git a/src/mixedbread/types/data_sources/connector_list_params.py b/src/mixedbread/types/data_sources/connector_list_params.py
new file mode 100644
index 00000000..cb3da959
--- /dev/null
+++ b/src/mixedbread/types/data_sources/connector_list_params.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import TypedDict
+
+__all__ = ["ConnectorListParams"]
+
+
+class ConnectorListParams(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)"""
diff --git a/src/mixedbread/types/data_sources/connector_update_params.py b/src/mixedbread/types/data_sources/connector_update_params.py
new file mode 100644
index 00000000..7e0d112b
--- /dev/null
+++ b/src/mixedbread/types/data_sources/connector_update_params.py
@@ -0,0 +1,32 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Union, Optional
+from typing_extensions import Required, TypedDict
+
+__all__ = ["ConnectorUpdateParams"]
+
+
+class ConnectorUpdateParams(TypedDict, total=False):
+ data_source_id: Required[str]
+ """The ID of the data source to update a connector for"""
+
+ name: Optional[str]
+ """The name of the connector"""
+
+ metadata: Optional[Dict[str, object]]
+ """The metadata of the connector"""
+
+ trigger_sync: Optional[bool]
+ """Whether the connector should be synced after update"""
+
+ polling_interval: Union[int, str, None]
+ """Polling interval for the connector.
+
+ Defaults to 30 minutes if not specified. Can be provided as:
+
+ - int: Number of seconds (e.g., 1800 for 30 minutes)
+ - str: Duration string (e.g., '30m', '1h', '2d') or ISO 8601 format (e.g.,
+ 'PT30M', 'P1D') Valid range: 15 seconds to 30 days
+ """
diff --git a/src/mixedbread/types/data_sources/data_source_connector.py b/src/mixedbread/types/data_sources/data_source_connector.py
new file mode 100644
index 00000000..0fc36723
--- /dev/null
+++ b/src/mixedbread/types/data_sources/data_source_connector.py
@@ -0,0 +1,55 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["DataSourceConnector"]
+
+
+class DataSourceConnector(BaseModel):
+ """Service-level representation of a connector."""
+
+ id: str
+ """The ID of the connector"""
+
+ created_at: datetime
+ """The creation time of the connector"""
+
+ updated_at: datetime
+ """The last update time of the connector"""
+
+ store_id: str
+ """The ID of the store"""
+
+ data_source_id: str
+ """The ID of the data source"""
+
+ name: Optional[str] = None
+ """The name of the connector"""
+
+ metadata: object
+ """The metadata of the connector"""
+
+ polling_interval: str
+ """The polling interval of the connector"""
+
+ started_at: Optional[datetime] = None
+ """The start time of the connector"""
+
+ finished_at: Optional[datetime] = None
+ """The finish time of the connector"""
+
+ last_synced_at: Optional[datetime] = None
+ """The last sync time of the connector"""
+
+ status: Literal["idle", "pending", "in_progress", "cancelled", "completed", "failed"]
+ """The sync status of the connector"""
+
+ error: Optional[Dict[str, object]] = None
+ """The sync error of the connector"""
+
+ object: Optional[Literal["data_source.connector"]] = None
+ """The type of the object"""
diff --git a/src/mixedbread/types/embedding.py b/src/mixedbread/types/embedding.py
new file mode 100644
index 00000000..19fd8136
--- /dev/null
+++ b/src/mixedbread/types/embedding.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from typing_extensions import Literal
+
+from .._models import BaseModel
+
+__all__ = ["Embedding"]
+
+
+class Embedding(BaseModel):
+ embedding: Union[List[float], List[int], str]
+ """The encoded embedding."""
+
+ index: int
+ """The index of the embedding."""
+
+ object: Optional[Literal["embedding"]] = None
+ """The object type of the embedding."""
diff --git a/src/mixedbread/types/embedding_create_params.py b/src/mixedbread/types/embedding_create_params.py
index 6ca77dcf..ab848f77 100644
--- a/src/mixedbread/types/embedding_create_params.py
+++ b/src/mixedbread/types/embedding_create_params.py
@@ -3,53 +3,32 @@
from __future__ import annotations
from typing import List, Union, Optional
-from typing_extensions import Literal, Required, TypeAlias, TypedDict
+from typing_extensions import Required, TypedDict
-__all__ = ["EmbeddingCreateParams", "Input", "InputImageURLInput", "InputImageURLInputImage", "InputTextInput"]
+from .._types import SequenceNotStr
+from .encoding_format import EncodingFormat
+__all__ = ["EmbeddingCreateParams"]
-class EmbeddingCreateParams(TypedDict, total=False):
- input: Required[Input]
- """The input to create embeddings for."""
+class EmbeddingCreateParams(TypedDict, total=False):
model: Required[str]
"""The model to use for creating embeddings."""
+ input: Required[Union[str, SequenceNotStr[str]]]
+ """The input to create embeddings for."""
+
dimensions: Optional[int]
"""The number of dimensions to use for the embeddings."""
- encoding_format: Union[
- Literal["float", "float16", "base64", "binary", "ubinary", "int8", "uint8"],
- List[Literal["float", "float16", "base64", "binary", "ubinary", "int8", "uint8"]],
- ]
- """The encoding format of the embeddings."""
-
- normalized: bool
- """Whether to normalize the embeddings."""
-
prompt: Optional[str]
"""The prompt to use for the embedding creation."""
+ normalized: bool
+ """Whether to normalize the embeddings."""
-class InputImageURLInputImage(TypedDict, total=False):
- url: Required[str]
- """The image URL. Can be either a URL or a Data URI."""
-
-
-class InputImageURLInput(TypedDict, total=False):
- image: Required[InputImageURLInputImage]
- """The image input specification."""
-
- type: Literal["image_url"]
- """Input type identifier"""
-
-
-class InputTextInput(TypedDict, total=False):
- text: Required[str]
- """Text content to process"""
-
- type: Literal["text"]
- """Input type identifier"""
-
+ encoding_format: Union[EncodingFormat, List[EncodingFormat]]
+ """The encoding format(s) of the embeddings.
-Input: TypeAlias = Union[str, InputImageURLInput, InputTextInput]
+ Can be a single format or a list of formats.
+ """
diff --git a/src/mixedbread/types/embedding_create_response.py b/src/mixedbread/types/embedding_create_response.py
index ffc8eff2..ba5adbf6 100644
--- a/src/mixedbread/types/embedding_create_response.py
+++ b/src/mixedbread/types/embedding_create_response.py
@@ -1,97 +1,35 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-import builtins
from typing import List, Union, Optional
from typing_extensions import Literal
from .._models import BaseModel
+from .embedding import Embedding
+from .shared.usage import Usage
+from .encoding_format import EncodingFormat
+from .multi_encoding_embedding import MultiEncodingEmbedding
-__all__ = ["EmbeddingCreateResponse", "DataUnionMember0", "DataUnionMember1", "DataUnionMember1Embedding", "Usage"]
-
-
-class DataUnionMember0(BaseModel):
- embedding: Union[List[float], List[int], str]
- """The encoded embedding."""
-
- index: int
- """The index of the embedding."""
-
- object: Literal["embedding"]
- """The object type of the embedding."""
-
-
-class DataUnionMember1Embedding(BaseModel):
- base64: Optional[List[str]] = None
-
- binary: Optional[List[int]] = None
-
- float: Optional[List[builtins.float]] = None
-
- int8: Optional[List[int]] = None
-
- ubinary: Optional[List[int]] = None
-
- uint8: Optional[List[int]] = None
-
-
-class DataUnionMember1(BaseModel):
- embedding: DataUnionMember1Embedding
- """
- The encoded embedding data by encoding format.Returned, if more than one
- encoding format is used.
- """
-
- index: int
- """The index of the embedding."""
-
- object: Literal["embedding_dict"]
- """The object type of the embedding."""
-
-
-class Usage(BaseModel):
- prompt_tokens: int
- """The number of tokens used for the prompt"""
-
- total_tokens: int
- """The total number of tokens used"""
-
- completion_tokens: Optional[int] = None
- """The number of tokens used for the completion"""
+__all__ = ["EmbeddingCreateResponse"]
class EmbeddingCreateResponse(BaseModel):
- data: Union[List[DataUnionMember0], List[DataUnionMember1]]
- """The created embeddings."""
-
- dimensions: Optional[int] = None
- """The number of dimensions used for the embeddings."""
-
- encoding_format: Union[
- Literal["float", "float16", "base64", "binary", "ubinary", "int8", "uint8"],
- List[Literal["float", "float16", "base64", "binary", "ubinary", "int8", "uint8"]],
- ]
- """The encoding format of the embeddings."""
+ usage: Usage
+ """The usage of the model"""
model: str
"""The model used"""
+ data: Union[List[Embedding], List[MultiEncodingEmbedding]]
+ """The created embeddings."""
+
+ object: Optional[Literal["list"]] = None
+ """The object type of the response"""
+
normalized: bool
"""Whether the embeddings are normalized."""
- usage: Usage
- """The usage of the model"""
+ encoding_format: Union[EncodingFormat, List[EncodingFormat]]
+ """The encoding formats of the embeddings."""
- object: Optional[
- Literal[
- "list",
- "job",
- "embedding",
- "embedding_dict",
- "text_document",
- "file",
- "vector_store",
- "vector_store.file",
- "api_key",
- ]
- ] = None
- """The object type of the response"""
+ dimensions: Optional[int] = None
+ """The number of dimensions used for the embeddings."""
diff --git a/src/mixedbread/types/encoding_format.py b/src/mixedbread/types/encoding_format.py
new file mode 100644
index 00000000..74960945
--- /dev/null
+++ b/src/mixedbread/types/encoding_format.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["EncodingFormat"]
+
+EncodingFormat: TypeAlias = Literal["float", "float16", "base64", "binary", "ubinary", "int8", "uint8"]
diff --git a/src/mixedbread/types/expires_after.py b/src/mixedbread/types/expires_after.py
new file mode 100644
index 00000000..4c3843e1
--- /dev/null
+++ b/src/mixedbread/types/expires_after.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from .._models import BaseModel
+
+__all__ = ["ExpiresAfter"]
+
+
+class ExpiresAfter(BaseModel):
+ """Represents an expiration policy for a store."""
+
+ anchor: Optional[Literal["last_active_at"]] = None
+ """Anchor date for the expiration policy"""
+
+ days: Optional[int] = None
+ """Number of days after which the store expires"""
diff --git a/src/mixedbread/types/expires_after_param.py b/src/mixedbread/types/expires_after_param.py
new file mode 100644
index 00000000..1715aacf
--- /dev/null
+++ b/src/mixedbread/types/expires_after_param.py
@@ -0,0 +1,17 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["ExpiresAfterParam"]
+
+
+class ExpiresAfterParam(TypedDict, total=False):
+ """Represents an expiration policy for a store."""
+
+ anchor: Literal["last_active_at"]
+ """Anchor date for the expiration policy"""
+
+ days: int
+ """Number of days after which the store expires"""
diff --git a/src/mixedbread/types/extractions/__init__.py b/src/mixedbread/types/extractions/__init__.py
index 6457c670..ca9fd1eb 100644
--- a/src/mixedbread/types/extractions/__init__.py
+++ b/src/mixedbread/types/extractions/__init__.py
@@ -3,12 +3,14 @@
from __future__ import annotations
from .extraction_job import ExtractionJob as ExtractionJob
+from .text_input_param import TextInputParam as TextInputParam
from .extraction_result import ExtractionResult as ExtractionResult
from .job_create_params import JobCreateParams as JobCreateParams
from .created_json_schema import CreatedJsonSchema as CreatedJsonSchema
from .enhanced_json_schema import EnhancedJsonSchema as EnhancedJsonSchema
from .schema_create_params import SchemaCreateParams as SchemaCreateParams
from .content_create_params import ContentCreateParams as ContentCreateParams
+from .image_url_input_param import ImageURLInputParam as ImageURLInputParam
from .schema_enhance_params import SchemaEnhanceParams as SchemaEnhanceParams
from .validated_json_schema import ValidatedJsonSchema as ValidatedJsonSchema
from .schema_validate_params import SchemaValidateParams as SchemaValidateParams
diff --git a/src/mixedbread/types/extractions/content_create_params.py b/src/mixedbread/types/extractions/content_create_params.py
index 2bbdada2..54faba84 100644
--- a/src/mixedbread/types/extractions/content_create_params.py
+++ b/src/mixedbread/types/extractions/content_create_params.py
@@ -2,14 +2,25 @@
from __future__ import annotations
-from typing_extensions import Required, TypedDict
+from typing import Dict, Union, Iterable, Optional
+from typing_extensions import Required, TypeAlias, TypedDict
-__all__ = ["ContentCreateParams"]
+from ..._types import SequenceNotStr
+from .text_input_param import TextInputParam
+from .image_url_input_param import ImageURLInputParam
+
+__all__ = ["ContentCreateParams", "ContentUnionMember2"]
class ContentCreateParams(TypedDict, total=False):
- content: Required[str]
+ content: Required[Union[str, SequenceNotStr[str], Iterable[ContentUnionMember2]]]
"""The content to extract from"""
- json_schema: Required[object]
+ json_schema: Required[Dict[str, object]]
"""The JSON schema to use for extraction"""
+
+ instructions: Optional[str]
+ """Additional instructions for the extraction"""
+
+
+ContentUnionMember2: TypeAlias = Union[TextInputParam, ImageURLInputParam]
diff --git a/src/mixedbread/types/extractions/created_json_schema.py b/src/mixedbread/types/extractions/created_json_schema.py
index 4b50c4c6..dec87e18 100644
--- a/src/mixedbread/types/extractions/created_json_schema.py
+++ b/src/mixedbread/types/extractions/created_json_schema.py
@@ -1,5 +1,6 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+from typing import Dict
from ..._models import BaseModel
@@ -7,5 +8,7 @@
class CreatedJsonSchema(BaseModel):
- json_schema: object
+ """Result of creating a JSON schema."""
+
+ json_schema: Dict[str, object]
"""The created JSON schema"""
diff --git a/src/mixedbread/types/extractions/enhanced_json_schema.py b/src/mixedbread/types/extractions/enhanced_json_schema.py
index 7b2ab04a..2e789cce 100644
--- a/src/mixedbread/types/extractions/enhanced_json_schema.py
+++ b/src/mixedbread/types/extractions/enhanced_json_schema.py
@@ -1,5 +1,6 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+from typing import Dict
from ..._models import BaseModel
@@ -7,5 +8,7 @@
class EnhancedJsonSchema(BaseModel):
- json_schema: object
+ """Result of enhancing a JSON schema."""
+
+ json_schema: Dict[str, object]
"""The enhanced JSON schema"""
diff --git a/src/mixedbread/types/extractions/extraction_job.py b/src/mixedbread/types/extractions/extraction_job.py
index d6e204c9..b3bececf 100644
--- a/src/mixedbread/types/extractions/extraction_job.py
+++ b/src/mixedbread/types/extractions/extraction_job.py
@@ -1,33 +1,47 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import List, Optional
+from typing import Dict, Optional
from datetime import datetime
-from typing_extensions import Literal
from ..._models import BaseModel
from .extraction_result import ExtractionResult
+from ..parsing.parsing_job_status import ParsingJobStatus
__all__ = ["ExtractionJob"]
class ExtractionJob(BaseModel):
+ """A job for extracting structured data from documents."""
+
id: str
- """The ID of the job"""
+ """Unique identifier for the extraction job"""
- result: Optional[ExtractionResult] = None
- """Result of an extraction operation."""
+ organization_id: str
+ """ID of the organization that owns this job"""
- status: Literal["none", "running", "canceled", "successful", "failed", "resumable", "pending"]
- """The status of the job"""
+ file_id: str
+ """ID of the file being extracted"""
- created_at: Optional[datetime] = None
- """The creation time of the job"""
+ created_at: datetime
+ """When the job was created"""
- errors: Optional[List[str]] = None
- """The errors of the job"""
+ updated_at: datetime
+ """When the job was last updated"""
+
+ started_at: Optional[datetime] = None
+ """When the job started processing"""
finished_at: Optional[datetime] = None
- """The finished time of the job"""
+ """When the job finished processing"""
+
+ status: ParsingJobStatus
+ """Current status of the job"""
+
+ result: Optional[ExtractionResult] = None
+ """The result of an extraction job."""
+
+ error: Optional[Dict[str, object]] = None
+ """Error information if failed"""
- object: Optional[Literal["job"]] = None
- """The type of the object"""
+ json_schema: Dict[str, object]
+ """The JSON schema used for extraction"""
diff --git a/src/mixedbread/types/extractions/extraction_result.py b/src/mixedbread/types/extractions/extraction_result.py
index fbac5d5f..fdf23e83 100644
--- a/src/mixedbread/types/extractions/extraction_result.py
+++ b/src/mixedbread/types/extractions/extraction_result.py
@@ -1,5 +1,6 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+from typing import Dict, List
from ..._models import BaseModel
@@ -7,5 +8,8 @@
class ExtractionResult(BaseModel):
- data: object
- """The extracted data"""
+ """The result of an extraction job."""
+
+ data: Dict[str, object]
+
+ warnings: List[str]
diff --git a/src/mixedbread/types/extractions/image_url_input_param.py b/src/mixedbread/types/extractions/image_url_input_param.py
new file mode 100644
index 00000000..cb631e56
--- /dev/null
+++ b/src/mixedbread/types/extractions/image_url_input_param.py
@@ -0,0 +1,27 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ImageURLInputParam", "ImageURL"]
+
+
+class ImageURL(TypedDict, total=False):
+ """The image input specification."""
+
+ url: Required[str]
+ """The image URL. Can be either a URL or a Data URI."""
+
+ format: str
+ """The image format/mimetype"""
+
+
+class ImageURLInputParam(TypedDict, total=False):
+ """Model for image input validation."""
+
+ type: Literal["image_url"]
+ """Input type identifier"""
+
+ image_url: Required[ImageURL]
+ """The image input specification."""
diff --git a/src/mixedbread/types/extractions/job_create_params.py b/src/mixedbread/types/extractions/job_create_params.py
index 476154b2..a252df4a 100644
--- a/src/mixedbread/types/extractions/job_create_params.py
+++ b/src/mixedbread/types/extractions/job_create_params.py
@@ -2,6 +2,7 @@
from __future__ import annotations
+from typing import Dict
from typing_extensions import Required, TypedDict
__all__ = ["JobCreateParams"]
@@ -11,5 +12,5 @@ class JobCreateParams(TypedDict, total=False):
file_id: Required[str]
"""The ID of the file to extract from"""
- json_schema: Required[object]
+ json_schema: Required[Dict[str, object]]
"""The JSON schema to use for extraction"""
diff --git a/src/mixedbread/types/extractions/schema_enhance_params.py b/src/mixedbread/types/extractions/schema_enhance_params.py
index e0facf34..8526c898 100644
--- a/src/mixedbread/types/extractions/schema_enhance_params.py
+++ b/src/mixedbread/types/extractions/schema_enhance_params.py
@@ -2,11 +2,12 @@
from __future__ import annotations
+from typing import Dict
from typing_extensions import Required, TypedDict
__all__ = ["SchemaEnhanceParams"]
class SchemaEnhanceParams(TypedDict, total=False):
- json_schema: Required[object]
+ json_schema: Required[Dict[str, object]]
"""The JSON schema to enhance"""
diff --git a/src/mixedbread/types/extractions/schema_validate_params.py b/src/mixedbread/types/extractions/schema_validate_params.py
index 947182ca..96a31b4f 100644
--- a/src/mixedbread/types/extractions/schema_validate_params.py
+++ b/src/mixedbread/types/extractions/schema_validate_params.py
@@ -2,11 +2,12 @@
from __future__ import annotations
+from typing import Dict
from typing_extensions import Required, TypedDict
__all__ = ["SchemaValidateParams"]
class SchemaValidateParams(TypedDict, total=False):
- json_schema: Required[object]
+ json_schema: Required[Dict[str, object]]
"""The JSON schema to validate"""
diff --git a/src/mixedbread/types/extractions/text_input_param.py b/src/mixedbread/types/extractions/text_input_param.py
new file mode 100644
index 00000000..9b459cf5
--- /dev/null
+++ b/src/mixedbread/types/extractions/text_input_param.py
@@ -0,0 +1,22 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["TextInputParam"]
+
+
+class TextInputParam(TypedDict, total=False):
+ """Model for text input validation.
+
+ Attributes:
+ type: Input type identifier, always "text"
+ text: The actual text content, with length and whitespace constraints
+ """
+
+ type: Literal["text"]
+ """Input type identifier"""
+
+ text: Required[str]
+ """Text content to process"""
diff --git a/src/mixedbread/types/extractions/validated_json_schema.py b/src/mixedbread/types/extractions/validated_json_schema.py
index 42e6da5b..79b2dff5 100644
--- a/src/mixedbread/types/extractions/validated_json_schema.py
+++ b/src/mixedbread/types/extractions/validated_json_schema.py
@@ -1,6 +1,6 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import List
+from typing import Dict, List
from ..._models import BaseModel
@@ -8,11 +8,13 @@
class ValidatedJsonSchema(BaseModel):
- errors: List[str]
- """List of validation errors"""
+ """Result of validating a JSON schema."""
is_valid: bool
"""Whether the schema is valid"""
- json_schema: object
+ errors: List[str]
+ """List of validation errors"""
+
+ json_schema: Dict[str, object]
"""The validated JSON schema"""
diff --git a/src/mixedbread/types/file_deleted.py b/src/mixedbread/types/file_delete_response.py
similarity index 85%
rename from src/mixedbread/types/file_deleted.py
rename to src/mixedbread/types/file_delete_response.py
index 5557b674..1cc6bbb7 100644
--- a/src/mixedbread/types/file_deleted.py
+++ b/src/mixedbread/types/file_delete_response.py
@@ -5,10 +5,10 @@
from .._models import BaseModel
-__all__ = ["FileDeleted"]
+__all__ = ["FileDeleteResponse"]
-class FileDeleted(BaseModel):
+class FileDeleteResponse(BaseModel):
id: str
"""The ID of the deleted file"""
diff --git a/src/mixedbread/types/file_list_params.py b/src/mixedbread/types/file_list_params.py
index 7ecfdf54..83207e3d 100644
--- a/src/mixedbread/types/file_list_params.py
+++ b/src/mixedbread/types/file_list_params.py
@@ -2,6 +2,7 @@
from __future__ import annotations
+from typing import Optional
from typing_extensions import TypedDict
__all__ = ["FileListParams"]
@@ -9,7 +10,22 @@
class FileListParams(TypedDict, total=False):
limit: int
- """Maximum number of items to return per page"""
+ """Maximum number of items to return per page (1-100)"""
- offset: int
- """Offset of the first item to return"""
+ 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)"""
+
+ q: Optional[str]
+ """Search query for fuzzy matching over name and description fields"""
diff --git a/src/mixedbread/types/file_object.py b/src/mixedbread/types/file_object.py
index 0b20b6fe..f677bd7f 100644
--- a/src/mixedbread/types/file_object.py
+++ b/src/mixedbread/types/file_object.py
@@ -8,23 +8,29 @@
class FileObject(BaseModel):
- id: str
- """Unique identifier for the file"""
+ """A model representing a file object in the system.
- bytes: int
- """Size of the file in bytes"""
+ This model contains metadata about files stored in the system, including
+ identifiers, size information, and timestamps.
+ """
- created_at: datetime
- """Timestamp when the file was created"""
+ id: str
+ """Unique identifier for the file"""
filename: str
"""Name of the file including extension"""
+ bytes: int
+ """Size of the file in bytes"""
+
mime_type: str
"""MIME type of the file"""
- updated_at: datetime
- """Timestamp when the file was last updated"""
-
version: int
"""Version of the file"""
+
+ created_at: datetime
+ """Timestamp when the file was created"""
+
+ updated_at: datetime
+ """Timestamp when the file was last updated"""
diff --git a/src/mixedbread/types/files/__init__.py b/src/mixedbread/types/files/__init__.py
index f8ee8b14..5b8dff28 100644
--- a/src/mixedbread/types/files/__init__.py
+++ b/src/mixedbread/types/files/__init__.py
@@ -1,3 +1,13 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
from __future__ import annotations
+
+from .upload_create_params import UploadCreateParams as UploadCreateParams
+from .upload_list_response import UploadListResponse as UploadListResponse
+from .multipart_upload_part import MultipartUploadPart as MultipartUploadPart
+from .upload_abort_response import UploadAbortResponse as UploadAbortResponse
+from .upload_complete_params import UploadCompleteParams as UploadCompleteParams
+from .upload_create_response import UploadCreateResponse as UploadCreateResponse
+from .upload_retrieve_response import UploadRetrieveResponse as UploadRetrieveResponse
+from .multipart_upload_part_url import MultipartUploadPartURL as MultipartUploadPartURL
+from .multipart_upload_part_param import MultipartUploadPartParam as MultipartUploadPartParam
diff --git a/src/mixedbread/types/files/multipart_upload_part.py b/src/mixedbread/types/files/multipart_upload_part.py
new file mode 100644
index 00000000..f3f6a226
--- /dev/null
+++ b/src/mixedbread/types/files/multipart_upload_part.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from ..._models import BaseModel
+
+__all__ = ["MultipartUploadPart"]
+
+
+class MultipartUploadPart(BaseModel):
+ part_number: int
+ """1-based part number"""
+
+ etag: str
+ """ETag returned by the storage backend after uploading the part"""
diff --git a/src/mixedbread/types/files/multipart_upload_part_param.py b/src/mixedbread/types/files/multipart_upload_part_param.py
new file mode 100644
index 00000000..78552c46
--- /dev/null
+++ b/src/mixedbread/types/files/multipart_upload_part_param.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["MultipartUploadPartParam"]
+
+
+class MultipartUploadPartParam(TypedDict, total=False):
+ part_number: Required[int]
+ """1-based part number"""
+
+ etag: Required[str]
+ """ETag returned by the storage backend after uploading the part"""
diff --git a/src/mixedbread/types/files/multipart_upload_part_url.py b/src/mixedbread/types/files/multipart_upload_part_url.py
new file mode 100644
index 00000000..ed51231e
--- /dev/null
+++ b/src/mixedbread/types/files/multipart_upload_part_url.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from ..._models import BaseModel
+
+__all__ = ["MultipartUploadPartURL"]
+
+
+class MultipartUploadPartURL(BaseModel):
+ part_number: int
+ """1-based part number"""
+
+ url: str
+ """Presigned URL for uploading this part"""
diff --git a/src/mixedbread/types/files/upload_abort_response.py b/src/mixedbread/types/files/upload_abort_response.py
new file mode 100644
index 00000000..aa8f02db
--- /dev/null
+++ b/src/mixedbread/types/files/upload_abort_response.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["UploadAbortResponse"]
+
+
+class UploadAbortResponse(BaseModel):
+ id: str
+ """The ID of the deleted file"""
+
+ deleted: Optional[bool] = None
+ """Whether the file was deleted"""
+
+ object: Optional[Literal["file"]] = None
+ """The type of the deleted object"""
diff --git a/src/mixedbread/types/files/upload_complete_params.py b/src/mixedbread/types/files/upload_complete_params.py
new file mode 100644
index 00000000..6e59c5a4
--- /dev/null
+++ b/src/mixedbread/types/files/upload_complete_params.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Iterable
+from typing_extensions import Required, TypedDict
+
+from .multipart_upload_part_param import MultipartUploadPartParam
+
+__all__ = ["UploadCompleteParams"]
+
+
+class UploadCompleteParams(TypedDict, total=False):
+ parts: Required[Iterable[MultipartUploadPartParam]]
+ """List of completed parts with their ETags"""
diff --git a/src/mixedbread/types/files/upload_create_params.py b/src/mixedbread/types/files/upload_create_params.py
new file mode 100644
index 00000000..8ce2656e
--- /dev/null
+++ b/src/mixedbread/types/files/upload_create_params.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["UploadCreateParams"]
+
+
+class UploadCreateParams(TypedDict, total=False):
+ filename: Required[str]
+ """Name of the file including extension"""
+
+ file_size: Required[int]
+ """Total size of the file in bytes"""
+
+ mime_type: Required[str]
+ """MIME type of the file"""
+
+ part_count: int
+ """Number of parts to split the upload into"""
diff --git a/src/mixedbread/types/files/upload_create_response.py b/src/mixedbread/types/files/upload_create_response.py
new file mode 100644
index 00000000..30a6dc0c
--- /dev/null
+++ b/src/mixedbread/types/files/upload_create_response.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+
+from ..._models import BaseModel
+from .multipart_upload_part_url import MultipartUploadPartURL
+
+__all__ = ["UploadCreateResponse"]
+
+
+class UploadCreateResponse(BaseModel):
+ id: str
+ """The multipart upload ID (use this to complete or abort)"""
+
+ part_urls: List[MultipartUploadPartURL]
+ """Presigned URLs for uploading parts"""
diff --git a/src/mixedbread/types/files/upload_list_response.py b/src/mixedbread/types/files/upload_list_response.py
new file mode 100644
index 00000000..f44a8e14
--- /dev/null
+++ b/src/mixedbread/types/files/upload_list_response.py
@@ -0,0 +1,32 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+
+from ..._models import BaseModel
+
+__all__ = ["UploadListResponse", "Data"]
+
+
+class Data(BaseModel):
+ id: str
+ """The multipart upload record ID"""
+
+ filename: str
+ """Original filename"""
+
+ file_size: int
+ """Total file size in bytes"""
+
+ mime_type: str
+ """MIME type of the file"""
+
+ part_count: int
+ """Number of parts the file was split into"""
+
+ created_at: str
+ """When the upload was initiated"""
+
+
+class UploadListResponse(BaseModel):
+ data: List[Data]
+ """List of in-progress multipart uploads"""
diff --git a/src/mixedbread/types/files/upload_retrieve_response.py b/src/mixedbread/types/files/upload_retrieve_response.py
new file mode 100644
index 00000000..eda17521
--- /dev/null
+++ b/src/mixedbread/types/files/upload_retrieve_response.py
@@ -0,0 +1,35 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List
+
+from ..._models import BaseModel
+from .multipart_upload_part import MultipartUploadPart
+from .multipart_upload_part_url import MultipartUploadPartURL
+
+__all__ = ["UploadRetrieveResponse"]
+
+
+class UploadRetrieveResponse(BaseModel):
+ id: str
+ """The multipart upload record ID"""
+
+ filename: str
+ """Original filename"""
+
+ file_size: int
+ """Total file size in bytes"""
+
+ mime_type: str
+ """MIME type of the file"""
+
+ part_count: int
+ """Number of parts the file was split into"""
+
+ created_at: str
+ """When the upload was initiated"""
+
+ completed_parts: List[MultipartUploadPart]
+ """Parts that have already been uploaded"""
+
+ part_urls: List[MultipartUploadPartURL]
+ """Presigned URLs for the parts that still need to be uploaded"""
diff --git a/src/mixedbread/types/info_response.py b/src/mixedbread/types/info_response.py
index fee93225..20844293 100644
--- a/src/mixedbread/types/info_response.py
+++ b/src/mixedbread/types/info_response.py
@@ -1,12 +1,13 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
from .._models import BaseModel
__all__ = ["InfoResponse"]
class InfoResponse(BaseModel):
+ """Info Pydantic Response Service Message"""
+
name: str
version: str
diff --git a/src/mixedbread/types/linear_data_source_param.py b/src/mixedbread/types/linear_data_source_param.py
new file mode 100644
index 00000000..69c2b431
--- /dev/null
+++ b/src/mixedbread/types/linear_data_source_param.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+from .oauth2_params import Oauth2Params
+
+__all__ = ["LinearDataSourceParam"]
+
+
+class LinearDataSourceParam(TypedDict, total=False):
+ """Parameters for creating or updating a Linear data source."""
+
+ type: Literal["linear"]
+ """The type of data source to create"""
+
+ name: Required[str]
+ """The name of the data source"""
+
+ metadata: object
+ """The metadata of the data source"""
+
+ auth_params: Optional[Oauth2Params]
+ """Base class for OAuth2 create or update parameters."""
diff --git a/src/mixedbread/types/multi_encoding_embedding.py b/src/mixedbread/types/multi_encoding_embedding.py
new file mode 100644
index 00000000..1d73d05c
--- /dev/null
+++ b/src/mixedbread/types/multi_encoding_embedding.py
@@ -0,0 +1,41 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+import builtins
+from typing import List, Optional
+from typing_extensions import Literal
+
+from .._models import BaseModel
+
+__all__ = ["MultiEncodingEmbedding", "Embedding"]
+
+
+class Embedding(BaseModel):
+ """
+ The encoded embedding data by encoding format.Returned, if more than one encoding format is used.
+ """
+
+ float: Optional[List[builtins.float]] = None
+
+ int8: Optional[List[int]] = None
+
+ uint8: Optional[List[int]] = None
+
+ binary: Optional[List[int]] = None
+
+ ubinary: Optional[List[int]] = None
+
+ base64: Optional[str] = None
+
+
+class MultiEncodingEmbedding(BaseModel):
+ embedding: Embedding
+ """
+ The encoded embedding data by encoding format.Returned, if more than one
+ encoding format is used.
+ """
+
+ index: int
+ """The index of the embedding."""
+
+ object: Optional[Literal["embedding_dict"]] = None
+ """The object type of the embedding."""
diff --git a/src/mixedbread/types/notion_data_source_param.py b/src/mixedbread/types/notion_data_source_param.py
new file mode 100644
index 00000000..12409ef6
--- /dev/null
+++ b/src/mixedbread/types/notion_data_source_param.py
@@ -0,0 +1,41 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Optional
+from typing_extensions import Literal, Required, TypeAlias, TypedDict
+
+from .oauth2_params import Oauth2Params
+
+__all__ = ["NotionDataSourceParam", "AuthParams", "AuthParamsAPIKeyCreateOrUpdateParams"]
+
+
+class AuthParamsAPIKeyCreateOrUpdateParams(TypedDict, total=False):
+ """Base class for API key create or update parameters."""
+
+ type: Literal["api_key"]
+
+ api_key: Required[str]
+ """The API key"""
+
+
+AuthParams: TypeAlias = Union[Oauth2Params, AuthParamsAPIKeyCreateOrUpdateParams]
+
+
+class NotionDataSourceParam(TypedDict, total=False):
+ """Parameters for creating or updating a Notion data source."""
+
+ type: Literal["notion"]
+ """The type of data source to create"""
+
+ name: Required[str]
+ """The name of the data source"""
+
+ metadata: object
+ """The metadata of the data source"""
+
+ auth_params: Optional[AuthParams]
+ """The authentication parameters of the data source.
+
+ Notion supports OAuth2 and API key.
+ """
diff --git a/src/mixedbread/types/oauth2_params.py b/src/mixedbread/types/oauth2_params.py
new file mode 100644
index 00000000..4bbc79f6
--- /dev/null
+++ b/src/mixedbread/types/oauth2_params.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["Oauth2Params"]
+
+
+class Oauth2Params(TypedDict, total=False):
+ """Base class for OAuth2 create or update parameters."""
+
+ type: Literal["oauth2"]
diff --git a/src/mixedbread/types/file_list_response.py b/src/mixedbread/types/pagination_with_total.py
similarity index 52%
rename from src/mixedbread/types/file_list_response.py
rename to src/mixedbread/types/pagination_with_total.py
index defc63f1..07ad8fb0 100644
--- a/src/mixedbread/types/file_list_response.py
+++ b/src/mixedbread/types/pagination_with_total.py
@@ -1,15 +1,15 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import List, Optional
-from typing_extensions import Literal
+from typing import Optional
from .._models import BaseModel
-from .file_object import FileObject
-__all__ = ["FileListResponse", "Pagination"]
+__all__ = ["PaginationWithTotal"]
-class Pagination(BaseModel):
+class PaginationWithTotal(BaseModel):
+ """Pagination model that includes total count of items."""
+
limit: Optional[int] = None
"""Maximum number of items to return per page"""
@@ -18,14 +18,3 @@ class Pagination(BaseModel):
total: Optional[int] = None
"""Total number of items available"""
-
-
-class FileListResponse(BaseModel):
- data: List[FileObject]
- """The list of files"""
-
- pagination: Pagination
- """Pagination model that includes total count of items."""
-
- object: Optional[Literal["list"]] = None
- """The object type of the response"""
diff --git a/src/mixedbread/types/parsing/__init__.py b/src/mixedbread/types/parsing/__init__.py
index 94393293..d3e38bbe 100644
--- a/src/mixedbread/types/parsing/__init__.py
+++ b/src/mixedbread/types/parsing/__init__.py
@@ -3,4 +3,11 @@
from __future__ import annotations
from .parsing_job import ParsingJob as ParsingJob
+from .element_type import ElementType as ElementType
+from .return_format import ReturnFormat as ReturnFormat
+from .job_list_params import JobListParams as JobListParams
+from .chunking_strategy import ChunkingStrategy as ChunkingStrategy
from .job_create_params import JobCreateParams as JobCreateParams
+from .job_list_response import JobListResponse as JobListResponse
+from .parsing_job_status import ParsingJobStatus as ParsingJobStatus
+from .job_delete_response import JobDeleteResponse as JobDeleteResponse
diff --git a/src/mixedbread/types/parsing/chunking_strategy.py b/src/mixedbread/types/parsing/chunking_strategy.py
new file mode 100644
index 00000000..7848a25f
--- /dev/null
+++ b/src/mixedbread/types/parsing/chunking_strategy.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["ChunkingStrategy"]
+
+ChunkingStrategy: TypeAlias = Literal["page"]
diff --git a/src/mixedbread/types/parsing/element_type.py b/src/mixedbread/types/parsing/element_type.py
new file mode 100644
index 00000000..e655f0b3
--- /dev/null
+++ b/src/mixedbread/types/parsing/element_type.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["ElementType"]
+
+ElementType: TypeAlias = Literal[
+ "header",
+ "footer",
+ "title",
+ "section-header",
+ "page-number",
+ "list-item",
+ "figure",
+ "table",
+ "form",
+ "text",
+ "footnote",
+]
diff --git a/src/mixedbread/types/parsing/job_create_params.py b/src/mixedbread/types/parsing/job_create_params.py
index a0d4602d..7f624260 100644
--- a/src/mixedbread/types/parsing/job_create_params.py
+++ b/src/mixedbread/types/parsing/job_create_params.py
@@ -5,6 +5,10 @@
from typing import List, Optional
from typing_extensions import Literal, Required, TypedDict
+from .element_type import ElementType
+from .return_format import ReturnFormat
+from .chunking_strategy import ChunkingStrategy
+
__all__ = ["JobCreateParams"]
@@ -12,27 +16,14 @@ class JobCreateParams(TypedDict, total=False):
file_id: Required[str]
"""The ID of the file to parse"""
- chunking_strategy: Literal["page"]
- """The strategy to use for chunking the content"""
-
- element_types: Optional[
- List[
- Literal[
- "caption",
- "footnote",
- "formula",
- "list-item",
- "page-footer",
- "page-header",
- "picture",
- "section-header",
- "table",
- "text",
- "title",
- ]
- ]
- ]
+ element_types: Optional[List[ElementType]]
"""The elements to extract from the document"""
- return_format: Literal["html", "markdown", "plain"]
+ chunking_strategy: ChunkingStrategy
+ """The strategy to use for chunking the content"""
+
+ return_format: ReturnFormat
"""The format of the returned content"""
+
+ mode: Literal["fast", "high_quality"]
+ """The strategy to use for OCR"""
diff --git a/src/mixedbread/types/parsing/job_delete_response.py b/src/mixedbread/types/parsing/job_delete_response.py
new file mode 100644
index 00000000..3f2cccf9
--- /dev/null
+++ b/src/mixedbread/types/parsing/job_delete_response.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["JobDeleteResponse"]
+
+
+class JobDeleteResponse(BaseModel):
+ """A deleted parsing job."""
+
+ id: str
+ """The ID of the deleted job"""
+
+ deleted: Optional[bool] = None
+ """Whether the job was deleted"""
+
+ object: Optional[Literal["parsing_job"]] = None
+ """The type of the object"""
diff --git a/src/mixedbread/types/parsing/job_list_params.py b/src/mixedbread/types/parsing/job_list_params.py
new file mode 100644
index 00000000..c34f3c09
--- /dev/null
+++ b/src/mixedbread/types/parsing/job_list_params.py
@@ -0,0 +1,36 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import List, Optional
+from typing_extensions import TypedDict
+
+from .parsing_job_status import ParsingJobStatus
+
+__all__ = ["JobListParams"]
+
+
+class JobListParams(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[ParsingJobStatus]]
+ """Status to filter by"""
+
+ q: Optional[str]
+ """Search query to filter by"""
diff --git a/src/mixedbread/types/parsing/job_list_response.py b/src/mixedbread/types/parsing/job_list_response.py
new file mode 100644
index 00000000..972537f9
--- /dev/null
+++ b/src/mixedbread/types/parsing/job_list_response.py
@@ -0,0 +1,44 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict, Optional
+from datetime import datetime
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+from .parsing_job_status import ParsingJobStatus
+
+__all__ = ["JobListResponse"]
+
+
+class JobListResponse(BaseModel):
+ """A parsing job item for list responses."""
+
+ id: str
+ """The ID of the job"""
+
+ file_id: str
+ """The ID of the file to parse"""
+
+ filename: Optional[str] = None
+ """The name of the file"""
+
+ status: ParsingJobStatus
+ """The status of the job"""
+
+ error: Optional[Dict[str, object]] = None
+ """The error of the job"""
+
+ started_at: Optional[datetime] = None
+ """The started time of the job"""
+
+ finished_at: Optional[datetime] = None
+ """The finished time of the job"""
+
+ created_at: Optional[datetime] = None
+ """The creation time of the job"""
+
+ updated_at: Optional[datetime] = None
+ """The updated time of the job"""
+
+ object: Optional[Literal["parsing_job"]] = None
+ """The type of the object"""
diff --git a/src/mixedbread/types/parsing/parsing_job.py b/src/mixedbread/types/parsing/parsing_job.py
index e457e260..2f3d250d 100644
--- a/src/mixedbread/types/parsing/parsing_job.py
+++ b/src/mixedbread/types/parsing/parsing_job.py
@@ -1,103 +1,107 @@
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-from typing import List, Optional
+from typing import Dict, List, Optional
from datetime import datetime
from typing_extensions import Literal
from ..._models import BaseModel
+from .element_type import ElementType
+from .return_format import ReturnFormat
+from .chunking_strategy import ChunkingStrategy
+from .parsing_job_status import ParsingJobStatus
__all__ = ["ParsingJob", "Result", "ResultChunk", "ResultChunkElement"]
class ResultChunkElement(BaseModel):
- bbox: List[object]
- """The bounding box coordinates [x1, y1, x2, y2]"""
+ """Represents an extracted element from a document with its content and metadata."""
+
+ type: ElementType
+ """The type of the extracted element"""
confidence: float
"""The confidence score of the extraction"""
- content: str
- """The full content of the extracted element"""
+ bbox: List[object]
+ """The bounding box coordinates [x1, y1, x2, y2]"""
page: int
"""The page number where the element was found"""
- type: Literal[
- "caption",
- "footnote",
- "formula",
- "list-item",
- "page-footer",
- "page-header",
- "picture",
- "section-header",
- "table",
- "text",
- "title",
- ]
- """The type of the extracted element"""
+ content: str
+ """The extracted text content of the element"""
summary: Optional[str] = None
"""A brief summary of the element's content"""
+ image: Optional[str] = None
+ """The base64-encoded image data for figure elements"""
+
class ResultChunk(BaseModel):
- content: str
+ """A chunk of text extracted from a document page."""
+
+ content: Optional[str] = None
"""The full content of the chunk"""
content_to_embed: str
- """The content to be used for embedding"""
+ """The content of the chunk to embed"""
elements: List[ResultChunkElement]
"""List of elements contained in this chunk"""
class Result(BaseModel):
- chunking_strategy: Literal["page"]
+ """Result of document parsing operation."""
+
+ chunking_strategy: ChunkingStrategy
"""The strategy used for chunking the document"""
- chunks: List[ResultChunk]
- """List of extracted chunks from the document"""
+ return_format: ReturnFormat
+ """The format of the returned content"""
- element_types: List[
- Literal[
- "caption",
- "footnote",
- "formula",
- "list-item",
- "page-footer",
- "page-header",
- "picture",
- "section-header",
- "table",
- "text",
- "title",
- ]
- ]
+ element_types: List[ElementType]
"""The types of elements extracted"""
- return_format: Literal["html", "markdown", "plain"]
- """The format of the returned content"""
+ chunks: List[ResultChunk]
+ """List of extracted chunks from the document"""
+
+ page_sizes: Optional[List[List[object]]] = None
+ """List of (width, height) tuples for each page"""
class ParsingJob(BaseModel):
+ """A job for parsing documents with its current state and result."""
+
id: str
"""The ID of the job"""
- status: Literal["none", "running", "canceled", "successful", "failed", "resumable", "pending"]
+ file_id: str
+ """The ID of the file to parse"""
+
+ filename: Optional[str] = None
+ """The name of the file"""
+
+ status: ParsingJobStatus
"""The status of the job"""
- created_at: Optional[datetime] = None
- """The creation time of the job"""
+ error: Optional[Dict[str, object]] = None
+ """The error of the job"""
+
+ result: Optional[Result] = None
+ """Result of document parsing operation."""
- errors: Optional[List[str]] = None
- """The errors of the job"""
+ started_at: Optional[datetime] = None
+ """The started time of the job"""
finished_at: Optional[datetime] = None
"""The finished time of the job"""
- object: Optional[Literal["job"]] = None
- """The type of the object"""
+ created_at: Optional[datetime] = None
+ """The creation time of the job"""
- result: Optional[Result] = None
- """Result of document parsing operation."""
+ updated_at: Optional[datetime] = None
+ """The updated time of the job"""
+
+ object: Optional[Literal["parsing_job"]] = None
+ """The type of the object"""
diff --git a/src/mixedbread/types/parsing/parsing_job_status.py b/src/mixedbread/types/parsing/parsing_job_status.py
new file mode 100644
index 00000000..03e03a48
--- /dev/null
+++ b/src/mixedbread/types/parsing/parsing_job_status.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["ParsingJobStatus"]
+
+ParsingJobStatus: TypeAlias = Literal["pending", "in_progress", "cancelled", "completed", "failed"]
diff --git a/src/mixedbread/types/parsing/return_format.py b/src/mixedbread/types/parsing/return_format.py
new file mode 100644
index 00000000..2b080722
--- /dev/null
+++ b/src/mixedbread/types/parsing/return_format.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["ReturnFormat"]
+
+ReturnFormat: TypeAlias = Literal["html", "markdown", "plain"]
diff --git a/src/mixedbread/types/rerank_response.py b/src/mixedbread/types/rerank_response.py
new file mode 100644
index 00000000..73ebfaaa
--- /dev/null
+++ b/src/mixedbread/types/rerank_response.py
@@ -0,0 +1,43 @@
+# 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 .shared.usage import Usage
+
+__all__ = ["RerankResponse", "Data"]
+
+
+class Data(BaseModel):
+ index: int
+ """The index of the document."""
+
+ score: float
+ """The score of the document."""
+
+ input: Optional[object] = None
+ """The input document."""
+
+ object: Optional[Literal["rank_result"]] = None
+ """The object type."""
+
+
+class RerankResponse(BaseModel):
+ usage: Usage
+ """The usage of the model"""
+
+ model: str
+ """The model used"""
+
+ data: List[Data]
+ """The ranked documents."""
+
+ object: Optional[Literal["list"]] = None
+ """The object type of the response"""
+
+ top_k: int
+ """The number of documents to return."""
+
+ return_input: bool
+ """Whether to return the documents."""
diff --git a/src/mixedbread/types/reranking_create_response.py b/src/mixedbread/types/reranking_create_response.py
deleted file mode 100644
index 28710545..00000000
--- a/src/mixedbread/types/reranking_create_response.py
+++ /dev/null
@@ -1,76 +0,0 @@
-# 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
-
-__all__ = ["RerankingCreateResponse", "Data", "Usage"]
-
-
-class Data(BaseModel):
- index: int
-
- input: object
- """The input document."""
-
- score: float
- """The score of the document."""
-
- object: Optional[
- Literal[
- "list",
- "job",
- "embedding",
- "embedding_dict",
- "text_document",
- "file",
- "vector_store",
- "vector_store.file",
- "api_key",
- ]
- ] = None
- """The object type."""
-
-
-class Usage(BaseModel):
- prompt_tokens: int
- """The number of tokens used for the prompt"""
-
- total_tokens: int
- """The total number of tokens used"""
-
- completion_tokens: Optional[int] = None
- """The number of tokens used for the completion"""
-
-
-class RerankingCreateResponse(BaseModel):
- data: List[Data]
- """The ranked documents."""
-
- model: str
- """The model used"""
-
- return_input: bool
- """Whether to return the documents."""
-
- top_k: int
- """The number of documents to return."""
-
- usage: Usage
- """The usage of the model"""
-
- object: Optional[
- Literal[
- "list",
- "job",
- "embedding",
- "embedding_dict",
- "text_document",
- "file",
- "vector_store",
- "vector_store.file",
- "api_key",
- ]
- ] = None
- """The object type of the response"""
diff --git a/src/mixedbread/types/scored_audio_url_input_chunk.py b/src/mixedbread/types/scored_audio_url_input_chunk.py
new file mode 100644
index 00000000..95292f9a
--- /dev/null
+++ b/src/mixedbread/types/scored_audio_url_input_chunk.py
@@ -0,0 +1,315 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import TYPE_CHECKING, Dict, List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from pydantic import Field as FieldInfo
+
+from .._utils import PropertyInfo
+from .._models import BaseModel
+
+__all__ = [
+ "ScoredAudioURLInputChunk",
+ "GeneratedMetadata",
+ "GeneratedMetadataMarkdownChunkGeneratedMetadata",
+ "GeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading",
+ "GeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext",
+ "GeneratedMetadataTextChunkGeneratedMetadata",
+ "GeneratedMetadataPdfChunkGeneratedMetadata",
+ "GeneratedMetadataCodeChunkGeneratedMetadata",
+ "GeneratedMetadataAudioChunkGeneratedMetadata",
+ "GeneratedMetadataVideoChunkGeneratedMetadata",
+ "GeneratedMetadataImageChunkGeneratedMetadata",
+ "AudioURL",
+]
+
+
+class GeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading(BaseModel):
+ level: int
+
+ text: str
+
+
+class GeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext(BaseModel):
+ level: int
+
+ text: str
+
+
+class GeneratedMetadataMarkdownChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["markdown"]] = None
+
+ file_type: Optional[Literal["text/markdown"]] = None
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ chunk_headings: Optional[List[GeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading]] = None
+
+ heading_context: Optional[List[GeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext]] = None
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ frontmatter: Optional[Dict[str, object]] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataTextChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["text"]] = None
+
+ file_type: Optional[Literal["text/plain"]] = None
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataPdfChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["pdf"]] = None
+
+ file_type: Optional[Literal["application/pdf"]] = None
+
+ total_pages: int
+
+ total_size: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataCodeChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["code"]] = None
+
+ file_type: str
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataAudioChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["audio"]] = None
+
+ file_type: str
+
+ file_size: int
+
+ total_duration_seconds: float
+
+ sample_rate: int
+
+ channels: int
+
+ audio_format: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataVideoChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["video"]] = None
+
+ file_type: str
+
+ file_size: Optional[int] = None
+
+ total_duration_seconds: float
+
+ fps: float
+
+ width: int
+
+ height: int
+
+ frame_count: int
+
+ has_audio_stream: Optional[bool] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataImageChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["image"]] = None
+
+ file_type: str
+
+ file_size: int
+
+ width: int
+
+ height: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+GeneratedMetadata: TypeAlias = Annotated[
+ Union[
+ GeneratedMetadataMarkdownChunkGeneratedMetadata,
+ GeneratedMetadataTextChunkGeneratedMetadata,
+ GeneratedMetadataPdfChunkGeneratedMetadata,
+ GeneratedMetadataCodeChunkGeneratedMetadata,
+ GeneratedMetadataAudioChunkGeneratedMetadata,
+ GeneratedMetadataVideoChunkGeneratedMetadata,
+ GeneratedMetadataImageChunkGeneratedMetadata,
+ None,
+ ],
+ PropertyInfo(discriminator="type"),
+]
+
+
+class AudioURL(BaseModel):
+ """Model for audio URL validation."""
+
+ url: str
+ """The audio URL. Can be either a URL or a Data URI."""
+
+
+class ScoredAudioURLInputChunk(BaseModel):
+ chunk_index: int
+ """position of the chunk in a file"""
+
+ mime_type: Optional[str] = None
+ """mime type of the chunk"""
+
+ generated_metadata: Optional[GeneratedMetadata] = None
+ """metadata of the chunk"""
+
+ model: Optional[str] = None
+ """model used for this chunk"""
+
+ score: float
+ """score of the chunk"""
+
+ file_id: str
+ """file id"""
+
+ filename: str
+ """filename"""
+
+ store_id: str
+ """store id"""
+
+ external_id: Optional[str] = None
+ """external identifier for this file"""
+
+ metadata: Optional[object] = None
+ """file metadata"""
+
+ type: Optional[Literal["audio_url"]] = None
+ """Input type identifier"""
+
+ transcription: Optional[str] = None
+ """speech recognition (sr) text of the audio"""
+
+ summary: Optional[str] = None
+ """summary of the audio"""
+
+ audio_url: Optional[AudioURL] = None
+ """Model for audio URL validation."""
+
+ sampling_rate: int
+ """The sampling rate of the audio."""
diff --git a/src/mixedbread/types/scored_image_url_input_chunk.py b/src/mixedbread/types/scored_image_url_input_chunk.py
new file mode 100644
index 00000000..1a8cf548
--- /dev/null
+++ b/src/mixedbread/types/scored_image_url_input_chunk.py
@@ -0,0 +1,315 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import TYPE_CHECKING, Dict, List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from pydantic import Field as FieldInfo
+
+from .._utils import PropertyInfo
+from .._models import BaseModel
+
+__all__ = [
+ "ScoredImageURLInputChunk",
+ "GeneratedMetadata",
+ "GeneratedMetadataMarkdownChunkGeneratedMetadata",
+ "GeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading",
+ "GeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext",
+ "GeneratedMetadataTextChunkGeneratedMetadata",
+ "GeneratedMetadataPdfChunkGeneratedMetadata",
+ "GeneratedMetadataCodeChunkGeneratedMetadata",
+ "GeneratedMetadataAudioChunkGeneratedMetadata",
+ "GeneratedMetadataVideoChunkGeneratedMetadata",
+ "GeneratedMetadataImageChunkGeneratedMetadata",
+ "ImageURL",
+]
+
+
+class GeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading(BaseModel):
+ level: int
+
+ text: str
+
+
+class GeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext(BaseModel):
+ level: int
+
+ text: str
+
+
+class GeneratedMetadataMarkdownChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["markdown"]] = None
+
+ file_type: Optional[Literal["text/markdown"]] = None
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ chunk_headings: Optional[List[GeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading]] = None
+
+ heading_context: Optional[List[GeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext]] = None
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ frontmatter: Optional[Dict[str, object]] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataTextChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["text"]] = None
+
+ file_type: Optional[Literal["text/plain"]] = None
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataPdfChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["pdf"]] = None
+
+ file_type: Optional[Literal["application/pdf"]] = None
+
+ total_pages: int
+
+ total_size: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataCodeChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["code"]] = None
+
+ file_type: str
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataAudioChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["audio"]] = None
+
+ file_type: str
+
+ file_size: int
+
+ total_duration_seconds: float
+
+ sample_rate: int
+
+ channels: int
+
+ audio_format: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataVideoChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["video"]] = None
+
+ file_type: str
+
+ file_size: Optional[int] = None
+
+ total_duration_seconds: float
+
+ fps: float
+
+ width: int
+
+ height: int
+
+ frame_count: int
+
+ has_audio_stream: Optional[bool] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataImageChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["image"]] = None
+
+ file_type: str
+
+ file_size: int
+
+ width: int
+
+ height: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+GeneratedMetadata: TypeAlias = Annotated[
+ Union[
+ GeneratedMetadataMarkdownChunkGeneratedMetadata,
+ GeneratedMetadataTextChunkGeneratedMetadata,
+ GeneratedMetadataPdfChunkGeneratedMetadata,
+ GeneratedMetadataCodeChunkGeneratedMetadata,
+ GeneratedMetadataAudioChunkGeneratedMetadata,
+ GeneratedMetadataVideoChunkGeneratedMetadata,
+ GeneratedMetadataImageChunkGeneratedMetadata,
+ None,
+ ],
+ PropertyInfo(discriminator="type"),
+]
+
+
+class ImageURL(BaseModel):
+ """Model for image URL validation."""
+
+ url: str
+ """The image URL. Can be either a URL or a Data URI."""
+
+ format: Optional[str] = None
+ """The image format/mimetype"""
+
+
+class ScoredImageURLInputChunk(BaseModel):
+ chunk_index: int
+ """position of the chunk in a file"""
+
+ mime_type: Optional[str] = None
+ """mime type of the chunk"""
+
+ generated_metadata: Optional[GeneratedMetadata] = None
+ """metadata of the chunk"""
+
+ model: Optional[str] = None
+ """model used for this chunk"""
+
+ score: float
+ """score of the chunk"""
+
+ file_id: str
+ """file id"""
+
+ filename: str
+ """filename"""
+
+ store_id: str
+ """store id"""
+
+ external_id: Optional[str] = None
+ """external identifier for this file"""
+
+ metadata: Optional[object] = None
+ """file metadata"""
+
+ type: Optional[Literal["image_url"]] = None
+ """Input type identifier"""
+
+ ocr_text: Optional[str] = None
+ """ocr text of the image"""
+
+ summary: Optional[str] = None
+ """summary of the image"""
+
+ image_url: Optional[ImageURL] = None
+ """Model for image URL validation."""
diff --git a/src/mixedbread/types/scored_text_input_chunk.py b/src/mixedbread/types/scored_text_input_chunk.py
new file mode 100644
index 00000000..41b33ea5
--- /dev/null
+++ b/src/mixedbread/types/scored_text_input_chunk.py
@@ -0,0 +1,301 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import TYPE_CHECKING, Dict, List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from pydantic import Field as FieldInfo
+
+from .._utils import PropertyInfo
+from .._models import BaseModel
+
+__all__ = [
+ "ScoredTextInputChunk",
+ "GeneratedMetadata",
+ "GeneratedMetadataMarkdownChunkGeneratedMetadata",
+ "GeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading",
+ "GeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext",
+ "GeneratedMetadataTextChunkGeneratedMetadata",
+ "GeneratedMetadataPdfChunkGeneratedMetadata",
+ "GeneratedMetadataCodeChunkGeneratedMetadata",
+ "GeneratedMetadataAudioChunkGeneratedMetadata",
+ "GeneratedMetadataVideoChunkGeneratedMetadata",
+ "GeneratedMetadataImageChunkGeneratedMetadata",
+]
+
+
+class GeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading(BaseModel):
+ level: int
+
+ text: str
+
+
+class GeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext(BaseModel):
+ level: int
+
+ text: str
+
+
+class GeneratedMetadataMarkdownChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["markdown"]] = None
+
+ file_type: Optional[Literal["text/markdown"]] = None
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ chunk_headings: Optional[List[GeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading]] = None
+
+ heading_context: Optional[List[GeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext]] = None
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ frontmatter: Optional[Dict[str, object]] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataTextChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["text"]] = None
+
+ file_type: Optional[Literal["text/plain"]] = None
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataPdfChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["pdf"]] = None
+
+ file_type: Optional[Literal["application/pdf"]] = None
+
+ total_pages: int
+
+ total_size: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataCodeChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["code"]] = None
+
+ file_type: str
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataAudioChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["audio"]] = None
+
+ file_type: str
+
+ file_size: int
+
+ total_duration_seconds: float
+
+ sample_rate: int
+
+ channels: int
+
+ audio_format: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataVideoChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["video"]] = None
+
+ file_type: str
+
+ file_size: Optional[int] = None
+
+ total_duration_seconds: float
+
+ fps: float
+
+ width: int
+
+ height: int
+
+ frame_count: int
+
+ has_audio_stream: Optional[bool] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataImageChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["image"]] = None
+
+ file_type: str
+
+ file_size: int
+
+ width: int
+
+ height: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+GeneratedMetadata: TypeAlias = Annotated[
+ Union[
+ GeneratedMetadataMarkdownChunkGeneratedMetadata,
+ GeneratedMetadataTextChunkGeneratedMetadata,
+ GeneratedMetadataPdfChunkGeneratedMetadata,
+ GeneratedMetadataCodeChunkGeneratedMetadata,
+ GeneratedMetadataAudioChunkGeneratedMetadata,
+ GeneratedMetadataVideoChunkGeneratedMetadata,
+ GeneratedMetadataImageChunkGeneratedMetadata,
+ None,
+ ],
+ PropertyInfo(discriminator="type"),
+]
+
+
+class ScoredTextInputChunk(BaseModel):
+ chunk_index: int
+ """position of the chunk in a file"""
+
+ mime_type: Optional[str] = None
+ """mime type of the chunk"""
+
+ generated_metadata: Optional[GeneratedMetadata] = None
+ """metadata of the chunk"""
+
+ model: Optional[str] = None
+ """model used for this chunk"""
+
+ score: float
+ """score of the chunk"""
+
+ file_id: str
+ """file id"""
+
+ filename: str
+ """filename"""
+
+ store_id: str
+ """store id"""
+
+ external_id: Optional[str] = None
+ """external identifier for this file"""
+
+ metadata: Optional[object] = None
+ """file metadata"""
+
+ type: Optional[Literal["text"]] = None
+ """Input type identifier"""
+
+ offset: Optional[int] = None
+ """The offset of the text in the file relative to the start of the file."""
+
+ text: Optional[str] = None
+ """Text content"""
diff --git a/src/mixedbread/types/scored_video_url_input_chunk.py b/src/mixedbread/types/scored_video_url_input_chunk.py
new file mode 100644
index 00000000..a33d0d3a
--- /dev/null
+++ b/src/mixedbread/types/scored_video_url_input_chunk.py
@@ -0,0 +1,312 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import TYPE_CHECKING, Dict, List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from pydantic import Field as FieldInfo
+
+from .._utils import PropertyInfo
+from .._models import BaseModel
+
+__all__ = [
+ "ScoredVideoURLInputChunk",
+ "GeneratedMetadata",
+ "GeneratedMetadataMarkdownChunkGeneratedMetadata",
+ "GeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading",
+ "GeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext",
+ "GeneratedMetadataTextChunkGeneratedMetadata",
+ "GeneratedMetadataPdfChunkGeneratedMetadata",
+ "GeneratedMetadataCodeChunkGeneratedMetadata",
+ "GeneratedMetadataAudioChunkGeneratedMetadata",
+ "GeneratedMetadataVideoChunkGeneratedMetadata",
+ "GeneratedMetadataImageChunkGeneratedMetadata",
+ "VideoURL",
+]
+
+
+class GeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading(BaseModel):
+ level: int
+
+ text: str
+
+
+class GeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext(BaseModel):
+ level: int
+
+ text: str
+
+
+class GeneratedMetadataMarkdownChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["markdown"]] = None
+
+ file_type: Optional[Literal["text/markdown"]] = None
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ chunk_headings: Optional[List[GeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading]] = None
+
+ heading_context: Optional[List[GeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext]] = None
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ frontmatter: Optional[Dict[str, object]] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataTextChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["text"]] = None
+
+ file_type: Optional[Literal["text/plain"]] = None
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataPdfChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["pdf"]] = None
+
+ file_type: Optional[Literal["application/pdf"]] = None
+
+ total_pages: int
+
+ total_size: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataCodeChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["code"]] = None
+
+ file_type: str
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataAudioChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["audio"]] = None
+
+ file_type: str
+
+ file_size: int
+
+ total_duration_seconds: float
+
+ sample_rate: int
+
+ channels: int
+
+ audio_format: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataVideoChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["video"]] = None
+
+ file_type: str
+
+ file_size: Optional[int] = None
+
+ total_duration_seconds: float
+
+ fps: float
+
+ width: int
+
+ height: int
+
+ frame_count: int
+
+ has_audio_stream: Optional[bool] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class GeneratedMetadataImageChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["image"]] = None
+
+ file_type: str
+
+ file_size: int
+
+ width: int
+
+ height: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+GeneratedMetadata: TypeAlias = Annotated[
+ Union[
+ GeneratedMetadataMarkdownChunkGeneratedMetadata,
+ GeneratedMetadataTextChunkGeneratedMetadata,
+ GeneratedMetadataPdfChunkGeneratedMetadata,
+ GeneratedMetadataCodeChunkGeneratedMetadata,
+ GeneratedMetadataAudioChunkGeneratedMetadata,
+ GeneratedMetadataVideoChunkGeneratedMetadata,
+ GeneratedMetadataImageChunkGeneratedMetadata,
+ None,
+ ],
+ PropertyInfo(discriminator="type"),
+]
+
+
+class VideoURL(BaseModel):
+ """Model for video URL validation."""
+
+ url: str
+ """The video URL. Can be either a URL or a Data URI."""
+
+
+class ScoredVideoURLInputChunk(BaseModel):
+ chunk_index: int
+ """position of the chunk in a file"""
+
+ mime_type: Optional[str] = None
+ """mime type of the chunk"""
+
+ generated_metadata: Optional[GeneratedMetadata] = None
+ """metadata of the chunk"""
+
+ model: Optional[str] = None
+ """model used for this chunk"""
+
+ score: float
+ """score of the chunk"""
+
+ file_id: str
+ """file id"""
+
+ filename: str
+ """filename"""
+
+ store_id: str
+ """store id"""
+
+ external_id: Optional[str] = None
+ """external identifier for this file"""
+
+ metadata: Optional[object] = None
+ """file metadata"""
+
+ type: Optional[Literal["video_url"]] = None
+ """Input type identifier"""
+
+ transcription: Optional[str] = None
+ """speech recognition (sr) text of the video"""
+
+ summary: Optional[str] = None
+ """summary of the video"""
+
+ video_url: Optional[VideoURL] = None
+ """Model for video URL validation."""
diff --git a/src/mixedbread/types/shared/__init__.py b/src/mixedbread/types/shared/__init__.py
new file mode 100644
index 00000000..66d5dcf9
--- /dev/null
+++ b/src/mixedbread/types/shared/__init__.py
@@ -0,0 +1,5 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .usage import Usage as Usage
+from .search_filter import SearchFilter as SearchFilter
+from .search_filter_condition import SearchFilterCondition as SearchFilterCondition
diff --git a/src/mixedbread/types/shared/search_filter.py b/src/mixedbread/types/shared/search_filter.py
new file mode 100644
index 00000000..c502e6db
--- /dev/null
+++ b/src/mixedbread/types/shared/search_filter.py
@@ -0,0 +1,40 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, List, Union, Optional
+from typing_extensions import TypeAlias, TypeAliasType
+
+from ..._compat import PYDANTIC_V1
+from ..._models import BaseModel
+from .search_filter_condition import SearchFilterCondition
+
+__all__ = ["SearchFilter", "All", "Any", "NoneType"]
+
+if TYPE_CHECKING or not PYDANTIC_V1:
+ All = TypeAliasType("All", Union["SearchFilter", SearchFilterCondition])
+else:
+ All: TypeAlias = Union["SearchFilter", SearchFilterCondition]
+
+if TYPE_CHECKING or not PYDANTIC_V1:
+ Any = TypeAliasType("Any", Union["SearchFilter", SearchFilterCondition])
+else:
+ Any: TypeAlias = Union["SearchFilter", SearchFilterCondition]
+
+if TYPE_CHECKING or not PYDANTIC_V1:
+ NoneType = TypeAliasType("NoneType", Union["SearchFilter", SearchFilterCondition])
+else:
+ NoneType: TypeAlias = Union["SearchFilter", SearchFilterCondition]
+
+
+class SearchFilter(BaseModel):
+ """Represents a filter with AND, OR, and NOT conditions."""
+
+ all: Optional[List[All]] = None
+ """List of conditions or filters to be ANDed together"""
+
+ any: Optional[List[Any]] = None
+ """List of conditions or filters to be ORed together"""
+
+ none: Optional[List[NoneType]] = None
+ """List of conditions or filters to be NOTed"""
diff --git a/src/mixedbread/types/shared/search_filter_condition.py b/src/mixedbread/types/shared/search_filter_condition.py
new file mode 100644
index 00000000..f50a91e1
--- /dev/null
+++ b/src/mixedbread/types/shared/search_filter_condition.py
@@ -0,0 +1,22 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["SearchFilterCondition"]
+
+
+class SearchFilterCondition(BaseModel):
+ """Represents a condition with a field, operator, and value."""
+
+ key: str
+ """The field to apply the condition on"""
+
+ value: object
+ """The value to compare against"""
+
+ operator: Literal[
+ "eq", "not_eq", "gt", "gte", "lt", "lte", "in", "not_in", "like", "starts_with", "not_like", "regex"
+ ]
+ """The operator for the condition"""
diff --git a/src/mixedbread/types/shared/usage.py b/src/mixedbread/types/shared/usage.py
new file mode 100644
index 00000000..eab91a6c
--- /dev/null
+++ b/src/mixedbread/types/shared/usage.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from ..._models import BaseModel
+
+__all__ = ["Usage"]
+
+
+class Usage(BaseModel):
+ prompt_tokens: int
+ """The number of tokens used for the prompt"""
+
+ total_tokens: int
+ """The total number of tokens used"""
+
+ completion_tokens: Optional[int] = None
+ """The number of tokens used for the completion"""
diff --git a/src/mixedbread/types/shared_params/__init__.py b/src/mixedbread/types/shared_params/__init__.py
new file mode 100644
index 00000000..c91e740d
--- /dev/null
+++ b/src/mixedbread/types/shared_params/__init__.py
@@ -0,0 +1,4 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .search_filter import SearchFilter as SearchFilter
+from .search_filter_condition import SearchFilterCondition as SearchFilterCondition
diff --git a/src/mixedbread/types/shared_params/search_filter.py b/src/mixedbread/types/shared_params/search_filter.py
new file mode 100644
index 00000000..fd468f38
--- /dev/null
+++ b/src/mixedbread/types/shared_params/search_filter.py
@@ -0,0 +1,39 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Union, Iterable, Optional
+from typing_extensions import TypeAlias, TypedDict, TypeAliasType
+
+from ..._compat import PYDANTIC_V1
+from .search_filter_condition import SearchFilterCondition
+
+__all__ = ["SearchFilter", "All", "Any", "NoneType"]
+
+if TYPE_CHECKING or not PYDANTIC_V1:
+ All = TypeAliasType("All", Union["SearchFilter", SearchFilterCondition])
+else:
+ All: TypeAlias = Union["SearchFilter", SearchFilterCondition]
+
+if TYPE_CHECKING or not PYDANTIC_V1:
+ Any = TypeAliasType("Any", Union["SearchFilter", SearchFilterCondition])
+else:
+ Any: TypeAlias = Union["SearchFilter", SearchFilterCondition]
+
+if TYPE_CHECKING or not PYDANTIC_V1:
+ NoneType = TypeAliasType("NoneType", Union["SearchFilter", SearchFilterCondition])
+else:
+ NoneType: TypeAlias = Union["SearchFilter", SearchFilterCondition]
+
+
+class SearchFilter(TypedDict, total=False):
+ """Represents a filter with AND, OR, and NOT conditions."""
+
+ all: Optional[Iterable[All]]
+ """List of conditions or filters to be ANDed together"""
+
+ any: Optional[Iterable[Any]]
+ """List of conditions or filters to be ORed together"""
+
+ none: Optional[Iterable[NoneType]]
+ """List of conditions or filters to be NOTed"""
diff --git a/src/mixedbread/types/shared_params/search_filter_condition.py b/src/mixedbread/types/shared_params/search_filter_condition.py
new file mode 100644
index 00000000..86efe345
--- /dev/null
+++ b/src/mixedbread/types/shared_params/search_filter_condition.py
@@ -0,0 +1,22 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["SearchFilterCondition"]
+
+
+class SearchFilterCondition(TypedDict, total=False):
+ """Represents a condition with a field, operator, and value."""
+
+ key: Required[str]
+ """The field to apply the condition on"""
+
+ value: Required[object]
+ """The value to compare against"""
+
+ operator: Required[
+ Literal["eq", "not_eq", "gt", "gte", "lt", "lte", "in", "not_in", "like", "starts_with", "not_like", "regex"]
+ ]
+ """The operator for the condition"""
diff --git a/src/mixedbread/types/store.py b/src/mixedbread/types/store.py
new file mode 100644
index 00000000..83de8954
--- /dev/null
+++ b/src/mixedbread/types/store.py
@@ -0,0 +1,112 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from datetime import datetime
+from typing_extensions import Literal, TypeAlias
+
+from .._models import BaseModel
+from .expires_after import ExpiresAfter
+
+__all__ = ["Store", "Config", "ConfigContextualization", "ConfigContextualizationContextualizationConfig", "FileCounts"]
+
+
+class ConfigContextualizationContextualizationConfig(BaseModel):
+ with_metadata: Union[bool, List[str], None] = None
+ """Include all metadata or specific fields in the contextualization.
+
+ Supports dot notation for nested fields (e.g., 'author.name'). When True, all
+ metadata is included (flattened). When a list, only specified fields are
+ included.
+ """
+
+
+ConfigContextualization: TypeAlias = Union[bool, ConfigContextualizationContextualizationConfig]
+
+
+class Config(BaseModel):
+ """Configuration for a store."""
+
+ contextualization: Optional[ConfigContextualization] = None
+ """Contextualize files with metadata"""
+
+ save_content: Optional[bool] = None
+ """Whether to save original content in the store.
+
+ When False, only vectors are indexed without the original content (index-only
+ mode). This is useful for data privacy. Note: Reranking is not supported when
+ content is not saved.
+ """
+
+
+class FileCounts(BaseModel):
+ """Counts of files in different states"""
+
+ pending: Optional[int] = None
+ """Number of files waiting to be processed"""
+
+ in_progress: Optional[int] = None
+ """Number of files currently being processed"""
+
+ cancelled: Optional[int] = None
+ """Number of files whose processing was cancelled"""
+
+ completed: Optional[int] = None
+ """Number of successfully processed files"""
+
+ failed: Optional[int] = None
+ """Number of files that failed processing"""
+
+ total: Optional[int] = None
+ """Total number of files"""
+
+
+class Store(BaseModel):
+ """Model representing a store with its metadata and timestamps."""
+
+ id: str
+ """Unique identifier for the store"""
+
+ name: str
+ """Name of the store"""
+
+ description: Optional[str] = None
+ """Detailed description of the store's purpose and contents"""
+
+ is_public: Optional[bool] = None
+ """Whether the store can be accessed by anyone with valid login credentials"""
+
+ metadata: Optional[object] = None
+ """Additional metadata associated with the store"""
+
+ config: Optional[Config] = None
+ """Configuration for a store."""
+
+ file_counts: Optional[FileCounts] = None
+ """Counts of files in different states"""
+
+ expires_after: Optional[ExpiresAfter] = None
+ """Represents an expiration policy for a store."""
+
+ status: Optional[Literal["expired", "in_progress", "completed"]] = None
+ """Processing status of the store"""
+
+ created_at: datetime
+ """Timestamp when the store was created"""
+
+ updated_at: datetime
+ """Timestamp when the store was last updated"""
+
+ last_active_at: Optional[datetime] = None
+ """Timestamp when the store was last used"""
+
+ usage_bytes: Optional[int] = None
+ """Total storage usage in bytes"""
+
+ usage_tokens: Optional[int] = None
+ """Total storage usage in tokens"""
+
+ expires_at: Optional[datetime] = None
+ """Optional expiration timestamp for the store"""
+
+ object: Optional[Literal["store"]] = None
+ """Type of the object"""
diff --git a/src/mixedbread/types/store_chunk_search_options_param.py b/src/mixedbread/types/store_chunk_search_options_param.py
new file mode 100644
index 00000000..90bb9816
--- /dev/null
+++ b/src/mixedbread/types/store_chunk_search_options_param.py
@@ -0,0 +1,73 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Optional
+from typing_extensions import TypeAlias, TypedDict
+
+from .._types import SequenceNotStr
+
+__all__ = ["StoreChunkSearchOptionsParam", "Rerank", "RerankRerankConfig", "Agentic", "AgenticAgenticSearchConfig"]
+
+
+class RerankRerankConfig(TypedDict, total=False):
+ """Represents a reranking configuration."""
+
+ model: str
+ """The name of the reranking model"""
+
+ with_metadata: Union[bool, SequenceNotStr[str]]
+ """Whether to include metadata in the reranked results"""
+
+ top_k: Optional[int]
+ """Maximum number of results to return after reranking.
+
+ If None, returns all reranked results.
+ """
+
+
+Rerank: TypeAlias = Union[bool, RerankRerankConfig]
+
+
+class AgenticAgenticSearchConfig(TypedDict, total=False):
+ """Configuration for agentic multi-query search."""
+
+ max_rounds: int
+ """Maximum number of search rounds"""
+
+ queries_per_round: int
+ """Maximum queries per round"""
+
+
+Agentic: TypeAlias = Union[bool, AgenticAgenticSearchConfig]
+
+
+class StoreChunkSearchOptionsParam(TypedDict, total=False):
+ """Options for configuring store chunk searches."""
+
+ score_threshold: float
+ """Minimum similarity score threshold"""
+
+ rewrite_query: bool
+ """Whether to rewrite the query.
+
+ Ignored when agentic is enabled (the agent handles query decomposition).
+ """
+
+ rerank: Optional[Rerank]
+ """Whether to rerank results and optional reranking configuration.
+
+ Ignored when agentic is enabled (the agent handles ranking).
+ """
+
+ agentic: Optional[Agentic]
+ """
+ Whether to use agentic multi-query search with automatic query decomposition and
+ ranking. When enabled, rewrite_query and rerank options are ignored.
+ """
+
+ return_metadata: bool
+ """Whether to return file metadata"""
+
+ apply_search_rules: bool
+ """Whether to apply search rules"""
diff --git a/src/mixedbread/types/store_create_params.py b/src/mixedbread/types/store_create_params.py
new file mode 100644
index 00000000..b7814cdd
--- /dev/null
+++ b/src/mixedbread/types/store_create_params.py
@@ -0,0 +1,65 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Optional
+from typing_extensions import TypeAlias, TypedDict
+
+from .._types import SequenceNotStr
+from .expires_after_param import ExpiresAfterParam
+
+__all__ = ["StoreCreateParams", "Config", "ConfigContextualization", "ConfigContextualizationContextualizationConfig"]
+
+
+class StoreCreateParams(TypedDict, total=False):
+ name: Optional[str]
+ """Name for the new store.
+
+ Can only contain lowercase letters, numbers, periods (.), and hyphens (-).
+ """
+
+ description: Optional[str]
+ """Description of the store"""
+
+ is_public: bool
+ """Whether the store can be accessed by anyone with valid login credentials"""
+
+ expires_after: Optional[ExpiresAfterParam]
+ """Represents an expiration policy for a store."""
+
+ metadata: object
+ """Optional metadata key-value pairs"""
+
+ config: Optional[Config]
+ """Configuration for a store."""
+
+ file_ids: Optional[SequenceNotStr[str]]
+ """Optional list of file IDs"""
+
+
+class ConfigContextualizationContextualizationConfig(TypedDict, total=False):
+ with_metadata: Union[bool, SequenceNotStr[str]]
+ """Include all metadata or specific fields in the contextualization.
+
+ Supports dot notation for nested fields (e.g., 'author.name'). When True, all
+ metadata is included (flattened). When a list, only specified fields are
+ included.
+ """
+
+
+ConfigContextualization: TypeAlias = Union[bool, ConfigContextualizationContextualizationConfig]
+
+
+class Config(TypedDict, total=False):
+ """Configuration for a store."""
+
+ contextualization: ConfigContextualization
+ """Contextualize files with metadata"""
+
+ save_content: bool
+ """Whether to save original content in the store.
+
+ When False, only vectors are indexed without the original content (index-only
+ mode). This is useful for data privacy. Note: Reranking is not supported when
+ content is not saved.
+ """
diff --git a/src/mixedbread/types/vector_store_deleted.py b/src/mixedbread/types/store_delete_response.py
similarity index 60%
rename from src/mixedbread/types/vector_store_deleted.py
rename to src/mixedbread/types/store_delete_response.py
index 393f8977..5a57079e 100644
--- a/src/mixedbread/types/vector_store_deleted.py
+++ b/src/mixedbread/types/store_delete_response.py
@@ -5,15 +5,17 @@
from .._models import BaseModel
-__all__ = ["VectorStoreDeleted"]
+__all__ = ["StoreDeleteResponse"]
-class VectorStoreDeleted(BaseModel):
+class StoreDeleteResponse(BaseModel):
+ """Response model for store deletion."""
+
id: str
- """ID of the deleted vector store"""
+ """ID of the deleted store"""
deleted: bool
"""Whether the deletion was successful"""
- object: Optional[Literal["vector_store"]] = None
+ object: Optional[Literal["store"]] = None
"""Type of the deleted object"""
diff --git a/src/mixedbread/types/store_list_params.py b/src/mixedbread/types/store_list_params.py
new file mode 100644
index 00000000..addec411
--- /dev/null
+++ b/src/mixedbread/types/store_list_params.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import TypedDict
+
+__all__ = ["StoreListParams"]
+
+
+class StoreListParams(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)"""
+
+ q: Optional[str]
+ """Search query for fuzzy matching over name and description fields"""
diff --git a/src/mixedbread/types/store_metadata_facets_params.py b/src/mixedbread/types/store_metadata_facets_params.py
new file mode 100644
index 00000000..64aa124e
--- /dev/null
+++ b/src/mixedbread/types/store_metadata_facets_params.py
@@ -0,0 +1,42 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable, Optional
+from typing_extensions import Required, TypeAlias, TypedDict
+
+from .._types import SequenceNotStr
+from .store_chunk_search_options_param import StoreChunkSearchOptionsParam
+from .shared_params.search_filter_condition import SearchFilterCondition
+
+__all__ = ["StoreMetadataFacetsParams", "Filters", "FiltersUnionMember2"]
+
+
+class StoreMetadataFacetsParams(TypedDict, total=False):
+ query: Optional[str]
+ """Search query text"""
+
+ store_identifiers: Required[SequenceNotStr[str]]
+ """IDs or names of stores to search"""
+
+ top_k: int
+ """Number of results to return"""
+
+ filters: Optional[Filters]
+ """Optional filter conditions"""
+
+ file_ids: Union[Iterable[object], SequenceNotStr[str], None]
+ """Optional list of file IDs to filter chunks by (inclusion filter)"""
+
+ search_options: StoreChunkSearchOptionsParam
+ """Search configuration options"""
+
+ facets: Optional[SequenceNotStr[str]]
+ """Optional list of facets to return. Use dot for nested fields."""
+
+
+FiltersUnionMember2: TypeAlias = Union["SearchFilter", SearchFilterCondition]
+
+Filters: TypeAlias = Union["SearchFilter", SearchFilterCondition, Iterable[FiltersUnionMember2]]
+
+from .shared_params.search_filter import SearchFilter
diff --git a/src/mixedbread/types/store_metadata_facets_response.py b/src/mixedbread/types/store_metadata_facets_response.py
new file mode 100644
index 00000000..6b993206
--- /dev/null
+++ b/src/mixedbread/types/store_metadata_facets_response.py
@@ -0,0 +1,14 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Dict
+
+from .._models import BaseModel
+
+__all__ = ["StoreMetadataFacetsResponse"]
+
+
+class StoreMetadataFacetsResponse(BaseModel):
+ """Represents metadata facets for a store."""
+
+ facets: Dict[str, Dict[str, object]]
+ """Metadata facets"""
diff --git a/src/mixedbread/types/store_question_answering_params.py b/src/mixedbread/types/store_question_answering_params.py
new file mode 100644
index 00000000..26abbb4e
--- /dev/null
+++ b/src/mixedbread/types/store_question_answering_params.py
@@ -0,0 +1,59 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable, Optional
+from typing_extensions import Required, TypeAlias, TypedDict
+
+from .._types import SequenceNotStr
+from .store_chunk_search_options_param import StoreChunkSearchOptionsParam
+from .shared_params.search_filter_condition import SearchFilterCondition
+
+__all__ = ["StoreQuestionAnsweringParams", "Filters", "FiltersUnionMember2", "QaOptions"]
+
+
+class StoreQuestionAnsweringParams(TypedDict, total=False):
+ query: str
+ """Question to answer.
+
+ If not provided, the question will be extracted from the passed messages.
+ """
+
+ store_identifiers: Required[SequenceNotStr[str]]
+ """IDs or names of stores to search"""
+
+ top_k: int
+ """Number of results to return"""
+
+ filters: Optional[Filters]
+ """Optional filter conditions"""
+
+ file_ids: Union[Iterable[object], SequenceNotStr[str], None]
+ """Optional list of file IDs to filter chunks by (inclusion filter)"""
+
+ search_options: StoreChunkSearchOptionsParam
+ """Search configuration options"""
+
+ stream: bool
+ """Whether to stream the answer"""
+
+ qa_options: QaOptions
+ """Question answering configuration options"""
+
+
+FiltersUnionMember2: TypeAlias = Union["SearchFilter", SearchFilterCondition]
+
+Filters: TypeAlias = Union["SearchFilter", SearchFilterCondition, Iterable[FiltersUnionMember2]]
+
+
+class QaOptions(TypedDict, total=False):
+ """Question answering configuration options"""
+
+ cite: bool
+ """Whether to use citations"""
+
+ multimodal: bool
+ """Whether to use multimodal context"""
+
+
+from .shared_params.search_filter import SearchFilter
diff --git a/src/mixedbread/types/store_question_answering_response.py b/src/mixedbread/types/store_question_answering_response.py
new file mode 100644
index 00000000..dd726f24
--- /dev/null
+++ b/src/mixedbread/types/store_question_answering_response.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from typing_extensions import Annotated, TypeAlias
+
+from .._utils import PropertyInfo
+from .._models import BaseModel
+from .scored_text_input_chunk import ScoredTextInputChunk
+from .scored_audio_url_input_chunk import ScoredAudioURLInputChunk
+from .scored_image_url_input_chunk import ScoredImageURLInputChunk
+from .scored_video_url_input_chunk import ScoredVideoURLInputChunk
+
+__all__ = ["StoreQuestionAnsweringResponse", "Source"]
+
+Source: TypeAlias = Annotated[
+ Union[ScoredTextInputChunk, ScoredImageURLInputChunk, ScoredAudioURLInputChunk, ScoredVideoURLInputChunk],
+ PropertyInfo(discriminator="type"),
+]
+
+
+class StoreQuestionAnsweringResponse(BaseModel):
+ """Results from a question answering operation."""
+
+ answer: str
+ """The answer generated by the LLM"""
+
+ sources: Optional[List[Source]] = None
+ """Source documents used to generate the answer"""
diff --git a/src/mixedbread/types/store_search_params.py b/src/mixedbread/types/store_search_params.py
new file mode 100644
index 00000000..7a526d7d
--- /dev/null
+++ b/src/mixedbread/types/store_search_params.py
@@ -0,0 +1,43 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable, Optional
+from typing_extensions import Required, TypeAlias, TypedDict
+
+from .._types import SequenceNotStr
+from .extractions.text_input_param import TextInputParam
+from .store_chunk_search_options_param import StoreChunkSearchOptionsParam
+from .extractions.image_url_input_param import ImageURLInputParam
+from .shared_params.search_filter_condition import SearchFilterCondition
+
+__all__ = ["StoreSearchParams", "Query", "Filters", "FiltersUnionMember2"]
+
+
+class StoreSearchParams(TypedDict, total=False):
+ query: Required[Query]
+ """Search query text"""
+
+ store_identifiers: Required[SequenceNotStr[str]]
+ """IDs or names of stores to search"""
+
+ top_k: int
+ """Number of results to return"""
+
+ filters: Optional[Filters]
+ """Optional filter conditions"""
+
+ file_ids: Union[Iterable[object], SequenceNotStr[str], None]
+ """Optional list of file IDs to filter chunks by (inclusion filter)"""
+
+ search_options: StoreChunkSearchOptionsParam
+ """Search configuration options"""
+
+
+Query: TypeAlias = Union[str, ImageURLInputParam, TextInputParam]
+
+FiltersUnionMember2: TypeAlias = Union["SearchFilter", SearchFilterCondition]
+
+Filters: TypeAlias = Union["SearchFilter", SearchFilterCondition, Iterable[FiltersUnionMember2]]
+
+from .shared_params.search_filter import SearchFilter
diff --git a/src/mixedbread/types/store_search_response.py b/src/mixedbread/types/store_search_response.py
new file mode 100644
index 00000000..6ab757c5
--- /dev/null
+++ b/src/mixedbread/types/store_search_response.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from .._utils import PropertyInfo
+from .._models import BaseModel
+from .scored_text_input_chunk import ScoredTextInputChunk
+from .scored_audio_url_input_chunk import ScoredAudioURLInputChunk
+from .scored_image_url_input_chunk import ScoredImageURLInputChunk
+from .scored_video_url_input_chunk import ScoredVideoURLInputChunk
+
+__all__ = ["StoreSearchResponse", "Data"]
+
+Data: TypeAlias = Annotated[
+ Union[ScoredTextInputChunk, ScoredImageURLInputChunk, ScoredAudioURLInputChunk, ScoredVideoURLInputChunk],
+ PropertyInfo(discriminator="type"),
+]
+
+
+class StoreSearchResponse(BaseModel):
+ object: Optional[Literal["list"]] = None
+ """The object type of the response"""
+
+ data: List[Data]
+ """The list of scored store file chunks"""
diff --git a/src/mixedbread/types/store_update_params.py b/src/mixedbread/types/store_update_params.py
new file mode 100644
index 00000000..e06bbf9d
--- /dev/null
+++ b/src/mixedbread/types/store_update_params.py
@@ -0,0 +1,30 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import TypedDict
+
+from .expires_after_param import ExpiresAfterParam
+
+__all__ = ["StoreUpdateParams"]
+
+
+class StoreUpdateParams(TypedDict, total=False):
+ name: Optional[str]
+ """New name for the store.
+
+ Can only contain lowercase letters, numbers, periods (.), and hyphens (-).
+ """
+
+ description: Optional[str]
+ """New description"""
+
+ is_public: Optional[bool]
+ """Whether the store can be accessed by anyone with valid login credentials"""
+
+ expires_after: Optional[ExpiresAfterParam]
+ """Represents an expiration policy for a store."""
+
+ metadata: object
+ """Optional metadata key-value pairs"""
diff --git a/src/mixedbread/types/vector_stores/__init__.py b/src/mixedbread/types/stores/__init__.py
similarity index 54%
rename from src/mixedbread/types/vector_stores/__init__.py
rename to src/mixedbread/types/stores/__init__.py
index dfdfd4e4..50862586 100644
--- a/src/mixedbread/types/vector_stores/__init__.py
+++ b/src/mixedbread/types/stores/__init__.py
@@ -2,10 +2,14 @@
from __future__ import annotations
+from .store_file import StoreFile as StoreFile
from .file_list_params import FileListParams as FileListParams
-from .vector_store_file import VectorStoreFile as VectorStoreFile
+from .scored_store_file import ScoredStoreFile as ScoredStoreFile
+from .store_file_status import StoreFileStatus as StoreFileStatus
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 .file_update_params import FileUpdateParams as FileUpdateParams
+from .file_delete_response import FileDeleteResponse as FileDeleteResponse
+from .file_retrieve_params import FileRetrieveParams as FileRetrieveParams
from .file_search_response import FileSearchResponse as FileSearchResponse
-from .vector_store_file_deleted import VectorStoreFileDeleted as VectorStoreFileDeleted
diff --git a/src/mixedbread/types/stores/file_create_params.py b/src/mixedbread/types/stores/file_create_params.py
new file mode 100644
index 00000000..c776ea33
--- /dev/null
+++ b/src/mixedbread/types/stores/file_create_params.py
@@ -0,0 +1,42 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["FileCreateParams", "Config", "Experimental"]
+
+
+class FileCreateParams(TypedDict, total=False):
+ metadata: object
+ """Optional metadata for the file"""
+
+ config: Config
+ """Configuration for adding the file"""
+
+ external_id: Optional[str]
+ """External identifier for this file in the store"""
+
+ overwrite: bool
+ """If true, overwrite an existing file with the same external_id"""
+
+ file_id: Required[str]
+ """ID of the file to add"""
+
+ experimental: Optional[Experimental]
+ """Configuration for a file."""
+
+
+class Config(TypedDict, total=False):
+ """Configuration for adding the file"""
+
+ parsing_strategy: Literal["fast", "high_quality"]
+ """Strategy for adding the file, this overrides the store-level default"""
+
+
+class Experimental(TypedDict, total=False):
+ """Configuration for a file."""
+
+ parsing_strategy: Literal["fast", "high_quality"]
+ """Strategy for adding the file, this overrides the store-level default"""
diff --git a/src/mixedbread/types/vector_stores/vector_store_file_deleted.py b/src/mixedbread/types/stores/file_delete_response.py
similarity index 68%
rename from src/mixedbread/types/vector_stores/vector_store_file_deleted.py
rename to src/mixedbread/types/stores/file_delete_response.py
index 3291c3b8..5788f3f8 100644
--- a/src/mixedbread/types/vector_stores/vector_store_file_deleted.py
+++ b/src/mixedbread/types/stores/file_delete_response.py
@@ -5,15 +5,17 @@
from ..._models import BaseModel
-__all__ = ["VectorStoreFileDeleted"]
+__all__ = ["FileDeleteResponse"]
-class VectorStoreFileDeleted(BaseModel):
+class FileDeleteResponse(BaseModel):
+ """Response model for file deletion."""
+
id: str
"""ID of the deleted file"""
deleted: Optional[bool] = None
"""Whether the deletion was successful"""
- object: Optional[Literal["vector_store.file"]] = None
+ object: Optional[Literal["store.file"]] = None
"""Type of the deleted object"""
diff --git a/src/mixedbread/types/stores/file_list_params.py b/src/mixedbread/types/stores/file_list_params.py
new file mode 100644
index 00000000..2089f9cf
--- /dev/null
+++ b/src/mixedbread/types/stores/file_list_params.py
@@ -0,0 +1,47 @@
+# 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 .store_file_status import StoreFileStatus
+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[StoreFileStatus]]
+ """Status to filter by"""
+
+ metadata_filter: Optional[MetadataFilter]
+ """Metadata filter to apply to the query"""
+
+ q: Optional[str]
+ """Search query for fuzzy matching over name and external_id fields"""
+
+
+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/stores/file_list_response.py b/src/mixedbread/types/stores/file_list_response.py
new file mode 100644
index 00000000..6ddaf8f1
--- /dev/null
+++ b/src/mixedbread/types/stores/file_list_response.py
@@ -0,0 +1,50 @@
+# 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 .store_file import StoreFile
+
+__all__ = ["FileListResponse", "Pagination"]
+
+
+class Pagination(BaseModel):
+ """Response model for cursor-based pagination."""
+
+ 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."""
+
+ object: Optional[Literal["list"]] = None
+ """The object type of the response"""
+
+ data: List[StoreFile]
+ """The list of store files"""
diff --git a/src/mixedbread/types/stores/file_retrieve_params.py b/src/mixedbread/types/stores/file_retrieve_params.py
new file mode 100644
index 00000000..b77c870a
--- /dev/null
+++ b/src/mixedbread/types/stores/file_retrieve_params.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable
+from typing_extensions import Required, TypedDict
+
+__all__ = ["FileRetrieveParams"]
+
+
+class FileRetrieveParams(TypedDict, total=False):
+ store_identifier: Required[str]
+ """The ID or name of the store"""
+
+ return_chunks: Union[bool, Iterable[int]]
+ """Whether to return the chunks for the file.
+
+ If a list of integers is provided, only the chunks at the specified indices will
+ be returned.
+ """
diff --git a/src/mixedbread/types/stores/file_search_params.py b/src/mixedbread/types/stores/file_search_params.py
new file mode 100644
index 00000000..6f6aa2d1
--- /dev/null
+++ b/src/mixedbread/types/stores/file_search_params.py
@@ -0,0 +1,122 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Union, Iterable, Optional
+from typing_extensions import Required, TypeAlias, TypedDict
+
+from ..._types import SequenceNotStr
+from ..extractions.text_input_param import TextInputParam
+from ..extractions.image_url_input_param import ImageURLInputParam
+from ..shared_params.search_filter_condition import SearchFilterCondition
+
+__all__ = [
+ "FileSearchParams",
+ "Query",
+ "Filters",
+ "FiltersUnionMember2",
+ "SearchOptions",
+ "SearchOptionsRerank",
+ "SearchOptionsRerankRerankConfig",
+ "SearchOptionsAgentic",
+ "SearchOptionsAgenticAgenticSearchConfig",
+]
+
+
+class FileSearchParams(TypedDict, total=False):
+ query: Required[Query]
+ """Search query text"""
+
+ store_identifiers: Required[SequenceNotStr[str]]
+ """IDs or names of stores to search"""
+
+ top_k: int
+ """Number of results to return"""
+
+ filters: Optional[Filters]
+ """Optional filter conditions"""
+
+ file_ids: Union[Iterable[object], SequenceNotStr[str], None]
+ """Optional list of file IDs to filter chunks by (inclusion filter)"""
+
+ search_options: SearchOptions
+ """Search configuration options"""
+
+
+Query: TypeAlias = Union[str, ImageURLInputParam, TextInputParam]
+
+FiltersUnionMember2: TypeAlias = Union["SearchFilter", SearchFilterCondition]
+
+Filters: TypeAlias = Union["SearchFilter", SearchFilterCondition, Iterable[FiltersUnionMember2]]
+
+
+class SearchOptionsRerankRerankConfig(TypedDict, total=False):
+ """Represents a reranking configuration."""
+
+ model: str
+ """The name of the reranking model"""
+
+ with_metadata: Union[bool, SequenceNotStr[str]]
+ """Whether to include metadata in the reranked results"""
+
+ top_k: Optional[int]
+ """Maximum number of results to return after reranking.
+
+ If None, returns all reranked results.
+ """
+
+
+SearchOptionsRerank: TypeAlias = Union[bool, SearchOptionsRerankRerankConfig]
+
+
+class SearchOptionsAgenticAgenticSearchConfig(TypedDict, total=False):
+ """Configuration for agentic multi-query search."""
+
+ max_rounds: int
+ """Maximum number of search rounds"""
+
+ queries_per_round: int
+ """Maximum queries per round"""
+
+
+SearchOptionsAgentic: TypeAlias = Union[bool, SearchOptionsAgenticAgenticSearchConfig]
+
+
+class SearchOptions(TypedDict, total=False):
+ """Search configuration options"""
+
+ score_threshold: float
+ """Minimum similarity score threshold"""
+
+ rewrite_query: bool
+ """Whether to rewrite the query.
+
+ Ignored when agentic is enabled (the agent handles query decomposition).
+ """
+
+ rerank: Optional[SearchOptionsRerank]
+ """Whether to rerank results and optional reranking configuration.
+
+ Ignored when agentic is enabled (the agent handles ranking).
+ """
+
+ agentic: Optional[SearchOptionsAgentic]
+ """
+ Whether to use agentic multi-query search with automatic query decomposition and
+ ranking. When enabled, rewrite_query and rerank options are ignored.
+ """
+
+ return_metadata: bool
+ """Whether to return file metadata"""
+
+ return_chunks: bool
+ """Whether to return matching text chunks"""
+
+ chunks_per_file: int
+ """Number of chunks to return for each file"""
+
+ apply_search_rules: bool
+ """Whether to apply search rules"""
+
+
+from ..shared_params.search_filter import SearchFilter
diff --git a/src/mixedbread/types/stores/file_search_response.py b/src/mixedbread/types/stores/file_search_response.py
new file mode 100644
index 00000000..304512e4
--- /dev/null
+++ b/src/mixedbread/types/stores/file_search_response.py
@@ -0,0 +1,17 @@
+# 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 .scored_store_file import ScoredStoreFile
+
+__all__ = ["FileSearchResponse"]
+
+
+class FileSearchResponse(BaseModel):
+ object: Optional[Literal["list"]] = None
+ """The object type of the response"""
+
+ data: List[ScoredStoreFile]
+ """The list of scored store files"""
diff --git a/src/mixedbread/types/stores/file_update_params.py b/src/mixedbread/types/stores/file_update_params.py
new file mode 100644
index 00000000..d8dc45e7
--- /dev/null
+++ b/src/mixedbread/types/stores/file_update_params.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Dict, Optional
+from typing_extensions import Required, TypedDict
+
+__all__ = ["FileUpdateParams"]
+
+
+class FileUpdateParams(TypedDict, total=False):
+ store_identifier: Required[str]
+ """The ID or name of the store"""
+
+ metadata: Optional[Dict[str, object]]
+ """Updated metadata for the file"""
diff --git a/src/mixedbread/types/stores/scored_store_file.py b/src/mixedbread/types/stores/scored_store_file.py
new file mode 100644
index 00000000..87483138
--- /dev/null
+++ b/src/mixedbread/types/stores/scored_store_file.py
@@ -0,0 +1,77 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import List, Union, Optional
+from datetime import datetime
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+from .store_file_status import StoreFileStatus
+from ..scored_text_input_chunk import ScoredTextInputChunk
+from ..scored_audio_url_input_chunk import ScoredAudioURLInputChunk
+from ..scored_image_url_input_chunk import ScoredImageURLInputChunk
+from ..scored_video_url_input_chunk import ScoredVideoURLInputChunk
+
+__all__ = ["ScoredStoreFile", "Config", "Chunk"]
+
+
+class Config(BaseModel):
+ """Configuration for a file."""
+
+ parsing_strategy: Optional[Literal["fast", "high_quality"]] = None
+ """Strategy for adding the file, this overrides the store-level default"""
+
+
+Chunk: TypeAlias = Annotated[
+ Union[ScoredTextInputChunk, ScoredImageURLInputChunk, ScoredAudioURLInputChunk, ScoredVideoURLInputChunk],
+ PropertyInfo(discriminator="type"),
+]
+
+
+class ScoredStoreFile(BaseModel):
+ """Represents a scored store file."""
+
+ id: str
+ """Unique identifier for the file"""
+
+ filename: Optional[str] = None
+ """Name of the file"""
+
+ metadata: Optional[object] = None
+ """Optional file metadata"""
+
+ external_id: Optional[str] = None
+ """External identifier for this file in the store"""
+
+ status: Optional[StoreFileStatus] = None
+ """Processing status of the file"""
+
+ last_error: Optional[object] = None
+ """Last error message if processing failed"""
+
+ store_id: str
+ """ID of the containing store"""
+
+ created_at: datetime
+ """Timestamp of store file creation"""
+
+ version: Optional[int] = None
+ """Version number of the file"""
+
+ usage_bytes: Optional[int] = None
+ """Storage usage in bytes"""
+
+ usage_tokens: Optional[int] = None
+ """Storage usage in tokens"""
+
+ config: Optional[Config] = None
+ """Configuration for a file."""
+
+ object: Optional[Literal["store.file"]] = None
+ """Type of the object"""
+
+ chunks: Optional[List[Chunk]] = None
+ """Array of scored file chunks"""
+
+ score: float
+ """score of the file"""
diff --git a/src/mixedbread/types/stores/store_file.py b/src/mixedbread/types/stores/store_file.py
new file mode 100644
index 00000000..0fe097ba
--- /dev/null
+++ b/src/mixedbread/types/stores/store_file.py
@@ -0,0 +1,1215 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import TYPE_CHECKING, Dict, List, Union, Optional
+from datetime import datetime
+from typing_extensions import Literal, Annotated, TypeAlias
+
+from pydantic import Field as FieldInfo
+
+from ..._utils import PropertyInfo
+from ..._models import BaseModel
+from .store_file_status import StoreFileStatus
+
+__all__ = [
+ "StoreFile",
+ "Config",
+ "Chunk",
+ "ChunkTextInputChunk",
+ "ChunkTextInputChunkGeneratedMetadata",
+ "ChunkTextInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadata",
+ "ChunkTextInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading",
+ "ChunkTextInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext",
+ "ChunkTextInputChunkGeneratedMetadataTextChunkGeneratedMetadata",
+ "ChunkTextInputChunkGeneratedMetadataPdfChunkGeneratedMetadata",
+ "ChunkTextInputChunkGeneratedMetadataCodeChunkGeneratedMetadata",
+ "ChunkTextInputChunkGeneratedMetadataAudioChunkGeneratedMetadata",
+ "ChunkTextInputChunkGeneratedMetadataVideoChunkGeneratedMetadata",
+ "ChunkTextInputChunkGeneratedMetadataImageChunkGeneratedMetadata",
+ "ChunkImageURLInputChunk",
+ "ChunkImageURLInputChunkGeneratedMetadata",
+ "ChunkImageURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadata",
+ "ChunkImageURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading",
+ "ChunkImageURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext",
+ "ChunkImageURLInputChunkGeneratedMetadataTextChunkGeneratedMetadata",
+ "ChunkImageURLInputChunkGeneratedMetadataPdfChunkGeneratedMetadata",
+ "ChunkImageURLInputChunkGeneratedMetadataCodeChunkGeneratedMetadata",
+ "ChunkImageURLInputChunkGeneratedMetadataAudioChunkGeneratedMetadata",
+ "ChunkImageURLInputChunkGeneratedMetadataVideoChunkGeneratedMetadata",
+ "ChunkImageURLInputChunkGeneratedMetadataImageChunkGeneratedMetadata",
+ "ChunkImageURLInputChunkImageURL",
+ "ChunkAudioURLInputChunk",
+ "ChunkAudioURLInputChunkGeneratedMetadata",
+ "ChunkAudioURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadata",
+ "ChunkAudioURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading",
+ "ChunkAudioURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext",
+ "ChunkAudioURLInputChunkGeneratedMetadataTextChunkGeneratedMetadata",
+ "ChunkAudioURLInputChunkGeneratedMetadataPdfChunkGeneratedMetadata",
+ "ChunkAudioURLInputChunkGeneratedMetadataCodeChunkGeneratedMetadata",
+ "ChunkAudioURLInputChunkGeneratedMetadataAudioChunkGeneratedMetadata",
+ "ChunkAudioURLInputChunkGeneratedMetadataVideoChunkGeneratedMetadata",
+ "ChunkAudioURLInputChunkGeneratedMetadataImageChunkGeneratedMetadata",
+ "ChunkAudioURLInputChunkAudioURL",
+ "ChunkVideoURLInputChunk",
+ "ChunkVideoURLInputChunkGeneratedMetadata",
+ "ChunkVideoURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadata",
+ "ChunkVideoURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading",
+ "ChunkVideoURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext",
+ "ChunkVideoURLInputChunkGeneratedMetadataTextChunkGeneratedMetadata",
+ "ChunkVideoURLInputChunkGeneratedMetadataPdfChunkGeneratedMetadata",
+ "ChunkVideoURLInputChunkGeneratedMetadataCodeChunkGeneratedMetadata",
+ "ChunkVideoURLInputChunkGeneratedMetadataAudioChunkGeneratedMetadata",
+ "ChunkVideoURLInputChunkGeneratedMetadataVideoChunkGeneratedMetadata",
+ "ChunkVideoURLInputChunkGeneratedMetadataImageChunkGeneratedMetadata",
+ "ChunkVideoURLInputChunkVideoURL",
+]
+
+
+class Config(BaseModel):
+ """Configuration for a file."""
+
+ parsing_strategy: Optional[Literal["fast", "high_quality"]] = None
+ """Strategy for adding the file, this overrides the store-level default"""
+
+
+class ChunkTextInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading(BaseModel):
+ level: int
+
+ text: str
+
+
+class ChunkTextInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext(BaseModel):
+ level: int
+
+ text: str
+
+
+class ChunkTextInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["markdown"]] = None
+
+ file_type: Optional[Literal["text/markdown"]] = None
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ chunk_headings: Optional[List[ChunkTextInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading]] = (
+ None
+ )
+
+ heading_context: Optional[
+ List[ChunkTextInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext]
+ ] = None
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ frontmatter: Optional[Dict[str, object]] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkTextInputChunkGeneratedMetadataTextChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["text"]] = None
+
+ file_type: Optional[Literal["text/plain"]] = None
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkTextInputChunkGeneratedMetadataPdfChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["pdf"]] = None
+
+ file_type: Optional[Literal["application/pdf"]] = None
+
+ total_pages: int
+
+ total_size: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkTextInputChunkGeneratedMetadataCodeChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["code"]] = None
+
+ file_type: str
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkTextInputChunkGeneratedMetadataAudioChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["audio"]] = None
+
+ file_type: str
+
+ file_size: int
+
+ total_duration_seconds: float
+
+ sample_rate: int
+
+ channels: int
+
+ audio_format: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkTextInputChunkGeneratedMetadataVideoChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["video"]] = None
+
+ file_type: str
+
+ file_size: Optional[int] = None
+
+ total_duration_seconds: float
+
+ fps: float
+
+ width: int
+
+ height: int
+
+ frame_count: int
+
+ has_audio_stream: Optional[bool] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkTextInputChunkGeneratedMetadataImageChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["image"]] = None
+
+ file_type: str
+
+ file_size: int
+
+ width: int
+
+ height: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+ChunkTextInputChunkGeneratedMetadata: TypeAlias = Annotated[
+ Union[
+ ChunkTextInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadata,
+ ChunkTextInputChunkGeneratedMetadataTextChunkGeneratedMetadata,
+ ChunkTextInputChunkGeneratedMetadataPdfChunkGeneratedMetadata,
+ ChunkTextInputChunkGeneratedMetadataCodeChunkGeneratedMetadata,
+ ChunkTextInputChunkGeneratedMetadataAudioChunkGeneratedMetadata,
+ ChunkTextInputChunkGeneratedMetadataVideoChunkGeneratedMetadata,
+ ChunkTextInputChunkGeneratedMetadataImageChunkGeneratedMetadata,
+ None,
+ ],
+ PropertyInfo(discriminator="type"),
+]
+
+
+class ChunkTextInputChunk(BaseModel):
+ chunk_index: int
+ """position of the chunk in a file"""
+
+ mime_type: Optional[str] = None
+ """mime type of the chunk"""
+
+ generated_metadata: Optional[ChunkTextInputChunkGeneratedMetadata] = None
+ """metadata of the chunk"""
+
+ model: Optional[str] = None
+ """model used for this chunk"""
+
+ type: Optional[Literal["text"]] = None
+ """Input type identifier"""
+
+ offset: Optional[int] = None
+ """The offset of the text in the file relative to the start of the file."""
+
+ text: Optional[str] = None
+ """Text content"""
+
+
+class ChunkImageURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading(BaseModel):
+ level: int
+
+ text: str
+
+
+class ChunkImageURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext(BaseModel):
+ level: int
+
+ text: str
+
+
+class ChunkImageURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["markdown"]] = None
+
+ file_type: Optional[Literal["text/markdown"]] = None
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ chunk_headings: Optional[
+ List[ChunkImageURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading]
+ ] = None
+
+ heading_context: Optional[
+ List[ChunkImageURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext]
+ ] = None
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ frontmatter: Optional[Dict[str, object]] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkImageURLInputChunkGeneratedMetadataTextChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["text"]] = None
+
+ file_type: Optional[Literal["text/plain"]] = None
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkImageURLInputChunkGeneratedMetadataPdfChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["pdf"]] = None
+
+ file_type: Optional[Literal["application/pdf"]] = None
+
+ total_pages: int
+
+ total_size: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkImageURLInputChunkGeneratedMetadataCodeChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["code"]] = None
+
+ file_type: str
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkImageURLInputChunkGeneratedMetadataAudioChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["audio"]] = None
+
+ file_type: str
+
+ file_size: int
+
+ total_duration_seconds: float
+
+ sample_rate: int
+
+ channels: int
+
+ audio_format: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkImageURLInputChunkGeneratedMetadataVideoChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["video"]] = None
+
+ file_type: str
+
+ file_size: Optional[int] = None
+
+ total_duration_seconds: float
+
+ fps: float
+
+ width: int
+
+ height: int
+
+ frame_count: int
+
+ has_audio_stream: Optional[bool] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkImageURLInputChunkGeneratedMetadataImageChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["image"]] = None
+
+ file_type: str
+
+ file_size: int
+
+ width: int
+
+ height: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+ChunkImageURLInputChunkGeneratedMetadata: TypeAlias = Annotated[
+ Union[
+ ChunkImageURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadata,
+ ChunkImageURLInputChunkGeneratedMetadataTextChunkGeneratedMetadata,
+ ChunkImageURLInputChunkGeneratedMetadataPdfChunkGeneratedMetadata,
+ ChunkImageURLInputChunkGeneratedMetadataCodeChunkGeneratedMetadata,
+ ChunkImageURLInputChunkGeneratedMetadataAudioChunkGeneratedMetadata,
+ ChunkImageURLInputChunkGeneratedMetadataVideoChunkGeneratedMetadata,
+ ChunkImageURLInputChunkGeneratedMetadataImageChunkGeneratedMetadata,
+ None,
+ ],
+ PropertyInfo(discriminator="type"),
+]
+
+
+class ChunkImageURLInputChunkImageURL(BaseModel):
+ """Model for image URL validation."""
+
+ 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"""
+
+ mime_type: Optional[str] = None
+ """mime type of the chunk"""
+
+ generated_metadata: Optional[ChunkImageURLInputChunkGeneratedMetadata] = None
+ """metadata of the chunk"""
+
+ model: Optional[str] = None
+ """model used for this chunk"""
+
+ type: Optional[Literal["image_url"]] = None
+ """Input type identifier"""
+
+ ocr_text: Optional[str] = None
+ """ocr text of the image"""
+
+ summary: Optional[str] = None
+ """summary of the image"""
+
+ image_url: Optional[ChunkImageURLInputChunkImageURL] = None
+ """Model for image URL validation."""
+
+
+class ChunkAudioURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading(BaseModel):
+ level: int
+
+ text: str
+
+
+class ChunkAudioURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext(BaseModel):
+ level: int
+
+ text: str
+
+
+class ChunkAudioURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["markdown"]] = None
+
+ file_type: Optional[Literal["text/markdown"]] = None
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ chunk_headings: Optional[
+ List[ChunkAudioURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading]
+ ] = None
+
+ heading_context: Optional[
+ List[ChunkAudioURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext]
+ ] = None
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ frontmatter: Optional[Dict[str, object]] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkAudioURLInputChunkGeneratedMetadataTextChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["text"]] = None
+
+ file_type: Optional[Literal["text/plain"]] = None
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkAudioURLInputChunkGeneratedMetadataPdfChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["pdf"]] = None
+
+ file_type: Optional[Literal["application/pdf"]] = None
+
+ total_pages: int
+
+ total_size: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkAudioURLInputChunkGeneratedMetadataCodeChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["code"]] = None
+
+ file_type: str
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkAudioURLInputChunkGeneratedMetadataAudioChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["audio"]] = None
+
+ file_type: str
+
+ file_size: int
+
+ total_duration_seconds: float
+
+ sample_rate: int
+
+ channels: int
+
+ audio_format: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkAudioURLInputChunkGeneratedMetadataVideoChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["video"]] = None
+
+ file_type: str
+
+ file_size: Optional[int] = None
+
+ total_duration_seconds: float
+
+ fps: float
+
+ width: int
+
+ height: int
+
+ frame_count: int
+
+ has_audio_stream: Optional[bool] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkAudioURLInputChunkGeneratedMetadataImageChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["image"]] = None
+
+ file_type: str
+
+ file_size: int
+
+ width: int
+
+ height: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+ChunkAudioURLInputChunkGeneratedMetadata: TypeAlias = Annotated[
+ Union[
+ ChunkAudioURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadata,
+ ChunkAudioURLInputChunkGeneratedMetadataTextChunkGeneratedMetadata,
+ ChunkAudioURLInputChunkGeneratedMetadataPdfChunkGeneratedMetadata,
+ ChunkAudioURLInputChunkGeneratedMetadataCodeChunkGeneratedMetadata,
+ ChunkAudioURLInputChunkGeneratedMetadataAudioChunkGeneratedMetadata,
+ ChunkAudioURLInputChunkGeneratedMetadataVideoChunkGeneratedMetadata,
+ ChunkAudioURLInputChunkGeneratedMetadataImageChunkGeneratedMetadata,
+ None,
+ ],
+ PropertyInfo(discriminator="type"),
+]
+
+
+class ChunkAudioURLInputChunkAudioURL(BaseModel):
+ """Model for audio URL validation."""
+
+ url: str
+ """The audio URL. Can be either a URL or a Data URI."""
+
+
+class ChunkAudioURLInputChunk(BaseModel):
+ chunk_index: int
+ """position of the chunk in a file"""
+
+ mime_type: Optional[str] = None
+ """mime type of the chunk"""
+
+ generated_metadata: Optional[ChunkAudioURLInputChunkGeneratedMetadata] = None
+ """metadata of the chunk"""
+
+ model: Optional[str] = None
+ """model used for this chunk"""
+
+ type: Optional[Literal["audio_url"]] = None
+ """Input type identifier"""
+
+ transcription: Optional[str] = None
+ """speech recognition (sr) text of the audio"""
+
+ summary: Optional[str] = None
+ """summary of the audio"""
+
+ audio_url: Optional[ChunkAudioURLInputChunkAudioURL] = None
+ """Model for audio URL validation."""
+
+ sampling_rate: int
+ """The sampling rate of the audio."""
+
+
+class ChunkVideoURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading(BaseModel):
+ level: int
+
+ text: str
+
+
+class ChunkVideoURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext(BaseModel):
+ level: int
+
+ text: str
+
+
+class ChunkVideoURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["markdown"]] = None
+
+ file_type: Optional[Literal["text/markdown"]] = None
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ chunk_headings: Optional[
+ List[ChunkVideoURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataChunkHeading]
+ ] = None
+
+ heading_context: Optional[
+ List[ChunkVideoURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadataHeadingContext]
+ ] = None
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ frontmatter: Optional[Dict[str, object]] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkVideoURLInputChunkGeneratedMetadataTextChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["text"]] = None
+
+ file_type: Optional[Literal["text/plain"]] = None
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkVideoURLInputChunkGeneratedMetadataPdfChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["pdf"]] = None
+
+ file_type: Optional[Literal["application/pdf"]] = None
+
+ total_pages: int
+
+ total_size: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkVideoURLInputChunkGeneratedMetadataCodeChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["code"]] = None
+
+ file_type: str
+
+ language: str
+
+ word_count: int
+
+ file_size: int
+
+ start_line: Optional[int] = None
+
+ num_lines: Optional[int] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkVideoURLInputChunkGeneratedMetadataAudioChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["audio"]] = None
+
+ file_type: str
+
+ file_size: int
+
+ total_duration_seconds: float
+
+ sample_rate: int
+
+ channels: int
+
+ audio_format: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkVideoURLInputChunkGeneratedMetadataVideoChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["video"]] = None
+
+ file_type: str
+
+ file_size: Optional[int] = None
+
+ total_duration_seconds: float
+
+ fps: float
+
+ width: int
+
+ height: int
+
+ frame_count: int
+
+ has_audio_stream: Optional[bool] = None
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+class ChunkVideoURLInputChunkGeneratedMetadataImageChunkGeneratedMetadata(BaseModel):
+ type: Optional[Literal["image"]] = None
+
+ file_type: str
+
+ file_size: int
+
+ width: int
+
+ height: int
+
+ file_extension: Optional[str] = None
+
+ if TYPE_CHECKING:
+ # Some versions of Pydantic <2.8.0 have a bug and don’t allow assigning a
+ # value to this field, so for compatibility we avoid doing it at runtime.
+ __pydantic_extra__: Dict[str, object] = FieldInfo(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ # Stub to indicate that arbitrary properties are accepted.
+ # To access properties that are not valid identifiers you can use `getattr`, e.g.
+ # `getattr(obj, '$type')`
+ def __getattr__(self, attr: str) -> object: ...
+ else:
+ __pydantic_extra__: Dict[str, object]
+
+
+ChunkVideoURLInputChunkGeneratedMetadata: TypeAlias = Annotated[
+ Union[
+ ChunkVideoURLInputChunkGeneratedMetadataMarkdownChunkGeneratedMetadata,
+ ChunkVideoURLInputChunkGeneratedMetadataTextChunkGeneratedMetadata,
+ ChunkVideoURLInputChunkGeneratedMetadataPdfChunkGeneratedMetadata,
+ ChunkVideoURLInputChunkGeneratedMetadataCodeChunkGeneratedMetadata,
+ ChunkVideoURLInputChunkGeneratedMetadataAudioChunkGeneratedMetadata,
+ ChunkVideoURLInputChunkGeneratedMetadataVideoChunkGeneratedMetadata,
+ ChunkVideoURLInputChunkGeneratedMetadataImageChunkGeneratedMetadata,
+ None,
+ ],
+ PropertyInfo(discriminator="type"),
+]
+
+
+class ChunkVideoURLInputChunkVideoURL(BaseModel):
+ """Model for video URL validation."""
+
+ url: str
+ """The video URL. Can be either a URL or a Data URI."""
+
+
+class ChunkVideoURLInputChunk(BaseModel):
+ chunk_index: int
+ """position of the chunk in a file"""
+
+ mime_type: Optional[str] = None
+ """mime type of the chunk"""
+
+ generated_metadata: Optional[ChunkVideoURLInputChunkGeneratedMetadata] = None
+ """metadata of the chunk"""
+
+ model: Optional[str] = None
+ """model used for this chunk"""
+
+ type: Optional[Literal["video_url"]] = None
+ """Input type identifier"""
+
+ transcription: Optional[str] = None
+ """speech recognition (sr) text of the video"""
+
+ summary: Optional[str] = None
+ """summary of the video"""
+
+ video_url: Optional[ChunkVideoURLInputChunkVideoURL] = None
+ """Model for video URL validation."""
+
+
+Chunk: TypeAlias = Annotated[
+ Union[ChunkTextInputChunk, ChunkImageURLInputChunk, ChunkAudioURLInputChunk, ChunkVideoURLInputChunk],
+ PropertyInfo(discriminator="type"),
+]
+
+
+class StoreFile(BaseModel):
+ """Represents a file stored in a store."""
+
+ id: str
+ """Unique identifier for the file"""
+
+ filename: Optional[str] = None
+ """Name of the file"""
+
+ metadata: Optional[object] = None
+ """Optional file metadata"""
+
+ external_id: Optional[str] = None
+ """External identifier for this file in the store"""
+
+ status: Optional[StoreFileStatus] = None
+ """Processing status of the file"""
+
+ last_error: Optional[object] = None
+ """Last error message if processing failed"""
+
+ store_id: str
+ """ID of the containing store"""
+
+ created_at: datetime
+ """Timestamp of store file creation"""
+
+ version: Optional[int] = None
+ """Version number of the file"""
+
+ usage_bytes: Optional[int] = None
+ """Storage usage in bytes"""
+
+ usage_tokens: Optional[int] = None
+ """Storage usage in tokens"""
+
+ config: Optional[Config] = None
+ """Configuration for a file."""
+
+ object: Optional[Literal["store.file"]] = None
+ """Type of the object"""
+
+ chunks: Optional[List[Chunk]] = None
+ """chunks"""
diff --git a/src/mixedbread/types/stores/store_file_status.py b/src/mixedbread/types/stores/store_file_status.py
new file mode 100644
index 00000000..a0061477
--- /dev/null
+++ b/src/mixedbread/types/stores/store_file_status.py
@@ -0,0 +1,7 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal, TypeAlias
+
+__all__ = ["StoreFileStatus"]
+
+StoreFileStatus: TypeAlias = Literal["pending", "in_progress", "cancelled", "completed", "failed"]
diff --git a/src/mixedbread/types/vector_store.py b/src/mixedbread/types/vector_store.py
deleted file mode 100644
index 262d13c3..00000000
--- a/src/mixedbread/types/vector_store.py
+++ /dev/null
@@ -1,69 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from typing import Optional
-from datetime import datetime
-from typing_extensions import Literal
-
-from .._models import BaseModel
-
-__all__ = ["VectorStore", "ExpiresAfter", "FileCounts"]
-
-
-class ExpiresAfter(BaseModel):
- anchor: Optional[Literal["last_used_at"]] = None
- """Anchor date for the expiration policy"""
-
- days: Optional[int] = None
- """Number of days after which the vector store expires"""
-
-
-class FileCounts(BaseModel):
- canceled: Optional[int] = None
- """Number of files whose processing was canceled"""
-
- failed: Optional[int] = None
- """Number of files that failed processing"""
-
- in_progress: Optional[int] = None
- """Number of files currently being processed"""
-
- successful: Optional[int] = None
- """Number of successfully processed files"""
-
- total: Optional[int] = None
- """Total number of files"""
-
-
-class VectorStore(BaseModel):
- id: str
- """Unique identifier for the vector store"""
-
- created_at: datetime
- """Timestamp when the vector store was created"""
-
- name: str
- """Name of the vector store"""
-
- updated_at: datetime
- """Timestamp when the vector store was last updated"""
-
- description: Optional[str] = None
- """Detailed description of the vector store's purpose and contents"""
-
- expires_after: Optional[ExpiresAfter] = None
- """Represents an expiration policy for a vector store."""
-
- expires_at: Optional[datetime] = None
- """Optional expiration timestamp for the vector store"""
-
- file_counts: Optional[FileCounts] = None
- """Counts of files in different states"""
-
- last_active_at: Optional[datetime] = None
- """Timestamp when the vector store was last used"""
-
- metadata: Optional[object] = None
- """Additional metadata associated with the vector store"""
-
- object: Optional[Literal["vector_store"]] = None
- """Type of the object"""
diff --git a/src/mixedbread/types/vector_store_create_params.py b/src/mixedbread/types/vector_store_create_params.py
deleted file mode 100644
index 891e58c9..00000000
--- a/src/mixedbread/types/vector_store_create_params.py
+++ /dev/null
@@ -1,33 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing import List, Optional
-from typing_extensions import Literal, TypedDict
-
-__all__ = ["VectorStoreCreateParams", "ExpiresAfter"]
-
-
-class VectorStoreCreateParams(TypedDict, total=False):
- description: Optional[str]
- """Description of the vector store"""
-
- expires_after: Optional[ExpiresAfter]
- """Represents an expiration policy for a vector store."""
-
- file_ids: Optional[List[str]]
- """Optional list of file IDs"""
-
- metadata: object
- """Optional metadata key-value pairs"""
-
- name: Optional[str]
- """Name for the new vector store"""
-
-
-class ExpiresAfter(TypedDict, total=False):
- anchor: Literal["last_used_at"]
- """Anchor date for the expiration policy"""
-
- days: int
- """Number of days after which the vector store expires"""
diff --git a/src/mixedbread/types/vector_store_list_params.py b/src/mixedbread/types/vector_store_list_params.py
deleted file mode 100644
index 39225421..00000000
--- a/src/mixedbread/types/vector_store_list_params.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing_extensions import TypedDict
-
-__all__ = ["VectorStoreListParams"]
-
-
-class VectorStoreListParams(TypedDict, total=False):
- limit: int
- """Maximum number of items to return per page"""
-
- offset: int
- """Offset of the first item to return"""
diff --git a/src/mixedbread/types/vector_store_list_response.py b/src/mixedbread/types/vector_store_list_response.py
deleted file mode 100644
index 0b1f21c2..00000000
--- a/src/mixedbread/types/vector_store_list_response.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# 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 import VectorStore
-
-__all__ = ["VectorStoreListResponse", "Pagination"]
-
-
-class Pagination(BaseModel):
- limit: Optional[int] = None
- """Maximum number of items to return per page"""
-
- offset: Optional[int] = None
- """Offset of the first item to return"""
-
- total: Optional[int] = None
- """Total number of items available"""
-
-
-class VectorStoreListResponse(BaseModel):
- data: List[VectorStore]
- """The list of vector stores"""
-
- pagination: Pagination
- """Pagination model that includes total count of items."""
-
- object: Optional[Literal["list"]] = None
- """The object type of the response"""
diff --git a/src/mixedbread/types/vector_store_question_answering_params.py b/src/mixedbread/types/vector_store_question_answering_params.py
deleted file mode 100644
index 1d901395..00000000
--- a/src/mixedbread/types/vector_store_question_answering_params.py
+++ /dev/null
@@ -1,50 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing import List
-from typing_extensions import Required, TypedDict
-
-__all__ = ["VectorStoreQuestionAnsweringParams", "QaOptions", "SearchOptions"]
-
-
-class VectorStoreQuestionAnsweringParams(TypedDict, total=False):
- vector_store_ids: Required[List[str]]
- """IDs of vector stores to search"""
-
- qa_options: QaOptions
- """Question answering configuration options"""
-
- query: str
- """Question to answer.
-
- If not provided, the question will be extracted from the passed messages.
- """
-
- search_options: SearchOptions
- """Search configuration options"""
-
- stream: bool
- """Whether to stream the answer"""
-
- top_k: int
- """Number of results to return"""
-
-
-class QaOptions(TypedDict, total=False):
- cite: bool
- """Whether to use citations"""
-
-
-class SearchOptions(TypedDict, total=False):
- return_chunks: bool
- """Whether to return matching text chunks"""
-
- return_metadata: bool
- """Whether to return file metadata"""
-
- rewrite_query: bool
- """Whether to rewrite the query"""
-
- score_threshold: float
- """Minimum similarity score threshold"""
diff --git a/src/mixedbread/types/vector_store_search_params.py b/src/mixedbread/types/vector_store_search_params.py
deleted file mode 100644
index 90900d4a..00000000
--- a/src/mixedbread/types/vector_store_search_params.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing import List
-from typing_extensions import Required, TypedDict
-
-__all__ = ["VectorStoreSearchParams", "SearchOptions"]
-
-
-class VectorStoreSearchParams(TypedDict, total=False):
- query: Required[str]
- """Search query text"""
-
- vector_store_ids: Required[List[str]]
- """IDs of vector stores to search"""
-
- search_options: SearchOptions
- """Search configuration options"""
-
- top_k: int
- """Number of results to return"""
-
-
-class SearchOptions(TypedDict, total=False):
- return_chunks: bool
- """Whether to return matching text chunks"""
-
- return_metadata: bool
- """Whether to return file metadata"""
-
- rewrite_query: bool
- """Whether to rewrite the query"""
-
- score_threshold: float
- """Minimum similarity score threshold"""
diff --git a/src/mixedbread/types/vector_store_search_response.py b/src/mixedbread/types/vector_store_search_response.py
deleted file mode 100644
index 53d32217..00000000
--- a/src/mixedbread/types/vector_store_search_response.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from typing import Dict, List, Union, Optional
-from typing_extensions import Literal, TypeAlias
-
-from .._models import BaseModel
-
-__all__ = [
- "VectorStoreSearchResponse",
- "Data",
- "DataValue",
- "DataValueImageURLInput",
- "DataValueImageURLInputImage",
- "DataValueTextInput",
-]
-
-
-class DataValueImageURLInputImage(BaseModel):
- url: str
- """The image URL. Can be either a URL or a Data URI."""
-
-
-class DataValueImageURLInput(BaseModel):
- image: DataValueImageURLInputImage
- """The image input specification."""
-
- type: Optional[Literal["image_url"]] = None
- """Input type identifier"""
-
-
-class DataValueTextInput(BaseModel):
- text: str
- """Text content to process"""
-
- type: Optional[Literal["text"]] = None
- """Input type identifier"""
-
-
-DataValue: TypeAlias = Union[str, DataValueImageURLInput, DataValueTextInput, Dict[str, object], None]
-
-
-class Data(BaseModel):
- file_id: str
- """file id"""
-
- position: int
- """position of the chunk in a file"""
-
- score: float
- """score of the chunk"""
-
- vector_store_id: str
- """vector store id"""
-
- content: Optional[str] = None
- """content of the chunk"""
-
- metadata: Optional[object] = None
- """file metadata"""
-
- value: Optional[DataValue] = None
- """value of the chunk"""
-
-
-class VectorStoreSearchResponse(BaseModel):
- data: List[Data]
- """The list of scored vector store file chunks"""
-
- object: Optional[Literal["list"]] = None
- """The object type of the response"""
diff --git a/src/mixedbread/types/vector_store_update_params.py b/src/mixedbread/types/vector_store_update_params.py
deleted file mode 100644
index 74008480..00000000
--- a/src/mixedbread/types/vector_store_update_params.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing import Optional
-from typing_extensions import Literal, TypedDict
-
-__all__ = ["VectorStoreUpdateParams", "ExpiresAfter"]
-
-
-class VectorStoreUpdateParams(TypedDict, total=False):
- description: Optional[str]
- """New description"""
-
- expires_after: Optional[ExpiresAfter]
- """Represents an expiration policy for a vector store."""
-
- metadata: object
- """Optional metadata key-value pairs"""
-
- name: Optional[str]
- """New name for the vector store"""
-
-
-class ExpiresAfter(TypedDict, total=False):
- anchor: Literal["last_used_at"]
- """Anchor date for the expiration policy"""
-
- days: int
- """Number of days after which the vector store expires"""
diff --git a/src/mixedbread/types/vector_stores/file_create_params.py b/src/mixedbread/types/vector_stores/file_create_params.py
deleted file mode 100644
index 930996a7..00000000
--- a/src/mixedbread/types/vector_stores/file_create_params.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing_extensions import Required, TypedDict
-
-__all__ = ["FileCreateParams"]
-
-
-class FileCreateParams(TypedDict, total=False):
- file_id: Required[str]
- """ID of the file to add"""
-
- metadata: object
- """Optional metadata for the file"""
diff --git a/src/mixedbread/types/vector_stores/file_list_response.py b/src/mixedbread/types/vector_stores/file_list_response.py
deleted file mode 100644
index aec81cf9..00000000
--- a/src/mixedbread/types/vector_stores/file_list_response.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# 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):
- limit: Optional[int] = None
- """Maximum number of items to return per page"""
-
- offset: Optional[int] = None
- """Offset of the first item to return"""
-
- total: Optional[int] = None
- """Total number of items available"""
-
-
-class FileListResponse(BaseModel):
- data: List[VectorStoreFile]
- """The list of vector store files"""
-
- pagination: Pagination
- """Pagination model that includes total count of items."""
-
- object: Optional[Literal["list"]] = None
- """The object type of the response"""
diff --git a/src/mixedbread/types/vector_stores/file_search_params.py b/src/mixedbread/types/vector_stores/file_search_params.py
deleted file mode 100644
index f98e3f10..00000000
--- a/src/mixedbread/types/vector_stores/file_search_params.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-from typing import List
-from typing_extensions import Required, TypedDict
-
-__all__ = ["FileSearchParams", "SearchOptions"]
-
-
-class FileSearchParams(TypedDict, total=False):
- query: Required[str]
- """Search query text"""
-
- vector_store_ids: Required[List[str]]
- """IDs of vector stores to search"""
-
- search_options: SearchOptions
- """Search configuration options"""
-
- top_k: int
- """Number of results to return"""
-
-
-class SearchOptions(TypedDict, total=False):
- return_chunks: bool
- """Whether to return matching text chunks"""
-
- return_metadata: bool
- """Whether to return file metadata"""
-
- rewrite_query: bool
- """Whether to rewrite the query"""
-
- score_threshold: float
- """Minimum similarity score threshold"""
diff --git a/src/mixedbread/types/vector_stores/file_search_response.py b/src/mixedbread/types/vector_stores/file_search_response.py
deleted file mode 100644
index 57ee464f..00000000
--- a/src/mixedbread/types/vector_stores/file_search_response.py
+++ /dev/null
@@ -1,98 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from typing import Dict, List, Union, Optional
-from datetime import datetime
-from typing_extensions import Literal, TypeAlias
-
-from ..._models import BaseModel
-
-__all__ = [
- "FileSearchResponse",
- "Data",
- "DataChunk",
- "DataChunkValue",
- "DataChunkValueImageURLInput",
- "DataChunkValueImageURLInputImage",
- "DataChunkValueTextInput",
-]
-
-
-class DataChunkValueImageURLInputImage(BaseModel):
- url: str
- """The image URL. Can be either a URL or a Data URI."""
-
-
-class DataChunkValueImageURLInput(BaseModel):
- image: DataChunkValueImageURLInputImage
- """The image input specification."""
-
- type: Optional[Literal["image_url"]] = None
- """Input type identifier"""
-
-
-class DataChunkValueTextInput(BaseModel):
- text: str
- """Text content to process"""
-
- type: Optional[Literal["text"]] = None
- """Input type identifier"""
-
-
-DataChunkValue: TypeAlias = Union[str, DataChunkValueImageURLInput, DataChunkValueTextInput, Dict[str, object], None]
-
-
-class DataChunk(BaseModel):
- file_id: str
- """file id"""
-
- position: int
- """position of the chunk in a file"""
-
- score: float
- """score of the chunk"""
-
- vector_store_id: str
- """vector store id"""
-
- content: Optional[str] = None
- """content of the chunk"""
-
- metadata: Optional[object] = None
- """file metadata"""
-
- value: Optional[DataChunkValue] = None
- """value of the chunk"""
-
-
-class Data(BaseModel):
- id: str
- """file id"""
-
- created_at: datetime
- """Timestamp of vector store file creation"""
-
- score: float
- """score of the file"""
-
- usage_bytes: int
- """usage in bytes"""
-
- vector_store_id: str
- """vector store id"""
-
- version: int
- """version of the file"""
-
- chunks: Optional[List[DataChunk]] = None
- """chunks"""
-
- metadata: Optional[object] = None
- """metadata"""
-
-
-class FileSearchResponse(BaseModel):
- data: List[Data]
- """The list of scored vector store files"""
-
- object: Optional[Literal["list"]] = None
- """The object type of the response"""
diff --git a/src/mixedbread/types/vector_stores/vector_store_file.py b/src/mixedbread/types/vector_stores/vector_store_file.py
deleted file mode 100644
index 996333bc..00000000
--- a/src/mixedbread/types/vector_stores/vector_store_file.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from typing import List, Optional
-from datetime import datetime
-from typing_extensions import Literal
-
-from ..._models import BaseModel
-from ..file_object import FileObject
-
-__all__ = ["VectorStoreFile"]
-
-
-class VectorStoreFile(BaseModel):
- id: str
- """Unique identifier for the file"""
-
- created_at: datetime
- """Timestamp of vector store file creation"""
-
- vector_store_id: str
- """ID of the containing vector store"""
-
- errors: Optional[List[str]] = None
- """List of error messages if processing failed"""
-
- file_object: Optional[FileObject] = None
- """A model representing a file object in the system.
-
- This model contains metadata about files stored in the system, including
- identifiers, size information, and timestamps.
- """
-
- metadata: Optional[object] = None
- """Optional file metadata"""
-
- object: Optional[Literal["vector_store.file"]] = None
- """Type of the object"""
-
- status: Optional[Literal["none", "running", "canceled", "successful", "failed", "resumable", "pending"]] = None
- """Processing status of the file"""
-
- usage_bytes: Optional[int] = None
- """Storage usage in bytes"""
-
- version: Optional[int] = None
- """Version number of the file"""
diff --git a/tests/api_resources/vector_stores/__init__.py b/tests/api_resources/data_sources/__init__.py
similarity index 100%
rename from tests/api_resources/vector_stores/__init__.py
rename to tests/api_resources/data_sources/__init__.py
diff --git a/tests/api_resources/data_sources/test_connectors.py b/tests/api_resources/data_sources/test_connectors.py
new file mode 100644
index 00000000..20d3847e
--- /dev/null
+++ b/tests/api_resources/data_sources/test_connectors.py
@@ -0,0 +1,546 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from mixedbread import Mixedbread, AsyncMixedbread
+from tests.utils import assert_matches_type
+from mixedbread.pagination import SyncCursor, AsyncCursor
+from mixedbread.types.data_sources import (
+ DataSourceConnector,
+ ConnectorDeleteResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestConnectors:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: Mixedbread) -> None:
+ connector = client.data_sources.connectors.create(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ store_id="store_id",
+ )
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: Mixedbread) -> None:
+ connector = client.data_sources.connectors.create(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ store_id="store_id",
+ name="name",
+ trigger_sync=True,
+ metadata={},
+ polling_interval=1800,
+ )
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: Mixedbread) -> None:
+ response = client.data_sources.connectors.with_raw_response.create(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ store_id="store_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ connector = response.parse()
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: Mixedbread) -> None:
+ with client.data_sources.connectors.with_streaming_response.create(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ store_id="store_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ connector = response.parse()
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_create(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `data_source_id` but received ''"):
+ client.data_sources.connectors.with_raw_response.create(
+ data_source_id="",
+ store_id="store_id",
+ )
+
+ @parametrize
+ def test_method_retrieve(self, client: Mixedbread) -> None:
+ connector = client.data_sources.connectors.retrieve(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Mixedbread) -> None:
+ response = client.data_sources.connectors.with_raw_response.retrieve(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ connector = response.parse()
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Mixedbread) -> None:
+ with client.data_sources.connectors.with_streaming_response.retrieve(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ connector = response.parse()
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `data_source_id` but received ''"):
+ client.data_sources.connectors.with_raw_response.retrieve(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `connector_id` but received ''"):
+ client.data_sources.connectors.with_raw_response.retrieve(
+ connector_id="",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ @parametrize
+ def test_method_update(self, client: Mixedbread) -> None:
+ connector = client.data_sources.connectors.update(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: Mixedbread) -> None:
+ connector = client.data_sources.connectors.update(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="name",
+ metadata={"foo": "bar"},
+ trigger_sync=True,
+ polling_interval=1800,
+ )
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ @parametrize
+ def test_raw_response_update(self, client: Mixedbread) -> None:
+ response = client.data_sources.connectors.with_raw_response.update(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ connector = response.parse()
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update(self, client: Mixedbread) -> None:
+ with client.data_sources.connectors.with_streaming_response.update(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ connector = response.parse()
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `data_source_id` but received ''"):
+ client.data_sources.connectors.with_raw_response.update(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `connector_id` but received ''"):
+ client.data_sources.connectors.with_raw_response.update(
+ connector_id="",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ @parametrize
+ def test_method_list(self, client: Mixedbread) -> None:
+ connector = client.data_sources.connectors.list(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(SyncCursor[DataSourceConnector], connector, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: Mixedbread) -> None:
+ connector = client.data_sources.connectors.list(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ limit=10,
+ after="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ before="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ include_total=False,
+ )
+ assert_matches_type(SyncCursor[DataSourceConnector], connector, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: Mixedbread) -> None:
+ response = client.data_sources.connectors.with_raw_response.list(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ connector = response.parse()
+ assert_matches_type(SyncCursor[DataSourceConnector], connector, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: Mixedbread) -> None:
+ with client.data_sources.connectors.with_streaming_response.list(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ connector = response.parse()
+ assert_matches_type(SyncCursor[DataSourceConnector], connector, 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 `data_source_id` but received ''"):
+ client.data_sources.connectors.with_raw_response.list(
+ data_source_id="",
+ )
+
+ @parametrize
+ def test_method_delete(self, client: Mixedbread) -> None:
+ connector = client.data_sources.connectors.delete(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(ConnectorDeleteResponse, connector, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: Mixedbread) -> None:
+ response = client.data_sources.connectors.with_raw_response.delete(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ connector = response.parse()
+ assert_matches_type(ConnectorDeleteResponse, connector, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: Mixedbread) -> None:
+ with client.data_sources.connectors.with_streaming_response.delete(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ connector = response.parse()
+ assert_matches_type(ConnectorDeleteResponse, connector, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `data_source_id` but received ''"):
+ client.data_sources.connectors.with_raw_response.delete(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `connector_id` but received ''"):
+ client.data_sources.connectors.with_raw_response.delete(
+ connector_id="",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+
+class TestAsyncConnectors:
+ 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:
+ connector = await async_client.data_sources.connectors.create(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ store_id="store_id",
+ )
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ connector = await async_client.data_sources.connectors.create(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ store_id="store_id",
+ name="name",
+ trigger_sync=True,
+ metadata={},
+ polling_interval=1800,
+ )
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.data_sources.connectors.with_raw_response.create(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ store_id="store_id",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ connector = await response.parse()
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.data_sources.connectors.with_streaming_response.create(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ store_id="store_id",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ connector = await response.parse()
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `data_source_id` but received ''"):
+ await async_client.data_sources.connectors.with_raw_response.create(
+ data_source_id="",
+ store_id="store_id",
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncMixedbread) -> None:
+ connector = await async_client.data_sources.connectors.retrieve(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.data_sources.connectors.with_raw_response.retrieve(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ connector = await response.parse()
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.data_sources.connectors.with_streaming_response.retrieve(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ connector = await response.parse()
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `data_source_id` but received ''"):
+ await async_client.data_sources.connectors.with_raw_response.retrieve(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `connector_id` but received ''"):
+ await async_client.data_sources.connectors.with_raw_response.retrieve(
+ connector_id="",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncMixedbread) -> None:
+ connector = await async_client.data_sources.connectors.update(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ connector = await async_client.data_sources.connectors.update(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="name",
+ metadata={"foo": "bar"},
+ trigger_sync=True,
+ polling_interval=1800,
+ )
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.data_sources.connectors.with_raw_response.update(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ connector = await response.parse()
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.data_sources.connectors.with_streaming_response.update(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ connector = await response.parse()
+ assert_matches_type(DataSourceConnector, connector, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `data_source_id` but received ''"):
+ await async_client.data_sources.connectors.with_raw_response.update(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `connector_id` but received ''"):
+ await async_client.data_sources.connectors.with_raw_response.update(
+ connector_id="",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncMixedbread) -> None:
+ connector = await async_client.data_sources.connectors.list(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(AsyncCursor[DataSourceConnector], connector, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ connector = await async_client.data_sources.connectors.list(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ limit=10,
+ after="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ before="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ include_total=False,
+ )
+ assert_matches_type(AsyncCursor[DataSourceConnector], connector, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.data_sources.connectors.with_raw_response.list(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ connector = await response.parse()
+ assert_matches_type(AsyncCursor[DataSourceConnector], connector, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.data_sources.connectors.with_streaming_response.list(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ connector = await response.parse()
+ assert_matches_type(AsyncCursor[DataSourceConnector], connector, 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 `data_source_id` but received ''"):
+ await async_client.data_sources.connectors.with_raw_response.list(
+ data_source_id="",
+ )
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncMixedbread) -> None:
+ connector = await async_client.data_sources.connectors.delete(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(ConnectorDeleteResponse, connector, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.data_sources.connectors.with_raw_response.delete(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ connector = await response.parse()
+ assert_matches_type(ConnectorDeleteResponse, connector, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.data_sources.connectors.with_streaming_response.delete(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ connector = await response.parse()
+ assert_matches_type(ConnectorDeleteResponse, connector, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `data_source_id` but received ''"):
+ await async_client.data_sources.connectors.with_raw_response.delete(
+ connector_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ data_source_id="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `connector_id` but received ''"):
+ await async_client.data_sources.connectors.with_raw_response.delete(
+ connector_id="",
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
diff --git a/tests/api_resources/extractions/test_content.py b/tests/api_resources/extractions/test_content.py
index 7beb468b..63ee1aed 100644
--- a/tests/api_resources/extractions/test_content.py
+++ b/tests/api_resources/extractions/test_content.py
@@ -20,16 +20,25 @@ class TestContent:
@parametrize
def test_method_create(self, client: Mixedbread) -> None:
content = client.extractions.content.create(
- content="content",
- json_schema={},
+ content="string",
+ json_schema={"foo": "bar"},
+ )
+ assert_matches_type(ExtractionResult, content, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: Mixedbread) -> None:
+ content = client.extractions.content.create(
+ content="string",
+ json_schema={"foo": "bar"},
+ instructions="instructions",
)
assert_matches_type(ExtractionResult, content, path=["response"])
@parametrize
def test_raw_response_create(self, client: Mixedbread) -> None:
response = client.extractions.content.with_raw_response.create(
- content="content",
- json_schema={},
+ content="string",
+ json_schema={"foo": "bar"},
)
assert response.is_closed is True
@@ -40,8 +49,8 @@ def test_raw_response_create(self, client: Mixedbread) -> None:
@parametrize
def test_streaming_response_create(self, client: Mixedbread) -> None:
with client.extractions.content.with_streaming_response.create(
- content="content",
- json_schema={},
+ content="string",
+ json_schema={"foo": "bar"},
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -53,21 +62,32 @@ 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:
content = await async_client.extractions.content.create(
- content="content",
- json_schema={},
+ content="string",
+ json_schema={"foo": "bar"},
+ )
+ assert_matches_type(ExtractionResult, content, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ content = await async_client.extractions.content.create(
+ content="string",
+ json_schema={"foo": "bar"},
+ instructions="instructions",
)
assert_matches_type(ExtractionResult, content, path=["response"])
@parametrize
async def test_raw_response_create(self, async_client: AsyncMixedbread) -> None:
response = await async_client.extractions.content.with_raw_response.create(
- content="content",
- json_schema={},
+ content="string",
+ json_schema={"foo": "bar"},
)
assert response.is_closed is True
@@ -78,8 +98,8 @@ async def test_raw_response_create(self, async_client: AsyncMixedbread) -> None:
@parametrize
async def test_streaming_response_create(self, async_client: AsyncMixedbread) -> None:
async with async_client.extractions.content.with_streaming_response.create(
- content="content",
- json_schema={},
+ content="string",
+ json_schema={"foo": "bar"},
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
diff --git a/tests/api_resources/extractions/test_jobs.py b/tests/api_resources/extractions/test_jobs.py
index bb055ba7..b79a66a4 100644
--- a/tests/api_resources/extractions/test_jobs.py
+++ b/tests/api_resources/extractions/test_jobs.py
@@ -21,7 +21,7 @@ class TestJobs:
def test_method_create(self, client: Mixedbread) -> None:
job = client.extractions.jobs.create(
file_id="file_id",
- json_schema={},
+ json_schema={"foo": "bar"},
)
assert_matches_type(ExtractionJob, job, path=["response"])
@@ -29,7 +29,7 @@ def test_method_create(self, client: Mixedbread) -> None:
def test_raw_response_create(self, client: Mixedbread) -> None:
response = client.extractions.jobs.with_raw_response.create(
file_id="file_id",
- json_schema={},
+ json_schema={"foo": "bar"},
)
assert response.is_closed is True
@@ -41,7 +41,7 @@ def test_raw_response_create(self, client: Mixedbread) -> None:
def test_streaming_response_create(self, client: Mixedbread) -> None:
with client.extractions.jobs.with_streaming_response.create(
file_id="file_id",
- json_schema={},
+ json_schema={"foo": "bar"},
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -91,13 +91,15 @@ 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:
job = await async_client.extractions.jobs.create(
file_id="file_id",
- json_schema={},
+ json_schema={"foo": "bar"},
)
assert_matches_type(ExtractionJob, job, path=["response"])
@@ -105,7 +107,7 @@ async def test_method_create(self, async_client: AsyncMixedbread) -> None:
async def test_raw_response_create(self, async_client: AsyncMixedbread) -> None:
response = await async_client.extractions.jobs.with_raw_response.create(
file_id="file_id",
- json_schema={},
+ json_schema={"foo": "bar"},
)
assert response.is_closed is True
@@ -117,7 +119,7 @@ async def test_raw_response_create(self, async_client: AsyncMixedbread) -> None:
async def test_streaming_response_create(self, async_client: AsyncMixedbread) -> None:
async with async_client.extractions.jobs.with_streaming_response.create(
file_id="file_id",
- json_schema={},
+ json_schema={"foo": "bar"},
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
diff --git a/tests/api_resources/extractions/test_schema.py b/tests/api_resources/extractions/test_schema.py
index 5bff1298..3de149ff 100644
--- a/tests/api_resources/extractions/test_schema.py
+++ b/tests/api_resources/extractions/test_schema.py
@@ -55,14 +55,14 @@ def test_streaming_response_create(self, client: Mixedbread) -> None:
@parametrize
def test_method_enhance(self, client: Mixedbread) -> None:
schema = client.extractions.schema.enhance(
- json_schema={},
+ json_schema={"foo": "bar"},
)
assert_matches_type(EnhancedJsonSchema, schema, path=["response"])
@parametrize
def test_raw_response_enhance(self, client: Mixedbread) -> None:
response = client.extractions.schema.with_raw_response.enhance(
- json_schema={},
+ json_schema={"foo": "bar"},
)
assert response.is_closed is True
@@ -73,7 +73,7 @@ def test_raw_response_enhance(self, client: Mixedbread) -> None:
@parametrize
def test_streaming_response_enhance(self, client: Mixedbread) -> None:
with client.extractions.schema.with_streaming_response.enhance(
- json_schema={},
+ json_schema={"foo": "bar"},
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -86,14 +86,14 @@ def test_streaming_response_enhance(self, client: Mixedbread) -> None:
@parametrize
def test_method_validate(self, client: Mixedbread) -> None:
schema = client.extractions.schema.validate(
- json_schema={},
+ json_schema={"foo": "bar"},
)
assert_matches_type(ValidatedJsonSchema, schema, path=["response"])
@parametrize
def test_raw_response_validate(self, client: Mixedbread) -> None:
response = client.extractions.schema.with_raw_response.validate(
- json_schema={},
+ json_schema={"foo": "bar"},
)
assert response.is_closed is True
@@ -104,7 +104,7 @@ def test_raw_response_validate(self, client: Mixedbread) -> None:
@parametrize
def test_streaming_response_validate(self, client: Mixedbread) -> None:
with client.extractions.schema.with_streaming_response.validate(
- json_schema={},
+ json_schema={"foo": "bar"},
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -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:
@@ -152,14 +154,14 @@ async def test_streaming_response_create(self, async_client: AsyncMixedbread) ->
@parametrize
async def test_method_enhance(self, async_client: AsyncMixedbread) -> None:
schema = await async_client.extractions.schema.enhance(
- json_schema={},
+ json_schema={"foo": "bar"},
)
assert_matches_type(EnhancedJsonSchema, schema, path=["response"])
@parametrize
async def test_raw_response_enhance(self, async_client: AsyncMixedbread) -> None:
response = await async_client.extractions.schema.with_raw_response.enhance(
- json_schema={},
+ json_schema={"foo": "bar"},
)
assert response.is_closed is True
@@ -170,7 +172,7 @@ async def test_raw_response_enhance(self, async_client: AsyncMixedbread) -> None
@parametrize
async def test_streaming_response_enhance(self, async_client: AsyncMixedbread) -> None:
async with async_client.extractions.schema.with_streaming_response.enhance(
- json_schema={},
+ json_schema={"foo": "bar"},
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -183,14 +185,14 @@ async def test_streaming_response_enhance(self, async_client: AsyncMixedbread) -
@parametrize
async def test_method_validate(self, async_client: AsyncMixedbread) -> None:
schema = await async_client.extractions.schema.validate(
- json_schema={},
+ json_schema={"foo": "bar"},
)
assert_matches_type(ValidatedJsonSchema, schema, path=["response"])
@parametrize
async def test_raw_response_validate(self, async_client: AsyncMixedbread) -> None:
response = await async_client.extractions.schema.with_raw_response.validate(
- json_schema={},
+ json_schema={"foo": "bar"},
)
assert response.is_closed is True
@@ -201,7 +203,7 @@ async def test_raw_response_validate(self, async_client: AsyncMixedbread) -> Non
@parametrize
async def test_streaming_response_validate(self, async_client: AsyncMixedbread) -> None:
async with async_client.extractions.schema.with_streaming_response.validate(
- json_schema={},
+ json_schema={"foo": "bar"},
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
diff --git a/tests/api_resources/files/test_content.py b/tests/api_resources/files/test_content.py
deleted file mode 100644
index d8d219f2..00000000
--- a/tests/api_resources/files/test_content.py
+++ /dev/null
@@ -1,140 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-import os
-from typing import Any, cast
-
-import httpx
-import pytest
-from respx import MockRouter
-
-from mixedbread import Mixedbread, AsyncMixedbread
-from mixedbread._response import (
- BinaryAPIResponse,
- AsyncBinaryAPIResponse,
- StreamedBinaryAPIResponse,
- AsyncStreamedBinaryAPIResponse,
-)
-
-base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
-
-
-class TestContent:
- parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_method_retrieve(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"})
- )
- content = client.files.content.retrieve(
- "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
- assert content.is_closed
- assert content.json() == {"foo": "bar"}
- assert cast(Any, content.is_closed) is True
- assert isinstance(content, BinaryAPIResponse)
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_raw_response_retrieve(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"})
- )
-
- content = client.files.content.with_raw_response.retrieve(
- "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- assert content.is_closed is True
- assert content.http_request.headers.get("X-Stainless-Lang") == "python"
- assert content.json() == {"foo": "bar"}
- assert isinstance(content, BinaryAPIResponse)
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_streaming_response_retrieve(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.content.with_streaming_response.retrieve(
- "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) as content:
- assert not content.is_closed
- assert content.http_request.headers.get("X-Stainless-Lang") == "python"
-
- assert content.json() == {"foo": "bar"}
- assert cast(Any, content.is_closed) is True
- assert isinstance(content, StreamedBinaryAPIResponse)
-
- assert cast(Any, content.is_closed) is True
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- def test_path_params_retrieve(self, client: Mixedbread) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"):
- client.files.content.with_raw_response.retrieve(
- "",
- )
-
-
-class TestAsyncContent:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_method_retrieve(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"})
- )
- content = await async_client.files.content.retrieve(
- "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
- assert content.is_closed
- assert await content.json() == {"foo": "bar"}
- assert cast(Any, content.is_closed) is True
- assert isinstance(content, AsyncBinaryAPIResponse)
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_raw_response_retrieve(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"})
- )
-
- content = await async_client.files.content.with_raw_response.retrieve(
- "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- assert content.is_closed is True
- assert content.http_request.headers.get("X-Stainless-Lang") == "python"
- assert await content.json() == {"foo": "bar"}
- assert isinstance(content, AsyncBinaryAPIResponse)
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_streaming_response_retrieve(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.content.with_streaming_response.retrieve(
- "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) as content:
- assert not content.is_closed
- assert content.http_request.headers.get("X-Stainless-Lang") == "python"
-
- assert await content.json() == {"foo": "bar"}
- assert cast(Any, content.is_closed) is True
- assert isinstance(content, AsyncStreamedBinaryAPIResponse)
-
- assert cast(Any, content.is_closed) is True
-
- @parametrize
- @pytest.mark.respx(base_url=base_url)
- async def test_path_params_retrieve(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.content.with_raw_response.retrieve(
- "",
- )
diff --git a/tests/api_resources/files/test_uploads.py b/tests/api_resources/files/test_uploads.py
new file mode 100644
index 00000000..2dd222a5
--- /dev/null
+++ b/tests/api_resources/files/test_uploads.py
@@ -0,0 +1,450 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from mixedbread import Mixedbread, AsyncMixedbread
+from tests.utils import assert_matches_type
+from mixedbread.types import FileObject
+from mixedbread.types.files import (
+ UploadListResponse,
+ UploadAbortResponse,
+ UploadCreateResponse,
+ UploadRetrieveResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestUploads:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: Mixedbread) -> None:
+ upload = client.files.uploads.create(
+ filename="document.pdf",
+ file_size=10485760,
+ mime_type="application/pdf",
+ )
+ assert_matches_type(UploadCreateResponse, upload, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: Mixedbread) -> None:
+ upload = client.files.uploads.create(
+ filename="document.pdf",
+ file_size=10485760,
+ mime_type="application/pdf",
+ part_count=3,
+ )
+ assert_matches_type(UploadCreateResponse, upload, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: Mixedbread) -> None:
+ response = client.files.uploads.with_raw_response.create(
+ filename="document.pdf",
+ file_size=10485760,
+ mime_type="application/pdf",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ upload = response.parse()
+ assert_matches_type(UploadCreateResponse, upload, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: Mixedbread) -> None:
+ with client.files.uploads.with_streaming_response.create(
+ filename="document.pdf",
+ file_size=10485760,
+ mime_type="application/pdf",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ upload = response.parse()
+ assert_matches_type(UploadCreateResponse, upload, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_retrieve(self, client: Mixedbread) -> None:
+ upload = client.files.uploads.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(UploadRetrieveResponse, upload, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Mixedbread) -> None:
+ response = client.files.uploads.with_raw_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ upload = response.parse()
+ assert_matches_type(UploadRetrieveResponse, upload, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Mixedbread) -> None:
+ with client.files.uploads.with_streaming_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ upload = response.parse()
+ assert_matches_type(UploadRetrieveResponse, upload, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"):
+ client.files.uploads.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ def test_method_list(self, client: Mixedbread) -> None:
+ upload = client.files.uploads.list()
+ assert_matches_type(UploadListResponse, upload, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: Mixedbread) -> None:
+ response = client.files.uploads.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ upload = response.parse()
+ assert_matches_type(UploadListResponse, upload, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: Mixedbread) -> None:
+ with client.files.uploads.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ upload = response.parse()
+ assert_matches_type(UploadListResponse, upload, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_abort(self, client: Mixedbread) -> None:
+ upload = client.files.uploads.abort(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(UploadAbortResponse, upload, path=["response"])
+
+ @parametrize
+ def test_raw_response_abort(self, client: Mixedbread) -> None:
+ response = client.files.uploads.with_raw_response.abort(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ upload = response.parse()
+ assert_matches_type(UploadAbortResponse, upload, path=["response"])
+
+ @parametrize
+ def test_streaming_response_abort(self, client: Mixedbread) -> None:
+ with client.files.uploads.with_streaming_response.abort(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ upload = response.parse()
+ assert_matches_type(UploadAbortResponse, upload, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_abort(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"):
+ client.files.uploads.with_raw_response.abort(
+ "",
+ )
+
+ @parametrize
+ def test_method_complete(self, client: Mixedbread) -> None:
+ upload = client.files.uploads.complete(
+ upload_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ parts=[
+ {
+ "part_number": 1,
+ "etag": "etag",
+ }
+ ],
+ )
+ assert_matches_type(FileObject, upload, path=["response"])
+
+ @parametrize
+ def test_raw_response_complete(self, client: Mixedbread) -> None:
+ response = client.files.uploads.with_raw_response.complete(
+ upload_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ parts=[
+ {
+ "part_number": 1,
+ "etag": "etag",
+ }
+ ],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ upload = response.parse()
+ assert_matches_type(FileObject, upload, path=["response"])
+
+ @parametrize
+ def test_streaming_response_complete(self, client: Mixedbread) -> None:
+ with client.files.uploads.with_streaming_response.complete(
+ upload_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ parts=[
+ {
+ "part_number": 1,
+ "etag": "etag",
+ }
+ ],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ upload = response.parse()
+ assert_matches_type(FileObject, upload, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_complete(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"):
+ client.files.uploads.with_raw_response.complete(
+ upload_id="",
+ parts=[
+ {
+ "part_number": 1,
+ "etag": "etag",
+ }
+ ],
+ )
+
+
+class TestAsyncUploads:
+ 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:
+ upload = await async_client.files.uploads.create(
+ filename="document.pdf",
+ file_size=10485760,
+ mime_type="application/pdf",
+ )
+ assert_matches_type(UploadCreateResponse, upload, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ upload = await async_client.files.uploads.create(
+ filename="document.pdf",
+ file_size=10485760,
+ mime_type="application/pdf",
+ part_count=3,
+ )
+ assert_matches_type(UploadCreateResponse, upload, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.files.uploads.with_raw_response.create(
+ filename="document.pdf",
+ file_size=10485760,
+ mime_type="application/pdf",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ upload = await response.parse()
+ assert_matches_type(UploadCreateResponse, upload, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.files.uploads.with_streaming_response.create(
+ filename="document.pdf",
+ file_size=10485760,
+ mime_type="application/pdf",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ upload = await response.parse()
+ assert_matches_type(UploadCreateResponse, upload, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncMixedbread) -> None:
+ upload = await async_client.files.uploads.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(UploadRetrieveResponse, upload, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.files.uploads.with_raw_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ upload = await response.parse()
+ assert_matches_type(UploadRetrieveResponse, upload, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.files.uploads.with_streaming_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ upload = await response.parse()
+ assert_matches_type(UploadRetrieveResponse, upload, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"):
+ await async_client.files.uploads.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncMixedbread) -> None:
+ upload = await async_client.files.uploads.list()
+ assert_matches_type(UploadListResponse, upload, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.files.uploads.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ upload = await response.parse()
+ assert_matches_type(UploadListResponse, upload, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.files.uploads.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ upload = await response.parse()
+ assert_matches_type(UploadListResponse, upload, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_abort(self, async_client: AsyncMixedbread) -> None:
+ upload = await async_client.files.uploads.abort(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(UploadAbortResponse, upload, path=["response"])
+
+ @parametrize
+ async def test_raw_response_abort(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.files.uploads.with_raw_response.abort(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ upload = await response.parse()
+ assert_matches_type(UploadAbortResponse, upload, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_abort(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.files.uploads.with_streaming_response.abort(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ upload = await response.parse()
+ assert_matches_type(UploadAbortResponse, upload, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_abort(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"):
+ await async_client.files.uploads.with_raw_response.abort(
+ "",
+ )
+
+ @parametrize
+ async def test_method_complete(self, async_client: AsyncMixedbread) -> None:
+ upload = await async_client.files.uploads.complete(
+ upload_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ parts=[
+ {
+ "part_number": 1,
+ "etag": "etag",
+ }
+ ],
+ )
+ assert_matches_type(FileObject, upload, path=["response"])
+
+ @parametrize
+ async def test_raw_response_complete(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.files.uploads.with_raw_response.complete(
+ upload_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ parts=[
+ {
+ "part_number": 1,
+ "etag": "etag",
+ }
+ ],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ upload = await response.parse()
+ assert_matches_type(FileObject, upload, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_complete(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.files.uploads.with_streaming_response.complete(
+ upload_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ parts=[
+ {
+ "part_number": 1,
+ "etag": "etag",
+ }
+ ],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ upload = await response.parse()
+ assert_matches_type(FileObject, upload, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_complete(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"):
+ await async_client.files.uploads.with_raw_response.complete(
+ upload_id="",
+ parts=[
+ {
+ "part_number": 1,
+ "etag": "etag",
+ }
+ ],
+ )
diff --git a/tests/api_resources/parsing/test_jobs.py b/tests/api_resources/parsing/test_jobs.py
index 1ec0df5b..50842fb3 100644
--- a/tests/api_resources/parsing/test_jobs.py
+++ b/tests/api_resources/parsing/test_jobs.py
@@ -9,7 +9,12 @@
from mixedbread import Mixedbread, AsyncMixedbread
from tests.utils import assert_matches_type
-from mixedbread.types.parsing import ParsingJob
+from mixedbread.pagination import SyncCursor, AsyncCursor
+from mixedbread.types.parsing import (
+ ParsingJob,
+ JobListResponse,
+ JobDeleteResponse,
+)
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -28,9 +33,10 @@ def test_method_create(self, client: Mixedbread) -> None:
def test_method_create_with_all_params(self, client: Mixedbread) -> None:
job = client.parsing.jobs.create(
file_id="file_id",
+ element_types=["header"],
chunking_strategy="page",
- element_types=["caption"],
return_format="html",
+ mode="fast",
)
assert_matches_type(ParsingJob, job, path=["response"])
@@ -61,14 +67,14 @@ def test_streaming_response_create(self, client: Mixedbread) -> None:
@parametrize
def test_method_retrieve(self, client: Mixedbread) -> None:
job = client.parsing.jobs.retrieve(
- "job_id",
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
)
assert_matches_type(ParsingJob, job, path=["response"])
@parametrize
def test_raw_response_retrieve(self, client: Mixedbread) -> None:
response = client.parsing.jobs.with_raw_response.retrieve(
- "job_id",
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
)
assert response.is_closed is True
@@ -79,7 +85,7 @@ def test_raw_response_retrieve(self, client: Mixedbread) -> None:
@parametrize
def test_streaming_response_retrieve(self, client: Mixedbread) -> None:
with client.parsing.jobs.with_streaming_response.retrieve(
- "job_id",
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -96,9 +102,124 @@ def test_path_params_retrieve(self, client: Mixedbread) -> None:
"",
)
+ @parametrize
+ def test_method_list(self, client: Mixedbread) -> None:
+ job = client.parsing.jobs.list()
+ assert_matches_type(SyncCursor[JobListResponse], job, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: Mixedbread) -> None:
+ job = client.parsing.jobs.list(
+ limit=10,
+ after="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ before="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ include_total=False,
+ statuses=["pending", "in_progress"],
+ q="x",
+ )
+ assert_matches_type(SyncCursor[JobListResponse], job, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: Mixedbread) -> None:
+ response = client.parsing.jobs.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ job = response.parse()
+ assert_matches_type(SyncCursor[JobListResponse], job, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: Mixedbread) -> None:
+ with client.parsing.jobs.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ job = response.parse()
+ assert_matches_type(SyncCursor[JobListResponse], job, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_delete(self, client: Mixedbread) -> None:
+ job = client.parsing.jobs.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(JobDeleteResponse, job, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: Mixedbread) -> None:
+ response = client.parsing.jobs.with_raw_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ job = response.parse()
+ assert_matches_type(JobDeleteResponse, job, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: Mixedbread) -> None:
+ with client.parsing.jobs.with_streaming_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ job = response.parse()
+ assert_matches_type(JobDeleteResponse, job, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `job_id` but received ''"):
+ client.parsing.jobs.with_raw_response.delete(
+ "",
+ )
+
+ @parametrize
+ def test_method_cancel(self, client: Mixedbread) -> None:
+ job = client.parsing.jobs.cancel(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(ParsingJob, job, path=["response"])
+
+ @parametrize
+ def test_raw_response_cancel(self, client: Mixedbread) -> None:
+ response = client.parsing.jobs.with_raw_response.cancel(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ job = response.parse()
+ assert_matches_type(ParsingJob, job, path=["response"])
+
+ @parametrize
+ def test_streaming_response_cancel(self, client: Mixedbread) -> None:
+ with client.parsing.jobs.with_streaming_response.cancel(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ job = response.parse()
+ assert_matches_type(ParsingJob, job, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_cancel(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `job_id` but received ''"):
+ client.parsing.jobs.with_raw_response.cancel(
+ "",
+ )
+
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:
@@ -111,9 +232,10 @@ async def test_method_create(self, async_client: AsyncMixedbread) -> None:
async def test_method_create_with_all_params(self, async_client: AsyncMixedbread) -> None:
job = await async_client.parsing.jobs.create(
file_id="file_id",
+ element_types=["header"],
chunking_strategy="page",
- element_types=["caption"],
return_format="html",
+ mode="fast",
)
assert_matches_type(ParsingJob, job, path=["response"])
@@ -144,14 +266,14 @@ async def test_streaming_response_create(self, async_client: AsyncMixedbread) ->
@parametrize
async def test_method_retrieve(self, async_client: AsyncMixedbread) -> None:
job = await async_client.parsing.jobs.retrieve(
- "job_id",
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
)
assert_matches_type(ParsingJob, job, path=["response"])
@parametrize
async def test_raw_response_retrieve(self, async_client: AsyncMixedbread) -> None:
response = await async_client.parsing.jobs.with_raw_response.retrieve(
- "job_id",
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
)
assert response.is_closed is True
@@ -162,7 +284,7 @@ async def test_raw_response_retrieve(self, async_client: AsyncMixedbread) -> Non
@parametrize
async def test_streaming_response_retrieve(self, async_client: AsyncMixedbread) -> None:
async with async_client.parsing.jobs.with_streaming_response.retrieve(
- "job_id",
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -178,3 +300,116 @@ async def test_path_params_retrieve(self, async_client: AsyncMixedbread) -> None
await async_client.parsing.jobs.with_raw_response.retrieve(
"",
)
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncMixedbread) -> None:
+ job = await async_client.parsing.jobs.list()
+ assert_matches_type(AsyncCursor[JobListResponse], job, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ job = await async_client.parsing.jobs.list(
+ limit=10,
+ after="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ before="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ include_total=False,
+ statuses=["pending", "in_progress"],
+ q="x",
+ )
+ assert_matches_type(AsyncCursor[JobListResponse], job, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.parsing.jobs.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ job = await response.parse()
+ assert_matches_type(AsyncCursor[JobListResponse], job, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.parsing.jobs.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ job = await response.parse()
+ assert_matches_type(AsyncCursor[JobListResponse], job, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncMixedbread) -> None:
+ job = await async_client.parsing.jobs.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(JobDeleteResponse, job, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.parsing.jobs.with_raw_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ job = await response.parse()
+ assert_matches_type(JobDeleteResponse, job, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.parsing.jobs.with_streaming_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ job = await response.parse()
+ assert_matches_type(JobDeleteResponse, job, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `job_id` but received ''"):
+ await async_client.parsing.jobs.with_raw_response.delete(
+ "",
+ )
+
+ @parametrize
+ async def test_method_cancel(self, async_client: AsyncMixedbread) -> None:
+ job = await async_client.parsing.jobs.cancel(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(ParsingJob, job, path=["response"])
+
+ @parametrize
+ async def test_raw_response_cancel(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.parsing.jobs.with_raw_response.cancel(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ job = await response.parse()
+ assert_matches_type(ParsingJob, job, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_cancel(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.parsing.jobs.with_streaming_response.cancel(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ job = await response.parse()
+ assert_matches_type(ParsingJob, job, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_cancel(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `job_id` but received ''"):
+ await async_client.parsing.jobs.with_raw_response.cancel(
+ "",
+ )
diff --git a/tests/api_resources/stores/__init__.py b/tests/api_resources/stores/__init__.py
new file mode 100644
index 00000000..fd8019a9
--- /dev/null
+++ b/tests/api_resources/stores/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/stores/test_files.py b/tests/api_resources/stores/test_files.py
new file mode 100644
index 00000000..f59802bf
--- /dev/null
+++ b/tests/api_resources/stores/test_files.py
@@ -0,0 +1,693 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from mixedbread import Mixedbread, AsyncMixedbread
+from tests.utils import assert_matches_type
+from mixedbread.types.stores import (
+ StoreFile,
+ FileListResponse,
+ FileDeleteResponse,
+ FileSearchResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestFiles:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: Mixedbread) -> None:
+ file = client.stores.files.create(
+ store_identifier="store_identifier",
+ file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(StoreFile, file, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: Mixedbread) -> None:
+ file = client.stores.files.create(
+ store_identifier="store_identifier",
+ metadata={},
+ config={"parsing_strategy": "fast"},
+ external_id="external_id",
+ overwrite=False,
+ file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ experimental={"parsing_strategy": "fast"},
+ )
+ assert_matches_type(StoreFile, file, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: Mixedbread) -> None:
+ response = client.stores.files.with_raw_response.create(
+ store_identifier="store_identifier",
+ file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ file = response.parse()
+ assert_matches_type(StoreFile, file, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: Mixedbread) -> None:
+ with client.stores.files.with_streaming_response.create(
+ store_identifier="store_identifier",
+ file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ file = response.parse()
+ assert_matches_type(StoreFile, file, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_create(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `store_identifier` but received ''"):
+ client.stores.files.with_raw_response.create(
+ store_identifier="",
+ file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ @parametrize
+ def test_method_retrieve(self, client: Mixedbread) -> None:
+ file = client.stores.files.retrieve(
+ file_identifier="file_identifier",
+ store_identifier="store_identifier",
+ )
+ assert_matches_type(StoreFile, file, path=["response"])
+
+ @parametrize
+ def test_method_retrieve_with_all_params(self, client: Mixedbread) -> None:
+ file = client.stores.files.retrieve(
+ file_identifier="file_identifier",
+ store_identifier="store_identifier",
+ return_chunks=True,
+ )
+ assert_matches_type(StoreFile, file, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Mixedbread) -> None:
+ response = client.stores.files.with_raw_response.retrieve(
+ file_identifier="file_identifier",
+ store_identifier="store_identifier",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ file = response.parse()
+ assert_matches_type(StoreFile, file, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Mixedbread) -> None:
+ with client.stores.files.with_streaming_response.retrieve(
+ file_identifier="file_identifier",
+ store_identifier="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(StoreFile, file, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `store_identifier` but received ''"):
+ client.stores.files.with_raw_response.retrieve(
+ file_identifier="file_identifier",
+ store_identifier="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_identifier` but received ''"):
+ client.stores.files.with_raw_response.retrieve(
+ file_identifier="",
+ store_identifier="store_identifier",
+ )
+
+ @parametrize
+ def test_method_update(self, client: Mixedbread) -> None:
+ file = client.stores.files.update(
+ file_identifier="file_identifier",
+ store_identifier="store_identifier",
+ )
+ assert_matches_type(StoreFile, file, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: Mixedbread) -> None:
+ file = client.stores.files.update(
+ file_identifier="file_identifier",
+ store_identifier="store_identifier",
+ metadata={"foo": "bar"},
+ )
+ assert_matches_type(StoreFile, file, path=["response"])
+
+ @parametrize
+ def test_raw_response_update(self, client: Mixedbread) -> None:
+ response = client.stores.files.with_raw_response.update(
+ file_identifier="file_identifier",
+ store_identifier="store_identifier",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ file = response.parse()
+ assert_matches_type(StoreFile, file, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update(self, client: Mixedbread) -> None:
+ with client.stores.files.with_streaming_response.update(
+ file_identifier="file_identifier",
+ store_identifier="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(StoreFile, file, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `store_identifier` but received ''"):
+ client.stores.files.with_raw_response.update(
+ file_identifier="file_identifier",
+ store_identifier="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_identifier` but received ''"):
+ client.stores.files.with_raw_response.update(
+ file_identifier="",
+ store_identifier="store_identifier",
+ )
+
+ @parametrize
+ def test_method_list(self, client: Mixedbread) -> None:
+ file = client.stores.files.list(
+ store_identifier="store_identifier",
+ )
+ assert_matches_type(FileListResponse, file, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: Mixedbread) -> None:
+ file = client.stores.files.list(
+ store_identifier="store_identifier",
+ limit=10,
+ after="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ before="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ include_total=False,
+ statuses=["pending"],
+ metadata_filter={
+ "all": [{}, {}],
+ "any": [{}, {}],
+ "none": [{}, {}],
+ },
+ q="x",
+ )
+ assert_matches_type(FileListResponse, file, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: Mixedbread) -> None:
+ response = client.stores.files.with_raw_response.list(
+ store_identifier="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.stores.files.with_streaming_response.list(
+ store_identifier="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 `store_identifier` but received ''"):
+ client.stores.files.with_raw_response.list(
+ store_identifier="",
+ )
+
+ @parametrize
+ def test_method_delete(self, client: Mixedbread) -> None:
+ file = client.stores.files.delete(
+ file_identifier="file_identifier",
+ store_identifier="store_identifier",
+ )
+ assert_matches_type(FileDeleteResponse, file, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: Mixedbread) -> None:
+ response = client.stores.files.with_raw_response.delete(
+ file_identifier="file_identifier",
+ store_identifier="store_identifier",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ file = response.parse()
+ assert_matches_type(FileDeleteResponse, file, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: Mixedbread) -> None:
+ with client.stores.files.with_streaming_response.delete(
+ file_identifier="file_identifier",
+ store_identifier="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(FileDeleteResponse, file, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `store_identifier` but received ''"):
+ client.stores.files.with_raw_response.delete(
+ file_identifier="file_identifier",
+ store_identifier="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_identifier` but received ''"):
+ client.stores.files.with_raw_response.delete(
+ file_identifier="",
+ store_identifier="store_identifier",
+ )
+
+ @parametrize
+ def test_method_search(self, client: Mixedbread) -> None:
+ file = client.stores.files.search(
+ query="how to configure SSL",
+ store_identifiers=["string"],
+ )
+ assert_matches_type(FileSearchResponse, file, path=["response"])
+
+ @parametrize
+ def test_method_search_with_all_params(self, client: Mixedbread) -> None:
+ file = client.stores.files.search(
+ query="how to configure SSL",
+ store_identifiers=["string"],
+ top_k=1,
+ filters={
+ "all": [{}, {}],
+ "any": [{}, {}],
+ "none": [{}, {}],
+ },
+ file_ids=["123e4567-e89b-12d3-a456-426614174000", "123e4567-e89b-12d3-a456-426614174001"],
+ search_options={
+ "score_threshold": 0,
+ "rewrite_query": True,
+ "rerank": True,
+ "agentic": True,
+ "return_metadata": True,
+ "return_chunks": True,
+ "chunks_per_file": 0,
+ "apply_search_rules": True,
+ },
+ )
+ assert_matches_type(FileSearchResponse, file, path=["response"])
+
+ @parametrize
+ def test_raw_response_search(self, client: Mixedbread) -> None:
+ response = client.stores.files.with_raw_response.search(
+ query="how to configure SSL",
+ store_identifiers=["string"],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ file = response.parse()
+ assert_matches_type(FileSearchResponse, file, path=["response"])
+
+ @parametrize
+ def test_streaming_response_search(self, client: Mixedbread) -> None:
+ with client.stores.files.with_streaming_response.search(
+ query="how to configure SSL",
+ store_identifiers=["string"],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ file = response.parse()
+ assert_matches_type(FileSearchResponse, file, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncFiles:
+ 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:
+ file = await async_client.stores.files.create(
+ store_identifier="store_identifier",
+ file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(StoreFile, file, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ file = await async_client.stores.files.create(
+ store_identifier="store_identifier",
+ metadata={},
+ config={"parsing_strategy": "fast"},
+ external_id="external_id",
+ overwrite=False,
+ file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ experimental={"parsing_strategy": "fast"},
+ )
+ assert_matches_type(StoreFile, file, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.stores.files.with_raw_response.create(
+ store_identifier="store_identifier",
+ file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ file = await response.parse()
+ assert_matches_type(StoreFile, file, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.stores.files.with_streaming_response.create(
+ store_identifier="store_identifier",
+ file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ file = await response.parse()
+ assert_matches_type(StoreFile, file, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `store_identifier` but received ''"):
+ await async_client.stores.files.with_raw_response.create(
+ store_identifier="",
+ file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncMixedbread) -> None:
+ file = await async_client.stores.files.retrieve(
+ file_identifier="file_identifier",
+ store_identifier="store_identifier",
+ )
+ assert_matches_type(StoreFile, file, path=["response"])
+
+ @parametrize
+ async def test_method_retrieve_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ file = await async_client.stores.files.retrieve(
+ file_identifier="file_identifier",
+ store_identifier="store_identifier",
+ return_chunks=True,
+ )
+ assert_matches_type(StoreFile, file, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.stores.files.with_raw_response.retrieve(
+ file_identifier="file_identifier",
+ store_identifier="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(StoreFile, file, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.stores.files.with_streaming_response.retrieve(
+ file_identifier="file_identifier",
+ store_identifier="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(StoreFile, file, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `store_identifier` but received ''"):
+ await async_client.stores.files.with_raw_response.retrieve(
+ file_identifier="file_identifier",
+ store_identifier="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_identifier` but received ''"):
+ await async_client.stores.files.with_raw_response.retrieve(
+ file_identifier="",
+ store_identifier="store_identifier",
+ )
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncMixedbread) -> None:
+ file = await async_client.stores.files.update(
+ file_identifier="file_identifier",
+ store_identifier="store_identifier",
+ )
+ assert_matches_type(StoreFile, file, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ file = await async_client.stores.files.update(
+ file_identifier="file_identifier",
+ store_identifier="store_identifier",
+ metadata={"foo": "bar"},
+ )
+ assert_matches_type(StoreFile, file, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.stores.files.with_raw_response.update(
+ file_identifier="file_identifier",
+ store_identifier="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(StoreFile, file, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.stores.files.with_streaming_response.update(
+ file_identifier="file_identifier",
+ store_identifier="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(StoreFile, file, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `store_identifier` but received ''"):
+ await async_client.stores.files.with_raw_response.update(
+ file_identifier="file_identifier",
+ store_identifier="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_identifier` but received ''"):
+ await async_client.stores.files.with_raw_response.update(
+ file_identifier="",
+ store_identifier="store_identifier",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncMixedbread) -> None:
+ file = await async_client.stores.files.list(
+ store_identifier="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.stores.files.list(
+ store_identifier="store_identifier",
+ limit=10,
+ after="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ before="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ include_total=False,
+ statuses=["pending"],
+ metadata_filter={
+ "all": [{}, {}],
+ "any": [{}, {}],
+ "none": [{}, {}],
+ },
+ q="x",
+ )
+ assert_matches_type(FileListResponse, file, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.stores.files.with_raw_response.list(
+ store_identifier="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.stores.files.with_streaming_response.list(
+ store_identifier="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 `store_identifier` but received ''"):
+ await async_client.stores.files.with_raw_response.list(
+ store_identifier="",
+ )
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncMixedbread) -> None:
+ file = await async_client.stores.files.delete(
+ file_identifier="file_identifier",
+ store_identifier="store_identifier",
+ )
+ assert_matches_type(FileDeleteResponse, file, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.stores.files.with_raw_response.delete(
+ file_identifier="file_identifier",
+ store_identifier="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(FileDeleteResponse, file, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.stores.files.with_streaming_response.delete(
+ file_identifier="file_identifier",
+ store_identifier="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(FileDeleteResponse, file, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `store_identifier` but received ''"):
+ await async_client.stores.files.with_raw_response.delete(
+ file_identifier="file_identifier",
+ store_identifier="",
+ )
+
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_identifier` but received ''"):
+ await async_client.stores.files.with_raw_response.delete(
+ file_identifier="",
+ store_identifier="store_identifier",
+ )
+
+ @parametrize
+ async def test_method_search(self, async_client: AsyncMixedbread) -> None:
+ file = await async_client.stores.files.search(
+ query="how to configure SSL",
+ store_identifiers=["string"],
+ )
+ assert_matches_type(FileSearchResponse, file, path=["response"])
+
+ @parametrize
+ async def test_method_search_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ file = await async_client.stores.files.search(
+ query="how to configure SSL",
+ store_identifiers=["string"],
+ top_k=1,
+ filters={
+ "all": [{}, {}],
+ "any": [{}, {}],
+ "none": [{}, {}],
+ },
+ file_ids=["123e4567-e89b-12d3-a456-426614174000", "123e4567-e89b-12d3-a456-426614174001"],
+ search_options={
+ "score_threshold": 0,
+ "rewrite_query": True,
+ "rerank": True,
+ "agentic": True,
+ "return_metadata": True,
+ "return_chunks": True,
+ "chunks_per_file": 0,
+ "apply_search_rules": True,
+ },
+ )
+ assert_matches_type(FileSearchResponse, file, path=["response"])
+
+ @parametrize
+ async def test_raw_response_search(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.stores.files.with_raw_response.search(
+ query="how to configure SSL",
+ store_identifiers=["string"],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ file = await response.parse()
+ assert_matches_type(FileSearchResponse, file, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_search(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.stores.files.with_streaming_response.search(
+ query="how to configure SSL",
+ store_identifiers=["string"],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ file = await response.parse()
+ assert_matches_type(FileSearchResponse, file, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/test_api_keys.py b/tests/api_resources/test_api_keys.py
new file mode 100644
index 00000000..aa8310ee
--- /dev/null
+++ b/tests/api_resources/test_api_keys.py
@@ -0,0 +1,476 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from mixedbread import Mixedbread, AsyncMixedbread
+from tests.utils import assert_matches_type
+from mixedbread.types import APIKey, APIKeyCreated, APIKeyDeleteResponse
+from mixedbread._utils import parse_datetime
+from mixedbread.pagination import SyncLimitOffset, AsyncLimitOffset
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestAPIKeys:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: Mixedbread) -> None:
+ api_key = client.api_keys.create()
+ assert_matches_type(APIKeyCreated, api_key, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: Mixedbread) -> None:
+ api_key = client.api_keys.create(
+ name="name",
+ scope=[
+ {
+ "method": "read",
+ "resource_type": "store",
+ "resource_id": "resource_id",
+ }
+ ],
+ expires_at=parse_datetime("2019-12-27T18:11:19.117Z"),
+ )
+ assert_matches_type(APIKeyCreated, api_key, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: Mixedbread) -> None:
+ response = client.api_keys.with_raw_response.create()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = response.parse()
+ assert_matches_type(APIKeyCreated, api_key, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: Mixedbread) -> None:
+ with client.api_keys.with_streaming_response.create() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = response.parse()
+ assert_matches_type(APIKeyCreated, api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_retrieve(self, client: Mixedbread) -> None:
+ api_key = client.api_keys.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Mixedbread) -> None:
+ response = client.api_keys.with_raw_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = response.parse()
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Mixedbread) -> None:
+ with client.api_keys.with_streaming_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = response.parse()
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_id` but received ''"):
+ client.api_keys.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ def test_method_list(self, client: Mixedbread) -> None:
+ api_key = client.api_keys.list()
+ assert_matches_type(SyncLimitOffset[APIKey], api_key, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: Mixedbread) -> None:
+ api_key = client.api_keys.list(
+ limit=1000,
+ offset=0,
+ )
+ assert_matches_type(SyncLimitOffset[APIKey], api_key, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: Mixedbread) -> None:
+ response = client.api_keys.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = response.parse()
+ assert_matches_type(SyncLimitOffset[APIKey], api_key, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: Mixedbread) -> None:
+ with client.api_keys.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = response.parse()
+ assert_matches_type(SyncLimitOffset[APIKey], api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_delete(self, client: Mixedbread) -> None:
+ api_key = client.api_keys.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: Mixedbread) -> None:
+ response = client.api_keys.with_raw_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = response.parse()
+ assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: Mixedbread) -> None:
+ with client.api_keys.with_streaming_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = response.parse()
+ assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_id` but received ''"):
+ client.api_keys.with_raw_response.delete(
+ "",
+ )
+
+ @parametrize
+ def test_method_reroll(self, client: Mixedbread) -> None:
+ api_key = client.api_keys.reroll(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(APIKeyCreated, api_key, path=["response"])
+
+ @parametrize
+ def test_raw_response_reroll(self, client: Mixedbread) -> None:
+ response = client.api_keys.with_raw_response.reroll(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = response.parse()
+ assert_matches_type(APIKeyCreated, api_key, path=["response"])
+
+ @parametrize
+ def test_streaming_response_reroll(self, client: Mixedbread) -> None:
+ with client.api_keys.with_streaming_response.reroll(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = response.parse()
+ assert_matches_type(APIKeyCreated, api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_reroll(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_id` but received ''"):
+ client.api_keys.with_raw_response.reroll(
+ "",
+ )
+
+ @parametrize
+ def test_method_revoke(self, client: Mixedbread) -> None:
+ api_key = client.api_keys.revoke(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ @parametrize
+ def test_raw_response_revoke(self, client: Mixedbread) -> None:
+ response = client.api_keys.with_raw_response.revoke(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = response.parse()
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ @parametrize
+ def test_streaming_response_revoke(self, client: Mixedbread) -> None:
+ with client.api_keys.with_streaming_response.revoke(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = response.parse()
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_revoke(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_id` but received ''"):
+ client.api_keys.with_raw_response.revoke(
+ "",
+ )
+
+
+class TestAsyncAPIKeys:
+ 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:
+ api_key = await async_client.api_keys.create()
+ assert_matches_type(APIKeyCreated, api_key, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ api_key = await async_client.api_keys.create(
+ name="name",
+ scope=[
+ {
+ "method": "read",
+ "resource_type": "store",
+ "resource_id": "resource_id",
+ }
+ ],
+ expires_at=parse_datetime("2019-12-27T18:11:19.117Z"),
+ )
+ assert_matches_type(APIKeyCreated, api_key, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.api_keys.with_raw_response.create()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = await response.parse()
+ assert_matches_type(APIKeyCreated, api_key, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.api_keys.with_streaming_response.create() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = await response.parse()
+ assert_matches_type(APIKeyCreated, api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncMixedbread) -> None:
+ api_key = await async_client.api_keys.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.api_keys.with_raw_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = await response.parse()
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.api_keys.with_streaming_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = await response.parse()
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_id` but received ''"):
+ await async_client.api_keys.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncMixedbread) -> None:
+ api_key = await async_client.api_keys.list()
+ assert_matches_type(AsyncLimitOffset[APIKey], api_key, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ api_key = await async_client.api_keys.list(
+ limit=1000,
+ offset=0,
+ )
+ assert_matches_type(AsyncLimitOffset[APIKey], api_key, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.api_keys.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = await response.parse()
+ assert_matches_type(AsyncLimitOffset[APIKey], api_key, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.api_keys.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = await response.parse()
+ assert_matches_type(AsyncLimitOffset[APIKey], api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncMixedbread) -> None:
+ api_key = await async_client.api_keys.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.api_keys.with_raw_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = await response.parse()
+ assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.api_keys.with_streaming_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = await response.parse()
+ assert_matches_type(APIKeyDeleteResponse, api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_id` but received ''"):
+ await async_client.api_keys.with_raw_response.delete(
+ "",
+ )
+
+ @parametrize
+ async def test_method_reroll(self, async_client: AsyncMixedbread) -> None:
+ api_key = await async_client.api_keys.reroll(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(APIKeyCreated, api_key, path=["response"])
+
+ @parametrize
+ async def test_raw_response_reroll(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.api_keys.with_raw_response.reroll(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = await response.parse()
+ assert_matches_type(APIKeyCreated, api_key, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_reroll(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.api_keys.with_streaming_response.reroll(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = await response.parse()
+ assert_matches_type(APIKeyCreated, api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_reroll(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_id` but received ''"):
+ await async_client.api_keys.with_raw_response.reroll(
+ "",
+ )
+
+ @parametrize
+ async def test_method_revoke(self, async_client: AsyncMixedbread) -> None:
+ api_key = await async_client.api_keys.revoke(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ @parametrize
+ async def test_raw_response_revoke(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.api_keys.with_raw_response.revoke(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ api_key = await response.parse()
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_revoke(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.api_keys.with_streaming_response.revoke(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ api_key = await response.parse()
+ assert_matches_type(APIKey, api_key, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_revoke(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `api_key_id` but received ''"):
+ await async_client.api_keys.with_raw_response.revoke(
+ "",
+ )
diff --git a/tests/api_resources/test_chat.py b/tests/api_resources/test_chat.py
new file mode 100644
index 00000000..a60009fc
--- /dev/null
+++ b/tests/api_resources/test_chat.py
@@ -0,0 +1,73 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from mixedbread import Mixedbread, AsyncMixedbread
+from tests.utils import assert_matches_type
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestChat:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create_completion(self, client: Mixedbread) -> None:
+ chat = client.chat.create_completion()
+ assert_matches_type(object, chat, path=["response"])
+
+ @parametrize
+ def test_raw_response_create_completion(self, client: Mixedbread) -> None:
+ response = client.chat.with_raw_response.create_completion()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ chat = response.parse()
+ assert_matches_type(object, chat, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create_completion(self, client: Mixedbread) -> None:
+ with client.chat.with_streaming_response.create_completion() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ chat = response.parse()
+ assert_matches_type(object, chat, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncChat:
+ 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:
+ chat = await async_client.chat.create_completion()
+ assert_matches_type(object, chat, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create_completion(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.chat.with_raw_response.create_completion()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ chat = await response.parse()
+ assert_matches_type(object, chat, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create_completion(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.chat.with_streaming_response.create_completion() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ chat = await response.parse()
+ assert_matches_type(object, chat, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/test_client.py b/tests/api_resources/test_client.py
new file mode 100644
index 00000000..7a05f7de
--- /dev/null
+++ b/tests/api_resources/test_client.py
@@ -0,0 +1,264 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from mixedbread import Mixedbread, AsyncMixedbread
+from tests.utils import assert_matches_type
+from mixedbread.types import (
+ InfoResponse,
+ RerankResponse,
+ EmbeddingCreateResponse,
+)
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestClient:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_embed(self, client: Mixedbread) -> None:
+ client_ = client.embed(
+ model="mixedbread-ai/mxbai-embed-large-v1",
+ input="x",
+ )
+ assert_matches_type(EmbeddingCreateResponse, client_, path=["response"])
+
+ @parametrize
+ def test_method_embed_with_all_params(self, client: Mixedbread) -> None:
+ client_ = client.embed(
+ model="mixedbread-ai/mxbai-embed-large-v1",
+ input="x",
+ dimensions=768,
+ prompt="Provide a detailed summary of the following text.",
+ normalized=True,
+ encoding_format="float",
+ )
+ assert_matches_type(EmbeddingCreateResponse, client_, path=["response"])
+
+ @parametrize
+ def test_raw_response_embed(self, client: Mixedbread) -> None:
+ response = client.with_raw_response.embed(
+ model="mixedbread-ai/mxbai-embed-large-v1",
+ input="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ client_ = response.parse()
+ assert_matches_type(EmbeddingCreateResponse, client_, path=["response"])
+
+ @parametrize
+ def test_streaming_response_embed(self, client: Mixedbread) -> None:
+ with client.with_streaming_response.embed(
+ model="mixedbread-ai/mxbai-embed-large-v1",
+ input="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ client_ = response.parse()
+ assert_matches_type(EmbeddingCreateResponse, client_, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_info(self, client: Mixedbread) -> None:
+ client_ = client.info()
+ assert_matches_type(InfoResponse, client_, path=["response"])
+
+ @parametrize
+ def test_raw_response_info(self, client: Mixedbread) -> None:
+ response = client.with_raw_response.info()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ client_ = response.parse()
+ assert_matches_type(InfoResponse, client_, path=["response"])
+
+ @parametrize
+ def test_streaming_response_info(self, client: Mixedbread) -> None:
+ with client.with_streaming_response.info() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ client_ = response.parse()
+ assert_matches_type(InfoResponse, client_, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_rerank(self, client: Mixedbread) -> None:
+ client_ = client.rerank(
+ query="What are the key features of the Mixedbread embedding model?",
+ input=["Document 1", "Document 2"],
+ )
+ assert_matches_type(RerankResponse, client_, path=["response"])
+
+ @parametrize
+ def test_method_rerank_with_all_params(self, client: Mixedbread) -> None:
+ client_ = client.rerank(
+ model="mixedbread-ai/mxbai-rerank-large-v2",
+ query="What are the key features of the Mixedbread embedding model?",
+ input=["Document 1", "Document 2"],
+ rank_fields=["content", "title"],
+ top_k=10,
+ return_input=False,
+ rewrite_query=False,
+ )
+ assert_matches_type(RerankResponse, client_, path=["response"])
+
+ @parametrize
+ def test_raw_response_rerank(self, client: Mixedbread) -> None:
+ response = client.with_raw_response.rerank(
+ query="What are the key features of the Mixedbread embedding model?",
+ input=["Document 1", "Document 2"],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ client_ = response.parse()
+ assert_matches_type(RerankResponse, client_, path=["response"])
+
+ @parametrize
+ def test_streaming_response_rerank(self, client: Mixedbread) -> None:
+ with client.with_streaming_response.rerank(
+ query="What are the key features of the Mixedbread embedding model?",
+ input=["Document 1", "Document 2"],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ client_ = response.parse()
+ assert_matches_type(RerankResponse, client_, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncClient:
+ 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:
+ client = await async_client.embed(
+ model="mixedbread-ai/mxbai-embed-large-v1",
+ input="x",
+ )
+ assert_matches_type(EmbeddingCreateResponse, client, path=["response"])
+
+ @parametrize
+ async def test_method_embed_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ client = await async_client.embed(
+ model="mixedbread-ai/mxbai-embed-large-v1",
+ input="x",
+ dimensions=768,
+ prompt="Provide a detailed summary of the following text.",
+ normalized=True,
+ encoding_format="float",
+ )
+ assert_matches_type(EmbeddingCreateResponse, client, path=["response"])
+
+ @parametrize
+ async def test_raw_response_embed(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.with_raw_response.embed(
+ model="mixedbread-ai/mxbai-embed-large-v1",
+ input="x",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ client = await response.parse()
+ assert_matches_type(EmbeddingCreateResponse, client, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_embed(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.with_streaming_response.embed(
+ model="mixedbread-ai/mxbai-embed-large-v1",
+ input="x",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ client = await response.parse()
+ assert_matches_type(EmbeddingCreateResponse, client, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_info(self, async_client: AsyncMixedbread) -> None:
+ client = await async_client.info()
+ assert_matches_type(InfoResponse, client, path=["response"])
+
+ @parametrize
+ async def test_raw_response_info(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.with_raw_response.info()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ client = await response.parse()
+ assert_matches_type(InfoResponse, client, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_info(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.with_streaming_response.info() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ client = await response.parse()
+ assert_matches_type(InfoResponse, client, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_rerank(self, async_client: AsyncMixedbread) -> None:
+ client = await async_client.rerank(
+ query="What are the key features of the Mixedbread embedding model?",
+ input=["Document 1", "Document 2"],
+ )
+ assert_matches_type(RerankResponse, client, path=["response"])
+
+ @parametrize
+ async def test_method_rerank_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ client = await async_client.rerank(
+ model="mixedbread-ai/mxbai-rerank-large-v2",
+ query="What are the key features of the Mixedbread embedding model?",
+ input=["Document 1", "Document 2"],
+ rank_fields=["content", "title"],
+ top_k=10,
+ return_input=False,
+ rewrite_query=False,
+ )
+ assert_matches_type(RerankResponse, client, path=["response"])
+
+ @parametrize
+ async def test_raw_response_rerank(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.with_raw_response.rerank(
+ query="What are the key features of the Mixedbread embedding model?",
+ input=["Document 1", "Document 2"],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ client = await response.parse()
+ assert_matches_type(RerankResponse, client, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_rerank(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.with_streaming_response.rerank(
+ query="What are the key features of the Mixedbread embedding model?",
+ input=["Document 1", "Document 2"],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ client = await response.parse()
+ assert_matches_type(RerankResponse, client, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/test_completions.py b/tests/api_resources/test_completions.py
deleted file mode 100644
index af7af678..00000000
--- a/tests/api_resources/test_completions.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-import os
-from typing import Any, cast
-
-import pytest
-
-from mixedbread import Mixedbread, AsyncMixedbread
-from tests.utils import assert_matches_type
-
-base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
-
-
-class TestCompletions:
- parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
-
- @parametrize
- def test_method_create(self, client: Mixedbread) -> None:
- completion = client.completions.create()
- assert_matches_type(object, completion, path=["response"])
-
- @parametrize
- def test_raw_response_create(self, client: Mixedbread) -> None:
- response = client.completions.with_raw_response.create()
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- completion = response.parse()
- assert_matches_type(object, completion, path=["response"])
-
- @parametrize
- def test_streaming_response_create(self, client: Mixedbread) -> None:
- with client.completions.with_streaming_response.create() as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- completion = response.parse()
- assert_matches_type(object, completion, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
-
-class TestAsyncCompletions:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
-
- @parametrize
- async def test_method_create(self, async_client: AsyncMixedbread) -> None:
- completion = await async_client.completions.create()
- assert_matches_type(object, completion, path=["response"])
-
- @parametrize
- async def test_raw_response_create(self, async_client: AsyncMixedbread) -> None:
- response = await async_client.completions.with_raw_response.create()
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- completion = await response.parse()
- assert_matches_type(object, completion, path=["response"])
-
- @parametrize
- async def test_streaming_response_create(self, async_client: AsyncMixedbread) -> None:
- async with async_client.completions.with_streaming_response.create() as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- completion = await response.parse()
- assert_matches_type(object, completion, path=["response"])
-
- assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/test_data_sources.py b/tests/api_resources/test_data_sources.py
new file mode 100644
index 00000000..5cb9991e
--- /dev/null
+++ b/tests/api_resources/test_data_sources.py
@@ -0,0 +1,626 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from mixedbread import Mixedbread, AsyncMixedbread
+from tests.utils import assert_matches_type
+from mixedbread.types import (
+ DataSource,
+ DataSourceDeleteResponse,
+)
+from mixedbread.pagination import SyncCursor, AsyncCursor
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestDataSources:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create_overload_1(self, client: Mixedbread) -> None:
+ data_source = client.data_sources.create(
+ name="name",
+ )
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params_overload_1(self, client: Mixedbread) -> None:
+ data_source = client.data_sources.create(
+ type="notion",
+ name="name",
+ metadata={},
+ auth_params={"type": "oauth2"},
+ )
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ def test_raw_response_create_overload_1(self, client: Mixedbread) -> None:
+ response = client.data_sources.with_raw_response.create(
+ name="name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ data_source = response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create_overload_1(self, client: Mixedbread) -> None:
+ with client.data_sources.with_streaming_response.create(
+ name="name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ data_source = response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_create_overload_2(self, client: Mixedbread) -> None:
+ data_source = client.data_sources.create(
+ name="name",
+ )
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params_overload_2(self, client: Mixedbread) -> None:
+ data_source = client.data_sources.create(
+ type="linear",
+ name="name",
+ metadata={},
+ auth_params={"type": "oauth2"},
+ )
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ def test_raw_response_create_overload_2(self, client: Mixedbread) -> None:
+ response = client.data_sources.with_raw_response.create(
+ name="name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ data_source = response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create_overload_2(self, client: Mixedbread) -> None:
+ with client.data_sources.with_streaming_response.create(
+ name="name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ data_source = response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_retrieve(self, client: Mixedbread) -> None:
+ data_source = client.data_sources.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Mixedbread) -> None:
+ response = client.data_sources.with_raw_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ data_source = response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Mixedbread) -> None:
+ with client.data_sources.with_streaming_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ data_source = response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `data_source_id` but received ''"):
+ client.data_sources.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ def test_method_update_overload_1(self, client: Mixedbread) -> None:
+ data_source = client.data_sources.update(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="name",
+ )
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params_overload_1(self, client: Mixedbread) -> None:
+ data_source = client.data_sources.update(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ type="notion",
+ name="name",
+ metadata={},
+ auth_params={"type": "oauth2"},
+ )
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ def test_raw_response_update_overload_1(self, client: Mixedbread) -> None:
+ response = client.data_sources.with_raw_response.update(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ data_source = response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update_overload_1(self, client: Mixedbread) -> None:
+ with client.data_sources.with_streaming_response.update(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ data_source = response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update_overload_1(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `data_source_id` but received ''"):
+ client.data_sources.with_raw_response.update(
+ data_source_id="",
+ name="name",
+ )
+
+ @parametrize
+ def test_method_update_overload_2(self, client: Mixedbread) -> None:
+ data_source = client.data_sources.update(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="name",
+ )
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params_overload_2(self, client: Mixedbread) -> None:
+ data_source = client.data_sources.update(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ type="linear",
+ name="name",
+ metadata={},
+ auth_params={"type": "oauth2"},
+ )
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ def test_raw_response_update_overload_2(self, client: Mixedbread) -> None:
+ response = client.data_sources.with_raw_response.update(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ data_source = response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update_overload_2(self, client: Mixedbread) -> None:
+ with client.data_sources.with_streaming_response.update(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ data_source = response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update_overload_2(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `data_source_id` but received ''"):
+ client.data_sources.with_raw_response.update(
+ data_source_id="",
+ name="name",
+ )
+
+ @parametrize
+ def test_method_list(self, client: Mixedbread) -> None:
+ data_source = client.data_sources.list()
+ assert_matches_type(SyncCursor[DataSource], data_source, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: Mixedbread) -> None:
+ data_source = client.data_sources.list(
+ limit=10,
+ after="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ before="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ include_total=False,
+ )
+ assert_matches_type(SyncCursor[DataSource], data_source, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: Mixedbread) -> None:
+ response = client.data_sources.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ data_source = response.parse()
+ assert_matches_type(SyncCursor[DataSource], data_source, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: Mixedbread) -> None:
+ with client.data_sources.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ data_source = response.parse()
+ assert_matches_type(SyncCursor[DataSource], data_source, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_delete(self, client: Mixedbread) -> None:
+ data_source = client.data_sources.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(DataSourceDeleteResponse, data_source, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: Mixedbread) -> None:
+ response = client.data_sources.with_raw_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ data_source = response.parse()
+ assert_matches_type(DataSourceDeleteResponse, data_source, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: Mixedbread) -> None:
+ with client.data_sources.with_streaming_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ data_source = response.parse()
+ assert_matches_type(DataSourceDeleteResponse, data_source, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `data_source_id` but received ''"):
+ client.data_sources.with_raw_response.delete(
+ "",
+ )
+
+
+class TestAsyncDataSources:
+ 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:
+ data_source = await async_client.data_sources.create(
+ name="name",
+ )
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params_overload_1(self, async_client: AsyncMixedbread) -> None:
+ data_source = await async_client.data_sources.create(
+ type="notion",
+ name="name",
+ metadata={},
+ auth_params={"type": "oauth2"},
+ )
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create_overload_1(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.data_sources.with_raw_response.create(
+ name="name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ data_source = await response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create_overload_1(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.data_sources.with_streaming_response.create(
+ name="name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ data_source = await response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_create_overload_2(self, async_client: AsyncMixedbread) -> None:
+ data_source = await async_client.data_sources.create(
+ name="name",
+ )
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params_overload_2(self, async_client: AsyncMixedbread) -> None:
+ data_source = await async_client.data_sources.create(
+ type="linear",
+ name="name",
+ metadata={},
+ auth_params={"type": "oauth2"},
+ )
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create_overload_2(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.data_sources.with_raw_response.create(
+ name="name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ data_source = await response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create_overload_2(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.data_sources.with_streaming_response.create(
+ name="name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ data_source = await response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncMixedbread) -> None:
+ data_source = await async_client.data_sources.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.data_sources.with_raw_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ data_source = await response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.data_sources.with_streaming_response.retrieve(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ data_source = await response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `data_source_id` but received ''"):
+ await async_client.data_sources.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ async def test_method_update_overload_1(self, async_client: AsyncMixedbread) -> None:
+ data_source = await async_client.data_sources.update(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="name",
+ )
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params_overload_1(self, async_client: AsyncMixedbread) -> None:
+ data_source = await async_client.data_sources.update(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ type="notion",
+ name="name",
+ metadata={},
+ auth_params={"type": "oauth2"},
+ )
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update_overload_1(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.data_sources.with_raw_response.update(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ data_source = await response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update_overload_1(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.data_sources.with_streaming_response.update(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ data_source = await response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update_overload_1(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `data_source_id` but received ''"):
+ await async_client.data_sources.with_raw_response.update(
+ data_source_id="",
+ name="name",
+ )
+
+ @parametrize
+ async def test_method_update_overload_2(self, async_client: AsyncMixedbread) -> None:
+ data_source = await async_client.data_sources.update(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="name",
+ )
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params_overload_2(self, async_client: AsyncMixedbread) -> None:
+ data_source = await async_client.data_sources.update(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ type="linear",
+ name="name",
+ metadata={},
+ auth_params={"type": "oauth2"},
+ )
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update_overload_2(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.data_sources.with_raw_response.update(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="name",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ data_source = await response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update_overload_2(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.data_sources.with_streaming_response.update(
+ data_source_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ name="name",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ data_source = await response.parse()
+ assert_matches_type(DataSource, data_source, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update_overload_2(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `data_source_id` but received ''"):
+ await async_client.data_sources.with_raw_response.update(
+ data_source_id="",
+ name="name",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncMixedbread) -> None:
+ data_source = await async_client.data_sources.list()
+ assert_matches_type(AsyncCursor[DataSource], data_source, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ data_source = await async_client.data_sources.list(
+ limit=10,
+ after="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ before="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ include_total=False,
+ )
+ assert_matches_type(AsyncCursor[DataSource], data_source, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.data_sources.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ data_source = await response.parse()
+ assert_matches_type(AsyncCursor[DataSource], data_source, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.data_sources.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ data_source = await response.parse()
+ assert_matches_type(AsyncCursor[DataSource], data_source, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncMixedbread) -> None:
+ data_source = await async_client.data_sources.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+ assert_matches_type(DataSourceDeleteResponse, data_source, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.data_sources.with_raw_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ data_source = await response.parse()
+ assert_matches_type(DataSourceDeleteResponse, data_source, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.data_sources.with_streaming_response.delete(
+ "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ data_source = await response.parse()
+ assert_matches_type(DataSourceDeleteResponse, data_source, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `data_source_id` but received ''"):
+ await async_client.data_sources.with_raw_response.delete(
+ "",
+ )
diff --git a/tests/api_resources/test_embeddings.py b/tests/api_resources/test_embeddings.py
index 62648a38..f962e880 100644
--- a/tests/api_resources/test_embeddings.py
+++ b/tests/api_resources/test_embeddings.py
@@ -20,28 +20,28 @@ class TestEmbeddings:
@parametrize
def test_method_create(self, client: Mixedbread) -> None:
embedding = client.embeddings.create(
- input="This is a sample text input.",
model="mixedbread-ai/mxbai-embed-large-v1",
+ input="x",
)
assert_matches_type(EmbeddingCreateResponse, embedding, path=["response"])
@parametrize
def test_method_create_with_all_params(self, client: Mixedbread) -> None:
embedding = client.embeddings.create(
- input="This is a sample text input.",
model="mixedbread-ai/mxbai-embed-large-v1",
+ input="x",
dimensions=768,
- encoding_format="float",
- normalized=True,
prompt="Provide a detailed summary of the following text.",
+ normalized=True,
+ encoding_format="float",
)
assert_matches_type(EmbeddingCreateResponse, embedding, path=["response"])
@parametrize
def test_raw_response_create(self, client: Mixedbread) -> None:
response = client.embeddings.with_raw_response.create(
- input="This is a sample text input.",
model="mixedbread-ai/mxbai-embed-large-v1",
+ input="x",
)
assert response.is_closed is True
@@ -52,8 +52,8 @@ def test_raw_response_create(self, client: Mixedbread) -> None:
@parametrize
def test_streaming_response_create(self, client: Mixedbread) -> None:
with client.embeddings.with_streaming_response.create(
- input="This is a sample text input.",
model="mixedbread-ai/mxbai-embed-large-v1",
+ input="x",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
@@ -65,33 +65,35 @@ 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:
embedding = await async_client.embeddings.create(
- input="This is a sample text input.",
model="mixedbread-ai/mxbai-embed-large-v1",
+ input="x",
)
assert_matches_type(EmbeddingCreateResponse, embedding, path=["response"])
@parametrize
async def test_method_create_with_all_params(self, async_client: AsyncMixedbread) -> None:
embedding = await async_client.embeddings.create(
- input="This is a sample text input.",
model="mixedbread-ai/mxbai-embed-large-v1",
+ input="x",
dimensions=768,
- encoding_format="float",
- normalized=True,
prompt="Provide a detailed summary of the following text.",
+ normalized=True,
+ encoding_format="float",
)
assert_matches_type(EmbeddingCreateResponse, embedding, path=["response"])
@parametrize
async def test_raw_response_create(self, async_client: AsyncMixedbread) -> None:
response = await async_client.embeddings.with_raw_response.create(
- input="This is a sample text input.",
model="mixedbread-ai/mxbai-embed-large-v1",
+ input="x",
)
assert response.is_closed is True
@@ -102,8 +104,8 @@ async def test_raw_response_create(self, async_client: AsyncMixedbread) -> None:
@parametrize
async def test_streaming_response_create(self, async_client: AsyncMixedbread) -> None:
async with async_client.embeddings.with_streaming_response.create(
- input="This is a sample text input.",
model="mixedbread-ai/mxbai-embed-large-v1",
+ input="x",
) as response:
assert not response.is_closed
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
diff --git a/tests/api_resources/test_files.py b/tests/api_resources/test_files.py
index 1685fe4e..62adce9b 100644
--- a/tests/api_resources/test_files.py
+++ b/tests/api_resources/test_files.py
@@ -5,15 +5,20 @@
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,
- FileDeleted,
- FileListResponse,
+from mixedbread.types import FileObject, FileDeleteResponse
+from mixedbread._response import (
+ BinaryAPIResponse,
+ AsyncBinaryAPIResponse,
+ StreamedBinaryAPIResponse,
+ AsyncStreamedBinaryAPIResponse,
)
+from mixedbread.pagination import SyncCursor, AsyncCursor
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
@@ -135,15 +140,18 @@ def test_path_params_update(self, client: Mixedbread) -> None:
@parametrize
def test_method_list(self, client: Mixedbread) -> None:
file = client.files.list()
- assert_matches_type(FileListResponse, file, path=["response"])
+ assert_matches_type(SyncCursor[FileObject], file, path=["response"])
@parametrize
def test_method_list_with_all_params(self, client: Mixedbread) -> None:
file = client.files.list(
- limit=0,
- offset=0,
+ limit=10,
+ after="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ before="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ include_total=False,
+ q="x",
)
- assert_matches_type(FileListResponse, file, path=["response"])
+ assert_matches_type(SyncCursor[FileObject], file, path=["response"])
@parametrize
def test_raw_response_list(self, client: Mixedbread) -> None:
@@ -152,7 +160,7 @@ def test_raw_response_list(self, client: Mixedbread) -> None:
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"])
+ assert_matches_type(SyncCursor[FileObject], file, path=["response"])
@parametrize
def test_streaming_response_list(self, client: Mixedbread) -> None:
@@ -161,7 +169,7 @@ def test_streaming_response_list(self, client: Mixedbread) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
file = response.parse()
- assert_matches_type(FileListResponse, file, path=["response"])
+ assert_matches_type(SyncCursor[FileObject], file, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -170,7 +178,7 @@ def test_method_delete(self, client: Mixedbread) -> None:
file = client.files.delete(
"182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
)
- assert_matches_type(FileDeleted, file, path=["response"])
+ assert_matches_type(FileDeleteResponse, file, path=["response"])
@parametrize
def test_raw_response_delete(self, client: Mixedbread) -> None:
@@ -181,7 +189,7 @@ def test_raw_response_delete(self, client: Mixedbread) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
file = response.parse()
- assert_matches_type(FileDeleted, file, path=["response"])
+ assert_matches_type(FileDeleteResponse, file, path=["response"])
@parametrize
def test_streaming_response_delete(self, client: Mixedbread) -> None:
@@ -192,7 +200,7 @@ def test_streaming_response_delete(self, client: Mixedbread) -> None:
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
file = response.parse()
- assert_matches_type(FileDeleted, file, path=["response"])
+ assert_matches_type(FileDeleteResponse, file, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -203,9 +211,67 @@ 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"])
+ 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:
@@ -321,15 +387,18 @@ async def test_path_params_update(self, async_client: AsyncMixedbread) -> None:
@parametrize
async def test_method_list(self, async_client: AsyncMixedbread) -> None:
file = await async_client.files.list()
- assert_matches_type(FileListResponse, file, path=["response"])
+ assert_matches_type(AsyncCursor[FileObject], file, path=["response"])
@parametrize
async def test_method_list_with_all_params(self, async_client: AsyncMixedbread) -> None:
file = await async_client.files.list(
- limit=0,
- offset=0,
+ limit=10,
+ after="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ before="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ include_total=False,
+ q="x",
)
- assert_matches_type(FileListResponse, file, path=["response"])
+ assert_matches_type(AsyncCursor[FileObject], file, path=["response"])
@parametrize
async def test_raw_response_list(self, async_client: AsyncMixedbread) -> None:
@@ -338,7 +407,7 @@ async def test_raw_response_list(self, async_client: AsyncMixedbread) -> None:
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"])
+ assert_matches_type(AsyncCursor[FileObject], file, path=["response"])
@parametrize
async def test_streaming_response_list(self, async_client: AsyncMixedbread) -> None:
@@ -347,7 +416,7 @@ async def test_streaming_response_list(self, async_client: AsyncMixedbread) -> N
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
file = await response.parse()
- assert_matches_type(FileListResponse, file, path=["response"])
+ assert_matches_type(AsyncCursor[FileObject], file, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -356,7 +425,7 @@ async def test_method_delete(self, async_client: AsyncMixedbread) -> None:
file = await async_client.files.delete(
"182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
)
- assert_matches_type(FileDeleted, file, path=["response"])
+ assert_matches_type(FileDeleteResponse, file, path=["response"])
@parametrize
async def test_raw_response_delete(self, async_client: AsyncMixedbread) -> None:
@@ -367,7 +436,7 @@ async def test_raw_response_delete(self, async_client: AsyncMixedbread) -> None:
assert response.is_closed is True
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
file = await response.parse()
- assert_matches_type(FileDeleted, file, path=["response"])
+ assert_matches_type(FileDeleteResponse, file, path=["response"])
@parametrize
async def test_streaming_response_delete(self, async_client: AsyncMixedbread) -> None:
@@ -378,7 +447,7 @@ async def test_streaming_response_delete(self, async_client: AsyncMixedbread) ->
assert response.http_request.headers.get("X-Stainless-Lang") == "python"
file = await response.parse()
- assert_matches_type(FileDeleted, file, path=["response"])
+ assert_matches_type(FileDeleteResponse, file, path=["response"])
assert cast(Any, response.is_closed) is True
@@ -388,3 +457,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(
+ "",
+ )
diff --git a/tests/api_resources/test_reranking.py b/tests/api_resources/test_reranking.py
deleted file mode 100644
index 009148f5..00000000
--- a/tests/api_resources/test_reranking.py
+++ /dev/null
@@ -1,114 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-import os
-from typing import Any, cast
-
-import pytest
-
-from mixedbread import Mixedbread, AsyncMixedbread
-from tests.utils import assert_matches_type
-from mixedbread.types import RerankingCreateResponse
-
-base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
-
-
-class TestReranking:
- parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
-
- @parametrize
- def test_method_create(self, client: Mixedbread) -> None:
- reranking = client.reranking.create(
- input=["Document 1", "Document 2"],
- query="What is mixedbread ai?",
- )
- assert_matches_type(RerankingCreateResponse, reranking, path=["response"])
-
- @parametrize
- def test_method_create_with_all_params(self, client: Mixedbread) -> None:
- reranking = client.reranking.create(
- input=["Document 1", "Document 2"],
- query="What is mixedbread ai?",
- model="x",
- rank_fields=["field1", "field2"],
- return_input=False,
- top_k=10,
- )
- assert_matches_type(RerankingCreateResponse, reranking, path=["response"])
-
- @parametrize
- def test_raw_response_create(self, client: Mixedbread) -> None:
- response = client.reranking.with_raw_response.create(
- input=["Document 1", "Document 2"],
- query="What is mixedbread ai?",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- reranking = response.parse()
- assert_matches_type(RerankingCreateResponse, reranking, path=["response"])
-
- @parametrize
- def test_streaming_response_create(self, client: Mixedbread) -> None:
- with client.reranking.with_streaming_response.create(
- input=["Document 1", "Document 2"],
- query="What is mixedbread ai?",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- reranking = response.parse()
- assert_matches_type(RerankingCreateResponse, reranking, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
-
-class TestAsyncReranking:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
-
- @parametrize
- async def test_method_create(self, async_client: AsyncMixedbread) -> None:
- reranking = await async_client.reranking.create(
- input=["Document 1", "Document 2"],
- query="What is mixedbread ai?",
- )
- assert_matches_type(RerankingCreateResponse, reranking, path=["response"])
-
- @parametrize
- async def test_method_create_with_all_params(self, async_client: AsyncMixedbread) -> None:
- reranking = await async_client.reranking.create(
- input=["Document 1", "Document 2"],
- query="What is mixedbread ai?",
- model="x",
- rank_fields=["field1", "field2"],
- return_input=False,
- top_k=10,
- )
- assert_matches_type(RerankingCreateResponse, reranking, path=["response"])
-
- @parametrize
- async def test_raw_response_create(self, async_client: AsyncMixedbread) -> None:
- response = await async_client.reranking.with_raw_response.create(
- input=["Document 1", "Document 2"],
- query="What is mixedbread ai?",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- reranking = await response.parse()
- assert_matches_type(RerankingCreateResponse, reranking, path=["response"])
-
- @parametrize
- async def test_streaming_response_create(self, async_client: AsyncMixedbread) -> None:
- async with async_client.reranking.with_streaming_response.create(
- input=["Document 1", "Document 2"],
- query="What is mixedbread ai?",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- reranking = await response.parse()
- assert_matches_type(RerankingCreateResponse, reranking, path=["response"])
-
- assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/test_service_info.py b/tests/api_resources/test_service_info.py
deleted file mode 100644
index 6bc48955..00000000
--- a/tests/api_resources/test_service_info.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-import os
-from typing import Any, cast
-
-import pytest
-
-from mixedbread import Mixedbread, AsyncMixedbread
-from tests.utils import assert_matches_type
-from mixedbread.types import InfoResponse
-
-base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
-
-
-class TestServiceInfo:
- parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
-
- @parametrize
- def test_method_retrieve(self, client: Mixedbread) -> None:
- service_info = client.service_info.retrieve()
- assert_matches_type(InfoResponse, service_info, path=["response"])
-
- @parametrize
- def test_raw_response_retrieve(self, client: Mixedbread) -> None:
- response = client.service_info.with_raw_response.retrieve()
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- service_info = response.parse()
- assert_matches_type(InfoResponse, service_info, path=["response"])
-
- @parametrize
- def test_streaming_response_retrieve(self, client: Mixedbread) -> None:
- with client.service_info.with_streaming_response.retrieve() as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- service_info = response.parse()
- assert_matches_type(InfoResponse, service_info, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
-
-class TestAsyncServiceInfo:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
-
- @parametrize
- async def test_method_retrieve(self, async_client: AsyncMixedbread) -> None:
- service_info = await async_client.service_info.retrieve()
- assert_matches_type(InfoResponse, service_info, path=["response"])
-
- @parametrize
- async def test_raw_response_retrieve(self, async_client: AsyncMixedbread) -> None:
- response = await async_client.service_info.with_raw_response.retrieve()
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- service_info = await response.parse()
- assert_matches_type(InfoResponse, service_info, path=["response"])
-
- @parametrize
- async def test_streaming_response_retrieve(self, async_client: AsyncMixedbread) -> None:
- async with async_client.service_info.with_streaming_response.retrieve() as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- service_info = await response.parse()
- assert_matches_type(InfoResponse, service_info, path=["response"])
-
- assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/test_stores.py b/tests/api_resources/test_stores.py
new file mode 100644
index 00000000..0a076705
--- /dev/null
+++ b/tests/api_resources/test_stores.py
@@ -0,0 +1,791 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+import os
+from typing import Any, cast
+
+import pytest
+
+from mixedbread import Mixedbread, AsyncMixedbread
+from tests.utils import assert_matches_type
+from mixedbread.types import (
+ Store,
+ StoreDeleteResponse,
+ StoreSearchResponse,
+ StoreMetadataFacetsResponse,
+ StoreQuestionAnsweringResponse,
+)
+from mixedbread.pagination import SyncCursor, AsyncCursor
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestStores:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: Mixedbread) -> None:
+ store = client.stores.create()
+ assert_matches_type(Store, store, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: Mixedbread) -> None:
+ store = client.stores.create(
+ name="technical-documentation",
+ description="Contains technical specifications and guides",
+ is_public=False,
+ expires_after={
+ "anchor": "last_active_at",
+ "days": 0,
+ },
+ metadata={},
+ config={
+ "contextualization": True,
+ "save_content": True,
+ },
+ file_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
+ )
+ assert_matches_type(Store, store, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: Mixedbread) -> None:
+ response = client.stores.with_raw_response.create()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ store = response.parse()
+ assert_matches_type(Store, store, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: Mixedbread) -> None:
+ with client.stores.with_streaming_response.create() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ store = response.parse()
+ assert_matches_type(Store, store, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_retrieve(self, client: Mixedbread) -> None:
+ store = client.stores.retrieve(
+ "store_identifier",
+ )
+ assert_matches_type(Store, store, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: Mixedbread) -> None:
+ response = client.stores.with_raw_response.retrieve(
+ "store_identifier",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ store = response.parse()
+ assert_matches_type(Store, store, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: Mixedbread) -> None:
+ with client.stores.with_streaming_response.retrieve(
+ "store_identifier",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ store = response.parse()
+ assert_matches_type(Store, store, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `store_identifier` but received ''"):
+ client.stores.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ def test_method_update(self, client: Mixedbread) -> None:
+ store = client.stores.update(
+ store_identifier="store_identifier",
+ )
+ assert_matches_type(Store, store, path=["response"])
+
+ @parametrize
+ def test_method_update_with_all_params(self, client: Mixedbread) -> None:
+ store = client.stores.update(
+ store_identifier="store_identifier",
+ name="x",
+ description="description",
+ is_public=True,
+ expires_after={
+ "anchor": "last_active_at",
+ "days": 0,
+ },
+ metadata={},
+ )
+ assert_matches_type(Store, store, path=["response"])
+
+ @parametrize
+ def test_raw_response_update(self, client: Mixedbread) -> None:
+ response = client.stores.with_raw_response.update(
+ store_identifier="store_identifier",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ store = response.parse()
+ assert_matches_type(Store, store, path=["response"])
+
+ @parametrize
+ def test_streaming_response_update(self, client: Mixedbread) -> None:
+ with client.stores.with_streaming_response.update(
+ store_identifier="store_identifier",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ store = response.parse()
+ assert_matches_type(Store, store, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_update(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `store_identifier` but received ''"):
+ client.stores.with_raw_response.update(
+ store_identifier="",
+ )
+
+ @parametrize
+ def test_method_list(self, client: Mixedbread) -> None:
+ store = client.stores.list()
+ assert_matches_type(SyncCursor[Store], store, path=["response"])
+
+ @parametrize
+ def test_method_list_with_all_params(self, client: Mixedbread) -> None:
+ store = client.stores.list(
+ limit=10,
+ after="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ before="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ include_total=False,
+ q="x",
+ )
+ assert_matches_type(SyncCursor[Store], store, path=["response"])
+
+ @parametrize
+ def test_raw_response_list(self, client: Mixedbread) -> None:
+ response = client.stores.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ store = response.parse()
+ assert_matches_type(SyncCursor[Store], store, path=["response"])
+
+ @parametrize
+ def test_streaming_response_list(self, client: Mixedbread) -> None:
+ with client.stores.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ store = response.parse()
+ assert_matches_type(SyncCursor[Store], store, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_delete(self, client: Mixedbread) -> None:
+ store = client.stores.delete(
+ "store_identifier",
+ )
+ assert_matches_type(StoreDeleteResponse, store, path=["response"])
+
+ @parametrize
+ def test_raw_response_delete(self, client: Mixedbread) -> None:
+ response = client.stores.with_raw_response.delete(
+ "store_identifier",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ store = response.parse()
+ assert_matches_type(StoreDeleteResponse, store, path=["response"])
+
+ @parametrize
+ def test_streaming_response_delete(self, client: Mixedbread) -> None:
+ with client.stores.with_streaming_response.delete(
+ "store_identifier",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ store = response.parse()
+ assert_matches_type(StoreDeleteResponse, store, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_delete(self, client: Mixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `store_identifier` but received ''"):
+ client.stores.with_raw_response.delete(
+ "",
+ )
+
+ @parametrize
+ def test_method_metadata_facets(self, client: Mixedbread) -> None:
+ store = client.stores.metadata_facets(
+ store_identifiers=["string"],
+ )
+ assert_matches_type(StoreMetadataFacetsResponse, store, path=["response"])
+
+ @parametrize
+ def test_method_metadata_facets_with_all_params(self, client: Mixedbread) -> None:
+ store = client.stores.metadata_facets(
+ query="how to configure SSL",
+ store_identifiers=["string"],
+ top_k=1,
+ filters={
+ "all": [{}, {}],
+ "any": [{}, {}],
+ "none": [{}, {}],
+ },
+ file_ids=["123e4567-e89b-12d3-a456-426614174000", "123e4567-e89b-12d3-a456-426614174001"],
+ search_options={
+ "score_threshold": 0,
+ "rewrite_query": True,
+ "rerank": True,
+ "agentic": True,
+ "return_metadata": True,
+ "apply_search_rules": True,
+ },
+ facets=["string"],
+ )
+ assert_matches_type(StoreMetadataFacetsResponse, store, path=["response"])
+
+ @parametrize
+ def test_raw_response_metadata_facets(self, client: Mixedbread) -> None:
+ response = client.stores.with_raw_response.metadata_facets(
+ store_identifiers=["string"],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ store = response.parse()
+ assert_matches_type(StoreMetadataFacetsResponse, store, path=["response"])
+
+ @parametrize
+ def test_streaming_response_metadata_facets(self, client: Mixedbread) -> None:
+ with client.stores.with_streaming_response.metadata_facets(
+ store_identifiers=["string"],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ store = response.parse()
+ assert_matches_type(StoreMetadataFacetsResponse, store, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_question_answering(self, client: Mixedbread) -> None:
+ store = client.stores.question_answering(
+ store_identifiers=["string"],
+ )
+ assert_matches_type(StoreQuestionAnsweringResponse, store, path=["response"])
+
+ @parametrize
+ def test_method_question_answering_with_all_params(self, client: Mixedbread) -> None:
+ store = client.stores.question_answering(
+ query="x",
+ store_identifiers=["string"],
+ top_k=1,
+ filters={
+ "all": [{}, {}],
+ "any": [{}, {}],
+ "none": [{}, {}],
+ },
+ file_ids=["123e4567-e89b-12d3-a456-426614174000", "123e4567-e89b-12d3-a456-426614174001"],
+ search_options={
+ "score_threshold": 0,
+ "rewrite_query": True,
+ "rerank": True,
+ "agentic": True,
+ "return_metadata": True,
+ "apply_search_rules": True,
+ },
+ stream=True,
+ qa_options={
+ "cite": True,
+ "multimodal": True,
+ },
+ )
+ assert_matches_type(StoreQuestionAnsweringResponse, store, path=["response"])
+
+ @parametrize
+ def test_raw_response_question_answering(self, client: Mixedbread) -> None:
+ response = client.stores.with_raw_response.question_answering(
+ store_identifiers=["string"],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ store = response.parse()
+ assert_matches_type(StoreQuestionAnsweringResponse, store, path=["response"])
+
+ @parametrize
+ def test_streaming_response_question_answering(self, client: Mixedbread) -> None:
+ with client.stores.with_streaming_response.question_answering(
+ store_identifiers=["string"],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ store = response.parse()
+ assert_matches_type(StoreQuestionAnsweringResponse, store, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_search(self, client: Mixedbread) -> None:
+ store = client.stores.search(
+ query="how to configure SSL",
+ store_identifiers=["string"],
+ )
+ assert_matches_type(StoreSearchResponse, store, path=["response"])
+
+ @parametrize
+ def test_method_search_with_all_params(self, client: Mixedbread) -> None:
+ store = client.stores.search(
+ query="how to configure SSL",
+ store_identifiers=["string"],
+ top_k=1,
+ filters={
+ "all": [{}, {}],
+ "any": [{}, {}],
+ "none": [{}, {}],
+ },
+ file_ids=["123e4567-e89b-12d3-a456-426614174000", "123e4567-e89b-12d3-a456-426614174001"],
+ search_options={
+ "score_threshold": 0,
+ "rewrite_query": True,
+ "rerank": True,
+ "agentic": True,
+ "return_metadata": True,
+ "apply_search_rules": True,
+ },
+ )
+ assert_matches_type(StoreSearchResponse, store, path=["response"])
+
+ @parametrize
+ def test_raw_response_search(self, client: Mixedbread) -> None:
+ response = client.stores.with_raw_response.search(
+ query="how to configure SSL",
+ store_identifiers=["string"],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ store = response.parse()
+ assert_matches_type(StoreSearchResponse, store, path=["response"])
+
+ @parametrize
+ def test_streaming_response_search(self, client: Mixedbread) -> None:
+ with client.stores.with_streaming_response.search(
+ query="how to configure SSL",
+ store_identifiers=["string"],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ store = response.parse()
+ assert_matches_type(StoreSearchResponse, store, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+
+class TestAsyncStores:
+ 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:
+ store = await async_client.stores.create()
+ assert_matches_type(Store, store, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ store = await async_client.stores.create(
+ name="technical-documentation",
+ description="Contains technical specifications and guides",
+ is_public=False,
+ expires_after={
+ "anchor": "last_active_at",
+ "days": 0,
+ },
+ metadata={},
+ config={
+ "contextualization": True,
+ "save_content": True,
+ },
+ file_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
+ )
+ assert_matches_type(Store, store, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.stores.with_raw_response.create()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ store = await response.parse()
+ assert_matches_type(Store, store, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.stores.with_streaming_response.create() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ store = await response.parse()
+ assert_matches_type(Store, store, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncMixedbread) -> None:
+ store = await async_client.stores.retrieve(
+ "store_identifier",
+ )
+ assert_matches_type(Store, store, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.stores.with_raw_response.retrieve(
+ "store_identifier",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ store = await response.parse()
+ assert_matches_type(Store, store, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.stores.with_streaming_response.retrieve(
+ "store_identifier",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ store = await response.parse()
+ assert_matches_type(Store, store, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `store_identifier` but received ''"):
+ await async_client.stores.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ async def test_method_update(self, async_client: AsyncMixedbread) -> None:
+ store = await async_client.stores.update(
+ store_identifier="store_identifier",
+ )
+ assert_matches_type(Store, store, path=["response"])
+
+ @parametrize
+ async def test_method_update_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ store = await async_client.stores.update(
+ store_identifier="store_identifier",
+ name="x",
+ description="description",
+ is_public=True,
+ expires_after={
+ "anchor": "last_active_at",
+ "days": 0,
+ },
+ metadata={},
+ )
+ assert_matches_type(Store, store, path=["response"])
+
+ @parametrize
+ async def test_raw_response_update(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.stores.with_raw_response.update(
+ store_identifier="store_identifier",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ store = await response.parse()
+ assert_matches_type(Store, store, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_update(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.stores.with_streaming_response.update(
+ store_identifier="store_identifier",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ store = await response.parse()
+ assert_matches_type(Store, store, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_update(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `store_identifier` but received ''"):
+ await async_client.stores.with_raw_response.update(
+ store_identifier="",
+ )
+
+ @parametrize
+ async def test_method_list(self, async_client: AsyncMixedbread) -> None:
+ store = await async_client.stores.list()
+ assert_matches_type(AsyncCursor[Store], store, path=["response"])
+
+ @parametrize
+ async def test_method_list_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ store = await async_client.stores.list(
+ limit=10,
+ after="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ before="eyJjcmVhdGVkX2F0IjoiMjAyNC0xMi0zMVQyMzo1OTo1OS4wMDBaIiwiaWQiOiJhYmMxMjMifQ==",
+ include_total=False,
+ q="x",
+ )
+ assert_matches_type(AsyncCursor[Store], store, path=["response"])
+
+ @parametrize
+ async def test_raw_response_list(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.stores.with_raw_response.list()
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ store = await response.parse()
+ assert_matches_type(AsyncCursor[Store], store, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_list(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.stores.with_streaming_response.list() as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ store = await response.parse()
+ assert_matches_type(AsyncCursor[Store], store, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_delete(self, async_client: AsyncMixedbread) -> None:
+ store = await async_client.stores.delete(
+ "store_identifier",
+ )
+ assert_matches_type(StoreDeleteResponse, store, path=["response"])
+
+ @parametrize
+ async def test_raw_response_delete(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.stores.with_raw_response.delete(
+ "store_identifier",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ store = await response.parse()
+ assert_matches_type(StoreDeleteResponse, store, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_delete(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.stores.with_streaming_response.delete(
+ "store_identifier",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ store = await response.parse()
+ assert_matches_type(StoreDeleteResponse, store, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_delete(self, async_client: AsyncMixedbread) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `store_identifier` but received ''"):
+ await async_client.stores.with_raw_response.delete(
+ "",
+ )
+
+ @parametrize
+ async def test_method_metadata_facets(self, async_client: AsyncMixedbread) -> None:
+ store = await async_client.stores.metadata_facets(
+ store_identifiers=["string"],
+ )
+ assert_matches_type(StoreMetadataFacetsResponse, store, path=["response"])
+
+ @parametrize
+ async def test_method_metadata_facets_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ store = await async_client.stores.metadata_facets(
+ query="how to configure SSL",
+ store_identifiers=["string"],
+ top_k=1,
+ filters={
+ "all": [{}, {}],
+ "any": [{}, {}],
+ "none": [{}, {}],
+ },
+ file_ids=["123e4567-e89b-12d3-a456-426614174000", "123e4567-e89b-12d3-a456-426614174001"],
+ search_options={
+ "score_threshold": 0,
+ "rewrite_query": True,
+ "rerank": True,
+ "agentic": True,
+ "return_metadata": True,
+ "apply_search_rules": True,
+ },
+ facets=["string"],
+ )
+ assert_matches_type(StoreMetadataFacetsResponse, store, path=["response"])
+
+ @parametrize
+ async def test_raw_response_metadata_facets(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.stores.with_raw_response.metadata_facets(
+ store_identifiers=["string"],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ store = await response.parse()
+ assert_matches_type(StoreMetadataFacetsResponse, store, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_metadata_facets(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.stores.with_streaming_response.metadata_facets(
+ store_identifiers=["string"],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ store = await response.parse()
+ assert_matches_type(StoreMetadataFacetsResponse, store, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_question_answering(self, async_client: AsyncMixedbread) -> None:
+ store = await async_client.stores.question_answering(
+ store_identifiers=["string"],
+ )
+ assert_matches_type(StoreQuestionAnsweringResponse, store, path=["response"])
+
+ @parametrize
+ async def test_method_question_answering_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ store = await async_client.stores.question_answering(
+ query="x",
+ store_identifiers=["string"],
+ top_k=1,
+ filters={
+ "all": [{}, {}],
+ "any": [{}, {}],
+ "none": [{}, {}],
+ },
+ file_ids=["123e4567-e89b-12d3-a456-426614174000", "123e4567-e89b-12d3-a456-426614174001"],
+ search_options={
+ "score_threshold": 0,
+ "rewrite_query": True,
+ "rerank": True,
+ "agentic": True,
+ "return_metadata": True,
+ "apply_search_rules": True,
+ },
+ stream=True,
+ qa_options={
+ "cite": True,
+ "multimodal": True,
+ },
+ )
+ assert_matches_type(StoreQuestionAnsweringResponse, store, path=["response"])
+
+ @parametrize
+ async def test_raw_response_question_answering(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.stores.with_raw_response.question_answering(
+ store_identifiers=["string"],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ store = await response.parse()
+ assert_matches_type(StoreQuestionAnsweringResponse, store, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_question_answering(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.stores.with_streaming_response.question_answering(
+ store_identifiers=["string"],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ store = await response.parse()
+ assert_matches_type(StoreQuestionAnsweringResponse, store, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_search(self, async_client: AsyncMixedbread) -> None:
+ store = await async_client.stores.search(
+ query="how to configure SSL",
+ store_identifiers=["string"],
+ )
+ assert_matches_type(StoreSearchResponse, store, path=["response"])
+
+ @parametrize
+ async def test_method_search_with_all_params(self, async_client: AsyncMixedbread) -> None:
+ store = await async_client.stores.search(
+ query="how to configure SSL",
+ store_identifiers=["string"],
+ top_k=1,
+ filters={
+ "all": [{}, {}],
+ "any": [{}, {}],
+ "none": [{}, {}],
+ },
+ file_ids=["123e4567-e89b-12d3-a456-426614174000", "123e4567-e89b-12d3-a456-426614174001"],
+ search_options={
+ "score_threshold": 0,
+ "rewrite_query": True,
+ "rerank": True,
+ "agentic": True,
+ "return_metadata": True,
+ "apply_search_rules": True,
+ },
+ )
+ assert_matches_type(StoreSearchResponse, store, path=["response"])
+
+ @parametrize
+ async def test_raw_response_search(self, async_client: AsyncMixedbread) -> None:
+ response = await async_client.stores.with_raw_response.search(
+ query="how to configure SSL",
+ store_identifiers=["string"],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ store = await response.parse()
+ assert_matches_type(StoreSearchResponse, store, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_search(self, async_client: AsyncMixedbread) -> None:
+ async with async_client.stores.with_streaming_response.search(
+ query="how to configure SSL",
+ store_identifiers=["string"],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ store = await response.parse()
+ assert_matches_type(StoreSearchResponse, store, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/test_vector_stores.py b/tests/api_resources/test_vector_stores.py
deleted file mode 100644
index 25798670..00000000
--- a/tests/api_resources/test_vector_stores.py
+++ /dev/null
@@ -1,621 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-import os
-from typing import Any, cast
-
-import pytest
-
-from mixedbread import Mixedbread, AsyncMixedbread
-from tests.utils import assert_matches_type
-from mixedbread.types import (
- VectorStore,
- VectorStoreDeleted,
- VectorStoreListResponse,
- VectorStoreSearchResponse,
-)
-
-base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
-
-
-class TestVectorStores:
- parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
-
- @parametrize
- def test_method_create(self, client: Mixedbread) -> None:
- vector_store = client.vector_stores.create()
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- @parametrize
- def test_method_create_with_all_params(self, client: Mixedbread) -> None:
- vector_store = client.vector_stores.create(
- description="Contains technical specifications and guides",
- expires_after={
- "anchor": "last_used_at",
- "days": 0,
- },
- file_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- metadata={},
- name="Technical Documentation",
- )
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- @parametrize
- def test_raw_response_create(self, client: Mixedbread) -> None:
- response = client.vector_stores.with_raw_response.create()
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- vector_store = response.parse()
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- @parametrize
- def test_streaming_response_create(self, client: Mixedbread) -> None:
- with client.vector_stores.with_streaming_response.create() as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- vector_store = response.parse()
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- def test_method_retrieve(self, client: Mixedbread) -> None:
- vector_store = client.vector_stores.retrieve(
- "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- @parametrize
- def test_raw_response_retrieve(self, client: Mixedbread) -> None:
- response = client.vector_stores.with_raw_response.retrieve(
- "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- vector_store = response.parse()
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- @parametrize
- def test_streaming_response_retrieve(self, client: Mixedbread) -> None:
- with client.vector_stores.with_streaming_response.retrieve(
- "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- vector_store = response.parse()
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- def test_path_params_retrieve(self, client: Mixedbread) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"):
- client.vector_stores.with_raw_response.retrieve(
- "",
- )
-
- @parametrize
- def test_method_update(self, client: Mixedbread) -> None:
- vector_store = client.vector_stores.update(
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- @parametrize
- def test_method_update_with_all_params(self, client: Mixedbread) -> None:
- vector_store = client.vector_stores.update(
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- description="x",
- expires_after={
- "anchor": "last_used_at",
- "days": 0,
- },
- metadata={},
- name="x",
- )
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- @parametrize
- def test_raw_response_update(self, client: Mixedbread) -> None:
- response = client.vector_stores.with_raw_response.update(
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- vector_store = response.parse()
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- @parametrize
- def test_streaming_response_update(self, client: Mixedbread) -> None:
- with client.vector_stores.with_streaming_response.update(
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- vector_store = response.parse()
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- def test_path_params_update(self, client: Mixedbread) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"):
- client.vector_stores.with_raw_response.update(
- vector_store_id="",
- )
-
- @parametrize
- def test_method_list(self, client: Mixedbread) -> None:
- vector_store = client.vector_stores.list()
- assert_matches_type(VectorStoreListResponse, vector_store, path=["response"])
-
- @parametrize
- def test_method_list_with_all_params(self, client: Mixedbread) -> None:
- vector_store = client.vector_stores.list(
- limit=0,
- offset=0,
- )
- assert_matches_type(VectorStoreListResponse, vector_store, path=["response"])
-
- @parametrize
- def test_raw_response_list(self, client: Mixedbread) -> None:
- response = client.vector_stores.with_raw_response.list()
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- vector_store = response.parse()
- assert_matches_type(VectorStoreListResponse, vector_store, path=["response"])
-
- @parametrize
- def test_streaming_response_list(self, client: Mixedbread) -> None:
- with client.vector_stores.with_streaming_response.list() as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- vector_store = response.parse()
- assert_matches_type(VectorStoreListResponse, vector_store, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- def test_method_delete(self, client: Mixedbread) -> None:
- vector_store = client.vector_stores.delete(
- "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
- assert_matches_type(VectorStoreDeleted, vector_store, path=["response"])
-
- @parametrize
- def test_raw_response_delete(self, client: Mixedbread) -> None:
- response = client.vector_stores.with_raw_response.delete(
- "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- vector_store = response.parse()
- assert_matches_type(VectorStoreDeleted, vector_store, path=["response"])
-
- @parametrize
- def test_streaming_response_delete(self, client: Mixedbread) -> None:
- with client.vector_stores.with_streaming_response.delete(
- "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- vector_store = response.parse()
- assert_matches_type(VectorStoreDeleted, vector_store, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- def test_path_params_delete(self, client: Mixedbread) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"):
- client.vector_stores.with_raw_response.delete(
- "",
- )
-
- @parametrize
- def test_method_question_answering(self, client: Mixedbread) -> None:
- vector_store = client.vector_stores.question_answering(
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- )
- assert_matches_type(object, vector_store, path=["response"])
-
- @parametrize
- def test_method_question_answering_with_all_params(self, client: Mixedbread) -> None:
- vector_store = client.vector_stores.question_answering(
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- qa_options={"cite": True},
- query="x",
- search_options={
- "return_chunks": True,
- "return_metadata": True,
- "rewrite_query": True,
- "score_threshold": 0,
- },
- stream=True,
- top_k=1,
- )
- assert_matches_type(object, vector_store, path=["response"])
-
- @parametrize
- def test_raw_response_question_answering(self, client: Mixedbread) -> None:
- response = client.vector_stores.with_raw_response.question_answering(
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- vector_store = response.parse()
- assert_matches_type(object, vector_store, path=["response"])
-
- @parametrize
- def test_streaming_response_question_answering(self, client: Mixedbread) -> None:
- with client.vector_stores.with_streaming_response.question_answering(
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- vector_store = response.parse()
- assert_matches_type(object, vector_store, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- def test_method_search(self, client: Mixedbread) -> None:
- vector_store = client.vector_stores.search(
- query="how to configure SSL",
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- )
- assert_matches_type(VectorStoreSearchResponse, vector_store, path=["response"])
-
- @parametrize
- def test_method_search_with_all_params(self, client: Mixedbread) -> None:
- vector_store = client.vector_stores.search(
- query="how to configure SSL",
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- search_options={
- "return_chunks": True,
- "return_metadata": True,
- "rewrite_query": True,
- "score_threshold": 0,
- },
- top_k=1,
- )
- assert_matches_type(VectorStoreSearchResponse, vector_store, path=["response"])
-
- @parametrize
- def test_raw_response_search(self, client: Mixedbread) -> None:
- response = client.vector_stores.with_raw_response.search(
- query="how to configure SSL",
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- vector_store = response.parse()
- assert_matches_type(VectorStoreSearchResponse, vector_store, path=["response"])
-
- @parametrize
- def test_streaming_response_search(self, client: Mixedbread) -> None:
- with client.vector_stores.with_streaming_response.search(
- query="how to configure SSL",
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- vector_store = response.parse()
- assert_matches_type(VectorStoreSearchResponse, vector_store, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
-
-class TestAsyncVectorStores:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
-
- @parametrize
- async def test_method_create(self, async_client: AsyncMixedbread) -> None:
- vector_store = await async_client.vector_stores.create()
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- @parametrize
- async def test_method_create_with_all_params(self, async_client: AsyncMixedbread) -> None:
- vector_store = await async_client.vector_stores.create(
- description="Contains technical specifications and guides",
- expires_after={
- "anchor": "last_used_at",
- "days": 0,
- },
- file_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- metadata={},
- name="Technical Documentation",
- )
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- @parametrize
- async def test_raw_response_create(self, async_client: AsyncMixedbread) -> None:
- response = await async_client.vector_stores.with_raw_response.create()
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- vector_store = await response.parse()
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- @parametrize
- async def test_streaming_response_create(self, async_client: AsyncMixedbread) -> None:
- async with async_client.vector_stores.with_streaming_response.create() as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- vector_store = await response.parse()
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- async def test_method_retrieve(self, async_client: AsyncMixedbread) -> None:
- vector_store = await async_client.vector_stores.retrieve(
- "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- @parametrize
- async def test_raw_response_retrieve(self, async_client: AsyncMixedbread) -> None:
- response = await async_client.vector_stores.with_raw_response.retrieve(
- "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- vector_store = await response.parse()
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- @parametrize
- async def test_streaming_response_retrieve(self, async_client: AsyncMixedbread) -> None:
- async with async_client.vector_stores.with_streaming_response.retrieve(
- "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- vector_store = await response.parse()
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- async def test_path_params_retrieve(self, async_client: AsyncMixedbread) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"):
- await async_client.vector_stores.with_raw_response.retrieve(
- "",
- )
-
- @parametrize
- async def test_method_update(self, async_client: AsyncMixedbread) -> None:
- vector_store = await async_client.vector_stores.update(
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- @parametrize
- async def test_method_update_with_all_params(self, async_client: AsyncMixedbread) -> None:
- vector_store = await async_client.vector_stores.update(
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- description="x",
- expires_after={
- "anchor": "last_used_at",
- "days": 0,
- },
- metadata={},
- name="x",
- )
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- @parametrize
- async def test_raw_response_update(self, async_client: AsyncMixedbread) -> None:
- response = await async_client.vector_stores.with_raw_response.update(
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- vector_store = await response.parse()
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- @parametrize
- async def test_streaming_response_update(self, async_client: AsyncMixedbread) -> None:
- async with async_client.vector_stores.with_streaming_response.update(
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- vector_store = await response.parse()
- assert_matches_type(VectorStore, vector_store, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- async def test_path_params_update(self, async_client: AsyncMixedbread) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"):
- await async_client.vector_stores.with_raw_response.update(
- vector_store_id="",
- )
-
- @parametrize
- async def test_method_list(self, async_client: AsyncMixedbread) -> None:
- vector_store = await async_client.vector_stores.list()
- assert_matches_type(VectorStoreListResponse, vector_store, path=["response"])
-
- @parametrize
- async def test_method_list_with_all_params(self, async_client: AsyncMixedbread) -> None:
- vector_store = await async_client.vector_stores.list(
- limit=0,
- offset=0,
- )
- assert_matches_type(VectorStoreListResponse, vector_store, path=["response"])
-
- @parametrize
- async def test_raw_response_list(self, async_client: AsyncMixedbread) -> None:
- response = await async_client.vector_stores.with_raw_response.list()
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- vector_store = await response.parse()
- assert_matches_type(VectorStoreListResponse, vector_store, path=["response"])
-
- @parametrize
- async def test_streaming_response_list(self, async_client: AsyncMixedbread) -> None:
- async with async_client.vector_stores.with_streaming_response.list() as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- vector_store = await response.parse()
- assert_matches_type(VectorStoreListResponse, vector_store, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- async def test_method_delete(self, async_client: AsyncMixedbread) -> None:
- vector_store = await async_client.vector_stores.delete(
- "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
- assert_matches_type(VectorStoreDeleted, vector_store, path=["response"])
-
- @parametrize
- async def test_raw_response_delete(self, async_client: AsyncMixedbread) -> None:
- response = await async_client.vector_stores.with_raw_response.delete(
- "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- vector_store = await response.parse()
- assert_matches_type(VectorStoreDeleted, vector_store, path=["response"])
-
- @parametrize
- async def test_streaming_response_delete(self, async_client: AsyncMixedbread) -> None:
- async with async_client.vector_stores.with_streaming_response.delete(
- "182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- vector_store = await response.parse()
- assert_matches_type(VectorStoreDeleted, vector_store, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- async def test_path_params_delete(self, async_client: AsyncMixedbread) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"):
- await async_client.vector_stores.with_raw_response.delete(
- "",
- )
-
- @parametrize
- async def test_method_question_answering(self, async_client: AsyncMixedbread) -> None:
- vector_store = await async_client.vector_stores.question_answering(
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- )
- assert_matches_type(object, vector_store, path=["response"])
-
- @parametrize
- async def test_method_question_answering_with_all_params(self, async_client: AsyncMixedbread) -> None:
- vector_store = await async_client.vector_stores.question_answering(
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- qa_options={"cite": True},
- query="x",
- search_options={
- "return_chunks": True,
- "return_metadata": True,
- "rewrite_query": True,
- "score_threshold": 0,
- },
- stream=True,
- top_k=1,
- )
- assert_matches_type(object, vector_store, path=["response"])
-
- @parametrize
- async def test_raw_response_question_answering(self, async_client: AsyncMixedbread) -> None:
- response = await async_client.vector_stores.with_raw_response.question_answering(
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- vector_store = await response.parse()
- assert_matches_type(object, vector_store, path=["response"])
-
- @parametrize
- async def test_streaming_response_question_answering(self, async_client: AsyncMixedbread) -> None:
- async with async_client.vector_stores.with_streaming_response.question_answering(
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- vector_store = await response.parse()
- assert_matches_type(object, vector_store, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- async def test_method_search(self, async_client: AsyncMixedbread) -> None:
- vector_store = await async_client.vector_stores.search(
- query="how to configure SSL",
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- )
- assert_matches_type(VectorStoreSearchResponse, vector_store, path=["response"])
-
- @parametrize
- async def test_method_search_with_all_params(self, async_client: AsyncMixedbread) -> None:
- vector_store = await async_client.vector_stores.search(
- query="how to configure SSL",
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- search_options={
- "return_chunks": True,
- "return_metadata": True,
- "rewrite_query": True,
- "score_threshold": 0,
- },
- top_k=1,
- )
- assert_matches_type(VectorStoreSearchResponse, vector_store, path=["response"])
-
- @parametrize
- async def test_raw_response_search(self, async_client: AsyncMixedbread) -> None:
- response = await async_client.vector_stores.with_raw_response.search(
- query="how to configure SSL",
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- vector_store = await response.parse()
- assert_matches_type(VectorStoreSearchResponse, vector_store, path=["response"])
-
- @parametrize
- async def test_streaming_response_search(self, async_client: AsyncMixedbread) -> None:
- async with async_client.vector_stores.with_streaming_response.search(
- query="how to configure SSL",
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- vector_store = await response.parse()
- assert_matches_type(VectorStoreSearchResponse, vector_store, path=["response"])
-
- assert cast(Any, response.is_closed) is True
diff --git a/tests/api_resources/vector_stores/test_files.py b/tests/api_resources/vector_stores/test_files.py
deleted file mode 100644
index c3900a06..00000000
--- a/tests/api_resources/vector_stores/test_files.py
+++ /dev/null
@@ -1,513 +0,0 @@
-# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
-
-from __future__ import annotations
-
-import os
-from typing import Any, cast
-
-import pytest
-
-from mixedbread import Mixedbread, AsyncMixedbread
-from tests.utils import assert_matches_type
-from mixedbread.types.vector_stores import (
- VectorStoreFile,
- FileListResponse,
- FileSearchResponse,
- VectorStoreFileDeleted,
-)
-
-base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
-
-
-class TestFiles:
- parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
-
- @parametrize
- def test_method_create(self, client: Mixedbread) -> None:
- file = client.vector_stores.files.create(
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
- assert_matches_type(VectorStoreFile, file, path=["response"])
-
- @parametrize
- def test_method_create_with_all_params(self, client: Mixedbread) -> None:
- file = client.vector_stores.files.create(
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- metadata={},
- )
- assert_matches_type(VectorStoreFile, file, path=["response"])
-
- @parametrize
- def test_raw_response_create(self, client: Mixedbread) -> None:
- response = client.vector_stores.files.with_raw_response.create(
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- file = response.parse()
- assert_matches_type(VectorStoreFile, file, path=["response"])
-
- @parametrize
- def test_streaming_response_create(self, client: Mixedbread) -> None:
- with client.vector_stores.files.with_streaming_response.create(
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- file = response.parse()
- assert_matches_type(VectorStoreFile, file, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- def test_path_params_create(self, client: Mixedbread) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"):
- client.vector_stores.files.with_raw_response.create(
- vector_store_id="",
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- @parametrize
- def test_method_retrieve(self, client: Mixedbread) -> None:
- file = client.vector_stores.files.retrieve(
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
- assert_matches_type(VectorStoreFile, file, path=["response"])
-
- @parametrize
- def test_raw_response_retrieve(self, client: Mixedbread) -> None:
- response = client.vector_stores.files.with_raw_response.retrieve(
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- file = response.parse()
- assert_matches_type(VectorStoreFile, file, path=["response"])
-
- @parametrize
- def test_streaming_response_retrieve(self, client: Mixedbread) -> None:
- with client.vector_stores.files.with_streaming_response.retrieve(
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- file = response.parse()
- assert_matches_type(VectorStoreFile, file, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- def test_path_params_retrieve(self, client: Mixedbread) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"):
- client.vector_stores.files.with_raw_response.retrieve(
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- vector_store_id="",
- )
-
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"):
- client.vector_stores.files.with_raw_response.retrieve(
- file_id="",
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- @parametrize
- def test_method_list(self, client: Mixedbread) -> None:
- file = client.vector_stores.files.list(
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
- 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_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- limit=0,
- offset=0,
- )
- 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_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- 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_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) 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_id` but received ''"):
- client.vector_stores.files.with_raw_response.list(
- vector_store_id="",
- )
-
- @parametrize
- def test_method_delete(self, client: Mixedbread) -> None:
- file = client.vector_stores.files.delete(
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
- assert_matches_type(VectorStoreFileDeleted, file, path=["response"])
-
- @parametrize
- def test_raw_response_delete(self, client: Mixedbread) -> None:
- response = client.vector_stores.files.with_raw_response.delete(
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- file = response.parse()
- assert_matches_type(VectorStoreFileDeleted, file, path=["response"])
-
- @parametrize
- def test_streaming_response_delete(self, client: Mixedbread) -> None:
- with client.vector_stores.files.with_streaming_response.delete(
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- file = response.parse()
- assert_matches_type(VectorStoreFileDeleted, file, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- def test_path_params_delete(self, client: Mixedbread) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"):
- client.vector_stores.files.with_raw_response.delete(
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- vector_store_id="",
- )
-
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"):
- client.vector_stores.files.with_raw_response.delete(
- file_id="",
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- @parametrize
- def test_method_search(self, client: Mixedbread) -> None:
- file = client.vector_stores.files.search(
- query="how to configure SSL",
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- )
- assert_matches_type(FileSearchResponse, file, path=["response"])
-
- @parametrize
- def test_method_search_with_all_params(self, client: Mixedbread) -> None:
- file = client.vector_stores.files.search(
- query="how to configure SSL",
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- search_options={
- "return_chunks": True,
- "return_metadata": True,
- "rewrite_query": True,
- "score_threshold": 0,
- },
- top_k=1,
- )
- assert_matches_type(FileSearchResponse, file, path=["response"])
-
- @parametrize
- def test_raw_response_search(self, client: Mixedbread) -> None:
- response = client.vector_stores.files.with_raw_response.search(
- query="how to configure SSL",
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- file = response.parse()
- assert_matches_type(FileSearchResponse, file, path=["response"])
-
- @parametrize
- def test_streaming_response_search(self, client: Mixedbread) -> None:
- with client.vector_stores.files.with_streaming_response.search(
- query="how to configure SSL",
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- file = response.parse()
- assert_matches_type(FileSearchResponse, file, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
-
-class TestAsyncFiles:
- parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
-
- @parametrize
- async def test_method_create(self, async_client: AsyncMixedbread) -> None:
- file = await async_client.vector_stores.files.create(
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
- assert_matches_type(VectorStoreFile, file, path=["response"])
-
- @parametrize
- async def test_method_create_with_all_params(self, async_client: AsyncMixedbread) -> None:
- file = await async_client.vector_stores.files.create(
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- metadata={},
- )
- assert_matches_type(VectorStoreFile, file, path=["response"])
-
- @parametrize
- async def test_raw_response_create(self, async_client: AsyncMixedbread) -> None:
- response = await async_client.vector_stores.files.with_raw_response.create(
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- file = await response.parse()
- assert_matches_type(VectorStoreFile, file, path=["response"])
-
- @parametrize
- async def test_streaming_response_create(self, async_client: AsyncMixedbread) -> None:
- async with async_client.vector_stores.files.with_streaming_response.create(
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- file = await response.parse()
- assert_matches_type(VectorStoreFile, file, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- async def test_path_params_create(self, async_client: AsyncMixedbread) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"):
- await async_client.vector_stores.files.with_raw_response.create(
- vector_store_id="",
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- @parametrize
- async def test_method_retrieve(self, async_client: AsyncMixedbread) -> None:
- file = await async_client.vector_stores.files.retrieve(
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
- assert_matches_type(VectorStoreFile, file, path=["response"])
-
- @parametrize
- async def test_raw_response_retrieve(self, async_client: AsyncMixedbread) -> None:
- response = await async_client.vector_stores.files.with_raw_response.retrieve(
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- file = await response.parse()
- assert_matches_type(VectorStoreFile, file, path=["response"])
-
- @parametrize
- async def test_streaming_response_retrieve(self, async_client: AsyncMixedbread) -> None:
- async with async_client.vector_stores.files.with_streaming_response.retrieve(
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- file = await response.parse()
- assert_matches_type(VectorStoreFile, file, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- async def test_path_params_retrieve(self, async_client: AsyncMixedbread) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"):
- await async_client.vector_stores.files.with_raw_response.retrieve(
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- vector_store_id="",
- )
-
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"):
- await async_client.vector_stores.files.with_raw_response.retrieve(
- file_id="",
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- @parametrize
- async def test_method_list(self, async_client: AsyncMixedbread) -> None:
- file = await async_client.vector_stores.files.list(
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
- 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_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- limit=0,
- offset=0,
- )
- 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_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- 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_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) 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_id` but received ''"):
- await async_client.vector_stores.files.with_raw_response.list(
- vector_store_id="",
- )
-
- @parametrize
- async def test_method_delete(self, async_client: AsyncMixedbread) -> None:
- file = await async_client.vector_stores.files.delete(
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
- assert_matches_type(VectorStoreFileDeleted, file, path=["response"])
-
- @parametrize
- async def test_raw_response_delete(self, async_client: AsyncMixedbread) -> None:
- response = await async_client.vector_stores.files.with_raw_response.delete(
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- file = await response.parse()
- assert_matches_type(VectorStoreFileDeleted, file, path=["response"])
-
- @parametrize
- async def test_streaming_response_delete(self, async_client: AsyncMixedbread) -> None:
- async with async_client.vector_stores.files.with_streaming_response.delete(
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- file = await response.parse()
- assert_matches_type(VectorStoreFileDeleted, file, path=["response"])
-
- assert cast(Any, response.is_closed) is True
-
- @parametrize
- async def test_path_params_delete(self, async_client: AsyncMixedbread) -> None:
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `vector_store_id` but received ''"):
- await async_client.vector_stores.files.with_raw_response.delete(
- file_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- vector_store_id="",
- )
-
- with pytest.raises(ValueError, match=r"Expected a non-empty value for `file_id` but received ''"):
- await async_client.vector_stores.files.with_raw_response.delete(
- file_id="",
- vector_store_id="182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e",
- )
-
- @parametrize
- async def test_method_search(self, async_client: AsyncMixedbread) -> None:
- file = await async_client.vector_stores.files.search(
- query="how to configure SSL",
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- )
- assert_matches_type(FileSearchResponse, file, path=["response"])
-
- @parametrize
- async def test_method_search_with_all_params(self, async_client: AsyncMixedbread) -> None:
- file = await async_client.vector_stores.files.search(
- query="how to configure SSL",
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- search_options={
- "return_chunks": True,
- "return_metadata": True,
- "rewrite_query": True,
- "score_threshold": 0,
- },
- top_k=1,
- )
- assert_matches_type(FileSearchResponse, file, path=["response"])
-
- @parametrize
- async def test_raw_response_search(self, async_client: AsyncMixedbread) -> None:
- response = await async_client.vector_stores.files.with_raw_response.search(
- query="how to configure SSL",
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- )
-
- assert response.is_closed is True
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
- file = await response.parse()
- assert_matches_type(FileSearchResponse, file, path=["response"])
-
- @parametrize
- async def test_streaming_response_search(self, async_client: AsyncMixedbread) -> None:
- async with async_client.vector_stores.files.with_streaming_response.search(
- query="how to configure SSL",
- vector_store_ids=["182bd5e5-6e1a-4fe4-a799-aa6d9a6ab26e"],
- ) as response:
- assert not response.is_closed
- assert response.http_request.headers.get("X-Stainless-Lang") == "python"
-
- file = await response.parse()
- assert_matches_type(FileSearchResponse, file, path=["response"])
-
- assert cast(Any, response.is_closed) is True
diff --git a/tests/conftest.py b/tests/conftest.py
index f546bdd5..9b35601c 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -1,16 +1,20 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
from __future__ import annotations
import os
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
+ from _pytest.fixtures import FixtureRequest # pyright: ignore[reportPrivateImportUsage]
pytest.register_assert_rewrite("tests.utils")
@@ -25,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")
@@ -43,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 5c2e3f24..8cc00170 100644
--- a/tests/test_client.py
+++ b/tests/test_client.py
@@ -8,12 +8,11 @@
import json
import asyncio
import inspect
-import subprocess
+import dataclasses
import tracemalloc
-from typing import Any, Union, cast
-from textwrap import dedent
+from typing import Any, Union, TypeVar, Callable, Iterable, Iterator, Optional, Coroutine, cast
from unittest import mock
-from typing_extensions import Literal
+from typing_extensions import Literal, AsyncIterator, override
import httpx
import pytest
@@ -22,18 +21,23 @@
from mixedbread import Mixedbread, AsyncMixedbread, APIResponseValidationError
from mixedbread._types import Omit
+from mixedbread._utils import asyncify
from mixedbread._models import BaseModel, FinalRequestOptions
-from mixedbread._constants import RAW_RESPONSE_HEADER
-from mixedbread._exceptions import APIStatusError, APITimeoutError, APIResponseValidationError
+from mixedbread._exceptions import APIStatusError, APITimeoutError, MixedbreadError, APIResponseValidationError
from mixedbread._base_client import (
DEFAULT_TIMEOUT,
HTTPX_DEFAULT_TIMEOUT,
BaseClient,
+ OtherPlatform,
+ DefaultHttpxClient,
+ DefaultAsyncHttpxClient,
+ get_platform,
make_request_options,
)
from .utils import update_env
+T = TypeVar("T")
base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
api_key = "My API Key"
@@ -48,6 +52,57 @@ def _low_retry_timeout(*_args: Any, **_kwargs: Any) -> float:
return 0.1
+def mirror_request_content(request: httpx.Request) -> httpx.Response:
+ return httpx.Response(200, content=request.content)
+
+
+# note: we can't use the httpx.MockTransport class as it consumes the request
+# body itself, which means we can't test that the body is read lazily
+class MockTransport(httpx.BaseTransport, httpx.AsyncBaseTransport):
+ def __init__(
+ self,
+ handler: Callable[[httpx.Request], httpx.Response]
+ | Callable[[httpx.Request], Coroutine[Any, Any, httpx.Response]],
+ ) -> None:
+ self.handler = handler
+
+ @override
+ def handle_request(
+ self,
+ request: httpx.Request,
+ ) -> httpx.Response:
+ assert not inspect.iscoroutinefunction(self.handler), "handler must not be a coroutine function"
+ assert inspect.isfunction(self.handler), "handler must be a function"
+ return self.handler(request)
+
+ @override
+ async def handle_async_request(
+ self,
+ request: httpx.Request,
+ ) -> httpx.Response:
+ assert inspect.iscoroutinefunction(self.handler), "handler must be a coroutine function"
+ return await self.handler(request)
+
+
+@dataclasses.dataclass
+class Counter:
+ value: int = 0
+
+
+def _make_sync_iterator(iterable: Iterable[T], counter: Optional[Counter] = None) -> Iterator[T]:
+ for item in iterable:
+ if counter:
+ counter.value += 1
+ yield item
+
+
+async def _make_async_iterator(iterable: Iterable[T], counter: Optional[Counter] = None) -> AsyncIterator[T]:
+ for item in iterable:
+ if counter:
+ counter.value += 1
+ yield item
+
+
def _get_open_connections(client: Mixedbread | AsyncMixedbread) -> int:
transport = client._client._transport
assert isinstance(transport, httpx.HTTPTransport) or isinstance(transport, httpx.AsyncHTTPTransport)
@@ -57,51 +112,49 @@ def _get_open_connections(client: Mixedbread | AsyncMixedbread) -> int:
class TestMixedbread:
- client = Mixedbread(base_url=base_url, api_key=api_key, _strict_response_validation=True)
-
@pytest.mark.respx(base_url=base_url)
- def test_raw_response(self, respx_mock: MockRouter) -> None:
+ def test_raw_response(self, respx_mock: MockRouter, client: Mixedbread) -> None:
respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- response = self.client.post("/foo", cast_to=httpx.Response)
+ response = client.post("/foo", cast_to=httpx.Response)
assert response.status_code == 200
assert isinstance(response, httpx.Response)
assert response.json() == {"foo": "bar"}
@pytest.mark.respx(base_url=base_url)
- def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None:
+ def test_raw_response_for_binary(self, respx_mock: MockRouter, client: Mixedbread) -> None:
respx_mock.post("/foo").mock(
return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}')
)
- response = self.client.post("/foo", cast_to=httpx.Response)
+ response = client.post("/foo", cast_to=httpx.Response)
assert response.status_code == 200
assert isinstance(response, httpx.Response)
assert response.json() == {"foo": "bar"}
- def test_copy(self) -> None:
- copied = self.client.copy()
- assert id(copied) != id(self.client)
+ def test_copy(self, client: Mixedbread) -> None:
+ copied = client.copy()
+ assert id(copied) != id(client)
- copied = self.client.copy(api_key="another My API Key")
+ copied = client.copy(api_key="another My API Key")
assert copied.api_key == "another My API Key"
- assert self.client.api_key == "My API Key"
+ assert client.api_key == "My API Key"
- def test_copy_default_options(self) -> None:
+ def test_copy_default_options(self, client: Mixedbread) -> None:
# options that have a default are overridden correctly
- copied = self.client.copy(max_retries=7)
+ copied = client.copy(max_retries=7)
assert copied.max_retries == 7
- assert self.client.max_retries == 2
+ assert client.max_retries == 2
copied2 = copied.copy(max_retries=6)
assert copied2.max_retries == 6
assert copied.max_retries == 7
# timeout
- assert isinstance(self.client.timeout, httpx.Timeout)
- copied = self.client.copy(timeout=None)
+ assert isinstance(client.timeout, httpx.Timeout)
+ copied = client.copy(timeout=None)
assert copied.timeout is None
- assert isinstance(self.client.timeout, httpx.Timeout)
+ assert isinstance(client.timeout, httpx.Timeout)
def test_copy_default_headers(self) -> None:
client = Mixedbread(
@@ -136,6 +189,7 @@ def test_copy_default_headers(self) -> None:
match="`default_headers` and `set_default_headers` arguments are mutually exclusive",
):
client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"})
+ client.close()
def test_copy_default_query(self) -> None:
client = Mixedbread(
@@ -173,13 +227,15 @@ def test_copy_default_query(self) -> None:
):
client.copy(set_default_query={}, default_query={"foo": "Bar"})
- def test_copy_signature(self) -> None:
+ client.close()
+
+ def test_copy_signature(self, client: Mixedbread) -> None:
# ensure the same parameters that can be passed to the client are defined in the `.copy()` method
init_signature = inspect.signature(
# mypy doesn't like that we access the `__init__` property.
- self.client.__init__, # type: ignore[misc]
+ client.__init__, # type: ignore[misc]
)
- copy_signature = inspect.signature(self.client.copy)
+ copy_signature = inspect.signature(client.copy)
exclude_params = {"transport", "proxies", "_strict_response_validation"}
for name in init_signature.parameters.keys():
@@ -189,12 +245,13 @@ 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"
- def test_copy_build_request(self) -> None:
+ @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, client: Mixedbread) -> None:
options = FinalRequestOptions(method="get", url="/foo")
def build_request(options: FinalRequestOptions) -> None:
- client = self.client.copy()
- client._build_request(options)
+ client_copy = client.copy()
+ client_copy._build_request(options)
# ensure that the machinery is warmed up before tracing starts.
build_request(options)
@@ -251,14 +308,12 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic
print(frame)
raise AssertionError()
- def test_request_timeout(self) -> None:
- request = self.client._build_request(FinalRequestOptions(method="get", url="/foo"))
+ def test_request_timeout(self, client: Mixedbread) -> None:
+ request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT
- request = self.client._build_request(
- FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0))
- )
+ request = client._build_request(FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0)))
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == httpx.Timeout(100.0)
@@ -271,6 +326,8 @@ def test_client_timeout_option(self) -> None:
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == httpx.Timeout(0)
+ client.close()
+
def test_http_client_timeout_option(self) -> None:
# custom timeout given to the httpx client should be used
with httpx.Client(timeout=None) as http_client:
@@ -282,6 +339,8 @@ def test_http_client_timeout_option(self) -> None:
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == httpx.Timeout(None)
+ client.close()
+
# no timeout given to the httpx client should not use the httpx default
with httpx.Client() as http_client:
client = Mixedbread(
@@ -292,6 +351,8 @@ def test_http_client_timeout_option(self) -> None:
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT
+ client.close()
+
# explicitly passing the default timeout currently results in it being ignored
with httpx.Client(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client:
client = Mixedbread(
@@ -302,6 +363,8 @@ def test_http_client_timeout_option(self) -> None:
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT # our default
+ client.close()
+
async def test_invalid_http_client(self) -> None:
with pytest.raises(TypeError, match="Invalid `http_client` arg"):
async with httpx.AsyncClient() as http_client:
@@ -313,14 +376,14 @@ async def test_invalid_http_client(self) -> None:
)
def test_default_headers_option(self) -> None:
- client = Mixedbread(
+ test_client = Mixedbread(
base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"}
)
- request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
+ request = test_client._build_request(FinalRequestOptions(method="get", url="/foo"))
assert request.headers.get("x-foo") == "bar"
assert request.headers.get("x-stainless-lang") == "python"
- client2 = Mixedbread(
+ test_client2 = Mixedbread(
base_url=base_url,
api_key=api_key,
_strict_response_validation=True,
@@ -329,10 +392,23 @@ def test_default_headers_option(self) -> None:
"X-Stainless-Lang": "my-overriding-header",
},
)
- request = client2._build_request(FinalRequestOptions(method="get", url="/foo"))
+ request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo"))
assert request.headers.get("x-foo") == "stainless"
assert request.headers.get("x-stainless-lang") == "my-overriding-header"
+ test_client.close()
+ test_client2.close()
+
+ def test_validate_headers(self) -> None:
+ client = Mixedbread(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+ request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
+ assert request.headers.get("Authorization") == f"Bearer {api_key}"
+
+ with pytest.raises(MixedbreadError):
+ with update_env(**{"MXBAI_API_KEY": Omit()}):
+ client2 = Mixedbread(base_url=base_url, api_key=None, _strict_response_validation=True)
+ _ = client2
+
def test_default_query_option(self) -> None:
client = Mixedbread(
base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"}
@@ -351,8 +427,10 @@ def test_default_query_option(self) -> None:
url = httpx.URL(request.url)
assert dict(url.params) == {"foo": "baz", "query_param": "overridden"}
- def test_request_extra_json(self) -> None:
- request = self.client._build_request(
+ client.close()
+
+ def test_request_extra_json(self, client: Mixedbread) -> None:
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -363,7 +441,7 @@ def test_request_extra_json(self) -> None:
data = json.loads(request.content.decode("utf-8"))
assert data == {"foo": "bar", "baz": False}
- request = self.client._build_request(
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -374,7 +452,7 @@ def test_request_extra_json(self) -> None:
assert data == {"baz": False}
# `extra_json` takes priority over `json_data` when keys clash
- request = self.client._build_request(
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -385,8 +463,8 @@ def test_request_extra_json(self) -> None:
data = json.loads(request.content.decode("utf-8"))
assert data == {"foo": "bar", "baz": None}
- def test_request_extra_headers(self) -> None:
- request = self.client._build_request(
+ def test_request_extra_headers(self, client: Mixedbread) -> None:
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -396,7 +474,7 @@ def test_request_extra_headers(self) -> None:
assert request.headers.get("X-Foo") == "Foo"
# `extra_headers` takes priority over `default_headers` when keys clash
- request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request(
+ request = client.with_options(default_headers={"X-Bar": "true"})._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -407,8 +485,8 @@ def test_request_extra_headers(self) -> None:
)
assert request.headers.get("X-Bar") == "false"
- def test_request_extra_query(self) -> None:
- request = self.client._build_request(
+ def test_request_extra_query(self, client: Mixedbread) -> None:
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -421,7 +499,7 @@ def test_request_extra_query(self) -> None:
assert params == {"my_query_param": "Foo"}
# if both `query` and `extra_query` are given, they are merged
- request = self.client._build_request(
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -435,7 +513,7 @@ def test_request_extra_query(self) -> None:
assert params == {"bar": "1", "foo": "2"}
# `extra_query` takes priority over `query` when keys clash
- request = self.client._build_request(
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -451,7 +529,7 @@ def test_request_extra_query(self) -> None:
def test_multipart_repeating_array(self, client: Mixedbread) -> None:
request = client._build_request(
FinalRequestOptions.construct(
- method="get",
+ method="post",
url="/foo",
headers={"Content-Type": "multipart/form-data; boundary=6b7ba517decee4a450543ea6ae821c82"},
json_data={"array": ["foo", "bar"]},
@@ -478,7 +556,71 @@ def test_multipart_repeating_array(self, client: Mixedbread) -> None:
]
@pytest.mark.respx(base_url=base_url)
- def test_basic_union_response(self, respx_mock: MockRouter) -> None:
+ def test_binary_content_upload(self, respx_mock: MockRouter, client: Mixedbread) -> None:
+ respx_mock.post("/upload").mock(side_effect=mirror_request_content)
+
+ file_content = b"Hello, this is a test file."
+
+ response = client.post(
+ "/upload",
+ content=file_content,
+ cast_to=httpx.Response,
+ options={"headers": {"Content-Type": "application/octet-stream"}},
+ )
+
+ assert response.status_code == 200
+ assert response.request.headers["Content-Type"] == "application/octet-stream"
+ assert response.content == file_content
+
+ def test_binary_content_upload_with_iterator(self) -> None:
+ file_content = b"Hello, this is a test file."
+ counter = Counter()
+ iterator = _make_sync_iterator([file_content], counter=counter)
+
+ def mock_handler(request: httpx.Request) -> httpx.Response:
+ assert counter.value == 0, "the request body should not have been read"
+ return httpx.Response(200, content=request.read())
+
+ with Mixedbread(
+ base_url=base_url,
+ api_key=api_key,
+ _strict_response_validation=True,
+ http_client=httpx.Client(transport=MockTransport(handler=mock_handler)),
+ ) as client:
+ response = client.post(
+ "/upload",
+ content=iterator,
+ cast_to=httpx.Response,
+ options={"headers": {"Content-Type": "application/octet-stream"}},
+ )
+
+ assert response.status_code == 200
+ assert response.request.headers["Content-Type"] == "application/octet-stream"
+ assert response.content == file_content
+ assert counter.value == 1
+
+ @pytest.mark.respx(base_url=base_url)
+ def test_binary_content_upload_with_body_is_deprecated(self, respx_mock: MockRouter, client: Mixedbread) -> None:
+ respx_mock.post("/upload").mock(side_effect=mirror_request_content)
+
+ file_content = b"Hello, this is a test file."
+
+ with pytest.deprecated_call(
+ match="Passing raw bytes as `body` is deprecated and will be removed in a future version. Please pass raw bytes via the `content` parameter instead."
+ ):
+ response = client.post(
+ "/upload",
+ body=file_content,
+ cast_to=httpx.Response,
+ options={"headers": {"Content-Type": "application/octet-stream"}},
+ )
+
+ assert response.status_code == 200
+ assert response.request.headers["Content-Type"] == "application/octet-stream"
+ assert response.content == file_content
+
+ @pytest.mark.respx(base_url=base_url)
+ def test_basic_union_response(self, respx_mock: MockRouter, client: Mixedbread) -> None:
class Model1(BaseModel):
name: str
@@ -487,12 +629,12 @@ class Model2(BaseModel):
respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
+ response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
assert isinstance(response, Model2)
assert response.foo == "bar"
@pytest.mark.respx(base_url=base_url)
- def test_union_response_different_types(self, respx_mock: MockRouter) -> None:
+ def test_union_response_different_types(self, respx_mock: MockRouter, client: Mixedbread) -> None:
"""Union of objects with the same field name using a different type"""
class Model1(BaseModel):
@@ -503,18 +645,18 @@ class Model2(BaseModel):
respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
+ response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
assert isinstance(response, Model2)
assert response.foo == "bar"
respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1}))
- response = self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
+ response = client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
assert isinstance(response, Model1)
assert response.foo == 1
@pytest.mark.respx(base_url=base_url)
- def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None:
+ def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter, client: Mixedbread) -> None:
"""
Response that sets Content-Type to something other than application/json but returns json data
"""
@@ -530,7 +672,7 @@ class Model(BaseModel):
)
)
- response = self.client.get("/foo", cast_to=Model)
+ response = client.get("/foo", cast_to=Model)
assert isinstance(response, Model)
assert response.foo == 2
@@ -542,6 +684,8 @@ def test_base_url_setter(self) -> None:
assert client.base_url == "https://example.com/from_setter/"
+ client.close()
+
def test_base_url_env(self) -> None:
with update_env(MIXEDBREAD_BASE_URL="http://localhost:5000/from/env"):
client = Mixedbread(api_key=api_key, _strict_response_validation=True)
@@ -555,7 +699,9 @@ def test_base_url_env(self) -> None:
client = Mixedbread(
base_url=None, api_key=api_key, _strict_response_validation=True, environment="production"
)
- assert str(client.base_url).startswith("https://api.mixedbread.ai")
+ assert str(client.base_url).startswith("https://api.mixedbread.com")
+
+ client.close()
@pytest.mark.parametrize(
"client",
@@ -581,6 +727,7 @@ def test_base_url_trailing_slash(self, client: Mixedbread) -> None:
),
)
assert request.url == "http://localhost:5000/custom/path/foo"
+ client.close()
@pytest.mark.parametrize(
"client",
@@ -606,6 +753,7 @@ def test_base_url_no_trailing_slash(self, client: Mixedbread) -> None:
),
)
assert request.url == "http://localhost:5000/custom/path/foo"
+ client.close()
@pytest.mark.parametrize(
"client",
@@ -631,35 +779,36 @@ def test_absolute_request_url(self, client: Mixedbread) -> None:
),
)
assert request.url == "https://myapi.com/foo"
+ client.close()
def test_copied_client_does_not_close_http(self) -> None:
- client = Mixedbread(base_url=base_url, api_key=api_key, _strict_response_validation=True)
- assert not client.is_closed()
+ test_client = Mixedbread(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+ assert not test_client.is_closed()
- copied = client.copy()
- assert copied is not client
+ copied = test_client.copy()
+ assert copied is not test_client
del copied
- assert not client.is_closed()
+ assert not test_client.is_closed()
def test_client_context_manager(self) -> None:
- client = Mixedbread(base_url=base_url, api_key=api_key, _strict_response_validation=True)
- with client as c2:
- assert c2 is client
+ test_client = Mixedbread(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+ with test_client as c2:
+ assert c2 is test_client
assert not c2.is_closed()
- assert not client.is_closed()
- assert client.is_closed()
+ assert not test_client.is_closed()
+ assert test_client.is_closed()
@pytest.mark.respx(base_url=base_url)
- def test_client_response_validation_error(self, respx_mock: MockRouter) -> None:
+ def test_client_response_validation_error(self, respx_mock: MockRouter, client: Mixedbread) -> None:
class Model(BaseModel):
foo: str
respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}}))
with pytest.raises(APIResponseValidationError) as exc:
- self.client.get("/foo", cast_to=Model)
+ client.get("/foo", cast_to=Model)
assert isinstance(exc.value.__cause__, ValidationError)
@@ -681,11 +830,14 @@ class Model(BaseModel):
with pytest.raises(APIResponseValidationError):
strict_client.get("/foo", cast_to=Model)
- client = Mixedbread(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+ non_strict_client = Mixedbread(base_url=base_url, api_key=api_key, _strict_response_validation=False)
- response = client.get("/foo", cast_to=Model)
+ response = non_strict_client.get("/foo", cast_to=Model)
assert isinstance(response, str) # type: ignore[unreachable]
+ strict_client.close()
+ non_strict_client.close()
+
@pytest.mark.parametrize(
"remaining_retries,retry_after,timeout",
[
@@ -708,9 +860,9 @@ class Model(BaseModel):
],
)
@mock.patch("time.time", mock.MagicMock(return_value=1696004797))
- def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None:
- client = Mixedbread(base_url=base_url, api_key=api_key, _strict_response_validation=True)
-
+ def test_parse_retry_after_header(
+ self, remaining_retries: int, retry_after: str, timeout: float, client: Mixedbread
+ ) -> None:
headers = httpx.Headers({"retry-after": retry_after})
options = FinalRequestOptions(method="get", url="/foo", max_retries=3)
calculated = client._calculate_retry_timeout(remaining_retries, options, headers)
@@ -718,33 +870,22 @@ def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str
@mock.patch("mixedbread._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
- def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None:
- respx_mock.post("/v1/files").mock(side_effect=httpx.TimeoutException("Test timeout error"))
+ def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter, client: Mixedbread) -> None:
+ respx_mock.post("/v1/stores").mock(side_effect=httpx.TimeoutException("Test timeout error"))
with pytest.raises(APITimeoutError):
- self.client.post(
- "/v1/files",
- body=cast(object, dict(file=b"raw file contents")),
- cast_to=httpx.Response,
- options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
- )
+ client.stores.with_streaming_response.create().__enter__()
- assert _get_open_connections(self.client) == 0
+ assert _get_open_connections(client) == 0
@mock.patch("mixedbread._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
- def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None:
- respx_mock.post("/v1/files").mock(return_value=httpx.Response(500))
+ def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter, client: Mixedbread) -> None:
+ respx_mock.post("/v1/stores").mock(return_value=httpx.Response(500))
with pytest.raises(APIStatusError):
- self.client.post(
- "/v1/files",
- body=cast(object, dict(file=b"raw file contents")),
- cast_to=httpx.Response,
- options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
- )
-
- assert _get_open_connections(self.client) == 0
+ client.stores.with_streaming_response.create().__enter__()
+ assert _get_open_connections(client) == 0
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
@mock.patch("mixedbread._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@@ -770,9 +911,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
return httpx.Response(500)
return httpx.Response(200)
- respx_mock.post("/v1/files").mock(side_effect=retry_handler)
+ respx_mock.post("/v1/stores").mock(side_effect=retry_handler)
- response = client.files.with_raw_response.create(file=b"raw file contents")
+ response = client.stores.with_raw_response.create()
assert response.retries_taken == failures_before_success
assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success
@@ -794,11 +935,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
return httpx.Response(500)
return httpx.Response(200)
- respx_mock.post("/v1/files").mock(side_effect=retry_handler)
+ respx_mock.post("/v1/stores").mock(side_effect=retry_handler)
- response = client.files.with_raw_response.create(
- file=b"raw file contents", extra_headers={"x-stainless-retry-count": Omit()}
- )
+ response = client.stores.with_raw_response.create(extra_headers={"x-stainless-retry-count": Omit()})
assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0
@@ -819,65 +958,114 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
return httpx.Response(500)
return httpx.Response(200)
- respx_mock.post("/v1/files").mock(side_effect=retry_handler)
+ respx_mock.post("/v1/stores").mock(side_effect=retry_handler)
- response = client.files.with_raw_response.create(
- file=b"raw file contents", extra_headers={"x-stainless-retry-count": "42"}
- )
+ response = client.stores.with_raw_response.create(extra_headers={"x-stainless-retry-count": "42"})
assert response.http_request.headers.get("x-stainless-retry-count") == "42"
+ def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None:
+ # Test that the proxy environment variables are set correctly
+ monkeypatch.setenv("HTTPS_PROXY", "https://example.org")
+ # Delete in case our environment has any proxy env vars set
+ monkeypatch.delenv("HTTP_PROXY", raising=False)
+ monkeypatch.delenv("ALL_PROXY", raising=False)
+ monkeypatch.delenv("NO_PROXY", raising=False)
+ monkeypatch.delenv("http_proxy", raising=False)
+ monkeypatch.delenv("https_proxy", raising=False)
+ monkeypatch.delenv("all_proxy", raising=False)
+ monkeypatch.delenv("no_proxy", raising=False)
+
+ client = DefaultHttpxClient()
+
+ mounts = tuple(client._mounts.items())
+ assert len(mounts) == 1
+ assert mounts[0][0].pattern == "https://"
+
+ @pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning")
+ def test_default_client_creation(self) -> None:
+ # Ensure that the client can be initialized without any exceptions
+ DefaultHttpxClient(
+ verify=True,
+ cert=None,
+ trust_env=True,
+ http1=True,
+ http2=False,
+ limits=httpx.Limits(max_connections=100, max_keepalive_connections=20),
+ )
-class TestAsyncMixedbread:
- client = AsyncMixedbread(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+ @pytest.mark.respx(base_url=base_url)
+ def test_follow_redirects(self, respx_mock: MockRouter, client: Mixedbread) -> None:
+ # Test that the default follow_redirects=True allows following redirects
+ respx_mock.post("/redirect").mock(
+ return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
+ )
+ respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"}))
+
+ response = client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response)
+ assert response.status_code == 200
+ assert response.json() == {"status": "ok"}
+
+ @pytest.mark.respx(base_url=base_url)
+ def test_follow_redirects_disabled(self, respx_mock: MockRouter, client: Mixedbread) -> None:
+ # Test that follow_redirects=False prevents following redirects
+ respx_mock.post("/redirect").mock(
+ return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
+ )
+
+ with pytest.raises(APIStatusError) as exc_info:
+ client.post("/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response)
+
+ assert exc_info.value.response.status_code == 302
+ assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected"
+
+class TestAsyncMixedbread:
@pytest.mark.respx(base_url=base_url)
- @pytest.mark.asyncio
- async def test_raw_response(self, respx_mock: MockRouter) -> None:
+ async def test_raw_response(self, respx_mock: MockRouter, async_client: AsyncMixedbread) -> None:
respx_mock.post("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- response = await self.client.post("/foo", cast_to=httpx.Response)
+ response = await async_client.post("/foo", cast_to=httpx.Response)
assert response.status_code == 200
assert isinstance(response, httpx.Response)
assert response.json() == {"foo": "bar"}
@pytest.mark.respx(base_url=base_url)
- @pytest.mark.asyncio
- async def test_raw_response_for_binary(self, respx_mock: MockRouter) -> None:
+ async def test_raw_response_for_binary(self, respx_mock: MockRouter, async_client: AsyncMixedbread) -> None:
respx_mock.post("/foo").mock(
return_value=httpx.Response(200, headers={"Content-Type": "application/binary"}, content='{"foo": "bar"}')
)
- response = await self.client.post("/foo", cast_to=httpx.Response)
+ response = await async_client.post("/foo", cast_to=httpx.Response)
assert response.status_code == 200
assert isinstance(response, httpx.Response)
assert response.json() == {"foo": "bar"}
- def test_copy(self) -> None:
- copied = self.client.copy()
- assert id(copied) != id(self.client)
+ def test_copy(self, async_client: AsyncMixedbread) -> None:
+ copied = async_client.copy()
+ assert id(copied) != id(async_client)
- copied = self.client.copy(api_key="another My API Key")
+ copied = async_client.copy(api_key="another My API Key")
assert copied.api_key == "another My API Key"
- assert self.client.api_key == "My API Key"
+ assert async_client.api_key == "My API Key"
- def test_copy_default_options(self) -> None:
+ def test_copy_default_options(self, async_client: AsyncMixedbread) -> None:
# options that have a default are overridden correctly
- copied = self.client.copy(max_retries=7)
+ copied = async_client.copy(max_retries=7)
assert copied.max_retries == 7
- assert self.client.max_retries == 2
+ assert async_client.max_retries == 2
copied2 = copied.copy(max_retries=6)
assert copied2.max_retries == 6
assert copied.max_retries == 7
# timeout
- assert isinstance(self.client.timeout, httpx.Timeout)
- copied = self.client.copy(timeout=None)
+ assert isinstance(async_client.timeout, httpx.Timeout)
+ copied = async_client.copy(timeout=None)
assert copied.timeout is None
- assert isinstance(self.client.timeout, httpx.Timeout)
+ assert isinstance(async_client.timeout, httpx.Timeout)
- def test_copy_default_headers(self) -> None:
+ async def test_copy_default_headers(self) -> None:
client = AsyncMixedbread(
base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"}
)
@@ -910,8 +1098,9 @@ def test_copy_default_headers(self) -> None:
match="`default_headers` and `set_default_headers` arguments are mutually exclusive",
):
client.copy(set_default_headers={}, default_headers={"X-Foo": "Bar"})
+ await client.close()
- def test_copy_default_query(self) -> None:
+ async def test_copy_default_query(self) -> None:
client = AsyncMixedbread(
base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"foo": "bar"}
)
@@ -947,13 +1136,15 @@ def test_copy_default_query(self) -> None:
):
client.copy(set_default_query={}, default_query={"foo": "Bar"})
- def test_copy_signature(self) -> None:
+ await client.close()
+
+ def test_copy_signature(self, async_client: AsyncMixedbread) -> None:
# ensure the same parameters that can be passed to the client are defined in the `.copy()` method
init_signature = inspect.signature(
# mypy doesn't like that we access the `__init__` property.
- self.client.__init__, # type: ignore[misc]
+ async_client.__init__, # type: ignore[misc]
)
- copy_signature = inspect.signature(self.client.copy)
+ copy_signature = inspect.signature(async_client.copy)
exclude_params = {"transport", "proxies", "_strict_response_validation"}
for name in init_signature.parameters.keys():
@@ -963,12 +1154,13 @@ 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"
- def test_copy_build_request(self) -> None:
+ @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, async_client: AsyncMixedbread) -> None:
options = FinalRequestOptions(method="get", url="/foo")
def build_request(options: FinalRequestOptions) -> None:
- client = self.client.copy()
- client._build_request(options)
+ client_copy = async_client.copy()
+ client_copy._build_request(options)
# ensure that the machinery is warmed up before tracing starts.
build_request(options)
@@ -1025,12 +1217,12 @@ def add_leak(leaks: list[tracemalloc.StatisticDiff], diff: tracemalloc.Statistic
print(frame)
raise AssertionError()
- async def test_request_timeout(self) -> None:
- request = self.client._build_request(FinalRequestOptions(method="get", url="/foo"))
+ async def test_request_timeout(self, async_client: AsyncMixedbread) -> None:
+ request = async_client._build_request(FinalRequestOptions(method="get", url="/foo"))
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT
- request = self.client._build_request(
+ request = async_client._build_request(
FinalRequestOptions(method="get", url="/foo", timeout=httpx.Timeout(100.0))
)
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
@@ -1045,6 +1237,8 @@ async def test_client_timeout_option(self) -> None:
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == httpx.Timeout(0)
+ await client.close()
+
async def test_http_client_timeout_option(self) -> None:
# custom timeout given to the httpx client should be used
async with httpx.AsyncClient(timeout=None) as http_client:
@@ -1056,6 +1250,8 @@ async def test_http_client_timeout_option(self) -> None:
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == httpx.Timeout(None)
+ await client.close()
+
# no timeout given to the httpx client should not use the httpx default
async with httpx.AsyncClient() as http_client:
client = AsyncMixedbread(
@@ -1066,6 +1262,8 @@ async def test_http_client_timeout_option(self) -> None:
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT
+ await client.close()
+
# explicitly passing the default timeout currently results in it being ignored
async with httpx.AsyncClient(timeout=HTTPX_DEFAULT_TIMEOUT) as http_client:
client = AsyncMixedbread(
@@ -1076,6 +1274,8 @@ async def test_http_client_timeout_option(self) -> None:
timeout = httpx.Timeout(**request.extensions["timeout"]) # type: ignore
assert timeout == DEFAULT_TIMEOUT # our default
+ await client.close()
+
def test_invalid_http_client(self) -> None:
with pytest.raises(TypeError, match="Invalid `http_client` arg"):
with httpx.Client() as http_client:
@@ -1086,15 +1286,15 @@ def test_invalid_http_client(self) -> None:
http_client=cast(Any, http_client),
)
- def test_default_headers_option(self) -> None:
- client = AsyncMixedbread(
+ async def test_default_headers_option(self) -> None:
+ test_client = AsyncMixedbread(
base_url=base_url, api_key=api_key, _strict_response_validation=True, default_headers={"X-Foo": "bar"}
)
- request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
+ request = test_client._build_request(FinalRequestOptions(method="get", url="/foo"))
assert request.headers.get("x-foo") == "bar"
assert request.headers.get("x-stainless-lang") == "python"
- client2 = AsyncMixedbread(
+ test_client2 = AsyncMixedbread(
base_url=base_url,
api_key=api_key,
_strict_response_validation=True,
@@ -1103,11 +1303,24 @@ def test_default_headers_option(self) -> None:
"X-Stainless-Lang": "my-overriding-header",
},
)
- request = client2._build_request(FinalRequestOptions(method="get", url="/foo"))
+ request = test_client2._build_request(FinalRequestOptions(method="get", url="/foo"))
assert request.headers.get("x-foo") == "stainless"
assert request.headers.get("x-stainless-lang") == "my-overriding-header"
- def test_default_query_option(self) -> None:
+ await test_client.close()
+ await test_client2.close()
+
+ def test_validate_headers(self) -> None:
+ client = AsyncMixedbread(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+ request = client._build_request(FinalRequestOptions(method="get", url="/foo"))
+ assert request.headers.get("Authorization") == f"Bearer {api_key}"
+
+ with pytest.raises(MixedbreadError):
+ with update_env(**{"MXBAI_API_KEY": Omit()}):
+ client2 = AsyncMixedbread(base_url=base_url, api_key=None, _strict_response_validation=True)
+ _ = client2
+
+ async def test_default_query_option(self) -> None:
client = AsyncMixedbread(
base_url=base_url, api_key=api_key, _strict_response_validation=True, default_query={"query_param": "bar"}
)
@@ -1125,8 +1338,10 @@ def test_default_query_option(self) -> None:
url = httpx.URL(request.url)
assert dict(url.params) == {"foo": "baz", "query_param": "overridden"}
- def test_request_extra_json(self) -> None:
- request = self.client._build_request(
+ await client.close()
+
+ def test_request_extra_json(self, client: Mixedbread) -> None:
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -1137,7 +1352,7 @@ def test_request_extra_json(self) -> None:
data = json.loads(request.content.decode("utf-8"))
assert data == {"foo": "bar", "baz": False}
- request = self.client._build_request(
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -1148,7 +1363,7 @@ def test_request_extra_json(self) -> None:
assert data == {"baz": False}
# `extra_json` takes priority over `json_data` when keys clash
- request = self.client._build_request(
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -1159,8 +1374,8 @@ def test_request_extra_json(self) -> None:
data = json.loads(request.content.decode("utf-8"))
assert data == {"foo": "bar", "baz": None}
- def test_request_extra_headers(self) -> None:
- request = self.client._build_request(
+ def test_request_extra_headers(self, client: Mixedbread) -> None:
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -1170,7 +1385,7 @@ def test_request_extra_headers(self) -> None:
assert request.headers.get("X-Foo") == "Foo"
# `extra_headers` takes priority over `default_headers` when keys clash
- request = self.client.with_options(default_headers={"X-Bar": "true"})._build_request(
+ request = client.with_options(default_headers={"X-Bar": "true"})._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -1181,8 +1396,8 @@ def test_request_extra_headers(self) -> None:
)
assert request.headers.get("X-Bar") == "false"
- def test_request_extra_query(self) -> None:
- request = self.client._build_request(
+ def test_request_extra_query(self, client: Mixedbread) -> None:
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -1195,7 +1410,7 @@ def test_request_extra_query(self) -> None:
assert params == {"my_query_param": "Foo"}
# if both `query` and `extra_query` are given, they are merged
- request = self.client._build_request(
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -1209,7 +1424,7 @@ def test_request_extra_query(self) -> None:
assert params == {"bar": "1", "foo": "2"}
# `extra_query` takes priority over `query` when keys clash
- request = self.client._build_request(
+ request = client._build_request(
FinalRequestOptions(
method="post",
url="/foo",
@@ -1225,7 +1440,7 @@ def test_request_extra_query(self) -> None:
def test_multipart_repeating_array(self, async_client: AsyncMixedbread) -> None:
request = async_client._build_request(
FinalRequestOptions.construct(
- method="get",
+ method="post",
url="/foo",
headers={"Content-Type": "multipart/form-data; boundary=6b7ba517decee4a450543ea6ae821c82"},
json_data={"array": ["foo", "bar"]},
@@ -1252,7 +1467,73 @@ def test_multipart_repeating_array(self, async_client: AsyncMixedbread) -> None:
]
@pytest.mark.respx(base_url=base_url)
- async def test_basic_union_response(self, respx_mock: MockRouter) -> None:
+ async def test_binary_content_upload(self, respx_mock: MockRouter, async_client: AsyncMixedbread) -> None:
+ respx_mock.post("/upload").mock(side_effect=mirror_request_content)
+
+ file_content = b"Hello, this is a test file."
+
+ response = await async_client.post(
+ "/upload",
+ content=file_content,
+ cast_to=httpx.Response,
+ options={"headers": {"Content-Type": "application/octet-stream"}},
+ )
+
+ assert response.status_code == 200
+ assert response.request.headers["Content-Type"] == "application/octet-stream"
+ assert response.content == file_content
+
+ async def test_binary_content_upload_with_asynciterator(self) -> None:
+ file_content = b"Hello, this is a test file."
+ counter = Counter()
+ iterator = _make_async_iterator([file_content], counter=counter)
+
+ async def mock_handler(request: httpx.Request) -> httpx.Response:
+ assert counter.value == 0, "the request body should not have been read"
+ return httpx.Response(200, content=await request.aread())
+
+ async with AsyncMixedbread(
+ base_url=base_url,
+ api_key=api_key,
+ _strict_response_validation=True,
+ http_client=httpx.AsyncClient(transport=MockTransport(handler=mock_handler)),
+ ) as client:
+ response = await client.post(
+ "/upload",
+ content=iterator,
+ cast_to=httpx.Response,
+ options={"headers": {"Content-Type": "application/octet-stream"}},
+ )
+
+ assert response.status_code == 200
+ assert response.request.headers["Content-Type"] == "application/octet-stream"
+ assert response.content == file_content
+ assert counter.value == 1
+
+ @pytest.mark.respx(base_url=base_url)
+ async def test_binary_content_upload_with_body_is_deprecated(
+ self, respx_mock: MockRouter, async_client: AsyncMixedbread
+ ) -> None:
+ respx_mock.post("/upload").mock(side_effect=mirror_request_content)
+
+ file_content = b"Hello, this is a test file."
+
+ with pytest.deprecated_call(
+ match="Passing raw bytes as `body` is deprecated and will be removed in a future version. Please pass raw bytes via the `content` parameter instead."
+ ):
+ response = await async_client.post(
+ "/upload",
+ body=file_content,
+ cast_to=httpx.Response,
+ options={"headers": {"Content-Type": "application/octet-stream"}},
+ )
+
+ assert response.status_code == 200
+ assert response.request.headers["Content-Type"] == "application/octet-stream"
+ assert response.content == file_content
+
+ @pytest.mark.respx(base_url=base_url)
+ async def test_basic_union_response(self, respx_mock: MockRouter, async_client: AsyncMixedbread) -> None:
class Model1(BaseModel):
name: str
@@ -1261,12 +1542,12 @@ class Model2(BaseModel):
respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
+ response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
assert isinstance(response, Model2)
assert response.foo == "bar"
@pytest.mark.respx(base_url=base_url)
- async def test_union_response_different_types(self, respx_mock: MockRouter) -> None:
+ async def test_union_response_different_types(self, respx_mock: MockRouter, async_client: AsyncMixedbread) -> None:
"""Union of objects with the same field name using a different type"""
class Model1(BaseModel):
@@ -1277,18 +1558,20 @@ class Model2(BaseModel):
respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
- response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
+ response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
assert isinstance(response, Model2)
assert response.foo == "bar"
respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": 1}))
- response = await self.client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
+ response = await async_client.get("/foo", cast_to=cast(Any, Union[Model1, Model2]))
assert isinstance(response, Model1)
assert response.foo == 1
@pytest.mark.respx(base_url=base_url)
- async def test_non_application_json_content_type_for_json_data(self, respx_mock: MockRouter) -> None:
+ async def test_non_application_json_content_type_for_json_data(
+ self, respx_mock: MockRouter, async_client: AsyncMixedbread
+ ) -> None:
"""
Response that sets Content-Type to something other than application/json but returns json data
"""
@@ -1304,11 +1587,11 @@ class Model(BaseModel):
)
)
- response = await self.client.get("/foo", cast_to=Model)
+ response = await async_client.get("/foo", cast_to=Model)
assert isinstance(response, Model)
assert response.foo == 2
- def test_base_url_setter(self) -> None:
+ async def test_base_url_setter(self) -> None:
client = AsyncMixedbread(
base_url="https://example.com/from_init", api_key=api_key, _strict_response_validation=True
)
@@ -1318,7 +1601,9 @@ def test_base_url_setter(self) -> None:
assert client.base_url == "https://example.com/from_setter/"
- def test_base_url_env(self) -> None:
+ await client.close()
+
+ async def test_base_url_env(self) -> None:
with update_env(MIXEDBREAD_BASE_URL="http://localhost:5000/from/env"):
client = AsyncMixedbread(api_key=api_key, _strict_response_validation=True)
assert client.base_url == "http://localhost:5000/from/env/"
@@ -1331,7 +1616,9 @@ def test_base_url_env(self) -> None:
client = AsyncMixedbread(
base_url=None, api_key=api_key, _strict_response_validation=True, environment="production"
)
- assert str(client.base_url).startswith("https://api.mixedbread.ai")
+ assert str(client.base_url).startswith("https://api.mixedbread.com")
+
+ await client.close()
@pytest.mark.parametrize(
"client",
@@ -1348,7 +1635,7 @@ def test_base_url_env(self) -> None:
],
ids=["standard", "custom http client"],
)
- def test_base_url_trailing_slash(self, client: AsyncMixedbread) -> None:
+ async def test_base_url_trailing_slash(self, client: AsyncMixedbread) -> None:
request = client._build_request(
FinalRequestOptions(
method="post",
@@ -1357,6 +1644,7 @@ def test_base_url_trailing_slash(self, client: AsyncMixedbread) -> None:
),
)
assert request.url == "http://localhost:5000/custom/path/foo"
+ await client.close()
@pytest.mark.parametrize(
"client",
@@ -1373,7 +1661,7 @@ def test_base_url_trailing_slash(self, client: AsyncMixedbread) -> None:
],
ids=["standard", "custom http client"],
)
- def test_base_url_no_trailing_slash(self, client: AsyncMixedbread) -> None:
+ async def test_base_url_no_trailing_slash(self, client: AsyncMixedbread) -> None:
request = client._build_request(
FinalRequestOptions(
method="post",
@@ -1382,6 +1670,7 @@ def test_base_url_no_trailing_slash(self, client: AsyncMixedbread) -> None:
),
)
assert request.url == "http://localhost:5000/custom/path/foo"
+ await client.close()
@pytest.mark.parametrize(
"client",
@@ -1398,7 +1687,7 @@ def test_base_url_no_trailing_slash(self, client: AsyncMixedbread) -> None:
],
ids=["standard", "custom http client"],
)
- def test_absolute_request_url(self, client: AsyncMixedbread) -> None:
+ async def test_absolute_request_url(self, client: AsyncMixedbread) -> None:
request = client._build_request(
FinalRequestOptions(
method="post",
@@ -1407,37 +1696,39 @@ def test_absolute_request_url(self, client: AsyncMixedbread) -> None:
),
)
assert request.url == "https://myapi.com/foo"
+ await client.close()
async def test_copied_client_does_not_close_http(self) -> None:
- client = AsyncMixedbread(base_url=base_url, api_key=api_key, _strict_response_validation=True)
- assert not client.is_closed()
+ test_client = AsyncMixedbread(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+ assert not test_client.is_closed()
- copied = client.copy()
- assert copied is not client
+ copied = test_client.copy()
+ assert copied is not test_client
del copied
await asyncio.sleep(0.2)
- assert not client.is_closed()
+ assert not test_client.is_closed()
async def test_client_context_manager(self) -> None:
- client = AsyncMixedbread(base_url=base_url, api_key=api_key, _strict_response_validation=True)
- async with client as c2:
- assert c2 is client
+ test_client = AsyncMixedbread(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+ async with test_client as c2:
+ assert c2 is test_client
assert not c2.is_closed()
- assert not client.is_closed()
- assert client.is_closed()
+ assert not test_client.is_closed()
+ assert test_client.is_closed()
@pytest.mark.respx(base_url=base_url)
- @pytest.mark.asyncio
- async def test_client_response_validation_error(self, respx_mock: MockRouter) -> None:
+ async def test_client_response_validation_error(
+ self, respx_mock: MockRouter, async_client: AsyncMixedbread
+ ) -> None:
class Model(BaseModel):
foo: str
respx_mock.get("/foo").mock(return_value=httpx.Response(200, json={"foo": {"invalid": True}}))
with pytest.raises(APIResponseValidationError) as exc:
- await self.client.get("/foo", cast_to=Model)
+ await async_client.get("/foo", cast_to=Model)
assert isinstance(exc.value.__cause__, ValidationError)
@@ -1448,7 +1739,6 @@ async def test_client_max_retries_validation(self) -> None:
)
@pytest.mark.respx(base_url=base_url)
- @pytest.mark.asyncio
async def test_received_text_for_expected_json(self, respx_mock: MockRouter) -> None:
class Model(BaseModel):
name: str
@@ -1460,11 +1750,14 @@ class Model(BaseModel):
with pytest.raises(APIResponseValidationError):
await strict_client.get("/foo", cast_to=Model)
- client = AsyncMixedbread(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+ non_strict_client = AsyncMixedbread(base_url=base_url, api_key=api_key, _strict_response_validation=False)
- response = await client.get("/foo", cast_to=Model)
+ response = await non_strict_client.get("/foo", cast_to=Model)
assert isinstance(response, str) # type: ignore[unreachable]
+ await strict_client.close()
+ await non_strict_client.close()
+
@pytest.mark.parametrize(
"remaining_retries,retry_after,timeout",
[
@@ -1487,49 +1780,40 @@ class Model(BaseModel):
],
)
@mock.patch("time.time", mock.MagicMock(return_value=1696004797))
- @pytest.mark.asyncio
- async def test_parse_retry_after_header(self, remaining_retries: int, retry_after: str, timeout: float) -> None:
- client = AsyncMixedbread(base_url=base_url, api_key=api_key, _strict_response_validation=True)
-
+ async def test_parse_retry_after_header(
+ self, remaining_retries: int, retry_after: str, timeout: float, async_client: AsyncMixedbread
+ ) -> None:
headers = httpx.Headers({"retry-after": retry_after})
options = FinalRequestOptions(method="get", url="/foo", max_retries=3)
- calculated = client._calculate_retry_timeout(remaining_retries, options, headers)
+ calculated = async_client._calculate_retry_timeout(remaining_retries, options, headers)
assert calculated == pytest.approx(timeout, 0.5 * 0.875) # pyright: ignore[reportUnknownMemberType]
@mock.patch("mixedbread._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
- async def test_retrying_timeout_errors_doesnt_leak(self, respx_mock: MockRouter) -> None:
- respx_mock.post("/v1/files").mock(side_effect=httpx.TimeoutException("Test timeout error"))
+ async def test_retrying_timeout_errors_doesnt_leak(
+ self, respx_mock: MockRouter, async_client: AsyncMixedbread
+ ) -> None:
+ respx_mock.post("/v1/stores").mock(side_effect=httpx.TimeoutException("Test timeout error"))
with pytest.raises(APITimeoutError):
- await self.client.post(
- "/v1/files",
- body=cast(object, dict(file=b"raw file contents")),
- cast_to=httpx.Response,
- options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
- )
+ await async_client.stores.with_streaming_response.create().__aenter__()
- assert _get_open_connections(self.client) == 0
+ assert _get_open_connections(async_client) == 0
@mock.patch("mixedbread._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
- async def test_retrying_status_errors_doesnt_leak(self, respx_mock: MockRouter) -> None:
- respx_mock.post("/v1/files").mock(return_value=httpx.Response(500))
+ async def test_retrying_status_errors_doesnt_leak(
+ self, respx_mock: MockRouter, async_client: AsyncMixedbread
+ ) -> None:
+ respx_mock.post("/v1/stores").mock(return_value=httpx.Response(500))
with pytest.raises(APIStatusError):
- await self.client.post(
- "/v1/files",
- body=cast(object, dict(file=b"raw file contents")),
- cast_to=httpx.Response,
- options={"headers": {RAW_RESPONSE_HEADER: "stream"}},
- )
-
- assert _get_open_connections(self.client) == 0
+ await async_client.stores.with_streaming_response.create().__aenter__()
+ assert _get_open_connections(async_client) == 0
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
@mock.patch("mixedbread._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
- @pytest.mark.asyncio
@pytest.mark.parametrize("failure_mode", ["status", "exception"])
async def test_retries_taken(
self,
@@ -1551,9 +1835,9 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
return httpx.Response(500)
return httpx.Response(200)
- respx_mock.post("/v1/files").mock(side_effect=retry_handler)
+ respx_mock.post("/v1/stores").mock(side_effect=retry_handler)
- response = await client.files.with_raw_response.create(file=b"raw file contents")
+ response = await client.stores.with_raw_response.create()
assert response.retries_taken == failures_before_success
assert int(response.http_request.headers.get("x-stainless-retry-count")) == failures_before_success
@@ -1561,7 +1845,6 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
@mock.patch("mixedbread._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
- @pytest.mark.asyncio
async def test_omit_retry_count_header(
self, async_client: AsyncMixedbread, failures_before_success: int, respx_mock: MockRouter
) -> None:
@@ -1576,18 +1859,15 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
return httpx.Response(500)
return httpx.Response(200)
- respx_mock.post("/v1/files").mock(side_effect=retry_handler)
+ respx_mock.post("/v1/stores").mock(side_effect=retry_handler)
- response = await client.files.with_raw_response.create(
- file=b"raw file contents", extra_headers={"x-stainless-retry-count": Omit()}
- )
+ response = await client.stores.with_raw_response.create(extra_headers={"x-stainless-retry-count": Omit()})
assert len(response.http_request.headers.get_list("x-stainless-retry-count")) == 0
@pytest.mark.parametrize("failures_before_success", [0, 2, 4])
@mock.patch("mixedbread._base_client.BaseClient._calculate_retry_timeout", _low_retry_timeout)
@pytest.mark.respx(base_url=base_url)
- @pytest.mark.asyncio
async def test_overwrite_retry_count_header(
self, async_client: AsyncMixedbread, failures_before_success: int, respx_mock: MockRouter
) -> None:
@@ -1602,45 +1882,69 @@ def retry_handler(_request: httpx.Request) -> httpx.Response:
return httpx.Response(500)
return httpx.Response(200)
- respx_mock.post("/v1/files").mock(side_effect=retry_handler)
+ respx_mock.post("/v1/stores").mock(side_effect=retry_handler)
- response = await client.files.with_raw_response.create(
- file=b"raw file contents", extra_headers={"x-stainless-retry-count": "42"}
- )
+ response = await client.stores.with_raw_response.create(extra_headers={"x-stainless-retry-count": "42"})
assert response.http_request.headers.get("x-stainless-retry-count") == "42"
- def test_get_platform(self) -> None:
- # A previous implementation of asyncify could leave threads unterminated when
- # used with nest_asyncio.
- #
- # Since nest_asyncio.apply() is global and cannot be un-applied, this
- # test is run in a separate process to avoid affecting other tests.
- test_code = dedent("""
- import asyncio
- import nest_asyncio
- import threading
-
- from mixedbread._utils import asyncify
- from mixedbread._base_client import get_platform
-
- async def test_main() -> None:
- result = await asyncify(get_platform)()
- print(result)
- for thread in threading.enumerate():
- print(thread.name)
-
- nest_asyncio.apply()
- asyncio.run(test_main())
- """)
- with subprocess.Popen(
- [sys.executable, "-c", test_code],
- text=True,
- ) as process:
- try:
- process.wait(2)
- if process.returncode:
- raise AssertionError("calling get_platform using asyncify resulted in a non-zero exit code")
- except subprocess.TimeoutExpired as e:
- process.kill()
- raise AssertionError("calling get_platform using asyncify resulted in a hung process") from e
+ async def test_get_platform(self) -> None:
+ platform = await asyncify(get_platform)()
+ assert isinstance(platform, (str, OtherPlatform))
+
+ async def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None:
+ # Test that the proxy environment variables are set correctly
+ monkeypatch.setenv("HTTPS_PROXY", "https://example.org")
+ # Delete in case our environment has any proxy env vars set
+ monkeypatch.delenv("HTTP_PROXY", raising=False)
+ monkeypatch.delenv("ALL_PROXY", raising=False)
+ monkeypatch.delenv("NO_PROXY", raising=False)
+ monkeypatch.delenv("http_proxy", raising=False)
+ monkeypatch.delenv("https_proxy", raising=False)
+ monkeypatch.delenv("all_proxy", raising=False)
+ monkeypatch.delenv("no_proxy", raising=False)
+
+ client = DefaultAsyncHttpxClient()
+
+ mounts = tuple(client._mounts.items())
+ assert len(mounts) == 1
+ assert mounts[0][0].pattern == "https://"
+
+ @pytest.mark.filterwarnings("ignore:.*deprecated.*:DeprecationWarning")
+ async def test_default_client_creation(self) -> None:
+ # Ensure that the client can be initialized without any exceptions
+ DefaultAsyncHttpxClient(
+ verify=True,
+ cert=None,
+ trust_env=True,
+ http1=True,
+ http2=False,
+ limits=httpx.Limits(max_connections=100, max_keepalive_connections=20),
+ )
+
+ @pytest.mark.respx(base_url=base_url)
+ async def test_follow_redirects(self, respx_mock: MockRouter, async_client: AsyncMixedbread) -> None:
+ # Test that the default follow_redirects=True allows following redirects
+ respx_mock.post("/redirect").mock(
+ return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
+ )
+ respx_mock.get("/redirected").mock(return_value=httpx.Response(200, json={"status": "ok"}))
+
+ response = await async_client.post("/redirect", body={"key": "value"}, cast_to=httpx.Response)
+ assert response.status_code == 200
+ assert response.json() == {"status": "ok"}
+
+ @pytest.mark.respx(base_url=base_url)
+ async def test_follow_redirects_disabled(self, respx_mock: MockRouter, async_client: AsyncMixedbread) -> None:
+ # Test that follow_redirects=False prevents following redirects
+ respx_mock.post("/redirect").mock(
+ return_value=httpx.Response(302, headers={"Location": f"{base_url}/redirected"})
+ )
+
+ with pytest.raises(APIStatusError) as exc_info:
+ await async_client.post(
+ "/redirect", body={"key": "value"}, options={"follow_redirects": False}, cast_to=httpx.Response
+ )
+
+ assert exc_info.value.response.status_code == 302
+ assert exc_info.value.response.headers["Location"] == f"{base_url}/redirected"
diff --git a/tests/test_models.py b/tests/test_models.py
index 0176e2e8..a8c78064 100644
--- a/tests/test_models.py
+++ b/tests/test_models.py
@@ -1,5 +1,5 @@
import json
-from typing import Any, Dict, List, Union, Optional, cast
+from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional, cast
from datetime import datetime, timezone
from typing_extensions import Literal, Annotated, TypeAliasType
@@ -8,8 +8,8 @@
from pydantic import Field
from mixedbread._utils import PropertyInfo
-from mixedbread._compat import PYDANTIC_V2, parse_obj, model_dump, model_json
-from mixedbread._models import BaseModel, construct_type
+from mixedbread._compat import PYDANTIC_V1, parse_obj, model_dump, model_json
+from mixedbread._models import DISCRIMINATOR_CACHE, BaseModel, construct_type
class BasicModel(BaseModel):
@@ -294,12 +294,12 @@ class Model(BaseModel):
assert cast(bool, m.foo) is True
m = Model.construct(foo={"name": 3})
- if PYDANTIC_V2:
- assert isinstance(m.foo, Submodel1)
- assert m.foo.name == 3 # type: ignore
- else:
+ if PYDANTIC_V1:
assert isinstance(m.foo, Submodel2)
assert m.foo.name == "3"
+ else:
+ assert isinstance(m.foo, Submodel1)
+ assert m.foo.name == 3 # type: ignore
def test_list_of_unions() -> None:
@@ -426,10 +426,10 @@ class Model(BaseModel):
expected = datetime(2019, 12, 27, 18, 11, 19, 117000, tzinfo=timezone.utc)
- if PYDANTIC_V2:
- expected_json = '{"created_at":"2019-12-27T18:11:19.117000Z"}'
- else:
+ if PYDANTIC_V1:
expected_json = '{"created_at": "2019-12-27T18:11:19.117000+00:00"}'
+ else:
+ expected_json = '{"created_at":"2019-12-27T18:11:19.117000Z"}'
model = Model.construct(created_at="2019-12-27T18:11:19.117Z")
assert model.created_at == expected
@@ -492,12 +492,15 @@ class Model(BaseModel):
resource_id: Optional[str] = None
m = Model.construct()
+ assert m.resource_id is None
assert "resource_id" not in m.model_fields_set
m = Model.construct(resource_id=None)
+ assert m.resource_id is None
assert "resource_id" in m.model_fields_set
m = Model.construct(resource_id="foo")
+ assert m.resource_id == "foo"
assert "resource_id" in m.model_fields_set
@@ -528,7 +531,7 @@ class Model2(BaseModel):
assert m4.to_dict(mode="python") == {"created_at": datetime.fromisoformat(time_str)}
assert m4.to_dict(mode="json") == {"created_at": time_str}
- if not PYDANTIC_V2:
+ if PYDANTIC_V1:
with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"):
m.to_dict(warnings=False)
@@ -553,7 +556,7 @@ class Model(BaseModel):
assert m3.model_dump() == {"foo": None}
assert m3.model_dump(exclude_none=True) == {}
- if not PYDANTIC_V2:
+ if PYDANTIC_V1:
with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"):
m.model_dump(round_trip=True)
@@ -577,10 +580,10 @@ class Model(BaseModel):
assert json.loads(m.to_json()) == {"FOO": "hello"}
assert json.loads(m.to_json(use_api_names=False)) == {"foo": "hello"}
- if PYDANTIC_V2:
- assert m.to_json(indent=None) == '{"FOO":"hello"}'
- else:
+ if PYDANTIC_V1:
assert m.to_json(indent=None) == '{"FOO": "hello"}'
+ else:
+ assert m.to_json(indent=None) == '{"FOO":"hello"}'
m2 = Model()
assert json.loads(m2.to_json()) == {}
@@ -592,7 +595,7 @@ class Model(BaseModel):
assert json.loads(m3.to_json()) == {"FOO": None}
assert json.loads(m3.to_json(exclude_none=True)) == {}
- if not PYDANTIC_V2:
+ if PYDANTIC_V1:
with pytest.raises(ValueError, match="warnings is only supported in Pydantic v2"):
m.to_json(warnings=False)
@@ -619,7 +622,7 @@ class Model(BaseModel):
assert json.loads(m3.model_dump_json()) == {"foo": None}
assert json.loads(m3.model_dump_json(exclude_none=True)) == {}
- if not PYDANTIC_V2:
+ if PYDANTIC_V1:
with pytest.raises(ValueError, match="round_trip is only supported in Pydantic v2"):
m.model_dump_json(round_trip=True)
@@ -676,12 +679,12 @@ class B(BaseModel):
)
assert isinstance(m, A)
assert m.type == "a"
- if PYDANTIC_V2:
- assert m.data == 100 # type: ignore[comparison-overlap]
- else:
+ if PYDANTIC_V1:
# pydantic v1 automatically converts inputs to strings
# if the expected type is a str
assert m.data == "100"
+ else:
+ assert m.data == 100 # type: ignore[comparison-overlap]
def test_discriminated_unions_unknown_variant() -> None:
@@ -765,12 +768,12 @@ class B(BaseModel):
)
assert isinstance(m, A)
assert m.foo_type == "a"
- if PYDANTIC_V2:
- assert m.data == 100 # type: ignore[comparison-overlap]
- else:
+ if PYDANTIC_V1:
# pydantic v1 automatically converts inputs to strings
# if the expected type is a str
assert m.data == "100"
+ else:
+ assert m.data == 100 # type: ignore[comparison-overlap]
def test_discriminated_unions_overlapping_discriminators_invalid_data() -> None:
@@ -806,7 +809,7 @@ class B(BaseModel):
UnionType = cast(Any, Union[A, B])
- assert not hasattr(UnionType, "__discriminator__")
+ assert not DISCRIMINATOR_CACHE.get(UnionType)
m = construct_type(
value={"type": "b", "data": "foo"}, type_=cast(Any, Annotated[UnionType, PropertyInfo(discriminator="type")])
@@ -815,7 +818,7 @@ class B(BaseModel):
assert m.type == "b"
assert m.data == "foo" # type: ignore[comparison-overlap]
- discriminator = UnionType.__discriminator__
+ discriminator = DISCRIMINATOR_CACHE.get(UnionType)
assert discriminator is not None
m = construct_type(
@@ -827,12 +830,12 @@ class B(BaseModel):
# if the discriminator details object stays the same between invocations then
# we hit the cache
- assert UnionType.__discriminator__ is discriminator
+ assert DISCRIMINATOR_CACHE.get(UnionType) is discriminator
-@pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1")
+@pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1")
def test_type_alias_type() -> None:
- Alias = TypeAliasType("Alias", str)
+ Alias = TypeAliasType("Alias", str) # pyright: ignore
class Model(BaseModel):
alias: Alias
@@ -846,7 +849,7 @@ class Model(BaseModel):
assert m.union == "bar"
-@pytest.mark.skipif(not PYDANTIC_V2, reason="TypeAliasType is not supported in Pydantic v1")
+@pytest.mark.skipif(PYDANTIC_V1, reason="TypeAliasType is not supported in Pydantic v1")
def test_field_named_cls() -> None:
class Model(BaseModel):
cls: str
@@ -854,3 +857,107 @@ class Model(BaseModel):
m = construct_type(value={"cls": "foo"}, type_=Model)
assert isinstance(m, Model)
assert isinstance(m.cls, str)
+
+
+def test_discriminated_union_case() -> None:
+ class A(BaseModel):
+ type: Literal["a"]
+
+ data: bool
+
+ class B(BaseModel):
+ type: Literal["b"]
+
+ data: List[Union[A, object]]
+
+ class ModelA(BaseModel):
+ type: Literal["modelA"]
+
+ data: int
+
+ class ModelB(BaseModel):
+ type: Literal["modelB"]
+
+ required: str
+
+ data: Union[A, B]
+
+ # when constructing ModelA | ModelB, value data doesn't match ModelB exactly - missing `required`
+ m = construct_type(
+ value={"type": "modelB", "data": {"type": "a", "data": True}},
+ type_=cast(Any, Annotated[Union[ModelA, ModelB], PropertyInfo(discriminator="type")]),
+ )
+
+ assert isinstance(m, ModelB)
+
+
+def test_nested_discriminated_union() -> None:
+ class InnerType1(BaseModel):
+ type: Literal["type_1"]
+
+ class InnerModel(BaseModel):
+ inner_value: str
+
+ class InnerType2(BaseModel):
+ type: Literal["type_2"]
+ some_inner_model: InnerModel
+
+ class Type1(BaseModel):
+ base_type: Literal["base_type_1"]
+ value: Annotated[
+ Union[
+ InnerType1,
+ InnerType2,
+ ],
+ PropertyInfo(discriminator="type"),
+ ]
+
+ class Type2(BaseModel):
+ base_type: Literal["base_type_2"]
+
+ T = Annotated[
+ Union[
+ Type1,
+ Type2,
+ ],
+ PropertyInfo(discriminator="base_type"),
+ ]
+
+ model = construct_type(
+ type_=T,
+ value={
+ "base_type": "base_type_1",
+ "value": {
+ "type": "type_2",
+ },
+ },
+ )
+ assert isinstance(model, Type1)
+ assert isinstance(model.value, InnerType2)
+
+
+@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2 for now")
+def test_extra_properties() -> None:
+ class Item(BaseModel):
+ prop: int
+
+ class Model(BaseModel):
+ __pydantic_extra__: Dict[str, Item] = Field(init=False) # pyright: ignore[reportIncompatibleVariableOverride]
+
+ other: str
+
+ if TYPE_CHECKING:
+
+ def __getattr__(self, attr: str) -> Item: ...
+
+ model = construct_type(
+ type_=Model,
+ value={
+ "a": {"prop": 1},
+ "other": "foo",
+ },
+ )
+ assert isinstance(model, Model)
+ assert model.a.prop == 1
+ assert isinstance(model.a, Item)
+ assert model.other == "foo"
diff --git a/tests/test_transform.py b/tests/test_transform.py
index 67fec2f4..1337f4f6 100644
--- a/tests/test_transform.py
+++ b/tests/test_transform.py
@@ -2,20 +2,20 @@
import io
import pathlib
-from typing import Any, List, Union, TypeVar, Iterable, Optional, cast
+from typing import Any, Dict, List, Union, TypeVar, Iterable, Optional, cast
from datetime import date, datetime
from typing_extensions import Required, Annotated, TypedDict
import pytest
-from mixedbread._types import Base64FileInput
+from mixedbread._types import Base64FileInput, omit, not_given
from mixedbread._utils import (
PropertyInfo,
transform as _transform,
parse_datetime,
async_transform as _async_transform,
)
-from mixedbread._compat import PYDANTIC_V2
+from mixedbread._compat import PYDANTIC_V1
from mixedbread._models import BaseModel
_T = TypeVar("_T")
@@ -189,7 +189,7 @@ class DateModel(BaseModel):
@pytest.mark.asyncio
async def test_iso8601_format(use_async: bool) -> None:
dt = datetime.fromisoformat("2023-02-23T14:16:36.337692+00:00")
- tz = "Z" if PYDANTIC_V2 else "+00:00"
+ tz = "+00:00" if PYDANTIC_V1 else "Z"
assert await transform({"foo": dt}, DatetimeDict, use_async) == {"foo": "2023-02-23T14:16:36.337692+00:00"} # type: ignore[comparison-overlap]
assert await transform(DatetimeModel(foo=dt), Any, use_async) == {"foo": "2023-02-23T14:16:36.337692" + tz} # type: ignore[comparison-overlap]
@@ -297,11 +297,11 @@ async def test_pydantic_unknown_field(use_async: bool) -> None:
@pytest.mark.asyncio
async def test_pydantic_mismatched_types(use_async: bool) -> None:
model = MyModel.construct(foo=True)
- if PYDANTIC_V2:
+ if PYDANTIC_V1:
+ params = await transform(model, Any, use_async)
+ else:
with pytest.warns(UserWarning):
params = await transform(model, Any, use_async)
- else:
- params = await transform(model, Any, use_async)
assert cast(Any, params) == {"foo": True}
@@ -309,11 +309,11 @@ async def test_pydantic_mismatched_types(use_async: bool) -> None:
@pytest.mark.asyncio
async def test_pydantic_mismatched_object_type(use_async: bool) -> None:
model = MyModel.construct(foo=MyModel.construct(hello="world"))
- if PYDANTIC_V2:
+ if PYDANTIC_V1:
+ params = await transform(model, Any, use_async)
+ else:
with pytest.warns(UserWarning):
params = await transform(model, Any, use_async)
- else:
- params = await transform(model, Any, use_async)
assert cast(Any, params) == {"foo": {"hello": "world"}}
@@ -388,6 +388,15 @@ def my_iter() -> Iterable[Baz8]:
}
+@parametrize
+@pytest.mark.asyncio
+async def test_dictionary_items(use_async: bool) -> None:
+ class DictItems(TypedDict):
+ foo_baz: Annotated[str, PropertyInfo(alias="fooBaz")]
+
+ assert await transform({"foo": {"foo_baz": "bar"}}, Dict[str, DictItems], use_async) == {"foo": {"fooBaz": "bar"}}
+
+
class TypedDictIterableUnionStr(TypedDict):
foo: Annotated[Union[str, Iterable[Baz8]], PropertyInfo(alias="FOO")]
@@ -423,3 +432,29 @@ async def test_base64_file_input(use_async: bool) -> None:
assert await transform({"foo": io.BytesIO(b"Hello, world!")}, TypedDictBase64Input, use_async) == {
"foo": "SGVsbG8sIHdvcmxkIQ=="
} # type: ignore[comparison-overlap]
+
+
+@parametrize
+@pytest.mark.asyncio
+async def test_transform_skipping(use_async: bool) -> None:
+ # lists of ints are left as-is
+ data = [1, 2, 3]
+ assert await transform(data, List[int], use_async) is data
+
+ # iterables of ints are converted to a list
+ data = iter([1, 2, 3])
+ assert await transform(data, Iterable[int], use_async) == [1, 2, 3]
+
+
+@parametrize
+@pytest.mark.asyncio
+async def test_strips_notgiven(use_async: bool) -> None:
+ assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"}
+ assert await transform({"foo_bar": not_given}, Foo1, use_async) == {}
+
+
+@parametrize
+@pytest.mark.asyncio
+async def test_strips_omit(use_async: bool) -> None:
+ assert await transform({"foo_bar": "bar"}, Foo1, use_async) == {"fooBar": "bar"}
+ assert await transform({"foo_bar": omit}, Foo1, use_async) == {}
diff --git a/tests/test_utils/test_datetime_parse.py b/tests/test_utils/test_datetime_parse.py
new file mode 100644
index 00000000..27b7a48d
--- /dev/null
+++ b/tests/test_utils/test_datetime_parse.py
@@ -0,0 +1,110 @@
+"""
+Copied from https://github.com/pydantic/pydantic/blob/v1.10.22/tests/test_datetime_parse.py
+with modifications so it works without pydantic v1 imports.
+"""
+
+from typing import Type, Union
+from datetime import date, datetime, timezone, timedelta
+
+import pytest
+
+from mixedbread._utils import parse_date, parse_datetime
+
+
+def create_tz(minutes: int) -> timezone:
+ return timezone(timedelta(minutes=minutes))
+
+
+@pytest.mark.parametrize(
+ "value,result",
+ [
+ # Valid inputs
+ ("1494012444.883309", date(2017, 5, 5)),
+ (b"1494012444.883309", date(2017, 5, 5)),
+ (1_494_012_444.883_309, date(2017, 5, 5)),
+ ("1494012444", date(2017, 5, 5)),
+ (1_494_012_444, date(2017, 5, 5)),
+ (0, date(1970, 1, 1)),
+ ("2012-04-23", date(2012, 4, 23)),
+ (b"2012-04-23", date(2012, 4, 23)),
+ ("2012-4-9", date(2012, 4, 9)),
+ (date(2012, 4, 9), date(2012, 4, 9)),
+ (datetime(2012, 4, 9, 12, 15), date(2012, 4, 9)),
+ # Invalid inputs
+ ("x20120423", ValueError),
+ ("2012-04-56", ValueError),
+ (19_999_999_999, date(2603, 10, 11)), # just before watershed
+ (20_000_000_001, date(1970, 8, 20)), # just after watershed
+ (1_549_316_052, date(2019, 2, 4)), # nowish in s
+ (1_549_316_052_104, date(2019, 2, 4)), # nowish in ms
+ (1_549_316_052_104_324, date(2019, 2, 4)), # nowish in μs
+ (1_549_316_052_104_324_096, date(2019, 2, 4)), # nowish in ns
+ ("infinity", date(9999, 12, 31)),
+ ("inf", date(9999, 12, 31)),
+ (float("inf"), date(9999, 12, 31)),
+ ("infinity ", date(9999, 12, 31)),
+ (int("1" + "0" * 100), date(9999, 12, 31)),
+ (1e1000, date(9999, 12, 31)),
+ ("-infinity", date(1, 1, 1)),
+ ("-inf", date(1, 1, 1)),
+ ("nan", ValueError),
+ ],
+)
+def test_date_parsing(value: Union[str, bytes, int, float], result: Union[date, Type[Exception]]) -> None:
+ if type(result) == type and issubclass(result, Exception): # pyright: ignore[reportUnnecessaryIsInstance]
+ with pytest.raises(result):
+ parse_date(value)
+ else:
+ assert parse_date(value) == result
+
+
+@pytest.mark.parametrize(
+ "value,result",
+ [
+ # Valid inputs
+ # values in seconds
+ ("1494012444.883309", datetime(2017, 5, 5, 19, 27, 24, 883_309, tzinfo=timezone.utc)),
+ (1_494_012_444.883_309, datetime(2017, 5, 5, 19, 27, 24, 883_309, tzinfo=timezone.utc)),
+ ("1494012444", datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)),
+ (b"1494012444", datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)),
+ (1_494_012_444, datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)),
+ # values in ms
+ ("1494012444000.883309", datetime(2017, 5, 5, 19, 27, 24, 883, tzinfo=timezone.utc)),
+ ("-1494012444000.883309", datetime(1922, 8, 29, 4, 32, 35, 999117, tzinfo=timezone.utc)),
+ (1_494_012_444_000, datetime(2017, 5, 5, 19, 27, 24, tzinfo=timezone.utc)),
+ ("2012-04-23T09:15:00", datetime(2012, 4, 23, 9, 15)),
+ ("2012-4-9 4:8:16", datetime(2012, 4, 9, 4, 8, 16)),
+ ("2012-04-23T09:15:00Z", datetime(2012, 4, 23, 9, 15, 0, 0, timezone.utc)),
+ ("2012-4-9 4:8:16-0320", datetime(2012, 4, 9, 4, 8, 16, 0, create_tz(-200))),
+ ("2012-04-23T10:20:30.400+02:30", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(150))),
+ ("2012-04-23T10:20:30.400+02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(120))),
+ ("2012-04-23T10:20:30.400-02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))),
+ (b"2012-04-23T10:20:30.400-02", datetime(2012, 4, 23, 10, 20, 30, 400_000, create_tz(-120))),
+ (datetime(2017, 5, 5), datetime(2017, 5, 5)),
+ (0, datetime(1970, 1, 1, 0, 0, 0, tzinfo=timezone.utc)),
+ # Invalid inputs
+ ("x20120423091500", ValueError),
+ ("2012-04-56T09:15:90", ValueError),
+ ("2012-04-23T11:05:00-25:00", ValueError),
+ (19_999_999_999, datetime(2603, 10, 11, 11, 33, 19, tzinfo=timezone.utc)), # just before watershed
+ (20_000_000_001, datetime(1970, 8, 20, 11, 33, 20, 1000, tzinfo=timezone.utc)), # just after watershed
+ (1_549_316_052, datetime(2019, 2, 4, 21, 34, 12, 0, tzinfo=timezone.utc)), # nowish in s
+ (1_549_316_052_104, datetime(2019, 2, 4, 21, 34, 12, 104_000, tzinfo=timezone.utc)), # nowish in ms
+ (1_549_316_052_104_324, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)), # nowish in μs
+ (1_549_316_052_104_324_096, datetime(2019, 2, 4, 21, 34, 12, 104_324, tzinfo=timezone.utc)), # nowish in ns
+ ("infinity", datetime(9999, 12, 31, 23, 59, 59, 999999)),
+ ("inf", datetime(9999, 12, 31, 23, 59, 59, 999999)),
+ ("inf ", datetime(9999, 12, 31, 23, 59, 59, 999999)),
+ (1e50, datetime(9999, 12, 31, 23, 59, 59, 999999)),
+ (float("inf"), datetime(9999, 12, 31, 23, 59, 59, 999999)),
+ ("-infinity", datetime(1, 1, 1, 0, 0)),
+ ("-inf", datetime(1, 1, 1, 0, 0)),
+ ("nan", ValueError),
+ ],
+)
+def test_datetime_parsing(value: Union[str, bytes, int, float], result: Union[datetime, Type[Exception]]) -> None:
+ if type(result) == type and issubclass(result, Exception): # pyright: ignore[reportUnnecessaryIsInstance]
+ with pytest.raises(result):
+ parse_datetime(value)
+ else:
+ assert parse_datetime(value) == result
diff --git a/tests/test_utils/test_json.py b/tests/test_utils/test_json.py
new file mode 100644
index 00000000..732c37f3
--- /dev/null
+++ b/tests/test_utils/test_json.py
@@ -0,0 +1,126 @@
+from __future__ import annotations
+
+import datetime
+from typing import Union
+
+import pydantic
+
+from mixedbread import _compat
+from mixedbread._utils._json import openapi_dumps
+
+
+class TestOpenapiDumps:
+ def test_basic(self) -> None:
+ data = {"key": "value", "number": 42}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"key":"value","number":42}'
+
+ def test_datetime_serialization(self) -> None:
+ dt = datetime.datetime(2023, 1, 1, 12, 0, 0)
+ data = {"datetime": dt}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"datetime":"2023-01-01T12:00:00"}'
+
+ def test_pydantic_model_serialization(self) -> None:
+ class User(pydantic.BaseModel):
+ first_name: str
+ last_name: str
+ age: int
+
+ model_instance = User(first_name="John", last_name="Kramer", age=83)
+ data = {"model": model_instance}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"model":{"first_name":"John","last_name":"Kramer","age":83}}'
+
+ def test_pydantic_model_with_default_values(self) -> None:
+ class User(pydantic.BaseModel):
+ name: str
+ role: str = "user"
+ active: bool = True
+ score: int = 0
+
+ model_instance = User(name="Alice")
+ data = {"model": model_instance}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"model":{"name":"Alice"}}'
+
+ def test_pydantic_model_with_default_values_overridden(self) -> None:
+ class User(pydantic.BaseModel):
+ name: str
+ role: str = "user"
+ active: bool = True
+
+ model_instance = User(name="Bob", role="admin", active=False)
+ data = {"model": model_instance}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"model":{"name":"Bob","role":"admin","active":false}}'
+
+ def test_pydantic_model_with_alias(self) -> None:
+ class User(pydantic.BaseModel):
+ first_name: str = pydantic.Field(alias="firstName")
+ last_name: str = pydantic.Field(alias="lastName")
+
+ model_instance = User(firstName="John", lastName="Doe")
+ data = {"model": model_instance}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"model":{"firstName":"John","lastName":"Doe"}}'
+
+ def test_pydantic_model_with_alias_and_default(self) -> None:
+ class User(pydantic.BaseModel):
+ user_name: str = pydantic.Field(alias="userName")
+ user_role: str = pydantic.Field(default="member", alias="userRole")
+ is_active: bool = pydantic.Field(default=True, alias="isActive")
+
+ model_instance = User(userName="charlie")
+ data = {"model": model_instance}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"model":{"userName":"charlie"}}'
+
+ model_with_overrides = User(userName="diana", userRole="admin", isActive=False)
+ data = {"model": model_with_overrides}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"model":{"userName":"diana","userRole":"admin","isActive":false}}'
+
+ def test_pydantic_model_with_nested_models_and_defaults(self) -> None:
+ class Address(pydantic.BaseModel):
+ street: str
+ city: str = "Unknown"
+
+ class User(pydantic.BaseModel):
+ name: str
+ address: Address
+ verified: bool = False
+
+ if _compat.PYDANTIC_V1:
+ # to handle forward references in Pydantic v1
+ User.update_forward_refs(**locals()) # type: ignore[reportDeprecated]
+
+ address = Address(street="123 Main St")
+ user = User(name="Diana", address=address)
+ data = {"user": user}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"user":{"name":"Diana","address":{"street":"123 Main St"}}}'
+
+ address_with_city = Address(street="456 Oak Ave", city="Boston")
+ user_verified = User(name="Eve", address=address_with_city, verified=True)
+ data = {"user": user_verified}
+ json_bytes = openapi_dumps(data)
+ assert (
+ json_bytes == b'{"user":{"name":"Eve","address":{"street":"456 Oak Ave","city":"Boston"},"verified":true}}'
+ )
+
+ def test_pydantic_model_with_optional_fields(self) -> None:
+ class User(pydantic.BaseModel):
+ name: str
+ email: Union[str, None]
+ phone: Union[str, None]
+
+ model_with_none = User(name="Eve", email=None, phone=None)
+ data = {"model": model_with_none}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"model":{"name":"Eve","email":null,"phone":null}}'
+
+ model_with_values = User(name="Frank", email="frank@example.com", phone=None)
+ data = {"model": model_with_values}
+ json_bytes = openapi_dumps(data)
+ assert json_bytes == b'{"model":{"name":"Frank","email":"frank@example.com","phone":null}}'
diff --git a/tests/test_utils/test_proxy.py b/tests/test_utils/test_proxy.py
index 9b54af18..76104cef 100644
--- a/tests/test_utils/test_proxy.py
+++ b/tests/test_utils/test_proxy.py
@@ -21,3 +21,14 @@ def test_recursive_proxy() -> None:
assert dir(proxy) == []
assert type(proxy).__name__ == "RecursiveLazyProxy"
assert type(operator.attrgetter("name.foo.bar.baz")(proxy)).__name__ == "RecursiveLazyProxy"
+
+
+def test_isinstance_does_not_error() -> None:
+ class AlwaysErrorProxy(LazyProxy[Any]):
+ @override
+ def __load__(self) -> Any:
+ raise RuntimeError("Mocking missing dependency")
+
+ proxy = AlwaysErrorProxy()
+ assert not isinstance(proxy, dict)
+ assert isinstance(proxy, LazyProxy)
diff --git a/tests/utils.py b/tests/utils.py
index bd4c1519..c9a2638f 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,10 +15,11 @@
is_list_type,
is_union_type,
extract_type_arg,
+ is_sequence_type,
is_annotated_type,
is_type_alias_type,
)
-from mixedbread._compat import PYDANTIC_V2, field_outer_type, get_model_fields
+from mixedbread._compat import PYDANTIC_V1, field_outer_type, get_model_fields
from mixedbread._models import BaseModel
BaseModelT = TypeVar("BaseModelT", bound=BaseModel)
@@ -27,12 +28,12 @@
def assert_matches_model(model: type[BaseModelT], value: BaseModelT, *, path: list[str]) -> bool:
for name, field in get_model_fields(model).items():
field_value = getattr(value, name)
- if PYDANTIC_V2:
- allow_none = False
- else:
+ if PYDANTIC_V1:
# in v1 nullability was structured differently
# https://docs.pydantic.dev/2.0/migration/#required-optional-and-nullable-fields
allow_none = getattr(field, "allow_none", False)
+ else:
+ allow_none = False
assert_matches_type(
field_outer_type(field),
@@ -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: