Commit baa9f07f

Robert Craigie <robert@craigie.dev>
2023-11-07 04:35:10
feat(api): releases from DevDay; assistants, multimodality, tools, dall-e-3, tts, and more (#682) tag: v1.1.0
* feat(api): releases from DevDay; assistants, multimodality, tools, dall-e-3, tts, and more * docs(api): improve docstrings * v1.1.0
1 parent 26d1a93
Changed files (144)
examples
src
openai
cli
resources
types
audio
beta
chat
fine_tuning
tests
examples/async_demo.py
examples/azure.py
examples/azure_ad.py
examples/demo.py
examples/module_client.py
src/openai/cli/_api/chat/completions.py
@@ -3,7 +3,7 @@ from __future__ import annotations
 import sys
 from typing import TYPE_CHECKING, List, Optional, cast
 from argparse import ArgumentParser
-from typing_extensions import NamedTuple
+from typing_extensions import Literal, NamedTuple
 
 from ..._utils import get_client
 from ..._models import BaseModel
@@ -97,7 +97,9 @@ class CLIChatCompletion:
     def create(args: CLIChatCompletionCreateArgs) -> None:
         params: CompletionCreateParams = {
             "model": args.model,
-            "messages": [{"role": message.role, "content": message.content} for message in args.message],
+            "messages": [
+                {"role": cast(Literal["user"], message.role), "content": message.content} for message in args.message
+            ],
             "n": args.n,
             "temperature": args.temperature,
             "top_p": args.top_p,
src/openai/cli/_api/files.py
@@ -1,6 +1,6 @@
 from __future__ import annotations
 
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Any, cast
 from argparse import ArgumentParser
 
 from .._utils import get_client, print_model
@@ -55,7 +55,12 @@ class CLIFile:
         with open(args.file, "rb") as file_reader:
             buffer_reader = BufferReader(file_reader.read(), desc="Upload progress")
 
-        file = get_client().files.create(file=(args.file, buffer_reader), purpose=args.purpose)
+        file = get_client().files.create(
+            file=(args.file, buffer_reader),
+            # casts required because the API is typed for enums
+            # but we don't want to validate that here for forwards-compat
+            purpose=cast(Any, args.purpose),
+        )
         print_model(file)
 
     @staticmethod
src/openai/resources/audio/__init__.py
@@ -1,6 +1,12 @@
 # File generated from our OpenAPI spec by Stainless.
 
 from .audio import Audio, AsyncAudio, AudioWithRawResponse, AsyncAudioWithRawResponse
+from .speech import (
+    Speech,
+    AsyncSpeech,
+    SpeechWithRawResponse,
+    AsyncSpeechWithRawResponse,
+)
 from .translations import (
     Translations,
     AsyncTranslations,
@@ -23,6 +29,10 @@ __all__ = [
     "AsyncTranslations",
     "TranslationsWithRawResponse",
     "AsyncTranslationsWithRawResponse",
+    "Speech",
+    "AsyncSpeech",
+    "SpeechWithRawResponse",
+    "AsyncSpeechWithRawResponse",
     "Audio",
     "AsyncAudio",
     "AudioWithRawResponse",
src/openai/resources/audio/audio.py
@@ -4,6 +4,12 @@ from __future__ import annotations
 
 from typing import TYPE_CHECKING
 
+from .speech import (
+    Speech,
+    AsyncSpeech,
+    SpeechWithRawResponse,
+    AsyncSpeechWithRawResponse,
+)
 from ..._resource import SyncAPIResource, AsyncAPIResource
 from .translations import (
     Translations,
@@ -27,24 +33,28 @@ __all__ = ["Audio", "AsyncAudio"]
 class Audio(SyncAPIResource):
     transcriptions: Transcriptions
     translations: Translations
+    speech: Speech
     with_raw_response: AudioWithRawResponse
 
     def __init__(self, client: OpenAI) -> None:
         super().__init__(client)
         self.transcriptions = Transcriptions(client)
         self.translations = Translations(client)
+        self.speech = Speech(client)
         self.with_raw_response = AudioWithRawResponse(self)
 
 
 class AsyncAudio(AsyncAPIResource):
     transcriptions: AsyncTranscriptions
     translations: AsyncTranslations
+    speech: AsyncSpeech
     with_raw_response: AsyncAudioWithRawResponse
 
     def __init__(self, client: AsyncOpenAI) -> None:
         super().__init__(client)
         self.transcriptions = AsyncTranscriptions(client)
         self.translations = AsyncTranslations(client)
+        self.speech = AsyncSpeech(client)
         self.with_raw_response = AsyncAudioWithRawResponse(self)
 
 
@@ -52,9 +62,11 @@ class AudioWithRawResponse:
     def __init__(self, audio: Audio) -> None:
         self.transcriptions = TranscriptionsWithRawResponse(audio.transcriptions)
         self.translations = TranslationsWithRawResponse(audio.translations)
+        self.speech = SpeechWithRawResponse(audio.speech)
 
 
 class AsyncAudioWithRawResponse:
     def __init__(self, audio: AsyncAudio) -> None:
         self.transcriptions = AsyncTranscriptionsWithRawResponse(audio.transcriptions)
         self.translations = AsyncTranslationsWithRawResponse(audio.translations)
+        self.speech = AsyncSpeechWithRawResponse(audio.speech)
src/openai/resources/audio/speech.py
@@ -0,0 +1,166 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Union
+from typing_extensions import Literal
+
+from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from ..._utils import maybe_transform
+from ..._resource import SyncAPIResource, AsyncAPIResource
+from ..._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from ...types.audio import speech_create_params
+from ..._base_client import HttpxBinaryResponseContent, make_request_options
+
+if TYPE_CHECKING:
+    from ..._client import OpenAI, AsyncOpenAI
+
+__all__ = ["Speech", "AsyncSpeech"]
+
+
+class Speech(SyncAPIResource):
+    with_raw_response: SpeechWithRawResponse
+
+    def __init__(self, client: OpenAI) -> None:
+        super().__init__(client)
+        self.with_raw_response = SpeechWithRawResponse(self)
+
+    def create(
+        self,
+        *,
+        input: str,
+        model: Union[str, Literal["tts-1", "tts-1-hd"]],
+        voice: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"],
+        response_format: Literal["mp3", "opus", "aac", "flac"] | NotGiven = NOT_GIVEN,
+        speed: float | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> HttpxBinaryResponseContent:
+        """
+        Generates audio from the input text.
+
+        Args:
+          input: The text to generate audio for. The maximum length is 4096 characters.
+
+          model:
+              One of the available [TTS models](https://platform.openai.com/docs/models/tts):
+              `tts-1` or `tts-1-hd`
+
+          voice: The voice to use when generating the audio. Supported voices are `alloy`,
+              `echo`, `fable`, `onyx`, `nova`, and `shimmer`.
+
+          response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, and `flac`.
+
+          speed: The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is
+              the default.
+
+          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(
+            "/audio/speech",
+            body=maybe_transform(
+                {
+                    "input": input,
+                    "model": model,
+                    "voice": voice,
+                    "response_format": response_format,
+                    "speed": speed,
+                },
+                speech_create_params.SpeechCreateParams,
+            ),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=HttpxBinaryResponseContent,
+        )
+
+
+class AsyncSpeech(AsyncAPIResource):
+    with_raw_response: AsyncSpeechWithRawResponse
+
+    def __init__(self, client: AsyncOpenAI) -> None:
+        super().__init__(client)
+        self.with_raw_response = AsyncSpeechWithRawResponse(self)
+
+    async def create(
+        self,
+        *,
+        input: str,
+        model: Union[str, Literal["tts-1", "tts-1-hd"]],
+        voice: Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"],
+        response_format: Literal["mp3", "opus", "aac", "flac"] | NotGiven = NOT_GIVEN,
+        speed: float | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> HttpxBinaryResponseContent:
+        """
+        Generates audio from the input text.
+
+        Args:
+          input: The text to generate audio for. The maximum length is 4096 characters.
+
+          model:
+              One of the available [TTS models](https://platform.openai.com/docs/models/tts):
+              `tts-1` or `tts-1-hd`
+
+          voice: The voice to use when generating the audio. Supported voices are `alloy`,
+              `echo`, `fable`, `onyx`, `nova`, and `shimmer`.
+
+          response_format: The format to audio in. Supported formats are `mp3`, `opus`, `aac`, and `flac`.
+
+          speed: The speed of the generated audio. Select a value from `0.25` to `4.0`. `1.0` is
+              the default.
+
+          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(
+            "/audio/speech",
+            body=maybe_transform(
+                {
+                    "input": input,
+                    "model": model,
+                    "voice": voice,
+                    "response_format": response_format,
+                    "speed": speed,
+                },
+                speech_create_params.SpeechCreateParams,
+            ),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=HttpxBinaryResponseContent,
+        )
+
+
+class SpeechWithRawResponse:
+    def __init__(self, speech: Speech) -> None:
+        self.create = to_raw_response_wrapper(
+            speech.create,
+        )
+
+
+class AsyncSpeechWithRawResponse:
+    def __init__(self, speech: AsyncSpeech) -> None:
+        self.create = async_to_raw_response_wrapper(
+            speech.create,
+        )
src/openai/resources/audio/transcriptions.py
@@ -60,8 +60,8 @@ class Transcriptions(SyncAPIResource):
               [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting)
               should match the audio language.
 
-          response_format: The format of the transcript output, in one of these options: json, text, srt,
-              verbose_json, or vtt.
+          response_format: The format of the transcript output, in one of these options: `json`, `text`,
+              `srt`, `verbose_json`, or `vtt`.
 
           temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the
               output more random, while lower values like 0.2 will make it more focused and
@@ -147,8 +147,8 @@ class AsyncTranscriptions(AsyncAPIResource):
               [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting)
               should match the audio language.
 
-          response_format: The format of the transcript output, in one of these options: json, text, srt,
-              verbose_json, or vtt.
+          response_format: The format of the transcript output, in one of these options: `json`, `text`,
+              `srt`, `verbose_json`, or `vtt`.
 
           temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the
               output more random, while lower values like 0.2 will make it more focused and
src/openai/resources/audio/translations.py
@@ -54,8 +54,8 @@ class Translations(SyncAPIResource):
               [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting)
               should be in English.
 
-          response_format: The format of the transcript output, in one of these options: json, text, srt,
-              verbose_json, or vtt.
+          response_format: The format of the transcript output, in one of these options: `json`, `text`,
+              `srt`, `verbose_json`, or `vtt`.
 
           temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the
               output more random, while lower values like 0.2 will make it more focused and
@@ -134,8 +134,8 @@ class AsyncTranslations(AsyncAPIResource):
               [prompt](https://platform.openai.com/docs/guides/speech-to-text/prompting)
               should be in English.
 
-          response_format: The format of the transcript output, in one of these options: json, text, srt,
-              verbose_json, or vtt.
+          response_format: The format of the transcript output, in one of these options: `json`, `text`,
+              `srt`, `verbose_json`, or `vtt`.
 
           temperature: The sampling temperature, between 0 and 1. Higher values like 0.8 will make the
               output more random, while lower values like 0.2 will make it more focused and
src/openai/resources/beta/assistants/__init__.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse
+from .assistants import (
+    Assistants,
+    AsyncAssistants,
+    AssistantsWithRawResponse,
+    AsyncAssistantsWithRawResponse,
+)
+
+__all__ = [
+    "Files",
+    "AsyncFiles",
+    "FilesWithRawResponse",
+    "AsyncFilesWithRawResponse",
+    "Assistants",
+    "AsyncAssistants",
+    "AssistantsWithRawResponse",
+    "AsyncAssistantsWithRawResponse",
+]
src/openai/resources/beta/assistants/assistants.py
@@ -0,0 +1,654 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, List, Optional
+from typing_extensions import Literal
+
+from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse
+from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from ...._utils import maybe_transform
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from ....pagination import SyncCursorPage, AsyncCursorPage
+from ....types.beta import (
+    Assistant,
+    AsssitantDeleted,
+    assistant_list_params,
+    assistant_create_params,
+    assistant_update_params,
+)
+from ...._base_client import AsyncPaginator, make_request_options
+
+if TYPE_CHECKING:
+    from ...._client import OpenAI, AsyncOpenAI
+
+__all__ = ["Assistants", "AsyncAssistants"]
+
+
+class Assistants(SyncAPIResource):
+    files: Files
+    with_raw_response: AssistantsWithRawResponse
+
+    def __init__(self, client: OpenAI) -> None:
+        super().__init__(client)
+        self.files = Files(client)
+        self.with_raw_response = AssistantsWithRawResponse(self)
+
+    def create(
+        self,
+        *,
+        model: str,
+        description: Optional[str] | NotGiven = NOT_GIVEN,
+        file_ids: List[str] | NotGiven = NOT_GIVEN,
+        instructions: Optional[str] | NotGiven = NOT_GIVEN,
+        metadata: Optional[object] | NotGiven = NOT_GIVEN,
+        name: Optional[str] | NotGiven = NOT_GIVEN,
+        tools: List[assistant_create_params.Tool] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> Assistant:
+        """
+        Create an assistant with a model and instructions.
+
+        Args:
+          model: ID of the model to use. You can use the
+              [List models](https://platform.openai.com/docs/api-reference/models/list) API to
+              see all of your available models, or see our
+              [Model overview](https://platform.openai.com/docs/models/overview) for
+              descriptions of them.
+
+          description: The description of the assistant. The maximum length is 512 characters.
+
+          file_ids: A list of [file](https://platform.openai.com/docs/api-reference/files) IDs
+              attached to this assistant. There can be a maximum of 20 files attached to the
+              assistant. Files are ordered by their creation date in ascending order.
+
+          instructions: The system instructions that the assistant uses. The maximum length is 32768
+              characters.
+
+          metadata: 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.
+
+          name: The name of the assistant. The maximum length is 256 characters.
+
+          tools: A list of tool enabled on the assistant. There can be a maximum of 128 tools per
+              assistant. Tools can be of types `code_interpreter`, `retrieval`, or `function`.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._post(
+            "/assistants",
+            body=maybe_transform(
+                {
+                    "model": model,
+                    "description": description,
+                    "file_ids": file_ids,
+                    "instructions": instructions,
+                    "metadata": metadata,
+                    "name": name,
+                    "tools": tools,
+                },
+                assistant_create_params.AssistantCreateParams,
+            ),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Assistant,
+        )
+
+    def retrieve(
+        self,
+        assistant_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 | None | NotGiven = NOT_GIVEN,
+    ) -> Assistant:
+        """
+        Retrieves an assistant.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._get(
+            f"/assistants/{assistant_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Assistant,
+        )
+
+    def update(
+        self,
+        assistant_id: str,
+        *,
+        description: Optional[str] | NotGiven = NOT_GIVEN,
+        file_ids: List[str] | NotGiven = NOT_GIVEN,
+        instructions: Optional[str] | NotGiven = NOT_GIVEN,
+        metadata: Optional[object] | NotGiven = NOT_GIVEN,
+        model: str | NotGiven = NOT_GIVEN,
+        name: Optional[str] | NotGiven = NOT_GIVEN,
+        tools: List[assistant_update_params.Tool] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> Assistant:
+        """Modifies an assistant.
+
+        Args:
+          description: The description of the assistant.
+
+        The maximum length is 512 characters.
+
+          file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs
+              attached to this assistant. There can be a maximum of 20 files attached to the
+              assistant. Files are ordered by their creation date in ascending order. If a
+              file was previosuly attached to the list but does not show up in the list, it
+              will be deleted from the assistant.
+
+          instructions: The system instructions that the assistant uses. The maximum length is 32768
+              characters.
+
+          metadata: 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.
+
+          model: ID of the model to use. You can use the
+              [List models](https://platform.openai.com/docs/api-reference/models/list) API to
+              see all of your available models, or see our
+              [Model overview](https://platform.openai.com/docs/models/overview) for
+              descriptions of them.
+
+          name: The name of the assistant. The maximum length is 256 characters.
+
+          tools: A list of tool enabled on the assistant. There can be a maximum of 128 tools per
+              assistant. Tools can be of types `code_interpreter`, `retrieval`, or `function`.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._post(
+            f"/assistants/{assistant_id}",
+            body=maybe_transform(
+                {
+                    "description": description,
+                    "file_ids": file_ids,
+                    "instructions": instructions,
+                    "metadata": metadata,
+                    "model": model,
+                    "name": name,
+                    "tools": tools,
+                },
+                assistant_update_params.AssistantUpdateParams,
+            ),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Assistant,
+        )
+
+    def list(
+        self,
+        *,
+        after: str | NotGiven = NOT_GIVEN,
+        before: str | NotGiven = NOT_GIVEN,
+        limit: int | NotGiven = NOT_GIVEN,
+        order: Literal["asc", "desc"] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> SyncCursorPage[Assistant]:
+        """Returns a list of assistants.
+
+        Args:
+          after: A cursor for use in pagination.
+
+        `after` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include after=obj_foo in order to
+              fetch the next page of the list.
+
+          before: A cursor for use in pagination. `before` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include before=obj_foo in order to
+              fetch the previous page of the list.
+
+          limit: A limit on the number of objects to be returned. Limit can range between 1 and
+              100, and the default is 20.
+
+          order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending
+              order and `desc` for descending order.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._get_api_list(
+            "/assistants",
+            page=SyncCursorPage[Assistant],
+            options=make_request_options(
+                extra_headers=extra_headers,
+                extra_query=extra_query,
+                extra_body=extra_body,
+                timeout=timeout,
+                query=maybe_transform(
+                    {
+                        "after": after,
+                        "before": before,
+                        "limit": limit,
+                        "order": order,
+                    },
+                    assistant_list_params.AssistantListParams,
+                ),
+            ),
+            model=Assistant,
+        )
+
+    def delete(
+        self,
+        assistant_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 | None | NotGiven = NOT_GIVEN,
+    ) -> AsssitantDeleted:
+        """
+        Delete an assistant.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._delete(
+            f"/assistants/{assistant_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=AsssitantDeleted,
+        )
+
+
+class AsyncAssistants(AsyncAPIResource):
+    files: AsyncFiles
+    with_raw_response: AsyncAssistantsWithRawResponse
+
+    def __init__(self, client: AsyncOpenAI) -> None:
+        super().__init__(client)
+        self.files = AsyncFiles(client)
+        self.with_raw_response = AsyncAssistantsWithRawResponse(self)
+
+    async def create(
+        self,
+        *,
+        model: str,
+        description: Optional[str] | NotGiven = NOT_GIVEN,
+        file_ids: List[str] | NotGiven = NOT_GIVEN,
+        instructions: Optional[str] | NotGiven = NOT_GIVEN,
+        metadata: Optional[object] | NotGiven = NOT_GIVEN,
+        name: Optional[str] | NotGiven = NOT_GIVEN,
+        tools: List[assistant_create_params.Tool] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> Assistant:
+        """
+        Create an assistant with a model and instructions.
+
+        Args:
+          model: ID of the model to use. You can use the
+              [List models](https://platform.openai.com/docs/api-reference/models/list) API to
+              see all of your available models, or see our
+              [Model overview](https://platform.openai.com/docs/models/overview) for
+              descriptions of them.
+
+          description: The description of the assistant. The maximum length is 512 characters.
+
+          file_ids: A list of [file](https://platform.openai.com/docs/api-reference/files) IDs
+              attached to this assistant. There can be a maximum of 20 files attached to the
+              assistant. Files are ordered by their creation date in ascending order.
+
+          instructions: The system instructions that the assistant uses. The maximum length is 32768
+              characters.
+
+          metadata: 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.
+
+          name: The name of the assistant. The maximum length is 256 characters.
+
+          tools: A list of tool enabled on the assistant. There can be a maximum of 128 tools per
+              assistant. Tools can be of types `code_interpreter`, `retrieval`, or `function`.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._post(
+            "/assistants",
+            body=maybe_transform(
+                {
+                    "model": model,
+                    "description": description,
+                    "file_ids": file_ids,
+                    "instructions": instructions,
+                    "metadata": metadata,
+                    "name": name,
+                    "tools": tools,
+                },
+                assistant_create_params.AssistantCreateParams,
+            ),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Assistant,
+        )
+
+    async def retrieve(
+        self,
+        assistant_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 | None | NotGiven = NOT_GIVEN,
+    ) -> Assistant:
+        """
+        Retrieves an assistant.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._get(
+            f"/assistants/{assistant_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Assistant,
+        )
+
+    async def update(
+        self,
+        assistant_id: str,
+        *,
+        description: Optional[str] | NotGiven = NOT_GIVEN,
+        file_ids: List[str] | NotGiven = NOT_GIVEN,
+        instructions: Optional[str] | NotGiven = NOT_GIVEN,
+        metadata: Optional[object] | NotGiven = NOT_GIVEN,
+        model: str | NotGiven = NOT_GIVEN,
+        name: Optional[str] | NotGiven = NOT_GIVEN,
+        tools: List[assistant_update_params.Tool] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> Assistant:
+        """Modifies an assistant.
+
+        Args:
+          description: The description of the assistant.
+
+        The maximum length is 512 characters.
+
+          file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs
+              attached to this assistant. There can be a maximum of 20 files attached to the
+              assistant. Files are ordered by their creation date in ascending order. If a
+              file was previosuly attached to the list but does not show up in the list, it
+              will be deleted from the assistant.
+
+          instructions: The system instructions that the assistant uses. The maximum length is 32768
+              characters.
+
+          metadata: 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.
+
+          model: ID of the model to use. You can use the
+              [List models](https://platform.openai.com/docs/api-reference/models/list) API to
+              see all of your available models, or see our
+              [Model overview](https://platform.openai.com/docs/models/overview) for
+              descriptions of them.
+
+          name: The name of the assistant. The maximum length is 256 characters.
+
+          tools: A list of tool enabled on the assistant. There can be a maximum of 128 tools per
+              assistant. Tools can be of types `code_interpreter`, `retrieval`, or `function`.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._post(
+            f"/assistants/{assistant_id}",
+            body=maybe_transform(
+                {
+                    "description": description,
+                    "file_ids": file_ids,
+                    "instructions": instructions,
+                    "metadata": metadata,
+                    "model": model,
+                    "name": name,
+                    "tools": tools,
+                },
+                assistant_update_params.AssistantUpdateParams,
+            ),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Assistant,
+        )
+
+    def list(
+        self,
+        *,
+        after: str | NotGiven = NOT_GIVEN,
+        before: str | NotGiven = NOT_GIVEN,
+        limit: int | NotGiven = NOT_GIVEN,
+        order: Literal["asc", "desc"] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> AsyncPaginator[Assistant, AsyncCursorPage[Assistant]]:
+        """Returns a list of assistants.
+
+        Args:
+          after: A cursor for use in pagination.
+
+        `after` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include after=obj_foo in order to
+              fetch the next page of the list.
+
+          before: A cursor for use in pagination. `before` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include before=obj_foo in order to
+              fetch the previous page of the list.
+
+          limit: A limit on the number of objects to be returned. Limit can range between 1 and
+              100, and the default is 20.
+
+          order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending
+              order and `desc` for descending order.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._get_api_list(
+            "/assistants",
+            page=AsyncCursorPage[Assistant],
+            options=make_request_options(
+                extra_headers=extra_headers,
+                extra_query=extra_query,
+                extra_body=extra_body,
+                timeout=timeout,
+                query=maybe_transform(
+                    {
+                        "after": after,
+                        "before": before,
+                        "limit": limit,
+                        "order": order,
+                    },
+                    assistant_list_params.AssistantListParams,
+                ),
+            ),
+            model=Assistant,
+        )
+
+    async def delete(
+        self,
+        assistant_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 | None | NotGiven = NOT_GIVEN,
+    ) -> AsssitantDeleted:
+        """
+        Delete an assistant.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._delete(
+            f"/assistants/{assistant_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=AsssitantDeleted,
+        )
+
+
+class AssistantsWithRawResponse:
+    def __init__(self, assistants: Assistants) -> None:
+        self.files = FilesWithRawResponse(assistants.files)
+
+        self.create = to_raw_response_wrapper(
+            assistants.create,
+        )
+        self.retrieve = to_raw_response_wrapper(
+            assistants.retrieve,
+        )
+        self.update = to_raw_response_wrapper(
+            assistants.update,
+        )
+        self.list = to_raw_response_wrapper(
+            assistants.list,
+        )
+        self.delete = to_raw_response_wrapper(
+            assistants.delete,
+        )
+
+
+class AsyncAssistantsWithRawResponse:
+    def __init__(self, assistants: AsyncAssistants) -> None:
+        self.files = AsyncFilesWithRawResponse(assistants.files)
+
+        self.create = async_to_raw_response_wrapper(
+            assistants.create,
+        )
+        self.retrieve = async_to_raw_response_wrapper(
+            assistants.retrieve,
+        )
+        self.update = async_to_raw_response_wrapper(
+            assistants.update,
+        )
+        self.list = async_to_raw_response_wrapper(
+            assistants.list,
+        )
+        self.delete = async_to_raw_response_wrapper(
+            assistants.delete,
+        )
src/openai/resources/beta/assistants/files.py
@@ -0,0 +1,414 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+from typing_extensions import Literal
+
+from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from ...._utils import maybe_transform
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from ....pagination import SyncCursorPage, AsyncCursorPage
+from ...._base_client import AsyncPaginator, make_request_options
+from ....types.beta.assistants import (
+    AssistantFile,
+    FileDeleteResponse,
+    file_list_params,
+    file_create_params,
+)
+
+if TYPE_CHECKING:
+    from ...._client import OpenAI, AsyncOpenAI
+
+__all__ = ["Files", "AsyncFiles"]
+
+
+class Files(SyncAPIResource):
+    with_raw_response: FilesWithRawResponse
+
+    def __init__(self, client: OpenAI) -> None:
+        super().__init__(client)
+        self.with_raw_response = FilesWithRawResponse(self)
+
+    def create(
+        self,
+        assistant_id: str,
+        *,
+        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 | None | NotGiven = NOT_GIVEN,
+    ) -> AssistantFile:
+        """
+        Create an assistant file by attaching a
+        [File](https://platform.openai.com/docs/api-reference/files) to an
+        [assistant](https://platform.openai.com/docs/api-reference/assistants).
+
+        Args:
+          file_id: A [File](https://platform.openai.com/docs/api-reference/files) ID (with
+              `purpose="assistants"`) that the assistant should use. Useful for tools like
+              `retrieval` and `code_interpreter` that can access files.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._post(
+            f"/assistants/{assistant_id}/files",
+            body=maybe_transform({"file_id": file_id}, file_create_params.FileCreateParams),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=AssistantFile,
+        )
+
+    def retrieve(
+        self,
+        file_id: str,
+        *,
+        assistant_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 | None | NotGiven = NOT_GIVEN,
+    ) -> AssistantFile:
+        """
+        Retrieves an AssistantFile.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._get(
+            f"/assistants/{assistant_id}/files/{file_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=AssistantFile,
+        )
+
+    def list(
+        self,
+        assistant_id: str,
+        *,
+        after: str | NotGiven = NOT_GIVEN,
+        before: str | NotGiven = NOT_GIVEN,
+        limit: int | NotGiven = NOT_GIVEN,
+        order: Literal["asc", "desc"] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> SyncCursorPage[AssistantFile]:
+        """
+        Returns a list of assistant files.
+
+        Args:
+          after: A cursor for use in pagination. `after` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include after=obj_foo in order to
+              fetch the next page of the list.
+
+          before: A cursor for use in pagination. `before` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include before=obj_foo in order to
+              fetch the previous page of the list.
+
+          limit: A limit on the number of objects to be returned. Limit can range between 1 and
+              100, and the default is 20.
+
+          order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending
+              order and `desc` for descending order.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._get_api_list(
+            f"/assistants/{assistant_id}/files",
+            page=SyncCursorPage[AssistantFile],
+            options=make_request_options(
+                extra_headers=extra_headers,
+                extra_query=extra_query,
+                extra_body=extra_body,
+                timeout=timeout,
+                query=maybe_transform(
+                    {
+                        "after": after,
+                        "before": before,
+                        "limit": limit,
+                        "order": order,
+                    },
+                    file_list_params.FileListParams,
+                ),
+            ),
+            model=AssistantFile,
+        )
+
+    def delete(
+        self,
+        file_id: str,
+        *,
+        assistant_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 | None | NotGiven = NOT_GIVEN,
+    ) -> FileDeleteResponse:
+        """
+        Delete an assistant file.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._delete(
+            f"/assistants/{assistant_id}/files/{file_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=FileDeleteResponse,
+        )
+
+
+class AsyncFiles(AsyncAPIResource):
+    with_raw_response: AsyncFilesWithRawResponse
+
+    def __init__(self, client: AsyncOpenAI) -> None:
+        super().__init__(client)
+        self.with_raw_response = AsyncFilesWithRawResponse(self)
+
+    async def create(
+        self,
+        assistant_id: str,
+        *,
+        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 | None | NotGiven = NOT_GIVEN,
+    ) -> AssistantFile:
+        """
+        Create an assistant file by attaching a
+        [File](https://platform.openai.com/docs/api-reference/files) to an
+        [assistant](https://platform.openai.com/docs/api-reference/assistants).
+
+        Args:
+          file_id: A [File](https://platform.openai.com/docs/api-reference/files) ID (with
+              `purpose="assistants"`) that the assistant should use. Useful for tools like
+              `retrieval` and `code_interpreter` that can access files.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._post(
+            f"/assistants/{assistant_id}/files",
+            body=maybe_transform({"file_id": file_id}, file_create_params.FileCreateParams),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=AssistantFile,
+        )
+
+    async def retrieve(
+        self,
+        file_id: str,
+        *,
+        assistant_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 | None | NotGiven = NOT_GIVEN,
+    ) -> AssistantFile:
+        """
+        Retrieves an AssistantFile.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._get(
+            f"/assistants/{assistant_id}/files/{file_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=AssistantFile,
+        )
+
+    def list(
+        self,
+        assistant_id: str,
+        *,
+        after: str | NotGiven = NOT_GIVEN,
+        before: str | NotGiven = NOT_GIVEN,
+        limit: int | NotGiven = NOT_GIVEN,
+        order: Literal["asc", "desc"] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> AsyncPaginator[AssistantFile, AsyncCursorPage[AssistantFile]]:
+        """
+        Returns a list of assistant files.
+
+        Args:
+          after: A cursor for use in pagination. `after` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include after=obj_foo in order to
+              fetch the next page of the list.
+
+          before: A cursor for use in pagination. `before` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include before=obj_foo in order to
+              fetch the previous page of the list.
+
+          limit: A limit on the number of objects to be returned. Limit can range between 1 and
+              100, and the default is 20.
+
+          order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending
+              order and `desc` for descending order.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._get_api_list(
+            f"/assistants/{assistant_id}/files",
+            page=AsyncCursorPage[AssistantFile],
+            options=make_request_options(
+                extra_headers=extra_headers,
+                extra_query=extra_query,
+                extra_body=extra_body,
+                timeout=timeout,
+                query=maybe_transform(
+                    {
+                        "after": after,
+                        "before": before,
+                        "limit": limit,
+                        "order": order,
+                    },
+                    file_list_params.FileListParams,
+                ),
+            ),
+            model=AssistantFile,
+        )
+
+    async def delete(
+        self,
+        file_id: str,
+        *,
+        assistant_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 | None | NotGiven = NOT_GIVEN,
+    ) -> FileDeleteResponse:
+        """
+        Delete an assistant file.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._delete(
+            f"/assistants/{assistant_id}/files/{file_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=FileDeleteResponse,
+        )
+
+
+class FilesWithRawResponse:
+    def __init__(self, files: Files) -> None:
+        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,
+        )
+
+
+class AsyncFilesWithRawResponse:
+    def __init__(self, files: AsyncFiles) -> None:
+        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,
+        )
src/openai/resources/beta/threads/messages/__init__.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse
+from .messages import (
+    Messages,
+    AsyncMessages,
+    MessagesWithRawResponse,
+    AsyncMessagesWithRawResponse,
+)
+
+__all__ = [
+    "Files",
+    "AsyncFiles",
+    "FilesWithRawResponse",
+    "AsyncFilesWithRawResponse",
+    "Messages",
+    "AsyncMessages",
+    "MessagesWithRawResponse",
+    "AsyncMessagesWithRawResponse",
+]
src/openai/resources/beta/threads/messages/files.py
@@ -0,0 +1,257 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+from typing_extensions import Literal
+
+from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from ....._utils import maybe_transform
+from ....._resource import SyncAPIResource, AsyncAPIResource
+from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from .....pagination import SyncCursorPage, AsyncCursorPage
+from ....._base_client import AsyncPaginator, make_request_options
+from .....types.beta.threads.messages import MessageFile, file_list_params
+
+if TYPE_CHECKING:
+    from ....._client import OpenAI, AsyncOpenAI
+
+__all__ = ["Files", "AsyncFiles"]
+
+
+class Files(SyncAPIResource):
+    with_raw_response: FilesWithRawResponse
+
+    def __init__(self, client: OpenAI) -> None:
+        super().__init__(client)
+        self.with_raw_response = FilesWithRawResponse(self)
+
+    def retrieve(
+        self,
+        file_id: str,
+        *,
+        thread_id: str,
+        message_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 | None | NotGiven = NOT_GIVEN,
+    ) -> MessageFile:
+        """
+        Retrieves a message file.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._get(
+            f"/threads/{thread_id}/messages/{message_id}/files/{file_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=MessageFile,
+        )
+
+    def list(
+        self,
+        message_id: str,
+        *,
+        thread_id: str,
+        after: str | NotGiven = NOT_GIVEN,
+        before: str | NotGiven = NOT_GIVEN,
+        limit: int | NotGiven = NOT_GIVEN,
+        order: Literal["asc", "desc"] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> SyncCursorPage[MessageFile]:
+        """Returns a list of message files.
+
+        Args:
+          after: A cursor for use in pagination.
+
+        `after` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include after=obj_foo in order to
+              fetch the next page of the list.
+
+          before: A cursor for use in pagination. `before` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include before=obj_foo in order to
+              fetch the previous page of the list.
+
+          limit: A limit on the number of objects to be returned. Limit can range between 1 and
+              100, and the default is 20.
+
+          order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending
+              order and `desc` for descending order.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._get_api_list(
+            f"/threads/{thread_id}/messages/{message_id}/files",
+            page=SyncCursorPage[MessageFile],
+            options=make_request_options(
+                extra_headers=extra_headers,
+                extra_query=extra_query,
+                extra_body=extra_body,
+                timeout=timeout,
+                query=maybe_transform(
+                    {
+                        "after": after,
+                        "before": before,
+                        "limit": limit,
+                        "order": order,
+                    },
+                    file_list_params.FileListParams,
+                ),
+            ),
+            model=MessageFile,
+        )
+
+
+class AsyncFiles(AsyncAPIResource):
+    with_raw_response: AsyncFilesWithRawResponse
+
+    def __init__(self, client: AsyncOpenAI) -> None:
+        super().__init__(client)
+        self.with_raw_response = AsyncFilesWithRawResponse(self)
+
+    async def retrieve(
+        self,
+        file_id: str,
+        *,
+        thread_id: str,
+        message_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 | None | NotGiven = NOT_GIVEN,
+    ) -> MessageFile:
+        """
+        Retrieves a message file.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._get(
+            f"/threads/{thread_id}/messages/{message_id}/files/{file_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=MessageFile,
+        )
+
+    def list(
+        self,
+        message_id: str,
+        *,
+        thread_id: str,
+        after: str | NotGiven = NOT_GIVEN,
+        before: str | NotGiven = NOT_GIVEN,
+        limit: int | NotGiven = NOT_GIVEN,
+        order: Literal["asc", "desc"] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> AsyncPaginator[MessageFile, AsyncCursorPage[MessageFile]]:
+        """Returns a list of message files.
+
+        Args:
+          after: A cursor for use in pagination.
+
+        `after` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include after=obj_foo in order to
+              fetch the next page of the list.
+
+          before: A cursor for use in pagination. `before` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include before=obj_foo in order to
+              fetch the previous page of the list.
+
+          limit: A limit on the number of objects to be returned. Limit can range between 1 and
+              100, and the default is 20.
+
+          order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending
+              order and `desc` for descending order.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._get_api_list(
+            f"/threads/{thread_id}/messages/{message_id}/files",
+            page=AsyncCursorPage[MessageFile],
+            options=make_request_options(
+                extra_headers=extra_headers,
+                extra_query=extra_query,
+                extra_body=extra_body,
+                timeout=timeout,
+                query=maybe_transform(
+                    {
+                        "after": after,
+                        "before": before,
+                        "limit": limit,
+                        "order": order,
+                    },
+                    file_list_params.FileListParams,
+                ),
+            ),
+            model=MessageFile,
+        )
+
+
+class FilesWithRawResponse:
+    def __init__(self, files: Files) -> None:
+        self.retrieve = to_raw_response_wrapper(
+            files.retrieve,
+        )
+        self.list = to_raw_response_wrapper(
+            files.list,
+        )
+
+
+class AsyncFilesWithRawResponse:
+    def __init__(self, files: AsyncFiles) -> None:
+        self.retrieve = async_to_raw_response_wrapper(
+            files.retrieve,
+        )
+        self.list = async_to_raw_response_wrapper(
+            files.list,
+        )
src/openai/resources/beta/threads/messages/messages.py
@@ -0,0 +1,477 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, List, Optional
+from typing_extensions import Literal
+
+from .files import Files, AsyncFiles, FilesWithRawResponse, AsyncFilesWithRawResponse
+from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from ....._utils import maybe_transform
+from ....._resource import SyncAPIResource, AsyncAPIResource
+from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from .....pagination import SyncCursorPage, AsyncCursorPage
+from ....._base_client import AsyncPaginator, make_request_options
+from .....types.beta.threads import (
+    ThreadMessage,
+    message_list_params,
+    message_create_params,
+    message_update_params,
+)
+
+if TYPE_CHECKING:
+    from ....._client import OpenAI, AsyncOpenAI
+
+__all__ = ["Messages", "AsyncMessages"]
+
+
+class Messages(SyncAPIResource):
+    files: Files
+    with_raw_response: MessagesWithRawResponse
+
+    def __init__(self, client: OpenAI) -> None:
+        super().__init__(client)
+        self.files = Files(client)
+        self.with_raw_response = MessagesWithRawResponse(self)
+
+    def create(
+        self,
+        thread_id: str,
+        *,
+        content: str,
+        role: Literal["user"],
+        file_ids: List[str] | NotGiven = NOT_GIVEN,
+        metadata: Optional[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 | None | NotGiven = NOT_GIVEN,
+    ) -> ThreadMessage:
+        """
+        Create a message.
+
+        Args:
+          content: The content of the message.
+
+          role: The role of the entity that is creating the message. Currently only `user` is
+              supported.
+
+          file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that
+              the message should use. There can be a maximum of 10 files attached to a
+              message. Useful for tools like `retrieval` and `code_interpreter` that can
+              access and use files.
+
+          metadata: 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.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._post(
+            f"/threads/{thread_id}/messages",
+            body=maybe_transform(
+                {
+                    "content": content,
+                    "role": role,
+                    "file_ids": file_ids,
+                    "metadata": metadata,
+                },
+                message_create_params.MessageCreateParams,
+            ),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=ThreadMessage,
+        )
+
+    def retrieve(
+        self,
+        message_id: str,
+        *,
+        thread_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 | None | NotGiven = NOT_GIVEN,
+    ) -> ThreadMessage:
+        """
+        Retrieve a message.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._get(
+            f"/threads/{thread_id}/messages/{message_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=ThreadMessage,
+        )
+
+    def update(
+        self,
+        message_id: str,
+        *,
+        thread_id: str,
+        metadata: Optional[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 | None | NotGiven = NOT_GIVEN,
+    ) -> ThreadMessage:
+        """
+        Modifies a message.
+
+        Args:
+          metadata: 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.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._post(
+            f"/threads/{thread_id}/messages/{message_id}",
+            body=maybe_transform({"metadata": metadata}, message_update_params.MessageUpdateParams),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=ThreadMessage,
+        )
+
+    def list(
+        self,
+        thread_id: str,
+        *,
+        after: str | NotGiven = NOT_GIVEN,
+        before: str | NotGiven = NOT_GIVEN,
+        limit: int | NotGiven = NOT_GIVEN,
+        order: Literal["asc", "desc"] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> SyncCursorPage[ThreadMessage]:
+        """
+        Returns a list of messages for a given thread.
+
+        Args:
+          after: A cursor for use in pagination. `after` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include after=obj_foo in order to
+              fetch the next page of the list.
+
+          before: A cursor for use in pagination. `before` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include before=obj_foo in order to
+              fetch the previous page of the list.
+
+          limit: A limit on the number of objects to be returned. Limit can range between 1 and
+              100, and the default is 20.
+
+          order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending
+              order and `desc` for descending order.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._get_api_list(
+            f"/threads/{thread_id}/messages",
+            page=SyncCursorPage[ThreadMessage],
+            options=make_request_options(
+                extra_headers=extra_headers,
+                extra_query=extra_query,
+                extra_body=extra_body,
+                timeout=timeout,
+                query=maybe_transform(
+                    {
+                        "after": after,
+                        "before": before,
+                        "limit": limit,
+                        "order": order,
+                    },
+                    message_list_params.MessageListParams,
+                ),
+            ),
+            model=ThreadMessage,
+        )
+
+
+class AsyncMessages(AsyncAPIResource):
+    files: AsyncFiles
+    with_raw_response: AsyncMessagesWithRawResponse
+
+    def __init__(self, client: AsyncOpenAI) -> None:
+        super().__init__(client)
+        self.files = AsyncFiles(client)
+        self.with_raw_response = AsyncMessagesWithRawResponse(self)
+
+    async def create(
+        self,
+        thread_id: str,
+        *,
+        content: str,
+        role: Literal["user"],
+        file_ids: List[str] | NotGiven = NOT_GIVEN,
+        metadata: Optional[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 | None | NotGiven = NOT_GIVEN,
+    ) -> ThreadMessage:
+        """
+        Create a message.
+
+        Args:
+          content: The content of the message.
+
+          role: The role of the entity that is creating the message. Currently only `user` is
+              supported.
+
+          file_ids: A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that
+              the message should use. There can be a maximum of 10 files attached to a
+              message. Useful for tools like `retrieval` and `code_interpreter` that can
+              access and use files.
+
+          metadata: 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.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._post(
+            f"/threads/{thread_id}/messages",
+            body=maybe_transform(
+                {
+                    "content": content,
+                    "role": role,
+                    "file_ids": file_ids,
+                    "metadata": metadata,
+                },
+                message_create_params.MessageCreateParams,
+            ),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=ThreadMessage,
+        )
+
+    async def retrieve(
+        self,
+        message_id: str,
+        *,
+        thread_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 | None | NotGiven = NOT_GIVEN,
+    ) -> ThreadMessage:
+        """
+        Retrieve a message.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._get(
+            f"/threads/{thread_id}/messages/{message_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=ThreadMessage,
+        )
+
+    async def update(
+        self,
+        message_id: str,
+        *,
+        thread_id: str,
+        metadata: Optional[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 | None | NotGiven = NOT_GIVEN,
+    ) -> ThreadMessage:
+        """
+        Modifies a message.
+
+        Args:
+          metadata: 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.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._post(
+            f"/threads/{thread_id}/messages/{message_id}",
+            body=maybe_transform({"metadata": metadata}, message_update_params.MessageUpdateParams),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=ThreadMessage,
+        )
+
+    def list(
+        self,
+        thread_id: str,
+        *,
+        after: str | NotGiven = NOT_GIVEN,
+        before: str | NotGiven = NOT_GIVEN,
+        limit: int | NotGiven = NOT_GIVEN,
+        order: Literal["asc", "desc"] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> AsyncPaginator[ThreadMessage, AsyncCursorPage[ThreadMessage]]:
+        """
+        Returns a list of messages for a given thread.
+
+        Args:
+          after: A cursor for use in pagination. `after` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include after=obj_foo in order to
+              fetch the next page of the list.
+
+          before: A cursor for use in pagination. `before` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include before=obj_foo in order to
+              fetch the previous page of the list.
+
+          limit: A limit on the number of objects to be returned. Limit can range between 1 and
+              100, and the default is 20.
+
+          order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending
+              order and `desc` for descending order.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._get_api_list(
+            f"/threads/{thread_id}/messages",
+            page=AsyncCursorPage[ThreadMessage],
+            options=make_request_options(
+                extra_headers=extra_headers,
+                extra_query=extra_query,
+                extra_body=extra_body,
+                timeout=timeout,
+                query=maybe_transform(
+                    {
+                        "after": after,
+                        "before": before,
+                        "limit": limit,
+                        "order": order,
+                    },
+                    message_list_params.MessageListParams,
+                ),
+            ),
+            model=ThreadMessage,
+        )
+
+
+class MessagesWithRawResponse:
+    def __init__(self, messages: Messages) -> None:
+        self.files = FilesWithRawResponse(messages.files)
+
+        self.create = to_raw_response_wrapper(
+            messages.create,
+        )
+        self.retrieve = to_raw_response_wrapper(
+            messages.retrieve,
+        )
+        self.update = to_raw_response_wrapper(
+            messages.update,
+        )
+        self.list = to_raw_response_wrapper(
+            messages.list,
+        )
+
+
+class AsyncMessagesWithRawResponse:
+    def __init__(self, messages: AsyncMessages) -> None:
+        self.files = AsyncFilesWithRawResponse(messages.files)
+
+        self.create = async_to_raw_response_wrapper(
+            messages.create,
+        )
+        self.retrieve = async_to_raw_response_wrapper(
+            messages.retrieve,
+        )
+        self.update = async_to_raw_response_wrapper(
+            messages.update,
+        )
+        self.list = async_to_raw_response_wrapper(
+            messages.list,
+        )
src/openai/resources/beta/threads/runs/__init__.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from .runs import Runs, AsyncRuns, RunsWithRawResponse, AsyncRunsWithRawResponse
+from .steps import Steps, AsyncSteps, StepsWithRawResponse, AsyncStepsWithRawResponse
+
+__all__ = [
+    "Steps",
+    "AsyncSteps",
+    "StepsWithRawResponse",
+    "AsyncStepsWithRawResponse",
+    "Runs",
+    "AsyncRuns",
+    "RunsWithRawResponse",
+    "AsyncRunsWithRawResponse",
+]
src/openai/resources/beta/threads/runs/runs.py
@@ -0,0 +1,654 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, List, Optional
+from typing_extensions import Literal
+
+from .steps import Steps, AsyncSteps, StepsWithRawResponse, AsyncStepsWithRawResponse
+from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from ....._utils import maybe_transform
+from ....._resource import SyncAPIResource, AsyncAPIResource
+from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from .....pagination import SyncCursorPage, AsyncCursorPage
+from ....._base_client import AsyncPaginator, make_request_options
+from .....types.beta.threads import (
+    Run,
+    run_list_params,
+    run_create_params,
+    run_update_params,
+    run_submit_tool_outputs_params,
+)
+
+if TYPE_CHECKING:
+    from ....._client import OpenAI, AsyncOpenAI
+
+__all__ = ["Runs", "AsyncRuns"]
+
+
+class Runs(SyncAPIResource):
+    steps: Steps
+    with_raw_response: RunsWithRawResponse
+
+    def __init__(self, client: OpenAI) -> None:
+        super().__init__(client)
+        self.steps = Steps(client)
+        self.with_raw_response = RunsWithRawResponse(self)
+
+    def create(
+        self,
+        thread_id: str,
+        *,
+        assistant_id: str,
+        instructions: Optional[str] | NotGiven = NOT_GIVEN,
+        metadata: Optional[object] | NotGiven = NOT_GIVEN,
+        model: Optional[str] | NotGiven = NOT_GIVEN,
+        tools: Optional[List[run_create_params.Tool]] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> Run:
+        """
+        Create a run.
+
+        Args:
+          assistant_id: The ID of the
+              [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
+              execute this run.
+
+          instructions: Override the default system message of the assistant. This is useful for
+              modifying the behavior on a per-run basis.
+
+          metadata: 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.
+
+          model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
+              be used to execute this run. If a value is provided here, it will override the
+              model associated with the assistant. If not, the model associated with the
+              assistant will be used.
+
+          tools: Override the tools the assistant can use for this run. This is useful for
+              modifying the behavior on a per-run basis.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._post(
+            f"/threads/{thread_id}/runs",
+            body=maybe_transform(
+                {
+                    "assistant_id": assistant_id,
+                    "instructions": instructions,
+                    "metadata": metadata,
+                    "model": model,
+                    "tools": tools,
+                },
+                run_create_params.RunCreateParams,
+            ),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Run,
+        )
+
+    def retrieve(
+        self,
+        run_id: str,
+        *,
+        thread_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 | None | NotGiven = NOT_GIVEN,
+    ) -> Run:
+        """
+        Retrieves a run.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._get(
+            f"/threads/{thread_id}/runs/{run_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Run,
+        )
+
+    def update(
+        self,
+        run_id: str,
+        *,
+        thread_id: str,
+        metadata: Optional[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 | None | NotGiven = NOT_GIVEN,
+    ) -> Run:
+        """
+        Modifies a run.
+
+        Args:
+          metadata: 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.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._post(
+            f"/threads/{thread_id}/runs/{run_id}",
+            body=maybe_transform({"metadata": metadata}, run_update_params.RunUpdateParams),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Run,
+        )
+
+    def list(
+        self,
+        thread_id: str,
+        *,
+        after: str | NotGiven = NOT_GIVEN,
+        before: str | NotGiven = NOT_GIVEN,
+        limit: int | NotGiven = NOT_GIVEN,
+        order: Literal["asc", "desc"] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> SyncCursorPage[Run]:
+        """
+        Returns a list of runs belonging to a thread.
+
+        Args:
+          after: A cursor for use in pagination. `after` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include after=obj_foo in order to
+              fetch the next page of the list.
+
+          before: A cursor for use in pagination. `before` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include before=obj_foo in order to
+              fetch the previous page of the list.
+
+          limit: A limit on the number of objects to be returned. Limit can range between 1 and
+              100, and the default is 20.
+
+          order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending
+              order and `desc` for descending order.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._get_api_list(
+            f"/threads/{thread_id}/runs",
+            page=SyncCursorPage[Run],
+            options=make_request_options(
+                extra_headers=extra_headers,
+                extra_query=extra_query,
+                extra_body=extra_body,
+                timeout=timeout,
+                query=maybe_transform(
+                    {
+                        "after": after,
+                        "before": before,
+                        "limit": limit,
+                        "order": order,
+                    },
+                    run_list_params.RunListParams,
+                ),
+            ),
+            model=Run,
+        )
+
+    def cancel(
+        self,
+        run_id: str,
+        *,
+        thread_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 | None | NotGiven = NOT_GIVEN,
+    ) -> Run:
+        """
+        Cancels a run that is `in_progress`.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._post(
+            f"/threads/{thread_id}/runs/{run_id}/cancel",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Run,
+        )
+
+    def submit_tool_outputs(
+        self,
+        run_id: str,
+        *,
+        thread_id: str,
+        tool_outputs: List[run_submit_tool_outputs_params.ToolOutput],
+        # 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 | None | NotGiven = NOT_GIVEN,
+    ) -> Run:
+        """
+        When a run has the `status: "requires_action"` and `required_action.type` is
+        `submit_tool_outputs`, this endpoint can be used to submit the outputs from the
+        tool calls once they're all completed. All outputs must be submitted in a single
+        request.
+
+        Args:
+          tool_outputs: A list of tools for which the outputs are being submitted.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._post(
+            f"/threads/{thread_id}/runs/{run_id}/submit_tool_outputs",
+            body=maybe_transform(
+                {"tool_outputs": tool_outputs}, run_submit_tool_outputs_params.RunSubmitToolOutputsParams
+            ),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Run,
+        )
+
+
+class AsyncRuns(AsyncAPIResource):
+    steps: AsyncSteps
+    with_raw_response: AsyncRunsWithRawResponse
+
+    def __init__(self, client: AsyncOpenAI) -> None:
+        super().__init__(client)
+        self.steps = AsyncSteps(client)
+        self.with_raw_response = AsyncRunsWithRawResponse(self)
+
+    async def create(
+        self,
+        thread_id: str,
+        *,
+        assistant_id: str,
+        instructions: Optional[str] | NotGiven = NOT_GIVEN,
+        metadata: Optional[object] | NotGiven = NOT_GIVEN,
+        model: Optional[str] | NotGiven = NOT_GIVEN,
+        tools: Optional[List[run_create_params.Tool]] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> Run:
+        """
+        Create a run.
+
+        Args:
+          assistant_id: The ID of the
+              [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
+              execute this run.
+
+          instructions: Override the default system message of the assistant. This is useful for
+              modifying the behavior on a per-run basis.
+
+          metadata: 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.
+
+          model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
+              be used to execute this run. If a value is provided here, it will override the
+              model associated with the assistant. If not, the model associated with the
+              assistant will be used.
+
+          tools: Override the tools the assistant can use for this run. This is useful for
+              modifying the behavior on a per-run basis.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._post(
+            f"/threads/{thread_id}/runs",
+            body=maybe_transform(
+                {
+                    "assistant_id": assistant_id,
+                    "instructions": instructions,
+                    "metadata": metadata,
+                    "model": model,
+                    "tools": tools,
+                },
+                run_create_params.RunCreateParams,
+            ),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Run,
+        )
+
+    async def retrieve(
+        self,
+        run_id: str,
+        *,
+        thread_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 | None | NotGiven = NOT_GIVEN,
+    ) -> Run:
+        """
+        Retrieves a run.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._get(
+            f"/threads/{thread_id}/runs/{run_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Run,
+        )
+
+    async def update(
+        self,
+        run_id: str,
+        *,
+        thread_id: str,
+        metadata: Optional[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 | None | NotGiven = NOT_GIVEN,
+    ) -> Run:
+        """
+        Modifies a run.
+
+        Args:
+          metadata: 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.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._post(
+            f"/threads/{thread_id}/runs/{run_id}",
+            body=maybe_transform({"metadata": metadata}, run_update_params.RunUpdateParams),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Run,
+        )
+
+    def list(
+        self,
+        thread_id: str,
+        *,
+        after: str | NotGiven = NOT_GIVEN,
+        before: str | NotGiven = NOT_GIVEN,
+        limit: int | NotGiven = NOT_GIVEN,
+        order: Literal["asc", "desc"] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> AsyncPaginator[Run, AsyncCursorPage[Run]]:
+        """
+        Returns a list of runs belonging to a thread.
+
+        Args:
+          after: A cursor for use in pagination. `after` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include after=obj_foo in order to
+              fetch the next page of the list.
+
+          before: A cursor for use in pagination. `before` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include before=obj_foo in order to
+              fetch the previous page of the list.
+
+          limit: A limit on the number of objects to be returned. Limit can range between 1 and
+              100, and the default is 20.
+
+          order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending
+              order and `desc` for descending order.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._get_api_list(
+            f"/threads/{thread_id}/runs",
+            page=AsyncCursorPage[Run],
+            options=make_request_options(
+                extra_headers=extra_headers,
+                extra_query=extra_query,
+                extra_body=extra_body,
+                timeout=timeout,
+                query=maybe_transform(
+                    {
+                        "after": after,
+                        "before": before,
+                        "limit": limit,
+                        "order": order,
+                    },
+                    run_list_params.RunListParams,
+                ),
+            ),
+            model=Run,
+        )
+
+    async def cancel(
+        self,
+        run_id: str,
+        *,
+        thread_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 | None | NotGiven = NOT_GIVEN,
+    ) -> Run:
+        """
+        Cancels a run that is `in_progress`.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._post(
+            f"/threads/{thread_id}/runs/{run_id}/cancel",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Run,
+        )
+
+    async def submit_tool_outputs(
+        self,
+        run_id: str,
+        *,
+        thread_id: str,
+        tool_outputs: List[run_submit_tool_outputs_params.ToolOutput],
+        # 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 | None | NotGiven = NOT_GIVEN,
+    ) -> Run:
+        """
+        When a run has the `status: "requires_action"` and `required_action.type` is
+        `submit_tool_outputs`, this endpoint can be used to submit the outputs from the
+        tool calls once they're all completed. All outputs must be submitted in a single
+        request.
+
+        Args:
+          tool_outputs: A list of tools for which the outputs are being submitted.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._post(
+            f"/threads/{thread_id}/runs/{run_id}/submit_tool_outputs",
+            body=maybe_transform(
+                {"tool_outputs": tool_outputs}, run_submit_tool_outputs_params.RunSubmitToolOutputsParams
+            ),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Run,
+        )
+
+
+class RunsWithRawResponse:
+    def __init__(self, runs: Runs) -> None:
+        self.steps = StepsWithRawResponse(runs.steps)
+
+        self.create = to_raw_response_wrapper(
+            runs.create,
+        )
+        self.retrieve = to_raw_response_wrapper(
+            runs.retrieve,
+        )
+        self.update = to_raw_response_wrapper(
+            runs.update,
+        )
+        self.list = to_raw_response_wrapper(
+            runs.list,
+        )
+        self.cancel = to_raw_response_wrapper(
+            runs.cancel,
+        )
+        self.submit_tool_outputs = to_raw_response_wrapper(
+            runs.submit_tool_outputs,
+        )
+
+
+class AsyncRunsWithRawResponse:
+    def __init__(self, runs: AsyncRuns) -> None:
+        self.steps = AsyncStepsWithRawResponse(runs.steps)
+
+        self.create = async_to_raw_response_wrapper(
+            runs.create,
+        )
+        self.retrieve = async_to_raw_response_wrapper(
+            runs.retrieve,
+        )
+        self.update = async_to_raw_response_wrapper(
+            runs.update,
+        )
+        self.list = async_to_raw_response_wrapper(
+            runs.list,
+        )
+        self.cancel = async_to_raw_response_wrapper(
+            runs.cancel,
+        )
+        self.submit_tool_outputs = async_to_raw_response_wrapper(
+            runs.submit_tool_outputs,
+        )
src/openai/resources/beta/threads/runs/steps.py
@@ -0,0 +1,255 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+from typing_extensions import Literal
+
+from ....._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from ....._utils import maybe_transform
+from ....._resource import SyncAPIResource, AsyncAPIResource
+from ....._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from .....pagination import SyncCursorPage, AsyncCursorPage
+from ....._base_client import AsyncPaginator, make_request_options
+from .....types.beta.threads.runs import RunStep, step_list_params
+
+if TYPE_CHECKING:
+    from ....._client import OpenAI, AsyncOpenAI
+
+__all__ = ["Steps", "AsyncSteps"]
+
+
+class Steps(SyncAPIResource):
+    with_raw_response: StepsWithRawResponse
+
+    def __init__(self, client: OpenAI) -> None:
+        super().__init__(client)
+        self.with_raw_response = StepsWithRawResponse(self)
+
+    def retrieve(
+        self,
+        step_id: str,
+        *,
+        thread_id: str,
+        run_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 | None | NotGiven = NOT_GIVEN,
+    ) -> RunStep:
+        """
+        Retrieves a run step.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._get(
+            f"/threads/{thread_id}/runs/{run_id}/steps/{step_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=RunStep,
+        )
+
+    def list(
+        self,
+        run_id: str,
+        *,
+        thread_id: str,
+        after: str | NotGiven = NOT_GIVEN,
+        before: str | NotGiven = NOT_GIVEN,
+        limit: int | NotGiven = NOT_GIVEN,
+        order: Literal["asc", "desc"] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> SyncCursorPage[RunStep]:
+        """
+        Returns a list of run steps belonging to a run.
+
+        Args:
+          after: A cursor for use in pagination. `after` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include after=obj_foo in order to
+              fetch the next page of the list.
+
+          before: A cursor for use in pagination. `before` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include before=obj_foo in order to
+              fetch the previous page of the list.
+
+          limit: A limit on the number of objects to be returned. Limit can range between 1 and
+              100, and the default is 20.
+
+          order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending
+              order and `desc` for descending order.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._get_api_list(
+            f"/threads/{thread_id}/runs/{run_id}/steps",
+            page=SyncCursorPage[RunStep],
+            options=make_request_options(
+                extra_headers=extra_headers,
+                extra_query=extra_query,
+                extra_body=extra_body,
+                timeout=timeout,
+                query=maybe_transform(
+                    {
+                        "after": after,
+                        "before": before,
+                        "limit": limit,
+                        "order": order,
+                    },
+                    step_list_params.StepListParams,
+                ),
+            ),
+            model=RunStep,
+        )
+
+
+class AsyncSteps(AsyncAPIResource):
+    with_raw_response: AsyncStepsWithRawResponse
+
+    def __init__(self, client: AsyncOpenAI) -> None:
+        super().__init__(client)
+        self.with_raw_response = AsyncStepsWithRawResponse(self)
+
+    async def retrieve(
+        self,
+        step_id: str,
+        *,
+        thread_id: str,
+        run_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 | None | NotGiven = NOT_GIVEN,
+    ) -> RunStep:
+        """
+        Retrieves a run step.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._get(
+            f"/threads/{thread_id}/runs/{run_id}/steps/{step_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=RunStep,
+        )
+
+    def list(
+        self,
+        run_id: str,
+        *,
+        thread_id: str,
+        after: str | NotGiven = NOT_GIVEN,
+        before: str | NotGiven = NOT_GIVEN,
+        limit: int | NotGiven = NOT_GIVEN,
+        order: Literal["asc", "desc"] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> AsyncPaginator[RunStep, AsyncCursorPage[RunStep]]:
+        """
+        Returns a list of run steps belonging to a run.
+
+        Args:
+          after: A cursor for use in pagination. `after` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include after=obj_foo in order to
+              fetch the next page of the list.
+
+          before: A cursor for use in pagination. `before` is an object ID that defines your place
+              in the list. For instance, if you make a list request and receive 100 objects,
+              ending with obj_foo, your subsequent call can include before=obj_foo in order to
+              fetch the previous page of the list.
+
+          limit: A limit on the number of objects to be returned. Limit can range between 1 and
+              100, and the default is 20.
+
+          order: Sort order by the `created_at` timestamp of the objects. `asc` for ascending
+              order and `desc` for descending order.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._get_api_list(
+            f"/threads/{thread_id}/runs/{run_id}/steps",
+            page=AsyncCursorPage[RunStep],
+            options=make_request_options(
+                extra_headers=extra_headers,
+                extra_query=extra_query,
+                extra_body=extra_body,
+                timeout=timeout,
+                query=maybe_transform(
+                    {
+                        "after": after,
+                        "before": before,
+                        "limit": limit,
+                        "order": order,
+                    },
+                    step_list_params.StepListParams,
+                ),
+            ),
+            model=RunStep,
+        )
+
+
+class StepsWithRawResponse:
+    def __init__(self, steps: Steps) -> None:
+        self.retrieve = to_raw_response_wrapper(
+            steps.retrieve,
+        )
+        self.list = to_raw_response_wrapper(
+            steps.list,
+        )
+
+
+class AsyncStepsWithRawResponse:
+    def __init__(self, steps: AsyncSteps) -> None:
+        self.retrieve = async_to_raw_response_wrapper(
+            steps.retrieve,
+        )
+        self.list = async_to_raw_response_wrapper(
+            steps.list,
+        )
src/openai/resources/beta/threads/__init__.py
@@ -0,0 +1,30 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from .runs import Runs, AsyncRuns, RunsWithRawResponse, AsyncRunsWithRawResponse
+from .threads import (
+    Threads,
+    AsyncThreads,
+    ThreadsWithRawResponse,
+    AsyncThreadsWithRawResponse,
+)
+from .messages import (
+    Messages,
+    AsyncMessages,
+    MessagesWithRawResponse,
+    AsyncMessagesWithRawResponse,
+)
+
+__all__ = [
+    "Runs",
+    "AsyncRuns",
+    "RunsWithRawResponse",
+    "AsyncRunsWithRawResponse",
+    "Messages",
+    "AsyncMessages",
+    "MessagesWithRawResponse",
+    "AsyncMessagesWithRawResponse",
+    "Threads",
+    "AsyncThreads",
+    "ThreadsWithRawResponse",
+    "AsyncThreadsWithRawResponse",
+]
src/openai/resources/beta/threads/threads.py
@@ -0,0 +1,541 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, List, Optional
+
+from .runs import Runs, AsyncRuns, RunsWithRawResponse, AsyncRunsWithRawResponse
+from .messages import (
+    Messages,
+    AsyncMessages,
+    MessagesWithRawResponse,
+    AsyncMessagesWithRawResponse,
+)
+from ...._types import NOT_GIVEN, Body, Query, Headers, NotGiven
+from ...._utils import maybe_transform
+from ...._resource import SyncAPIResource, AsyncAPIResource
+from ...._response import to_raw_response_wrapper, async_to_raw_response_wrapper
+from ....types.beta import (
+    Thread,
+    ThreadDeleted,
+    thread_create_params,
+    thread_update_params,
+    thread_create_and_run_params,
+)
+from ...._base_client import make_request_options
+from ....types.beta.threads import Run
+
+if TYPE_CHECKING:
+    from ...._client import OpenAI, AsyncOpenAI
+
+__all__ = ["Threads", "AsyncThreads"]
+
+
+class Threads(SyncAPIResource):
+    runs: Runs
+    messages: Messages
+    with_raw_response: ThreadsWithRawResponse
+
+    def __init__(self, client: OpenAI) -> None:
+        super().__init__(client)
+        self.runs = Runs(client)
+        self.messages = Messages(client)
+        self.with_raw_response = ThreadsWithRawResponse(self)
+
+    def create(
+        self,
+        *,
+        messages: List[thread_create_params.Message] | NotGiven = NOT_GIVEN,
+        metadata: Optional[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 | None | NotGiven = NOT_GIVEN,
+    ) -> Thread:
+        """
+        Create a thread.
+
+        Args:
+          messages: A list of [messages](https://platform.openai.com/docs/api-reference/messages) to
+              start the thread with.
+
+          metadata: 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.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._post(
+            "/threads",
+            body=maybe_transform(
+                {
+                    "messages": messages,
+                    "metadata": metadata,
+                },
+                thread_create_params.ThreadCreateParams,
+            ),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Thread,
+        )
+
+    def retrieve(
+        self,
+        thread_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 | None | NotGiven = NOT_GIVEN,
+    ) -> Thread:
+        """
+        Retrieves a thread.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._get(
+            f"/threads/{thread_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Thread,
+        )
+
+    def update(
+        self,
+        thread_id: str,
+        *,
+        metadata: Optional[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 | None | NotGiven = NOT_GIVEN,
+    ) -> Thread:
+        """
+        Modifies a thread.
+
+        Args:
+          metadata: 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.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._post(
+            f"/threads/{thread_id}",
+            body=maybe_transform({"metadata": metadata}, thread_update_params.ThreadUpdateParams),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Thread,
+        )
+
+    def delete(
+        self,
+        thread_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 | None | NotGiven = NOT_GIVEN,
+    ) -> ThreadDeleted:
+        """
+        Delete a thread.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._delete(
+            f"/threads/{thread_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=ThreadDeleted,
+        )
+
+    def create_and_run(
+        self,
+        *,
+        assistant_id: str,
+        instructions: Optional[str] | NotGiven = NOT_GIVEN,
+        metadata: Optional[object] | NotGiven = NOT_GIVEN,
+        model: Optional[str] | NotGiven = NOT_GIVEN,
+        thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN,
+        tools: Optional[List[thread_create_and_run_params.Tool]] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> Run:
+        """
+        Create a thread and run it in one request.
+
+        Args:
+          assistant_id: The ID of the
+              [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
+              execute this run.
+
+          instructions: Override the default system message of the assistant. This is useful for
+              modifying the behavior on a per-run basis.
+
+          metadata: 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.
+
+          model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
+              be used to execute this run. If a value is provided here, it will override the
+              model associated with the assistant. If not, the model associated with the
+              assistant will be used.
+
+          thread: If no thread is provided, an empty thread will be created.
+
+          tools: Override the tools the assistant can use for this run. This is useful for
+              modifying the behavior on a per-run basis.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return self._post(
+            "/threads/runs",
+            body=maybe_transform(
+                {
+                    "assistant_id": assistant_id,
+                    "instructions": instructions,
+                    "metadata": metadata,
+                    "model": model,
+                    "thread": thread,
+                    "tools": tools,
+                },
+                thread_create_and_run_params.ThreadCreateAndRunParams,
+            ),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Run,
+        )
+
+
+class AsyncThreads(AsyncAPIResource):
+    runs: AsyncRuns
+    messages: AsyncMessages
+    with_raw_response: AsyncThreadsWithRawResponse
+
+    def __init__(self, client: AsyncOpenAI) -> None:
+        super().__init__(client)
+        self.runs = AsyncRuns(client)
+        self.messages = AsyncMessages(client)
+        self.with_raw_response = AsyncThreadsWithRawResponse(self)
+
+    async def create(
+        self,
+        *,
+        messages: List[thread_create_params.Message] | NotGiven = NOT_GIVEN,
+        metadata: Optional[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 | None | NotGiven = NOT_GIVEN,
+    ) -> Thread:
+        """
+        Create a thread.
+
+        Args:
+          messages: A list of [messages](https://platform.openai.com/docs/api-reference/messages) to
+              start the thread with.
+
+          metadata: 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.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._post(
+            "/threads",
+            body=maybe_transform(
+                {
+                    "messages": messages,
+                    "metadata": metadata,
+                },
+                thread_create_params.ThreadCreateParams,
+            ),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Thread,
+        )
+
+    async def retrieve(
+        self,
+        thread_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 | None | NotGiven = NOT_GIVEN,
+    ) -> Thread:
+        """
+        Retrieves a thread.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._get(
+            f"/threads/{thread_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Thread,
+        )
+
+    async def update(
+        self,
+        thread_id: str,
+        *,
+        metadata: Optional[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 | None | NotGiven = NOT_GIVEN,
+    ) -> Thread:
+        """
+        Modifies a thread.
+
+        Args:
+          metadata: 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.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._post(
+            f"/threads/{thread_id}",
+            body=maybe_transform({"metadata": metadata}, thread_update_params.ThreadUpdateParams),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Thread,
+        )
+
+    async def delete(
+        self,
+        thread_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 | None | NotGiven = NOT_GIVEN,
+    ) -> ThreadDeleted:
+        """
+        Delete a thread.
+
+        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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._delete(
+            f"/threads/{thread_id}",
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=ThreadDeleted,
+        )
+
+    async def create_and_run(
+        self,
+        *,
+        assistant_id: str,
+        instructions: Optional[str] | NotGiven = NOT_GIVEN,
+        metadata: Optional[object] | NotGiven = NOT_GIVEN,
+        model: Optional[str] | NotGiven = NOT_GIVEN,
+        thread: thread_create_and_run_params.Thread | NotGiven = NOT_GIVEN,
+        tools: Optional[List[thread_create_and_run_params.Tool]] | 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 | None | NotGiven = NOT_GIVEN,
+    ) -> Run:
+        """
+        Create a thread and run it in one request.
+
+        Args:
+          assistant_id: The ID of the
+              [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
+              execute this run.
+
+          instructions: Override the default system message of the assistant. This is useful for
+              modifying the behavior on a per-run basis.
+
+          metadata: 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.
+
+          model: The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
+              be used to execute this run. If a value is provided here, it will override the
+              model associated with the assistant. If not, the model associated with the
+              assistant will be used.
+
+          thread: If no thread is provided, an empty thread will be created.
+
+          tools: Override the tools the assistant can use for this run. This is useful for
+              modifying the behavior on a per-run basis.
+
+          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
+        """
+        extra_headers = {"OpenAI-Beta": "assistants=v1", **(extra_headers or {})}
+        return await self._post(
+            "/threads/runs",
+            body=maybe_transform(
+                {
+                    "assistant_id": assistant_id,
+                    "instructions": instructions,
+                    "metadata": metadata,
+                    "model": model,
+                    "thread": thread,
+                    "tools": tools,
+                },
+                thread_create_and_run_params.ThreadCreateAndRunParams,
+            ),
+            options=make_request_options(
+                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+            ),
+            cast_to=Run,
+        )
+
+
+class ThreadsWithRawResponse:
+    def __init__(self, threads: Threads) -> None:
+        self.runs = RunsWithRawResponse(threads.runs)
+        self.messages = MessagesWithRawResponse(threads.messages)
+
+        self.create = to_raw_response_wrapper(
+            threads.create,
+        )
+        self.retrieve = to_raw_response_wrapper(
+            threads.retrieve,
+        )
+        self.update = to_raw_response_wrapper(
+            threads.update,
+        )
+        self.delete = to_raw_response_wrapper(
+            threads.delete,
+        )
+        self.create_and_run = to_raw_response_wrapper(
+            threads.create_and_run,
+        )
+
+
+class AsyncThreadsWithRawResponse:
+    def __init__(self, threads: AsyncThreads) -> None:
+        self.runs = AsyncRunsWithRawResponse(threads.runs)
+        self.messages = AsyncMessagesWithRawResponse(threads.messages)
+
+        self.create = async_to_raw_response_wrapper(
+            threads.create,
+        )
+        self.retrieve = async_to_raw_response_wrapper(
+            threads.retrieve,
+        )
+        self.update = async_to_raw_response_wrapper(
+            threads.update,
+        )
+        self.delete = async_to_raw_response_wrapper(
+            threads.delete,
+        )
+        self.create_and_run = async_to_raw_response_wrapper(
+            threads.create_and_run,
+        )
src/openai/resources/beta/__init__.py
@@ -0,0 +1,30 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from .beta import Beta, AsyncBeta, BetaWithRawResponse, AsyncBetaWithRawResponse
+from .threads import (
+    Threads,
+    AsyncThreads,
+    ThreadsWithRawResponse,
+    AsyncThreadsWithRawResponse,
+)
+from .assistants import (
+    Assistants,
+    AsyncAssistants,
+    AssistantsWithRawResponse,
+    AsyncAssistantsWithRawResponse,
+)
+
+__all__ = [
+    "Assistants",
+    "AsyncAssistants",
+    "AssistantsWithRawResponse",
+    "AsyncAssistantsWithRawResponse",
+    "Threads",
+    "AsyncThreads",
+    "ThreadsWithRawResponse",
+    "AsyncThreadsWithRawResponse",
+    "Beta",
+    "AsyncBeta",
+    "BetaWithRawResponse",
+    "AsyncBetaWithRawResponse",
+]
src/openai/resources/beta/beta.py
@@ -0,0 +1,60 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING
+
+from .threads import (
+    Threads,
+    AsyncThreads,
+    ThreadsWithRawResponse,
+    AsyncThreadsWithRawResponse,
+)
+from .assistants import (
+    Assistants,
+    AsyncAssistants,
+    AssistantsWithRawResponse,
+    AsyncAssistantsWithRawResponse,
+)
+from ..._resource import SyncAPIResource, AsyncAPIResource
+
+if TYPE_CHECKING:
+    from ..._client import OpenAI, AsyncOpenAI
+
+__all__ = ["Beta", "AsyncBeta"]
+
+
+class Beta(SyncAPIResource):
+    assistants: Assistants
+    threads: Threads
+    with_raw_response: BetaWithRawResponse
+
+    def __init__(self, client: OpenAI) -> None:
+        super().__init__(client)
+        self.assistants = Assistants(client)
+        self.threads = Threads(client)
+        self.with_raw_response = BetaWithRawResponse(self)
+
+
+class AsyncBeta(AsyncAPIResource):
+    assistants: AsyncAssistants
+    threads: AsyncThreads
+    with_raw_response: AsyncBetaWithRawResponse
+
+    def __init__(self, client: AsyncOpenAI) -> None:
+        super().__init__(client)
+        self.assistants = AsyncAssistants(client)
+        self.threads = AsyncThreads(client)
+        self.with_raw_response = AsyncBetaWithRawResponse(self)
+
+
+class BetaWithRawResponse:
+    def __init__(self, beta: Beta) -> None:
+        self.assistants = AssistantsWithRawResponse(beta.assistants)
+        self.threads = ThreadsWithRawResponse(beta.threads)
+
+
+class AsyncBetaWithRawResponse:
+    def __init__(self, beta: AsyncBeta) -> None:
+        self.assistants = AsyncAssistantsWithRawResponse(beta.assistants)
+        self.threads = AsyncThreadsWithRawResponse(beta.threads)
src/openai/resources/chat/completions.py
@@ -13,7 +13,9 @@ from ..._streaming import Stream, AsyncStream
 from ...types.chat import (
     ChatCompletion,
     ChatCompletionChunk,
+    ChatCompletionToolParam,
     ChatCompletionMessageParam,
+    ChatCompletionToolChoiceOptionParam,
     completion_create_params,
 )
 from ..._base_client import make_request_options
@@ -59,9 +61,13 @@ class Completions(SyncAPIResource):
         max_tokens: Optional[int] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         presence_penalty: Optional[float] | NotGiven = NOT_GIVEN,
+        response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN,
+        seed: Optional[int] | NotGiven = NOT_GIVEN,
         stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN,
         stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN,
         temperature: Optional[float] | NotGiven = NOT_GIVEN,
+        tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN,
+        tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN,
         top_p: Optional[float] | NotGiven = NOT_GIVEN,
         user: str | NotGiven = NOT_GIVEN,
         # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -88,18 +94,24 @@ class Completions(SyncAPIResource):
 
               [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
 
-          function_call: Controls how the model calls functions. "none" means the model will not call a
-              function and instead generates a message. "auto" means the model can pick
-              between generating a message or calling a function. Specifying a particular
-              function via `{"name": "my_function"}` forces the model to call that function.
-              "none" is the default when no functions are present. "auto" is the default if
+          function_call: Deprecated in favor of `tool_choice`.
+
+              Controls which (if any) function is called by the model. `none` means the model
+              will not call a function and instead generates a message. `auto` means the model
+              can pick between generating a message or calling a function. Specifying a
+              particular function via `{"name": "my_function"}` forces the model to call that
+              function.
+
+              `none` is the default when no functions are present. `auto`` is the default if
               functions are present.
 
-          functions: A list of functions the model may generate JSON inputs for.
+          functions: Deprecated in favor of `tools`.
+
+              A list of functions the model may generate JSON inputs for.
 
           logit_bias: Modify the likelihood of specified tokens appearing in the completion.
 
-              Accepts a json object that maps tokens (specified by their token ID in the
+              Accepts a JSON object that maps tokens (specified by their token ID in the
               tokenizer) to an associated bias value from -100 to 100. Mathematically, the
               bias is added to the logits generated by the model prior to sampling. The exact
               effect will vary per model, but values between -1 and 1 should decrease or
@@ -121,6 +133,15 @@ class Completions(SyncAPIResource):
 
               [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
 
+          response_format: An object specifying the format that the model must output. Used to enable JSON
+              mode.
+
+          seed: This feature is in Beta. If specified, our system will make a best effort to
+              sample deterministically, such that repeated requests with the same `seed` and
+              parameters should return the same result. Determinism is not guaranteed, and you
+              should refer to the `system_fingerprint` response parameter to monitor changes
+              in the backend.
+
           stop: Up to 4 sequences where the API will stop generating further tokens.
 
           stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be
@@ -136,6 +157,20 @@ class Completions(SyncAPIResource):
 
               We generally recommend altering this or `top_p` but not both.
 
+          tool_choice: Controls which (if any) function is called by the model. `none` means the model
+              will not call a function and instead generates a message. `auto` means the model
+              can pick between generating a message or calling a function. Specifying a
+              particular function via
+              `{"type: "function", "function": {"name": "my_function"}}` forces the model to
+              call that function.
+
+              `none` is the default when no functions are present. `auto` is the default if
+              functions are present.
+
+          tools: A list of tools the model may call. Currently, only functions are supported as a
+              tool. Use this to provide a list of functions the model may generate JSON inputs
+              for.
+
           top_p: An alternative to sampling with temperature, called nucleus sampling, where the
               model considers the results of the tokens with top_p probability mass. So 0.1
               means only the tokens comprising the top 10% probability mass are considered.
@@ -185,8 +220,12 @@ class Completions(SyncAPIResource):
         max_tokens: Optional[int] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         presence_penalty: Optional[float] | NotGiven = NOT_GIVEN,
+        response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN,
+        seed: Optional[int] | NotGiven = NOT_GIVEN,
         stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN,
         temperature: Optional[float] | NotGiven = NOT_GIVEN,
+        tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN,
+        tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN,
         top_p: Optional[float] | NotGiven = NOT_GIVEN,
         user: str | NotGiven = NOT_GIVEN,
         # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -220,18 +259,24 @@ class Completions(SyncAPIResource):
 
               [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
 
-          function_call: Controls how the model calls functions. "none" means the model will not call a
-              function and instead generates a message. "auto" means the model can pick
-              between generating a message or calling a function. Specifying a particular
-              function via `{"name": "my_function"}` forces the model to call that function.
-              "none" is the default when no functions are present. "auto" is the default if
+          function_call: Deprecated in favor of `tool_choice`.
+
+              Controls which (if any) function is called by the model. `none` means the model
+              will not call a function and instead generates a message. `auto` means the model
+              can pick between generating a message or calling a function. Specifying a
+              particular function via `{"name": "my_function"}` forces the model to call that
+              function.
+
+              `none` is the default when no functions are present. `auto`` is the default if
               functions are present.
 
-          functions: A list of functions the model may generate JSON inputs for.
+          functions: Deprecated in favor of `tools`.
+
+              A list of functions the model may generate JSON inputs for.
 
           logit_bias: Modify the likelihood of specified tokens appearing in the completion.
 
-              Accepts a json object that maps tokens (specified by their token ID in the
+              Accepts a JSON object that maps tokens (specified by their token ID in the
               tokenizer) to an associated bias value from -100 to 100. Mathematically, the
               bias is added to the logits generated by the model prior to sampling. The exact
               effect will vary per model, but values between -1 and 1 should decrease or
@@ -253,6 +298,15 @@ class Completions(SyncAPIResource):
 
               [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
 
+          response_format: An object specifying the format that the model must output. Used to enable JSON
+              mode.
+
+          seed: This feature is in Beta. If specified, our system will make a best effort to
+              sample deterministically, such that repeated requests with the same `seed` and
+              parameters should return the same result. Determinism is not guaranteed, and you
+              should refer to the `system_fingerprint` response parameter to monitor changes
+              in the backend.
+
           stop: Up to 4 sequences where the API will stop generating further tokens.
 
           temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will
@@ -261,6 +315,20 @@ class Completions(SyncAPIResource):
 
               We generally recommend altering this or `top_p` but not both.
 
+          tool_choice: Controls which (if any) function is called by the model. `none` means the model
+              will not call a function and instead generates a message. `auto` means the model
+              can pick between generating a message or calling a function. Specifying a
+              particular function via
+              `{"type: "function", "function": {"name": "my_function"}}` forces the model to
+              call that function.
+
+              `none` is the default when no functions are present. `auto` is the default if
+              functions are present.
+
+          tools: A list of tools the model may call. Currently, only functions are supported as a
+              tool. Use this to provide a list of functions the model may generate JSON inputs
+              for.
+
           top_p: An alternative to sampling with temperature, called nucleus sampling, where the
               model considers the results of the tokens with top_p probability mass. So 0.1
               means only the tokens comprising the top 10% probability mass are considered.
@@ -310,8 +378,12 @@ class Completions(SyncAPIResource):
         max_tokens: Optional[int] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         presence_penalty: Optional[float] | NotGiven = NOT_GIVEN,
+        response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN,
+        seed: Optional[int] | NotGiven = NOT_GIVEN,
         stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN,
         temperature: Optional[float] | NotGiven = NOT_GIVEN,
+        tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN,
+        tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN,
         top_p: Optional[float] | NotGiven = NOT_GIVEN,
         user: str | NotGiven = NOT_GIVEN,
         # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -345,18 +417,24 @@ class Completions(SyncAPIResource):
 
               [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
 
-          function_call: Controls how the model calls functions. "none" means the model will not call a
-              function and instead generates a message. "auto" means the model can pick
-              between generating a message or calling a function. Specifying a particular
-              function via `{"name": "my_function"}` forces the model to call that function.
-              "none" is the default when no functions are present. "auto" is the default if
+          function_call: Deprecated in favor of `tool_choice`.
+
+              Controls which (if any) function is called by the model. `none` means the model
+              will not call a function and instead generates a message. `auto` means the model
+              can pick between generating a message or calling a function. Specifying a
+              particular function via `{"name": "my_function"}` forces the model to call that
+              function.
+
+              `none` is the default when no functions are present. `auto`` is the default if
               functions are present.
 
-          functions: A list of functions the model may generate JSON inputs for.
+          functions: Deprecated in favor of `tools`.
+
+              A list of functions the model may generate JSON inputs for.
 
           logit_bias: Modify the likelihood of specified tokens appearing in the completion.
 
-              Accepts a json object that maps tokens (specified by their token ID in the
+              Accepts a JSON object that maps tokens (specified by their token ID in the
               tokenizer) to an associated bias value from -100 to 100. Mathematically, the
               bias is added to the logits generated by the model prior to sampling. The exact
               effect will vary per model, but values between -1 and 1 should decrease or
@@ -378,6 +456,15 @@ class Completions(SyncAPIResource):
 
               [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
 
+          response_format: An object specifying the format that the model must output. Used to enable JSON
+              mode.
+
+          seed: This feature is in Beta. If specified, our system will make a best effort to
+              sample deterministically, such that repeated requests with the same `seed` and
+              parameters should return the same result. Determinism is not guaranteed, and you
+              should refer to the `system_fingerprint` response parameter to monitor changes
+              in the backend.
+
           stop: Up to 4 sequences where the API will stop generating further tokens.
 
           temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will
@@ -386,6 +473,20 @@ class Completions(SyncAPIResource):
 
               We generally recommend altering this or `top_p` but not both.
 
+          tool_choice: Controls which (if any) function is called by the model. `none` means the model
+              will not call a function and instead generates a message. `auto` means the model
+              can pick between generating a message or calling a function. Specifying a
+              particular function via
+              `{"type: "function", "function": {"name": "my_function"}}` forces the model to
+              call that function.
+
+              `none` is the default when no functions are present. `auto` is the default if
+              functions are present.
+
+          tools: A list of tools the model may call. Currently, only functions are supported as a
+              tool. Use this to provide a list of functions the model may generate JSON inputs
+              for.
+
           top_p: An alternative to sampling with temperature, called nucleus sampling, where the
               model considers the results of the tokens with top_p probability mass. So 0.1
               means only the tokens comprising the top 10% probability mass are considered.
@@ -434,9 +535,13 @@ class Completions(SyncAPIResource):
         max_tokens: Optional[int] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         presence_penalty: Optional[float] | NotGiven = NOT_GIVEN,
+        response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN,
+        seed: Optional[int] | NotGiven = NOT_GIVEN,
         stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN,
         stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN,
         temperature: Optional[float] | NotGiven = NOT_GIVEN,
+        tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN,
+        tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN,
         top_p: Optional[float] | NotGiven = NOT_GIVEN,
         user: str | NotGiven = NOT_GIVEN,
         # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -459,9 +564,13 @@ class Completions(SyncAPIResource):
                     "max_tokens": max_tokens,
                     "n": n,
                     "presence_penalty": presence_penalty,
+                    "response_format": response_format,
+                    "seed": seed,
                     "stop": stop,
                     "stream": stream,
                     "temperature": temperature,
+                    "tool_choice": tool_choice,
+                    "tools": tools,
                     "top_p": top_p,
                     "user": user,
                 },
@@ -511,9 +620,13 @@ class AsyncCompletions(AsyncAPIResource):
         max_tokens: Optional[int] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         presence_penalty: Optional[float] | NotGiven = NOT_GIVEN,
+        response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN,
+        seed: Optional[int] | NotGiven = NOT_GIVEN,
         stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN,
         stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN,
         temperature: Optional[float] | NotGiven = NOT_GIVEN,
+        tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN,
+        tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN,
         top_p: Optional[float] | NotGiven = NOT_GIVEN,
         user: str | NotGiven = NOT_GIVEN,
         # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -540,18 +653,24 @@ class AsyncCompletions(AsyncAPIResource):
 
               [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
 
-          function_call: Controls how the model calls functions. "none" means the model will not call a
-              function and instead generates a message. "auto" means the model can pick
-              between generating a message or calling a function. Specifying a particular
-              function via `{"name": "my_function"}` forces the model to call that function.
-              "none" is the default when no functions are present. "auto" is the default if
+          function_call: Deprecated in favor of `tool_choice`.
+
+              Controls which (if any) function is called by the model. `none` means the model
+              will not call a function and instead generates a message. `auto` means the model
+              can pick between generating a message or calling a function. Specifying a
+              particular function via `{"name": "my_function"}` forces the model to call that
+              function.
+
+              `none` is the default when no functions are present. `auto`` is the default if
               functions are present.
 
-          functions: A list of functions the model may generate JSON inputs for.
+          functions: Deprecated in favor of `tools`.
+
+              A list of functions the model may generate JSON inputs for.
 
           logit_bias: Modify the likelihood of specified tokens appearing in the completion.
 
-              Accepts a json object that maps tokens (specified by their token ID in the
+              Accepts a JSON object that maps tokens (specified by their token ID in the
               tokenizer) to an associated bias value from -100 to 100. Mathematically, the
               bias is added to the logits generated by the model prior to sampling. The exact
               effect will vary per model, but values between -1 and 1 should decrease or
@@ -573,6 +692,15 @@ class AsyncCompletions(AsyncAPIResource):
 
               [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
 
+          response_format: An object specifying the format that the model must output. Used to enable JSON
+              mode.
+
+          seed: This feature is in Beta. If specified, our system will make a best effort to
+              sample deterministically, such that repeated requests with the same `seed` and
+              parameters should return the same result. Determinism is not guaranteed, and you
+              should refer to the `system_fingerprint` response parameter to monitor changes
+              in the backend.
+
           stop: Up to 4 sequences where the API will stop generating further tokens.
 
           stream: If set, partial message deltas will be sent, like in ChatGPT. Tokens will be
@@ -588,6 +716,20 @@ class AsyncCompletions(AsyncAPIResource):
 
               We generally recommend altering this or `top_p` but not both.
 
+          tool_choice: Controls which (if any) function is called by the model. `none` means the model
+              will not call a function and instead generates a message. `auto` means the model
+              can pick between generating a message or calling a function. Specifying a
+              particular function via
+              `{"type: "function", "function": {"name": "my_function"}}` forces the model to
+              call that function.
+
+              `none` is the default when no functions are present. `auto` is the default if
+              functions are present.
+
+          tools: A list of tools the model may call. Currently, only functions are supported as a
+              tool. Use this to provide a list of functions the model may generate JSON inputs
+              for.
+
           top_p: An alternative to sampling with temperature, called nucleus sampling, where the
               model considers the results of the tokens with top_p probability mass. So 0.1
               means only the tokens comprising the top 10% probability mass are considered.
@@ -637,8 +779,12 @@ class AsyncCompletions(AsyncAPIResource):
         max_tokens: Optional[int] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         presence_penalty: Optional[float] | NotGiven = NOT_GIVEN,
+        response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN,
+        seed: Optional[int] | NotGiven = NOT_GIVEN,
         stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN,
         temperature: Optional[float] | NotGiven = NOT_GIVEN,
+        tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN,
+        tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN,
         top_p: Optional[float] | NotGiven = NOT_GIVEN,
         user: str | NotGiven = NOT_GIVEN,
         # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -672,18 +818,24 @@ class AsyncCompletions(AsyncAPIResource):
 
               [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
 
-          function_call: Controls how the model calls functions. "none" means the model will not call a
-              function and instead generates a message. "auto" means the model can pick
-              between generating a message or calling a function. Specifying a particular
-              function via `{"name": "my_function"}` forces the model to call that function.
-              "none" is the default when no functions are present. "auto" is the default if
+          function_call: Deprecated in favor of `tool_choice`.
+
+              Controls which (if any) function is called by the model. `none` means the model
+              will not call a function and instead generates a message. `auto` means the model
+              can pick between generating a message or calling a function. Specifying a
+              particular function via `{"name": "my_function"}` forces the model to call that
+              function.
+
+              `none` is the default when no functions are present. `auto`` is the default if
               functions are present.
 
-          functions: A list of functions the model may generate JSON inputs for.
+          functions: Deprecated in favor of `tools`.
+
+              A list of functions the model may generate JSON inputs for.
 
           logit_bias: Modify the likelihood of specified tokens appearing in the completion.
 
-              Accepts a json object that maps tokens (specified by their token ID in the
+              Accepts a JSON object that maps tokens (specified by their token ID in the
               tokenizer) to an associated bias value from -100 to 100. Mathematically, the
               bias is added to the logits generated by the model prior to sampling. The exact
               effect will vary per model, but values between -1 and 1 should decrease or
@@ -705,6 +857,15 @@ class AsyncCompletions(AsyncAPIResource):
 
               [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
 
+          response_format: An object specifying the format that the model must output. Used to enable JSON
+              mode.
+
+          seed: This feature is in Beta. If specified, our system will make a best effort to
+              sample deterministically, such that repeated requests with the same `seed` and
+              parameters should return the same result. Determinism is not guaranteed, and you
+              should refer to the `system_fingerprint` response parameter to monitor changes
+              in the backend.
+
           stop: Up to 4 sequences where the API will stop generating further tokens.
 
           temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will
@@ -713,6 +874,20 @@ class AsyncCompletions(AsyncAPIResource):
 
               We generally recommend altering this or `top_p` but not both.
 
+          tool_choice: Controls which (if any) function is called by the model. `none` means the model
+              will not call a function and instead generates a message. `auto` means the model
+              can pick between generating a message or calling a function. Specifying a
+              particular function via
+              `{"type: "function", "function": {"name": "my_function"}}` forces the model to
+              call that function.
+
+              `none` is the default when no functions are present. `auto` is the default if
+              functions are present.
+
+          tools: A list of tools the model may call. Currently, only functions are supported as a
+              tool. Use this to provide a list of functions the model may generate JSON inputs
+              for.
+
           top_p: An alternative to sampling with temperature, called nucleus sampling, where the
               model considers the results of the tokens with top_p probability mass. So 0.1
               means only the tokens comprising the top 10% probability mass are considered.
@@ -762,8 +937,12 @@ class AsyncCompletions(AsyncAPIResource):
         max_tokens: Optional[int] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         presence_penalty: Optional[float] | NotGiven = NOT_GIVEN,
+        response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN,
+        seed: Optional[int] | NotGiven = NOT_GIVEN,
         stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN,
         temperature: Optional[float] | NotGiven = NOT_GIVEN,
+        tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN,
+        tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN,
         top_p: Optional[float] | NotGiven = NOT_GIVEN,
         user: str | NotGiven = NOT_GIVEN,
         # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -797,18 +976,24 @@ class AsyncCompletions(AsyncAPIResource):
 
               [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
 
-          function_call: Controls how the model calls functions. "none" means the model will not call a
-              function and instead generates a message. "auto" means the model can pick
-              between generating a message or calling a function. Specifying a particular
-              function via `{"name": "my_function"}` forces the model to call that function.
-              "none" is the default when no functions are present. "auto" is the default if
+          function_call: Deprecated in favor of `tool_choice`.
+
+              Controls which (if any) function is called by the model. `none` means the model
+              will not call a function and instead generates a message. `auto` means the model
+              can pick between generating a message or calling a function. Specifying a
+              particular function via `{"name": "my_function"}` forces the model to call that
+              function.
+
+              `none` is the default when no functions are present. `auto`` is the default if
               functions are present.
 
-          functions: A list of functions the model may generate JSON inputs for.
+          functions: Deprecated in favor of `tools`.
+
+              A list of functions the model may generate JSON inputs for.
 
           logit_bias: Modify the likelihood of specified tokens appearing in the completion.
 
-              Accepts a json object that maps tokens (specified by their token ID in the
+              Accepts a JSON object that maps tokens (specified by their token ID in the
               tokenizer) to an associated bias value from -100 to 100. Mathematically, the
               bias is added to the logits generated by the model prior to sampling. The exact
               effect will vary per model, but values between -1 and 1 should decrease or
@@ -830,6 +1015,15 @@ class AsyncCompletions(AsyncAPIResource):
 
               [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
 
+          response_format: An object specifying the format that the model must output. Used to enable JSON
+              mode.
+
+          seed: This feature is in Beta. If specified, our system will make a best effort to
+              sample deterministically, such that repeated requests with the same `seed` and
+              parameters should return the same result. Determinism is not guaranteed, and you
+              should refer to the `system_fingerprint` response parameter to monitor changes
+              in the backend.
+
           stop: Up to 4 sequences where the API will stop generating further tokens.
 
           temperature: What sampling temperature to use, between 0 and 2. Higher values like 0.8 will
@@ -838,6 +1032,20 @@ class AsyncCompletions(AsyncAPIResource):
 
               We generally recommend altering this or `top_p` but not both.
 
+          tool_choice: Controls which (if any) function is called by the model. `none` means the model
+              will not call a function and instead generates a message. `auto` means the model
+              can pick between generating a message or calling a function. Specifying a
+              particular function via
+              `{"type: "function", "function": {"name": "my_function"}}` forces the model to
+              call that function.
+
+              `none` is the default when no functions are present. `auto` is the default if
+              functions are present.
+
+          tools: A list of tools the model may call. Currently, only functions are supported as a
+              tool. Use this to provide a list of functions the model may generate JSON inputs
+              for.
+
           top_p: An alternative to sampling with temperature, called nucleus sampling, where the
               model considers the results of the tokens with top_p probability mass. So 0.1
               means only the tokens comprising the top 10% probability mass are considered.
@@ -886,9 +1094,13 @@ class AsyncCompletions(AsyncAPIResource):
         max_tokens: Optional[int] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         presence_penalty: Optional[float] | NotGiven = NOT_GIVEN,
+        response_format: completion_create_params.ResponseFormat | NotGiven = NOT_GIVEN,
+        seed: Optional[int] | NotGiven = NOT_GIVEN,
         stop: Union[Optional[str], List[str]] | NotGiven = NOT_GIVEN,
         stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN,
         temperature: Optional[float] | NotGiven = NOT_GIVEN,
+        tool_choice: ChatCompletionToolChoiceOptionParam | NotGiven = NOT_GIVEN,
+        tools: List[ChatCompletionToolParam] | NotGiven = NOT_GIVEN,
         top_p: Optional[float] | NotGiven = NOT_GIVEN,
         user: str | NotGiven = NOT_GIVEN,
         # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
@@ -911,9 +1123,13 @@ class AsyncCompletions(AsyncAPIResource):
                     "max_tokens": max_tokens,
                     "n": n,
                     "presence_penalty": presence_penalty,
+                    "response_format": response_format,
+                    "seed": seed,
                     "stop": stop,
                     "stream": stream,
                     "temperature": temperature,
+                    "tool_choice": tool_choice,
+                    "tools": tools,
                     "top_p": top_p,
                     "user": user,
                 },
src/openai/resources/__init__.py
@@ -1,5 +1,6 @@
 # File generated from our OpenAPI spec by Stainless.
 
+from .beta import Beta, AsyncBeta, BetaWithRawResponse, AsyncBetaWithRawResponse
 from .chat import Chat, AsyncChat, ChatWithRawResponse, AsyncChatWithRawResponse
 from .audio import Audio, AsyncAudio, AudioWithRawResponse, AsyncAudioWithRawResponse
 from .edits import Edits, AsyncEdits, EditsWithRawResponse, AsyncEditsWithRawResponse
@@ -92,4 +93,8 @@ __all__ = [
     "AsyncFineTunes",
     "FineTunesWithRawResponse",
     "AsyncFineTunesWithRawResponse",
+    "Beta",
+    "AsyncBeta",
+    "BetaWithRawResponse",
+    "AsyncBetaWithRawResponse",
 ]
src/openai/resources/completions.py
@@ -54,6 +54,7 @@ class Completions(SyncAPIResource):
         max_tokens: Optional[int] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         presence_penalty: Optional[float] | NotGiven = NOT_GIVEN,
+        seed: Optional[int] | NotGiven = NOT_GIVEN,
         stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN,
         stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN,
         suffix: Optional[str] | NotGiven = NOT_GIVEN,
@@ -104,7 +105,7 @@ class Completions(SyncAPIResource):
 
           logit_bias: Modify the likelihood of specified tokens appearing in the completion.
 
-              Accepts a json object that maps tokens (specified by their token ID in the GPT
+              Accepts a JSON object that maps tokens (specified by their token ID in the GPT
               tokenizer) to an associated bias value from -100 to 100. You can use this
               [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to
               convert text to token IDs. Mathematically, the bias is added to the logits
@@ -142,6 +143,13 @@ class Completions(SyncAPIResource):
 
               [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
 
+          seed: If specified, our system will make a best effort to sample deterministically,
+              such that repeated requests with the same `seed` and parameters should return
+              the same result.
+
+              Determinism is not guaranteed, and you should refer to the `system_fingerprint`
+              response parameter to monitor changes in the backend.
+
           stop: Up to 4 sequences where the API will stop generating further tokens. The
               returned text will not contain the stop sequence.
 
@@ -209,6 +217,7 @@ class Completions(SyncAPIResource):
         max_tokens: Optional[int] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         presence_penalty: Optional[float] | NotGiven = NOT_GIVEN,
+        seed: Optional[int] | NotGiven = NOT_GIVEN,
         stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN,
         suffix: Optional[str] | NotGiven = NOT_GIVEN,
         temperature: Optional[float] | NotGiven = NOT_GIVEN,
@@ -265,7 +274,7 @@ class Completions(SyncAPIResource):
 
           logit_bias: Modify the likelihood of specified tokens appearing in the completion.
 
-              Accepts a json object that maps tokens (specified by their token ID in the GPT
+              Accepts a JSON object that maps tokens (specified by their token ID in the GPT
               tokenizer) to an associated bias value from -100 to 100. You can use this
               [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to
               convert text to token IDs. Mathematically, the bias is added to the logits
@@ -303,6 +312,13 @@ class Completions(SyncAPIResource):
 
               [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
 
+          seed: If specified, our system will make a best effort to sample deterministically,
+              such that repeated requests with the same `seed` and parameters should return
+              the same result.
+
+              Determinism is not guaranteed, and you should refer to the `system_fingerprint`
+              response parameter to monitor changes in the backend.
+
           stop: Up to 4 sequences where the API will stop generating further tokens. The
               returned text will not contain the stop sequence.
 
@@ -363,6 +379,7 @@ class Completions(SyncAPIResource):
         max_tokens: Optional[int] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         presence_penalty: Optional[float] | NotGiven = NOT_GIVEN,
+        seed: Optional[int] | NotGiven = NOT_GIVEN,
         stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN,
         suffix: Optional[str] | NotGiven = NOT_GIVEN,
         temperature: Optional[float] | NotGiven = NOT_GIVEN,
@@ -419,7 +436,7 @@ class Completions(SyncAPIResource):
 
           logit_bias: Modify the likelihood of specified tokens appearing in the completion.
 
-              Accepts a json object that maps tokens (specified by their token ID in the GPT
+              Accepts a JSON object that maps tokens (specified by their token ID in the GPT
               tokenizer) to an associated bias value from -100 to 100. You can use this
               [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to
               convert text to token IDs. Mathematically, the bias is added to the logits
@@ -457,6 +474,13 @@ class Completions(SyncAPIResource):
 
               [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
 
+          seed: If specified, our system will make a best effort to sample deterministically,
+              such that repeated requests with the same `seed` and parameters should return
+              the same result.
+
+              Determinism is not guaranteed, and you should refer to the `system_fingerprint`
+              response parameter to monitor changes in the backend.
+
           stop: Up to 4 sequences where the API will stop generating further tokens. The
               returned text will not contain the stop sequence.
 
@@ -516,6 +540,7 @@ class Completions(SyncAPIResource):
         max_tokens: Optional[int] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         presence_penalty: Optional[float] | NotGiven = NOT_GIVEN,
+        seed: Optional[int] | NotGiven = NOT_GIVEN,
         stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN,
         stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN,
         suffix: Optional[str] | NotGiven = NOT_GIVEN,
@@ -543,6 +568,7 @@ class Completions(SyncAPIResource):
                     "max_tokens": max_tokens,
                     "n": n,
                     "presence_penalty": presence_penalty,
+                    "seed": seed,
                     "stop": stop,
                     "stream": stream,
                     "suffix": suffix,
@@ -596,6 +622,7 @@ class AsyncCompletions(AsyncAPIResource):
         max_tokens: Optional[int] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         presence_penalty: Optional[float] | NotGiven = NOT_GIVEN,
+        seed: Optional[int] | NotGiven = NOT_GIVEN,
         stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN,
         stream: Optional[Literal[False]] | NotGiven = NOT_GIVEN,
         suffix: Optional[str] | NotGiven = NOT_GIVEN,
@@ -646,7 +673,7 @@ class AsyncCompletions(AsyncAPIResource):
 
           logit_bias: Modify the likelihood of specified tokens appearing in the completion.
 
-              Accepts a json object that maps tokens (specified by their token ID in the GPT
+              Accepts a JSON object that maps tokens (specified by their token ID in the GPT
               tokenizer) to an associated bias value from -100 to 100. You can use this
               [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to
               convert text to token IDs. Mathematically, the bias is added to the logits
@@ -684,6 +711,13 @@ class AsyncCompletions(AsyncAPIResource):
 
               [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
 
+          seed: If specified, our system will make a best effort to sample deterministically,
+              such that repeated requests with the same `seed` and parameters should return
+              the same result.
+
+              Determinism is not guaranteed, and you should refer to the `system_fingerprint`
+              response parameter to monitor changes in the backend.
+
           stop: Up to 4 sequences where the API will stop generating further tokens. The
               returned text will not contain the stop sequence.
 
@@ -751,6 +785,7 @@ class AsyncCompletions(AsyncAPIResource):
         max_tokens: Optional[int] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         presence_penalty: Optional[float] | NotGiven = NOT_GIVEN,
+        seed: Optional[int] | NotGiven = NOT_GIVEN,
         stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN,
         suffix: Optional[str] | NotGiven = NOT_GIVEN,
         temperature: Optional[float] | NotGiven = NOT_GIVEN,
@@ -807,7 +842,7 @@ class AsyncCompletions(AsyncAPIResource):
 
           logit_bias: Modify the likelihood of specified tokens appearing in the completion.
 
-              Accepts a json object that maps tokens (specified by their token ID in the GPT
+              Accepts a JSON object that maps tokens (specified by their token ID in the GPT
               tokenizer) to an associated bias value from -100 to 100. You can use this
               [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to
               convert text to token IDs. Mathematically, the bias is added to the logits
@@ -845,6 +880,13 @@ class AsyncCompletions(AsyncAPIResource):
 
               [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
 
+          seed: If specified, our system will make a best effort to sample deterministically,
+              such that repeated requests with the same `seed` and parameters should return
+              the same result.
+
+              Determinism is not guaranteed, and you should refer to the `system_fingerprint`
+              response parameter to monitor changes in the backend.
+
           stop: Up to 4 sequences where the API will stop generating further tokens. The
               returned text will not contain the stop sequence.
 
@@ -905,6 +947,7 @@ class AsyncCompletions(AsyncAPIResource):
         max_tokens: Optional[int] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         presence_penalty: Optional[float] | NotGiven = NOT_GIVEN,
+        seed: Optional[int] | NotGiven = NOT_GIVEN,
         stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN,
         suffix: Optional[str] | NotGiven = NOT_GIVEN,
         temperature: Optional[float] | NotGiven = NOT_GIVEN,
@@ -961,7 +1004,7 @@ class AsyncCompletions(AsyncAPIResource):
 
           logit_bias: Modify the likelihood of specified tokens appearing in the completion.
 
-              Accepts a json object that maps tokens (specified by their token ID in the GPT
+              Accepts a JSON object that maps tokens (specified by their token ID in the GPT
               tokenizer) to an associated bias value from -100 to 100. You can use this
               [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to
               convert text to token IDs. Mathematically, the bias is added to the logits
@@ -999,6 +1042,13 @@ class AsyncCompletions(AsyncAPIResource):
 
               [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
 
+          seed: If specified, our system will make a best effort to sample deterministically,
+              such that repeated requests with the same `seed` and parameters should return
+              the same result.
+
+              Determinism is not guaranteed, and you should refer to the `system_fingerprint`
+              response parameter to monitor changes in the backend.
+
           stop: Up to 4 sequences where the API will stop generating further tokens. The
               returned text will not contain the stop sequence.
 
@@ -1058,6 +1108,7 @@ class AsyncCompletions(AsyncAPIResource):
         max_tokens: Optional[int] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         presence_penalty: Optional[float] | NotGiven = NOT_GIVEN,
+        seed: Optional[int] | NotGiven = NOT_GIVEN,
         stop: Union[Optional[str], List[str], None] | NotGiven = NOT_GIVEN,
         stream: Optional[Literal[False]] | Literal[True] | NotGiven = NOT_GIVEN,
         suffix: Optional[str] | NotGiven = NOT_GIVEN,
@@ -1085,6 +1136,7 @@ class AsyncCompletions(AsyncAPIResource):
                     "max_tokens": max_tokens,
                     "n": n,
                     "presence_penalty": presence_penalty,
+                    "seed": seed,
                     "stop": stop,
                     "stream": stream,
                     "suffix": suffix,
src/openai/resources/files.py
@@ -4,8 +4,9 @@ from __future__ import annotations
 
 import time
 from typing import TYPE_CHECKING, Mapping, cast
+from typing_extensions import Literal
 
-from ..types import FileObject, FileDeleted, file_create_params
+from ..types import FileObject, FileDeleted, file_list_params, file_create_params
 from .._types import NOT_GIVEN, Body, Query, Headers, NotGiven, FileTypes
 from .._utils import extract_files, maybe_transform, deepcopy_minimal
 from .._resource import SyncAPIResource, AsyncAPIResource
@@ -30,7 +31,7 @@ class Files(SyncAPIResource):
         self,
         *,
         file: FileTypes,
-        purpose: str,
+        purpose: Literal["fine-tune", "assistants"],
         # 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,
@@ -40,22 +41,28 @@ class Files(SyncAPIResource):
     ) -> FileObject:
         """Upload a file that can be used across various endpoints/features.
 
-        Currently, the
-        size of all the files uploaded by one organization can be up to 1 GB. Please
-        [contact us](https://help.openai.com/) if you need to increase the storage
-        limit.
+        The size of
+        all the files uploaded by one organization can be up to 100 GB.
 
-        Args:
-          file: The file object (not file name) to be uploaded.
+        The size of individual files for can be a maximum of 512MB. See the
+        [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) to
+        learn more about the types of files supported. The Fine-tuning API only supports
+        `.jsonl` files.
+
+        Please [contact us](https://help.openai.com/) if you need to increase these
+        storage limits.
 
-              If the `purpose` is set to "fine-tune", the file will be used for fine-tuning.
+        Args:
+          file: The File object (not file name) to be uploaded.
 
           purpose: The intended purpose of the uploaded file.
 
               Use "fine-tune" for
-              [fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). This
-              allows us to validate the format of the uploaded file is correct for
-              fine-tuning.
+              [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning) and
+              "assistants" for
+              [Assistants](https://platform.openai.com/docs/api-reference/assistants) and
+              [Messages](https://platform.openai.com/docs/api-reference/messages). This allows
+              us to validate the format of the uploaded file is correct for fine-tuning.
 
           extra_headers: Send extra headers
 
@@ -122,6 +129,7 @@ class Files(SyncAPIResource):
     def list(
         self,
         *,
+        purpose: 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,
@@ -129,12 +137,29 @@ class Files(SyncAPIResource):
         extra_body: Body | None = None,
         timeout: float | None | NotGiven = NOT_GIVEN,
     ) -> SyncPage[FileObject]:
-        """Returns a list of files that belong to the user's organization."""
+        """
+        Returns a list of files that belong to the user's organization.
+
+        Args:
+          purpose: Only return files with the given 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._get_api_list(
             "/files",
             page=SyncPage[FileObject],
             options=make_request_options(
-                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+                extra_headers=extra_headers,
+                extra_query=extra_query,
+                extra_body=extra_body,
+                timeout=timeout,
+                query=maybe_transform({"purpose": purpose}, file_list_params.FileListParams),
             ),
             model=FileObject,
         )
@@ -237,7 +262,7 @@ class AsyncFiles(AsyncAPIResource):
         self,
         *,
         file: FileTypes,
-        purpose: str,
+        purpose: Literal["fine-tune", "assistants"],
         # 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,
@@ -247,22 +272,28 @@ class AsyncFiles(AsyncAPIResource):
     ) -> FileObject:
         """Upload a file that can be used across various endpoints/features.
 
-        Currently, the
-        size of all the files uploaded by one organization can be up to 1 GB. Please
-        [contact us](https://help.openai.com/) if you need to increase the storage
-        limit.
+        The size of
+        all the files uploaded by one organization can be up to 100 GB.
 
-        Args:
-          file: The file object (not file name) to be uploaded.
+        The size of individual files for can be a maximum of 512MB. See the
+        [Assistants Tools guide](https://platform.openai.com/docs/assistants/tools) to
+        learn more about the types of files supported. The Fine-tuning API only supports
+        `.jsonl` files.
+
+        Please [contact us](https://help.openai.com/) if you need to increase these
+        storage limits.
 
-              If the `purpose` is set to "fine-tune", the file will be used for fine-tuning.
+        Args:
+          file: The File object (not file name) to be uploaded.
 
           purpose: The intended purpose of the uploaded file.
 
               Use "fine-tune" for
-              [fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). This
-              allows us to validate the format of the uploaded file is correct for
-              fine-tuning.
+              [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning) and
+              "assistants" for
+              [Assistants](https://platform.openai.com/docs/api-reference/assistants) and
+              [Messages](https://platform.openai.com/docs/api-reference/messages). This allows
+              us to validate the format of the uploaded file is correct for fine-tuning.
 
           extra_headers: Send extra headers
 
@@ -329,6 +360,7 @@ class AsyncFiles(AsyncAPIResource):
     def list(
         self,
         *,
+        purpose: 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,
@@ -336,12 +368,29 @@ class AsyncFiles(AsyncAPIResource):
         extra_body: Body | None = None,
         timeout: float | None | NotGiven = NOT_GIVEN,
     ) -> AsyncPaginator[FileObject, AsyncPage[FileObject]]:
-        """Returns a list of files that belong to the user's organization."""
+        """
+        Returns a list of files that belong to the user's organization.
+
+        Args:
+          purpose: Only return files with the given 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._get_api_list(
             "/files",
             page=AsyncPage[FileObject],
             options=make_request_options(
-                extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout
+                extra_headers=extra_headers,
+                extra_query=extra_query,
+                extra_body=extra_body,
+                timeout=timeout,
+                query=maybe_transform({"purpose": purpose}, file_list_params.FileListParams),
             ),
             model=FileObject,
         )
src/openai/resources/images.py
@@ -2,7 +2,7 @@
 
 from __future__ import annotations
 
-from typing import TYPE_CHECKING, Mapping, Optional, cast
+from typing import TYPE_CHECKING, Union, Mapping, Optional, cast
 from typing_extensions import Literal
 
 from ..types import (
@@ -34,6 +34,7 @@ class Images(SyncAPIResource):
         self,
         *,
         image: FileTypes,
+        model: Union[str, Literal["dall-e-2"], None] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN,
         size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN,
@@ -52,7 +53,11 @@ class Images(SyncAPIResource):
           image: The image to use as the basis for the variation(s). Must be a valid PNG file,
               less than 4MB, and square.
 
-          n: The number of images to generate. Must be between 1 and 10.
+          model: The model to use for image generation. Only `dall-e-2` is supported at this
+              time.
+
+          n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only
+              `n=1` is supported.
 
           response_format: The format in which the generated images are returned. Must be one of `url` or
               `b64_json`.
@@ -75,6 +80,7 @@ class Images(SyncAPIResource):
         body = deepcopy_minimal(
             {
                 "image": image,
+                "model": model,
                 "n": n,
                 "response_format": response_format,
                 "size": size,
@@ -104,6 +110,7 @@ class Images(SyncAPIResource):
         image: FileTypes,
         prompt: str,
         mask: FileTypes | NotGiven = NOT_GIVEN,
+        model: Union[str, Literal["dall-e-2"], None] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN,
         size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN,
@@ -129,6 +136,9 @@ class Images(SyncAPIResource):
               indicate where `image` should be edited. Must be a valid PNG file, less than
               4MB, and have the same dimensions as `image`.
 
+          model: The model to use for image generation. Only `dall-e-2` is supported at this
+              time.
+
           n: The number of images to generate. Must be between 1 and 10.
 
           response_format: The format in which the generated images are returned. Must be one of `url` or
@@ -154,6 +164,7 @@ class Images(SyncAPIResource):
                 "image": image,
                 "prompt": prompt,
                 "mask": mask,
+                "model": model,
                 "n": n,
                 "response_format": response_format,
                 "size": size,
@@ -181,9 +192,12 @@ class Images(SyncAPIResource):
         self,
         *,
         prompt: str,
+        model: Union[str, Literal["dall-e-2", "dall-e-3"], None] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
+        quality: Literal["standard", "hd"] | NotGiven = NOT_GIVEN,
         response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN,
-        size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN,
+        size: Optional[Literal["256x256", "512x512", "1024x1024", "1792x1024", "1024x1792"]] | NotGiven = NOT_GIVEN,
+        style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN,
         user: 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.
@@ -197,15 +211,28 @@ class Images(SyncAPIResource):
 
         Args:
           prompt: A text description of the desired image(s). The maximum length is 1000
-              characters.
+              characters for `dall-e-2` and 4000 characters for `dall-e-3`.
 
-          n: The number of images to generate. Must be between 1 and 10.
+          model: The model to use for image generation.
+
+          n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only
+              `n=1` is supported.
+
+          quality: The quality of the image that will be generated. `hd` creates images with finer
+              details and greater consistency across the image. This param is only supported
+              for `dall-e-3`.
 
           response_format: The format in which the generated images are returned. Must be one of `url` or
               `b64_json`.
 
           size: The size of the generated images. Must be one of `256x256`, `512x512`, or
-              `1024x1024`.
+              `1024x1024` for `dall-e-2`. Must be one of `1024x1024`, `1792x1024`, or
+              `1024x1792` for `dall-e-3` models.
+
+          style: The style of the generated images. Must be one of `vivid` or `natural`. Vivid
+              causes the model to lean towards generating hyper-real and dramatic images.
+              Natural causes the model to produce more natural, less hyper-real looking
+              images. This param is only supported for `dall-e-3`.
 
           user: A unique identifier representing your end-user, which can help OpenAI to monitor
               and detect abuse.
@@ -224,9 +251,12 @@ class Images(SyncAPIResource):
             body=maybe_transform(
                 {
                     "prompt": prompt,
+                    "model": model,
                     "n": n,
+                    "quality": quality,
                     "response_format": response_format,
                     "size": size,
+                    "style": style,
                     "user": user,
                 },
                 image_generate_params.ImageGenerateParams,
@@ -249,6 +279,7 @@ class AsyncImages(AsyncAPIResource):
         self,
         *,
         image: FileTypes,
+        model: Union[str, Literal["dall-e-2"], None] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN,
         size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN,
@@ -267,7 +298,11 @@ class AsyncImages(AsyncAPIResource):
           image: The image to use as the basis for the variation(s). Must be a valid PNG file,
               less than 4MB, and square.
 
-          n: The number of images to generate. Must be between 1 and 10.
+          model: The model to use for image generation. Only `dall-e-2` is supported at this
+              time.
+
+          n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only
+              `n=1` is supported.
 
           response_format: The format in which the generated images are returned. Must be one of `url` or
               `b64_json`.
@@ -290,6 +325,7 @@ class AsyncImages(AsyncAPIResource):
         body = deepcopy_minimal(
             {
                 "image": image,
+                "model": model,
                 "n": n,
                 "response_format": response_format,
                 "size": size,
@@ -319,6 +355,7 @@ class AsyncImages(AsyncAPIResource):
         image: FileTypes,
         prompt: str,
         mask: FileTypes | NotGiven = NOT_GIVEN,
+        model: Union[str, Literal["dall-e-2"], None] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
         response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN,
         size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN,
@@ -344,6 +381,9 @@ class AsyncImages(AsyncAPIResource):
               indicate where `image` should be edited. Must be a valid PNG file, less than
               4MB, and have the same dimensions as `image`.
 
+          model: The model to use for image generation. Only `dall-e-2` is supported at this
+              time.
+
           n: The number of images to generate. Must be between 1 and 10.
 
           response_format: The format in which the generated images are returned. Must be one of `url` or
@@ -369,6 +409,7 @@ class AsyncImages(AsyncAPIResource):
                 "image": image,
                 "prompt": prompt,
                 "mask": mask,
+                "model": model,
                 "n": n,
                 "response_format": response_format,
                 "size": size,
@@ -396,9 +437,12 @@ class AsyncImages(AsyncAPIResource):
         self,
         *,
         prompt: str,
+        model: Union[str, Literal["dall-e-2", "dall-e-3"], None] | NotGiven = NOT_GIVEN,
         n: Optional[int] | NotGiven = NOT_GIVEN,
+        quality: Literal["standard", "hd"] | NotGiven = NOT_GIVEN,
         response_format: Optional[Literal["url", "b64_json"]] | NotGiven = NOT_GIVEN,
-        size: Optional[Literal["256x256", "512x512", "1024x1024"]] | NotGiven = NOT_GIVEN,
+        size: Optional[Literal["256x256", "512x512", "1024x1024", "1792x1024", "1024x1792"]] | NotGiven = NOT_GIVEN,
+        style: Optional[Literal["vivid", "natural"]] | NotGiven = NOT_GIVEN,
         user: 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.
@@ -412,15 +456,28 @@ class AsyncImages(AsyncAPIResource):
 
         Args:
           prompt: A text description of the desired image(s). The maximum length is 1000
-              characters.
+              characters for `dall-e-2` and 4000 characters for `dall-e-3`.
 
-          n: The number of images to generate. Must be between 1 and 10.
+          model: The model to use for image generation.
+
+          n: The number of images to generate. Must be between 1 and 10. For `dall-e-3`, only
+              `n=1` is supported.
+
+          quality: The quality of the image that will be generated. `hd` creates images with finer
+              details and greater consistency across the image. This param is only supported
+              for `dall-e-3`.
 
           response_format: The format in which the generated images are returned. Must be one of `url` or
               `b64_json`.
 
           size: The size of the generated images. Must be one of `256x256`, `512x512`, or
-              `1024x1024`.
+              `1024x1024` for `dall-e-2`. Must be one of `1024x1024`, `1792x1024`, or
+              `1024x1792` for `dall-e-3` models.
+
+          style: The style of the generated images. Must be one of `vivid` or `natural`. Vivid
+              causes the model to lean towards generating hyper-real and dramatic images.
+              Natural causes the model to produce more natural, less hyper-real looking
+              images. This param is only supported for `dall-e-3`.
 
           user: A unique identifier representing your end-user, which can help OpenAI to monitor
               and detect abuse.
@@ -439,9 +496,12 @@ class AsyncImages(AsyncAPIResource):
             body=maybe_transform(
                 {
                     "prompt": prompt,
+                    "model": model,
                     "n": n,
+                    "quality": quality,
                     "response_format": response_format,
                     "size": size,
+                    "style": style,
                     "user": user,
                 },
                 image_generate_params.ImageGenerateParams,
src/openai/types/audio/__init__.py
@@ -4,6 +4,7 @@ from __future__ import annotations
 
 from .translation import Translation as Translation
 from .transcription import Transcription as Transcription
+from .speech_create_params import SpeechCreateParams as SpeechCreateParams
 from .translation_create_params import (
     TranslationCreateParams as TranslationCreateParams,
 )
src/openai/types/audio/speech_create_params.py
@@ -0,0 +1,34 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import Union
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["SpeechCreateParams"]
+
+
+class SpeechCreateParams(TypedDict, total=False):
+    input: Required[str]
+    """The text to generate audio for. The maximum length is 4096 characters."""
+
+    model: Required[Union[str, Literal["tts-1", "tts-1-hd"]]]
+    """
+    One of the available [TTS models](https://platform.openai.com/docs/models/tts):
+    `tts-1` or `tts-1-hd`
+    """
+
+    voice: Required[Literal["alloy", "echo", "fable", "onyx", "nova", "shimmer"]]
+    """The voice to use when generating the audio.
+
+    Supported voices are `alloy`, `echo`, `fable`, `onyx`, `nova`, and `shimmer`.
+    """
+
+    response_format: Literal["mp3", "opus", "aac", "flac"]
+    """The format to audio in. Supported formats are `mp3`, `opus`, `aac`, and `flac`."""
+
+    speed: float
+    """The speed of the generated audio.
+
+    Select a value from `0.25` to `4.0`. `1.0` is the default.
+    """
src/openai/types/audio/transcription_create_params.py
@@ -38,8 +38,8 @@ class TranscriptionCreateParams(TypedDict, total=False):
 
     response_format: Literal["json", "text", "srt", "verbose_json", "vtt"]
     """
-    The format of the transcript output, in one of these options: json, text, srt,
-    verbose_json, or vtt.
+    The format of the transcript output, in one of these options: `json`, `text`,
+    `srt`, `verbose_json`, or `vtt`.
     """
 
     temperature: float
src/openai/types/audio/translation_create_params.py
@@ -30,8 +30,8 @@ class TranslationCreateParams(TypedDict, total=False):
 
     response_format: str
     """
-    The format of the transcript output, in one of these options: json, text, srt,
-    verbose_json, or vtt.
+    The format of the transcript output, in one of these options: `json`, `text`,
+    `srt`, `verbose_json`, or `vtt`.
     """
 
     temperature: float
src/openai/types/beta/assistants/__init__.py
@@ -0,0 +1,8 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from .assistant_file import AssistantFile as AssistantFile
+from .file_list_params import FileListParams as FileListParams
+from .file_create_params import FileCreateParams as FileCreateParams
+from .file_delete_response import FileDeleteResponse as FileDeleteResponse
src/openai/types/beta/assistants/assistant_file.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+
+__all__ = ["AssistantFile"]
+
+
+class AssistantFile(BaseModel):
+    id: str
+    """The identifier, which can be referenced in API endpoints."""
+
+    assistant_id: str
+    """The assistant ID that the file is attached to."""
+
+    created_at: int
+    """The Unix timestamp (in seconds) for when the assistant file was created."""
+
+    object: Literal["assistant.file"]
+    """The object type, which is always `assistant.file`."""
src/openai/types/beta/assistants/file_create_params.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["FileCreateParams"]
+
+
+class FileCreateParams(TypedDict, total=False):
+    file_id: Required[str]
+    """
+    A [File](https://platform.openai.com/docs/api-reference/files) ID (with
+    `purpose="assistants"`) that the assistant should use. Useful for tools like
+    `retrieval` and `code_interpreter` that can access files.
+    """
src/openai/types/beta/assistants/file_delete_response.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+
+__all__ = ["FileDeleteResponse"]
+
+
+class FileDeleteResponse(BaseModel):
+    id: str
+
+    deleted: bool
+
+    object: Literal["assistant.file.deleted"]
src/openai/types/beta/assistants/file_list_params.py
@@ -0,0 +1,39 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["FileListParams"]
+
+
+class FileListParams(TypedDict, total=False):
+    after: str
+    """A cursor for use in pagination.
+
+    `after` is an object ID that defines your place in the list. For instance, if
+    you make a list request and receive 100 objects, ending with obj_foo, your
+    subsequent call can include after=obj_foo in order to fetch the next page of the
+    list.
+    """
+
+    before: str
+    """A cursor for use in pagination.
+
+    `before` is an object ID that defines your place in the list. For instance, if
+    you make a list request and receive 100 objects, ending with obj_foo, your
+    subsequent call can include before=obj_foo in order to fetch the previous page
+    of the list.
+    """
+
+    limit: int
+    """A limit on the number of objects to be returned.
+
+    Limit can range between 1 and 100, and the default is 20.
+    """
+
+    order: Literal["asc", "desc"]
+    """Sort order by the `created_at` timestamp of the objects.
+
+    `asc` for ascending order and `desc` for descending order.
+    """
src/openai/types/beta/chat/__init__.py
@@ -0,0 +1,3 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
src/openai/types/beta/threads/messages/__init__.py
@@ -0,0 +1,6 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from .message_file import MessageFile as MessageFile
+from .file_list_params import FileListParams as FileListParams
src/openai/types/beta/threads/messages/file_list_params.py
@@ -0,0 +1,41 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["FileListParams"]
+
+
+class FileListParams(TypedDict, total=False):
+    thread_id: Required[str]
+
+    after: str
+    """A cursor for use in pagination.
+
+    `after` is an object ID that defines your place in the list. For instance, if
+    you make a list request and receive 100 objects, ending with obj_foo, your
+    subsequent call can include after=obj_foo in order to fetch the next page of the
+    list.
+    """
+
+    before: str
+    """A cursor for use in pagination.
+
+    `before` is an object ID that defines your place in the list. For instance, if
+    you make a list request and receive 100 objects, ending with obj_foo, your
+    subsequent call can include before=obj_foo in order to fetch the previous page
+    of the list.
+    """
+
+    limit: int
+    """A limit on the number of objects to be returned.
+
+    Limit can range between 1 and 100, and the default is 20.
+    """
+
+    order: Literal["asc", "desc"]
+    """Sort order by the `created_at` timestamp of the objects.
+
+    `asc` for ascending order and `desc` for descending order.
+    """
src/openai/types/beta/threads/messages/message_file.py
@@ -0,0 +1,25 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from typing_extensions import Literal
+
+from ....._models import BaseModel
+
+__all__ = ["MessageFile"]
+
+
+class MessageFile(BaseModel):
+    id: str
+    """The identifier, which can be referenced in API endpoints."""
+
+    created_at: int
+    """The Unix timestamp (in seconds) for when the message file was created."""
+
+    message_id: str
+    """
+    The ID of the [message](https://platform.openai.com/docs/api-reference/messages)
+    that the [File](https://platform.openai.com/docs/api-reference/files) is
+    attached to.
+    """
+
+    object: Literal["thread.message.file"]
+    """The object type, which is always `thread.message.file`."""
src/openai/types/beta/threads/runs/__init__.py
@@ -0,0 +1,13 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from .run_step import RunStep as RunStep
+from .code_tool_call import CodeToolCall as CodeToolCall
+from .step_list_params import StepListParams as StepListParams
+from .function_tool_call import FunctionToolCall as FunctionToolCall
+from .retrieval_tool_call import RetrievalToolCall as RetrievalToolCall
+from .tool_calls_step_details import ToolCallsStepDetails as ToolCallsStepDetails
+from .message_creation_step_details import (
+    MessageCreationStepDetails as MessageCreationStepDetails,
+)
src/openai/types/beta/threads/runs/code_tool_call.py
@@ -0,0 +1,67 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from typing import List, Union
+from typing_extensions import Literal
+
+from ....._models import BaseModel
+
+__all__ = [
+    "CodeToolCall",
+    "CodeInterpreter",
+    "CodeInterpreterOutput",
+    "CodeInterpreterOutputLogs",
+    "CodeInterpreterOutputImage",
+    "CodeInterpreterOutputImageImage",
+]
+
+
+class CodeInterpreterOutputLogs(BaseModel):
+    logs: str
+    """The text output from the Code Interpreter tool call."""
+
+    type: Literal["logs"]
+    """Always `logs`."""
+
+
+class CodeInterpreterOutputImageImage(BaseModel):
+    file_id: str
+    """
+    The [file](https://platform.openai.com/docs/api-reference/files) ID of the
+    image.
+    """
+
+
+class CodeInterpreterOutputImage(BaseModel):
+    image: CodeInterpreterOutputImageImage
+
+    type: Literal["image"]
+    """Always `image`."""
+
+
+CodeInterpreterOutput = Union[CodeInterpreterOutputLogs, CodeInterpreterOutputImage]
+
+
+class CodeInterpreter(BaseModel):
+    input: str
+    """The input to the Code Interpreter tool call."""
+
+    outputs: List[CodeInterpreterOutput]
+    """The outputs from the Code Interpreter tool call.
+
+    Code Interpreter can output one or more items, including text (`logs`) or images
+    (`image`). Each of these are represented by a different object type.
+    """
+
+
+class CodeToolCall(BaseModel):
+    id: str
+    """The ID of the tool call."""
+
+    code_interpreter: CodeInterpreter
+    """The Code Interpreter tool call definition."""
+
+    type: Literal["code_interpreter"]
+    """The type of tool call.
+
+    This is always going to be `code_interpreter` for this type of tool call.
+    """
src/openai/types/beta/threads/runs/function_tool_call.py
@@ -0,0 +1,38 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from typing import Optional
+from typing_extensions import Literal
+
+from ....._models import BaseModel
+
+__all__ = ["FunctionToolCall", "Function"]
+
+
+class Function(BaseModel):
+    arguments: str
+    """The arguments passed to the function."""
+
+    name: str
+    """The name of the function."""
+
+    output: Optional[str]
+    """The output of the function.
+
+    This will be `null` if the outputs have not been
+    [submitted](https://platform.openai.com/docs/api-reference/runs/submitToolOutputs)
+    yet.
+    """
+
+
+class FunctionToolCall(BaseModel):
+    id: str
+    """The ID of the tool call object."""
+
+    function: Function
+    """The definition of the function that was called."""
+
+    type: Literal["function"]
+    """The type of tool call.
+
+    This is always going to be `function` for this type of tool call.
+    """
src/openai/types/beta/threads/runs/message_creation_step_details.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from typing_extensions import Literal
+
+from ....._models import BaseModel
+
+__all__ = ["MessageCreationStepDetails", "MessageCreation"]
+
+
+class MessageCreation(BaseModel):
+    message_id: str
+    """The ID of the message that was created by this run step."""
+
+
+class MessageCreationStepDetails(BaseModel):
+    message_creation: MessageCreation
+
+    type: Literal["message_creation"]
+    """Always `message_creation``."""
src/openai/types/beta/threads/runs/retrieval_tool_call.py
@@ -0,0 +1,21 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from typing_extensions import Literal
+
+from ....._models import BaseModel
+
+__all__ = ["RetrievalToolCall"]
+
+
+class RetrievalToolCall(BaseModel):
+    id: str
+    """The ID of the tool call object."""
+
+    retrieval: object
+    """For now, this is always going to be an empty object."""
+
+    type: Literal["retrieval"]
+    """The type of tool call.
+
+    This is always going to be `retrieval` for this type of tool call.
+    """
src/openai/types/beta/threads/runs/run_step.py
@@ -0,0 +1,93 @@
+# File generated from our OpenAPI spec by Stainless.
+
+import builtins
+from typing import Union, Optional
+from typing_extensions import Literal
+
+from ....._models import BaseModel
+from .tool_calls_step_details import ToolCallsStepDetails
+from .message_creation_step_details import MessageCreationStepDetails
+
+__all__ = ["RunStep", "LastError", "StepDetails"]
+
+
+class LastError(BaseModel):
+    code: Literal["server_error", "rate_limit_exceeded"]
+    """One of `server_error` or `rate_limit_exceeded`."""
+
+    message: str
+    """A human-readable description of the error."""
+
+
+StepDetails = Union[MessageCreationStepDetails, ToolCallsStepDetails]
+
+
+class RunStep(BaseModel):
+    id: str
+    """The identifier of the run step, which can be referenced in API endpoints."""
+
+    assistant_id: str
+    """
+    The ID of the
+    [assistant](https://platform.openai.com/docs/api-reference/assistants)
+    associated with the run step.
+    """
+
+    cancelled_at: Optional[int]
+    """The Unix timestamp (in seconds) for when the run step was cancelled."""
+
+    completed_at: Optional[int]
+    """The Unix timestamp (in seconds) for when the run step completed."""
+
+    created_at: int
+    """The Unix timestamp (in seconds) for when the run step was created."""
+
+    expired_at: Optional[int]
+    """The Unix timestamp (in seconds) for when the run step expired.
+
+    A step is considered expired if the parent run is expired.
+    """
+
+    failed_at: Optional[int]
+    """The Unix timestamp (in seconds) for when the run step failed."""
+
+    last_error: Optional[LastError]
+    """The last error associated with this run step.
+
+    Will be `null` if there are no errors.
+    """
+
+    metadata: Optional[builtins.object]
+    """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.
+    """
+
+    object: Literal["assistant.run.step"]
+    """The object type, which is always `assistant.run.step``."""
+
+    run_id: str
+    """
+    The ID of the [run](https://platform.openai.com/docs/api-reference/runs) that
+    this run step is a part of.
+    """
+
+    status: Literal["in_progress", "cancelled", "failed", "completed", "expired"]
+    """
+    The status of the run, which can be either `in_progress`, `cancelled`, `failed`,
+    `completed`, or `expired`.
+    """
+
+    step_details: StepDetails
+    """The details of the run step."""
+
+    thread_id: str
+    """
+    The ID of the [thread](https://platform.openai.com/docs/api-reference/threads)
+    that was run.
+    """
+
+    type: Literal["message_creation", "tool_calls"]
+    """The type of run step, which can be either `message_creation` or `tool_calls`."""
src/openai/types/beta/threads/runs/step_list_params.py
@@ -0,0 +1,41 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["StepListParams"]
+
+
+class StepListParams(TypedDict, total=False):
+    thread_id: Required[str]
+
+    after: str
+    """A cursor for use in pagination.
+
+    `after` is an object ID that defines your place in the list. For instance, if
+    you make a list request and receive 100 objects, ending with obj_foo, your
+    subsequent call can include after=obj_foo in order to fetch the next page of the
+    list.
+    """
+
+    before: str
+    """A cursor for use in pagination.
+
+    `before` is an object ID that defines your place in the list. For instance, if
+    you make a list request and receive 100 objects, ending with obj_foo, your
+    subsequent call can include before=obj_foo in order to fetch the previous page
+    of the list.
+    """
+
+    limit: int
+    """A limit on the number of objects to be returned.
+
+    Limit can range between 1 and 100, and the default is 20.
+    """
+
+    order: Literal["asc", "desc"]
+    """Sort order by the `created_at` timestamp of the objects.
+
+    `asc` for ascending order and `desc` for descending order.
+    """
src/openai/types/beta/threads/runs/tool_calls_step_details.py
@@ -0,0 +1,25 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from typing import List, Union
+from typing_extensions import Literal
+
+from ....._models import BaseModel
+from .code_tool_call import CodeToolCall
+from .function_tool_call import FunctionToolCall
+from .retrieval_tool_call import RetrievalToolCall
+
+__all__ = ["ToolCallsStepDetails", "ToolCall"]
+
+ToolCall = Union[CodeToolCall, RetrievalToolCall, FunctionToolCall]
+
+
+class ToolCallsStepDetails(BaseModel):
+    tool_calls: List[ToolCall]
+    """An array of tool calls the run step was involved in.
+
+    These can be associated with one of three types of tools: `code_interpreter`,
+    `retrieval`, or `function`.
+    """
+
+    type: Literal["tool_calls"]
+    """Always `tool_calls`."""
src/openai/types/beta/threads/__init__.py
@@ -0,0 +1,22 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from .run import Run as Run
+from .thread_message import ThreadMessage as ThreadMessage
+from .run_list_params import RunListParams as RunListParams
+from .run_create_params import RunCreateParams as RunCreateParams
+from .run_update_params import RunUpdateParams as RunUpdateParams
+from .message_list_params import MessageListParams as MessageListParams
+from .message_content_text import MessageContentText as MessageContentText
+from .message_create_params import MessageCreateParams as MessageCreateParams
+from .message_update_params import MessageUpdateParams as MessageUpdateParams
+from .message_content_image_file import (
+    MessageContentImageFile as MessageContentImageFile,
+)
+from .run_submit_tool_outputs_params import (
+    RunSubmitToolOutputsParams as RunSubmitToolOutputsParams,
+)
+from .required_action_function_tool_call import (
+    RequiredActionFunctionToolCall as RequiredActionFunctionToolCall,
+)
src/openai/types/beta/threads/message_content_image_file.py
@@ -0,0 +1,22 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+
+__all__ = ["MessageContentImageFile", "ImageFile"]
+
+
+class ImageFile(BaseModel):
+    file_id: str
+    """
+    The [File](https://platform.openai.com/docs/api-reference/files) ID of the image
+    in the message content.
+    """
+
+
+class MessageContentImageFile(BaseModel):
+    image_file: ImageFile
+
+    type: Literal["image_file"]
+    """Always `image_file`."""
src/openai/types/beta/threads/message_content_text.py
@@ -0,0 +1,74 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from typing import List, Union
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+
+__all__ = [
+    "MessageContentText",
+    "Text",
+    "TextAnnotation",
+    "TextAnnotationFileCitation",
+    "TextAnnotationFileCitationFileCitation",
+    "TextAnnotationFilePath",
+    "TextAnnotationFilePathFilePath",
+]
+
+
+class TextAnnotationFileCitationFileCitation(BaseModel):
+    file_id: str
+    """The ID of the specific File the citation is from."""
+
+    quote: str
+    """The specific quote in the file."""
+
+
+class TextAnnotationFileCitation(BaseModel):
+    end_index: int
+
+    file_citation: TextAnnotationFileCitationFileCitation
+
+    start_index: int
+
+    text: str
+    """The text in the message content that needs to be replaced."""
+
+    type: Literal["file_citation"]
+    """Always `file_citation`."""
+
+
+class TextAnnotationFilePathFilePath(BaseModel):
+    file_id: str
+    """The ID of the file that was generated."""
+
+
+class TextAnnotationFilePath(BaseModel):
+    end_index: int
+
+    file_path: TextAnnotationFilePathFilePath
+
+    start_index: int
+
+    text: str
+    """The text in the message content that needs to be replaced."""
+
+    type: Literal["file_path"]
+    """Always `file_path`."""
+
+
+TextAnnotation = Union[TextAnnotationFileCitation, TextAnnotationFilePath]
+
+
+class Text(BaseModel):
+    annotations: List[TextAnnotation]
+
+    value: str
+    """The data that makes up the text."""
+
+
+class MessageContentText(BaseModel):
+    text: Text
+
+    type: Literal["text"]
+    """Always `text`."""
src/openai/types/beta/threads/message_create_params.py
@@ -0,0 +1,35 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import List, Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["MessageCreateParams"]
+
+
+class MessageCreateParams(TypedDict, total=False):
+    content: Required[str]
+    """The content of the message."""
+
+    role: Required[Literal["user"]]
+    """The role of the entity that is creating the message.
+
+    Currently only `user` is supported.
+    """
+
+    file_ids: List[str]
+    """
+    A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that
+    the message should use. There can be a maximum of 10 files attached to a
+    message. Useful for tools like `retrieval` and `code_interpreter` that can
+    access and use files.
+    """
+
+    metadata: Optional[object]
+    """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.
+    """
src/openai/types/beta/threads/message_list_params.py
@@ -0,0 +1,39 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["MessageListParams"]
+
+
+class MessageListParams(TypedDict, total=False):
+    after: str
+    """A cursor for use in pagination.
+
+    `after` is an object ID that defines your place in the list. For instance, if
+    you make a list request and receive 100 objects, ending with obj_foo, your
+    subsequent call can include after=obj_foo in order to fetch the next page of the
+    list.
+    """
+
+    before: str
+    """A cursor for use in pagination.
+
+    `before` is an object ID that defines your place in the list. For instance, if
+    you make a list request and receive 100 objects, ending with obj_foo, your
+    subsequent call can include before=obj_foo in order to fetch the previous page
+    of the list.
+    """
+
+    limit: int
+    """A limit on the number of objects to be returned.
+
+    Limit can range between 1 and 100, and the default is 20.
+    """
+
+    order: Literal["asc", "desc"]
+    """Sort order by the `created_at` timestamp of the objects.
+
+    `asc` for ascending order and `desc` for descending order.
+    """
src/openai/types/beta/threads/message_update_params.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Required, TypedDict
+
+__all__ = ["MessageUpdateParams"]
+
+
+class MessageUpdateParams(TypedDict, total=False):
+    thread_id: Required[str]
+
+    metadata: Optional[object]
+    """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.
+    """
src/openai/types/beta/threads/required_action_function_tool_call.py
@@ -0,0 +1,34 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+
+__all__ = ["RequiredActionFunctionToolCall", "Function"]
+
+
+class Function(BaseModel):
+    arguments: str
+    """The arguments that the model expects you to pass to the function."""
+
+    name: str
+    """The name of the function."""
+
+
+class RequiredActionFunctionToolCall(BaseModel):
+    id: str
+    """The ID of the tool call.
+
+    This ID must be referenced when you submit the tool outputs in using the
+    [Submit tool outputs to run](https://platform.openai.com/docs/api-reference/runs/submitToolOutputs)
+    endpoint.
+    """
+
+    function: Function
+    """The function definition."""
+
+    type: Literal["function"]
+    """The type of tool call the output is required for.
+
+    For now, this is always `function`.
+    """
src/openai/types/beta/threads/run.py
@@ -0,0 +1,182 @@
+# File generated from our OpenAPI spec by Stainless.
+
+import builtins
+from typing import Dict, List, Union, Optional
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+from .required_action_function_tool_call import RequiredActionFunctionToolCall
+
+__all__ = [
+    "Run",
+    "LastError",
+    "RequiredAction",
+    "RequiredActionSubmitToolOutputs",
+    "Tool",
+    "ToolAssistantToolsCode",
+    "ToolAssistantToolsRetrieval",
+    "ToolAssistantToolsFunction",
+    "ToolAssistantToolsFunctionFunction",
+]
+
+
+class LastError(BaseModel):
+    code: Literal["server_error", "rate_limit_exceeded"]
+    """One of `server_error` or `rate_limit_exceeded`."""
+
+    message: str
+    """A human-readable description of the error."""
+
+
+class RequiredActionSubmitToolOutputs(BaseModel):
+    tool_calls: List[RequiredActionFunctionToolCall]
+    """A list of the relevant tool calls."""
+
+
+class RequiredAction(BaseModel):
+    submit_tool_outputs: RequiredActionSubmitToolOutputs
+    """Details on the tool outputs needed for this run to continue."""
+
+    type: Literal["submit_tool_outputs"]
+    """For now, this is always `submit_tool_outputs`."""
+
+
+class ToolAssistantToolsCode(BaseModel):
+    type: Literal["code_interpreter"]
+    """The type of tool being defined: `code_interpreter`"""
+
+
+class ToolAssistantToolsRetrieval(BaseModel):
+    type: Literal["retreival"]
+    """The type of tool being defined: `retreival`"""
+
+
+class ToolAssistantToolsFunctionFunction(BaseModel):
+    description: str
+    """
+    A description of what the function does, used by the model to choose when and
+    how to call the function.
+    """
+
+    name: str
+    """The name of the function to be called.
+
+    Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length
+    of 64.
+    """
+
+    parameters: Dict[str, builtins.object]
+    """The parameters the functions accepts, described as a JSON Schema object.
+
+    See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling)
+    for examples, and the
+    [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for
+    documentation about the format.
+
+    To describe a function that accepts no parameters, provide the value
+    `{"type": "object", "properties": {}}`.
+    """
+
+
+class ToolAssistantToolsFunction(BaseModel):
+    function: ToolAssistantToolsFunctionFunction
+    """The function definition."""
+
+    type: Literal["function"]
+    """The type of tool being defined: `function`"""
+
+
+Tool = Union[ToolAssistantToolsCode, ToolAssistantToolsRetrieval, ToolAssistantToolsFunction]
+
+
+class Run(BaseModel):
+    id: str
+    """The identifier, which can be referenced in API endpoints."""
+
+    assistant_id: str
+    """
+    The ID of the
+    [assistant](https://platform.openai.com/docs/api-reference/assistants) used for
+    execution of this run.
+    """
+
+    cancelled_at: Optional[int]
+    """The Unix timestamp (in seconds) for when the run was cancelled."""
+
+    completed_at: Optional[int]
+    """The Unix timestamp (in seconds) for when the run was completed."""
+
+    created_at: int
+    """The Unix timestamp (in seconds) for when the run was created."""
+
+    expires_at: int
+    """The Unix timestamp (in seconds) for when the run will expire."""
+
+    failed_at: Optional[int]
+    """The Unix timestamp (in seconds) for when the run failed."""
+
+    file_ids: List[str]
+    """
+    The list of [File](https://platform.openai.com/docs/api-reference/files) IDs the
+    [assistant](https://platform.openai.com/docs/api-reference/assistants) used for
+    this run.
+    """
+
+    instructions: str
+    """
+    The instructions that the
+    [assistant](https://platform.openai.com/docs/api-reference/assistants) used for
+    this run.
+    """
+
+    last_error: Optional[LastError]
+    """The last error associated with this run. Will be `null` if there are no errors."""
+
+    metadata: Optional[builtins.object]
+    """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.
+    """
+
+    model: str
+    """
+    The model that the
+    [assistant](https://platform.openai.com/docs/api-reference/assistants) used for
+    this run.
+    """
+
+    object: Literal["assistant.run"]
+    """The object type, which is always `assistant.run`."""
+
+    required_action: Optional[RequiredAction]
+    """Details on the action required to continue the run.
+
+    Will be `null` if no action is required.
+    """
+
+    started_at: Optional[int]
+    """The Unix timestamp (in seconds) for when the run was started."""
+
+    status: Literal[
+        "queued", "in_progress", "requires_action", "cancelling", "cancelled", "failed", "completed", "expired"
+    ]
+    """
+    The status of the run, which can be either `queued`, `in_progress`,
+    `requires_action`, `cancelling`, `cancelled`, `failed`, `completed`, or
+    `expired`.
+    """
+
+    thread_id: str
+    """
+    The ID of the [thread](https://platform.openai.com/docs/api-reference/threads)
+    that was executed on as a part of this run.
+    """
+
+    tools: List[Tool]
+    """
+    The list of tools that the
+    [assistant](https://platform.openai.com/docs/api-reference/assistants) used for
+    this run.
+    """
src/openai/types/beta/threads/run_create_params.py
@@ -0,0 +1,100 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import Dict, List, Union, Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = [
+    "RunCreateParams",
+    "Tool",
+    "ToolAssistantToolsCode",
+    "ToolAssistantToolsRetrieval",
+    "ToolAssistantToolsFunction",
+    "ToolAssistantToolsFunctionFunction",
+]
+
+
+class RunCreateParams(TypedDict, total=False):
+    assistant_id: Required[str]
+    """
+    The ID of the
+    [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
+    execute this run.
+    """
+
+    instructions: Optional[str]
+    """Override the default system message of the assistant.
+
+    This is useful for modifying the behavior on a per-run basis.
+    """
+
+    metadata: Optional[object]
+    """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.
+    """
+
+    model: Optional[str]
+    """
+    The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
+    be used to execute this run. If a value is provided here, it will override the
+    model associated with the assistant. If not, the model associated with the
+    assistant will be used.
+    """
+
+    tools: Optional[List[Tool]]
+    """Override the tools the assistant can use for this run.
+
+    This is useful for modifying the behavior on a per-run basis.
+    """
+
+
+class ToolAssistantToolsCode(TypedDict, total=False):
+    type: Required[Literal["code_interpreter"]]
+    """The type of tool being defined: `code_interpreter`"""
+
+
+class ToolAssistantToolsRetrieval(TypedDict, total=False):
+    type: Required[Literal["retreival"]]
+    """The type of tool being defined: `retreival`"""
+
+
+class ToolAssistantToolsFunctionFunction(TypedDict, total=False):
+    description: Required[str]
+    """
+    A description of what the function does, used by the model to choose when and
+    how to call the function.
+    """
+
+    name: Required[str]
+    """The name of the function to be called.
+
+    Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length
+    of 64.
+    """
+
+    parameters: Required[Dict[str, object]]
+    """The parameters the functions accepts, described as a JSON Schema object.
+
+    See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling)
+    for examples, and the
+    [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for
+    documentation about the format.
+
+    To describe a function that accepts no parameters, provide the value
+    `{"type": "object", "properties": {}}`.
+    """
+
+
+class ToolAssistantToolsFunction(TypedDict, total=False):
+    function: Required[ToolAssistantToolsFunctionFunction]
+    """The function definition."""
+
+    type: Required[Literal["function"]]
+    """The type of tool being defined: `function`"""
+
+
+Tool = Union[ToolAssistantToolsCode, ToolAssistantToolsRetrieval, ToolAssistantToolsFunction]
src/openai/types/beta/threads/run_list_params.py
@@ -0,0 +1,39 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["RunListParams"]
+
+
+class RunListParams(TypedDict, total=False):
+    after: str
+    """A cursor for use in pagination.
+
+    `after` is an object ID that defines your place in the list. For instance, if
+    you make a list request and receive 100 objects, ending with obj_foo, your
+    subsequent call can include after=obj_foo in order to fetch the next page of the
+    list.
+    """
+
+    before: str
+    """A cursor for use in pagination.
+
+    `before` is an object ID that defines your place in the list. For instance, if
+    you make a list request and receive 100 objects, ending with obj_foo, your
+    subsequent call can include before=obj_foo in order to fetch the previous page
+    of the list.
+    """
+
+    limit: int
+    """A limit on the number of objects to be returned.
+
+    Limit can range between 1 and 100, and the default is 20.
+    """
+
+    order: Literal["asc", "desc"]
+    """Sort order by the `created_at` timestamp of the objects.
+
+    `asc` for ascending order and `desc` for descending order.
+    """
src/openai/types/beta/threads/run_submit_tool_outputs_params.py
@@ -0,0 +1,26 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import List
+from typing_extensions import Required, TypedDict
+
+__all__ = ["RunSubmitToolOutputsParams", "ToolOutput"]
+
+
+class RunSubmitToolOutputsParams(TypedDict, total=False):
+    thread_id: Required[str]
+
+    tool_outputs: Required[List[ToolOutput]]
+    """A list of tools for which the outputs are being submitted."""
+
+
+class ToolOutput(TypedDict, total=False):
+    output: str
+    """The output of the tool call to be submitted to continue the run."""
+
+    tool_call_id: str
+    """
+    The ID of the tool call in the `required_action` object within the run object
+    the output is being submitted for.
+    """
src/openai/types/beta/threads/run_update_params.py
@@ -0,0 +1,20 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Required, TypedDict
+
+__all__ = ["RunUpdateParams"]
+
+
+class RunUpdateParams(TypedDict, total=False):
+    thread_id: Required[str]
+
+    metadata: Optional[object]
+    """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.
+    """
src/openai/types/beta/threads/thread_message.py
@@ -0,0 +1,65 @@
+# File generated from our OpenAPI spec by Stainless.
+
+import builtins
+from typing import List, Union, Optional
+from typing_extensions import Literal
+
+from ...._models import BaseModel
+from .message_content_text import MessageContentText
+from .message_content_image_file import MessageContentImageFile
+
+__all__ = ["ThreadMessage", "Content"]
+
+Content = Union[MessageContentImageFile, MessageContentText]
+
+
+class ThreadMessage(BaseModel):
+    id: str
+    """The identifier, which can be referenced in API endpoints."""
+
+    assistant_id: Optional[str]
+    """
+    If applicable, the ID of the
+    [assistant](https://platform.openai.com/docs/api-reference/assistants) that
+    authored this message.
+    """
+
+    content: List[Content]
+    """The content of the message in array of text and/or images."""
+
+    created_at: int
+    """The Unix timestamp (in seconds) for when the message was created."""
+
+    file_ids: List[str]
+    """
+    A list of [file](https://platform.openai.com/docs/api-reference/files) IDs that
+    the assistant should use. Useful for tools like retrieval and code_interpreter
+    that can access files. A maximum of 10 files can be attached to a message.
+    """
+
+    metadata: Optional[builtins.object]
+    """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.
+    """
+
+    object: Literal["thread.message"]
+    """The object type, which is always `thread.message`."""
+
+    role: Literal["user", "assistant"]
+    """The entity that produced the message. One of `user` or `assistant`."""
+
+    run_id: Optional[str]
+    """
+    If applicable, the ID of the
+    [run](https://platform.openai.com/docs/api-reference/runs) associated with the
+    authoring of this message.
+    """
+
+    thread_id: str
+    """
+    The [thread](https://platform.openai.com/docs/api-reference/threads) ID that
+    this message belongs to.
+    """
src/openai/types/beta/__init__.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from .thread import Thread as Thread
+from .assistant import Assistant as Assistant
+from .thread_deleted import ThreadDeleted as ThreadDeleted
+from .asssitant_deleted import AsssitantDeleted as AsssitantDeleted
+from .thread_create_params import ThreadCreateParams as ThreadCreateParams
+from .thread_update_params import ThreadUpdateParams as ThreadUpdateParams
+from .assistant_list_params import AssistantListParams as AssistantListParams
+from .assistant_create_params import AssistantCreateParams as AssistantCreateParams
+from .assistant_update_params import AssistantUpdateParams as AssistantUpdateParams
+from .thread_create_and_run_params import (
+    ThreadCreateAndRunParams as ThreadCreateAndRunParams,
+)
src/openai/types/beta/assistant.py
@@ -0,0 +1,112 @@
+# File generated from our OpenAPI spec by Stainless.
+
+import builtins
+from typing import Dict, List, Union, Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["Assistant", "Tool", "ToolCodeInterpreter", "ToolRetreival", "ToolFunction", "ToolFunctionFunction"]
+
+
+class ToolCodeInterpreter(BaseModel):
+    type: Literal["code_interpreter"]
+    """The type of tool being defined: `code_interpreter`"""
+
+
+class ToolRetreival(BaseModel):
+    type: Literal["retreival"]
+    """The type of tool being defined: `retreival`"""
+
+
+class ToolFunctionFunction(BaseModel):
+    description: str
+    """
+    A description of what the function does, used by the model to choose when and
+    how to call the function.
+    """
+
+    name: str
+    """The name of the function to be called.
+
+    Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length
+    of 64.
+    """
+
+    parameters: Dict[str, builtins.object]
+    """The parameters the functions accepts, described as a JSON Schema object.
+
+    See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling)
+    for examples, and the
+    [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for
+    documentation about the format.
+
+    To describe a function that accepts no parameters, provide the value
+    `{"type": "object", "properties": {}}`.
+    """
+
+
+class ToolFunction(BaseModel):
+    function: ToolFunctionFunction
+    """The function definition."""
+
+    type: Literal["function"]
+    """The type of tool being defined: `function`"""
+
+
+Tool = Union[ToolCodeInterpreter, ToolRetreival, ToolFunction]
+
+
+class Assistant(BaseModel):
+    id: str
+    """The identifier, which can be referenced in API endpoints."""
+
+    created_at: int
+    """The Unix timestamp (in seconds) for when the assistant was created."""
+
+    description: Optional[str]
+    """The description of the assistant. The maximum length is 512 characters."""
+
+    file_ids: List[str]
+    """
+    A list of [file](https://platform.openai.com/docs/api-reference/files) IDs
+    attached to this assistant. There can be a maximum of 20 files attached to the
+    assistant. Files are ordered by their creation date in ascending order.
+    """
+
+    instructions: Optional[str]
+    """The system instructions that the assistant uses.
+
+    The maximum length is 32768 characters.
+    """
+
+    metadata: Optional[builtins.object]
+    """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.
+    """
+
+    model: str
+    """ID of the model to use.
+
+    You can use the
+    [List models](https://platform.openai.com/docs/api-reference/models/list) API to
+    see all of your available models, or see our
+    [Model overview](https://platform.openai.com/docs/models/overview) for
+    descriptions of them.
+    """
+
+    name: Optional[str]
+    """The name of the assistant. The maximum length is 256 characters."""
+
+    object: Literal["assistant"]
+    """The object type, which is always `assistant`."""
+
+    tools: List[Tool]
+    """A list of tool enabled on the assistant.
+
+    There can be a maximum of 128 tools per assistant. Tools can be of types
+    `code_interpreter`, `retrieval`, or `function`.
+    """
src/openai/types/beta/assistant_create_params.py
@@ -0,0 +1,109 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import Dict, List, Union, Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = [
+    "AssistantCreateParams",
+    "Tool",
+    "ToolAssistantToolsCode",
+    "ToolAssistantToolsRetrieval",
+    "ToolAssistantToolsFunction",
+    "ToolAssistantToolsFunctionFunction",
+]
+
+
+class AssistantCreateParams(TypedDict, total=False):
+    model: Required[str]
+    """ID of the model to use.
+
+    You can use the
+    [List models](https://platform.openai.com/docs/api-reference/models/list) API to
+    see all of your available models, or see our
+    [Model overview](https://platform.openai.com/docs/models/overview) for
+    descriptions of them.
+    """
+
+    description: Optional[str]
+    """The description of the assistant. The maximum length is 512 characters."""
+
+    file_ids: List[str]
+    """
+    A list of [file](https://platform.openai.com/docs/api-reference/files) IDs
+    attached to this assistant. There can be a maximum of 20 files attached to the
+    assistant. Files are ordered by their creation date in ascending order.
+    """
+
+    instructions: Optional[str]
+    """The system instructions that the assistant uses.
+
+    The maximum length is 32768 characters.
+    """
+
+    metadata: Optional[object]
+    """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.
+    """
+
+    name: Optional[str]
+    """The name of the assistant. The maximum length is 256 characters."""
+
+    tools: List[Tool]
+    """A list of tool enabled on the assistant.
+
+    There can be a maximum of 128 tools per assistant. Tools can be of types
+    `code_interpreter`, `retrieval`, or `function`.
+    """
+
+
+class ToolAssistantToolsCode(TypedDict, total=False):
+    type: Required[Literal["code_interpreter"]]
+    """The type of tool being defined: `code_interpreter`"""
+
+
+class ToolAssistantToolsRetrieval(TypedDict, total=False):
+    type: Required[Literal["retreival"]]
+    """The type of tool being defined: `retreival`"""
+
+
+class ToolAssistantToolsFunctionFunction(TypedDict, total=False):
+    description: Required[str]
+    """
+    A description of what the function does, used by the model to choose when and
+    how to call the function.
+    """
+
+    name: Required[str]
+    """The name of the function to be called.
+
+    Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length
+    of 64.
+    """
+
+    parameters: Required[Dict[str, object]]
+    """The parameters the functions accepts, described as a JSON Schema object.
+
+    See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling)
+    for examples, and the
+    [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for
+    documentation about the format.
+
+    To describe a function that accepts no parameters, provide the value
+    `{"type": "object", "properties": {}}`.
+    """
+
+
+class ToolAssistantToolsFunction(TypedDict, total=False):
+    function: Required[ToolAssistantToolsFunctionFunction]
+    """The function definition."""
+
+    type: Required[Literal["function"]]
+    """The type of tool being defined: `function`"""
+
+
+Tool = Union[ToolAssistantToolsCode, ToolAssistantToolsRetrieval, ToolAssistantToolsFunction]
src/openai/types/beta/assistant_list_params.py
@@ -0,0 +1,39 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, TypedDict
+
+__all__ = ["AssistantListParams"]
+
+
+class AssistantListParams(TypedDict, total=False):
+    after: str
+    """A cursor for use in pagination.
+
+    `after` is an object ID that defines your place in the list. For instance, if
+    you make a list request and receive 100 objects, ending with obj_foo, your
+    subsequent call can include after=obj_foo in order to fetch the next page of the
+    list.
+    """
+
+    before: str
+    """A cursor for use in pagination.
+
+    `before` is an object ID that defines your place in the list. For instance, if
+    you make a list request and receive 100 objects, ending with obj_foo, your
+    subsequent call can include before=obj_foo in order to fetch the previous page
+    of the list.
+    """
+
+    limit: int
+    """A limit on the number of objects to be returned.
+
+    Limit can range between 1 and 100, and the default is 20.
+    """
+
+    order: Literal["asc", "desc"]
+    """Sort order by the `created_at` timestamp of the objects.
+
+    `asc` for ascending order and `desc` for descending order.
+    """
src/openai/types/beta/assistant_update_params.py
@@ -0,0 +1,111 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import Dict, List, Union, Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = [
+    "AssistantUpdateParams",
+    "Tool",
+    "ToolAssistantToolsCode",
+    "ToolAssistantToolsRetrieval",
+    "ToolAssistantToolsFunction",
+    "ToolAssistantToolsFunctionFunction",
+]
+
+
+class AssistantUpdateParams(TypedDict, total=False):
+    description: Optional[str]
+    """The description of the assistant. The maximum length is 512 characters."""
+
+    file_ids: List[str]
+    """
+    A list of [File](https://platform.openai.com/docs/api-reference/files) IDs
+    attached to this assistant. There can be a maximum of 20 files attached to the
+    assistant. Files are ordered by their creation date in ascending order. If a
+    file was previosuly attached to the list but does not show up in the list, it
+    will be deleted from the assistant.
+    """
+
+    instructions: Optional[str]
+    """The system instructions that the assistant uses.
+
+    The maximum length is 32768 characters.
+    """
+
+    metadata: Optional[object]
+    """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.
+    """
+
+    model: str
+    """ID of the model to use.
+
+    You can use the
+    [List models](https://platform.openai.com/docs/api-reference/models/list) API to
+    see all of your available models, or see our
+    [Model overview](https://platform.openai.com/docs/models/overview) for
+    descriptions of them.
+    """
+
+    name: Optional[str]
+    """The name of the assistant. The maximum length is 256 characters."""
+
+    tools: List[Tool]
+    """A list of tool enabled on the assistant.
+
+    There can be a maximum of 128 tools per assistant. Tools can be of types
+    `code_interpreter`, `retrieval`, or `function`.
+    """
+
+
+class ToolAssistantToolsCode(TypedDict, total=False):
+    type: Required[Literal["code_interpreter"]]
+    """The type of tool being defined: `code_interpreter`"""
+
+
+class ToolAssistantToolsRetrieval(TypedDict, total=False):
+    type: Required[Literal["retreival"]]
+    """The type of tool being defined: `retreival`"""
+
+
+class ToolAssistantToolsFunctionFunction(TypedDict, total=False):
+    description: Required[str]
+    """
+    A description of what the function does, used by the model to choose when and
+    how to call the function.
+    """
+
+    name: Required[str]
+    """The name of the function to be called.
+
+    Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length
+    of 64.
+    """
+
+    parameters: Required[Dict[str, object]]
+    """The parameters the functions accepts, described as a JSON Schema object.
+
+    See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling)
+    for examples, and the
+    [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for
+    documentation about the format.
+
+    To describe a function that accepts no parameters, provide the value
+    `{"type": "object", "properties": {}}`.
+    """
+
+
+class ToolAssistantToolsFunction(TypedDict, total=False):
+    function: Required[ToolAssistantToolsFunctionFunction]
+    """The function definition."""
+
+    type: Required[Literal["function"]]
+    """The type of tool being defined: `function`"""
+
+
+Tool = Union[ToolAssistantToolsCode, ToolAssistantToolsRetrieval, ToolAssistantToolsFunction]
src/openai/types/beta/asssitant_deleted.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["AsssitantDeleted"]
+
+
+class AsssitantDeleted(BaseModel):
+    id: str
+
+    deleted: bool
+
+    object: Literal["assistant.deleted"]
src/openai/types/beta/thread.py
@@ -0,0 +1,28 @@
+# File generated from our OpenAPI spec by Stainless.
+
+import builtins
+from typing import Optional
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["Thread"]
+
+
+class Thread(BaseModel):
+    id: str
+    """The identifier, which can be referenced in API endpoints."""
+
+    created_at: int
+    """The Unix timestamp (in seconds) for when the thread was created."""
+
+    metadata: Optional[builtins.object]
+    """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.
+    """
+
+    object: Literal["thread"]
+    """The object type, which is always `thread`."""
src/openai/types/beta/thread_create_and_run_params.py
@@ -0,0 +1,148 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import Dict, List, Union, Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = [
+    "ThreadCreateAndRunParams",
+    "Thread",
+    "ThreadMessage",
+    "Tool",
+    "ToolAssistantToolsCode",
+    "ToolAssistantToolsRetrieval",
+    "ToolAssistantToolsFunction",
+    "ToolAssistantToolsFunctionFunction",
+]
+
+
+class ThreadCreateAndRunParams(TypedDict, total=False):
+    assistant_id: Required[str]
+    """
+    The ID of the
+    [assistant](https://platform.openai.com/docs/api-reference/assistants) to use to
+    execute this run.
+    """
+
+    instructions: Optional[str]
+    """Override the default system message of the assistant.
+
+    This is useful for modifying the behavior on a per-run basis.
+    """
+
+    metadata: Optional[object]
+    """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.
+    """
+
+    model: Optional[str]
+    """
+    The ID of the [Model](https://platform.openai.com/docs/api-reference/models) to
+    be used to execute this run. If a value is provided here, it will override the
+    model associated with the assistant. If not, the model associated with the
+    assistant will be used.
+    """
+
+    thread: Thread
+    """If no thread is provided, an empty thread will be created."""
+
+    tools: Optional[List[Tool]]
+    """Override the tools the assistant can use for this run.
+
+    This is useful for modifying the behavior on a per-run basis.
+    """
+
+
+class ThreadMessage(TypedDict, total=False):
+    content: Required[str]
+    """The content of the message."""
+
+    role: Required[Literal["user"]]
+    """The role of the entity that is creating the message.
+
+    Currently only `user` is supported.
+    """
+
+    file_ids: List[str]
+    """
+    A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that
+    the message should use. There can be a maximum of 10 files attached to a
+    message. Useful for tools like `retrieval` and `code_interpreter` that can
+    access and use files.
+    """
+
+    metadata: Optional[object]
+    """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.
+    """
+
+
+class Thread(TypedDict, total=False):
+    messages: List[ThreadMessage]
+    """
+    A list of [messages](https://platform.openai.com/docs/api-reference/messages) to
+    start the thread with.
+    """
+
+    metadata: Optional[object]
+    """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.
+    """
+
+
+class ToolAssistantToolsCode(TypedDict, total=False):
+    type: Required[Literal["code_interpreter"]]
+    """The type of tool being defined: `code_interpreter`"""
+
+
+class ToolAssistantToolsRetrieval(TypedDict, total=False):
+    type: Required[Literal["retreival"]]
+    """The type of tool being defined: `retreival`"""
+
+
+class ToolAssistantToolsFunctionFunction(TypedDict, total=False):
+    description: Required[str]
+    """
+    A description of what the function does, used by the model to choose when and
+    how to call the function.
+    """
+
+    name: Required[str]
+    """The name of the function to be called.
+
+    Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length
+    of 64.
+    """
+
+    parameters: Required[Dict[str, object]]
+    """The parameters the functions accepts, described as a JSON Schema object.
+
+    See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling)
+    for examples, and the
+    [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for
+    documentation about the format.
+
+    To describe a function that accepts no parameters, provide the value
+    `{"type": "object", "properties": {}}`.
+    """
+
+
+class ToolAssistantToolsFunction(TypedDict, total=False):
+    function: Required[ToolAssistantToolsFunctionFunction]
+    """The function definition."""
+
+    type: Required[Literal["function"]]
+    """The type of tool being defined: `function`"""
+
+
+Tool = Union[ToolAssistantToolsCode, ToolAssistantToolsRetrieval, ToolAssistantToolsFunction]
src/openai/types/beta/thread_create_params.py
@@ -0,0 +1,51 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import List, Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ThreadCreateParams", "Message"]
+
+
+class ThreadCreateParams(TypedDict, total=False):
+    messages: List[Message]
+    """
+    A list of [messages](https://platform.openai.com/docs/api-reference/messages) to
+    start the thread with.
+    """
+
+    metadata: Optional[object]
+    """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.
+    """
+
+
+class Message(TypedDict, total=False):
+    content: Required[str]
+    """The content of the message."""
+
+    role: Required[Literal["user"]]
+    """The role of the entity that is creating the message.
+
+    Currently only `user` is supported.
+    """
+
+    file_ids: List[str]
+    """
+    A list of [File](https://platform.openai.com/docs/api-reference/files) IDs that
+    the message should use. There can be a maximum of 10 files attached to a
+    message. Useful for tools like `retrieval` and `code_interpreter` that can
+    access and use files.
+    """
+
+    metadata: Optional[object]
+    """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.
+    """
src/openai/types/beta/thread_deleted.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["ThreadDeleted"]
+
+
+class ThreadDeleted(BaseModel):
+    id: str
+
+    deleted: bool
+
+    object: Literal["thread.deleted"]
src/openai/types/beta/thread_update_params.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import TypedDict
+
+__all__ = ["ThreadUpdateParams"]
+
+
+class ThreadUpdateParams(TypedDict, total=False):
+    metadata: Optional[object]
+    """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.
+    """
src/openai/types/chat/__init__.py
@@ -7,6 +7,48 @@ from .chat_completion_role import ChatCompletionRole as ChatCompletionRole
 from .chat_completion_chunk import ChatCompletionChunk as ChatCompletionChunk
 from .chat_completion_message import ChatCompletionMessage as ChatCompletionMessage
 from .completion_create_params import CompletionCreateParams as CompletionCreateParams
+from .chat_completion_tool_param import (
+    ChatCompletionToolParam as ChatCompletionToolParam,
+)
 from .chat_completion_message_param import (
     ChatCompletionMessageParam as ChatCompletionMessageParam,
 )
+from .chat_completion_message_tool_call import (
+    ChatCompletionMessageToolCall as ChatCompletionMessageToolCall,
+)
+from .chat_completion_content_part_param import (
+    ChatCompletionContentPartParam as ChatCompletionContentPartParam,
+)
+from .chat_completion_tool_message_param import (
+    ChatCompletionToolMessageParam as ChatCompletionToolMessageParam,
+)
+from .chat_completion_user_message_param import (
+    ChatCompletionUserMessageParam as ChatCompletionUserMessageParam,
+)
+from .chat_completion_system_message_param import (
+    ChatCompletionSystemMessageParam as ChatCompletionSystemMessageParam,
+)
+from .chat_completion_function_message_param import (
+    ChatCompletionFunctionMessageParam as ChatCompletionFunctionMessageParam,
+)
+from .chat_completion_assistant_message_param import (
+    ChatCompletionAssistantMessageParam as ChatCompletionAssistantMessageParam,
+)
+from .chat_completion_content_part_text_param import (
+    ChatCompletionContentPartTextParam as ChatCompletionContentPartTextParam,
+)
+from .chat_completion_message_tool_call_param import (
+    ChatCompletionMessageToolCallParam as ChatCompletionMessageToolCallParam,
+)
+from .chat_completion_named_tool_choice_param import (
+    ChatCompletionNamedToolChoiceParam as ChatCompletionNamedToolChoiceParam,
+)
+from .chat_completion_content_part_image_param import (
+    ChatCompletionContentPartImageParam as ChatCompletionContentPartImageParam,
+)
+from .chat_completion_tool_choice_option_param import (
+    ChatCompletionToolChoiceOptionParam as ChatCompletionToolChoiceOptionParam,
+)
+from .chat_completion_function_call_option_param import (
+    ChatCompletionFunctionCallOptionParam as ChatCompletionFunctionCallOptionParam,
+)
src/openai/types/chat/chat_completion.py
@@ -11,13 +11,14 @@ __all__ = ["ChatCompletion", "Choice"]
 
 
 class Choice(BaseModel):
-    finish_reason: Literal["stop", "length", "function_call", "content_filter"]
+    finish_reason: Literal["stop", "length", "tool_calls", "content_filter", "function_call"]
     """The reason the model stopped generating tokens.
 
     This will be `stop` if the model hit a natural stop point or a provided stop
     sequence, `length` if the maximum number of tokens specified in the request was
     reached, `content_filter` if content was omitted due to a flag from our content
-    filters, or `function_call` if the model called a function.
+    filters, `tool_calls` if the model called a tool, or `function_call`
+    (deprecated) if the model called a function.
     """
 
     index: int
@@ -43,8 +44,15 @@ class ChatCompletion(BaseModel):
     model: str
     """The model used for the chat completion."""
 
-    object: str
+    object: Literal["chat.completion"]
     """The object type, which is always `chat.completion`."""
 
+    system_fingerprint: Optional[str] = None
+    """This fingerprint represents the backend configuration that the model runs with.
+
+    Can be used in conjunction with the `seed` request parameter to understand when
+    backend changes have been made that might impact determinism.
+    """
+
     usage: Optional[CompletionUsage] = None
     """Usage statistics for the completion request."""
src/openai/types/chat/chat_completion_assistant_message_param.py
@@ -0,0 +1,41 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import List, Optional
+from typing_extensions import Literal, Required, TypedDict
+
+from .chat_completion_message_tool_call_param import ChatCompletionMessageToolCallParam
+
+__all__ = ["ChatCompletionAssistantMessageParam", "FunctionCall"]
+
+
+class FunctionCall(TypedDict, total=False):
+    arguments: Required[str]
+    """
+    The arguments to call the function with, as generated by the model in JSON
+    format. Note that the model does not always generate valid JSON, and may
+    hallucinate parameters not defined by your function schema. Validate the
+    arguments in your code before calling your function.
+    """
+
+    name: Required[str]
+    """The name of the function to call."""
+
+
+class ChatCompletionAssistantMessageParam(TypedDict, total=False):
+    content: Required[Optional[str]]
+    """The contents of the assistant message."""
+
+    role: Required[Literal["assistant"]]
+    """The role of the messages author, in this case `assistant`."""
+
+    function_call: FunctionCall
+    """Deprecated and replaced by `tool_calls`.
+
+    The name and arguments of a function that should be called, as generated by the
+    model.
+    """
+
+    tool_calls: List[ChatCompletionMessageToolCallParam]
+    """The tool calls generated by the model, such as function calls."""
src/openai/types/chat/chat_completion_chunk.py
@@ -4,9 +4,15 @@ from typing import List, Optional
 from typing_extensions import Literal
 
 from ..._models import BaseModel
-from .chat_completion_role import ChatCompletionRole
 
-__all__ = ["ChatCompletionChunk", "Choice", "ChoiceDelta", "ChoiceDeltaFunctionCall"]
+__all__ = [
+    "ChatCompletionChunk",
+    "Choice",
+    "ChoiceDelta",
+    "ChoiceDeltaFunctionCall",
+    "ChoiceDeltaToolCall",
+    "ChoiceDeltaToolCallFunction",
+]
 
 
 class ChoiceDeltaFunctionCall(BaseModel):
@@ -22,31 +28,60 @@ class ChoiceDeltaFunctionCall(BaseModel):
     """The name of the function to call."""
 
 
+class ChoiceDeltaToolCallFunction(BaseModel):
+    arguments: Optional[str] = None
+    """
+    The arguments to call the function with, as generated by the model in JSON
+    format. Note that the model does not always generate valid JSON, and may
+    hallucinate parameters not defined by your function schema. Validate the
+    arguments in your code before calling your function.
+    """
+
+    name: Optional[str] = None
+    """The name of the function to call."""
+
+
+class ChoiceDeltaToolCall(BaseModel):
+    index: int
+
+    id: Optional[str] = None
+    """The ID of the tool call."""
+
+    function: Optional[ChoiceDeltaToolCallFunction] = None
+
+    type: Optional[Literal["function"]] = None
+    """The type of the tool. Currently, only `function` is supported."""
+
+
 class ChoiceDelta(BaseModel):
     content: Optional[str] = None
     """The contents of the chunk message."""
 
     function_call: Optional[ChoiceDeltaFunctionCall] = None
-    """
+    """Deprecated and replaced by `tool_calls`.
+
     The name and arguments of a function that should be called, as generated by the
     model.
     """
 
-    role: Optional[ChatCompletionRole] = None
+    role: Optional[Literal["system", "user", "assistant", "tool"]] = None
     """The role of the author of this message."""
 
+    tool_calls: Optional[List[ChoiceDeltaToolCall]] = None
+
 
 class Choice(BaseModel):
     delta: ChoiceDelta
     """A chat completion delta generated by streamed model responses."""
 
-    finish_reason: Optional[Literal["stop", "length", "function_call", "content_filter"]]
+    finish_reason: Optional[Literal["stop", "length", "tool_calls", "content_filter", "function_call"]]
     """The reason the model stopped generating tokens.
 
     This will be `stop` if the model hit a natural stop point or a provided stop
     sequence, `length` if the maximum number of tokens specified in the request was
     reached, `content_filter` if content was omitted due to a flag from our content
-    filters, or `function_call` if the model called a function.
+    filters, `tool_calls` if the model called a tool, or `function_call`
+    (deprecated) if the model called a function.
     """
 
     index: int
@@ -72,5 +107,5 @@ class ChatCompletionChunk(BaseModel):
     model: str
     """The model to generate the completion."""
 
-    object: str
+    object: Literal["chat.completion.chunk"]
     """The object type, which is always `chat.completion.chunk`."""
src/openai/types/chat/chat_completion_content_part_image_param.py
@@ -0,0 +1,22 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ChatCompletionContentPartImageParam", "ImageURL"]
+
+
+class ImageURL(TypedDict, total=False):
+    detail: Literal["auto", "low", "high"]
+    """Specifies the detail level of the image."""
+
+    url: str
+    """Either a URL of the image or the base64 encoded image data."""
+
+
+class ChatCompletionContentPartImageParam(TypedDict, total=False):
+    image_url: Required[ImageURL]
+
+    type: Required[Literal["image_url"]]
+    """The type of the content part."""
src/openai/types/chat/chat_completion_content_part_param.py
@@ -0,0 +1,14 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import Union
+
+from .chat_completion_content_part_text_param import ChatCompletionContentPartTextParam
+from .chat_completion_content_part_image_param import (
+    ChatCompletionContentPartImageParam,
+)
+
+__all__ = ["ChatCompletionContentPartParam"]
+
+ChatCompletionContentPartParam = Union[ChatCompletionContentPartTextParam, ChatCompletionContentPartImageParam]
src/openai/types/chat/chat_completion_content_part_text_param.py
@@ -0,0 +1,15 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ChatCompletionContentPartTextParam"]
+
+
+class ChatCompletionContentPartTextParam(TypedDict, total=False):
+    text: Required[str]
+    """The text content."""
+
+    type: Required[Literal["text"]]
+    """The type of the content part."""
src/openai/types/chat/chat_completion_function_call_option_param.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing_extensions import Required, TypedDict
+
+__all__ = ["ChatCompletionFunctionCallOptionParam"]
+
+
+class ChatCompletionFunctionCallOptionParam(TypedDict, total=False):
+    name: Required[str]
+    """The name of the function to call."""
src/openai/types/chat/chat_completion_function_message_param.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ChatCompletionFunctionMessageParam"]
+
+
+class ChatCompletionFunctionMessageParam(TypedDict, total=False):
+    content: Required[Optional[str]]
+    """The return value from the function call, to return to the model."""
+
+    name: Required[str]
+    """The name of the function to call."""
+
+    role: Required[Literal["function"]]
+    """The role of the messages author, in this case `function`."""
src/openai/types/chat/chat_completion_message.py
@@ -1,9 +1,10 @@
 # File generated from our OpenAPI spec by Stainless.
 
-from typing import Optional
+from typing import List, Optional
+from typing_extensions import Literal
 
 from ..._models import BaseModel
-from .chat_completion_role import ChatCompletionRole
+from .chat_completion_message_tool_call import ChatCompletionMessageToolCall
 
 __all__ = ["ChatCompletionMessage", "FunctionCall"]
 
@@ -25,11 +26,15 @@ class ChatCompletionMessage(BaseModel):
     content: Optional[str]
     """The contents of the message."""
 
-    role: ChatCompletionRole
+    role: Literal["assistant"]
     """The role of the author of this message."""
 
     function_call: Optional[FunctionCall] = None
-    """
+    """Deprecated and replaced by `tool_calls`.
+
     The name and arguments of a function that should be called, as generated by the
     model.
     """
+
+    tool_calls: Optional[List[ChatCompletionMessageToolCall]] = None
+    """The tool calls generated by the model, such as function calls."""
src/openai/types/chat/chat_completion_message_param.py
@@ -2,49 +2,20 @@
 
 from __future__ import annotations
 
-from typing import Optional
-from typing_extensions import Literal, Required, TypedDict
-
-__all__ = ["ChatCompletionMessageParam", "FunctionCall"]
-
-
-class FunctionCall(TypedDict, total=False):
-    arguments: Required[str]
-    """
-    The arguments to call the function with, as generated by the model in JSON
-    format. Note that the model does not always generate valid JSON, and may
-    hallucinate parameters not defined by your function schema. Validate the
-    arguments in your code before calling your function.
-    """
-
-    name: Required[str]
-    """The name of the function to call."""
-
-
-class ChatCompletionMessageParam(TypedDict, total=False):
-    content: Required[Optional[str]]
-    """The contents of the message.
-
-    `content` is required for all messages, and may be null for assistant messages
-    with function calls.
-    """
-
-    role: Required[Literal["system", "user", "assistant", "function"]]
-    """The role of the messages author.
-
-    One of `system`, `user`, `assistant`, or `function`.
-    """
-
-    function_call: FunctionCall
-    """
-    The name and arguments of a function that should be called, as generated by the
-    model.
-    """
-
-    name: str
-    """The name of the author of this message.
-
-    `name` is required if role is `function`, and it should be the name of the
-    function whose response is in the `content`. May contain a-z, A-Z, 0-9, and
-    underscores, with a maximum length of 64 characters.
-    """
+from typing import Union
+
+from .chat_completion_tool_message_param import ChatCompletionToolMessageParam
+from .chat_completion_user_message_param import ChatCompletionUserMessageParam
+from .chat_completion_system_message_param import ChatCompletionSystemMessageParam
+from .chat_completion_function_message_param import ChatCompletionFunctionMessageParam
+from .chat_completion_assistant_message_param import ChatCompletionAssistantMessageParam
+
+__all__ = ["ChatCompletionMessageParam"]
+
+ChatCompletionMessageParam = Union[
+    ChatCompletionSystemMessageParam,
+    ChatCompletionUserMessageParam,
+    ChatCompletionAssistantMessageParam,
+    ChatCompletionToolMessageParam,
+    ChatCompletionFunctionMessageParam,
+]
src/openai/types/chat/chat_completion_message_tool_call.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from typing_extensions import Literal
+
+from ..._models import BaseModel
+
+__all__ = ["ChatCompletionMessageToolCall", "Function"]
+
+
+class Function(BaseModel):
+    arguments: str
+    """
+    The arguments to call the function with, as generated by the model in JSON
+    format. Note that the model does not always generate valid JSON, and may
+    hallucinate parameters not defined by your function schema. Validate the
+    arguments in your code before calling your function.
+    """
+
+    name: str
+    """The name of the function to call."""
+
+
+class ChatCompletionMessageToolCall(BaseModel):
+    id: str
+    """The ID of the tool call."""
+
+    function: Function
+    """The function that the model called."""
+
+    type: Literal["function"]
+    """The type of the tool. Currently, only `function` is supported."""
src/openai/types/chat/chat_completion_message_tool_call_param.py
@@ -0,0 +1,31 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ChatCompletionMessageToolCallParam", "Function"]
+
+
+class Function(TypedDict, total=False):
+    arguments: Required[str]
+    """
+    The arguments to call the function with, as generated by the model in JSON
+    format. Note that the model does not always generate valid JSON, and may
+    hallucinate parameters not defined by your function schema. Validate the
+    arguments in your code before calling your function.
+    """
+
+    name: Required[str]
+    """The name of the function to call."""
+
+
+class ChatCompletionMessageToolCallParam(TypedDict, total=False):
+    id: Required[str]
+    """The ID of the tool call."""
+
+    function: Required[Function]
+    """The function that the model called."""
+
+    type: Required[Literal["function"]]
+    """The type of the tool. Currently, only `function` is supported."""
src/openai/types/chat/chat_completion_named_tool_choice_param.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ChatCompletionNamedToolChoiceParam", "Function"]
+
+
+class Function(TypedDict, total=False):
+    name: Required[str]
+    """The name of the function to call."""
+
+
+class ChatCompletionNamedToolChoiceParam(TypedDict, total=False):
+    function: Function
+
+    type: Literal["function"]
+    """The type of the tool. Currently, only `function` is supported."""
src/openai/types/chat/chat_completion_role.py
@@ -4,4 +4,4 @@ from typing_extensions import Literal
 
 __all__ = ["ChatCompletionRole"]
 
-ChatCompletionRole = Literal["system", "user", "assistant", "function"]
+ChatCompletionRole = Literal["system", "user", "assistant", "tool", "function"]
src/openai/types/chat/chat_completion_system_message_param.py
@@ -0,0 +1,16 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ChatCompletionSystemMessageParam"]
+
+
+class ChatCompletionSystemMessageParam(TypedDict, total=False):
+    content: Required[Optional[str]]
+    """The contents of the system message."""
+
+    role: Required[Literal["system"]]
+    """The role of the messages author, in this case `system`."""
src/openai/types/chat/chat_completion_tool_choice_option_param.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import Union
+from typing_extensions import Literal
+
+from .chat_completion_named_tool_choice_param import ChatCompletionNamedToolChoiceParam
+
+__all__ = ["ChatCompletionToolChoiceOptionParam"]
+
+ChatCompletionToolChoiceOptionParam = Union[Literal["none", "auto"], ChatCompletionNamedToolChoiceParam]
src/openai/types/chat/chat_completion_tool_message_param.py
@@ -0,0 +1,19 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import Optional
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ChatCompletionToolMessageParam"]
+
+
+class ChatCompletionToolMessageParam(TypedDict, total=False):
+    content: Required[Optional[str]]
+    """The contents of the tool message."""
+
+    role: Required[Literal["tool"]]
+    """The role of the messages author, in this case `tool`."""
+
+    tool_call_id: Required[str]
+    """Tool call that this message is responding to."""
src/openai/types/chat/chat_completion_tool_param.py
@@ -0,0 +1,42 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import Dict
+from typing_extensions import Literal, Required, TypedDict
+
+__all__ = ["ChatCompletionToolParam", "Function"]
+
+
+class Function(TypedDict, total=False):
+    name: Required[str]
+    """The name of the function to be called.
+
+    Must be a-z, A-Z, 0-9, or contain underscores and dashes, with a maximum length
+    of 64.
+    """
+
+    parameters: Required[Dict[str, object]]
+    """The parameters the functions accepts, described as a JSON Schema object.
+
+    See the [guide](https://platform.openai.com/docs/guides/gpt/function-calling)
+    for examples, and the
+    [JSON Schema reference](https://json-schema.org/understanding-json-schema/) for
+    documentation about the format.
+
+    To describe a function that accepts no parameters, provide the value
+    `{"type": "object", "properties": {}}`.
+    """
+
+    description: str
+    """
+    A description of what the function does, used by the model to choose when and
+    how to call the function.
+    """
+
+
+class ChatCompletionToolParam(TypedDict, total=False):
+    function: Required[Function]
+
+    type: Required[Literal["function"]]
+    """The type of the tool. Currently, only `function` is supported."""
src/openai/types/chat/chat_completion_user_message_param.py
@@ -0,0 +1,18 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing import List, Union
+from typing_extensions import Literal, Required, TypedDict
+
+from .chat_completion_content_part_param import ChatCompletionContentPartParam
+
+__all__ = ["ChatCompletionUserMessageParam"]
+
+
+class ChatCompletionUserMessageParam(TypedDict, total=False):
+    content: Required[Union[str, List[ChatCompletionContentPartParam], None]]
+    """The contents of the user message."""
+
+    role: Required[Literal["user"]]
+    """The role of the messages author, in this case `user`."""
src/openai/types/chat/completion_create_params.py
@@ -5,13 +5,20 @@ from __future__ import annotations
 from typing import Dict, List, Union, Optional
 from typing_extensions import Literal, Required, TypedDict
 
+from .chat_completion_tool_param import ChatCompletionToolParam
 from .chat_completion_message_param import ChatCompletionMessageParam
+from .chat_completion_tool_choice_option_param import (
+    ChatCompletionToolChoiceOptionParam,
+)
+from .chat_completion_function_call_option_param import (
+    ChatCompletionFunctionCallOptionParam,
+)
 
 __all__ = [
     "CompletionCreateParamsBase",
     "FunctionCall",
-    "FunctionCallFunctionCallOption",
     "Function",
+    "ResponseFormat",
     "CompletionCreateParamsNonStreaming",
     "CompletionCreateParamsStreaming",
 ]
@@ -59,22 +66,28 @@ class CompletionCreateParamsBase(TypedDict, total=False):
     """
 
     function_call: FunctionCall
-    """Controls how the model calls functions.
+    """Deprecated in favor of `tool_choice`.
 
-    "none" means the model will not call a function and instead generates a message.
-    "auto" means the model can pick between generating a message or calling a
-    function. Specifying a particular function via `{"name": "my_function"}` forces
-    the model to call that function. "none" is the default when no functions are
-    present. "auto" is the default if functions are present.
+    Controls which (if any) function is called by the model. `none` means the model
+    will not call a function and instead generates a message. `auto` means the model
+    can pick between generating a message or calling a function. Specifying a
+    particular function via `{"name": "my_function"}` forces the model to call that
+    function.
+
+    `none` is the default when no functions are present. `auto`` is the default if
+    functions are present.
     """
 
     functions: List[Function]
-    """A list of functions the model may generate JSON inputs for."""
+    """Deprecated in favor of `tools`.
+
+    A list of functions the model may generate JSON inputs for.
+    """
 
     logit_bias: Optional[Dict[str, int]]
     """Modify the likelihood of specified tokens appearing in the completion.
 
-    Accepts a json object that maps tokens (specified by their token ID in the
+    Accepts a JSON object that maps tokens (specified by their token ID in the
     tokenizer) to an associated bias value from -100 to 100. Mathematically, the
     bias is added to the logits generated by the model prior to sampling. The exact
     effect will vary per model, but values between -1 and 1 should decrease or
@@ -103,6 +116,21 @@ class CompletionCreateParamsBase(TypedDict, total=False):
     [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
     """
 
+    response_format: ResponseFormat
+    """An object specifying the format that the model must output.
+
+    Used to enable JSON mode.
+    """
+
+    seed: Optional[int]
+    """This feature is in Beta.
+
+    If specified, our system will make a best effort to sample deterministically,
+    such that repeated requests with the same `seed` and parameters should return
+    the same result. Determinism is not guaranteed, and you should refer to the
+    `system_fingerprint` response parameter to monitor changes in the backend.
+    """
+
     stop: Union[Optional[str], List[str]]
     """Up to 4 sequences where the API will stop generating further tokens."""
 
@@ -115,6 +143,26 @@ class CompletionCreateParamsBase(TypedDict, total=False):
     We generally recommend altering this or `top_p` but not both.
     """
 
+    tool_choice: ChatCompletionToolChoiceOptionParam
+    """
+    Controls which (if any) function is called by the model. `none` means the model
+    will not call a function and instead generates a message. `auto` means the model
+    can pick between generating a message or calling a function. Specifying a
+    particular function via
+    `{"type: "function", "function": {"name": "my_function"}}` forces the model to
+    call that function.
+
+    `none` is the default when no functions are present. `auto` is the default if
+    functions are present.
+    """
+
+    tools: List[ChatCompletionToolParam]
+    """A list of tools the model may call.
+
+    Currently, only functions are supported as a tool. Use this to provide a list of
+    functions the model may generate JSON inputs for.
+    """
+
     top_p: Optional[float]
     """
     An alternative to sampling with temperature, called nucleus sampling, where the
@@ -132,12 +180,7 @@ class CompletionCreateParamsBase(TypedDict, total=False):
     """
 
 
-class FunctionCallFunctionCallOption(TypedDict, total=False):
-    name: Required[str]
-    """The name of the function to call."""
-
-
-FunctionCall = Union[Literal["none", "auto"], FunctionCallFunctionCallOption]
+FunctionCall = Union[Literal["none", "auto"], ChatCompletionFunctionCallOptionParam]
 
 
 class Function(TypedDict, total=False):
@@ -167,6 +210,23 @@ class Function(TypedDict, total=False):
     """
 
 
+class ResponseFormat(TypedDict, total=False):
+    type: Literal["text", "json_object"]
+    """Setting to `json_object` enables JSON mode.
+
+    This guarantees that the message the model generates is valid JSON.
+
+    Note that your system prompt must still instruct the model to produce JSON, and
+    to help ensure you don't forget, the API will throw an error if the string
+    `JSON` does not appear in your system message. Also note that the message
+    content may be partial (i.e. cut off) if `finish_reason="length"`, which
+    indicates the generation exceeded `max_tokens` or the conversation exceeded the
+    max context length.
+
+    Must be one of `text` or `json_object`.
+    """
+
+
 class CompletionCreateParamsNonStreaming(CompletionCreateParamsBase):
     stream: Optional[Literal[False]]
     """If set, partial message deltas will be sent, like in ChatGPT.
src/openai/types/fine_tuning/fine_tuning_job.py
@@ -67,7 +67,7 @@ class FineTuningJob(BaseModel):
     model: str
     """The base model that is being fine-tuned."""
 
-    object: str
+    object: Literal["fine_tuning.job"]
     """The object type, which is always "fine_tuning.job"."""
 
     organization_id: str
@@ -80,7 +80,7 @@ class FineTuningJob(BaseModel):
     [Files API](https://platform.openai.com/docs/api-reference/files/retrieve-contents).
     """
 
-    status: str
+    status: Literal["validating_files", "queued", "running", "succeeded", "failed", "cancelled"]
     """
     The current status of the fine-tuning job, which can be either
     `validating_files`, `queued`, `running`, `succeeded`, `failed`, or `cancelled`.
src/openai/types/fine_tuning/fine_tuning_job_event.py
@@ -16,4 +16,4 @@ class FineTuningJobEvent(BaseModel):
 
     message: str
 
-    object: str
+    object: Literal["fine_tuning.job.event"]
src/openai/types/fine_tuning/job_create_params.py
@@ -58,6 +58,19 @@ class JobCreateParams(TypedDict, total=False):
 
 
 class Hyperparameters(TypedDict, total=False):
+    batch_size: Union[Literal["auto"], int]
+    """Number of examples in each batch.
+
+    A larger batch size means that model parameters are updated less frequently, but
+    with lower variance.
+    """
+
+    learning_rate_multiplier: Union[Literal["auto"], float]
+    """Scaling factor for the learning rate.
+
+    A smaller learning rate may be useful to avoid overfitting.
+    """
+
     n_epochs: Union[Literal["auto"], int]
     """The number of epochs to train the model for.
 
src/openai/types/__init__.py
@@ -16,6 +16,7 @@ from .model_deleted import ModelDeleted as ModelDeleted
 from .fine_tune_event import FineTuneEvent as FineTuneEvent
 from .images_response import ImagesResponse as ImagesResponse
 from .completion_usage import CompletionUsage as CompletionUsage
+from .file_list_params import FileListParams as FileListParams
 from .completion_choice import CompletionChoice as CompletionChoice
 from .image_edit_params import ImageEditParams as ImageEditParams
 from .edit_create_params import EditCreateParams as EditCreateParams
src/openai/types/completion.py
@@ -1,6 +1,7 @@
 # File generated from our OpenAPI spec by Stainless.
 
 from typing import List, Optional
+from typing_extensions import Literal
 
 from .._models import BaseModel
 from .completion_usage import CompletionUsage
@@ -22,8 +23,15 @@ class Completion(BaseModel):
     model: str
     """The model used for completion."""
 
-    object: str
+    object: Literal["text_completion"]
     """The object type, which is always "text_completion" """
 
+    system_fingerprint: Optional[str] = None
+    """This fingerprint represents the backend configuration that the model runs with.
+
+    Can be used in conjunction with the `seed` request parameter to understand when
+    backend changes have been made that might impact determinism.
+    """
+
     usage: Optional[CompletionUsage] = None
     """Usage statistics for the completion request."""
src/openai/types/completion_create_params.py
@@ -73,7 +73,7 @@ class CompletionCreateParamsBase(TypedDict, total=False):
     logit_bias: Optional[Dict[str, int]]
     """Modify the likelihood of specified tokens appearing in the completion.
 
-    Accepts a json object that maps tokens (specified by their token ID in the GPT
+    Accepts a JSON object that maps tokens (specified by their token ID in the GPT
     tokenizer) to an associated bias value from -100 to 100. You can use this
     [tokenizer tool](/tokenizer?view=bpe) (which works for both GPT-2 and GPT-3) to
     convert text to token IDs. Mathematically, the bias is added to the logits
@@ -122,6 +122,16 @@ class CompletionCreateParamsBase(TypedDict, total=False):
     [See more information about frequency and presence penalties.](https://platform.openai.com/docs/guides/gpt/parameter-details)
     """
 
+    seed: Optional[int]
+    """
+    If specified, our system will make a best effort to sample deterministically,
+    such that repeated requests with the same `seed` and parameters should return
+    the same result.
+
+    Determinism is not guaranteed, and you should refer to the `system_fingerprint`
+    response parameter to monitor changes in the backend.
+    """
+
     stop: Union[Optional[str], List[str], None]
     """Up to 4 sequences where the API will stop generating further tokens.
 
src/openai/types/create_embedding_response.py
@@ -1,6 +1,7 @@
 # File generated from our OpenAPI spec by Stainless.
 
 from typing import List
+from typing_extensions import Literal
 
 from .._models import BaseModel
 from .embedding import Embedding
@@ -23,7 +24,7 @@ class CreateEmbeddingResponse(BaseModel):
     model: str
     """The name of the model used to generate the embedding."""
 
-    object: str
+    object: Literal["embedding"]
     """The object type, which is always "embedding"."""
 
     usage: Usage
src/openai/types/edit.py
@@ -33,7 +33,7 @@ class Edit(BaseModel):
     created: int
     """The Unix timestamp (in seconds) of when the edit was created."""
 
-    object: str
+    object: Literal["edit"]
     """The object type, which is always `edit`."""
 
     usage: CompletionUsage
src/openai/types/embedding.py
@@ -1,6 +1,7 @@
 # File generated from our OpenAPI spec by Stainless.
 
 from typing import List
+from typing_extensions import Literal
 
 from .._models import BaseModel
 
@@ -18,5 +19,5 @@ class Embedding(BaseModel):
     index: int
     """The index of the embedding in the list of embeddings."""
 
-    object: str
+    object: Literal["embedding"]
     """The object type, which is always "embedding"."""
src/openai/types/file_create_params.py
@@ -2,7 +2,7 @@
 
 from __future__ import annotations
 
-from typing_extensions import Required, TypedDict
+from typing_extensions import Literal, Required, TypedDict
 
 from .._types import FileTypes
 
@@ -11,16 +11,15 @@ __all__ = ["FileCreateParams"]
 
 class FileCreateParams(TypedDict, total=False):
     file: Required[FileTypes]
-    """The file object (not file name) to be uploaded.
+    """The File object (not file name) to be uploaded."""
 
-    If the `purpose` is set to "fine-tune", the file will be used for fine-tuning.
-    """
-
-    purpose: Required[str]
+    purpose: Required[Literal["fine-tune", "assistants"]]
     """The intended purpose of the uploaded file.
 
     Use "fine-tune" for
-    [fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning). This
-    allows us to validate the format of the uploaded file is correct for
-    fine-tuning.
+    [Fine-tuning](https://platform.openai.com/docs/api-reference/fine-tuning) and
+    "assistants" for
+    [Assistants](https://platform.openai.com/docs/api-reference/assistants) and
+    [Messages](https://platform.openai.com/docs/api-reference/messages). This allows
+    us to validate the format of the uploaded file is correct for fine-tuning.
     """
src/openai/types/file_deleted.py
@@ -1,5 +1,7 @@
 # File generated from our OpenAPI spec by Stainless.
 
+from typing_extensions import Literal
+
 from .._models import BaseModel
 
 __all__ = ["FileDeleted"]
@@ -10,4 +12,4 @@ class FileDeleted(BaseModel):
 
     deleted: bool
 
-    object: str
+    object: Literal["file"]
src/openai/types/file_list_params.py
@@ -0,0 +1,12 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+from typing_extensions import TypedDict
+
+__all__ = ["FileListParams"]
+
+
+class FileListParams(TypedDict, total=False):
+    purpose: str
+    """Only return files with the given purpose."""
src/openai/types/file_object.py
@@ -1,6 +1,7 @@
 # File generated from our OpenAPI spec by Stainless.
 
 from typing import Optional
+from typing_extensions import Literal
 
 from .._models import BaseModel
 
@@ -12,7 +13,7 @@ class FileObject(BaseModel):
     """The file identifier, which can be referenced in the API endpoints."""
 
     bytes: int
-    """The size of the file in bytes."""
+    """The size of the file, in bytes."""
 
     created_at: int
     """The Unix timestamp (in seconds) for when the file was created."""
@@ -20,21 +21,26 @@ class FileObject(BaseModel):
     filename: str
     """The name of the file."""
 
-    object: str
-    """The object type, which is always "file"."""
+    object: Literal["file"]
+    """The object type, which is always `file`."""
 
-    purpose: str
-    """The intended purpose of the file. Currently, only "fine-tune" is supported."""
+    purpose: Literal["fine-tune", "fine-tune-results", "assistants", "assistants_output"]
+    """The intended purpose of the file.
 
-    status: Optional[str] = None
+    Supported values are `fine-tune`, `fine-tune-results`, `assistants`, and
+    `assistants_output`.
     """
-    The current status of the file, which can be either `uploaded`, `processed`,
-    `pending`, `error`, `deleting` or `deleted`.
+
+    status: Literal["uploaded", "processed", "error"]
+    """Deprecated.
+
+    The current status of the file, which can be either `uploaded`, `processed`, or
+    `error`.
     """
 
     status_details: Optional[str] = None
-    """Additional details about the status of the file.
+    """Deprecated.
 
-    If the file is in the `error` state, this will include a message describing the
-    error.
+    For details on why a fine-tuning training file failed validation, see the
+    `error` field on `fine_tuning.job`.
     """
src/openai/types/fine_tune.py
@@ -1,6 +1,7 @@
 # File generated from our OpenAPI spec by Stainless.
 
 from typing import List, Optional
+from typing_extensions import Literal
 
 from .._models import BaseModel
 from .file_object import FileObject
@@ -63,7 +64,7 @@ class FineTune(BaseModel):
     model: str
     """The base model that is being fine-tuned."""
 
-    object: str
+    object: Literal["fine-tune"]
     """The object type, which is always "fine-tune"."""
 
     organization_id: str
src/openai/types/fine_tune_event.py
@@ -1,5 +1,7 @@
 # File generated from our OpenAPI spec by Stainless.
 
+from typing_extensions import Literal
+
 from .._models import BaseModel
 
 __all__ = ["FineTuneEvent"]
@@ -12,4 +14,4 @@ class FineTuneEvent(BaseModel):
 
     message: str
 
-    object: str
+    object: Literal["fine-tune-event"]
src/openai/types/fine_tune_events_list_response.py
@@ -1,6 +1,7 @@
 # File generated from our OpenAPI spec by Stainless.
 
 from typing import List
+from typing_extensions import Literal
 
 from .._models import BaseModel
 from .fine_tune_event import FineTuneEvent
@@ -11,4 +12,4 @@ __all__ = ["FineTuneEventsListResponse"]
 class FineTuneEventsListResponse(BaseModel):
     data: List[FineTuneEvent]
 
-    object: str
+    object: Literal["list"]
src/openai/types/image.py
@@ -14,5 +14,11 @@ class Image(BaseModel):
     `b64_json`.
     """
 
+    revised_prompt: Optional[str] = None
+    """
+    The prompt that was used to generate the image, if there was any revision to the
+    prompt.
+    """
+
     url: Optional[str] = None
     """The URL of the generated image, if `response_format` is `url` (default)."""
src/openai/types/image_create_variation_params.py
@@ -2,7 +2,7 @@
 
 from __future__ import annotations
 
-from typing import Optional
+from typing import Union, Optional
 from typing_extensions import Literal, Required, TypedDict
 
 from .._types import FileTypes
@@ -17,8 +17,17 @@ class ImageCreateVariationParams(TypedDict, total=False):
     Must be a valid PNG file, less than 4MB, and square.
     """
 
+    model: Union[str, Literal["dall-e-2"], None]
+    """The model to use for image generation.
+
+    Only `dall-e-2` is supported at this time.
+    """
+
     n: Optional[int]
-    """The number of images to generate. Must be between 1 and 10."""
+    """The number of images to generate.
+
+    Must be between 1 and 10. For `dall-e-3`, only `n=1` is supported.
+    """
 
     response_format: Optional[Literal["url", "b64_json"]]
     """The format in which the generated images are returned.
src/openai/types/image_edit_params.py
@@ -2,7 +2,7 @@
 
 from __future__ import annotations
 
-from typing import Optional
+from typing import Union, Optional
 from typing_extensions import Literal, Required, TypedDict
 
 from .._types import FileTypes
@@ -31,6 +31,12 @@ class ImageEditParams(TypedDict, total=False):
     PNG file, less than 4MB, and have the same dimensions as `image`.
     """
 
+    model: Union[str, Literal["dall-e-2"], None]
+    """The model to use for image generation.
+
+    Only `dall-e-2` is supported at this time.
+    """
+
     n: Optional[int]
     """The number of images to generate. Must be between 1 and 10."""
 
src/openai/types/image_generate_params.py
@@ -2,7 +2,7 @@
 
 from __future__ import annotations
 
-from typing import Optional
+from typing import Union, Optional
 from typing_extensions import Literal, Required, TypedDict
 
 __all__ = ["ImageGenerateParams"]
@@ -12,11 +12,25 @@ class ImageGenerateParams(TypedDict, total=False):
     prompt: Required[str]
     """A text description of the desired image(s).
 
-    The maximum length is 1000 characters.
+    The maximum length is 1000 characters for `dall-e-2` and 4000 characters for
+    `dall-e-3`.
     """
 
+    model: Union[str, Literal["dall-e-2", "dall-e-3"], None]
+    """The model to use for image generation."""
+
     n: Optional[int]
-    """The number of images to generate. Must be between 1 and 10."""
+    """The number of images to generate.
+
+    Must be between 1 and 10. For `dall-e-3`, only `n=1` is supported.
+    """
+
+    quality: Literal["standard", "hd"]
+    """The quality of the image that will be generated.
+
+    `hd` creates images with finer details and greater consistency across the image.
+    This param is only supported for `dall-e-3`.
+    """
 
     response_format: Optional[Literal["url", "b64_json"]]
     """The format in which the generated images are returned.
@@ -24,10 +38,20 @@ class ImageGenerateParams(TypedDict, total=False):
     Must be one of `url` or `b64_json`.
     """
 
-    size: Optional[Literal["256x256", "512x512", "1024x1024"]]
+    size: Optional[Literal["256x256", "512x512", "1024x1024", "1792x1024", "1024x1792"]]
     """The size of the generated images.
 
-    Must be one of `256x256`, `512x512`, or `1024x1024`.
+    Must be one of `256x256`, `512x512`, or `1024x1024` for `dall-e-2`. Must be one
+    of `1024x1024`, `1792x1024`, or `1024x1792` for `dall-e-3` models.
+    """
+
+    style: Optional[Literal["vivid", "natural"]]
+    """The style of the generated images.
+
+    Must be one of `vivid` or `natural`. Vivid causes the model to lean towards
+    generating hyper-real and dramatic images. Natural causes the model to produce
+    more natural, less hyper-real looking images. This param is only supported for
+    `dall-e-3`.
     """
 
     user: str
src/openai/types/model.py
@@ -1,5 +1,7 @@
 # File generated from our OpenAPI spec by Stainless.
 
+from typing_extensions import Literal
+
 from .._models import BaseModel
 
 __all__ = ["Model"]
@@ -12,7 +14,7 @@ class Model(BaseModel):
     created: int
     """The Unix timestamp (in seconds) when the model was created."""
 
-    object: str
+    object: Literal["model"]
     """The object type, which is always "model"."""
 
     owned_by: str
src/openai/__init__.py
@@ -329,6 +329,7 @@ def _reset_client() -> None:  # type: ignore[reportUnusedFunction]
     _client = None
 
 
+from ._module_client import beta as beta
 from ._module_client import chat as chat
 from ._module_client import audio as audio
 from ._module_client import edits as edits
src/openai/_client.py
@@ -52,6 +52,7 @@ class OpenAI(SyncAPIClient):
     models: resources.Models
     fine_tuning: resources.FineTuning
     fine_tunes: resources.FineTunes
+    beta: resources.Beta
     with_raw_response: OpenAIWithRawResponse
 
     # client options
@@ -125,6 +126,7 @@ class OpenAI(SyncAPIClient):
         self.models = resources.Models(self)
         self.fine_tuning = resources.FineTuning(self)
         self.fine_tunes = resources.FineTunes(self)
+        self.beta = resources.Beta(self)
         self.with_raw_response = OpenAIWithRawResponse(self)
 
     @property
@@ -257,6 +259,7 @@ class AsyncOpenAI(AsyncAPIClient):
     models: resources.AsyncModels
     fine_tuning: resources.AsyncFineTuning
     fine_tunes: resources.AsyncFineTunes
+    beta: resources.AsyncBeta
     with_raw_response: AsyncOpenAIWithRawResponse
 
     # client options
@@ -330,6 +333,7 @@ class AsyncOpenAI(AsyncAPIClient):
         self.models = resources.AsyncModels(self)
         self.fine_tuning = resources.AsyncFineTuning(self)
         self.fine_tunes = resources.AsyncFineTunes(self)
+        self.beta = resources.AsyncBeta(self)
         self.with_raw_response = AsyncOpenAIWithRawResponse(self)
 
     @property
@@ -466,6 +470,7 @@ class OpenAIWithRawResponse:
         self.models = resources.ModelsWithRawResponse(client.models)
         self.fine_tuning = resources.FineTuningWithRawResponse(client.fine_tuning)
         self.fine_tunes = resources.FineTunesWithRawResponse(client.fine_tunes)
+        self.beta = resources.BetaWithRawResponse(client.beta)
 
 
 class AsyncOpenAIWithRawResponse:
@@ -481,6 +486,7 @@ class AsyncOpenAIWithRawResponse:
         self.models = resources.AsyncModelsWithRawResponse(client.models)
         self.fine_tuning = resources.AsyncFineTuningWithRawResponse(client.fine_tuning)
         self.fine_tunes = resources.AsyncFineTunesWithRawResponse(client.fine_tunes)
+        self.beta = resources.AsyncBetaWithRawResponse(client.beta)
 
 
 Client = OpenAI
src/openai/_module_client.py
@@ -12,6 +12,12 @@ class ChatProxy(LazyProxy[resources.Chat]):
         return _load_client().chat
 
 
+class BetaProxy(LazyProxy[resources.Beta]):
+    @override
+    def __load__(self) -> resources.Beta:
+        return _load_client().beta
+
+
 class EditsProxy(LazyProxy[resources.Edits]):
     @override
     def __load__(self) -> resources.Edits:
@@ -73,6 +79,7 @@ class FineTuningProxy(LazyProxy[resources.FineTuning]):
 
 
 chat: resources.Chat = ChatProxy().__as_proxied__()
+beta: resources.Beta = BetaProxy().__as_proxied__()
 edits: resources.Edits = EditsProxy().__as_proxied__()
 files: resources.Files = FilesProxy().__as_proxied__()
 audio: resources.Audio = AudioProxy().__as_proxied__()
src/openai/_version.py
@@ -1,4 +1,4 @@
 # File generated from our OpenAPI spec by Stainless.
 
 __title__ = "openai"
-__version__ = "1.0.1"
+__version__ = "1.1.0"
src/openai/pagination.py
@@ -1,7 +1,7 @@
 # File generated from our OpenAPI spec by Stainless.
 
 from typing import Any, List, Generic, TypeVar, Optional, cast
-from typing_extensions import Protocol, override, runtime_checkable
+from typing_extensions import Literal, Protocol, override, runtime_checkable
 
 from ._types import ModelT
 from ._models import BaseModel
@@ -21,7 +21,7 @@ class SyncPage(BaseSyncPage[ModelT], BasePage[ModelT], Generic[ModelT]):
     """Note: no pagination actually occurs yet, this is for forwards-compatibility."""
 
     data: List[ModelT]
-    object: str
+    object: Literal["list"]
 
     @override
     def _get_page_items(self) -> List[ModelT]:
@@ -40,7 +40,7 @@ class AsyncPage(BaseAsyncPage[ModelT], BasePage[ModelT], Generic[ModelT]):
     """Note: no pagination actually occurs yet, this is for forwards-compatibility."""
 
     data: List[ModelT]
-    object: str
+    object: Literal["list"]
 
     @override
     def _get_page_items(self) -> List[ModelT]:
tests/api_resources/audio/test_speech.py
@@ -0,0 +1,110 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+import os
+
+import httpx
+import pytest
+from respx import MockRouter
+
+from openai import OpenAI, AsyncOpenAI
+from openai._types import BinaryResponseContent
+from openai._client import OpenAI, AsyncOpenAI
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+api_key = "My API Key"
+
+
+class TestSpeech:
+    strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+    loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+    parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"])
+
+    @parametrize
+    @pytest.mark.respx(base_url=base_url)
+    def test_method_create(self, client: OpenAI, respx_mock: MockRouter) -> None:
+        respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+        speech = client.audio.speech.create(
+            input="string",
+            model="string",
+            voice="alloy",
+        )
+        assert isinstance(speech, BinaryResponseContent)
+        assert speech.json() == {"foo": "bar"}
+
+    @parametrize
+    @pytest.mark.respx(base_url=base_url)
+    def test_method_create_with_all_params(self, client: OpenAI, respx_mock: MockRouter) -> None:
+        respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+        speech = respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+        client.audio.speech.create(
+            input="string",
+            model="string",
+            voice="alloy",
+            response_format="mp3",
+            speed=0.25,
+        )
+        assert isinstance(speech, BinaryResponseContent)
+        assert speech.json() == {"foo": "bar"}
+
+    @parametrize
+    @pytest.mark.respx(base_url=base_url)
+    def test_raw_response_create(self, client: OpenAI, respx_mock: MockRouter) -> None:
+        respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+        response = client.audio.speech.with_raw_response.create(
+            input="string",
+            model="string",
+            voice="alloy",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        speech = response.parse()
+        assert isinstance(speech, BinaryResponseContent)
+        assert speech.json() == {"foo": "bar"}
+
+
+class TestAsyncSpeech:
+    strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+    loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+    parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"])
+
+    @parametrize
+    @pytest.mark.respx(base_url=base_url)
+    async def test_method_create(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None:
+        respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+        speech = await client.audio.speech.create(
+            input="string",
+            model="string",
+            voice="alloy",
+        )
+        assert isinstance(speech, BinaryResponseContent)
+        assert speech.json() == {"foo": "bar"}
+
+    @parametrize
+    @pytest.mark.respx(base_url=base_url)
+    async def test_method_create_with_all_params(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None:
+        respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+        speech = respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+        await client.audio.speech.create(
+            input="string",
+            model="string",
+            voice="alloy",
+            response_format="mp3",
+            speed=0.25,
+        )
+        assert isinstance(speech, BinaryResponseContent)
+        assert speech.json() == {"foo": "bar"}
+
+    @parametrize
+    @pytest.mark.respx(base_url=base_url)
+    async def test_raw_response_create(self, client: AsyncOpenAI, respx_mock: MockRouter) -> None:
+        respx_mock.post("/audio/speech").mock(return_value=httpx.Response(200, json={"foo": "bar"}))
+        response = await client.audio.speech.with_raw_response.create(
+            input="string",
+            model="string",
+            voice="alloy",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        speech = response.parse()
+        assert isinstance(speech, BinaryResponseContent)
+        assert speech.json() == {"foo": "bar"}
tests/api_resources/beta/assistants/__init__.py
@@ -0,0 +1,1 @@
+# File generated from our OpenAPI spec by Stainless.
tests/api_resources/beta/assistants/test_files.py
@@ -0,0 +1,190 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+import os
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai._client import OpenAI, AsyncOpenAI
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.beta.assistants import AssistantFile, FileDeleteResponse
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+api_key = "My API Key"
+
+
+class TestFiles:
+    strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+    loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+    parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"])
+
+    @parametrize
+    def test_method_create(self, client: OpenAI) -> None:
+        file = client.beta.assistants.files.create(
+            "file-AF1WoRqd3aJAHsqc9NY7iL8F",
+            file_id="string",
+        )
+        assert_matches_type(AssistantFile, file, path=["response"])
+
+    @parametrize
+    def test_raw_response_create(self, client: OpenAI) -> None:
+        response = client.beta.assistants.files.with_raw_response.create(
+            "file-AF1WoRqd3aJAHsqc9NY7iL8F",
+            file_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        file = response.parse()
+        assert_matches_type(AssistantFile, file, path=["response"])
+
+    @parametrize
+    def test_method_retrieve(self, client: OpenAI) -> None:
+        file = client.beta.assistants.files.retrieve(
+            "string",
+            assistant_id="string",
+        )
+        assert_matches_type(AssistantFile, file, path=["response"])
+
+    @parametrize
+    def test_raw_response_retrieve(self, client: OpenAI) -> None:
+        response = client.beta.assistants.files.with_raw_response.retrieve(
+            "string",
+            assistant_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        file = response.parse()
+        assert_matches_type(AssistantFile, file, path=["response"])
+
+    @parametrize
+    def test_method_list(self, client: OpenAI) -> None:
+        file = client.beta.assistants.files.list(
+            "string",
+        )
+        assert_matches_type(SyncCursorPage[AssistantFile], file, path=["response"])
+
+    @parametrize
+    def test_method_list_with_all_params(self, client: OpenAI) -> None:
+        file = client.beta.assistants.files.list(
+            "string",
+            after="string",
+            before="string",
+            limit=0,
+            order="asc",
+        )
+        assert_matches_type(SyncCursorPage[AssistantFile], file, path=["response"])
+
+    @parametrize
+    def test_raw_response_list(self, client: OpenAI) -> None:
+        response = client.beta.assistants.files.with_raw_response.list(
+            "string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        file = response.parse()
+        assert_matches_type(SyncCursorPage[AssistantFile], file, path=["response"])
+
+    @parametrize
+    def test_method_delete(self, client: OpenAI) -> None:
+        file = client.beta.assistants.files.delete(
+            "string",
+            assistant_id="string",
+        )
+        assert_matches_type(FileDeleteResponse, file, path=["response"])
+
+    @parametrize
+    def test_raw_response_delete(self, client: OpenAI) -> None:
+        response = client.beta.assistants.files.with_raw_response.delete(
+            "string",
+            assistant_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        file = response.parse()
+        assert_matches_type(FileDeleteResponse, file, path=["response"])
+
+
+class TestAsyncFiles:
+    strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+    loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+    parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"])
+
+    @parametrize
+    async def test_method_create(self, client: AsyncOpenAI) -> None:
+        file = await client.beta.assistants.files.create(
+            "file-AF1WoRqd3aJAHsqc9NY7iL8F",
+            file_id="string",
+        )
+        assert_matches_type(AssistantFile, file, path=["response"])
+
+    @parametrize
+    async def test_raw_response_create(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.assistants.files.with_raw_response.create(
+            "file-AF1WoRqd3aJAHsqc9NY7iL8F",
+            file_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        file = response.parse()
+        assert_matches_type(AssistantFile, file, path=["response"])
+
+    @parametrize
+    async def test_method_retrieve(self, client: AsyncOpenAI) -> None:
+        file = await client.beta.assistants.files.retrieve(
+            "string",
+            assistant_id="string",
+        )
+        assert_matches_type(AssistantFile, file, path=["response"])
+
+    @parametrize
+    async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.assistants.files.with_raw_response.retrieve(
+            "string",
+            assistant_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        file = response.parse()
+        assert_matches_type(AssistantFile, file, path=["response"])
+
+    @parametrize
+    async def test_method_list(self, client: AsyncOpenAI) -> None:
+        file = await client.beta.assistants.files.list(
+            "string",
+        )
+        assert_matches_type(AsyncCursorPage[AssistantFile], file, path=["response"])
+
+    @parametrize
+    async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None:
+        file = await client.beta.assistants.files.list(
+            "string",
+            after="string",
+            before="string",
+            limit=0,
+            order="asc",
+        )
+        assert_matches_type(AsyncCursorPage[AssistantFile], file, path=["response"])
+
+    @parametrize
+    async def test_raw_response_list(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.assistants.files.with_raw_response.list(
+            "string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        file = response.parse()
+        assert_matches_type(AsyncCursorPage[AssistantFile], file, path=["response"])
+
+    @parametrize
+    async def test_method_delete(self, client: AsyncOpenAI) -> None:
+        file = await client.beta.assistants.files.delete(
+            "string",
+            assistant_id="string",
+        )
+        assert_matches_type(FileDeleteResponse, file, path=["response"])
+
+    @parametrize
+    async def test_raw_response_delete(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.assistants.files.with_raw_response.delete(
+            "string",
+            assistant_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        file = response.parse()
+        assert_matches_type(FileDeleteResponse, file, path=["response"])
tests/api_resources/beta/chat/__init__.py
@@ -0,0 +1,1 @@
+# File generated from our OpenAPI spec by Stainless.
tests/api_resources/beta/threads/messages/__init__.py
@@ -0,0 +1,1 @@
+# File generated from our OpenAPI spec by Stainless.
tests/api_resources/beta/threads/messages/test_files.py
@@ -0,0 +1,128 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+import os
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai._client import OpenAI, AsyncOpenAI
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.beta.threads.messages import MessageFile
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+api_key = "My API Key"
+
+
+class TestFiles:
+    strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+    loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+    parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"])
+
+    @parametrize
+    def test_method_retrieve(self, client: OpenAI) -> None:
+        file = client.beta.threads.messages.files.retrieve(
+            "file-AF1WoRqd3aJAHsqc9NY7iL8F",
+            thread_id="thread_AF1WoRqd3aJAHsqc9NY7iL8F",
+            message_id="msg_AF1WoRqd3aJAHsqc9NY7iL8F",
+        )
+        assert_matches_type(MessageFile, file, path=["response"])
+
+    @parametrize
+    def test_raw_response_retrieve(self, client: OpenAI) -> None:
+        response = client.beta.threads.messages.files.with_raw_response.retrieve(
+            "file-AF1WoRqd3aJAHsqc9NY7iL8F",
+            thread_id="thread_AF1WoRqd3aJAHsqc9NY7iL8F",
+            message_id="msg_AF1WoRqd3aJAHsqc9NY7iL8F",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        file = response.parse()
+        assert_matches_type(MessageFile, file, path=["response"])
+
+    @parametrize
+    def test_method_list(self, client: OpenAI) -> None:
+        file = client.beta.threads.messages.files.list(
+            "string",
+            thread_id="string",
+        )
+        assert_matches_type(SyncCursorPage[MessageFile], file, path=["response"])
+
+    @parametrize
+    def test_method_list_with_all_params(self, client: OpenAI) -> None:
+        file = client.beta.threads.messages.files.list(
+            "string",
+            thread_id="string",
+            after="string",
+            before="string",
+            limit=0,
+            order="asc",
+        )
+        assert_matches_type(SyncCursorPage[MessageFile], file, path=["response"])
+
+    @parametrize
+    def test_raw_response_list(self, client: OpenAI) -> None:
+        response = client.beta.threads.messages.files.with_raw_response.list(
+            "string",
+            thread_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        file = response.parse()
+        assert_matches_type(SyncCursorPage[MessageFile], file, path=["response"])
+
+
+class TestAsyncFiles:
+    strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+    loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+    parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"])
+
+    @parametrize
+    async def test_method_retrieve(self, client: AsyncOpenAI) -> None:
+        file = await client.beta.threads.messages.files.retrieve(
+            "file-AF1WoRqd3aJAHsqc9NY7iL8F",
+            thread_id="thread_AF1WoRqd3aJAHsqc9NY7iL8F",
+            message_id="msg_AF1WoRqd3aJAHsqc9NY7iL8F",
+        )
+        assert_matches_type(MessageFile, file, path=["response"])
+
+    @parametrize
+    async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.threads.messages.files.with_raw_response.retrieve(
+            "file-AF1WoRqd3aJAHsqc9NY7iL8F",
+            thread_id="thread_AF1WoRqd3aJAHsqc9NY7iL8F",
+            message_id="msg_AF1WoRqd3aJAHsqc9NY7iL8F",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        file = response.parse()
+        assert_matches_type(MessageFile, file, path=["response"])
+
+    @parametrize
+    async def test_method_list(self, client: AsyncOpenAI) -> None:
+        file = await client.beta.threads.messages.files.list(
+            "string",
+            thread_id="string",
+        )
+        assert_matches_type(AsyncCursorPage[MessageFile], file, path=["response"])
+
+    @parametrize
+    async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None:
+        file = await client.beta.threads.messages.files.list(
+            "string",
+            thread_id="string",
+            after="string",
+            before="string",
+            limit=0,
+            order="asc",
+        )
+        assert_matches_type(AsyncCursorPage[MessageFile], file, path=["response"])
+
+    @parametrize
+    async def test_raw_response_list(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.threads.messages.files.with_raw_response.list(
+            "string",
+            thread_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        file = response.parse()
+        assert_matches_type(AsyncCursorPage[MessageFile], file, path=["response"])
tests/api_resources/beta/threads/runs/__init__.py
@@ -0,0 +1,1 @@
+# File generated from our OpenAPI spec by Stainless.
tests/api_resources/beta/threads/runs/test_steps.py
@@ -0,0 +1,128 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+import os
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai._client import OpenAI, AsyncOpenAI
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.beta.threads.runs import RunStep
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+api_key = "My API Key"
+
+
+class TestSteps:
+    strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+    loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+    parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"])
+
+    @parametrize
+    def test_method_retrieve(self, client: OpenAI) -> None:
+        step = client.beta.threads.runs.steps.retrieve(
+            "string",
+            thread_id="string",
+            run_id="string",
+        )
+        assert_matches_type(RunStep, step, path=["response"])
+
+    @parametrize
+    def test_raw_response_retrieve(self, client: OpenAI) -> None:
+        response = client.beta.threads.runs.steps.with_raw_response.retrieve(
+            "string",
+            thread_id="string",
+            run_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        step = response.parse()
+        assert_matches_type(RunStep, step, path=["response"])
+
+    @parametrize
+    def test_method_list(self, client: OpenAI) -> None:
+        step = client.beta.threads.runs.steps.list(
+            "string",
+            thread_id="string",
+        )
+        assert_matches_type(SyncCursorPage[RunStep], step, path=["response"])
+
+    @parametrize
+    def test_method_list_with_all_params(self, client: OpenAI) -> None:
+        step = client.beta.threads.runs.steps.list(
+            "string",
+            thread_id="string",
+            after="string",
+            before="string",
+            limit=0,
+            order="asc",
+        )
+        assert_matches_type(SyncCursorPage[RunStep], step, path=["response"])
+
+    @parametrize
+    def test_raw_response_list(self, client: OpenAI) -> None:
+        response = client.beta.threads.runs.steps.with_raw_response.list(
+            "string",
+            thread_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        step = response.parse()
+        assert_matches_type(SyncCursorPage[RunStep], step, path=["response"])
+
+
+class TestAsyncSteps:
+    strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+    loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+    parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"])
+
+    @parametrize
+    async def test_method_retrieve(self, client: AsyncOpenAI) -> None:
+        step = await client.beta.threads.runs.steps.retrieve(
+            "string",
+            thread_id="string",
+            run_id="string",
+        )
+        assert_matches_type(RunStep, step, path=["response"])
+
+    @parametrize
+    async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.threads.runs.steps.with_raw_response.retrieve(
+            "string",
+            thread_id="string",
+            run_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        step = response.parse()
+        assert_matches_type(RunStep, step, path=["response"])
+
+    @parametrize
+    async def test_method_list(self, client: AsyncOpenAI) -> None:
+        step = await client.beta.threads.runs.steps.list(
+            "string",
+            thread_id="string",
+        )
+        assert_matches_type(AsyncCursorPage[RunStep], step, path=["response"])
+
+    @parametrize
+    async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None:
+        step = await client.beta.threads.runs.steps.list(
+            "string",
+            thread_id="string",
+            after="string",
+            before="string",
+            limit=0,
+            order="asc",
+        )
+        assert_matches_type(AsyncCursorPage[RunStep], step, path=["response"])
+
+    @parametrize
+    async def test_raw_response_list(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.threads.runs.steps.with_raw_response.list(
+            "string",
+            thread_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        step = response.parse()
+        assert_matches_type(AsyncCursorPage[RunStep], step, path=["response"])
tests/api_resources/beta/threads/__init__.py
@@ -0,0 +1,1 @@
+# File generated from our OpenAPI spec by Stainless.
tests/api_resources/beta/threads/test_messages.py
@@ -0,0 +1,234 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+import os
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai._client import OpenAI, AsyncOpenAI
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.beta.threads import ThreadMessage
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+api_key = "My API Key"
+
+
+class TestMessages:
+    strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+    loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+    parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"])
+
+    @parametrize
+    def test_method_create(self, client: OpenAI) -> None:
+        message = client.beta.threads.messages.create(
+            "string",
+            content="x",
+            role="user",
+        )
+        assert_matches_type(ThreadMessage, message, path=["response"])
+
+    @parametrize
+    def test_method_create_with_all_params(self, client: OpenAI) -> None:
+        message = client.beta.threads.messages.create(
+            "string",
+            content="x",
+            role="user",
+            file_ids=["string"],
+            metadata={},
+        )
+        assert_matches_type(ThreadMessage, message, path=["response"])
+
+    @parametrize
+    def test_raw_response_create(self, client: OpenAI) -> None:
+        response = client.beta.threads.messages.with_raw_response.create(
+            "string",
+            content="x",
+            role="user",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        message = response.parse()
+        assert_matches_type(ThreadMessage, message, path=["response"])
+
+    @parametrize
+    def test_method_retrieve(self, client: OpenAI) -> None:
+        message = client.beta.threads.messages.retrieve(
+            "string",
+            thread_id="string",
+        )
+        assert_matches_type(ThreadMessage, message, path=["response"])
+
+    @parametrize
+    def test_raw_response_retrieve(self, client: OpenAI) -> None:
+        response = client.beta.threads.messages.with_raw_response.retrieve(
+            "string",
+            thread_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        message = response.parse()
+        assert_matches_type(ThreadMessage, message, path=["response"])
+
+    @parametrize
+    def test_method_update(self, client: OpenAI) -> None:
+        message = client.beta.threads.messages.update(
+            "string",
+            thread_id="string",
+        )
+        assert_matches_type(ThreadMessage, message, path=["response"])
+
+    @parametrize
+    def test_method_update_with_all_params(self, client: OpenAI) -> None:
+        message = client.beta.threads.messages.update(
+            "string",
+            thread_id="string",
+            metadata={},
+        )
+        assert_matches_type(ThreadMessage, message, path=["response"])
+
+    @parametrize
+    def test_raw_response_update(self, client: OpenAI) -> None:
+        response = client.beta.threads.messages.with_raw_response.update(
+            "string",
+            thread_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        message = response.parse()
+        assert_matches_type(ThreadMessage, message, path=["response"])
+
+    @parametrize
+    def test_method_list(self, client: OpenAI) -> None:
+        message = client.beta.threads.messages.list(
+            "string",
+        )
+        assert_matches_type(SyncCursorPage[ThreadMessage], message, path=["response"])
+
+    @parametrize
+    def test_method_list_with_all_params(self, client: OpenAI) -> None:
+        message = client.beta.threads.messages.list(
+            "string",
+            after="string",
+            before="string",
+            limit=0,
+            order="asc",
+        )
+        assert_matches_type(SyncCursorPage[ThreadMessage], message, path=["response"])
+
+    @parametrize
+    def test_raw_response_list(self, client: OpenAI) -> None:
+        response = client.beta.threads.messages.with_raw_response.list(
+            "string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        message = response.parse()
+        assert_matches_type(SyncCursorPage[ThreadMessage], message, path=["response"])
+
+
+class TestAsyncMessages:
+    strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+    loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+    parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"])
+
+    @parametrize
+    async def test_method_create(self, client: AsyncOpenAI) -> None:
+        message = await client.beta.threads.messages.create(
+            "string",
+            content="x",
+            role="user",
+        )
+        assert_matches_type(ThreadMessage, message, path=["response"])
+
+    @parametrize
+    async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None:
+        message = await client.beta.threads.messages.create(
+            "string",
+            content="x",
+            role="user",
+            file_ids=["string"],
+            metadata={},
+        )
+        assert_matches_type(ThreadMessage, message, path=["response"])
+
+    @parametrize
+    async def test_raw_response_create(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.threads.messages.with_raw_response.create(
+            "string",
+            content="x",
+            role="user",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        message = response.parse()
+        assert_matches_type(ThreadMessage, message, path=["response"])
+
+    @parametrize
+    async def test_method_retrieve(self, client: AsyncOpenAI) -> None:
+        message = await client.beta.threads.messages.retrieve(
+            "string",
+            thread_id="string",
+        )
+        assert_matches_type(ThreadMessage, message, path=["response"])
+
+    @parametrize
+    async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.threads.messages.with_raw_response.retrieve(
+            "string",
+            thread_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        message = response.parse()
+        assert_matches_type(ThreadMessage, message, path=["response"])
+
+    @parametrize
+    async def test_method_update(self, client: AsyncOpenAI) -> None:
+        message = await client.beta.threads.messages.update(
+            "string",
+            thread_id="string",
+        )
+        assert_matches_type(ThreadMessage, message, path=["response"])
+
+    @parametrize
+    async def test_method_update_with_all_params(self, client: AsyncOpenAI) -> None:
+        message = await client.beta.threads.messages.update(
+            "string",
+            thread_id="string",
+            metadata={},
+        )
+        assert_matches_type(ThreadMessage, message, path=["response"])
+
+    @parametrize
+    async def test_raw_response_update(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.threads.messages.with_raw_response.update(
+            "string",
+            thread_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        message = response.parse()
+        assert_matches_type(ThreadMessage, message, path=["response"])
+
+    @parametrize
+    async def test_method_list(self, client: AsyncOpenAI) -> None:
+        message = await client.beta.threads.messages.list(
+            "string",
+        )
+        assert_matches_type(AsyncCursorPage[ThreadMessage], message, path=["response"])
+
+    @parametrize
+    async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None:
+        message = await client.beta.threads.messages.list(
+            "string",
+            after="string",
+            before="string",
+            limit=0,
+            order="asc",
+        )
+        assert_matches_type(AsyncCursorPage[ThreadMessage], message, path=["response"])
+
+    @parametrize
+    async def test_raw_response_list(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.threads.messages.with_raw_response.list(
+            "string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        message = response.parse()
+        assert_matches_type(AsyncCursorPage[ThreadMessage], message, path=["response"])
tests/api_resources/beta/threads/test_runs.py
@@ -0,0 +1,308 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+import os
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai._client import OpenAI, AsyncOpenAI
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.beta.threads import Run
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+api_key = "My API Key"
+
+
+class TestRuns:
+    strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+    loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+    parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"])
+
+    @parametrize
+    def test_method_create(self, client: OpenAI) -> None:
+        run = client.beta.threads.runs.create(
+            "string",
+            assistant_id="string",
+        )
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    def test_method_create_with_all_params(self, client: OpenAI) -> None:
+        run = client.beta.threads.runs.create(
+            "string",
+            assistant_id="string",
+            instructions="string",
+            metadata={},
+            model="string",
+            tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}],
+        )
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    def test_raw_response_create(self, client: OpenAI) -> None:
+        response = client.beta.threads.runs.with_raw_response.create(
+            "string",
+            assistant_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        run = response.parse()
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    def test_method_retrieve(self, client: OpenAI) -> None:
+        run = client.beta.threads.runs.retrieve(
+            "string",
+            thread_id="string",
+        )
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    def test_raw_response_retrieve(self, client: OpenAI) -> None:
+        response = client.beta.threads.runs.with_raw_response.retrieve(
+            "string",
+            thread_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        run = response.parse()
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    def test_method_update(self, client: OpenAI) -> None:
+        run = client.beta.threads.runs.update(
+            "string",
+            thread_id="string",
+        )
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    def test_method_update_with_all_params(self, client: OpenAI) -> None:
+        run = client.beta.threads.runs.update(
+            "string",
+            thread_id="string",
+            metadata={},
+        )
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    def test_raw_response_update(self, client: OpenAI) -> None:
+        response = client.beta.threads.runs.with_raw_response.update(
+            "string",
+            thread_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        run = response.parse()
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    def test_method_list(self, client: OpenAI) -> None:
+        run = client.beta.threads.runs.list(
+            "string",
+        )
+        assert_matches_type(SyncCursorPage[Run], run, path=["response"])
+
+    @parametrize
+    def test_method_list_with_all_params(self, client: OpenAI) -> None:
+        run = client.beta.threads.runs.list(
+            "string",
+            after="string",
+            before="string",
+            limit=0,
+            order="asc",
+        )
+        assert_matches_type(SyncCursorPage[Run], run, path=["response"])
+
+    @parametrize
+    def test_raw_response_list(self, client: OpenAI) -> None:
+        response = client.beta.threads.runs.with_raw_response.list(
+            "string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        run = response.parse()
+        assert_matches_type(SyncCursorPage[Run], run, path=["response"])
+
+    @parametrize
+    def test_method_cancel(self, client: OpenAI) -> None:
+        run = client.beta.threads.runs.cancel(
+            "string",
+            thread_id="string",
+        )
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    def test_raw_response_cancel(self, client: OpenAI) -> None:
+        response = client.beta.threads.runs.with_raw_response.cancel(
+            "string",
+            thread_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        run = response.parse()
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    def test_method_submit_tool_outputs(self, client: OpenAI) -> None:
+        run = client.beta.threads.runs.submit_tool_outputs(
+            "string",
+            thread_id="string",
+            tool_outputs=[{}, {}, {}],
+        )
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    def test_raw_response_submit_tool_outputs(self, client: OpenAI) -> None:
+        response = client.beta.threads.runs.with_raw_response.submit_tool_outputs(
+            "string",
+            thread_id="string",
+            tool_outputs=[{}, {}, {}],
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        run = response.parse()
+        assert_matches_type(Run, run, path=["response"])
+
+
+class TestAsyncRuns:
+    strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+    loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+    parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"])
+
+    @parametrize
+    async def test_method_create(self, client: AsyncOpenAI) -> None:
+        run = await client.beta.threads.runs.create(
+            "string",
+            assistant_id="string",
+        )
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None:
+        run = await client.beta.threads.runs.create(
+            "string",
+            assistant_id="string",
+            instructions="string",
+            metadata={},
+            model="string",
+            tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}],
+        )
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    async def test_raw_response_create(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.threads.runs.with_raw_response.create(
+            "string",
+            assistant_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        run = response.parse()
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    async def test_method_retrieve(self, client: AsyncOpenAI) -> None:
+        run = await client.beta.threads.runs.retrieve(
+            "string",
+            thread_id="string",
+        )
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.threads.runs.with_raw_response.retrieve(
+            "string",
+            thread_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        run = response.parse()
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    async def test_method_update(self, client: AsyncOpenAI) -> None:
+        run = await client.beta.threads.runs.update(
+            "string",
+            thread_id="string",
+        )
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    async def test_method_update_with_all_params(self, client: AsyncOpenAI) -> None:
+        run = await client.beta.threads.runs.update(
+            "string",
+            thread_id="string",
+            metadata={},
+        )
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    async def test_raw_response_update(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.threads.runs.with_raw_response.update(
+            "string",
+            thread_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        run = response.parse()
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    async def test_method_list(self, client: AsyncOpenAI) -> None:
+        run = await client.beta.threads.runs.list(
+            "string",
+        )
+        assert_matches_type(AsyncCursorPage[Run], run, path=["response"])
+
+    @parametrize
+    async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None:
+        run = await client.beta.threads.runs.list(
+            "string",
+            after="string",
+            before="string",
+            limit=0,
+            order="asc",
+        )
+        assert_matches_type(AsyncCursorPage[Run], run, path=["response"])
+
+    @parametrize
+    async def test_raw_response_list(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.threads.runs.with_raw_response.list(
+            "string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        run = response.parse()
+        assert_matches_type(AsyncCursorPage[Run], run, path=["response"])
+
+    @parametrize
+    async def test_method_cancel(self, client: AsyncOpenAI) -> None:
+        run = await client.beta.threads.runs.cancel(
+            "string",
+            thread_id="string",
+        )
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    async def test_raw_response_cancel(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.threads.runs.with_raw_response.cancel(
+            "string",
+            thread_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        run = response.parse()
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    async def test_method_submit_tool_outputs(self, client: AsyncOpenAI) -> None:
+        run = await client.beta.threads.runs.submit_tool_outputs(
+            "string",
+            thread_id="string",
+            tool_outputs=[{}, {}, {}],
+        )
+        assert_matches_type(Run, run, path=["response"])
+
+    @parametrize
+    async def test_raw_response_submit_tool_outputs(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.threads.runs.with_raw_response.submit_tool_outputs(
+            "string",
+            thread_id="string",
+            tool_outputs=[{}, {}, {}],
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        run = response.parse()
+        assert_matches_type(Run, run, path=["response"])
tests/api_resources/beta/__init__.py
@@ -0,0 +1,1 @@
+# File generated from our OpenAPI spec by Stainless.
tests/api_resources/beta/test_assistants.py
@@ -0,0 +1,254 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+import os
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai._client import OpenAI, AsyncOpenAI
+from openai.pagination import SyncCursorPage, AsyncCursorPage
+from openai.types.beta import Assistant, AsssitantDeleted
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+api_key = "My API Key"
+
+
+class TestAssistants:
+    strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+    loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+    parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"])
+
+    @parametrize
+    def test_method_create(self, client: OpenAI) -> None:
+        assistant = client.beta.assistants.create(
+            model="string",
+        )
+        assert_matches_type(Assistant, assistant, path=["response"])
+
+    @parametrize
+    def test_method_create_with_all_params(self, client: OpenAI) -> None:
+        assistant = client.beta.assistants.create(
+            model="string",
+            description="string",
+            file_ids=["string", "string", "string"],
+            instructions="string",
+            metadata={},
+            name="string",
+            tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}],
+        )
+        assert_matches_type(Assistant, assistant, path=["response"])
+
+    @parametrize
+    def test_raw_response_create(self, client: OpenAI) -> None:
+        response = client.beta.assistants.with_raw_response.create(
+            model="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        assistant = response.parse()
+        assert_matches_type(Assistant, assistant, path=["response"])
+
+    @parametrize
+    def test_method_retrieve(self, client: OpenAI) -> None:
+        assistant = client.beta.assistants.retrieve(
+            "string",
+        )
+        assert_matches_type(Assistant, assistant, path=["response"])
+
+    @parametrize
+    def test_raw_response_retrieve(self, client: OpenAI) -> None:
+        response = client.beta.assistants.with_raw_response.retrieve(
+            "string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        assistant = response.parse()
+        assert_matches_type(Assistant, assistant, path=["response"])
+
+    @parametrize
+    def test_method_update(self, client: OpenAI) -> None:
+        assistant = client.beta.assistants.update(
+            "string",
+        )
+        assert_matches_type(Assistant, assistant, path=["response"])
+
+    @parametrize
+    def test_method_update_with_all_params(self, client: OpenAI) -> None:
+        assistant = client.beta.assistants.update(
+            "string",
+            description="string",
+            file_ids=["string", "string", "string"],
+            instructions="string",
+            metadata={},
+            model="string",
+            name="string",
+            tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}],
+        )
+        assert_matches_type(Assistant, assistant, path=["response"])
+
+    @parametrize
+    def test_raw_response_update(self, client: OpenAI) -> None:
+        response = client.beta.assistants.with_raw_response.update(
+            "string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        assistant = response.parse()
+        assert_matches_type(Assistant, assistant, path=["response"])
+
+    @parametrize
+    def test_method_list(self, client: OpenAI) -> None:
+        assistant = client.beta.assistants.list()
+        assert_matches_type(SyncCursorPage[Assistant], assistant, path=["response"])
+
+    @parametrize
+    def test_method_list_with_all_params(self, client: OpenAI) -> None:
+        assistant = client.beta.assistants.list(
+            after="string",
+            before="string",
+            limit=0,
+            order="asc",
+        )
+        assert_matches_type(SyncCursorPage[Assistant], assistant, path=["response"])
+
+    @parametrize
+    def test_raw_response_list(self, client: OpenAI) -> None:
+        response = client.beta.assistants.with_raw_response.list()
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        assistant = response.parse()
+        assert_matches_type(SyncCursorPage[Assistant], assistant, path=["response"])
+
+    @parametrize
+    def test_method_delete(self, client: OpenAI) -> None:
+        assistant = client.beta.assistants.delete(
+            "string",
+        )
+        assert_matches_type(AsssitantDeleted, assistant, path=["response"])
+
+    @parametrize
+    def test_raw_response_delete(self, client: OpenAI) -> None:
+        response = client.beta.assistants.with_raw_response.delete(
+            "string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        assistant = response.parse()
+        assert_matches_type(AsssitantDeleted, assistant, path=["response"])
+
+
+class TestAsyncAssistants:
+    strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+    loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+    parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"])
+
+    @parametrize
+    async def test_method_create(self, client: AsyncOpenAI) -> None:
+        assistant = await client.beta.assistants.create(
+            model="string",
+        )
+        assert_matches_type(Assistant, assistant, path=["response"])
+
+    @parametrize
+    async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None:
+        assistant = await client.beta.assistants.create(
+            model="string",
+            description="string",
+            file_ids=["string", "string", "string"],
+            instructions="string",
+            metadata={},
+            name="string",
+            tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}],
+        )
+        assert_matches_type(Assistant, assistant, path=["response"])
+
+    @parametrize
+    async def test_raw_response_create(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.assistants.with_raw_response.create(
+            model="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        assistant = response.parse()
+        assert_matches_type(Assistant, assistant, path=["response"])
+
+    @parametrize
+    async def test_method_retrieve(self, client: AsyncOpenAI) -> None:
+        assistant = await client.beta.assistants.retrieve(
+            "string",
+        )
+        assert_matches_type(Assistant, assistant, path=["response"])
+
+    @parametrize
+    async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.assistants.with_raw_response.retrieve(
+            "string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        assistant = response.parse()
+        assert_matches_type(Assistant, assistant, path=["response"])
+
+    @parametrize
+    async def test_method_update(self, client: AsyncOpenAI) -> None:
+        assistant = await client.beta.assistants.update(
+            "string",
+        )
+        assert_matches_type(Assistant, assistant, path=["response"])
+
+    @parametrize
+    async def test_method_update_with_all_params(self, client: AsyncOpenAI) -> None:
+        assistant = await client.beta.assistants.update(
+            "string",
+            description="string",
+            file_ids=["string", "string", "string"],
+            instructions="string",
+            metadata={},
+            model="string",
+            name="string",
+            tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}],
+        )
+        assert_matches_type(Assistant, assistant, path=["response"])
+
+    @parametrize
+    async def test_raw_response_update(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.assistants.with_raw_response.update(
+            "string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        assistant = response.parse()
+        assert_matches_type(Assistant, assistant, path=["response"])
+
+    @parametrize
+    async def test_method_list(self, client: AsyncOpenAI) -> None:
+        assistant = await client.beta.assistants.list()
+        assert_matches_type(AsyncCursorPage[Assistant], assistant, path=["response"])
+
+    @parametrize
+    async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None:
+        assistant = await client.beta.assistants.list(
+            after="string",
+            before="string",
+            limit=0,
+            order="asc",
+        )
+        assert_matches_type(AsyncCursorPage[Assistant], assistant, path=["response"])
+
+    @parametrize
+    async def test_raw_response_list(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.assistants.with_raw_response.list()
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        assistant = response.parse()
+        assert_matches_type(AsyncCursorPage[Assistant], assistant, path=["response"])
+
+    @parametrize
+    async def test_method_delete(self, client: AsyncOpenAI) -> None:
+        assistant = await client.beta.assistants.delete(
+            "string",
+        )
+        assert_matches_type(AsssitantDeleted, assistant, path=["response"])
+
+    @parametrize
+    async def test_raw_response_delete(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.assistants.with_raw_response.delete(
+            "string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        assistant = response.parse()
+        assert_matches_type(AsssitantDeleted, assistant, path=["response"])
tests/api_resources/beta/test_threads.py
@@ -0,0 +1,318 @@
+# File generated from our OpenAPI spec by Stainless.
+
+from __future__ import annotations
+
+import os
+
+import pytest
+
+from openai import OpenAI, AsyncOpenAI
+from tests.utils import assert_matches_type
+from openai._client import OpenAI, AsyncOpenAI
+from openai.types.beta import Thread, ThreadDeleted
+from openai.types.beta.threads import Run
+
+base_url = os.environ.get("TEST_API_BASE_URL", "http://127.0.0.1:4010")
+api_key = "My API Key"
+
+
+class TestThreads:
+    strict_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+    loose_client = OpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+    parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"])
+
+    @parametrize
+    def test_method_create(self, client: OpenAI) -> None:
+        thread = client.beta.threads.create()
+        assert_matches_type(Thread, thread, path=["response"])
+
+    @parametrize
+    def test_method_create_with_all_params(self, client: OpenAI) -> None:
+        thread = client.beta.threads.create(
+            messages=[
+                {
+                    "role": "user",
+                    "content": "x",
+                    "file_ids": ["string"],
+                    "metadata": {},
+                },
+                {
+                    "role": "user",
+                    "content": "x",
+                    "file_ids": ["string"],
+                    "metadata": {},
+                },
+                {
+                    "role": "user",
+                    "content": "x",
+                    "file_ids": ["string"],
+                    "metadata": {},
+                },
+            ],
+            metadata={},
+        )
+        assert_matches_type(Thread, thread, path=["response"])
+
+    @parametrize
+    def test_raw_response_create(self, client: OpenAI) -> None:
+        response = client.beta.threads.with_raw_response.create()
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        thread = response.parse()
+        assert_matches_type(Thread, thread, path=["response"])
+
+    @parametrize
+    def test_method_retrieve(self, client: OpenAI) -> None:
+        thread = client.beta.threads.retrieve(
+            "string",
+        )
+        assert_matches_type(Thread, thread, path=["response"])
+
+    @parametrize
+    def test_raw_response_retrieve(self, client: OpenAI) -> None:
+        response = client.beta.threads.with_raw_response.retrieve(
+            "string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        thread = response.parse()
+        assert_matches_type(Thread, thread, path=["response"])
+
+    @parametrize
+    def test_method_update(self, client: OpenAI) -> None:
+        thread = client.beta.threads.update(
+            "string",
+        )
+        assert_matches_type(Thread, thread, path=["response"])
+
+    @parametrize
+    def test_method_update_with_all_params(self, client: OpenAI) -> None:
+        thread = client.beta.threads.update(
+            "string",
+            metadata={},
+        )
+        assert_matches_type(Thread, thread, path=["response"])
+
+    @parametrize
+    def test_raw_response_update(self, client: OpenAI) -> None:
+        response = client.beta.threads.with_raw_response.update(
+            "string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        thread = response.parse()
+        assert_matches_type(Thread, thread, path=["response"])
+
+    @parametrize
+    def test_method_delete(self, client: OpenAI) -> None:
+        thread = client.beta.threads.delete(
+            "string",
+        )
+        assert_matches_type(ThreadDeleted, thread, path=["response"])
+
+    @parametrize
+    def test_raw_response_delete(self, client: OpenAI) -> None:
+        response = client.beta.threads.with_raw_response.delete(
+            "string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        thread = response.parse()
+        assert_matches_type(ThreadDeleted, thread, path=["response"])
+
+    @parametrize
+    def test_method_create_and_run(self, client: OpenAI) -> None:
+        thread = client.beta.threads.create_and_run(
+            assistant_id="string",
+        )
+        assert_matches_type(Run, thread, path=["response"])
+
+    @parametrize
+    def test_method_create_and_run_with_all_params(self, client: OpenAI) -> None:
+        thread = client.beta.threads.create_and_run(
+            assistant_id="string",
+            instructions="string",
+            metadata={},
+            model="string",
+            thread={
+                "messages": [
+                    {
+                        "role": "user",
+                        "content": "x",
+                        "file_ids": ["string"],
+                        "metadata": {},
+                    },
+                    {
+                        "role": "user",
+                        "content": "x",
+                        "file_ids": ["string"],
+                        "metadata": {},
+                    },
+                    {
+                        "role": "user",
+                        "content": "x",
+                        "file_ids": ["string"],
+                        "metadata": {},
+                    },
+                ],
+                "metadata": {},
+            },
+            tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}],
+        )
+        assert_matches_type(Run, thread, path=["response"])
+
+    @parametrize
+    def test_raw_response_create_and_run(self, client: OpenAI) -> None:
+        response = client.beta.threads.with_raw_response.create_and_run(
+            assistant_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        thread = response.parse()
+        assert_matches_type(Run, thread, path=["response"])
+
+
+class TestAsyncThreads:
+    strict_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=True)
+    loose_client = AsyncOpenAI(base_url=base_url, api_key=api_key, _strict_response_validation=False)
+    parametrize = pytest.mark.parametrize("client", [strict_client, loose_client], ids=["strict", "loose"])
+
+    @parametrize
+    async def test_method_create(self, client: AsyncOpenAI) -> None:
+        thread = await client.beta.threads.create()
+        assert_matches_type(Thread, thread, path=["response"])
+
+    @parametrize
+    async def test_method_create_with_all_params(self, client: AsyncOpenAI) -> None:
+        thread = await client.beta.threads.create(
+            messages=[
+                {
+                    "role": "user",
+                    "content": "x",
+                    "file_ids": ["string"],
+                    "metadata": {},
+                },
+                {
+                    "role": "user",
+                    "content": "x",
+                    "file_ids": ["string"],
+                    "metadata": {},
+                },
+                {
+                    "role": "user",
+                    "content": "x",
+                    "file_ids": ["string"],
+                    "metadata": {},
+                },
+            ],
+            metadata={},
+        )
+        assert_matches_type(Thread, thread, path=["response"])
+
+    @parametrize
+    async def test_raw_response_create(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.threads.with_raw_response.create()
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        thread = response.parse()
+        assert_matches_type(Thread, thread, path=["response"])
+
+    @parametrize
+    async def test_method_retrieve(self, client: AsyncOpenAI) -> None:
+        thread = await client.beta.threads.retrieve(
+            "string",
+        )
+        assert_matches_type(Thread, thread, path=["response"])
+
+    @parametrize
+    async def test_raw_response_retrieve(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.threads.with_raw_response.retrieve(
+            "string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        thread = response.parse()
+        assert_matches_type(Thread, thread, path=["response"])
+
+    @parametrize
+    async def test_method_update(self, client: AsyncOpenAI) -> None:
+        thread = await client.beta.threads.update(
+            "string",
+        )
+        assert_matches_type(Thread, thread, path=["response"])
+
+    @parametrize
+    async def test_method_update_with_all_params(self, client: AsyncOpenAI) -> None:
+        thread = await client.beta.threads.update(
+            "string",
+            metadata={},
+        )
+        assert_matches_type(Thread, thread, path=["response"])
+
+    @parametrize
+    async def test_raw_response_update(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.threads.with_raw_response.update(
+            "string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        thread = response.parse()
+        assert_matches_type(Thread, thread, path=["response"])
+
+    @parametrize
+    async def test_method_delete(self, client: AsyncOpenAI) -> None:
+        thread = await client.beta.threads.delete(
+            "string",
+        )
+        assert_matches_type(ThreadDeleted, thread, path=["response"])
+
+    @parametrize
+    async def test_raw_response_delete(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.threads.with_raw_response.delete(
+            "string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        thread = response.parse()
+        assert_matches_type(ThreadDeleted, thread, path=["response"])
+
+    @parametrize
+    async def test_method_create_and_run(self, client: AsyncOpenAI) -> None:
+        thread = await client.beta.threads.create_and_run(
+            assistant_id="string",
+        )
+        assert_matches_type(Run, thread, path=["response"])
+
+    @parametrize
+    async def test_method_create_and_run_with_all_params(self, client: AsyncOpenAI) -> None:
+        thread = await client.beta.threads.create_and_run(
+            assistant_id="string",
+            instructions="string",
+            metadata={},
+            model="string",
+            thread={
+                "messages": [
+                    {
+                        "role": "user",
+                        "content": "x",
+                        "file_ids": ["string"],
+                        "metadata": {},
+                    },
+                    {
+                        "role": "user",
+                        "content": "x",
+                        "file_ids": ["string"],
+                        "metadata": {},
+                    },
+                    {
+                        "role": "user",
+                        "content": "x",
+                        "file_ids": ["string"],
+                        "metadata": {},
+                    },
+                ],
+                "metadata": {},
+            },
+            tools=[{"type": "code_interpreter"}, {"type": "code_interpreter"}, {"type": "code_interpreter"}],
+        )
+        assert_matches_type(Run, thread, path=["response"])
+
+    @parametrize
+    async def test_raw_response_create_and_run(self, client: AsyncOpenAI) -> None:
+        response = await client.beta.threads.with_raw_response.create_and_run(
+            assistant_id="string",
+        )
+        assert response.http_request.headers.get("X-Stainless-Lang") == "python"
+        thread = response.parse()
+        assert_matches_type(Run, thread, path=["response"])
tests/api_resources/chat/test_completions.py
@@ -39,11 +39,6 @@ class TestCompletions:
             messages=[
                 {
                     "content": "string",
-                    "function_call": {
-                        "arguments": "string",
-                        "name": "string",
-                    },
-                    "name": "string",
                     "role": "system",
                 }
             ],
@@ -61,9 +56,38 @@ class TestCompletions:
             max_tokens=0,
             n=1,
             presence_penalty=-2,
+            response_format={"type": "json_object"},
+            seed=-9223372036854776000,
             stop="string",
             stream=False,
             temperature=1,
+            tool_choice="none",
+            tools=[
+                {
+                    "type": "function",
+                    "function": {
+                        "description": "string",
+                        "name": "string",
+                        "parameters": {"foo": "bar"},
+                    },
+                },
+                {
+                    "type": "function",
+                    "function": {
+                        "description": "string",
+                        "name": "string",
+                        "parameters": {"foo": "bar"},
+                    },
+                },
+                {
+                    "type": "function",
+                    "function": {
+                        "description": "string",
+                        "name": "string",
+                        "parameters": {"foo": "bar"},
+                    },
+                },
+            ],
             top_p=1,
             user="user-1234",
         )
@@ -103,11 +127,6 @@ class TestCompletions:
             messages=[
                 {
                     "content": "string",
-                    "function_call": {
-                        "arguments": "string",
-                        "name": "string",
-                    },
-                    "name": "string",
                     "role": "system",
                 }
             ],
@@ -126,8 +145,37 @@ class TestCompletions:
             max_tokens=0,
             n=1,
             presence_penalty=-2,
+            response_format={"type": "json_object"},
+            seed=-9223372036854776000,
             stop="string",
             temperature=1,
+            tool_choice="none",
+            tools=[
+                {
+                    "type": "function",
+                    "function": {
+                        "description": "string",
+                        "name": "string",
+                        "parameters": {"foo": "bar"},
+                    },
+                },
+                {
+                    "type": "function",
+                    "function": {
+                        "description": "string",
+                        "name": "string",
+                        "parameters": {"foo": "bar"},
+                    },
+                },
+                {
+                    "type": "function",
+                    "function": {
+                        "description": "string",
+                        "name": "string",
+                        "parameters": {"foo": "bar"},
+                    },
+                },
+            ],
             top_p=1,
             user="user-1234",
         )
@@ -172,11 +220,6 @@ class TestAsyncCompletions:
             messages=[
                 {
                     "content": "string",
-                    "function_call": {
-                        "arguments": "string",
-                        "name": "string",
-                    },
-                    "name": "string",
                     "role": "system",
                 }
             ],
@@ -194,9 +237,38 @@ class TestAsyncCompletions:
             max_tokens=0,
             n=1,
             presence_penalty=-2,
+            response_format={"type": "json_object"},
+            seed=-9223372036854776000,
             stop="string",
             stream=False,
             temperature=1,
+            tool_choice="none",
+            tools=[
+                {
+                    "type": "function",
+                    "function": {
+                        "description": "string",
+                        "name": "string",
+                        "parameters": {"foo": "bar"},
+                    },
+                },
+                {
+                    "type": "function",
+                    "function": {
+                        "description": "string",
+                        "name": "string",
+                        "parameters": {"foo": "bar"},
+                    },
+                },
+                {
+                    "type": "function",
+                    "function": {
+                        "description": "string",
+                        "name": "string",
+                        "parameters": {"foo": "bar"},
+                    },
+                },
+            ],
             top_p=1,
             user="user-1234",
         )
@@ -236,11 +308,6 @@ class TestAsyncCompletions:
             messages=[
                 {
                     "content": "string",
-                    "function_call": {
-                        "arguments": "string",
-                        "name": "string",
-                    },
-                    "name": "string",
                     "role": "system",
                 }
             ],
@@ -259,8 +326,37 @@ class TestAsyncCompletions:
             max_tokens=0,
             n=1,
             presence_penalty=-2,
+            response_format={"type": "json_object"},
+            seed=-9223372036854776000,
             stop="string",
             temperature=1,
+            tool_choice="none",
+            tools=[
+                {
+                    "type": "function",
+                    "function": {
+                        "description": "string",
+                        "name": "string",
+                        "parameters": {"foo": "bar"},
+                    },
+                },
+                {
+                    "type": "function",
+                    "function": {
+                        "description": "string",
+                        "name": "string",
+                        "parameters": {"foo": "bar"},
+                    },
+                },
+                {
+                    "type": "function",
+                    "function": {
+                        "description": "string",
+                        "name": "string",
+                        "parameters": {"foo": "bar"},
+                    },
+                },
+            ],
             top_p=1,
             user="user-1234",
         )
tests/api_resources/fine_tuning/test_jobs.py
@@ -34,7 +34,11 @@ class TestJobs:
         job = client.fine_tuning.jobs.create(
             model="gpt-3.5-turbo",
             training_file="file-abc123",
-            hyperparameters={"n_epochs": "auto"},
+            hyperparameters={
+                "batch_size": "auto",
+                "learning_rate_multiplier": "auto",
+                "n_epochs": "auto",
+            },
             suffix="x",
             validation_file="file-abc123",
         )
@@ -146,7 +150,11 @@ class TestAsyncJobs:
         job = await client.fine_tuning.jobs.create(
             model="gpt-3.5-turbo",
             training_file="file-abc123",
-            hyperparameters={"n_epochs": "auto"},
+            hyperparameters={
+                "batch_size": "auto",
+                "learning_rate_multiplier": "auto",
+                "n_epochs": "auto",
+            },
             suffix="x",
             validation_file="file-abc123",
         )
tests/api_resources/test_completions.py
@@ -41,6 +41,7 @@ class TestCompletions:
             max_tokens=16,
             n=1,
             presence_penalty=-2,
+            seed=-9223372036854776000,
             stop="\n",
             stream=False,
             suffix="test.",
@@ -82,6 +83,7 @@ class TestCompletions:
             max_tokens=16,
             n=1,
             presence_penalty=-2,
+            seed=-9223372036854776000,
             stop="\n",
             suffix="test.",
             temperature=1,
@@ -126,6 +128,7 @@ class TestAsyncCompletions:
             max_tokens=16,
             n=1,
             presence_penalty=-2,
+            seed=-9223372036854776000,
             stop="\n",
             stream=False,
             suffix="test.",
@@ -167,6 +170,7 @@ class TestAsyncCompletions:
             max_tokens=16,
             n=1,
             presence_penalty=-2,
+            seed=-9223372036854776000,
             stop="\n",
             suffix="test.",
             temperature=1,
tests/api_resources/test_files.py
@@ -25,7 +25,7 @@ class TestFiles:
     def test_method_create(self, client: OpenAI) -> None:
         file = client.files.create(
             file=b"raw file contents",
-            purpose="string",
+            purpose="fine-tune",
         )
         assert_matches_type(FileObject, file, path=["response"])
 
@@ -33,7 +33,7 @@ class TestFiles:
     def test_raw_response_create(self, client: OpenAI) -> None:
         response = client.files.with_raw_response.create(
             file=b"raw file contents",
-            purpose="string",
+            purpose="fine-tune",
         )
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
@@ -60,6 +60,13 @@ class TestFiles:
         file = client.files.list()
         assert_matches_type(SyncPage[FileObject], file, path=["response"])
 
+    @parametrize
+    def test_method_list_with_all_params(self, client: OpenAI) -> None:
+        file = client.files.list(
+            purpose="string",
+        )
+        assert_matches_type(SyncPage[FileObject], file, path=["response"])
+
     @parametrize
     def test_raw_response_list(self, client: OpenAI) -> None:
         response = client.files.with_raw_response.list()
@@ -109,7 +116,7 @@ class TestAsyncFiles:
     async def test_method_create(self, client: AsyncOpenAI) -> None:
         file = await client.files.create(
             file=b"raw file contents",
-            purpose="string",
+            purpose="fine-tune",
         )
         assert_matches_type(FileObject, file, path=["response"])
 
@@ -117,7 +124,7 @@ class TestAsyncFiles:
     async def test_raw_response_create(self, client: AsyncOpenAI) -> None:
         response = await client.files.with_raw_response.create(
             file=b"raw file contents",
-            purpose="string",
+            purpose="fine-tune",
         )
         assert response.http_request.headers.get("X-Stainless-Lang") == "python"
         file = response.parse()
@@ -144,6 +151,13 @@ class TestAsyncFiles:
         file = await client.files.list()
         assert_matches_type(AsyncPage[FileObject], file, path=["response"])
 
+    @parametrize
+    async def test_method_list_with_all_params(self, client: AsyncOpenAI) -> None:
+        file = await client.files.list(
+            purpose="string",
+        )
+        assert_matches_type(AsyncPage[FileObject], file, path=["response"])
+
     @parametrize
     async def test_raw_response_list(self, client: AsyncOpenAI) -> None:
         response = await client.files.with_raw_response.list()
tests/api_resources/test_images.py
@@ -31,6 +31,7 @@ class TestImages:
     def test_method_create_variation_with_all_params(self, client: OpenAI) -> None:
         image = client.images.create_variation(
             image=b"raw file contents",
+            model="dall-e-2",
             n=1,
             response_format="url",
             size="1024x1024",
@@ -61,6 +62,7 @@ class TestImages:
             image=b"raw file contents",
             prompt="A cute baby sea otter wearing a beret",
             mask=b"raw file contents",
+            model="dall-e-2",
             n=1,
             response_format="url",
             size="1024x1024",
@@ -89,9 +91,12 @@ class TestImages:
     def test_method_generate_with_all_params(self, client: OpenAI) -> None:
         image = client.images.generate(
             prompt="A cute baby sea otter",
+            model="dall-e-3",
             n=1,
+            quality="standard",
             response_format="url",
             size="1024x1024",
+            style="vivid",
             user="user-1234",
         )
         assert_matches_type(ImagesResponse, image, path=["response"])
@@ -122,6 +127,7 @@ class TestAsyncImages:
     async def test_method_create_variation_with_all_params(self, client: AsyncOpenAI) -> None:
         image = await client.images.create_variation(
             image=b"raw file contents",
+            model="dall-e-2",
             n=1,
             response_format="url",
             size="1024x1024",
@@ -152,6 +158,7 @@ class TestAsyncImages:
             image=b"raw file contents",
             prompt="A cute baby sea otter wearing a beret",
             mask=b"raw file contents",
+            model="dall-e-2",
             n=1,
             response_format="url",
             size="1024x1024",
@@ -180,9 +187,12 @@ class TestAsyncImages:
     async def test_method_generate_with_all_params(self, client: AsyncOpenAI) -> None:
         image = await client.images.generate(
             prompt="A cute baby sea otter",
+            model="dall-e-3",
             n=1,
+            quality="standard",
             response_format="url",
             size="1024x1024",
+            style="vivid",
             user="user-1234",
         )
         assert_matches_type(ImagesResponse, image, path=["response"])
.stats.yml
@@ -1,1 +1,1 @@
-configured_endpoints: 28
+configured_endpoints: 57
api.md
@@ -19,10 +19,23 @@ Types:
 ```python
 from openai.types.chat import (
     ChatCompletion,
+    ChatCompletionAssistantMessageParam,
     ChatCompletionChunk,
+    ChatCompletionContentPart,
+    ChatCompletionContentPartImage,
+    ChatCompletionContentPartText,
+    ChatCompletionFunctionCallOption,
+    ChatCompletionFunctionMessageParam,
     ChatCompletionMessage,
     ChatCompletionMessageParam,
+    ChatCompletionMessageToolCall,
+    ChatCompletionNamedToolChoice,
     ChatCompletionRole,
+    ChatCompletionSystemMessageParam,
+    ChatCompletionTool,
+    ChatCompletionToolChoiceOption,
+    ChatCompletionToolMessageParam,
+    ChatCompletionUserMessageParam,
 )
 ```
 
@@ -66,7 +79,7 @@ Methods:
 
 - <code title="post /files">client.files.<a href="./src/openai/resources/files.py">create</a>(\*\*<a href="src/openai/types/file_create_params.py">params</a>) -> <a href="./src/openai/types/file_object.py">FileObject</a></code>
 - <code title="get /files/{file_id}">client.files.<a href="./src/openai/resources/files.py">retrieve</a>(file_id) -> <a href="./src/openai/types/file_object.py">FileObject</a></code>
-- <code title="get /files">client.files.<a href="./src/openai/resources/files.py">list</a>() -> <a href="./src/openai/types/file_object.py">SyncPage[FileObject]</a></code>
+- <code title="get /files">client.files.<a href="./src/openai/resources/files.py">list</a>(\*\*<a href="src/openai/types/file_list_params.py">params</a>) -> <a href="./src/openai/types/file_object.py">SyncPage[FileObject]</a></code>
 - <code title="delete /files/{file_id}">client.files.<a href="./src/openai/resources/files.py">delete</a>(file_id) -> <a href="./src/openai/types/file_deleted.py">FileDeleted</a></code>
 - <code title="get /files/{file_id}/content">client.files.<a href="./src/openai/resources/files.py">retrieve_content</a>(file_id) -> str</code>
 - <code>client.files.<a href="./src/openai/resources/files.py">wait_for_processing</a>(\*args) -> FileObject</code>
@@ -111,6 +124,12 @@ Methods:
 
 - <code title="post /audio/translations">client.audio.translations.<a href="./src/openai/resources/audio/translations.py">create</a>(\*\*<a href="src/openai/types/audio/translation_create_params.py">params</a>) -> <a href="./src/openai/types/audio/translation.py">Translation</a></code>
 
+## Speech
+
+Methods:
+
+- <code title="post /audio/speech">client.audio.speech.<a href="./src/openai/resources/audio/speech.py">create</a>(\*\*<a href="src/openai/types/audio/speech_create_params.py">params</a>) -> HttpxBinaryResponseContent</code>
+
 # Moderations
 
 Types:
@@ -170,3 +189,122 @@ Methods:
 - <code title="get /fine-tunes">client.fine_tunes.<a href="./src/openai/resources/fine_tunes.py">list</a>() -> <a href="./src/openai/types/fine_tune.py">SyncPage[FineTune]</a></code>
 - <code title="post /fine-tunes/{fine_tune_id}/cancel">client.fine_tunes.<a href="./src/openai/resources/fine_tunes.py">cancel</a>(fine_tune_id) -> <a href="./src/openai/types/fine_tune.py">FineTune</a></code>
 - <code title="get /fine-tunes/{fine_tune_id}/events">client.fine_tunes.<a href="./src/openai/resources/fine_tunes.py">list_events</a>(fine_tune_id, \*\*<a href="src/openai/types/fine_tune_list_events_params.py">params</a>) -> <a href="./src/openai/types/fine_tune_events_list_response.py">FineTuneEventsListResponse</a></code>
+
+# Beta
+
+## Assistants
+
+Types:
+
+```python
+from openai.types.beta import Assistant, AsssitantDeleted
+```
+
+Methods:
+
+- <code title="post /assistants">client.beta.assistants.<a href="./src/openai/resources/beta/assistants/assistants.py">create</a>(\*\*<a href="src/openai/types/beta/assistant_create_params.py">params</a>) -> <a href="./src/openai/types/beta/assistant.py">Assistant</a></code>
+- <code title="get /assistants/{assistant_id}">client.beta.assistants.<a href="./src/openai/resources/beta/assistants/assistants.py">retrieve</a>(assistant_id) -> <a href="./src/openai/types/beta/assistant.py">Assistant</a></code>
+- <code title="post /assistants/{assistant_id}">client.beta.assistants.<a href="./src/openai/resources/beta/assistants/assistants.py">update</a>(assistant_id, \*\*<a href="src/openai/types/beta/assistant_update_params.py">params</a>) -> <a href="./src/openai/types/beta/assistant.py">Assistant</a></code>
+- <code title="get /assistants">client.beta.assistants.<a href="./src/openai/resources/beta/assistants/assistants.py">list</a>(\*\*<a href="src/openai/types/beta/assistant_list_params.py">params</a>) -> <a href="./src/openai/types/beta/assistant.py">SyncCursorPage[Assistant]</a></code>
+- <code title="delete /assistants/{assistant_id}">client.beta.assistants.<a href="./src/openai/resources/beta/assistants/assistants.py">delete</a>(assistant_id) -> <a href="./src/openai/types/beta/asssitant_deleted.py">AsssitantDeleted</a></code>
+
+### Files
+
+Types:
+
+```python
+from openai.types.beta.assistants import AssistantFile, FileDeleteResponse
+```
+
+Methods:
+
+- <code title="post /assistants/{assistant_id}/files">client.beta.assistants.files.<a href="./src/openai/resources/beta/assistants/files.py">create</a>(assistant_id, \*\*<a href="src/openai/types/beta/assistants/file_create_params.py">params</a>) -> <a href="./src/openai/types/beta/assistants/assistant_file.py">AssistantFile</a></code>
+- <code title="get /assistants/{assistant_id}/files/{file_id}">client.beta.assistants.files.<a href="./src/openai/resources/beta/assistants/files.py">retrieve</a>(file_id, \*, assistant_id) -> <a href="./src/openai/types/beta/assistants/assistant_file.py">AssistantFile</a></code>
+- <code title="get /assistants/{assistant_id}/files">client.beta.assistants.files.<a href="./src/openai/resources/beta/assistants/files.py">list</a>(assistant_id, \*\*<a href="src/openai/types/beta/assistants/file_list_params.py">params</a>) -> <a href="./src/openai/types/beta/assistants/assistant_file.py">SyncCursorPage[AssistantFile]</a></code>
+- <code title="delete /assistants/{assistant_id}/files/{file_id}">client.beta.assistants.files.<a href="./src/openai/resources/beta/assistants/files.py">delete</a>(file_id, \*, assistant_id) -> <a href="./src/openai/types/beta/assistants/file_delete_response.py">FileDeleteResponse</a></code>
+
+## Threads
+
+Types:
+
+```python
+from openai.types.beta import Thread, ThreadDeleted
+```
+
+Methods:
+
+- <code title="post /threads">client.beta.threads.<a href="./src/openai/resources/beta/threads/threads.py">create</a>(\*\*<a href="src/openai/types/beta/thread_create_params.py">params</a>) -> <a href="./src/openai/types/beta/thread.py">Thread</a></code>
+- <code title="get /threads/{thread_id}">client.beta.threads.<a href="./src/openai/resources/beta/threads/threads.py">retrieve</a>(thread_id) -> <a href="./src/openai/types/beta/thread.py">Thread</a></code>
+- <code title="post /threads/{thread_id}">client.beta.threads.<a href="./src/openai/resources/beta/threads/threads.py">update</a>(thread_id, \*\*<a href="src/openai/types/beta/thread_update_params.py">params</a>) -> <a href="./src/openai/types/beta/thread.py">Thread</a></code>
+- <code title="delete /threads/{thread_id}">client.beta.threads.<a href="./src/openai/resources/beta/threads/threads.py">delete</a>(thread_id) -> <a href="./src/openai/types/beta/thread_deleted.py">ThreadDeleted</a></code>
+- <code title="post /threads/runs">client.beta.threads.<a href="./src/openai/resources/beta/threads/threads.py">create_and_run</a>(\*\*<a href="src/openai/types/beta/thread_create_and_run_params.py">params</a>) -> <a href="./src/openai/types/beta/threads/run.py">Run</a></code>
+
+### Runs
+
+Types:
+
+```python
+from openai.types.beta.threads import RequiredActionFunctionToolCall, Run
+```
+
+Methods:
+
+- <code title="post /threads/{thread_id}/runs">client.beta.threads.runs.<a href="./src/openai/resources/beta/threads/runs/runs.py">create</a>(thread_id, \*\*<a href="src/openai/types/beta/threads/run_create_params.py">params</a>) -> <a href="./src/openai/types/beta/threads/run.py">Run</a></code>
+- <code title="get /threads/{thread_id}/runs/{run_id}">client.beta.threads.runs.<a href="./src/openai/resources/beta/threads/runs/runs.py">retrieve</a>(run_id, \*, thread_id) -> <a href="./src/openai/types/beta/threads/run.py">Run</a></code>
+- <code title="post /threads/{thread_id}/runs/{run_id}">client.beta.threads.runs.<a href="./src/openai/resources/beta/threads/runs/runs.py">update</a>(run_id, \*, thread_id, \*\*<a href="src/openai/types/beta/threads/run_update_params.py">params</a>) -> <a href="./src/openai/types/beta/threads/run.py">Run</a></code>
+- <code title="get /threads/{thread_id}/runs">client.beta.threads.runs.<a href="./src/openai/resources/beta/threads/runs/runs.py">list</a>(thread_id, \*\*<a href="src/openai/types/beta/threads/run_list_params.py">params</a>) -> <a href="./src/openai/types/beta/threads/run.py">SyncCursorPage[Run]</a></code>
+- <code title="post /threads/{thread_id}/runs/{run_id}/cancel">client.beta.threads.runs.<a href="./src/openai/resources/beta/threads/runs/runs.py">cancel</a>(run_id, \*, thread_id) -> <a href="./src/openai/types/beta/threads/run.py">Run</a></code>
+- <code title="post /threads/{thread_id}/runs/{run_id}/submit_tool_outputs">client.beta.threads.runs.<a href="./src/openai/resources/beta/threads/runs/runs.py">submit_tool_outputs</a>(run_id, \*, thread_id, \*\*<a href="src/openai/types/beta/threads/run_submit_tool_outputs_params.py">params</a>) -> <a href="./src/openai/types/beta/threads/run.py">Run</a></code>
+
+#### Steps
+
+Types:
+
+```python
+from openai.types.beta.threads.runs import (
+    CodeToolCall,
+    FunctionToolCall,
+    MessageCreationStepDetails,
+    RetrievalToolCall,
+    RunStep,
+    ToolCallsStepDetails,
+)
+```
+
+Methods:
+
+- <code title="get /threads/{thread_id}/runs/{run_id}/steps/{step_id}">client.beta.threads.runs.steps.<a href="./src/openai/resources/beta/threads/runs/steps.py">retrieve</a>(step_id, \*, thread_id, run_id) -> <a href="./src/openai/types/beta/threads/runs/run_step.py">RunStep</a></code>
+- <code title="get /threads/{thread_id}/runs/{run_id}/steps">client.beta.threads.runs.steps.<a href="./src/openai/resources/beta/threads/runs/steps.py">list</a>(run_id, \*, thread_id, \*\*<a href="src/openai/types/beta/threads/runs/step_list_params.py">params</a>) -> <a href="./src/openai/types/beta/threads/runs/run_step.py">SyncCursorPage[RunStep]</a></code>
+
+### Messages
+
+Types:
+
+```python
+from openai.types.beta.threads import (
+    MessageContentImageFile,
+    MessageContentText,
+    ThreadMessage,
+    ThreadMessageDeleted,
+)
+```
+
+Methods:
+
+- <code title="post /threads/{thread_id}/messages">client.beta.threads.messages.<a href="./src/openai/resources/beta/threads/messages/messages.py">create</a>(thread_id, \*\*<a href="src/openai/types/beta/threads/message_create_params.py">params</a>) -> <a href="./src/openai/types/beta/threads/thread_message.py">ThreadMessage</a></code>
+- <code title="get /threads/{thread_id}/messages/{message_id}">client.beta.threads.messages.<a href="./src/openai/resources/beta/threads/messages/messages.py">retrieve</a>(message_id, \*, thread_id) -> <a href="./src/openai/types/beta/threads/thread_message.py">ThreadMessage</a></code>
+- <code title="post /threads/{thread_id}/messages/{message_id}">client.beta.threads.messages.<a href="./src/openai/resources/beta/threads/messages/messages.py">update</a>(message_id, \*, thread_id, \*\*<a href="src/openai/types/beta/threads/message_update_params.py">params</a>) -> <a href="./src/openai/types/beta/threads/thread_message.py">ThreadMessage</a></code>
+- <code title="get /threads/{thread_id}/messages">client.beta.threads.messages.<a href="./src/openai/resources/beta/threads/messages/messages.py">list</a>(thread_id, \*\*<a href="src/openai/types/beta/threads/message_list_params.py">params</a>) -> <a href="./src/openai/types/beta/threads/thread_message.py">SyncCursorPage[ThreadMessage]</a></code>
+
+#### Files
+
+Types:
+
+```python
+from openai.types.beta.threads.messages import MessageFile
+```
+
+Methods:
+
+- <code title="get /threads/{thread_id}/messages/{message_id}/files/{file_id}">client.beta.threads.messages.files.<a href="./src/openai/resources/beta/threads/messages/files.py">retrieve</a>(file_id, \*, thread_id, message_id) -> <a href="./src/openai/types/beta/threads/messages/message_file.py">MessageFile</a></code>
+- <code title="get /threads/{thread_id}/messages/{message_id}/files">client.beta.threads.messages.files.<a href="./src/openai/resources/beta/threads/messages/files.py">list</a>(message_id, \*, thread_id, \*\*<a href="src/openai/types/beta/threads/messages/file_list_params.py">params</a>) -> <a href="./src/openai/types/beta/threads/messages/message_file.py">SyncCursorPage[MessageFile]</a></code>
pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "openai"
-version = "1.0.1"
+version = "1.1.0"
 description = "Client library for the openai API"
 readme = "README.md"
 license = "Apache-2.0"