diff --git a/.stats.yml b/.stats.yml
index 27e2ce5ede..4e4cb5509c 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1,2 +1,2 @@
-configured_endpoints: 64
-openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-518ca6c60061d3e8bc0971facf40d752f2aea62e3522cc168ad29a1f29cab3dd.yml
+configured_endpoints: 68
+openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/openai-77cfff37114bc9f141c7e6107eb5f1b38d8cc99bc3d4ce03a066db2b6b649c69.yml
diff --git a/api.md b/api.md
index de69f11dca..82a5360edd 100644
--- a/api.md
+++ b/api.md
@@ -415,3 +415,29 @@ Methods:
- client.batches.retrieve(batch_id) -> Batch
- client.batches.list(\*\*params) -> SyncCursorPage[Batch]
- client.batches.cancel(batch_id) -> Batch
+
+# Uploads
+
+Types:
+
+```python
+from openai.types import Upload
+```
+
+Methods:
+
+- client.uploads.create(\*\*params) -> Upload
+- client.uploads.cancel(upload_id) -> Upload
+- client.uploads.complete(upload_id, \*\*params) -> Upload
+
+## Parts
+
+Types:
+
+```python
+from openai.types.uploads import UploadPart
+```
+
+Methods:
+
+- client.uploads.parts.create(upload_id, \*\*params) -> UploadPart
diff --git a/src/openai/_client.py b/src/openai/_client.py
index 8f3060c6f6..8b404e234d 100644
--- a/src/openai/_client.py
+++ b/src/openai/_client.py
@@ -58,6 +58,7 @@ class OpenAI(SyncAPIClient):
fine_tuning: resources.FineTuning
beta: resources.Beta
batches: resources.Batches
+ uploads: resources.Uploads
with_raw_response: OpenAIWithRawResponse
with_streaming_response: OpenAIWithStreamedResponse
@@ -143,6 +144,7 @@ def __init__(
self.fine_tuning = resources.FineTuning(self)
self.beta = resources.Beta(self)
self.batches = resources.Batches(self)
+ self.uploads = resources.Uploads(self)
self.with_raw_response = OpenAIWithRawResponse(self)
self.with_streaming_response = OpenAIWithStreamedResponse(self)
@@ -270,6 +272,7 @@ class AsyncOpenAI(AsyncAPIClient):
fine_tuning: resources.AsyncFineTuning
beta: resources.AsyncBeta
batches: resources.AsyncBatches
+ uploads: resources.AsyncUploads
with_raw_response: AsyncOpenAIWithRawResponse
with_streaming_response: AsyncOpenAIWithStreamedResponse
@@ -355,6 +358,7 @@ def __init__(
self.fine_tuning = resources.AsyncFineTuning(self)
self.beta = resources.AsyncBeta(self)
self.batches = resources.AsyncBatches(self)
+ self.uploads = resources.AsyncUploads(self)
self.with_raw_response = AsyncOpenAIWithRawResponse(self)
self.with_streaming_response = AsyncOpenAIWithStreamedResponse(self)
@@ -483,6 +487,7 @@ def __init__(self, client: OpenAI) -> None:
self.fine_tuning = resources.FineTuningWithRawResponse(client.fine_tuning)
self.beta = resources.BetaWithRawResponse(client.beta)
self.batches = resources.BatchesWithRawResponse(client.batches)
+ self.uploads = resources.UploadsWithRawResponse(client.uploads)
class AsyncOpenAIWithRawResponse:
@@ -498,6 +503,7 @@ def __init__(self, client: AsyncOpenAI) -> None:
self.fine_tuning = resources.AsyncFineTuningWithRawResponse(client.fine_tuning)
self.beta = resources.AsyncBetaWithRawResponse(client.beta)
self.batches = resources.AsyncBatchesWithRawResponse(client.batches)
+ self.uploads = resources.AsyncUploadsWithRawResponse(client.uploads)
class OpenAIWithStreamedResponse:
@@ -513,6 +519,7 @@ def __init__(self, client: OpenAI) -> None:
self.fine_tuning = resources.FineTuningWithStreamingResponse(client.fine_tuning)
self.beta = resources.BetaWithStreamingResponse(client.beta)
self.batches = resources.BatchesWithStreamingResponse(client.batches)
+ self.uploads = resources.UploadsWithStreamingResponse(client.uploads)
class AsyncOpenAIWithStreamedResponse:
@@ -528,6 +535,7 @@ def __init__(self, client: AsyncOpenAI) -> None:
self.fine_tuning = resources.AsyncFineTuningWithStreamingResponse(client.fine_tuning)
self.beta = resources.AsyncBetaWithStreamingResponse(client.beta)
self.batches = resources.AsyncBatchesWithStreamingResponse(client.batches)
+ self.uploads = resources.AsyncUploadsWithStreamingResponse(client.uploads)
Client = OpenAI
diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py
index ecae4243fc..e2cc1c4b0c 100644
--- a/src/openai/resources/__init__.py
+++ b/src/openai/resources/__init__.py
@@ -56,6 +56,14 @@
BatchesWithStreamingResponse,
AsyncBatchesWithStreamingResponse,
)
+from .uploads import (
+ Uploads,
+ AsyncUploads,
+ UploadsWithRawResponse,
+ AsyncUploadsWithRawResponse,
+ UploadsWithStreamingResponse,
+ AsyncUploadsWithStreamingResponse,
+)
from .embeddings import (
Embeddings,
AsyncEmbeddings,
@@ -156,4 +164,10 @@
"AsyncBatchesWithRawResponse",
"BatchesWithStreamingResponse",
"AsyncBatchesWithStreamingResponse",
+ "Uploads",
+ "AsyncUploads",
+ "UploadsWithRawResponse",
+ "AsyncUploadsWithRawResponse",
+ "UploadsWithStreamingResponse",
+ "AsyncUploadsWithStreamingResponse",
]
diff --git a/src/openai/resources/chat/completions.py b/src/openai/resources/chat/completions.py
index d73ece2109..88892d1d64 100644
--- a/src/openai/resources/chat/completions.py
+++ b/src/openai/resources/chat/completions.py
@@ -171,6 +171,7 @@ def create(
exhausted.
- If set to 'default', the request will be processed using the default service
tier with a lower uptime SLA and no latency guarentee.
+ - When not set, the default behavior is 'auto'.
When this parameter is set, the response body will include the `service_tier`
utilized.
@@ -366,6 +367,7 @@ def create(
exhausted.
- If set to 'default', the request will be processed using the default service
tier with a lower uptime SLA and no latency guarentee.
+ - When not set, the default behavior is 'auto'.
When this parameter is set, the response body will include the `service_tier`
utilized.
@@ -554,6 +556,7 @@ def create(
exhausted.
- If set to 'default', the request will be processed using the default service
tier with a lower uptime SLA and no latency guarentee.
+ - When not set, the default behavior is 'auto'.
When this parameter is set, the response body will include the `service_tier`
utilized.
@@ -817,6 +820,7 @@ async def create(
exhausted.
- If set to 'default', the request will be processed using the default service
tier with a lower uptime SLA and no latency guarentee.
+ - When not set, the default behavior is 'auto'.
When this parameter is set, the response body will include the `service_tier`
utilized.
@@ -1012,6 +1016,7 @@ async def create(
exhausted.
- If set to 'default', the request will be processed using the default service
tier with a lower uptime SLA and no latency guarentee.
+ - When not set, the default behavior is 'auto'.
When this parameter is set, the response body will include the `service_tier`
utilized.
@@ -1200,6 +1205,7 @@ async def create(
exhausted.
- If set to 'default', the request will be processed using the default service
tier with a lower uptime SLA and no latency guarentee.
+ - When not set, the default behavior is 'auto'.
When this parameter is set, the response body will include the `service_tier`
utilized.
diff --git a/src/openai/resources/uploads/__init__.py b/src/openai/resources/uploads/__init__.py
new file mode 100644
index 0000000000..12d1056f9e
--- /dev/null
+++ b/src/openai/resources/uploads/__init__.py
@@ -0,0 +1,33 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .parts import (
+ Parts,
+ AsyncParts,
+ PartsWithRawResponse,
+ AsyncPartsWithRawResponse,
+ PartsWithStreamingResponse,
+ AsyncPartsWithStreamingResponse,
+)
+from .uploads import (
+ Uploads,
+ AsyncUploads,
+ UploadsWithRawResponse,
+ AsyncUploadsWithRawResponse,
+ UploadsWithStreamingResponse,
+ AsyncUploadsWithStreamingResponse,
+)
+
+__all__ = [
+ "Parts",
+ "AsyncParts",
+ "PartsWithRawResponse",
+ "AsyncPartsWithRawResponse",
+ "PartsWithStreamingResponse",
+ "AsyncPartsWithStreamingResponse",
+ "Uploads",
+ "AsyncUploads",
+ "UploadsWithRawResponse",
+ "AsyncUploadsWithRawResponse",
+ "UploadsWithStreamingResponse",
+ "AsyncUploadsWithStreamingResponse",
+]
diff --git a/src/openai/resources/uploads/parts.py b/src/openai/resources/uploads/parts.py
new file mode 100644
index 0000000000..3ec2592b1e
--- /dev/null
+++ b/src/openai/resources/uploads/parts.py
@@ -0,0 +1,188 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing import Mapping, cast
+
+import httpx
+
+from ... import _legacy_response
+from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes
+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 to_streamed_response_wrapper, async_to_streamed_response_wrapper
+from ..._base_client import make_request_options
+from ...types.uploads import part_create_params
+from ...types.uploads.upload_part import UploadPart
+
+__all__ = ["Parts", "AsyncParts"]
+
+
+class Parts(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> PartsWithRawResponse:
+ return PartsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> PartsWithStreamingResponse:
+ return PartsWithStreamingResponse(self)
+
+ def create(
+ self,
+ upload_id: str,
+ *,
+ data: FileTypes,
+ # 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,
+ ) -> UploadPart:
+ """
+ Adds a
+ [Part](https://platform.openai.com/docs/api-reference/uploads/part-object) to an
+ [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object.
+ A Part represents a chunk of bytes from the file you are trying to upload.
+
+ Each Part can be at most 64 MB, and you can add Parts until you hit the Upload
+ maximum of 8 GB.
+
+ It is possible to add multiple Parts in parallel. You can decide the intended
+ order of the Parts when you
+ [complete the Upload](https://platform.openai.com/docs/api-reference/uploads/complete).
+
+ Args:
+ data: The chunk of bytes for this Part.
+
+ 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}")
+ body = deepcopy_minimal({"data": data})
+ files = extract_files(cast(Mapping[str, object], body), paths=[["data"]])
+ # It should be noted that the actual Content-Type header that will be
+ # sent to the server will contain a `boundary` parameter, e.g.
+ # multipart/form-data; boundary=---abc--
+ extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
+ return self._post(
+ f"/uploads/{upload_id}/parts",
+ body=maybe_transform(body, part_create_params.PartCreateParams),
+ files=files,
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=UploadPart,
+ )
+
+
+class AsyncParts(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncPartsWithRawResponse:
+ return AsyncPartsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncPartsWithStreamingResponse:
+ return AsyncPartsWithStreamingResponse(self)
+
+ async def create(
+ self,
+ upload_id: str,
+ *,
+ data: FileTypes,
+ # 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,
+ ) -> UploadPart:
+ """
+ Adds a
+ [Part](https://platform.openai.com/docs/api-reference/uploads/part-object) to an
+ [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object.
+ A Part represents a chunk of bytes from the file you are trying to upload.
+
+ Each Part can be at most 64 MB, and you can add Parts until you hit the Upload
+ maximum of 8 GB.
+
+ It is possible to add multiple Parts in parallel. You can decide the intended
+ order of the Parts when you
+ [complete the Upload](https://platform.openai.com/docs/api-reference/uploads/complete).
+
+ Args:
+ data: The chunk of bytes for this Part.
+
+ 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}")
+ body = deepcopy_minimal({"data": data})
+ files = extract_files(cast(Mapping[str, object], body), paths=[["data"]])
+ # It should be noted that the actual Content-Type header that will be
+ # sent to the server will contain a `boundary` parameter, e.g.
+ # multipart/form-data; boundary=---abc--
+ extra_headers = {"Content-Type": "multipart/form-data", **(extra_headers or {})}
+ return await self._post(
+ f"/uploads/{upload_id}/parts",
+ body=await async_maybe_transform(body, part_create_params.PartCreateParams),
+ files=files,
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=UploadPart,
+ )
+
+
+class PartsWithRawResponse:
+ def __init__(self, parts: Parts) -> None:
+ self._parts = parts
+
+ self.create = _legacy_response.to_raw_response_wrapper(
+ parts.create,
+ )
+
+
+class AsyncPartsWithRawResponse:
+ def __init__(self, parts: AsyncParts) -> None:
+ self._parts = parts
+
+ self.create = _legacy_response.async_to_raw_response_wrapper(
+ parts.create,
+ )
+
+
+class PartsWithStreamingResponse:
+ def __init__(self, parts: Parts) -> None:
+ self._parts = parts
+
+ self.create = to_streamed_response_wrapper(
+ parts.create,
+ )
+
+
+class AsyncPartsWithStreamingResponse:
+ def __init__(self, parts: AsyncParts) -> None:
+ self._parts = parts
+
+ self.create = async_to_streamed_response_wrapper(
+ parts.create,
+ )
diff --git a/src/openai/resources/uploads/uploads.py b/src/openai/resources/uploads/uploads.py
new file mode 100644
index 0000000000..4100423d3e
--- /dev/null
+++ b/src/openai/resources/uploads/uploads.py
@@ -0,0 +1,473 @@
+# 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 Literal
+
+import httpx
+
+from ... import _legacy_response
+from .parts import (
+ Parts,
+ AsyncParts,
+ PartsWithRawResponse,
+ AsyncPartsWithRawResponse,
+ PartsWithStreamingResponse,
+ AsyncPartsWithStreamingResponse,
+)
+from ...types import upload_create_params, upload_complete_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_streamed_response_wrapper, async_to_streamed_response_wrapper
+from ..._base_client import make_request_options
+from ...types.upload import Upload
+
+__all__ = ["Uploads", "AsyncUploads"]
+
+
+class Uploads(SyncAPIResource):
+ @cached_property
+ def parts(self) -> Parts:
+ return Parts(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> UploadsWithRawResponse:
+ return UploadsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> UploadsWithStreamingResponse:
+ return UploadsWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ bytes: int,
+ filename: str,
+ mime_type: str,
+ purpose: Literal["assistants", "batch", "fine-tune", "vision"],
+ # 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,
+ ) -> Upload:
+ """
+ Creates an intermediate
+ [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object
+ that you can add
+ [Parts](https://platform.openai.com/docs/api-reference/uploads/part-object) to.
+ Currently, an Upload can accept at most 8 GB in total and expires after an hour
+ after you create it.
+
+ Once you complete the Upload, we will create a
+ [File](https://platform.openai.com/docs/api-reference/files/object) object that
+ contains all the parts you uploaded. This File is usable in the rest of our
+ platform as a regular File object.
+
+ For certain `purpose`s, the correct `mime_type` must be specified. Please refer
+ to documentation for the supported MIME types for your use case:
+
+ - [Assistants](https://platform.openai.com/docs/assistants/tools/file-search/supported-files)
+
+ For guidance on the proper filename extensions for each purpose, please follow
+ the documentation on
+ [creating a File](https://platform.openai.com/docs/api-reference/files/create).
+
+ Args:
+ bytes: The number of bytes in the file you are uploading.
+
+ filename: The name of the file to upload.
+
+ mime_type: The MIME type of the file.
+
+ This must fall within the supported MIME types for your file purpose. See the
+ supported MIME types for assistants and vision.
+
+ purpose: The intended purpose of the uploaded file.
+
+ See the
+ [documentation on File purposes](https://platform.openai.com/docs/api-reference/files/create#files-create-purpose).
+
+ 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(
+ "/uploads",
+ body=maybe_transform(
+ {
+ "bytes": bytes,
+ "filename": filename,
+ "mime_type": mime_type,
+ "purpose": purpose,
+ },
+ upload_create_params.UploadCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Upload,
+ )
+
+ def cancel(
+ 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,
+ ) -> Upload:
+ """Cancels the Upload.
+
+ No Parts may be added after an Upload is cancelled.
+
+ Args:
+ 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"/uploads/{upload_id}/cancel",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Upload,
+ )
+
+ def complete(
+ self,
+ upload_id: str,
+ *,
+ part_ids: List[str],
+ md5: 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,
+ ) -> Upload:
+ """
+ Completes the
+ [Upload](https://platform.openai.com/docs/api-reference/uploads/object).
+
+ Within the returned Upload object, there is a nested
+ [File](https://platform.openai.com/docs/api-reference/files/object) object that
+ is ready to use in the rest of the platform.
+
+ You can specify the order of the Parts by passing in an ordered list of the Part
+ IDs.
+
+ The number of bytes uploaded upon completion must match the number of bytes
+ initially specified when creating the Upload object. No Parts may be added after
+ an Upload is completed.
+
+ Args:
+ part_ids: The ordered list of Part IDs.
+
+ md5: The optional md5 checksum for the file contents to verify if the bytes uploaded
+ matches what you expect.
+
+ 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"/uploads/{upload_id}/complete",
+ body=maybe_transform(
+ {
+ "part_ids": part_ids,
+ "md5": md5,
+ },
+ upload_complete_params.UploadCompleteParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Upload,
+ )
+
+
+class AsyncUploads(AsyncAPIResource):
+ @cached_property
+ def parts(self) -> AsyncParts:
+ return AsyncParts(self._client)
+
+ @cached_property
+ def with_raw_response(self) -> AsyncUploadsWithRawResponse:
+ return AsyncUploadsWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncUploadsWithStreamingResponse:
+ return AsyncUploadsWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ bytes: int,
+ filename: str,
+ mime_type: str,
+ purpose: Literal["assistants", "batch", "fine-tune", "vision"],
+ # 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,
+ ) -> Upload:
+ """
+ Creates an intermediate
+ [Upload](https://platform.openai.com/docs/api-reference/uploads/object) object
+ that you can add
+ [Parts](https://platform.openai.com/docs/api-reference/uploads/part-object) to.
+ Currently, an Upload can accept at most 8 GB in total and expires after an hour
+ after you create it.
+
+ Once you complete the Upload, we will create a
+ [File](https://platform.openai.com/docs/api-reference/files/object) object that
+ contains all the parts you uploaded. This File is usable in the rest of our
+ platform as a regular File object.
+
+ For certain `purpose`s, the correct `mime_type` must be specified. Please refer
+ to documentation for the supported MIME types for your use case:
+
+ - [Assistants](https://platform.openai.com/docs/assistants/tools/file-search/supported-files)
+
+ For guidance on the proper filename extensions for each purpose, please follow
+ the documentation on
+ [creating a File](https://platform.openai.com/docs/api-reference/files/create).
+
+ Args:
+ bytes: The number of bytes in the file you are uploading.
+
+ filename: The name of the file to upload.
+
+ mime_type: The MIME type of the file.
+
+ This must fall within the supported MIME types for your file purpose. See the
+ supported MIME types for assistants and vision.
+
+ purpose: The intended purpose of the uploaded file.
+
+ See the
+ [documentation on File purposes](https://platform.openai.com/docs/api-reference/files/create#files-create-purpose).
+
+ 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(
+ "/uploads",
+ body=await async_maybe_transform(
+ {
+ "bytes": bytes,
+ "filename": filename,
+ "mime_type": mime_type,
+ "purpose": purpose,
+ },
+ upload_create_params.UploadCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Upload,
+ )
+
+ async def cancel(
+ 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,
+ ) -> Upload:
+ """Cancels the Upload.
+
+ No Parts may be added after an Upload is cancelled.
+
+ Args:
+ 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"/uploads/{upload_id}/cancel",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Upload,
+ )
+
+ async def complete(
+ self,
+ upload_id: str,
+ *,
+ part_ids: List[str],
+ md5: 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,
+ ) -> Upload:
+ """
+ Completes the
+ [Upload](https://platform.openai.com/docs/api-reference/uploads/object).
+
+ Within the returned Upload object, there is a nested
+ [File](https://platform.openai.com/docs/api-reference/files/object) object that
+ is ready to use in the rest of the platform.
+
+ You can specify the order of the Parts by passing in an ordered list of the Part
+ IDs.
+
+ The number of bytes uploaded upon completion must match the number of bytes
+ initially specified when creating the Upload object. No Parts may be added after
+ an Upload is completed.
+
+ Args:
+ part_ids: The ordered list of Part IDs.
+
+ md5: The optional md5 checksum for the file contents to verify if the bytes uploaded
+ matches what you expect.
+
+ 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"/uploads/{upload_id}/complete",
+ body=await async_maybe_transform(
+ {
+ "part_ids": part_ids,
+ "md5": md5,
+ },
+ upload_complete_params.UploadCompleteParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Upload,
+ )
+
+
+class UploadsWithRawResponse:
+ def __init__(self, uploads: Uploads) -> None:
+ self._uploads = uploads
+
+ self.create = _legacy_response.to_raw_response_wrapper(
+ uploads.create,
+ )
+ self.cancel = _legacy_response.to_raw_response_wrapper(
+ uploads.cancel,
+ )
+ self.complete = _legacy_response.to_raw_response_wrapper(
+ uploads.complete,
+ )
+
+ @cached_property
+ def parts(self) -> PartsWithRawResponse:
+ return PartsWithRawResponse(self._uploads.parts)
+
+
+class AsyncUploadsWithRawResponse:
+ def __init__(self, uploads: AsyncUploads) -> None:
+ self._uploads = uploads
+
+ self.create = _legacy_response.async_to_raw_response_wrapper(
+ uploads.create,
+ )
+ self.cancel = _legacy_response.async_to_raw_response_wrapper(
+ uploads.cancel,
+ )
+ self.complete = _legacy_response.async_to_raw_response_wrapper(
+ uploads.complete,
+ )
+
+ @cached_property
+ def parts(self) -> AsyncPartsWithRawResponse:
+ return AsyncPartsWithRawResponse(self._uploads.parts)
+
+
+class UploadsWithStreamingResponse:
+ def __init__(self, uploads: Uploads) -> None:
+ self._uploads = uploads
+
+ self.create = to_streamed_response_wrapper(
+ uploads.create,
+ )
+ self.cancel = to_streamed_response_wrapper(
+ uploads.cancel,
+ )
+ self.complete = to_streamed_response_wrapper(
+ uploads.complete,
+ )
+
+ @cached_property
+ def parts(self) -> PartsWithStreamingResponse:
+ return PartsWithStreamingResponse(self._uploads.parts)
+
+
+class AsyncUploadsWithStreamingResponse:
+ def __init__(self, uploads: AsyncUploads) -> None:
+ self._uploads = uploads
+
+ self.create = async_to_streamed_response_wrapper(
+ uploads.create,
+ )
+ self.cancel = async_to_streamed_response_wrapper(
+ uploads.cancel,
+ )
+ self.complete = async_to_streamed_response_wrapper(
+ uploads.complete,
+ )
+
+ @cached_property
+ def parts(self) -> AsyncPartsWithStreamingResponse:
+ return AsyncPartsWithStreamingResponse(self._uploads.parts)
diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py
index 7873efb34f..71f4a59b9e 100644
--- a/src/openai/types/__init__.py
+++ b/src/openai/types/__init__.py
@@ -10,6 +10,7 @@
FunctionDefinition as FunctionDefinition,
FunctionParameters as FunctionParameters,
)
+from .upload import Upload as Upload
from .embedding import Embedding as Embedding
from .chat_model import ChatModel as ChatModel
from .completion import Completion as Completion
@@ -28,7 +29,9 @@
from .file_create_params import FileCreateParams as FileCreateParams
from .batch_create_params import BatchCreateParams as BatchCreateParams
from .batch_request_counts import BatchRequestCounts as BatchRequestCounts
+from .upload_create_params import UploadCreateParams as UploadCreateParams
from .image_generate_params import ImageGenerateParams as ImageGenerateParams
+from .upload_complete_params import UploadCompleteParams as UploadCompleteParams
from .embedding_create_params import EmbeddingCreateParams as EmbeddingCreateParams
from .completion_create_params import CompletionCreateParams as CompletionCreateParams
from .moderation_create_params import ModerationCreateParams as ModerationCreateParams
diff --git a/src/openai/types/chat/completion_create_params.py b/src/openai/types/chat/completion_create_params.py
index 85157653f2..783922539f 100644
--- a/src/openai/types/chat/completion_create_params.py
+++ b/src/openai/types/chat/completion_create_params.py
@@ -155,6 +155,7 @@ class CompletionCreateParamsBase(TypedDict, total=False):
exhausted.
- If set to 'default', the request will be processed using the default service
tier with a lower uptime SLA and no latency guarentee.
+ - When not set, the default behavior is 'auto'.
When this parameter is set, the response body will include the `service_tier`
utilized.
diff --git a/src/openai/types/upload.py b/src/openai/types/upload.py
new file mode 100644
index 0000000000..1cf8ee97f8
--- /dev/null
+++ b/src/openai/types/upload.py
@@ -0,0 +1,42 @@
+# 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
+from .file_object import FileObject
+
+__all__ = ["Upload"]
+
+
+class Upload(BaseModel):
+ id: str
+ """The Upload unique identifier, which can be referenced in API endpoints."""
+
+ bytes: int
+ """The intended number of bytes to be uploaded."""
+
+ created_at: int
+ """The Unix timestamp (in seconds) for when the Upload was created."""
+
+ expires_at: int
+ """The Unix timestamp (in seconds) for when the Upload was created."""
+
+ filename: str
+ """The name of the file to be uploaded."""
+
+ object: Literal["upload"]
+ """The object type, which is always "upload"."""
+
+ purpose: str
+ """The intended purpose of the file.
+
+ [Please refer here](https://platform.openai.com/docs/api-reference/files/object#files/object-purpose)
+ for acceptable values.
+ """
+
+ status: Literal["pending", "completed", "cancelled", "expired"]
+ """The status of the Upload."""
+
+ file: Optional[FileObject] = None
+ """The ready File object after the Upload is completed."""
diff --git a/src/openai/types/upload_complete_params.py b/src/openai/types/upload_complete_params.py
new file mode 100644
index 0000000000..cce568d5c6
--- /dev/null
+++ b/src/openai/types/upload_complete_params.py
@@ -0,0 +1,19 @@
+# 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__ = ["UploadCompleteParams"]
+
+
+class UploadCompleteParams(TypedDict, total=False):
+ part_ids: Required[List[str]]
+ """The ordered list of Part IDs."""
+
+ md5: str
+ """
+ The optional md5 checksum for the file contents to verify if the bytes uploaded
+ matches what you expect.
+ """
diff --git a/src/openai/types/upload_create_params.py b/src/openai/types/upload_create_params.py
new file mode 100644
index 0000000000..3165ebcc7a
--- /dev/null
+++ b/src/openai/types/upload_create_params.py
@@ -0,0 +1,29 @@
+# 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__ = ["UploadCreateParams"]
+
+
+class UploadCreateParams(TypedDict, total=False):
+ bytes: Required[int]
+ """The number of bytes in the file you are uploading."""
+
+ filename: Required[str]
+ """The name of the file to upload."""
+
+ mime_type: Required[str]
+ """The MIME type of the file.
+
+ This must fall within the supported MIME types for your file purpose. See the
+ supported MIME types for assistants and vision.
+ """
+
+ purpose: Required[Literal["assistants", "batch", "fine-tune", "vision"]]
+ """The intended purpose of the uploaded file.
+
+ See the
+ [documentation on File purposes](https://platform.openai.com/docs/api-reference/files/create#files-create-purpose).
+ """
diff --git a/src/openai/types/uploads/__init__.py b/src/openai/types/uploads/__init__.py
new file mode 100644
index 0000000000..41deb0ab4b
--- /dev/null
+++ b/src/openai/types/uploads/__init__.py
@@ -0,0 +1,6 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from .upload_part import UploadPart as UploadPart
+from .part_create_params import PartCreateParams as PartCreateParams
diff --git a/src/openai/types/uploads/part_create_params.py b/src/openai/types/uploads/part_create_params.py
new file mode 100644
index 0000000000..9851ca41e9
--- /dev/null
+++ b/src/openai/types/uploads/part_create_params.py
@@ -0,0 +1,14 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+from ..._types import FileTypes
+
+__all__ = ["PartCreateParams"]
+
+
+class PartCreateParams(TypedDict, total=False):
+ data: Required[FileTypes]
+ """The chunk of bytes for this Part."""
diff --git a/src/openai/types/uploads/upload_part.py b/src/openai/types/uploads/upload_part.py
new file mode 100644
index 0000000000..e09621d8f9
--- /dev/null
+++ b/src/openai/types/uploads/upload_part.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["UploadPart"]
+
+
+class UploadPart(BaseModel):
+ id: str
+ """The upload Part unique identifier, which can be referenced in API endpoints."""
+
+ created_at: int
+ """The Unix timestamp (in seconds) for when the Part was created."""
+
+ object: Literal["upload.part"]
+ """The object type, which is always `upload.part`."""
+
+ upload_id: str
+ """The ID of the Upload object that this Part was added to."""
diff --git a/tests/api_resources/test_uploads.py b/tests/api_resources/test_uploads.py
new file mode 100644
index 0000000000..cb62df6b51
--- /dev/null
+++ b/tests/api_resources/test_uploads.py
@@ -0,0 +1,280 @@
+# 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 openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.types import Upload
+
+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: OpenAI) -> None:
+ upload = client.uploads.create(
+ bytes=0,
+ filename="filename",
+ mime_type="mime_type",
+ purpose="assistants",
+ )
+ assert_matches_type(Upload, upload, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: OpenAI) -> None:
+ response = client.uploads.with_raw_response.create(
+ bytes=0,
+ filename="filename",
+ mime_type="mime_type",
+ purpose="assistants",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ upload = response.parse()
+ assert_matches_type(Upload, upload, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: OpenAI) -> None:
+ with client.uploads.with_streaming_response.create(
+ bytes=0,
+ filename="filename",
+ mime_type="mime_type",
+ purpose="assistants",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ upload = response.parse()
+ assert_matches_type(Upload, upload, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_cancel(self, client: OpenAI) -> None:
+ upload = client.uploads.cancel(
+ "upload_abc123",
+ )
+ assert_matches_type(Upload, upload, path=["response"])
+
+ @parametrize
+ def test_raw_response_cancel(self, client: OpenAI) -> None:
+ response = client.uploads.with_raw_response.cancel(
+ "upload_abc123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ upload = response.parse()
+ assert_matches_type(Upload, upload, path=["response"])
+
+ @parametrize
+ def test_streaming_response_cancel(self, client: OpenAI) -> None:
+ with client.uploads.with_streaming_response.cancel(
+ "upload_abc123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ upload = response.parse()
+ assert_matches_type(Upload, upload, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_cancel(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"):
+ client.uploads.with_raw_response.cancel(
+ "",
+ )
+
+ @parametrize
+ def test_method_complete(self, client: OpenAI) -> None:
+ upload = client.uploads.complete(
+ upload_id="upload_abc123",
+ part_ids=["string", "string", "string"],
+ )
+ assert_matches_type(Upload, upload, path=["response"])
+
+ @parametrize
+ def test_method_complete_with_all_params(self, client: OpenAI) -> None:
+ upload = client.uploads.complete(
+ upload_id="upload_abc123",
+ part_ids=["string", "string", "string"],
+ md5="md5",
+ )
+ assert_matches_type(Upload, upload, path=["response"])
+
+ @parametrize
+ def test_raw_response_complete(self, client: OpenAI) -> None:
+ response = client.uploads.with_raw_response.complete(
+ upload_id="upload_abc123",
+ part_ids=["string", "string", "string"],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ upload = response.parse()
+ assert_matches_type(Upload, upload, path=["response"])
+
+ @parametrize
+ def test_streaming_response_complete(self, client: OpenAI) -> None:
+ with client.uploads.with_streaming_response.complete(
+ upload_id="upload_abc123",
+ part_ids=["string", "string", "string"],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ upload = response.parse()
+ assert_matches_type(Upload, upload, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_complete(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"):
+ client.uploads.with_raw_response.complete(
+ upload_id="",
+ part_ids=["string", "string", "string"],
+ )
+
+
+class TestAsyncUploads:
+ parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncOpenAI) -> None:
+ upload = await async_client.uploads.create(
+ bytes=0,
+ filename="filename",
+ mime_type="mime_type",
+ purpose="assistants",
+ )
+ assert_matches_type(Upload, upload, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.uploads.with_raw_response.create(
+ bytes=0,
+ filename="filename",
+ mime_type="mime_type",
+ purpose="assistants",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ upload = response.parse()
+ assert_matches_type(Upload, upload, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.uploads.with_streaming_response.create(
+ bytes=0,
+ filename="filename",
+ mime_type="mime_type",
+ purpose="assistants",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ upload = await response.parse()
+ assert_matches_type(Upload, upload, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_cancel(self, async_client: AsyncOpenAI) -> None:
+ upload = await async_client.uploads.cancel(
+ "upload_abc123",
+ )
+ assert_matches_type(Upload, upload, path=["response"])
+
+ @parametrize
+ async def test_raw_response_cancel(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.uploads.with_raw_response.cancel(
+ "upload_abc123",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ upload = response.parse()
+ assert_matches_type(Upload, upload, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_cancel(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.uploads.with_streaming_response.cancel(
+ "upload_abc123",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ upload = await response.parse()
+ assert_matches_type(Upload, upload, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_cancel(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"):
+ await async_client.uploads.with_raw_response.cancel(
+ "",
+ )
+
+ @parametrize
+ async def test_method_complete(self, async_client: AsyncOpenAI) -> None:
+ upload = await async_client.uploads.complete(
+ upload_id="upload_abc123",
+ part_ids=["string", "string", "string"],
+ )
+ assert_matches_type(Upload, upload, path=["response"])
+
+ @parametrize
+ async def test_method_complete_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ upload = await async_client.uploads.complete(
+ upload_id="upload_abc123",
+ part_ids=["string", "string", "string"],
+ md5="md5",
+ )
+ assert_matches_type(Upload, upload, path=["response"])
+
+ @parametrize
+ async def test_raw_response_complete(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.uploads.with_raw_response.complete(
+ upload_id="upload_abc123",
+ part_ids=["string", "string", "string"],
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ upload = response.parse()
+ assert_matches_type(Upload, upload, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_complete(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.uploads.with_streaming_response.complete(
+ upload_id="upload_abc123",
+ part_ids=["string", "string", "string"],
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ upload = await response.parse()
+ assert_matches_type(Upload, upload, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_complete(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"):
+ await async_client.uploads.with_raw_response.complete(
+ upload_id="",
+ part_ids=["string", "string", "string"],
+ )
diff --git a/tests/api_resources/uploads/__init__.py b/tests/api_resources/uploads/__init__.py
new file mode 100644
index 0000000000..fd8019a9a1
--- /dev/null
+++ b/tests/api_resources/uploads/__init__.py
@@ -0,0 +1 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
diff --git a/tests/api_resources/uploads/test_parts.py b/tests/api_resources/uploads/test_parts.py
new file mode 100644
index 0000000000..2bba241a6d
--- /dev/null
+++ b/tests/api_resources/uploads/test_parts.py
@@ -0,0 +1,106 @@
+# 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 openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai.types.uploads import UploadPart
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestParts:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: OpenAI) -> None:
+ part = client.uploads.parts.create(
+ upload_id="upload_abc123",
+ data=b"raw file contents",
+ )
+ assert_matches_type(UploadPart, part, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: OpenAI) -> None:
+ response = client.uploads.parts.with_raw_response.create(
+ upload_id="upload_abc123",
+ data=b"raw file contents",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ part = response.parse()
+ assert_matches_type(UploadPart, part, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: OpenAI) -> None:
+ with client.uploads.parts.with_streaming_response.create(
+ upload_id="upload_abc123",
+ data=b"raw file contents",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ part = response.parse()
+ assert_matches_type(UploadPart, part, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_create(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"):
+ client.uploads.parts.with_raw_response.create(
+ upload_id="",
+ data=b"raw file contents",
+ )
+
+
+class TestAsyncParts:
+ parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncOpenAI) -> None:
+ part = await async_client.uploads.parts.create(
+ upload_id="upload_abc123",
+ data=b"raw file contents",
+ )
+ assert_matches_type(UploadPart, part, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.uploads.parts.with_raw_response.create(
+ upload_id="upload_abc123",
+ data=b"raw file contents",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ part = response.parse()
+ assert_matches_type(UploadPart, part, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.uploads.parts.with_streaming_response.create(
+ upload_id="upload_abc123",
+ data=b"raw file contents",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ part = await response.parse()
+ assert_matches_type(UploadPart, part, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_create(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `upload_id` but received ''"):
+ await async_client.uploads.parts.with_raw_response.create(
+ upload_id="",
+ data=b"raw file contents",
+ )