diff --git a/.stats.yml b/.stats.yml
index 284caebf44..47c2bce1cc 100644
--- a/.stats.yml
+++ b/.stats.yml
@@ -1 +1 @@
-configured_endpoints: 52
+configured_endpoints: 55
diff --git a/api.md b/api.md
index cc3c91a8d5..38f77592e8 100644
--- a/api.md
+++ b/api.md
@@ -361,3 +361,17 @@ Methods:
- client.beta.threads.messages.files.retrieve(file_id, \*, thread_id, message_id) -> MessageFile
- client.beta.threads.messages.files.list(message_id, \*, thread_id, \*\*params) -> SyncCursorPage[MessageFile]
+
+# Batches
+
+Types:
+
+```python
+from openai.types import Batch, BatchError, BatchRequestCounts
+```
+
+Methods:
+
+- client.batches.create(\*\*params) -> Batch
+- client.batches.retrieve(batch_id) -> Batch
+- client.batches.cancel(batch_id) -> Batch
diff --git a/src/openai/__init__.py b/src/openai/__init__.py
index 1daa26f7b7..490ba017f0 100644
--- a/src/openai/__init__.py
+++ b/src/openai/__init__.py
@@ -335,6 +335,7 @@ def _reset_client() -> None: # type: ignore[reportUnusedFunction]
files as files,
images as images,
models as models,
+ batches as batches,
embeddings as embeddings,
completions as completions,
fine_tuning as fine_tuning,
diff --git a/src/openai/_client.py b/src/openai/_client.py
index e9169df72a..5a6852e571 100644
--- a/src/openai/_client.py
+++ b/src/openai/_client.py
@@ -57,6 +57,7 @@ class OpenAI(SyncAPIClient):
models: resources.Models
fine_tuning: resources.FineTuning
beta: resources.Beta
+ batches: resources.Batches
with_raw_response: OpenAIWithRawResponse
with_streaming_response: OpenAIWithStreamedResponse
@@ -134,6 +135,7 @@ def __init__(
self.models = resources.Models(self)
self.fine_tuning = resources.FineTuning(self)
self.beta = resources.Beta(self)
+ self.batches = resources.Batches(self)
self.with_raw_response = OpenAIWithRawResponse(self)
self.with_streaming_response = OpenAIWithStreamedResponse(self)
@@ -257,6 +259,7 @@ class AsyncOpenAI(AsyncAPIClient):
models: resources.AsyncModels
fine_tuning: resources.AsyncFineTuning
beta: resources.AsyncBeta
+ batches: resources.AsyncBatches
with_raw_response: AsyncOpenAIWithRawResponse
with_streaming_response: AsyncOpenAIWithStreamedResponse
@@ -334,6 +337,7 @@ def __init__(
self.models = resources.AsyncModels(self)
self.fine_tuning = resources.AsyncFineTuning(self)
self.beta = resources.AsyncBeta(self)
+ self.batches = resources.AsyncBatches(self)
self.with_raw_response = AsyncOpenAIWithRawResponse(self)
self.with_streaming_response = AsyncOpenAIWithStreamedResponse(self)
@@ -458,6 +462,7 @@ def __init__(self, client: OpenAI) -> None:
self.models = resources.ModelsWithRawResponse(client.models)
self.fine_tuning = resources.FineTuningWithRawResponse(client.fine_tuning)
self.beta = resources.BetaWithRawResponse(client.beta)
+ self.batches = resources.BatchesWithRawResponse(client.batches)
class AsyncOpenAIWithRawResponse:
@@ -472,6 +477,7 @@ def __init__(self, client: AsyncOpenAI) -> None:
self.models = resources.AsyncModelsWithRawResponse(client.models)
self.fine_tuning = resources.AsyncFineTuningWithRawResponse(client.fine_tuning)
self.beta = resources.AsyncBetaWithRawResponse(client.beta)
+ self.batches = resources.AsyncBatchesWithRawResponse(client.batches)
class OpenAIWithStreamedResponse:
@@ -486,6 +492,7 @@ def __init__(self, client: OpenAI) -> None:
self.models = resources.ModelsWithStreamingResponse(client.models)
self.fine_tuning = resources.FineTuningWithStreamingResponse(client.fine_tuning)
self.beta = resources.BetaWithStreamingResponse(client.beta)
+ self.batches = resources.BatchesWithStreamingResponse(client.batches)
class AsyncOpenAIWithStreamedResponse:
@@ -500,6 +507,7 @@ def __init__(self, client: AsyncOpenAI) -> None:
self.models = resources.AsyncModelsWithStreamingResponse(client.models)
self.fine_tuning = resources.AsyncFineTuningWithStreamingResponse(client.fine_tuning)
self.beta = resources.AsyncBetaWithStreamingResponse(client.beta)
+ self.batches = resources.AsyncBatchesWithStreamingResponse(client.batches)
Client = OpenAI
diff --git a/src/openai/_module_client.py b/src/openai/_module_client.py
index 9227f5e2b4..6f7356eb3c 100644
--- a/src/openai/_module_client.py
+++ b/src/openai/_module_client.py
@@ -42,6 +42,12 @@ def __load__(self) -> resources.Models:
return _load_client().models
+class BatchesProxy(LazyProxy[resources.Batches]):
+ @override
+ def __load__(self) -> resources.Batches:
+ return _load_client().batches
+
+
class EmbeddingsProxy(LazyProxy[resources.Embeddings]):
@override
def __load__(self) -> resources.Embeddings:
@@ -72,6 +78,7 @@ def __load__(self) -> resources.FineTuning:
audio: resources.Audio = AudioProxy().__as_proxied__()
images: resources.Images = ImagesProxy().__as_proxied__()
models: resources.Models = ModelsProxy().__as_proxied__()
+batches: resources.Batches = BatchesProxy().__as_proxied__()
embeddings: resources.Embeddings = EmbeddingsProxy().__as_proxied__()
completions: resources.Completions = CompletionsProxy().__as_proxied__()
moderations: resources.Moderations = ModerationsProxy().__as_proxied__()
diff --git a/src/openai/resources/__init__.py b/src/openai/resources/__init__.py
index 64aa12d260..ecae4243fc 100644
--- a/src/openai/resources/__init__.py
+++ b/src/openai/resources/__init__.py
@@ -48,6 +48,14 @@
ModelsWithStreamingResponse,
AsyncModelsWithStreamingResponse,
)
+from .batches import (
+ Batches,
+ AsyncBatches,
+ BatchesWithRawResponse,
+ AsyncBatchesWithRawResponse,
+ BatchesWithStreamingResponse,
+ AsyncBatchesWithStreamingResponse,
+)
from .embeddings import (
Embeddings,
AsyncEmbeddings,
@@ -142,4 +150,10 @@
"AsyncBetaWithRawResponse",
"BetaWithStreamingResponse",
"AsyncBetaWithStreamingResponse",
+ "Batches",
+ "AsyncBatches",
+ "BatchesWithRawResponse",
+ "AsyncBatchesWithRawResponse",
+ "BatchesWithStreamingResponse",
+ "AsyncBatchesWithStreamingResponse",
]
diff --git a/src/openai/resources/batches.py b/src/openai/resources/batches.py
new file mode 100644
index 0000000000..0921ccb194
--- /dev/null
+++ b/src/openai/resources/batches.py
@@ -0,0 +1,354 @@
+# 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 Literal
+
+import httpx
+
+from .. import _legacy_response
+from ..types import Batch, batch_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_streamed_response_wrapper, async_to_streamed_response_wrapper
+from .._base_client import (
+ make_request_options,
+)
+
+__all__ = ["Batches", "AsyncBatches"]
+
+
+class Batches(SyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> BatchesWithRawResponse:
+ return BatchesWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> BatchesWithStreamingResponse:
+ return BatchesWithStreamingResponse(self)
+
+ def create(
+ self,
+ *,
+ completion_window: Literal["24h"],
+ endpoint: Literal["/v1/chat/completions"],
+ input_file_id: str,
+ metadata: Optional[Dict[str, 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,
+ ) -> Batch:
+ """
+ Creates and executes a batch from an uploaded file of requests
+
+ Args:
+ completion_window: The time frame within which the batch should be processed. Currently only `24h`
+ is supported.
+
+ endpoint: The endpoint to be used for all requests in the batch. Currently only
+ `/v1/chat/completions` is supported.
+
+ input_file_id: The ID of an uploaded file that contains requests for the new batch.
+
+ See [upload file](https://platform.openai.com/docs/api-reference/files/create)
+ for how to upload a file.
+
+ Your input file must be formatted as a JSONL file, and must be uploaded with the
+ purpose `batch`.
+
+ metadata: Optional custom metadata for the batch.
+
+ 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(
+ "/batches",
+ body=maybe_transform(
+ {
+ "completion_window": completion_window,
+ "endpoint": endpoint,
+ "input_file_id": input_file_id,
+ "metadata": metadata,
+ },
+ batch_create_params.BatchCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Batch,
+ )
+
+ def retrieve(
+ self,
+ batch_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,
+ ) -> Batch:
+ """
+ Retrieves a batch.
+
+ 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 batch_id:
+ raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}")
+ return self._get(
+ f"/batches/{batch_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Batch,
+ )
+
+ def cancel(
+ self,
+ batch_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,
+ ) -> Batch:
+ """
+ Cancels an in-progress batch.
+
+ 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 batch_id:
+ raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}")
+ return self._post(
+ f"/batches/{batch_id}/cancel",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Batch,
+ )
+
+
+class AsyncBatches(AsyncAPIResource):
+ @cached_property
+ def with_raw_response(self) -> AsyncBatchesWithRawResponse:
+ return AsyncBatchesWithRawResponse(self)
+
+ @cached_property
+ def with_streaming_response(self) -> AsyncBatchesWithStreamingResponse:
+ return AsyncBatchesWithStreamingResponse(self)
+
+ async def create(
+ self,
+ *,
+ completion_window: Literal["24h"],
+ endpoint: Literal["/v1/chat/completions"],
+ input_file_id: str,
+ metadata: Optional[Dict[str, 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,
+ ) -> Batch:
+ """
+ Creates and executes a batch from an uploaded file of requests
+
+ Args:
+ completion_window: The time frame within which the batch should be processed. Currently only `24h`
+ is supported.
+
+ endpoint: The endpoint to be used for all requests in the batch. Currently only
+ `/v1/chat/completions` is supported.
+
+ input_file_id: The ID of an uploaded file that contains requests for the new batch.
+
+ See [upload file](https://platform.openai.com/docs/api-reference/files/create)
+ for how to upload a file.
+
+ Your input file must be formatted as a JSONL file, and must be uploaded with the
+ purpose `batch`.
+
+ metadata: Optional custom metadata for the batch.
+
+ 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(
+ "/batches",
+ body=await async_maybe_transform(
+ {
+ "completion_window": completion_window,
+ "endpoint": endpoint,
+ "input_file_id": input_file_id,
+ "metadata": metadata,
+ },
+ batch_create_params.BatchCreateParams,
+ ),
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Batch,
+ )
+
+ async def retrieve(
+ self,
+ batch_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,
+ ) -> Batch:
+ """
+ Retrieves a batch.
+
+ 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 batch_id:
+ raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}")
+ return await self._get(
+ f"/batches/{batch_id}",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Batch,
+ )
+
+ async def cancel(
+ self,
+ batch_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,
+ ) -> Batch:
+ """
+ Cancels an in-progress batch.
+
+ 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 batch_id:
+ raise ValueError(f"Expected a non-empty value for `batch_id` but received {batch_id!r}")
+ return await self._post(
+ f"/batches/{batch_id}/cancel",
+ options=make_request_options(
+ extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+ ),
+ cast_to=Batch,
+ )
+
+
+class BatchesWithRawResponse:
+ def __init__(self, batches: Batches) -> None:
+ self._batches = batches
+
+ self.create = _legacy_response.to_raw_response_wrapper(
+ batches.create,
+ )
+ self.retrieve = _legacy_response.to_raw_response_wrapper(
+ batches.retrieve,
+ )
+ self.cancel = _legacy_response.to_raw_response_wrapper(
+ batches.cancel,
+ )
+
+
+class AsyncBatchesWithRawResponse:
+ def __init__(self, batches: AsyncBatches) -> None:
+ self._batches = batches
+
+ self.create = _legacy_response.async_to_raw_response_wrapper(
+ batches.create,
+ )
+ self.retrieve = _legacy_response.async_to_raw_response_wrapper(
+ batches.retrieve,
+ )
+ self.cancel = _legacy_response.async_to_raw_response_wrapper(
+ batches.cancel,
+ )
+
+
+class BatchesWithStreamingResponse:
+ def __init__(self, batches: Batches) -> None:
+ self._batches = batches
+
+ self.create = to_streamed_response_wrapper(
+ batches.create,
+ )
+ self.retrieve = to_streamed_response_wrapper(
+ batches.retrieve,
+ )
+ self.cancel = to_streamed_response_wrapper(
+ batches.cancel,
+ )
+
+
+class AsyncBatchesWithStreamingResponse:
+ def __init__(self, batches: AsyncBatches) -> None:
+ self._batches = batches
+
+ self.create = async_to_streamed_response_wrapper(
+ batches.create,
+ )
+ self.retrieve = async_to_streamed_response_wrapper(
+ batches.retrieve,
+ )
+ self.cancel = async_to_streamed_response_wrapper(
+ batches.cancel,
+ )
diff --git a/src/openai/types/__init__.py b/src/openai/types/__init__.py
index 0917e22a8f..4bbcdddc2a 100644
--- a/src/openai/types/__init__.py
+++ b/src/openai/types/__init__.py
@@ -2,6 +2,7 @@
from __future__ import annotations
+from .batch import Batch as Batch
from .image import Image as Image
from .model import Model as Model
from .shared import (
@@ -12,6 +13,7 @@
from .embedding import Embedding as Embedding
from .completion import Completion as Completion
from .moderation import Moderation as Moderation
+from .batch_error import BatchError as BatchError
from .file_object import FileObject as FileObject
from .file_content import FileContent as FileContent
from .file_deleted import FileDeleted as FileDeleted
@@ -22,6 +24,8 @@
from .completion_choice import CompletionChoice as CompletionChoice
from .image_edit_params import ImageEditParams as ImageEditParams
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 .image_generate_params import ImageGenerateParams as ImageGenerateParams
from .embedding_create_params import EmbeddingCreateParams as EmbeddingCreateParams
from .completion_create_params import CompletionCreateParams as CompletionCreateParams
diff --git a/src/openai/types/batch.py b/src/openai/types/batch.py
new file mode 100644
index 0000000000..bde04d1a24
--- /dev/null
+++ b/src/openai/types/batch.py
@@ -0,0 +1,85 @@
+# 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
+from .batch_error import BatchError
+from .batch_request_counts import BatchRequestCounts
+
+__all__ = ["Batch", "Errors"]
+
+
+class Errors(BaseModel):
+ data: Optional[List[BatchError]] = None
+
+ object: Optional[str] = None
+ """The object type, which is always `list`."""
+
+
+class Batch(BaseModel):
+ id: str
+
+ completion_window: str
+ """The time frame within which the batch should be processed."""
+
+ created_at: str
+ """The Unix timestamp (in seconds) for when the batch was created."""
+
+ endpoint: str
+ """The OpenAI API endpoint used by the batch."""
+
+ input_file_id: str
+ """The ID of the input file for the batch."""
+
+ object: Literal["batch"]
+ """The object type, which is always `batch`."""
+
+ status: Literal[
+ "validating", "failed", "in_progress", "finalizing", "completed", "expired", "cancelling", "cancelled"
+ ]
+ """The current status of the batch."""
+
+ cancelled_at: Optional[str] = None
+ """The Unix timestamp (in seconds) for when the batch was cancelled."""
+
+ cancelling_at: Optional[str] = None
+ """The Unix timestamp (in seconds) for when the batch started cancelling."""
+
+ completed_at: Optional[str] = None
+ """The Unix timestamp (in seconds) for when the batch was completed."""
+
+ error_file_id: Optional[str] = None
+ """The ID of the file containing the outputs of requests with errors."""
+
+ errors: Optional[Errors] = None
+
+ expired_at: Optional[str] = None
+ """The Unix timestamp (in seconds) for when the batch expired."""
+
+ expires_at: Optional[str] = None
+ """The Unix timestamp (in seconds) for when the batch will expire."""
+
+ failed_at: Optional[str] = None
+ """The Unix timestamp (in seconds) for when the batch failed."""
+
+ finalizing_at: Optional[str] = None
+ """The Unix timestamp (in seconds) for when the batch started finalizing."""
+
+ in_progress_at: Optional[str] = None
+ """The Unix timestamp (in seconds) for when the batch started processing."""
+
+ metadata: Optional[builtins.object] = None
+ """Set of 16 key-value pairs that can be attached to an object.
+
+ This can be useful for storing additional information about the object in a
+ structured format. Keys can be a maximum of 64 characters long and values can be
+ a maxium of 512 characters long.
+ """
+
+ output_file_id: Optional[str] = None
+ """The ID of the file containing the outputs of successfully executed requests."""
+
+ request_counts: Optional[BatchRequestCounts] = None
+ """The request counts for different statuses within the batch."""
diff --git a/src/openai/types/batch_create_params.py b/src/openai/types/batch_create_params.py
new file mode 100644
index 0000000000..6a22be8626
--- /dev/null
+++ b/src/openai/types/batch_create_params.py
@@ -0,0 +1,35 @@
+# 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 Literal, Required, TypedDict
+
+__all__ = ["BatchCreateParams"]
+
+
+class BatchCreateParams(TypedDict, total=False):
+ completion_window: Required[Literal["24h"]]
+ """The time frame within which the batch should be processed.
+
+ Currently only `24h` is supported.
+ """
+
+ endpoint: Required[Literal["/v1/chat/completions"]]
+ """The endpoint to be used for all requests in the batch.
+
+ Currently only `/v1/chat/completions` is supported.
+ """
+
+ input_file_id: Required[str]
+ """The ID of an uploaded file that contains requests for the new batch.
+
+ See [upload file](https://platform.openai.com/docs/api-reference/files/create)
+ for how to upload a file.
+
+ Your input file must be formatted as a JSONL file, and must be uploaded with the
+ purpose `batch`.
+ """
+
+ metadata: Optional[Dict[str, str]]
+ """Optional custom metadata for the batch."""
diff --git a/src/openai/types/batch_error.py b/src/openai/types/batch_error.py
new file mode 100644
index 0000000000..1cdd808dbd
--- /dev/null
+++ b/src/openai/types/batch_error.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from typing import Optional
+
+from .._models import BaseModel
+
+__all__ = ["BatchError"]
+
+
+class BatchError(BaseModel):
+ code: Optional[str] = None
+ """An error code identifying the error type."""
+
+ line: Optional[int] = None
+ """The line number of the input file where the error occurred, if applicable."""
+
+ message: Optional[str] = None
+ """A human-readable message providing more details about the error."""
+
+ param: Optional[str] = None
+ """The name of the parameter that caused the error, if applicable."""
diff --git a/src/openai/types/batch_request_counts.py b/src/openai/types/batch_request_counts.py
new file mode 100644
index 0000000000..068b071af1
--- /dev/null
+++ b/src/openai/types/batch_request_counts.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
+
+from .._models import BaseModel
+
+__all__ = ["BatchRequestCounts"]
+
+
+class BatchRequestCounts(BaseModel):
+ completed: int
+ """Number of requests that have been completed successfully."""
+
+ failed: int
+ """Number of requests that have failed."""
+
+ total: int
+ """Total number of requests in the batch."""
diff --git a/tests/api_resources/test_batches.py b/tests/api_resources/test_batches.py
new file mode 100644
index 0000000000..aafeff8116
--- /dev/null
+++ b/tests/api_resources/test_batches.py
@@ -0,0 +1,268 @@
+# 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 Batch
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+
+
+class TestBatches:
+ parametrize = pytest.mark.parametrize("client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ def test_method_create(self, client: OpenAI) -> None:
+ batch = client.batches.create(
+ completion_window="24h",
+ endpoint="/v1/chat/completions",
+ input_file_id="string",
+ )
+ assert_matches_type(Batch, batch, path=["response"])
+
+ @parametrize
+ def test_method_create_with_all_params(self, client: OpenAI) -> None:
+ batch = client.batches.create(
+ completion_window="24h",
+ endpoint="/v1/chat/completions",
+ input_file_id="string",
+ metadata={"foo": "string"},
+ )
+ assert_matches_type(Batch, batch, path=["response"])
+
+ @parametrize
+ def test_raw_response_create(self, client: OpenAI) -> None:
+ response = client.batches.with_raw_response.create(
+ completion_window="24h",
+ endpoint="/v1/chat/completions",
+ input_file_id="string",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ batch = response.parse()
+ assert_matches_type(Batch, batch, path=["response"])
+
+ @parametrize
+ def test_streaming_response_create(self, client: OpenAI) -> None:
+ with client.batches.with_streaming_response.create(
+ completion_window="24h",
+ endpoint="/v1/chat/completions",
+ input_file_id="string",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ batch = response.parse()
+ assert_matches_type(Batch, batch, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_method_retrieve(self, client: OpenAI) -> None:
+ batch = client.batches.retrieve(
+ "string",
+ )
+ assert_matches_type(Batch, batch, path=["response"])
+
+ @parametrize
+ def test_raw_response_retrieve(self, client: OpenAI) -> None:
+ response = client.batches.with_raw_response.retrieve(
+ "string",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ batch = response.parse()
+ assert_matches_type(Batch, batch, path=["response"])
+
+ @parametrize
+ def test_streaming_response_retrieve(self, client: OpenAI) -> None:
+ with client.batches.with_streaming_response.retrieve(
+ "string",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ batch = response.parse()
+ assert_matches_type(Batch, batch, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ def test_path_params_retrieve(self, client: OpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"):
+ client.batches.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ def test_method_cancel(self, client: OpenAI) -> None:
+ batch = client.batches.cancel(
+ "string",
+ )
+ assert_matches_type(Batch, batch, path=["response"])
+
+ @parametrize
+ def test_raw_response_cancel(self, client: OpenAI) -> None:
+ response = client.batches.with_raw_response.cancel(
+ "string",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ batch = response.parse()
+ assert_matches_type(Batch, batch, path=["response"])
+
+ @parametrize
+ def test_streaming_response_cancel(self, client: OpenAI) -> None:
+ with client.batches.with_streaming_response.cancel(
+ "string",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ batch = response.parse()
+ assert_matches_type(Batch, batch, 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 `batch_id` but received ''"):
+ client.batches.with_raw_response.cancel(
+ "",
+ )
+
+
+class TestAsyncBatches:
+ parametrize = pytest.mark.parametrize("async_client", [False, True], indirect=True, ids=["loose", "strict"])
+
+ @parametrize
+ async def test_method_create(self, async_client: AsyncOpenAI) -> None:
+ batch = await async_client.batches.create(
+ completion_window="24h",
+ endpoint="/v1/chat/completions",
+ input_file_id="string",
+ )
+ assert_matches_type(Batch, batch, path=["response"])
+
+ @parametrize
+ async def test_method_create_with_all_params(self, async_client: AsyncOpenAI) -> None:
+ batch = await async_client.batches.create(
+ completion_window="24h",
+ endpoint="/v1/chat/completions",
+ input_file_id="string",
+ metadata={"foo": "string"},
+ )
+ assert_matches_type(Batch, batch, path=["response"])
+
+ @parametrize
+ async def test_raw_response_create(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.batches.with_raw_response.create(
+ completion_window="24h",
+ endpoint="/v1/chat/completions",
+ input_file_id="string",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ batch = response.parse()
+ assert_matches_type(Batch, batch, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_create(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.batches.with_streaming_response.create(
+ completion_window="24h",
+ endpoint="/v1/chat/completions",
+ input_file_id="string",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ batch = await response.parse()
+ assert_matches_type(Batch, batch, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_method_retrieve(self, async_client: AsyncOpenAI) -> None:
+ batch = await async_client.batches.retrieve(
+ "string",
+ )
+ assert_matches_type(Batch, batch, path=["response"])
+
+ @parametrize
+ async def test_raw_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.batches.with_raw_response.retrieve(
+ "string",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ batch = response.parse()
+ assert_matches_type(Batch, batch, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_retrieve(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.batches.with_streaming_response.retrieve(
+ "string",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ batch = await response.parse()
+ assert_matches_type(Batch, batch, path=["response"])
+
+ assert cast(Any, response.is_closed) is True
+
+ @parametrize
+ async def test_path_params_retrieve(self, async_client: AsyncOpenAI) -> None:
+ with pytest.raises(ValueError, match=r"Expected a non-empty value for `batch_id` but received ''"):
+ await async_client.batches.with_raw_response.retrieve(
+ "",
+ )
+
+ @parametrize
+ async def test_method_cancel(self, async_client: AsyncOpenAI) -> None:
+ batch = await async_client.batches.cancel(
+ "string",
+ )
+ assert_matches_type(Batch, batch, path=["response"])
+
+ @parametrize
+ async def test_raw_response_cancel(self, async_client: AsyncOpenAI) -> None:
+ response = await async_client.batches.with_raw_response.cancel(
+ "string",
+ )
+
+ assert response.is_closed is True
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+ batch = response.parse()
+ assert_matches_type(Batch, batch, path=["response"])
+
+ @parametrize
+ async def test_streaming_response_cancel(self, async_client: AsyncOpenAI) -> None:
+ async with async_client.batches.with_streaming_response.cancel(
+ "string",
+ ) as response:
+ assert not response.is_closed
+ assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+
+ batch = await response.parse()
+ assert_matches_type(Batch, batch, 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 `batch_id` but received ''"):
+ await async_client.batches.with_raw_response.cancel(
+ "",
+ )